HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
weightedIterator.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_WEIGHTED_ITERATOR_H
8 #define PXR_EXEC_VDF_WEIGHTED_ITERATOR_H
9 
10 /// \file
11 
12 #include "pxr/pxr.h"
13 
16 #include "pxr/exec/vdf/iterator.h"
17 #include "pxr/exec/vdf/node.h"
18 #include "pxr/exec/vdf/vector.h"
19 
20 #include "pxr/base/tf/diagnostic.h"
21 
22 #include <cstddef>
23 #include <initializer_list>
24 
26 
27 /// The information held per weight slot in a weighted iterator.
28 ///
30 {
31  /// The vector of weights we are iterating over.
33 
34  /// The current iterator index into the VdfIndexedWeights above.
35  size_t currentIndex;
36 };
37 
38 /// Array of weight slots for weighted iterators.
39 ///
40 /// Inline storage is provided for one weight slot to avoid heap allocation in
41 /// the common case. However, the local storage and remote storage pointer are
42 /// not overlapped as this results in undesirable overhead when accessing
43 /// elements. Iterators are almost always stack allocated and
44 /// VdfWeightedIterator is non-copyable making compactness less valuable.
45 ///
47 {
48  Vdf_WeightSlotArray(const Vdf_WeightSlotArray &) = delete;
49  Vdf_WeightSlotArray& operator=(const Vdf_WeightSlotArray &) = delete;
50 
51 public:
56  using const_iterator = const Vdf_WeightSlot *;
57 
58  /// Construct an empty array.
59  ///
61  : _begin(nullptr)
62  , _end(nullptr)
63  {}
64 
65  /// Destructor.
66  ///
68  if (_begin != _local) {
69  delete[] _begin;
70  }
71  }
72 
73  /// Allocate storage for \p numInputs elements.
74  ///
75  /// This function may only be called once during the lifetime of the
76  /// array. The size of the array is fixed the first time it is allocated.
77  /// Elements in the array are uninitialized.
78  ///
79  void Allocate(uint32_t numInputs) {
80  if (!TF_VERIFY(!_begin && !_end)) {
81  return;
82  }
83 
84  _begin = numInputs > NUM_LOCAL_STORAGE
85  ? new value_type[numInputs]
86  : _local;
87  _end = _begin + numInputs;
88  }
89 
90  /// Return the number of slots in the array.
91  ///
92  size_t size() const {
93  return _end - _begin;
94  }
95 
96  /// Return an iterator to the first slot in the array.
97  ///
99  return _begin;
100  }
101 
102  /// Return an iterator to the end of the array.
103  ///
105  return _end;
106  }
107 
108  /// Return an iterator to the first slot in the array.
109  ///
111  return _begin;
112  }
113 
114  /// Return an iterator to the end of the array.
115  ///
116  const_iterator end() const {
117  return _end;
118  }
119 
120  /// Access the slot at index \p slot.
121  //
122  reference operator[](size_t slot) {
123  return _begin[slot];
124  }
125 
126  /// Access the slot at index \p slot.
127  //
128  const_reference operator[](size_t slot) const {
129  return _begin[slot];
130  }
131 
132  /// Returns the next index that has a weight at or after \p index.
133  /// Updates the currentIndex.
134  ///
136 
137 private:
138  value_type *_begin;
139  value_type *_end;
140 
141  // The amount of local storage reserved for slots.
142  // A single slot is stored locally, multiple slots are stored remotely.
143  static const size_t NUM_LOCAL_STORAGE = 1;
144 
145  value_type _local[NUM_LOCAL_STORAGE];
146 };
147 
148 
149 ////////////////////////////////////////////////////////////////////////////////
150 ///
151 /// This iterator can be used to iterate through an input that is weighted by
152 /// one or more weight vectors.
153 ///
154 template<class IteratorType>
155 class VdfWeightedIterator final : public VdfIterator
156 {
157 public:
158 
159  /// Type of the elements this iterator gives access to.
160  ///
162 
163  /// Type of a reference to a value of this iterator.
164  ///
166 
167  /// Constructs a weighted iterator using a single weight name.
168  ///
169  template<typename... Args>
171  const VdfContext &context,
172  const TfToken &weightName,
173  Args&&... args) :
174  _iterator(context, std::forward<Args>(args)...) {
175  const TfToken *weightNamePtr = &weightName;
176  _Init(context, weightNamePtr, weightNamePtr + 1);
177  }
178 
179  /// Constructs a weighted iterator using an initializer list for weight
180  /// names.
181  ///
182  template<typename... Args>
184  const VdfContext &context,
185  std::initializer_list<TfToken> weightNames,
186  Args&&... args) :
187  _iterator(context, std::forward<Args>(args)...) {
188  _Init(context, weightNames.begin(), weightNames.end());
189  }
190 
191  /// Constructs a weighted iterator using a vector for weight names.
192  ///
193  template<typename... Args>
195  const VdfContext &context,
196  const std::vector<TfToken> &weightNames,
197  Args&&... args) :
198  _iterator(context, std::forward<Args>(args)...) {
199  const TfToken *weightNamesBegin = weightNames.data();
200  const TfToken *weightNamesEnd = weightNamesBegin + weightNames.size();
201  _Init(context, weightNamesBegin, weightNamesEnd);
202  }
203 
204  /// Destructor.
205  ///
206  ~VdfWeightedIterator() = default;
207 
208  /// Increment operator to point to the next element.
209  ///
211 
212  /// Returns reference to current element.
213  ///
215  return _iterator.operator*();
216  }
217 
218  /// Returns true if the iterator is done iterating and false otherwise.
219  ///
220  bool IsAtEnd() const {
221  return _iterator.IsAtEnd();
222  }
223 
224  /// Returns the current index for the current connection.
225  ///
226  /// This method should not generally be used.
227  ///
228  int GetCurrentIndex() const {
229  return Vdf_GetIteratorIndex(_iterator);
230  }
231 
232  /// Advance the iterator to the end.
233  ///
234  void AdvanceToEnd() {
235  _iterator.AdvanceToEnd();
236  }
237 
238  /// Returns the weight at the current element. If no weight is
239  /// explicitly present at the given \p slot, \p defWeight (default is 0.0)
240  /// is returned.
241  ///
242  double GetWeight(size_t slot = 0, double defWeight = 0.0) const {
243  _GetExplicitWeight(slot, &defWeight);
244  return defWeight;
245  }
246 
247  /// Returns true if the weight at the current element is explicitly set
248  /// at \p slot.
249  ///
250  bool HasExplicitWeight(size_t slot) const {
251  double ret;
252  return _GetExplicitWeight(slot, &ret);
253  }
254 
255  /// Returns a pair (bool, double) indicating whether there is a weight
256  /// explicitly present at the given \p slot and giving the weight or the
257  /// given default weight as fallback.
258  ///
259  std::pair<bool, double> GetExplicitWeight(
260  size_t slot = 0, double defWeight = 0.0) const;
261 
262  /// Get the number of weight slots used.
263  ///
264  size_t GetNumSlots() const {
265  return _slots.size();
266  }
267 
268  /// Returns the # of explicit weights for \p slot.
269  ///
270  size_t GetNumExplicitWeights(size_t slot = 0) const {
271  return (slot < _slots.size()) ?
272  _slots[slot].weights->GetSize() : 0;
273  }
274 
275 private:
276 
277  // Noncopyable
278  VdfWeightedIterator(const VdfWeightedIterator &) = delete;
279  VdfWeightedIterator &operator=(const VdfWeightedIterator &) = delete;
280 
281  // Returns the current index into the data source.
283  return Vdf_GetIteratorIndex(it._iterator);
284  }
285 
286  // Two constructors above call this shared code upon construction
287  void _Init(
288  const VdfContext &context,
289  const TfToken *begin,
290  const TfToken *end);
291 
292  // Extracts weighted data from weightInput and adds it to our vector
293  // of weight vectors.
294  void _AddWeightsInput(
295  Vdf_WeightSlot *slot,
296  const VdfInput *weightInput,
297  const VdfContext &context);
298 
299  // Returns true and the weigth in *ret if the weight is explicity set for
300  // this slot, otherwise, it returns false.
301  bool _GetExplicitWeight(size_t slot, double *ret) const;
302 
303  // Implements the template parameter specified behavior
304  //
305  // The affects mask iterator is advanced to the next explicit weight
306  // only if the template parameter is SkipElementsWithNoWeights.
307  // The currentIndex is always updated.
308  //
309  // Advances the current _iterator to the first index where we have
310  // both a weight explicity set and an element set in the mask.
311  void _AdvanceIterator();
312 
313 private:
314 
315  // The underlying iterator.
316  IteratorType _iterator;
317  Vdf_WeightSlotArray _slots;
318 };
319 
320 ////////////////////////////////////////////////////////////////////////////////
321 
322 template<class IteratorType>
323 void
325  const VdfContext &context,
326  const TfToken *begin,
327  const TfToken *end)
328 {
329  // If there's nothing set in the mask, there's no need to go on.
330  if (_iterator.IsAtEnd()) {
331  return;
332  }
333 
334  // Get the number of slots.
335  const std::ptrdiff_t numInputs = std::distance(begin, end);
336 
337  // Reserve storage for the slots, if required.
338  if (numInputs > 0) {
339  _slots.Allocate(numInputs);
340  }
341 
342  // Add all the weights inputs.
343  Vdf_WeightSlot *slot = _slots.begin();
344  const VdfNode &node = _GetNode(context);
345  for (const TfToken *it = begin; it != end; ++it, ++slot) {
346  const VdfInput *input = node.GetInput(*it);
347  if (!input) {
349  "Can't find input '%s' on node %s",
350  it->GetText(),
351  node.GetDebugName().c_str());
352  }
353  _AddWeightsInput(slot, input, context);
354  }
355 
356  // No inputs.
357  if (numInputs == 0) {
358  TF_CODING_ERROR("Weighted Iterator instantiated with no weights.");
359  return;
360  }
361 
362  // By here, *_iterator has been initialized with the first set mask.
363  // What we'd like to do is advance it to the next index such that
364  // we have both an explicit weight and a set element in the mask.
365  _AdvanceIterator();
366 }
367 
368 template<class IteratorType>
369 void
371  Vdf_WeightSlot *slot,
372  const VdfInput *weightInput,
373  const VdfContext &context)
374 {
375  // We always expect exactly one input connection.
376  if (weightInput && weightInput->GetNumConnections() == 1) {
377  const VdfConnection &connection = (*weightInput)[0];
378  const VdfVector &out = _GetRequiredInputValue(
379  context, connection, connection.GetMask());
380 
381  // We always expect exactly one element.
382  if (ARCH_LIKELY(out.GetSize() == 1)) {
383  // Note that here we hold on to a pointer from the executor because
384  // we don't want to copy the vector of weights. It is okay to hold
385  // on to the pointer only because the lifetime of this iterator is
386  // limited.
387  slot->weights = &out.GetReadAccessor<VdfIndexedWeights>()[0];
388  slot->currentIndex = 0;
389  return;
390  }
391 
392  TF_CODING_ERROR("Weight input must have exactly one element (got %zu)",
393  out.GetSize());
394  }
395 
396  else if (weightInput && weightInput->GetNumConnections() > 1) {
397  // This is an error, all weight connectors must have exactly one input.
398  TF_CODING_ERROR("Weight connector must have at most one input (got %zu)",
399  weightInput->GetNumConnections());
400  }
401 
402  // Not exactly one input connection and data element.
403  slot->weights = nullptr;
404  slot->currentIndex = 0;
405 }
406 
407 template<class IteratorType>
410 {
411  // We need to differentiate between two cases here:
412  //
413  // a) There can be holes in the mask _iterator works on, we want to skip
414  // those fast.
415  // b) There can be holes in the explicit weights, which we also want to
416  // skip fast.
417 
418  // Advance iterator to the next element as indicated by mask, this may
419  // or may not skip holes in the mask.
420 
421  _iterator.operator++();
422  _AdvanceIterator();
423 
424  return *this;
425 }
426 
427 template<class IteratorType>
428 void
430 {
431  while (!_iterator.IsAtEnd()) {
432 
433  // Find the next index that has an explicit weight after currentIndex
434  const int currentIndex = Vdf_GetIteratorIndex(_iterator);
435  const int nextExplicitIndex =
436  _slots.AdvanceToNextExplicitIndex(currentIndex);
437 
438  // If there are no more explicit weights, we're done.
439  if (nextExplicitIndex == std::numeric_limits<int>::max()) {
440  // We're done iterating, set _iterator to end().
441  _iterator.AdvanceToEnd();
442  break;
443 
444  } else if (nextExplicitIndex != currentIndex) {
445 
446  // In this case the next explicit weight was further along than
447  // our iterator. So let's try to advance the iterator.
448  while (!_iterator.IsAtEnd() &&
449  Vdf_GetIteratorIndex(_iterator) < nextExplicitIndex) {
450  ++_iterator;
451  }
452 
453  if (_iterator.IsAtEnd() ||
454  Vdf_GetIteratorIndex(_iterator) == nextExplicitIndex) {
455  // Great! Both the iterator and the next explicit weights
456  // have a value, or we have reached the end of iterator. We're
457  // done.
458  break;
459  }
460 
461  // If we get here, the iterator did not visit an element at the
462  // explicit index, and we have now advanced it beyond the explicit
463  // index. Let's retry at the current index.
464 
465  } else {
466  // There is a next explicit weight at our current iterator index.
467  // We're done.
468  break;
469  }
470  }
471 }
472 
473 inline int
475  int index)
476 {
477  int nextExplicitIndex = std::numeric_limits<int>::max();
478 
479  // Iterate over all the weight slots.
480  for (Vdf_WeightSlot &p : *this) {
481  const VdfIndexedWeights *w = p.weights;
482 
483  // If we're done with that slot, don't bother trying to find one.
484  if (ARCH_UNLIKELY(!w || p.currentIndex >= w->GetSize())) {
485  continue;
486  }
487 
488  TF_DEV_AXIOM(w);
489 
490  // We'll try to do a quick local search from our last known
491  // position, for speed.
493  if (p.currentIndex < w->GetSize()) {
494  // We already found the next index that we care about, we
495  // won't do any more work than is necessary.
496  int wIndexVal = w->GetIndex(p.currentIndex);
497  if (wIndexVal < nextExplicitIndex) {
498  nextExplicitIndex = wIndexVal;
499  }
500  }
501  }
502 
503  return nextExplicitIndex;
504 }
505 
506 template<class IteratorType>
507 std::pair<bool, double>
509  size_t slot, double defWeight) const
510 {
511  bool hasExplicitWeight = _GetExplicitWeight(slot, &defWeight);
512  return std::pair<bool, double>(hasExplicitWeight, defWeight);
513 }
514 
515 template<class IteratorType>
516 inline bool
518  size_t slot, double *ret) const
519 {
520  TF_DEV_AXIOM(ret);
521 
522  if (slot < _slots.size()) {
523  const Vdf_WeightSlot &p = _slots[slot];
524  if (p.weights &&
525  p.currentIndex < p.weights->GetSize() &&
526  p.weights->GetIndex(p.currentIndex) ==
527  Vdf_GetIteratorIndex(_iterator)) {
528  *ret = p.weights->GetData(p.currentIndex);
529  return true;
530  }
531  }
532  return false;
533 }
534 
536 
537 #endif
const_iterator begin() const
#define ARCH_LIKELY(x)
Definition: hints.h:29
bool HasExplicitWeight(size_t slot) const
VdfWeightedIterator & operator++()
const_iterator end() const
Vdf_WeightSlot value_type
reference operator[](size_t slot)
Access the slot at index slot.
size_t GetSize() const
Definition: vector.h:146
int GetCurrentIndex() const
IteratorType::reference reference
#define PXR_NAMESPACE_OPEN_SCOPE
Definition: pxr.h:73
size_t GetNumConnections() const
Definition: input.h:58
VDF_API const std::string GetDebugName() const
#define TF_CODING_ERROR
Definition: node.h:52
const VdfInput * GetInput(const TfToken &inputName) const
Definition: node.h:164
const VdfIndexedWeights * weights
The vector of weights we are iterating over.
VdfWeightedIterator(const VdfContext &context, const std::vector< TfToken > &weightNames, Args &&...args)
uint64 value_type
Definition: GA_PrimCompat.h:29
reference operator*() const
size_t size() const
Return the size of the string that this token represents.
Definition: token.h:169
size_t GetFirstDataIndex(size_t currentIndex) const
Definition: indexedData.h:279
size_t size() const
Definition: input.h:35
size_t GetSize() const
Definition: indexedData.h:53
#define ARCH_UNLIKELY(x)
Definition: hints.h:30
#define TF_DEV_AXIOM(cond)
Definition: token.h:70
char const * data() const
Synonym for GetText().
Definition: token.h:185
size_t GetNumExplicitWeights(size_t slot=0) const
GLuint GLuint end
Definition: glcorearb.h:475
double GetWeight(size_t slot=0, double defWeight=0.0) const
std::pair< bool, double > GetExplicitWeight(size_t slot=0, double defWeight=0.0) const
size_t GetNumSlots() const
~VdfWeightedIterator()=default
ReadAccessor< TYPE > GetReadAccessor() const
Definition: vector.h:521
friend int Vdf_GetIteratorIndex(const VdfWeightedIterator &it)
PcpNodeRef_ChildrenIterator begin(const PcpNodeRef::child_const_range &r)
Support for range-based for loops for PcpNodeRef children ranges.
Definition: node.h:587
int AdvanceToNextExplicitIndex(int index)
GLuint index
Definition: glcorearb.h:786
const_reference operator[](size_t slot) const
Access the slot at index slot.
ImageBuf OIIO_API max(Image_or_Const A, Image_or_Const B, ROI roi={}, int nthreads=0)
#define PXR_NAMESPACE_CLOSE_SCOPE
Definition: pxr.h:74
VdfWeightedIterator(const VdfContext &context, std::initializer_list< TfToken > weightNames, Args &&...args)
**If you just want to fire and args
Definition: thread.h:618
GLubyte GLubyte GLubyte GLubyte w
Definition: glcorearb.h:857
SIM_API const UT_StringHolder distance
that also have some descendant prim *whose name begins with which in turn has a child named baz where *the predicate and *a name There is also one special expression reference
VdfByValueOrConstRef< T > GetData(size_t i) const
Definition: indexedData.h:79
const VdfMask & GetMask() const
Returns the mask for this connection.
Definition: connection.h:99
size_t currentIndex
The current iterator index into the VdfIndexedWeights above.
int GetIndex(size_t i) const
Definition: indexedData.h:73
IteratorType::value_type value_type
VdfWeightedIterator(const VdfContext &context, const TfToken &weightName, Args &&...args)
void Allocate(uint32_t numInputs)