HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
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 
10 ///\file
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 
24 
25 class VdfMask;
26 class VdfNetwork;
27 class VdfVector;
28 
29 /// Forward definition of the traits class, which will be specialized by the
30 /// derived data manager implementation, and contain at least a type definition
31 /// for the DataHandle.
32 template<typename DerivedClass> struct Vdf_ExecutorDataManagerTraits;
33 
34 ///////////////////////////////////////////////////////////////////////////////
35 ///
36 /// \class VdfExecutorDataManager
37 ///
38 /// \brief This class provides functionality to manage the executor specific
39 /// data associated with each output in the network.
40 ///
41 /// The data manager implementations use static polymorphism to dispatch the
42 /// API on VdfExecutorDataManager. See Vdf_ExecutorDataManagerInterface for
43 /// which methods are expected to be implemented by the derived classes.
44 ///
45 
46 template<typename DerivedClass>
49  DerivedClass,
50  typename Vdf_ExecutorDataManagerTraits<DerivedClass>::DataHandle>
51 {
52 public:
53 
54  /// The data handle type defined via the specialized traits class.
55  ///
56  typedef
59 
60  /// The base class type.
61  ///
62  typedef
65 
66  /// \name Cache Management
67  /// @{
68 
69  /// Returns the input value flowing across the given \p connection with the
70  /// given \p mask. If the the cache is not valid, or if the cache does not
71  /// contain all the elements in \p mask, returns NULL. If no output data
72  /// exists for \p output, it will not be created.
73  ///
74  const VdfVector *GetInputValue(
75  const VdfConnection &connection,
76  const VdfMask &mask) const;
77 
78  /// Returns the cached value for a given \p output and \p mask. If the the
79  /// cache is not valid, or if the cache does not contain all the elements in
80  /// \p mask, returns NULL. If no output data exists for \p output, it will
81  /// not be created.
82  ///
84  const DataHandle handle,
85  const VdfMask &mask) const;
86 
87  /// Returns a new or existing output value for writing data into.
88  ///
90  const VdfOutput &output,
91  const DataHandle handle) const;
92 
93  /// Sets the cached value for a given \p output, creating the output cache
94  /// if necessary.
95  ///
96  /// If the output already contains data, it will be merged with the new
97  /// data as indicated by \p value and \p mask.
98  ///
99  void SetOutputValue(
100  const VdfOutput &output,
101  const VdfVector &value,
102  const VdfMask &mask);
103 
104  /// Transfers ownership of \p value to the given \p output, returning
105  /// \c true if the transfer of ownership succeeds. If successful, the
106  /// data manager assumes responsibility for the lifetime of \p value.
107  ///
108  bool TakeOutputValue(
109  const VdfOutput &output,
110  VdfVector *value,
111  const VdfMask &mask);
112 
113  /// Called to set destOutput's buffer output to be a reference to the
114  /// \p sourceValue.
115  ///
117  const VdfVector *sourceValue,
118  const VdfId destOutputId) const;
119 
120  /// Creates a new cache for an output, given the output data object.
121  ///
123  const VdfOutput &output,
124  VdfExecutorBufferData *bufferData) const;
125 
126  /// Duplicates the output data associated with \p sourceOutput and copies
127  /// it to \p destOutput.
128  ///
129  void DuplicateOutputData(
130  const VdfOutput &sourceOutput,
131  const VdfOutput &destOutput);
132 
133  /// Marks the output whose data is \p bufferData as computed for the
134  /// entries in \p mask.
135  ///
137  VdfExecutorBufferData *bufferData,
138  const VdfMask &mask) {
139  bufferData->SetExecutorCacheMask(mask);
140  }
141 
142  /// @}
143 
144 
145  /// \name Invalidation
146  /// @{
147 
148  /// Returns \c true if the output is already invalid for the given
149  /// \p invalidationMask.
150  ///
151  bool IsOutputInvalid(
152  const VdfId outputId,
153  const VdfMask &invalidationMask) const;
154 
155  /// Marks \p output as invalid.
156  ///
157  /// Returns \c true if there was anything to invalidate and false if the
158  /// \p output was already invalid.
159  ///
160  bool InvalidateOutput(const VdfOutput &output,
161  const VdfMask &invalidationMask);
162 
163  /// Marks the data at the given \p output as having been touched by
164  /// evaluation.
165  ///
166  void Touch(const VdfOutput &output) const;
167 
168  /// Increments the the current invalidation timestamp on this executor.
169  ///
171  _invalidationTimestamp = timestamp;
172  }
173 
174  /// Returns the current invalidation timestamp on this executor.
175  ///
177  return _invalidationTimestamp;
178  }
179 
180  /// Returns \c true, if the invalidation timestamps between \p sourceData
181  /// and \p destData do not match, i.e. the source output should be
182  /// mung buffer locked.
184  const DataHandle sourceHandle,
185  const DataHandle destHandle) const;
186 
187  /// @}
188 
189 
190  /// \name Buffer Passing
191  /// @{
192 
193  /// This method is called to pass a buffer from \p fromOutput to \p
194  /// toOutput. The \p keepMask is the mask of elements that \p fromOutput
195  /// should keep after the pass.
196  ///
197  /// Returns the cache data that ends up in \p toOutput.
198  ///
200  const VdfOutput &fromOutput,
201  VdfExecutorBufferData *fromBuffer,
202  const VdfOutput &toOutput,
203  VdfExecutorBufferData *toBuffer,
204  const VdfMask &keepMask);
205 
206  /// @}
207 
208 protected:
209 
210  /// Constructor. Note that the invalidation timestamp is initialized to be
211  /// ahead of the initial timestamp in the VdfExecutorData. This is to
212  /// allow the executor to correctly identify when data has never been
213  /// invalidated before.
214  ///
216  _invalidationTimestamp(
217  VdfExecutorInvalidationData::InitialInvalidationTimestamp + 1)
218  {}
219 
220  /// Prevent destruction via base class pointers (static polymorphism only).
221  ///
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 
233 ///////////////////////////////////////////////////////////////////////////////
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 
635 
636 #endif
bool IsEmpty() const
Definition: vector.h:150
bool IsEmpty() const
Definition: mask.h:168
VdfInvalidationTimestamp GetInvalidationTimestamp() const
#define PXR_NAMESPACE_OPEN_SCOPE
Definition: pxr.h:73
GLsizei const GLfloat * value
Definition: glcorearb.h:824
size_t GetNumConnections() const
Definition: input.h:58
VdfId GetId() const
Definition: output.h:100
Vdf_ExecutorDataManagerInterface< DerivedClass, DataHandle > Base
A VdfMask is placed on connections to specify the data flowing through them.
Definition: mask.h:36
const VdfNode & GetNode() const
Definition: output.h:57
**But if you need a result
Definition: thread.h:622
bool HasInvalidationTimestampMismatch(const DataHandle sourceHandle, const DataHandle destHandle) const
bool IsAnySet() const
Definition: mask.h:216
VDF_API const VdfOutputSpec & GetSpec() const
This class provides functionality to manage the executor specific data associated with each output in...
Definition: input.h:35
bool TakeOutputValue(const VdfOutput &output, VdfVector *value, const VdfMask &mask)
void SetOutputValue(const VdfOutput &output, const VdfVector &value, const VdfMask &mask)
#define TF_DEV_AXIOM(cond)
VdfVector * CreateExecutorCache(const VdfOutputSpec &spec)
bool InvalidateOutput(const VdfOutput &output, const VdfMask &invalidationMask)
void Copy(const VdfVector &rhs, const VdfMask &mask)
Definition: vector.h:281
VdfVector * CreateOutputCache(const VdfOutput &output, VdfExecutorBufferData *bufferData) const
void ResetExecutorCache(const VdfMask &mask)
const VdfNetwork & GetNetwork() const
Definition: node.h:138
const VdfInput & GetTargetInput() const
Returns the input connector (ie. target) for this connection.
Definition: connection.h:87
bool Vdf_IsPoolOutput(const VdfOutput &output)
void SetComputedOutputMask(VdfExecutorBufferData *bufferData, const VdfMask &mask)
VdfSMBLData holds per-output data that is meant to be consumed by the executor. This data is an optio...
Definition: smblData.h:30
GLint GLuint mask
Definition: glcorearb.h:124
VDF_API void Merge(const VdfVector &rhs, const VdfMask::Bits &bits)
void SetExecutorCacheMask(const VdfMask &mask)
~VdfExecutorDataManager()=default
bool IsOutputInvalid(const VdfId outputId, const VdfMask &invalidationMask) const
This object is responsible for storing the executor buffer data, comprised of the executor cache vect...
VdfVector * PassBuffer(const VdfOutput &fromOutput, VdfExecutorBufferData *fromBuffer, const VdfOutput &toOutput, VdfExecutorBufferData *toBuffer, const VdfMask &keepMask)
Vdf_ExecutorDataManagerTraits< DerivedClass >::DataHandle DataHandle
bool IsAllZeros() const
Definition: mask.h:206
VdfVector * GetOrCreateOutputValueForWriting(const VdfOutput &output, const DataHandle handle) const
void SetReferenceOutputValue(const VdfVector *sourceValue, const VdfId destOutputId) const
const VdfOutput & GetSourceOutput() const
Returns the output (ie. source) for this connection.
Definition: connection.h:63
const VdfMask & GetExecutorCacheMask() const
The interface contract for the static polymorphism used by executor data manager implementations.
const VdfOutput * GetAssociatedOutput() const
Definition: input.h:82
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
void Touch(const VdfOutput &output) const
void DuplicateOutputData(const VdfOutput &sourceOutput, const VdfOutput &destOutput)
const VdfVector * GetInputValue(const VdfConnection &connection, const VdfMask &mask) const
VdfVector * SwapExecutorCache(VdfExecutorBufferData *rhs)
VdfVector * GetExecutorCache() const
VdfVector * GetOutputValueForReading(const DataHandle handle, const VdfMask &mask) const
void UpdateInvalidationTimestamp(VdfInvalidationTimestamp timestamp)
uint64_t VdfId
The unique identifier type for Vdf objects.
Definition: types.h:107