Loading...
Searching...
No Matches
mask.h
Go to the documentation of this file.
1//
2// Copyright 2025 Pixar
3//
4// Licensed under the terms set forth in the LICENSE.txt file available at
5// https://openusd.org/license.
6//
7#ifndef PXR_EXEC_VDF_MASK_H
8#define PXR_EXEC_VDF_MASK_H
9
11
12#include "pxr/pxr.h"
13
14#include "pxr/exec/vdf/api.h"
15
16#include "pxr/base/tf/compressedBits.h"
17#include "pxr/base/tf/delegatedCountPtr.h"
20
21#include <atomic>
22#include <functional>
23#include <iosfwd>
24#include <string>
25#include <utility>
26
27PXR_NAMESPACE_OPEN_SCOPE
28
37{
38 class _BitsImpl;
39
40public:
44
48
51 explicit VdfMask(size_t size) {
52 // If size == 0, we want to leave the optional _bits uninitialized.
53 // This is important, because we use the uninitialized optional as a
54 // sentinal for a zero size mask, and we need to be consistent about
55 // that.
56 if (size != 0) {
57 _bits = _FindOrInsert(VdfMask::Bits(size));
58 }
59 }
60
63 explicit VdfMask(VdfMask::Bits const &bits) {
64 // If the bits denote a 1x1 mask, use the static 1x1 mask for
65 // initialization. This prevents a call to _FindOrInsert, which locks
66 // on the mask registry.
67 if (bits.GetSize() == 1 && bits.AreAllSet()) {
68 *this = _GetAllOnes1();
69 }
70
71 // If size == 0, we want to leave the optional _bits uninitialized.
72 // This is important, because we use the uninitialized optional as a
73 // sentinal for a zero size mask, and we need to be consistent about
74 // that.
75 else if (bits.GetSize() != 0) {
76 _bits = _FindOrInsert(bits);
77 }
78 }
79
85 explicit VdfMask(VdfMask::Bits &&bits) {
86 if (bits.GetSize() != 0) {
87 _bits = _FindOrEmplace(std::move(bits));
88 }
89 }
90
93 void Swap(VdfMask &rhs) noexcept {
94 _bits.swap(rhs._bits);
95 }
96
99 friend void swap(VdfMask &lhs, VdfMask &rhs) noexcept {
100 lhs.Swap(rhs);
101 }
102
105 void SetAll() {
106 if (!_bits) {
107 return;
108 }
109
110 VdfMask::Bits bits(_bits->Get().GetSize());
111 bits.Complement();
112 _bits = _FindOrInsert(bits);
113 }
114
119 void SetIndex(size_t index) {
120 if (!TF_VERIFY(_bits)) {
121 return;
122 }
123
124 VdfMask::Bits copy(_bits->Get());
125 copy.Set(index);
126 _bits = _FindOrEmplace(std::move(copy));
127 }
128
131 bool IsSet(size_t index) const {
132 if (!TF_VERIFY(_bits)) {
133 return false;
134 }
135
136 return _bits->Get().IsSet(index);
137 }
138
143 void ClearIndex(size_t index) {
144 if (!TF_VERIFY(_bits)) {
145 return;
146 }
147
148 VdfMask::Bits copy(_bits->Get());
149 copy.Clear(index);
150 _bits = _FindOrEmplace(std::move(copy));
151 }
152
158 size_t GetSize() const {
159 if (!_bits) {
160 return 0;
161 }
162
163 return _bits->Get().GetSize();
164 }
165
168 bool IsEmpty() const {
169 return !_bits;
170 }
171
175 bool Overlaps(const VdfMask &mask) const {
176 if (_bits == mask._bits) {
177 return IsAnySet();
178 }
179
180 return GetBits().HasNonEmptyIntersection(mask.GetBits());
181 }
182
186 bool Contains(const VdfMask &mask) const {
187 if (_bits == mask._bits) {
188 return true;
189 }
190
191 return !mask.GetBits().HasNonEmptyDifference(GetBits());
192 }
193
196 bool IsAllOnes() const {
197 if (!_bits) {
198 return true;
199 }
200
201 return _bits->Get().AreAllSet();
202 }
203
206 bool IsAllZeros() const {
207 if (!_bits) {
208 return true;
209 }
210
211 return _bits->Get().AreAllUnset();
212 }
213
216 bool IsAnySet() const {
217 if (!_bits) {
218 return false;
219 }
220
221 return _bits->Get().IsAnySet();
222 }
223
226 size_t GetFirstSet() const {
227 if (!_bits) {
228 return 0;
229 }
230
231 return _bits->Get().GetFirstSet();
232 }
233
236 size_t GetLastSet() const {
237 if (!_bits) {
238 return 0;
239 }
240
241 return _bits->Get().GetLastSet();
242 }
243
246 size_t GetNumSet() const {
247 if (!_bits) {
248 return 0;
249 }
250
251 return _bits->Get().GetNumSet();
252 }
253
258 bool IsContiguous() const {
259 if (!_bits) {
260 return false;
261 }
262
263 return _bits->Get().AreContiguouslySet();
264 }
265
268
271 bool operator==(const VdfMask &rhs) const {
272 return _bits == rhs._bits;
273 }
274
275 bool operator!=(const VdfMask &rhs) const {
276 return !(*this == rhs);
277 }
278
283 {
284 bool operator()(const VdfMask &lhs, const VdfMask &rhs) const
285 {
286 return lhs._bits < rhs._bits;
287 }
288 };
289
295 if (_bits == rhs._bits) {
296 return *this;
297 }
298
299 _bits = _FindOrEmplace(GetBits() & rhs.GetBits());
300 return *this;
301 }
302
303 VdfMask operator&(const VdfMask &rhs) const {
304 VdfMask r(*this);
305 r &= rhs;
306 return r;
307 }
308
314 if (_bits == rhs._bits) {
315 return *this;
316 }
317
318 _bits = _FindOrEmplace(GetBits() | rhs.GetBits());
319 return *this;
320 }
321
322 VdfMask operator|(const VdfMask &rhs) const {
323 VdfMask r(*this);
324 r |= rhs;
325 return r;
326 }
327
334 if (!_bits && TF_VERIFY(!rhs._bits)) {
335 return *this;
336 }
337
338 _bits = _FindOrEmplace(GetBits() ^ rhs.GetBits());
339 return *this;
340 }
341
342 VdfMask operator^(const VdfMask &rhs) const {
343 VdfMask r(*this);
344 r ^= rhs;
345 return r;
346 }
347
354 if (!_bits && TF_VERIFY(!rhs._bits)) {
355 return *this;
356 }
357
358 VdfMask::Bits copy = GetBits();
359 copy -= rhs.GetBits();
360 _bits = _FindOrEmplace(std::move(copy));
361 return *this;
362 }
363
364 VdfMask operator-(const VdfMask &rhs) const {
365 VdfMask r(*this);
366 r -= rhs;
367 return r;
368 }
369
373 if (!_bits) {
374 return *this;
375 }
376
377 _bits = _FindOrEmplace(
378 VdfMask::Bits(_bits->Get(), VdfMask::Bits::ComplementTag));
379 return *this;
380 }
381
386 if (!_bits) {
387 *this = rhs;
388 } else {
389 *this |= rhs;
390 }
391 return *this;
392 }
393
395
396
399 class iterator {
400 using _BaseIterator = VdfMask::Bits::AllSetView::const_iterator;
401
402 public:
403 using value_type = _BaseIterator::value_type;
404
408
411 bool operator==(const iterator &rhs) const {
412 return _it == rhs._it;
413 }
414
417 bool operator!=(const iterator &rhs) const {
418 return !operator==(rhs);
419 }
420
423 value_type operator*() const {
424 return *_it;
425 }
426
430 ++_it;
431 return *this;
432 }
433
436 bool IsAtEnd() const {
437 return _it.IsAtEnd();
438 }
439
443 _it = _BaseIterator();
444 }
445
449 int AdvanceTo(value_type index) {
450 if (_it.IsAtEnd()) {
451 return 0;
452 }
453
454 // The index must be ahead of the current iterator position.
455 TF_DEV_AXIOM(index >= *_it);
456
457 // We can simply increment the underlying VdfMask::Bits iterator
458 // until we reach (past) index.
459 while (!_it.IsAtEnd() && *_it < index) {
460 ++_it;
461 }
462 return *_it;
463 }
464
465 private:
466
467 // Only a mask is a allowed to create an iterator.
468 iterator(const VdfMask::Bits *bits) :
469 _it(bits->GetAllSetView().begin())
470 {}
471
472 friend class VdfMask;
473
474 // The wrapped VdfMask::Bits iterator
475 _BaseIterator _it;
476 };
477
478
482 iterator begin() const {
483 if (!_bits) {
485 }
486
487 return iterator(&_bits->Get());
488 }
489
490
494 static VdfMask AllOnes(size_t size) {
495 // special-case all-ones of size 1 and 0
496 if (size == 0) {
497 return VdfMask();
498 } else if (size == 1) {
499 return _GetAllOnes1();
500 }
501
502 VdfMask::Bits bits(size);
503 bits.SetAll();
504 return VdfMask(bits);
505 }
506
509 static VdfMask AllZeros(size_t size) {
510 // special-case all-zeros of size 0
511 if (size == 0) {
512 return VdfMask();
513 }
514
515 return VdfMask(size);
516 }
517
521
528 std::string GetRLEString() const {
529 if (!_bits) {
530 return std::string();
531 }
532
533 return _bits->Get().GetAsRLEString();
534 }
535
539 size_t GetMemoryUsage() const {
540 if (!_bits) {
541 return 0;
542 }
543
544 return _bits->Get().GetAllocatedSize();
545 }
546
548
549
553
556 VdfMask::Bits const &GetBits() const {
557 if (!_bits) {
559 }
560
561 return _bits->Get();
562 }
563
565 size_t GetHash() const {
566 return std::hash<_BitsImpl*>()(_bits.get());
567 }
568
571 struct HashFunctor {
572 size_t operator()(const VdfMask &mask) const {
573 return mask.GetHash();
574 }
575 };
576
578
579private:
580
581 struct _AllOnes1Factory
582 {
583 static VdfMask * New()
584 {
585 return new VdfMask(_AllOnes1Factory());
586 }
587 };
588
589 explicit VdfMask(const _AllOnes1Factory &) {
590 Bits allOnes1Bits(1);
591 allOnes1Bits.SetAll();
592 _bits = _FindOrInsert(allOnes1Bits);
593 }
594
595 static VdfMask _GetAllOnes1() {
596 return *_allOnes1;
597 }
598
599 // Befriend the stream operator so that VdfMasks support output streaming.
600 friend VDF_API std::ostream & operator<<(
601 std::ostream &os, const VdfMask &mask);
602
603 // Refcounted hash table nodes for VdfMask::Bits.
604 //
605 class _BitsImpl
606 {
607 public:
608 // Non-copyable
609 _BitsImpl(const _BitsImpl &) = delete;
610 _BitsImpl& operator=(const _BitsImpl &) = delete;
611
612 // Non-movable
613 _BitsImpl(_BitsImpl &&) = delete;
614 _BitsImpl& operator=(_BitsImpl &&) = delete;
615
616 // Provide const access to the bits. Flyweighting requires that
617 // the value is never mutated.
618 //
619 const VdfMask::Bits &Get() const { return _bits; }
620
621 // Return the pre-computed hash value for _bits.
622 //
623 // Implemented in MaskRegistry.h
624 //
625 size_t GetHash() const;
626
627 private:
628 // next points to the next entry in the hash bucket (if any) for
629 // \p bits.
630 //
631 _BitsImpl(_BitsImpl *next, size_t hash, VdfMask::Bits &&bits);
632
633 friend inline void TfDelegatedCountIncrement(_BitsImpl *p) noexcept;
634 friend inline void TfDelegatedCountDecrement(_BitsImpl *p) noexcept;
635
636 friend class Vdf_MaskRegistry;
637
638 _BitsImpl *_next;
639 size_t _hash;
640 VdfMask::Bits _bits;
641 std::atomic<int> _refCount;
642 // Note that the resurrection count is bounded by the number of threads
643 // concurrently accessing masks, thus a 16-bit integer is sufficient.
644 std::atomic<uint16_t> _resurrectionCount;
645 bool _isImmortal;
646 };
647
648 friend inline void TfDelegatedCountIncrement(_BitsImpl *p) noexcept;
649 friend inline void TfDelegatedCountDecrement(_BitsImpl *p) noexcept;
650
651 friend class Vdf_MaskRegistry;
652
653 typedef TfDelegatedCountPtr<_BitsImpl> _BitsImplRefPtr;
654
655 // Return a ref ptr to the \c _BitsImpl corresponding to \p *bits. If an
656 // existing _BitsImpl for \p *bits is not found, create a new one by
657 // *moving* the contents out of \p *bits and into the new \c _BitsImpl.
658 //
659 // Do not use \p *bits after passing it to this function.
660 //
661 VDF_API
662 static _BitsImplRefPtr _FindOrEmplace(VdfMask::Bits &&bits);
663
664 // Return a ref ptr to the \c _BitsImpl corresponding to \p bits. If an
665 // existing _BitsImpl for \p bits is not found, create a new one by
666 // copying \p bits.
667 //
668 VDF_API
669 static _BitsImplRefPtr _FindOrInsert(const VdfMask::Bits &bits);
670
671 // Erase the _BitsImpl pointed to by \p bits.
672 //
673 VDF_API
674 static void _EraseBits(_BitsImpl *bits);
675
676private:
677 // Default constructed / empty masks are represented as null
678 // _BitsImplRefPtr.
679 _BitsImplRefPtr _bits;
680
681 // AllOnes mask of size 1 optimization.
682 VDF_API static TfStaticData<VdfMask, _AllOnes1Factory> _allOnes1;
683};
684
685// Specialize TfDelegatedCountPtr operations for VdfMask::_BitsImpl.
686inline void TfDelegatedCountIncrement(VdfMask::_BitsImpl *p) noexcept
687{
688 // For immortal masks there is no need to maintain the reference count.
689 if (p->_isImmortal) {
690 return;
691 }
692
693 // There's no need for a stronger memory ordering here because we can only
694 // increase the ref count by way of an existing reference and sharing an
695 // existing VdfMask between threads requires external synchronization,
696 // just like any other non-atomic type.
697 //
698 // Note that Vdf_MaskRegistry manages reference counting and serialization
699 // for threads that are looking up the same bits concurrently rather than
700 // simply making copies of an existing VdfMask.
701 p->_refCount.fetch_add(1, std::memory_order_relaxed);
702}
703inline void TfDelegatedCountDecrement(VdfMask::_BitsImpl *p) noexcept
704{
705 // For immortal masks there is no need to maintain the reference count.
706 if (p->_isImmortal) {
707 return;
708 }
709
710 // Many threads may decrement the ref count but only one thread will be
711 // responsible for deleting it. However, we must ensure that all of the
712 // memory operations in all of the threads happen before the final thread
713 // performs the deletion. To establish this happens-before relationship,
714 // we need a release-acquire pair of atomic operations.
715 const int prevRC = p->_refCount.fetch_sub(1, std::memory_order_release);
716 if (prevRC == 1) {
717 // Use an acquire fence here because we only need to synchronize with
718 // the decrement accesses when we're about to perform the deletion.
719 std::atomic_thread_fence(std::memory_order_acquire);
720 VdfMask::_EraseBits(p);
721 }
722}
723
724// Output stream operator
725VDF_API
726std::ostream &
727operator<<(std::ostream &os, const VdfMask &mask);
728
729PXR_NAMESPACE_CLOSE_SCOPE
730
731#endif
Low-level utilities for informing users of various internal and external diagnostic conditions.
Fast, compressed bit array which is capable of performing logical operations without first decompress...
void SetAll()
Sets all bits to one.
size_t GetSize() const
Returns the size of the bit array, ie.
bool AreAllSet() const
Returns true, if all the bits in this bit array are set.
void Clear(size_t index)
Clears bit # index to zero.
void Set(size_t index)
Sets bit # index to zero.
TfCompressedBits & Complement()
Flips all bits.
bool HasNonEmptyDifference(const TfCompressedBits &rhs) const
Returns true if the result of an asymmetric set different is non-zero.
static const TfCompressedBits & GetEmpty()
Returns an empty TfBits.
bool HasNonEmptyIntersection(const TfCompressedBits &rhs) const
Returns true if the result of the intersection with rhs would be non-zero.
Stores a pointer to a ValueType which uses TfDelegatedCountIncrement and TfDelegatedCountDecrement to...
void swap(TfDelegatedCountPtr &other) noexcept
Swap this object's held pointer with other's.
RawPtrType get() const noexcept
Return the underlying pointer.
Create or return a previously created object instance of global data.
Definition: staticData.h:96
Iterator class used to iterate through the elements of the mask.
Definition: mask.h:399
bool IsAtEnd() const
Returns true if the iteration is finished.
Definition: mask.h:436
void AdvanceToEnd()
Advance the iterator to the end.
Definition: mask.h:442
bool operator!=(const iterator &rhs) const
Returns true if this iterator and rhs do not compare equal.
Definition: mask.h:417
iterator()
Constructs an null iterator that is already at end.
Definition: mask.h:407
bool operator==(const iterator &rhs) const
Returns true if this iterator and rhs compare equal.
Definition: mask.h:411
iterator & operator++()
Increment the iterator to the next element.
Definition: mask.h:429
value_type operator*() const
Returns the index of the current element.
Definition: mask.h:423
int AdvanceTo(value_type index)
Advance the iterator to the first index that is set in the mask located at or after index.
Definition: mask.h:449
A VdfMask is placed on connections to specify the data flowing through them.
Definition: mask.h:37
void SetAll()
Enables all the bits in the mask.
Definition: mask.h:105
size_t GetSize() const
Returns the size of the mask.
Definition: mask.h:158
VdfMask(VdfMask::Bits &&bits)
Constructs a mask by moving the contents of bits into the mask.
Definition: mask.h:85
iterator begin() const
Returns an iterator that can be used to iterate through the elements of the mask.
Definition: mask.h:482
static VdfMask AllZeros(size_t size)
Returns a mask of the requested size where no element is set.
Definition: mask.h:509
VdfMask & operator-=(const VdfMask &rhs)
Performs an asymmetric set difference.
Definition: mask.h:353
void SetIndex(size_t index)
Adds the given index to the mask.
Definition: mask.h:119
bool IsAnySet() const
Returns true, if there is at least a single set entry.
Definition: mask.h:216
bool IsContiguous() const
Returns true if the set bits in the mask are contiguous.
Definition: mask.h:258
VdfMask & SetOrAppend(const VdfMask &rhs)
Sets this mask to rhs if this mask is of zero size.
Definition: mask.h:385
TfCompressedBits Bits
Typedef on the internal bitset implementation used.
Definition: mask.h:43
VdfMask & operator^=(const VdfMask &rhs)
Xors two masks together.
Definition: mask.h:333
void ClearIndex(size_t index)
Removes the given index from the mask.
Definition: mask.h:143
bool IsAllZeros() const
Returns true if this mask has all entries unset.
Definition: mask.h:206
bool operator==(const VdfMask &rhs) const
Returns true if this and rhs are equal, false otherwise.
Definition: mask.h:271
VdfMask(VdfMask::Bits const &bits)
Constructs a mask from VdfMask::Bits.
Definition: mask.h:63
bool IsAllOnes() const
Returns true if this mask has all entries set.
Definition: mask.h:196
size_t GetLastSet() const
Returns the last set bit in the mask.
Definition: mask.h:236
std::string GetRLEString() const
Returns the mask in an RLE format.
Definition: mask.h:528
bool IsSet(size_t index) const
Returns true if mask at index is set.
Definition: mask.h:131
bool IsEmpty() const
Returns true if this mask is empty, i.e.
Definition: mask.h:168
bool Overlaps(const VdfMask &mask) const
Returns true if this mask and mask have any set entries in common, and false otherwise.
Definition: mask.h:175
size_t GetFirstSet() const
Returns the first set bit in the mask.
Definition: mask.h:226
VdfMask & operator|=(const VdfMask &rhs)
Ors two masks together.
Definition: mask.h:313
VdfMask & operator&=(const VdfMask &rhs)
Ands two masks together.
Definition: mask.h:294
static VdfMask AllOnes(size_t size)
Returns a mask of the requested size that will iterate over all elements.
Definition: mask.h:494
size_t GetHash() const
Returns a hash for the mask.
Definition: mask.h:565
size_t GetMemoryUsage() const
Returns the amount of memory in bytes used by this mask.
Definition: mask.h:539
VdfMask & Complement()
Complement.
Definition: mask.h:372
VdfMask(size_t size)
Constructs a mask of size size.
Definition: mask.h:51
VdfMask::Bits const & GetBits() const
Get this mask's content as CtCompressedfBits.
Definition: mask.h:556
VdfMask()
Constructs an empty mask.
Definition: mask.h:47
bool Contains(const VdfMask &mask) const
Returns true if mask is a subset-of or equal to this mask, false otherwise.
Definition: mask.h:186
friend void swap(VdfMask &lhs, VdfMask &rhs) noexcept
Swap lhs's bits with rhs.
Definition: mask.h:99
size_t GetNumSet() const
Returns the number of set bits in the mask.
Definition: mask.h:246
void Swap(VdfMask &rhs) noexcept
Swap this mask's bits with rhs.
Definition: mask.h:93
GF_API std::ostream & operator<<(std::ostream &, const GfBBox3d &)
Output a GfBBox3d using the format [(range) matrix zeroArea].
#define TF_DEV_AXIOM(cond)
The same as TF_AXIOM, but compiled only in dev builds.
Definition: diagnostic.h:205
#define TF_VERIFY(cond, format,...)
Checks a condition and reports an error if it evaluates false.
Definition: diagnostic.h:266
Arbitrary total ordering of masks.
Definition: mask.h:283
Hash Functor.
Definition: mask.h:571