Loading...
Searching...
No Matches
parallelExecutorDataManager.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_PARALLEL_EXECUTOR_DATA_MANAGER_H
8#define PXR_EXEC_VDF_PARALLEL_EXECUTOR_DATA_MANAGER_H
9
11
12#include "pxr/pxr.h"
13
17#include "pxr/exec/vdf/output.h"
18#include "pxr/exec/vdf/parallelExecutorDataManagerInterface.h"
20#include "pxr/exec/vdf/types.h"
21
22PXR_NAMESPACE_OPEN_SCOPE
23
24class VdfMask;
25class VdfVector;
26
30template<typename DerivedClass> struct Vdf_ParallelExecutorDataManagerTraits;
31
41template < typename DerivedClass >
44 DerivedClass,
45 typename Vdf_ParallelExecutorDataManagerTraits<DerivedClass>::DataHandle
46 >
47{
48public:
49
52 typedef
55
58 typedef
61
68 const VdfConnection &connection,
69 const VdfMask &mask) const;
70
77 const DataHandle dataHandle,
78 const VdfMask &mask) const;
79
83 const VdfOutput &output,
84 const DataHandle dataHandle) const;
85
100 const VdfOutput &output,
101 const VdfVector &value,
102 const VdfMask &mask);
103
114 const VdfOutput &output,
115 VdfVector *value,
116 const VdfMask &mask);
117
122 const VdfVector *sourceValue,
123 const VdfId destOutputId) const;
124
128 const VdfOutput &output,
129 VdfExecutorBufferData *bufferData);
130
136 const VdfOutput &output,
137 VdfExecutorBufferData *bufferData,
138 const VdfMask::Bits &bits);
139
146 const VdfOutput &sourceOutput,
147 const VdfOutput &destOutput);
148
153 const VdfId outputId,
154 const VdfMask &invalidationMask) const;
155
164 const VdfOutput &output,
165 const VdfMask &invalidationMask);
166
170 void Touch(const VdfOutput &output) const;
171
177 const VdfInvalidationTimestamp &timestamp) {
178 _invalidationTimestamp = timestamp;
179 }
180
184 return _invalidationTimestamp;
185 }
186
192 const DataHandle &sourceHandle,
193 const DataHandle &destHandle) const;
194
195protected:
196
200 _invalidationTimestamp(
201 VdfExecutorInvalidationData::InitialInvalidationTimestamp + 1)
202 { }
203
207
208private:
209
210 // Returns an output value for reading.
211 //
212 VdfVector *_GetOutputValueForReading(
213 VdfExecutorBufferData *bufferData,
214 const VdfMask &mask) const;
215
216 // The current invalidation timestamp, recording the timestamp that was
217 // applied to the last (if any) round of outputs traversed during
218 // invalidation. This record and the timestamps on individual ExecutorData
219 // objects are the keys to activating mung buffer locking.
220 //
221 VdfInvalidationTimestamp _invalidationTimestamp;
222};
223
225
226template<typename DerivedClass>
227VdfVector *
229 const VdfConnection &connection,
230 const VdfMask &mask) const
231{
232 // For associated inputs, we need to grab the input value from the
233 // associated output. This is because read/write buffers have been
234 // prepared before the node callback is invoked.
235 // Values for read outputs originate from the source output on the
236 // input connection.
237
238 const VdfInput &input = connection.GetTargetInput();
239 const VdfOutput *ao = input.GetAssociatedOutput();
240
241 if (!ao || input.GetNumConnections() != 1) {
242 return GetOutputValueForReading(
243 Base::_GetDataHandle(connection.GetSourceOutput().GetId()), mask);
244 }
245
246 // Note that read/write output values are always passed via the private
247 // buffers. The only time read/writes are published is when they reach
248 // an output that no longer passes the value, or when a kept buffer is
249 // being published. Therefore, we always read output values for read/writes
250 // out of the private buffer, if requested as an input value.
251
252 const DataHandle dataHandle = Base::_GetDataHandle(ao->GetId());
253 return Base::_IsValidDataHandle(dataHandle)
254 ? _GetOutputValueForReading(
255 Base::_GetPrivateBufferData(dataHandle), mask)
256 : nullptr;
257}
258
259template<typename DerivedClass>
260VdfVector *
262 const DataHandle dataHandle,
263 const VdfMask &mask) const
264{
265 // Output values are always read from public buffers. When nodes are
266 // evaluated, they mutate the private buffers, which are then published
267 // as soon as the node completes. Note, however, that read/write buffers
268 // will never be published until they reach on output that no longer passes
269 // the value.
270
271 if (!Base::_IsValidDataHandle(dataHandle)) {
272 return nullptr;
273 }
274
275 // Attempt to read from the public buffer, first.
276 VdfExecutorBufferData *publicData = Base::_GetPublicBufferData(dataHandle);
277 if (VdfVector *value = _GetOutputValueForReading(publicData, mask)) {
278 return value;
279 }
280
281 // Then, fall back to reading from the transferred data, if available.
282 VdfExecutorBufferData *transferData =
283 Base::_GetTransferredBufferData(dataHandle);
284 return transferData
285 ? _GetOutputValueForReading(transferData, mask)
286 : nullptr;
287}
288
289template<typename DerivedClass>
290VdfVector *
292 const VdfOutput &output,
293 const DataHandle handle) const
294{
295 // If the handle is not valid, we can't return a value for writing.
296 if (!Base::_IsValidDataHandle(handle)) {
297 return nullptr;
298 }
299
300 // Note that output values are always written to private buffers. These
301 // private buffers will then be published once the node has completed
302 // evaluation.
303
304 VdfExecutorBufferData *bufferData = Base::_GetPrivateBufferData(handle);
305 if (VdfVector *value = bufferData->GetExecutorCache()) {
306 return value;
307 }
308
309 // Create a new output value, if there isn't one already available.
310 return CreateOutputCache(output, bufferData);
311}
312
313template < typename DerivedClass >
314void
316 const VdfOutput &output,
317 const VdfVector &value,
318 const VdfMask &mask)
319{
320 // Make sure the data manager is appropriately sized.
321 Base::_Resize(output.GetNode().GetNetwork());
322
323 // Mark the output as having been touched by evaluation, in order
324 // for it to be considered by invalidation.
325 const VdfId outputId = output.GetId();
326 Base::_Touch(outputId);
327
328 // Retrieve the data at the output
329 const DataHandle handle = Base::_GetOrCreateDataHandle(outputId);
330 VdfExecutorBufferData *privateBuffer = Base::_GetPrivateBufferData(handle);
331 VdfExecutorBufferData *publicBuffer = Base::_GetPublicBufferData(handle);
332
333 // Merge with existing data or replace?
334 const VdfMask &publicMask = publicBuffer->GetExecutorCacheMask();
335 const VdfVector *publicValue = publicBuffer->GetExecutorCache();
336 const bool mergeData =
337 publicValue &&
338 !publicMask.IsEmpty() &&
339 publicMask != mask;
340
341 // Set the new output value, merging in the previously available public
342 // data, if any.
343 if (mergeData) {
344 const VdfMask privateMask = publicMask | mask;
345 VdfVector *outputValue = privateBuffer->CreateExecutorCache(
346 output.GetSpec(), privateMask.GetBits());
347 outputValue->Merge(*publicValue, publicMask - mask);
348 outputValue->Merge(value, mask);
349 privateBuffer->SetExecutorCacheMask(privateMask);
350 } else {
351 VdfVector *outputValue =
352 privateBuffer->CreateExecutorCache(output.GetSpec());
353 outputValue->Copy(value, mask);
354 privateBuffer->SetExecutorCacheMask(mask);
355 }
356
357 // We just set the private buffer data, now let's publish it. Note, that
358 // we can no longer modify the private or public buffer data after this
359 // step, since it could lead to race conditions.
360 Base::_PublishPrivateBufferData(handle);
361}
362
363template < typename DerivedClass >
364bool
366 const VdfOutput &output,
367 VdfVector *value,
368 const VdfMask &mask)
369{
370 // Make sure the data manager is appropriately sized.
371 Base::_Resize(output.GetNode().GetNetwork());
372
373 // Mark the output as having been touched by evaluation, in order
374 // for it to be considered by invalidation.
375 const VdfId outputId = output.GetId();
376 Base::_Touch(outputId);
377
378 // Retrieve the data at the output
379 const DataHandle handle = Base::_GetOrCreateDataHandle(outputId);
380
381 // Attempt to transfer the value.
382 return Base::_TransferBufferData(handle, value, mask);
383}
384
385template < typename DerivedClass >
386void
388 const VdfVector *sourceValue,
389 const VdfId destOutputId) const
390{
391 const DataHandle handle = Base::_GetDataHandle(destOutputId);
392 TF_DEV_AXIOM(Base::_IsValidDataHandle(handle));
393
394 VdfExecutorBufferData *bufferData = Base::_GetPrivateBufferData(handle);
395
396 // XXX:cleanup This const_cast is real trouble.
397 bufferData->YieldOwnership(const_cast<VdfVector *>(sourceValue));
398}
399
400template < typename DerivedClass >
401VdfVector *
403 const VdfOutput &output,
404 VdfExecutorBufferData *bufferData)
405{
407 "Vdf", "VdfParallelExecutorDataManager::CreateOutputCache");
408
409 // If the executor is providing its own cache-reuse mechanism, then
410 // we assert that the cache is NULL before we get here. Otherwise,
411 // we will try to reuse whatever cache is there already.
412 TF_DEV_AXIOM(!bufferData->GetExecutorCache());
413
414 return bufferData->CreateExecutorCache(output.GetSpec());
415}
416
417template < typename DerivedClass >
418VdfVector *
420 const VdfOutput &output,
421 VdfExecutorBufferData *bufferData,
422 const VdfMask::Bits &bits)
423{
425 "Vdf", "VdfParallelExecutorDataManager::CreateOutputCache");
426
427 // If the executor is providing its own cache-reuse mechanism, then
428 // we assert that the cache is NULL before we get here. Otherwise,
429 // we will try to reuse whatever cache is there already.
430 TF_DEV_AXIOM(!bufferData->GetExecutorCache());
431
432 return bufferData->CreateExecutorCache(output.GetSpec(), bits);
433}
434
435template < typename DerivedClass >
436void
438 const VdfOutput &sourceOutput,
439 const VdfOutput &destOutput)
440{
441 // Make sure the data manager is appropriately sized for us to copy
442 // the source value to the destination output.
443 Base::_Resize(destOutput.GetNode().GetNetwork());
444
445 // Untouch the destination data, unless the source data has been touched.
446 // This needs to happen even if we don't get a sourceHandle below.
447 const VdfId destOutputId = destOutput.GetId();
448 const VdfId sourceOutputId = sourceOutput.GetId();
449 Base::_Untouch(destOutputId);
450 if (Base::_Untouch(sourceOutputId)) {
451 Base::_Touch(sourceOutputId);
452 Base::_Touch(destOutputId);
453 }
454
455 // If the source output data exists, clone it to the destination
456 // output data.
457 const DataHandle sourceHandle = Base::_GetDataHandle(sourceOutputId);
458 if (!Base::_IsValidDataHandle(sourceHandle)) {
459 return;
460 }
461
462 // Get the destination data handle.
463 const DataHandle destHandle = Base::_GetOrCreateDataHandle(destOutputId);
464
465 // Clone the buffer data.
466 Base::_GetPublicBufferData(sourceHandle)->Clone(
467 Base::_GetPublicBufferData(destHandle));
468
469 // Clone the invalidation data.
470 Base::_GetInvalidationData(sourceHandle)->Clone(
471 Base::_GetInvalidationData(destHandle));
472
473 // Copy the invalidation timestamp.
474 Base::_SetInvalidationTimestamp(
475 destHandle, Base::_GetInvalidationTimestamp(sourceHandle));
476}
477
478template < typename DerivedClass >
479bool
481 const VdfId outputId,
482 const VdfMask &invalidationMask) const
483{
484 // If the output has been touched by evaluation, it is valid.
485 const bool wasTouched = Base::_IsTouched(outputId);
486 if (wasTouched) {
487 return false;
488 }
489
490 // If there is no data handle for the output, the output has never been
491 // evaluated and therefore is still invalid.
492 const DataHandle handle = Base::_GetDataHandle(outputId);
493 if (!Base::_IsValidDataHandle(handle)) {
494 return true;
495 }
496
497 // Let's check if the invalidation data is marked invalid for the given
498 // mask.
499 return Base::_GetInvalidationData(handle)->IsInvalid(
500 invalidationMask, wasTouched);
501}
502
503template < typename DerivedClass >
504bool
506 const VdfOutput &output,
507 const VdfMask &invalidationMask)
508{
509 // Untouch the output. If it has been touched, we need to creata a data
510 // handle for it, even though it may have never been evaluated with this
511 // data manager.
512 const VdfId outputId = output.GetId();
513 const bool wasTouched = Base::_Untouch(outputId);
514
515 // Retrieve the data handle for the output.
516 const DataHandle handle = wasTouched
517 ? Base::_GetOrCreateDataHandle(outputId)
518 : Base::_GetDataHandle(outputId);
519
520 // Thou shalt not invalidate what has not been evaluated (unless it has
521 // been touched, of course)!
522 if (!Base::_IsValidDataHandle(handle)) {
523 return false;
524 }
525
526 // Invalidate the output via the VdfExecutorInvalidationData. Make sure
527 // to also untouch the output, if it has previously been touched by
528 // evaluation.
529 const bool didInvalidate = Base::_GetInvalidationData(handle)->Invalidate(
530 invalidationMask, wasTouched);
531
532 // If the output had previously been touched, and it has now been
533 // invalidated. Make sure to invalidate the VdfExecutorBufferData.
534 if (didInvalidate) {
535
536 // Update the invalidation timestamp, by applying the timestamp from
537 // the data manager to the timestamp stored at the output.
538 Base::_SetInvalidationTimestamp(handle, GetInvalidationTimestamp());
539
540 // Retrieve the buffer data stored at the output.
541 VdfExecutorBufferData *bufferData = Base::_GetPublicBufferData(handle);
542
543 // We only need to invalidate the executor cache if the buffer has one.
544 if (!bufferData->GetExecutorCacheMask().IsEmpty()) {
545
546 // If this is an output in the pool, let's apply sparse
547 // invalidation.
548 if (Vdf_IsPoolOutput(output)) {
549
550 // Sparsely invalidate the executor cache mask, using the bits
551 // in the invalidation mask. During a steady-state mung, the
552 // cache and invalidation masks will likely always be the same
553 // across iterations, so we memoize this operation.
554 //
555 // XXX: Memoize!
556 //
557 VdfMask newCacheMask =
558 bufferData->GetExecutorCacheMask() - invalidationMask;
559
560 // If the new cache mask is now all-zeros, remove the cache
561 // entirely, otherwise simply set the new cache mask.
562 if (newCacheMask.IsAllZeros()) {
563 bufferData->ResetExecutorCache();
564 } else {
565 bufferData->SetExecutorCacheMask(newCacheMask);
566 }
567 }
568
569 // Otherwise, we simply remove the cache entirely.
570 else {
571 bufferData->ResetExecutorCache();
572 }
573 }
574
575 // Also clear any transferred data.
576 Base::_ResetTransferredBufferData(handle);
577
578 // We did some invalidation.
579 return true;
580 }
581
582 // Nothing to invalidate.
583 return false;
584}
585
586template < typename DerivedClass >
587void
589 const VdfOutput &output) const
590{
591 Base::_Touch(output.GetId());
592}
593
594template < typename DerivedClass >
595bool
597 const DataHandle &sourceHandle,
598 const DataHandle &destHandle) const
599{
600 // For this method to return true, indicating that the source output should
601 // be locked for mung buffer locking, the invalidation timestamp stored in
602 // the destination data object must match the current invalidation
603 // timestamp. Furthermore, the source data object must have an invalidation
604 // timestamp different from our current invalidation timestamp.
605 // Essentially, this means that in the latest round of invalidation, the
606 // destination output has been invalidated, whereas the source output
607 // remained untouched.
608 // If this method returns true, the cache at the source output can be
609 // mung buffer locked, because it won't receive invalidation, whereas any
610 // output below (and including) the destination output will!
611
612 return
613 Base::_IsValidDataHandle(sourceHandle) &&
614 Base::_IsValidDataHandle(destHandle) &&
615 Base::_GetInvalidationTimestamp(sourceHandle) !=
616 _invalidationTimestamp &&
617 Base::_GetInvalidationTimestamp(destHandle) ==
618 _invalidationTimestamp;
619}
620
621template < typename DerivedClass >
622VdfVector *
624 VdfExecutorBufferData *bufferData,
625 const VdfMask &mask) const
626{
627 // We have the output value if
628 // o The output data exists
629 // o The cache is not empty
630 // o One of the following is true:
631 // o The request mask has no bits set because we ask for an
632 // attribute with shape of length zero (e.g., points attribute
633 // with zero points in it.
634 // or
635 // o The output is not dirty and
636 // o The computed mask covers what is requested.
637
638 VdfVector *value = bufferData->GetExecutorCache();
639 const bool hasValue =
640 value &&
641 (mask.IsAllZeros() ||
642 (bufferData->GetExecutorCacheMask().IsAnySet() &&
643 bufferData->GetExecutorCacheMask().Contains(mask)));
644
645 return hasValue ? value : nullptr;
646}
647
648PXR_NAMESPACE_CLOSE_SCOPE
649
650#endif
Fast, compressed bit array which is capable of performing logical operations without first decompress...
Scoped (i.e.
Definition: mallocTag.h:249
The interface contract for the static polymorphism used by parallel executor data manager implementat...
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.
const VdfMask & GetExecutorCacheMask() const
Get the available mask.
VdfVector * GetExecutorCache() const
Returns the executor cache stored at this buffer data instance.
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.
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
VdfMask::Bits const & GetBits() const
Get this mask's content as CtCompressedfBits.
Definition: mask.h:556
bool Contains(const VdfMask &mask) const
Returns true if mask is a subset-of or equal to this mask, false otherwise.
Definition: mask.h:186
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.
This class provides functionality to manage executor data stored as VdfExecutorData from multiple thr...
static VdfVector * CreateOutputCache(const VdfOutput &output, VdfExecutorBufferData *bufferData, const VdfMask::Bits &bits)
Creates a new cache for an output, given the output data object.
bool IsOutputInvalid(const VdfId outputId, const VdfMask &invalidationMask) const
Returns true if the output is already invalid for the given invalidationMask.
Vdf_ParallelExecutorDataManagerTraits< DerivedClass >::DataHandle DataHandle
The data handle type defined via the specialized traits class.
~VdfParallelExecutorDataManager()
Prevent destruction via base class pointers (static polymorphism only).
VdfVector * GetInputValue(const VdfConnection &connection, const VdfMask &mask) const
Returns the input value flowing across the given connection with the given mask.
const VdfInvalidationTimestamp & GetInvalidationTimestamp() const
Returns the current invalidation timestamp on this executor.
void UpdateInvalidationTimestamp(const VdfInvalidationTimestamp &timestamp)
Increments the the current invalidation timestamp on this executor.
static VdfVector * CreateOutputCache(const VdfOutput &output, VdfExecutorBufferData *bufferData)
Creates a new cache for an output, given the output data object.
Vdf_ParallelExecutorDataManagerInterface< DerivedClass, DataHandle > Base
The base class type.
void Touch(const VdfOutput &output) const
Marks the data at the given output as having been touched by evaluation.
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 * GetOutputValueForReading(const DataHandle dataHandle, const VdfMask &mask) const
Returns the cached value for a given output and mask.
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.
bool TakeOutputValue(const VdfOutput &output, VdfVector *value, const VdfMask &mask)
Transfers the ownership of value to the given output.
bool InvalidateOutput(const VdfOutput &output, const VdfMask &invalidationMask)
Marks output as invalid.
bool HasInvalidationTimestampMismatch(const DataHandle &sourceHandle, const DataHandle &destHandle) const
Returns true, if the invalidation timestamps between sourceExecutorData and destExecutorData do not m...
VdfVector * GetOrCreateOutputValueForWriting(const VdfOutput &output, const DataHandle dataHandle) const
Returns a new or existing output value.
This class is used to abstract away knowledge of the cache data used for each node.
Definition: vector.h:56
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...