Loading...
Searching...
No Matches
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
23PXR_NAMESPACE_OPEN_SCOPE
24
25class VdfMask;
26class VdfNetwork;
27class VdfVector;
28
32template<typename DerivedClass> struct Vdf_ExecutorDataManagerTraits;
33
45
46template<typename DerivedClass>
49 DerivedClass,
50 typename Vdf_ExecutorDataManagerTraits<DerivedClass>::DataHandle>
51{
52public:
53
56 typedef
59
62 typedef
65
68
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
100 const VdfOutput &output,
101 const VdfVector &value,
102 const VdfMask &mask);
103
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
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
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
208protected:
209
216 _invalidationTimestamp(
217 VdfExecutorInvalidationData::InitialInvalidationTimestamp + 1)
218 {}
219
223
224private:
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
235template<typename DerivedClass>
236const 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
256template<typename DerivedClass>
257VdfVector *
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
287template<typename DerivedClass>
288VdfVector *
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
308template<typename DerivedClass>
309void
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
352template<typename DerivedClass>
353bool
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
383template<typename DerivedClass>
384void
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
398template<typename DerivedClass>
399VdfVector *
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
413template<typename DerivedClass>
414void
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
458template<typename DerivedClass>
459bool
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
481template<typename DerivedClass>
482bool
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
549template<typename DerivedClass>
550void
552{
553 Base::_Touch(Base::_GetOrCreateDataHandle(output.GetId()));
554}
555
556template<typename DerivedClass>
557VdfVector *
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 {
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
607template<typename DerivedClass>
608bool
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
634PXR_NAMESPACE_CLOSE_SCOPE
635
636#endif
Scoped (i.e.
Definition: mallocTag.h:249
The interface contract for the static polymorphism used by executor data manager implementations.
A class that fully represents a connection between two VdfNodes.
Definition: connection.h:30
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
This object is responsible for storing the executor buffer data, comprised of the executor cache vect...
void ResetExecutorCache(const VdfMask &mask)
Reset the executor cache without releasing any memory and set the executor cache mask to mask.
VdfVector * SwapExecutorCache(VdfExecutorBufferData *rhs)
Swaps the executor cache at this buffer, with that of another buffer.
const VdfMask & GetExecutorCacheMask() const
Get the available mask.
VdfVector * GetExecutorCache() const
Returns the executor cache stored at this buffer data instance.
void TakeOwnership(VdfVector *v)
Assumes ownership of the given vector.
VdfVector * CreateExecutorCache(const VdfOutputSpec &spec)
Creates a new executor cache for this buffer.
void SetExecutorCacheMask(const VdfMask &mask)
Sets the available mask.
void YieldOwnership()
Yields ownership of the internal vector, i.e.
This class provides functionality to manage the executor specific data associated with each output in...
VdfInvalidationTimestamp GetInvalidationTimestamp() const
Returns the current invalidation timestamp on this executor.
bool IsOutputInvalid(const VdfId outputId, const VdfMask &invalidationMask) const
Returns true if the output is already invalid for the given invalidationMask.
Vdf_ExecutorDataManagerInterface< DerivedClass, DataHandle > Base
The base class type.
VdfVector * GetOutputValueForReading(const DataHandle handle, const VdfMask &mask) const
Returns the cached value for a given output and mask.
~VdfExecutorDataManager()=default
Prevent destruction via base class pointers (static polymorphism only).
bool HasInvalidationTimestampMismatch(const DataHandle sourceHandle, const DataHandle destHandle) const
Returns true, if the invalidation timestamps between sourceData and destData do not match,...
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.
const VdfVector * GetInputValue(const VdfConnection &connection, const VdfMask &mask) const
Returns the input value flowing across the given connection with the given mask.
void SetComputedOutputMask(VdfExecutorBufferData *bufferData, const VdfMask &mask)
Marks the output whose data is bufferData as computed for the entries in mask.
VdfExecutorDataManager()
Constructor.
void Touch(const VdfOutput &output) const
Marks the data at the given output as having been touched by evaluation.
void UpdateInvalidationTimestamp(VdfInvalidationTimestamp timestamp)
Increments the the current invalidation timestamp on this executor.
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.
VdfVector * GetOrCreateOutputValueForWriting(const VdfOutput &output, const DataHandle handle) const
Returns a new or existing output value for writing data into.
void DuplicateOutputData(const VdfOutput &sourceOutput, const VdfOutput &destOutput)
Duplicates the output data associated with sourceOutput and copies it to destOutput.
void SetReferenceOutputValue(const VdfVector *sourceValue, const VdfId destOutputId) const
Called to set destOutput's buffer output to be a reference to the sourceValue.
Vdf_ExecutorDataManagerTraits< DerivedClass >::DataHandle DataHandle
The data handle type defined via the specialized traits class.
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...
VdfVector * CreateOutputCache(const VdfOutput &output, VdfExecutorBufferData *bufferData) const
Creates a new cache for an output, given the output data object.
bool InvalidateOutput(const VdfOutput &output, const VdfMask &invalidationMask)
Marks output as invalid.
A VdfInput is used to connect a VdfNode to one or more VdfNodes' outputs.
Definition: input.h:36
size_t GetNumConnections() const
Returns the number of connections for this input.
Definition: input.h:58
const VdfOutput * GetAssociatedOutput() const
Returns the output corresponding to this input.
Definition: input.h:82
A VdfMask is placed on connections to specify the data flowing through them.
Definition: mask.h:37
bool IsAnySet() const
Returns true, if there is at least a single set entry.
Definition: mask.h:216
bool IsAllZeros() const
Returns true if this mask has all entries unset.
Definition: mask.h:206
bool IsEmpty() const
Returns true if this mask is empty, i.e.
Definition: mask.h:168
A VdfNetwork is a collection of VdfNodes and their connections.
Definition: network.h:60
const VdfNetwork & GetNetwork() const
Returns the network to which this node belongs.
Definition: node.h:138
A VdfOutput represents an output on a node.
Definition: output.h:32
const VdfNode & GetNode() const
Returns the owning node for this output.
Definition: output.h:57
VdfId GetId() const
The unique id of this output.
Definition: output.h:100
VDF_API const VdfOutputSpec & GetSpec() const
Returns the connector specification object for this output.
VdfSMBLData holds per-output data that is meant to be consumed by the executor.
Definition: smblData.h:31
This class is used to abstract away knowledge of the cache data used for each node.
Definition: vector.h:56
bool IsEmpty() const
Returns whether or not this vector is empty.
Definition: vector.h:150
void Copy(const VdfVector &rhs, const VdfMask &mask)
Copies the contents of rhs into this vector.
Definition: vector.h:274
VDF_API void Merge(const VdfVector &rhs, const VdfMask::Bits &bits)
Merges the contents of rhs into this vector.
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
#define TF_DEV_AXIOM(cond)
The same as TF_AXIOM, but compiled only in dev builds.
Definition: diagnostic.h:205
bool Vdf_IsPoolOutput(const VdfOutput &output)
Returns true if output is a pool output, i.e., an output that has an associated input,...
Forward definition of the traits class, which will be specialized by the derived data manager impleme...