Loading...
Searching...
No Matches
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
11
12#include "pxr/pxr.h"
13
14#include "pxr/exec/vdf/api.h"
17#include "pxr/exec/vdf/output.h"
19#include "pxr/exec/vdf/types.h"
20
21#include <tbb/concurrent_vector.h>
22
23#include <memory>
24
25PXR_NAMESPACE_OPEN_SCOPE
26
27class VdfNetwork;
28
40{
41public:
42
45 typedef size_t DataHandle;
46
49 static const size_t InvalidHandle = size_t(-1);
50
53 VDF_API
55
59 VDF_API
60 void Resize(const VdfNetwork &network);
61
67 inline DataHandle GetOrCreateDataHandle(const VdfId outputId);
68
73 inline DataHandle GetDataHandle(const VdfId outputId) const;
74
81 return &_bufferData[handle];
82 }
83
90 VdfExecutorInvalidationData * GetInvalidationData(const DataHandle handle) {
91 return &_invalidationData[handle];
92 }
93
101 const DataHandle handle) const {
102 return _outputData[handle].invalidationTimestamp;
103 }
104
111 const DataHandle &handle,
113 _outputData[handle].invalidationTimestamp = ts;
114 }
115
123 VdfSMBLData * GetSMBLData(const DataHandle handle) const {
124 return _smblData[handle].get();
125 }
126
134 if (!_smblData[handle]) {
135 _smblData[handle].reset(new VdfSMBLData());
136 }
137 return _smblData[handle].get();
138 }
139
146 bool IsTouched(const DataHandle handle) {
147 return _outputData[handle].touched;
148 }
149
156 void Touch(const DataHandle handle) {
157 _outputData[handle].touched = true;
158 }
159
166 bool Untouch(const DataHandle handle) {
167 const bool wasTouched = _outputData[handle].touched;
168 _outputData[handle].touched = false;
169 return wasTouched;
170 }
171
174 size_t GetSize() const {
175 return _locations.size();
176 }
177
180 size_t GetNumData() const {
181 return _bufferData.size();
182 }
183
188 inline void Reset(const DataHandle handle, const VdfId outputId);
189
192 VDF_API
193 void Clear();
194
195private:
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 =
209 VdfExecutorInvalidationData::InitialInvalidationTimestamp;
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
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
323}
324
325void
326Vdf_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
334void
335Vdf_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
343PXR_NAMESPACE_CLOSE_SCOPE
344
345#endif
A vector-like container for executor data used by the VdfDataManagerVector.
size_t GetSize() const
Returns the size of the container.
size_t GetNumData() const
Returns the number of outputs that have data associated with them.
VDF_API void Resize(const VdfNetwork &network)
Resize the vector to be able to accommodate all outputs in the given network.
VdfExecutorBufferData * GetBufferData(const DataHandle handle)
Returns the VdfExecutorBufferData associated with the given handle.
VdfInvalidationTimestamp GetInvalidationTimestamp(const DataHandle handle) const
Returns the VdfInvalidationTimestamp associated with the given handle.
VdfExecutorInvalidationData * GetInvalidationData(const DataHandle handle)
Returns the VdfExecutorInvalidationData associated with the given handle.
VdfSMBLData * GetOrCreateSMBLData(const DataHandle handle)
Returns an existing VdfSMBLData associated with the given handle or creates a new one of none exists.
size_t DataHandle
The data handle type is an index into the internal data vectors.
static const size_t InvalidHandle
This sentinel index denotes an invalid handle.
VdfSMBLData * GetSMBLData(const DataHandle handle) const
Returns an existing VdfSMBLData associated with the given handle.
void Touch(const DataHandle handle)
Marks the data at the given handle as having been touched by evaluation.
VDF_API ~Vdf_ExecutorDataVector()
Destructor.
void Reset(const DataHandle handle, const VdfId outputId)
Resets the output data at the given data handle to a newly constructed state.
bool Untouch(const DataHandle handle)
Marks the data at the given handle as not having been touched by evaluation.
DataHandle GetDataHandle(const VdfId outputId) const
Returns an existing data handle for the given output.
void SetInvalidationTimestamp(const DataHandle &handle, VdfInvalidationTimestamp ts)
Sets the invalidation timestamp for the give data handle.
VDF_API void Clear()
Clears all the data in the container.
bool IsTouched(const DataHandle handle)
Returns true if the data at the given handle has been touched by evaluation.
DataHandle GetOrCreateDataHandle(const VdfId outputId)
Returns an existing data handle, or creates a new one for the given output.
This object is responsible for storing the executor buffer data, comprised of the executor cache vect...
A VdfNetwork is a collection of VdfNodes and their connections.
Definition: network.h:60
static VdfIndex GetIndexFromId(const VdfId id)
Get the output index from the output id.
Definition: output.h:109
VdfSMBLData holds per-output data that is meant to be consumed by the executor.
Definition: smblData.h:31
uint64_t VdfId
The unique identifier type for Vdf objects.
Definition: types.h:107
unsigned int VdfInvalidationTimestamp
Type of the timestamp that identifies the most recent round of invalidation.
Definition: types.h:74
uint32_t VdfIndex
The index type for Vdf objects.
Definition: types.h:110
#define TF_DEV_AXIOM(cond)
The same as TF_AXIOM, but compiled only in dev builds.
Definition: diagnostic.h:205