HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
readWriteAccessor.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_READ_WRITE_ACCESSOR_H
8 #define PXR_EXEC_VDF_READ_WRITE_ACCESSOR_H
9 
10 ///\file
11 
12 #include "pxr/pxr.h"
13 
14 #include "pxr/exec/vdf/context.h"
15 #include "pxr/exec/vdf/iterator.h"
16 
17 #include "pxr/base/arch/hints.h"
18 
20 
21 ///////////////////////////////////////////////////////////////////////////////
22 ///
23 /// \class VdfReadWriteAccessor
24 ///
25 /// VdfReadWriteAccessor allows for random access to output data. The index
26 /// into the data is in iteration space, i.e. access to index N returns the
27 /// value of the N-th element as visited by the VdfReadWriteIterator.
28 ///
29 /// \warning Due to performance caveats described below, accessing values
30 /// through an iterator (e.g. VdfReadWriteIterator) is preferred, if the data
31 /// is accessed in a forward iterating pattern.
32 ///
33 /// If the memory layout of the output values is not contiguous in the output
34 /// buffer (e.g. a non-contiguous affects mask), the accessor will redirect
35 /// access to the underlying data. This indirection can be costly. If the data
36 /// is contiguous in memory, fast access will be provided through what is
37 /// essentially access through pointer indirection / indexing of array elements.
38 ///
39 /// Note that the memory layout of output buffers is an implementation detail
40 /// of the system influenced by many factors. Subsequently, no assumptions can
41 /// be made about whether access will take the fast- or the slow-path.
42 ///
43 /// The only way to guarantee fast indirection is by accessing data through
44 /// iterators (e.g. VdfReadWriteIterator). The use of iterators instead
45 /// of using VdfReadWriteAccessor is strongly encouraged.
46 ///
47 template < typename T >
49 {
50 public:
51 
52  /// Constructs a read/write accessor for the given input or output. If no
53  /// input with the specified \p name exists on the current node, or if the
54  /// input does not have an associated output, attempt to find an output
55  /// named \p name. Emits a coding error if \p name does not name an input
56  /// or an output.
57  ///
58  VdfReadWriteAccessor(const VdfContext &context, const TfToken &name);
59 
60  /// Constructs a read/write accessor for the only output on the current
61  /// node. If the node has more than a single output, a coding error will be
62  /// emitted.
63  ///
64  VdfReadWriteAccessor(const VdfContext &context) :
65  VdfReadWriteAccessor(context, TfToken())
66  {}
67 
68  /// Provides constant random access to the data stored at the output. Note
69  /// that \p index must be within [0, GetSize()). Out of bounds access will
70  /// lead to undefined behavior.
71  ///
72  const T &operator[](size_t index) const {
73  return const_cast<VdfReadWriteAccessor *>(this)->operator[](index);
74  }
75 
76  /// Provides mutable random access to the data stored at the output. Note
77  /// that \p index must be within [0, GetSize()). Out of bounds access will
78  /// lead to undefined behavior.
79  ///
80  T &operator[](size_t index);
81 
82  /// Returns \c true if there is no data stored at the output.
83  ///
84  bool IsEmpty() const {
85  return GetSize() == 0;
86  }
87 
88  /// Returns the size of the data stored at the output.
89  ///
90  size_t GetSize() const {
91  return _size;
92  }
93 
94 private:
95 
96  // The accessor to the output data.
98 
99  // The mask with accessible data elements. All elements are accessible if
100  // this mask is empty.
101  VdfMask _mask;
102 
103  // The offset into the data.
104  size_t _offset;
105 
106  // The size of the data.
107  size_t _size;
108 
109 };
110 
111 ///////////////////////////////////////////////////////////////////////////////
112 
113 template < typename T >
115  const VdfContext &context,
116  const TfToken &name) :
117  _offset(0),
118  _size(0)
119 {
120  // Get the required output for writing. This will emit a coding error if
121  // there is no valid output.
122  const VdfOutput *output = _GetRequiredOutputForWriting(context, name);
123  if (!output) {
124  return;
125  }
126 
127  // Retrieve the relevant masks at the output. This will return false if the
128  // output is not scheduled, i.e. the accessor will remain empty.
129  const VdfMask *requestMask = nullptr;
130  const VdfMask *affectsMask = nullptr;
131  if (!_GetOutputMasks(context, *output, &requestMask, &affectsMask)) {
132  return;
133  }
134 
135  // Get the value to write to. It is an error for this value not to be
136  // available. The executor engine is responsible for creating it.
137  VdfVector *v = _GetOutputValueForWriting(context, *output);
138  if (!TF_VERIFY(v, "Output '%s' is missing buffer.",
139  output->GetName().GetText())) {
140  return;
141  }
142 
143  // Get the accessor to the value.
144  _accessor = v->GetReadWriteAccessor<T>();
145 
146  // If there is an affects mask on this output, and that mask is not
147  // all-ones, use the mask to redirect data access. If the mask is
148  // contiguous, we can simply use the first index as an offset into the data.
149  // The size is the number of bits set on the mask.
150  if (affectsMask && !affectsMask->IsAllOnes()) {
151  if (affectsMask->IsContiguous()) {
152  _offset = affectsMask->GetFirstSet();
153  } else {
154  _mask = *affectsMask;
155  }
156  _size = affectsMask->GetNumSet();
157  }
158 
159  // If there is no affects mask, or if the affects mask is all ones we can
160  // provide access without redirection. The size is the number of values on
161  // the vector accessor.
162  else {
163  _size = _accessor.GetNumValues();
164  }
165 }
166 
167 template < typename T >
168 T &
170 {
171  // Perform out of bounds check in debug builds.
172  TF_DEV_AXIOM(index < GetSize());
173 
174  // The fast-path is for data that is contiguous in memory. The offset is
175  // often 0, but the addition is fast enough to perform indiscriminately and
176  // instead of a branch.
177  if (ARCH_LIKELY(_mask.IsEmpty())) {
178  return _accessor[index + _offset];
179  }
180 
181  // If a mask is used to redirect data access, we need to map the provided
182  // index to the n-th set bit in the mask. This is the slow-path.
183  return _accessor[_mask.GetBits().FindNthSet(index)];
184 }
185 
187 
188 #endif
#define ARCH_LIKELY(x)
Definition: hints.h:29
ReadWriteAccessor< TYPE > GetReadWriteAccessor() const
Definition: vector.h:456
bool IsContiguous() const
Definition: mask.h:258
size_t GetFirstSet() const
Definition: mask.h:226
const GLdouble * v
Definition: glcorearb.h:837
#define PXR_NAMESPACE_OPEN_SCOPE
Definition: pxr.h:73
VdfReadWriteAccessor(const VdfContext &context)
A VdfMask is placed on connections to specify the data flowing through them.
Definition: mask.h:36
const T & operator[](size_t index) const
VDF_API VdfVector * _GetOutputValueForWriting(const VdfContext &context, const VdfOutput &output) const
#define TF_DEV_AXIOM(cond)
Definition: token.h:70
VdfReadWriteAccessor(const VdfContext &context, const TfToken &name)
VDF_API const TfToken & GetName() const
GLuint const GLchar * name
Definition: glcorearb.h:786
char const * GetText() const
Definition: token.h:179
VDF_API const VdfOutput * _GetRequiredOutputForWriting(const VdfContext &context, const TfToken &name) const
size_t GetNumSet() const
Definition: mask.h:246
size_t GetSize() const
GLuint index
Definition: glcorearb.h:786
size_t GetNumValues() const
Definition: vector.h:423
#define PXR_NAMESPACE_CLOSE_SCOPE
Definition: pxr.h:74
VDF_API bool _GetOutputMasks(const VdfContext &context, const VdfOutput &output, const VdfMask **requestMask, const VdfMask **affectsMask) const
bool IsAllOnes() const
Definition: mask.h:196