executorDataManager.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_MANAGER_H
8 #define PXR_EXEC_VDF_EXECUTOR_DATA_MANAGER_H
9 
11 
12 #include "pxr/pxr.h"
13 
18 #include "pxr/exec/vdf/node.h"
19 #include "pxr/exec/vdf/output.h"
21 #include "pxr/exec/vdf/types.h"
22 
23 PXR_NAMESPACE_OPEN_SCOPE
24 
25 class VdfMask;
26 class VdfNetwork;
27 class VdfVector;
28 
32 template<typename DerivedClass> struct Vdf_ExecutorDataManagerTraits;
33 
45 
46 template<typename DerivedClass>
49  DerivedClass,
50  typename Vdf_ExecutorDataManagerTraits<DerivedClass>::DataHandle>
51 {
52 public:
53 
56  typedef
59 
62  typedef
65 
68 
74  const VdfVector *GetInputValue(
75  const VdfConnection &connection,
76  const VdfMask &mask) const;
77 
84  const DataHandle handle,
85  const VdfMask &mask) const;
86 
90  const VdfOutput &output,
91  const DataHandle handle) const;
92 
99  void SetOutputValue(
100  const VdfOutput &output,
101  const VdfVector &value,
102  const VdfMask &mask);
103 
108  bool TakeOutputValue(
109  const VdfOutput &output,
110  VdfVector *value,
111  const VdfMask &mask);
112 
117  const VdfVector *sourceValue,
118  const VdfId destOutputId) const;
119 
123  const VdfOutput &output,
124  VdfExecutorBufferData *bufferData) const;
125 
129  void DuplicateOutputData(
130  const VdfOutput &sourceOutput,
131  const VdfOutput &destOutput);
132 
137  VdfExecutorBufferData *bufferData,
138  const VdfMask &mask) {
139  bufferData->SetExecutorCacheMask(mask);
140  }
141 
143 
144 
147 
151  bool IsOutputInvalid(
152  const VdfId outputId,
153  const VdfMask &invalidationMask) const;
154 
160  bool InvalidateOutput(const VdfOutput &output,
161  const VdfMask &invalidationMask);
162 
166  void Touch(const VdfOutput &output) const;
167 
171  _invalidationTimestamp = timestamp;
172  }
173 
177  return _invalidationTimestamp;
178  }
179 
184  const DataHandle sourceHandle,
185  const DataHandle destHandle) const;
186 
188 
189 
192 
200  const VdfOutput &fromOutput,
201  VdfExecutorBufferData *fromBuffer,
202  const VdfOutput &toOutput,
203  VdfExecutorBufferData *toBuffer,
204  const VdfMask &keepMask);
205 
207 
208 protected:
209 
216  _invalidationTimestamp(
217  VdfExecutorInvalidationData::InitialInvalidationTimestamp + 1)
218  {}
219 
222  ~VdfExecutorDataManager() = default;
223 
224 private:
225 
226  // The current invalidation timestamp, recording the timestamp that was
227  // applied to the last (if any) round of outputs traversed during
228  // invalidation. This record and the timestamps on individual ExecutorData
229  // objects are the keys to activating mung buffer locking.
230  VdfInvalidationTimestamp _invalidationTimestamp;
231 };
232 
234 
235 template<typename DerivedClass>
236 const VdfVector *
238  const VdfConnection &connection,
239  const VdfMask &mask) const
240 {
241  // For associated inputs, we need to grab the input value from the
242  // associated output. This is because read/write buffers have been
243  // prepared before the node callback is invoked.
244  // Values for read outputs originate from the source output on the
245  // input connection.
246 
247  const VdfInput &input = connection.GetTargetInput();
248  const VdfOutput *ao = input.GetAssociatedOutput();
249  const VdfOutput &readOutput = ao && input.GetNumConnections() == 1
250  ? *ao
251  : connection.GetSourceOutput();
252  return GetOutputValueForReading(
253  Base::_GetDataHandle(readOutput.GetId()), mask);
254 }
255 
256 template<typename DerivedClass>
257 VdfVector *
259  const DataHandle handle,
260  const VdfMask &mask) const
261 {
262  // We have the output value if
263  // o The output data exists
264  // o The cache is not empty
265  // o One of the following is true:
266  // o The request mask has no bits set because we ask for an
267  // attribute with shape of length zero (e.g., points attribute
268  // with zero points in it).
269  // or
270  // o The output is not dirty and
271  // o The computed mask covers what is requested.
272 
273  const bool hasValue =
274  Base::_IsValidDataHandle(handle) &&
275  Base::_GetBufferData(handle)->GetExecutorCache() &&
276  (mask.IsAllZeros() ||
277  (Base::_GetBufferData(handle)
278  ->GetExecutorCacheMask().IsAnySet() &&
279  Base::_GetBufferData(handle)
280  ->GetExecutorCacheMask().Contains(mask)));
281 
282  return hasValue
283  ? Base::_GetBufferData(handle)->GetExecutorCache()
284  : NULL;
285 }
286 
287 template<typename DerivedClass>
288 VdfVector *
290  const VdfOutput &output,
291  const DataHandle handle) const
292 {
293  // If the specified handle is not valid, return a null pointer.
294  if (!Base::_IsValidDataHandle(handle)) {
295  return nullptr;
296  }
297 
298  // Return the output value, if available.
299  VdfExecutorBufferData *bufferData = Base::_GetBufferData(handle);
300  if (VdfVector *value = bufferData->GetExecutorCache()) {
301  return value;
302  }
303 
304  // Create a new output value, if there isn't one already available.
305  return CreateOutputCache(output, bufferData);
306 }
307 
308 template<typename DerivedClass>
309 void
311  const VdfOutput &output,
312  const VdfVector &value,
313  const VdfMask &mask)
314 {
315  // Make sure the data manager is appropriately sized.
316  Base::_Resize(output.GetNode().GetNetwork());
317 
318  // Retrieve the vector at the output
319  const DataHandle handle = Base::_GetOrCreateDataHandle(output.GetId());
320  VdfExecutorBufferData *bufferData = Base::_GetBufferData(handle);
321 
322  // If there is no output value available, create a new one
323  VdfVector *outputValue = bufferData->GetExecutorCache();
324  if (!outputValue) {
325  // This also touches the new output data
326  outputValue = CreateOutputCache(output, bufferData);
327  }
328 
329  // Mark the output as having been touched by evaluation, in order
330  // for it to be considered by invalidation.
331  Base::_Touch(handle);
332 
333  // Merge with existing data or replace?
334  const VdfMask &cacheMask = bufferData->GetExecutorCacheMask();
335  const bool mergeData =
336  !outputValue->IsEmpty() &&
337  !cacheMask.IsEmpty() &&
338  cacheMask != mask;
339 
340  // Set the new output value, by either merging into the existing vector,
341  // or simply replacing it all together
342  if (mergeData) {
343  outputValue->Merge(value, mask);
344  } else {
345  outputValue->Copy(value, mask);
346  }
347 
348  // Set the new executor cache mask
349  SetComputedOutputMask(bufferData, mergeData ? cacheMask | mask : mask);
350 }
351 
352 template<typename DerivedClass>
353 bool
355  const VdfOutput &output,
356  VdfVector *value,
357  const VdfMask &mask)
358 {
359  // Make sure the data manager is appropriately sized.
360  Base::_Resize(output.GetNode().GetNetwork());
361 
362  // Retrieve the vector at the output
363  const DataHandle handle = Base::_GetOrCreateDataHandle(output.GetId());
364  VdfExecutorBufferData *bufferData = Base::_GetBufferData(handle);
365 
366  // Return if there is already a cache associated with the output.
367  if (bufferData->GetExecutorCache()) {
368  return false;
369  }
370 
371  // Otherwise, transfer ownership of the value into the buffer.
372  bufferData->TakeOwnership(value);
373  bufferData->SetExecutorCacheMask(mask);
374 
375  // Mark the output as having been touched by evaluation, in order
376  // for it to be considered by invalidation.
377  Base::_Touch(handle);
378 
379  // Successfully transferred the value.
380  return true;
381 }
382 
383 template<typename DerivedClass>
384 void
386  const VdfVector *sourceValue,
387  const VdfId destOutputId) const
388 {
389  const DataHandle handle = Base::_GetDataHandle(destOutputId);
390  TF_DEV_AXIOM(Base::_IsValidDataHandle(handle));
391 
392  VdfExecutorBufferData *bufferData = Base::_GetBufferData(handle);
393 
394  // XXX:cleanup This const_cast is real trouble.
395  bufferData->YieldOwnership(const_cast<VdfVector *>(sourceValue));
396 }
397 
398 template<typename DerivedClass>
399 VdfVector *
401  const VdfOutput &output,
402  VdfExecutorBufferData *bufferData) const
403 {
404  // If the executor is providing its own cache-reuse mechanism, then
405  // we assert that the cache is NULL before we get here. Otherwise,
406  // we will try to reuse whatever cache is there already.
407  TF_DEV_AXIOM(!bufferData->GetExecutorCache());
408 
409  // This storage is freed when bufferData is destructed.
410  return bufferData->CreateExecutorCache(output.GetSpec());
411 }
412 
413 template<typename DerivedClass>
414 void
416  const VdfOutput &sourceOutput,
417  const VdfOutput &destOutput)
418 {
419  // If the source output data exists, clone it to the destination
420  // output data.
421  const DataHandle sourceHandle = Base::_GetDataHandle(sourceOutput.GetId());
422  if (!Base::_IsValidDataHandle(sourceHandle)) {
423  return;
424  }
425 
426  // Make sure the data manager is appropriately sized for us to copy
427  // the source value to the destination output.
428  Base::_Resize(destOutput.GetNode().GetNetwork());
429 
430  // Get the destination data handle.
431  const DataHandle destHandle =
432  Base::_GetOrCreateDataHandle(destOutput.GetId());
433 
434  // Clone the buffer data.
435  Base::_GetBufferData(sourceHandle)->Clone(Base::_GetBufferData(destHandle));
436 
437  // Clone the invalidation data.
438  Base::_GetInvalidationData(sourceHandle)->Clone(
439  Base::_GetInvalidationData(destHandle));
440 
441  // Copy the invalidation timestamp.
442  Base::_SetInvalidationTimestamp(
443  destHandle, Base::_GetInvalidationTimestamp(sourceHandle));
444 
445  // Untouch the destination data, unless the source data has been touched.
446  Base::_Untouch(destHandle);
447  if (Base::_Untouch(sourceHandle)) {
448  Base::_Touch(sourceHandle);
449  Base::_Touch(destHandle);
450  }
451 
452  // Clear the SMBL data, if any.
453  if (VdfSMBLData *smblData = Base::_GetSMBLData(destHandle)) {
454  smblData->Clear();
455  }
456 }
457 
458 template<typename DerivedClass>
459 bool
461  const VdfId outputId,
462  const VdfMask &invalidationMask) const
463 {
464  // If there is no data handle for the given output, it cannot possibly have
465  // been computed, therefore it is still invalid.
466  const DataHandle handle = Base::_GetDataHandle(outputId);
467  if (!Base::_IsValidDataHandle(handle)) {
468  return true;
469  }
470 
471  // If the output has been touched by evaluation, it is valid. If the output
472  // has not been touched, check if the given mask is marked as invalid in the
473  // invalidation data.
474  const bool wasTouched = Base::_IsTouched(handle);
475  return
476  !wasTouched &&
477  Base::_GetInvalidationData(handle)->IsInvalid(
478  invalidationMask, wasTouched);
479 }
480 
481 template<typename DerivedClass>
482 bool
484  const VdfOutput &output,
485  const VdfMask &invalidationMask)
486 {
487  // Retrieve the data handle for the output.
488  const DataHandle handle = Base::_GetDataHandle(output.GetId());
489 
490  // Thou shalt not invalidate what has not been evaluated!
491  if (!Base::_IsValidDataHandle(handle)) {
492  return false;
493  }
494 
495  // Invalidate the output via the VdfExecutorInvalidationData. Make sure
496  // to also untouch the output, if it has previously been touched by
497  // evaluation.
498  const bool didInvalidate = Base::_GetInvalidationData(handle)->Invalidate(
499  invalidationMask, Base::_Untouch(handle));
500 
501  // If the output had previously been touched, and it has now been
502  // invalidated. Make sure to invalidate the VdfExecutorBufferData.
503  if (didInvalidate) {
504 
505  // Update the invalidation timestamp, by applying the timestamp from
506  // the data manager to the timestamp stored at the output.
507  Base::_SetInvalidationTimestamp(handle, GetInvalidationTimestamp());
508 
509  // Retrieve the buffer data stored at the output.
510  VdfExecutorBufferData *bufferData = Base::_GetBufferData(handle);
511 
512  // If this is an output in the pool, let's apply sparse invalidation.
513  if (Vdf_IsPoolOutput(output)) {
514 
515  // Sparsely invalidate the executor cache mask, using the bits in
516  // the invalidation mask. During a steady-state mung, the cache and
517  // invalidation masks will likely always be the same across
518  // iterations, so we memoize this operation.
519  //
520  // XXX: Don't always create SMBL data for this. Store the memoized
521  // result in VdfExecutorInvalidationData?
522  VdfMask newCacheMask =
523  Base::_GetOrCreateSMBLData(handle)->InvalidateCacheMask(
524  bufferData->GetExecutorCacheMask(),
525  invalidationMask);
526 
527  // If the new cache mask is now all-zeros, remove the cache
528  // entirely, otherwise simply set the new cache mask.
529  if (newCacheMask.IsAllZeros()) {
530  bufferData->ResetExecutorCache();
531  } else {
532  bufferData->SetExecutorCacheMask(newCacheMask);
533  }
534  }
535 
536  // Otherwise, we simply remove the cache entirely.
537  else {
538  bufferData->ResetExecutorCache();
539  }
540 
541  // We did some invalidation.
542  return true;
543  }
544 
545  // Nothing to invalidate.
546  return false;
547 }
548 
549 template<typename DerivedClass>
550 void
552 {
553  Base::_Touch(Base::_GetOrCreateDataHandle(output.GetId()));
554 }
555 
556 template<typename DerivedClass>
557 VdfVector *
559  const VdfOutput &fromOutput,
560  VdfExecutorBufferData *fromBuffer,
561  const VdfOutput &toOutput,
562  VdfExecutorBufferData *toBuffer,
563  const VdfMask &keepMask)
564 {
565  // If we don't have a cache here, there is nothing to pass.
566  // It is up to the user to handle this case correctly. We wouldn't
567  // normally expect to get into this case unless a speculating executor is
568  // trying to read from its parent executor.
569  if (!fromBuffer->GetExecutorCache()) {
570  return nullptr;
571  }
572 
573  // Swap the from- and toBuffers.
574  VdfVector *result = toBuffer->SwapExecutorCache(fromBuffer);
575 
576  if (keepMask.IsEmpty()) {
577 
578  // If we don't need to keep anything, then it's a straight pass through.
579  // Simply make sure that there is no cache stored at the fromBuffer.
580  fromBuffer->ResetExecutorCache();
581 
582  } else {
583  TfAutoMallocTag2 tag(
584  "Vdf", "VdfExecutorDataManager<DerivedClass>::PassBuffer (keep)");
585 
586  // Create a cache in the fromOutput. This is where we store the
587  // kept value.
588  VdfVector *keptValue = CreateOutputCache(fromOutput, fromBuffer);
589 
590  // Copy the subset that we want fromOutput to keep. Note, that it is
591  // okay to keep an empty buffer. That just means that the schedule has
592  // determined that a buffer must reside at this output, in order for
593  // all-zero mask cache lookups to return a valid vector.
594  if (keepMask.IsAnySet()) {
595  keptValue->Copy(*result, keepMask);
596  }
597 
598  // What's set in the keepMask is what remains cached at fromBuffer.
599  fromBuffer->SetExecutorCacheMask(keepMask);
600 
601  }
602 
603  // Return the value now stored at toOutput.
604  return result;
605 }
606 
607 template<typename DerivedClass>
608 bool
610  const DataHandle sourceHandle,
611  const DataHandle destHandle) const
612 {
613  // For this method to return true, indicating that the source output should
614  // be locked for mung buffer locking, the invalidation timestamp stored in
615  // the destination data object must match the current invalidation
616  // timestamp. Furthermore, the source data object must have an invalidation
617  // timestamp different from our current invalidation timestamp.
618  // Essentially, this means that in the latest round of invalidation, the
619  // destination output has been invalidated, whereas the source output
620  // remained untouched.
621  // If this method returns true, the cache at the source output can be
622  // mung buffer locked, because it won't receive invalidation, whereas any
623  // output below (and including) the destination output will!
624 
625  return
626  Base::_IsValidDataHandle(sourceHandle) &&
627  Base::_IsValidDataHandle(destHandle) &&
628  Base::_GetInvalidationTimestamp(destHandle) ==
629  _invalidationTimestamp &&
630  Base::_GetInvalidationTimestamp(sourceHandle) !=
631  _invalidationTimestamp;
632 }
633 
634 PXR_NAMESPACE_CLOSE_SCOPE
635 
636 #endif
#define TF_DEV_AXIOM(cond)
The same as TF_AXIOM, but compiled only in dev builds.
Definition: diagnostic.h:206
A VdfNetwork is a collection of VdfNodes and their connections.
Definition: network.h:59
A class that fully represents a connection between two VdfNodes.
Definition: connection.h:29
const VdfNetwork & GetNetwork() const
Returns the network to which this node belongs.
Definition: node.h:138
VdfId GetId() const
The unique id of this output.
Definition: output.h:100
bool TakeOutputValue(const VdfOutput &output, VdfVector *value, const VdfMask &mask)
Transfers ownership of value to the given output, returning true if the transfer of ownership succeed...
bool IsEmpty() const
Returns whether or not this vector is empty.
Definition: vector.h:153
Vdf_ExecutorDataManagerTraits< DerivedClass >::DataHandle DataHandle
The data handle type defined via the specialized traits class.
void DuplicateOutputData(const VdfOutput &sourceOutput, const VdfOutput &destOutput)
Duplicates the output data associated with sourceOutput and copies it to destOutput.
A VdfMask is placed on connections to specify the data flowing through them.
Definition: mask.h:36
void UpdateInvalidationTimestamp(VdfInvalidationTimestamp timestamp)
Increments the the current invalidation timestamp on this executor.
VDF_API void Merge(const VdfVector &rhs, const VdfMask::Bits &bits)
Merges the contents of rhs into this vector.
This class provides functionality to manage the executor specific data associated with each output in...
const VdfOutput * GetAssociatedOutput() const
Returns the output corresponding to this input.
Definition: input.h:82
This class is used to abstract away knowledge of the cache data used for each node.
Definition: vector.h:58
Vdf_ExecutorDataManagerInterface< DerivedClass, DataHandle > Base
The base class type.
const VdfMask & GetExecutorCacheMask() const
Get the available mask.
~VdfExecutorDataManager()=default
Prevent destruction via base class pointers (static polymorphism only).
A VdfInput is used to connect a VdfNode to one or more VdfNodes' outputs.
Definition: input.h:35
VDF_API const VdfOutputSpec & GetSpec() const
Returns the connector specification object for this output.
const VdfOutput & GetSourceOutput() const
Returns the output (ie. source) for this connection.
Definition: connection.h:63
const VdfInput & GetTargetInput() const
Returns the input connector (ie. target) for this connection.
Definition: connection.h:87
void ResetExecutorCache(const VdfMask &mask)
Reset the executor cache without releasing any memory and set the executor cache mask to mask.
void SetComputedOutputMask(VdfExecutorBufferData *bufferData, const VdfMask &mask)
Marks the output whose data is bufferData as computed for the entries in mask.
VdfVector * CreateOutputCache(const VdfOutput &output, VdfExecutorBufferData *bufferData) const
Creates a new cache for an output, given the output data object.
size_t GetNumConnections() const
Returns the number of connections for this input.
Definition: input.h:58
VdfVector * GetExecutorCache() const
Returns the executor cache stored at this buffer data instance.
bool Vdf_IsPoolOutput(const VdfOutput &output)
Returns true if output is a pool output, i.e., an output that has an associated input,...
VdfSMBLData holds per-output data that is meant to be consumed by the executor.
Definition: smblData.h:30
void Touch(const VdfOutput &output) const
Marks the data at the given output as having been touched by evaluation.
Scoped (i.e.
Definition: mallocTag.h:251
bool IsAllZeros() const
Returns true if this mask has all entries unset.
Definition: mask.h:206
This object is responsible for storing the executor buffer data, comprised of the executor cache vect...
bool IsOutputInvalid(const VdfId outputId, const VdfMask &invalidationMask) const
Returns true if the output is already invalid for the given invalidationMask.
void YieldOwnership()
Yields ownership of the internal vector, i.e.
VdfVector * PassBuffer(const VdfOutput &fromOutput, VdfExecutorBufferData *fromBuffer, const VdfOutput &toOutput, VdfExecutorBufferData *toBuffer, const VdfMask &keepMask)
This method is called to pass a buffer from fromOutput to toOutput.
bool IsAnySet() const
Returns true, if there is at least a single set entry.
Definition: mask.h:216
VdfVector * SwapExecutorCache(VdfExecutorBufferData *rhs)
Swaps the executor cache at this buffer, with that of another buffer.
void SetExecutorCacheMask(const VdfMask &mask)
Sets the available mask.
A VdfOutput represents an output on a node.
Definition: output.h:31
const VdfNode & GetNode() const
Returns the owning node for this output.
Definition: output.h:57
const VdfVector * GetInputValue(const VdfConnection &connection, const VdfMask &mask) const
Returns the input value flowing across the given connection with the given mask.
VdfVector * CreateExecutorCache(const VdfOutputSpec &spec)
Creates a new executor cache for this buffer.
The interface contract for the static polymorphism used by executor data manager implementations.
VdfExecutorDataManager()
Constructor.
unsigned int VdfInvalidationTimestamp
Type of the timestamp that identifies the most recent round of invalidation.
Definition: types.h:74
void Copy(const VdfVector &rhs, const VdfMask &mask)
Copies the contents of rhs into this vector.
Definition: vector.h:284
VdfInvalidationTimestamp GetInvalidationTimestamp() const
Returns the current invalidation timestamp on this executor.
bool HasInvalidationTimestampMismatch(const DataHandle sourceHandle, const DataHandle destHandle) const
Returns true, if the invalidation timestamps between sourceData and destData do not match,...
VdfVector * GetOrCreateOutputValueForWriting(const VdfOutput &output, const DataHandle handle) const
Returns a new or existing output value for writing data into.
void SetOutputValue(const VdfOutput &output, const VdfVector &value, const VdfMask &mask)
Sets the cached value for a given output, creating the output cache if necessary.
bool IsEmpty() const
Returns true if this mask is empty, i.e.
Definition: mask.h:168
VdfVector * GetOutputValueForReading(const DataHandle handle, const VdfMask &mask) const
Returns the cached value for a given output and mask.
void SetReferenceOutputValue(const VdfVector *sourceValue, const VdfId destOutputId) const
Called to set destOutput's buffer output to be a reference to the sourceValue.
bool InvalidateOutput(const VdfOutput &output, const VdfMask &invalidationMask)
Marks output as invalid.
Forward definition of the traits class, which will be specialized by the derived data manager impleme...
uint64_t VdfId
The unique identifier type for Vdf objects.
Definition: types.h:107