HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
executorDataVector.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_EXECUTOR_DATA_VECTOR_H
8 #define PXR_EXEC_VDF_EXECUTOR_DATA_VECTOR_H
9 
10 ///\file
11 
12 #include "pxr/pxr.h"
13 
14 #include "pxr/exec/vdf/api.h"
17 #include "pxr/exec/vdf/output.h"
18 #include "pxr/exec/vdf/smblData.h"
19 #include "pxr/exec/vdf/types.h"
20 
21 #include <tbb/concurrent_vector.h>
22 
23 #include <memory>
24 
26 
27 class VdfNetwork;
28 
29 ///////////////////////////////////////////////////////////////////////////////
30 ///
31 /// \class Vdf_ExecutorDataVector
32 ///
33 /// \brief A vector-like container for executor data used by the
34 /// VdfDataManagerVector. Unlike a hash-map, the executor data storage laid out
35 /// somewhat contiguously in memory, and may therefore be quicker to access.
36 /// The access pattern during the first round of data access determines the
37 /// memory layout.
38 ///
40 {
41 public:
42 
43  /// The data handle type is an index into the internal data vectors.
44  ///
45  typedef size_t DataHandle;
46 
47  /// This sentinel index denotes an invalid handle.
48  ///
49  static const size_t InvalidHandle = size_t(-1);
50 
51  /// Destructor.
52  ///
53  VDF_API
55 
56  /// Resize the vector to be able to accommodate all outputs in the given
57  /// network.
58  ///
59  VDF_API
60  void Resize(const VdfNetwork &network);
61 
62  /// Returns an existing data handle, or creates a new one for the given
63  /// \p output.
64  ///
65  /// This method is guaranteed to return a valid data handle.
66  ///
67  inline DataHandle GetOrCreateDataHandle(const VdfId outputId);
68 
69  /// Returns an existing data handle for the given \p output. This method
70  /// will return an invalid data handle, if no handle has been created
71  /// for the given \p output.
72  ///
73  inline DataHandle GetDataHandle(const VdfId outputId) const;
74 
75  /// Returns the VdfExecutorBufferData associated with the given \p handle.
76  ///
77  /// Note it is undefined behavior to call this method with an invalid
78  /// data \p handle.
79  ///
81  return &_bufferData[handle];
82  }
83 
84  /// Returns the VdfExecutorInvalidationData associated with the given
85  /// \p handle.
86  ///
87  /// Note it is undefined behavior to call this method with an invalid
88  /// data \p handle.
89  ///
91  return &_invalidationData[handle];
92  }
93 
94  /// Returns the VdfInvalidationTimestamp associated with the given
95  /// \p handle.
96  ///
97  /// Note it is undefined behavior to call this method with an invalid
98  /// data \p handle.
99  ///
101  const DataHandle handle) const {
102  return _outputData[handle].invalidationTimestamp;
103  }
104 
105  /// Sets the invalidation \p timestamp for the give data \p handle.
106  ///
107  /// Note it is undefined behavior to call this method with an invalid
108  /// data \p handle.
109  ///
111  const DataHandle &handle,
113  _outputData[handle].invalidationTimestamp = ts;
114  }
115 
116  /// Returns an existing \p VdfSMBLData associated with the given \p handle.
117  /// Returns \c nullptr if there is no SMBL data associated with this
118  /// data \p handle.
119  ///
120  /// Note it is undefined behavior to call this method with an invalid
121  /// data \p handle.
122  ///
123  VdfSMBLData * GetSMBLData(const DataHandle handle) const {
124  return _smblData[handle].get();
125  }
126 
127  /// Returns an existing \p VdfSMBLData associated with the given \p handle
128  /// or creates a new one of none exists.
129  ///
130  /// Note it is undefined behavior to call this method with an invalid
131  /// data \p handle.
132  ///
134  if (!_smblData[handle]) {
135  _smblData[handle].reset(new VdfSMBLData());
136  }
137  return _smblData[handle].get();
138  }
139 
140  /// Returns \c true if the data at the given \p handle has been touched by
141  /// evaluation.
142  ///
143  /// Note it is undefined behavior to call this method with an invalid
144  /// data \p handle.
145  ///
146  bool IsTouched(const DataHandle handle) {
147  return _outputData[handle].touched;
148  }
149 
150  /// Marks the data at the given \p handle as having been touched by
151  /// evaluation.
152  ///
153  /// Note it is undefined behavior to call this method with an invalid
154  /// data \p handle.
155  ///
156  void Touch(const DataHandle handle) {
157  _outputData[handle].touched = true;
158  }
159 
160  /// Marks the data at the given \p handle as not having been touched by
161  /// evaluation. Returns \c true if the data has previously been touched.
162  ///
163  /// Note it is undefined behavior to call this method with an invalid
164  /// data \p handle.
165  ///
166  bool Untouch(const DataHandle handle) {
167  const bool wasTouched = _outputData[handle].touched;
168  _outputData[handle].touched = false;
169  return wasTouched;
170  }
171 
172  /// Returns the size of the container.
173  ///
174  size_t GetSize() const {
175  return _locations.size();
176  }
177 
178  /// Returns the number of outputs that have data associated with them.
179  ///
180  size_t GetNumData() const {
181  return _bufferData.size();
182  }
183 
184  /// Resets the output data at the given data \p handle to a newly
185  /// constructed state. The output with \p outputId is the new owner
186  /// of the output data.
187  ///
188  inline void Reset(const DataHandle handle, const VdfId outputId);
189 
190  /// Clears all the data in the container.
191  ///
192  VDF_API
193  void Clear();
194 
195 private:
196 
197  // The auxiliary output data stored for each output in the vector.
198  struct _OutputData {
199  _OutputData(const VdfId oid) :
200  outputId(oid),
201  invalidationTimestamp(
202  VdfExecutorInvalidationData::InitialInvalidationTimestamp),
203  touched(false)
204  {}
205 
206  void Reset(const VdfId oid) {
207  outputId = oid;
208  invalidationTimestamp =
210  touched = false;
211  }
212 
213  VdfId outputId;
214  VdfInvalidationTimestamp invalidationTimestamp;
215  bool touched;
216  };
217 
218  // Type of each segment in the locations array. An array of integers.
219  using _LocationsSegment = uint32_t *;
220 
221  // Create a new segment at the provided index out of line, and return a
222  // pointer to the new segment.
223  VDF_API
224  _LocationsSegment _CreateSegment(size_t segmentIndex);
225 
226  // Pushes a new data entry into the internal vectors for the output with
227  // the given outputId.
228  inline void _CreateData(const VdfId outputId);
229 
230  // Reserve this many executor data entries
231  constexpr static size_t _InitialExecutorDataNum = 1000;
232 
233  // The size of a segment in the segmented locations array. Must be a power
234  // of two.
235  constexpr static size_t _SegmentSize = 4096;
236 
237  // The locations array, indexing the data vector.
238  std::vector<_LocationsSegment> _locations;
239 
240  // The data vector. Note we use a tbb::concurrent_vector here not for its
241  // thread-safe properties, but for the fact that it allocates chunks of
242  // contiguous memory, and does not copy/move any existing elements when
243  // the container grows.
244  tbb::concurrent_vector<_OutputData> _outputData;
245  tbb::concurrent_vector<VdfExecutorBufferData> _bufferData;
246  tbb::concurrent_vector<VdfExecutorInvalidationData> _invalidationData;
247  tbb::concurrent_vector<std::unique_ptr<VdfSMBLData>> _smblData;
248 
249 };
250 
251 ///////////////////////////////////////////////////////////////////////////////
252 
255 {
256  // Get the output index.
257  const VdfIndex outputIndex = VdfOutput::GetIndexFromId(outputId);
258 
259  // Compute the index of the segment.
260  const size_t segmentIndex = outputIndex / _SegmentSize;
261  TF_DEV_AXIOM(segmentIndex < _locations.size());
262 
263  // Retrieve the corresponding segment, or create a new one of necessary.
264  _LocationsSegment segment = _locations[segmentIndex];
265  if (!segment) {
266  segment = _CreateSegment(segmentIndex);
267  }
268 
269  // Using the segment offset, look up the location in the segment.
270  const size_t segmentOffset = outputIndex & (_SegmentSize - 1);
271  uint32_t * const location = &segment[segmentOffset];
272 
273  // The location may be uninitialized, so let's do a bounds check against
274  // the data vector. We further have to check whether the index at the
275  // proposed location in the data vector points back to the same output.
276  // If either of this is false, we will insert a new entry into the data
277  // vector and update the location.
278  const size_t numData = GetNumData();
279  if (*location >= numData ||
280  outputIndex != VdfOutput::GetIndexFromId(
281  _outputData[*location].outputId)) {
282  *location = numData;
283  _CreateData(outputId);
284  }
285 
286  // If the location points to the right place in the data vector, we must
287  // also verify the output version. Note, that we do not have to extract
288  // the version from the id. At this point, the entire id better match!
289  // If not, the version has changed an the output data must be reset.
290  else if (outputId != _outputData[*location].outputId) {
291  Reset(*location, outputId);
292  }
293 
294  // Return the newly inserted or existing data entry.
295  return *location;
296 }
297 
300 {
301  // Get the output index.
302  const VdfIndex outputIndex = VdfOutput::GetIndexFromId(outputId);
303 
304  // Compute the index to the segment.
305  const size_t segmentIndex = outputIndex / _SegmentSize;
306 
307  // If the segment index is out of bounds or the segment has not been
308  // allocated, we can bail out right away.
309  if (segmentIndex >= _locations.size() || !_locations[segmentIndex]) {
310  return InvalidHandle;
311  }
312 
313  // If the location points to a valid entry in the data vector, and the
314  // data at that index matches the output id, we can return the data.
315  // Otherwise, the location may either be garbage, or the output version
316  // may have changed.
317  const size_t segmentOffset = outputIndex & (_SegmentSize - 1);
318  const uint32_t location = _locations[segmentIndex][segmentOffset];
319  return
320  location < GetNumData() && outputId == _outputData[location].outputId
321  ? location
322  : InvalidHandle;
323 }
324 
325 void
326 Vdf_ExecutorDataVector::Reset(const DataHandle handle, const VdfId outputId)
327 {
328  _outputData[handle].Reset(outputId);
329  _bufferData[handle].Reset();
330  _invalidationData[handle].Reset();
331  _smblData[handle].reset();
332 }
333 
334 void
335 Vdf_ExecutorDataVector::_CreateData(const VdfId outputId)
336 {
337  _outputData.emplace_back(outputId);
338  _bufferData.emplace_back();
339  _invalidationData.emplace_back();
340  _smblData.emplace_back();
341 }
342 
344 
345 #endif
VDF_API void Clear()
VdfSMBLData * GetOrCreateSMBLData(const DataHandle handle)
bool IsTouched(const DataHandle handle)
void Touch(const DataHandle handle)
#define PXR_NAMESPACE_OPEN_SCOPE
Definition: pxr.h:73
VDF_API void Resize(const VdfNetwork &network)
DataHandle GetDataHandle(const VdfId outputId) const
#define VDF_API
Definition: api.h:25
static constexpr VdfInvalidationTimestamp InitialInvalidationTimestamp
bool Untouch(const DataHandle handle)
#define TF_DEV_AXIOM(cond)
VdfExecutorInvalidationData * GetInvalidationData(const DataHandle handle)
void Reset(const DataHandle handle, const VdfId outputId)
VdfSMBLData holds per-output data that is meant to be consumed by the executor. This data is an optio...
Definition: smblData.h:30
GLint location
Definition: glcorearb.h:805
void SetInvalidationTimestamp(const DataHandle &handle, VdfInvalidationTimestamp ts)
VdfSMBLData * GetSMBLData(const DataHandle handle) const
This object is responsible for storing the executor buffer data, comprised of the executor cache vect...
VdfExecutorBufferData * GetBufferData(const DataHandle handle)
VdfInvalidationTimestamp GetInvalidationTimestamp(const DataHandle handle) const
A vector-like container for executor data used by the VdfDataManagerVector. Unlike a hash-map...
static VdfIndex GetIndexFromId(const VdfId id)
Definition: output.h:109
unsigned int VdfInvalidationTimestamp
Type of the timestamp that identifies the most recent round of invalidation.
Definition: types.h:74
#define PXR_NAMESPACE_CLOSE_SCOPE
Definition: pxr.h:74
VDF_API ~Vdf_ExecutorDataVector()
static const size_t InvalidHandle
DataHandle GetOrCreateDataHandle(const VdfId outputId)
uint64_t VdfId
The unique identifier type for Vdf objects.
Definition: types.h:107
uint32_t VdfIndex
The index type for Vdf objects.
Definition: types.h:110