HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
speculationExecutorEngine.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_SPECULATION_EXECUTOR_ENGINE_H
8 #define PXR_EXEC_VDF_SPECULATION_EXECUTOR_ENGINE_H
9 
10 ///\file
11 
12 #include "pxr/pxr.h"
13 
15 
16 #include "pxr/exec/vdf/context.h"
20 #include "pxr/exec/vdf/node.h"
23 
25 
27 
28 ///////////////////////////////////////////////////////////////////////////////
29 ///
30 /// \class VdfSpeculationExecutorEngine
31 ///
32 /// \brief This class provides an executor engine to the speculation executor.
33 ///
34 /// \remark This class inherits from VdfPullBasedExecutorEngine only to
35 /// share code. It is not meant to behave polymorphically.
36 ///
37 template <typename DataManagerType>
39  public VdfPullBasedExecutorEngine<DataManagerType>
40 {
41  // Base type definition
42  typedef
44  Base;
45 
46 public:
47 
48  /// Constructs an engine used by the speculation executor.
49  ///
51  const VdfSpeculationExecutorBase &speculationExecutor,
52  DataManagerType *dataManager) :
53  VdfPullBasedExecutorEngine<DataManagerType>(
54  speculationExecutor, dataManager),
55  _writeBackExecutor(const_cast<VdfExecutorInterface *>(
56  speculationExecutor.GetNonSpeculationParentExecutor())) {
57  TF_VERIFY(_writeBackExecutor);
58  }
59 
60  /// Executes the given \p schedule with a \p computeRequest and an optional
61  /// /p errorLogger.
62  ///
64  const VdfSchedule &schedule,
65  const VdfRequest &computeRequest,
66  VdfExecutorErrorLogger *errorLogger) {
68  schedule, computeRequest, errorLogger,
69  [](const VdfMaskedOutput &, size_t){});
70  }
71 
72  /// Executes the given \p schedule with a \p computeRequest and an optional
73  /// /p errorLogger. Invokes \p callback after evaluation of each uncached
74  /// output in the request, and immediatelly after hitting the cache for
75  /// cached outputs in the request.
76  ///
77  template <typename F>
78  void RunSchedule(
79  const VdfSchedule &schedule,
80  const VdfRequest &computeRequest,
81  VdfExecutorErrorLogger *errorLogger,
82  F &&callback);
83 
84 private:
85  // MSVC errors out if Base::ExecutionStageStart is accessed in the
86  // _OutputToExecute constructor, because the enumerator is protected in the
87  // base class. GCC and clang do not consider this an error. To work around
88  // this issue, we loft the enumerator into the namespace of the derived
89  // class where _OutputToExecute() has the proper privileges across all
90  // compilers.
91  static constexpr typename Base::_ExecutionStage _ExecutionStageStart =
93 
94  // This struct contains the necessary state to compute an output.
95  //
96  struct _OutputToExecute {
97 
98  // Constructor that takes schedule node and output.
99  _OutputToExecute(const VdfSchedule::OutputId &outputId) :
100  outputId(outputId),
101  stage(_ExecutionStageStart),
102  numPushed(0),
103  inputsSpeculate(false)
104  { }
105 
106  // The schedule identifier for the output to execute.
107  VdfSchedule::OutputId outputId;
108 
109  // The current phase of this output in the execution stack.
110  typename Base::_ExecutionStage stage;
111 
112  // The number of inputs that this output is waiting on.
113  int numPushed;
114 
115  // Whether or not our read inputs speculate.
116  bool inputsSpeculate;
117  };
118 
119  // This method adds \p output to the \p outputs vector.
120  // Returns \c true if it added a new output and \c false otherwise.
121  //
122  bool _PushBackOutputForSpeculation(
123  std::vector< _OutputToExecute > *outputs,
124  const VdfOutput &output,
125  const VdfSchedule &schedule);
126 
127  // Method that makes sure that data is available for the given \p output
128  // before returning.
129  //
130  void _ExecuteOutputForSpeculation(
131  const VdfEvaluationState &state,
132  const VdfOutput &output,
133  TfBits *executedNodes,
134  TfBits *speculatedNodes);
135 
136  // Write the computed output back to the write-back executor.
137  void _WriteBackComputedOutput(
138  const VdfOutput &output,
139  const VdfSchedule::OutputId &outputId,
140  const VdfSchedule &schedule);
141 
142  // The parent executor this speculation engine is going to write back to.
143  VdfExecutorInterface *_writeBackExecutor;
144 
145 };
146 
147 ///////////////////////////////////////////////////////////////////////////////
148 
149 template <typename DataManagerType>
150 bool
152  std::vector< _OutputToExecute > *outputs,
153  const VdfOutput &output,
154  const VdfSchedule &schedule)
155 {
156  VdfSchedule::OutputId outputId = schedule.GetOutputId(output);
157 
158  if (outputId.IsValid()) {
159  outputs->push_back(_OutputToExecute(outputId));
160  return true;
161  }
162 
163  // The output to push is not actually scheduled, which guarantees
164  // that is value will never be needed by any computations. So
165  // just skip it.
166  return false;
167 }
168 
169 template <typename DataManagerType>
170 template <typename F>
171 void
173  const VdfSchedule &schedule,
174  const VdfRequest &computeRequest,
175  VdfExecutorErrorLogger *errorLogger,
176  F &&callback)
177 {
178  TRACE_FUNCTION();
179 
180  // Make sure the executor data manager is appropriately sized.
181  Base::_GetDataManager()->Resize(*schedule.GetNetwork());
182 
183  const size_t numNodes = schedule.GetScheduleNodeVector().size();
184 
185  // Has a bit set for any node that has already been run.
186  TfBits executedNodes(numNodes);
187 
188  // Has a bit set for any node, which had one ore more inputs speculated.
189  TfBits speculatedNodes(numNodes);
190 
191  // The persistent evaluation state
192  VdfEvaluationState state(Base::_GetExecutor(), schedule, errorLogger);
193 
194  // Now execute the uncached, requested outputs.
195  VdfRequest::IndexedView requestView(computeRequest);
196  for (size_t i = 0; i < requestView.GetSize(); ++i) {
197  // Skip outputs not included in the request.
198  const VdfMaskedOutput *maskedOutput = requestView.Get(i);
199  if (!maskedOutput) {
200  continue;
201  }
202 
203  // Skip outputs that have already been cached. However, we must invoke
204  // the callback to notify the client side that evaluation of the
205  // requested output has completed.
206  const VdfOutput &output = *maskedOutput->GetOutput();
207  const VdfMask &mask = maskedOutput->GetMask();
208  if (Base::_GetExecutor().GetOutputValue(output, mask)) {
209  callback(*maskedOutput, i);
210  continue;
211  }
212 
213  _ExecuteOutputForSpeculation(
214  state, output, &executedNodes, &speculatedNodes);
215 
216  // If we've been interrupted, bail out.
217  if (Base::_GetExecutor().HasBeenInterrupted()) {
218  break;
219  }
220 
221  // Invoke the callback once the output has been evaluated, but only
222  // if the executor has not been interrupted.
223  else {
224  callback(*maskedOutput, i);
225  }
226  }
227 }
228 
229 template <typename DataManagerType>
230 void
232  const VdfEvaluationState &state,
233  const VdfOutput &output,
234  TfBits *executedNodes,
235  TfBits *speculatedNodes)
236 {
237  TF_DEBUG(VDF_SEE_TRACE).Msg("{ SpeculationOutputExecuteBegin();\n");
238 
239  // The current schedule
240  const VdfSchedule &schedule = state.GetSchedule();
241 
242  // This is the stack of the outputs currently in the process of execution.
243  std::vector< _OutputToExecute > outputsStack;
244 
245  // This is a stack used for the return values of outputs. A return value
246  // of true means that the output couldn't be evaluated due to speculation.
247  // XXX:optimization
248  // It's possible to get rid of this vector all together if outputs were
249  // allowed to write directly into their caller's stack space.
250  std::vector< bool > speculated;
251 
252  // Add the initial output to start executing. This call will check for
253  // already cached values.
254  _PushBackOutputForSpeculation(&outputsStack, output, schedule);
255 
256  bool hasBeenInterrupted = Base::_GetExecutor().HasBeenInterrupted();
257 
258  while (!outputsStack.empty() && !hasBeenInterrupted) {
259 
260  const VdfSchedule::OutputId &outputId = outputsStack.back().outputId;
261  const int scheduleNodeIndex = schedule.GetScheduleNodeIndex(outputId);
262  const VdfNode &node = *schedule.GetNode(outputId);
263  typename Base::_ExecutionStage stage = outputsStack.back().stage;
264  const size_t outputIndex = outputsStack.size() - 1;
265 
266  bool affective = schedule.IsAffective(outputId);
267 
268  // Pop all the return values from our inputs and check to see
269  // if any of them were 'true' (meaning that they hit a speculation
270  // path).
271  bool previousStageSpeculated = false;
272  while (outputsStack.back().numPushed) {
273  outputsStack.back().numPushed--;
274  previousStageSpeculated |= speculated.back();
275  speculated.pop_back();
276  }
277 
278  switch (stage) {
279 
280  case Base::ExecutionStageStart:
281 
282  TF_DEBUG(VDF_SEE_TRACE)
283  .Msg("{ SpeculationBeginNode(%p, \"%s\");\n",
284  &node, node.GetDebugName().c_str());
285 
286  // If this is the node that started the speculation, we need to
287  // skip it. Note that this means we encountered a true data
288  // dependency cycle and have a bad result. Additionally, we may
289  // write back the bad result to any parent executors.
290  if (static_cast<const VdfSpeculationExecutorBase &>(
291  Base::_GetExecutor()).IsSpeculatingNode(&node)) {
292  speculated.push_back(true);
293  outputsStack.pop_back();
294 
295  TF_DEBUG(VDF_SEE_TRACE)
296  .Msg("SpeculationEndNodeSpeculationNode(); (cycle) }\n");
297 
298  continue;
299  }
300 
301  // If this node has already been executed, do not run it a second
302  // time. However, make sure to push the right value onto the
303  // speculated stack, based on whether the node had inputs we
304  // speculated about, the last time it was run.
305  if (executedNodes->IsSet(scheduleNodeIndex)) {
306  speculated.push_back(
307  speculatedNodes->IsSet(scheduleNodeIndex));
308  outputsStack.pop_back();
309 
310  TF_DEBUG(VDF_SEE_TRACE)
311  .Msg("SpeculationEndNodeRedundantCompute(); }\n");
312 
313  continue;
314  }
315 
316  // If we are already cached for this output (or if our parent
317  // executor is), then we can provide a value, we can return early.
318  if (Base::_GetExecutor().GetOutputValue(
319  *schedule.GetOutput(outputId),
320  schedule.GetRequestMask(outputId))) {
321  speculated.push_back(false);
322  outputsStack.pop_back();
323 
324  TF_DEBUG(VDF_SEE_TRACE)
325  .Msg("SpeculationEndNodeFoundCache(); }\n");
326 
327  continue;
328  }
329 
330  TF_DEV_AXIOM(outputsStack.back().numPushed == 0);
331 
332  // The first stage of computation is to execute all the
333  // prerequisites for current output.
334  outputsStack.back().stage = Base::ExecutionStagePreRequisitesDone;
335 
336  // Push back all the prerequisites
337  if (affective) {
338  for (const VdfScheduleInput &input : schedule.GetInputs(node)) {
339  if (input.input->GetSpec().IsPrerequisite()) {
340  const bool pushed = _PushBackOutputForSpeculation(
341  &outputsStack, *input.source, schedule);
342  outputsStack[outputIndex].numPushed += pushed;
343  }
344  }
345  }
346 
347  // Little optimization to not go back to the top of the loop
348  // for no reason.
349  if (outputsStack[outputIndex].numPushed > 0) {
350  break;
351  } // else fall through to the next stage.
352 
353 
354  case Base::ExecutionStagePreRequisitesDone:
355 
356 
357  // Now that our prerequisites are done, unroll our return stack.
358 
359 
360  // Update whether or not our previousStageSpeculated
361  outputsStack.back().inputsSpeculate |= previousStageSpeculated;
362 
363  // The second stage of computation is to use the prerequisites
364  // to determine what other inputs need to run to satisfy the
365  // current output.
366 
367  // Mark that the next stage of computation
368  outputsStack.back().stage = Base::ExecutionStageReadsDone;
369 
370  // Only need to run the reads of an output that will do something
371  // and if our pre-requisites were computed without speculation.
372  if (affective && !previousStageSpeculated) {
373 
374  // Get the list of required inputs based on the prerequisite
375  // computations.
376  VdfRequiredInputsPredicate inputsPredicate =
377  node.GetRequiredInputsPredicate(VdfContext(state, node));
378 
379  // Run the required reads first.
380  // Here we try to run the "read" inputs before the "read/write"
381  // inputs so that we can maximize the chance of being able to
382  // re-use the buffer.
383  if (inputsPredicate.HasRequiredReads()) {
384  for (const VdfScheduleInput &input :
385  schedule.GetInputs(node)) {
386  if (inputsPredicate.IsRequiredRead(*input.input)) {
387  const bool pushed = _PushBackOutputForSpeculation(
388  &outputsStack, *input.source, schedule);
389  outputsStack[outputIndex].numPushed += pushed;
390  }
391  }
392  }
393  }
394 
395  // Little optimization to not go back to the top of the loop
396  // for no reason.
397  if (outputsStack[outputIndex].numPushed > 0) {
398  break;
399  } // else fall through to the next stage.
400 
401  case Base::ExecutionStageReadsDone:
402 
403  // Mark that the next stage of computation
404  outputsStack.back().stage = Base::ExecutionStageCompute;
405 
406  // Mark whether or not our read inputs depend on a speculation.
407  outputsStack.back().inputsSpeculate |= previousStageSpeculated;
408 
409  // Now run the read/writes last.
410  for (const VdfScheduleInput &input : schedule.GetInputs(node)) {
411  const VdfOutput *assocOutput =
412  input.input->GetAssociatedOutput();
413  if (!assocOutput) {
414  continue;
415  }
416 
417  // Does this output have a pass-through scheduled?
418  const VdfSchedule::OutputId &assocOutputId =
419  schedule.GetOutputId(*assocOutput);
420  if (assocOutputId.IsValid()) {
421  if (const VdfOutput *fromBufferOutput =
422  schedule.GetFromBufferOutput(assocOutputId)) {
423  const bool pushed = _PushBackOutputForSpeculation(
424  &outputsStack, *fromBufferOutput, schedule);
425  outputsStack[outputIndex].numPushed += pushed;
426  continue;
427  }
428  }
429 
430  // If the associated output is not scheduled, or it does not
431  // have a pass-through scheduled, we need to consider all
432  // connected source outputs!
433  const bool pushed = _PushBackOutputForSpeculation(
434  &outputsStack, *input.source, schedule);
435  outputsStack[outputIndex].numPushed += pushed;
436  }
437 
438  // Little optimization to not go back to the top of the loop
439  // for no reason.
440  if (outputsStack[outputIndex].numPushed > 0) {
441  break;
442  } // else fall through to the next stage.
443 
444 
445  default:
446 
447  // Mark whether or not our read/write inputs depend on
448  // a speculation.
449  outputsStack.back().inputsSpeculate |= previousStageSpeculated;
450 
451  // Set a bit indicating that this node has been executed.
452  executedNodes->Set(scheduleNodeIndex);
453 
454  // If any of our inputs speculated, there is nothing we can do.
455  // Skip this node, but make sure to still touch its outputs.
456  if (outputsStack.back().inputsSpeculate) {
457 
458  TF_DEBUG(VDF_SEE_TRACE)
459  .Msg("SpeculationSkipNode (cycle) (\"%s\"); }\n",
460  node.GetDebugName().c_str());
461 
462  // This node has speculated inputs
463  speculatedNodes->Set(scheduleNodeIndex);
464  speculated.push_back(true);
465 
466  // Compute this node, if it is affective, or pass-through if any
467  // of the reads speculated.
468  } else if (affective) {
469  // None of our inputs speculated, we can just compute as
470  // normal.
471  Base::_ComputeNode(state, node);
472  speculated.push_back(false);
473 
474  TF_DEBUG(VDF_SEE_TRACE)
475  .Msg("SpeculationComputedNode(\"%s\"); }\n",
476  node.GetDebugName().c_str());
477 
478  // The node is not affective, and none of its reads or read/writes
479  // did speculate.
480  } else {
481  // None of the outputs on this node contribute to the
482  // results in the request, so we will skip over this node
483  // by passing through all the outputs with associated
484  // inputs and use the fallback value for all the outputs
485  // that don't.
486  TF_DEBUG(VDF_SEE_TRACE)
487  .Msg("SpeculationPassThrough(\"%s\"); }\n",
488  node.GetDebugName().c_str());
489 
490  Base::_PassThroughNode(schedule, node);
491  speculated.push_back(false);
492 
493  }
494 
495  // Check interruption.
496  hasBeenInterrupted = Base::_GetExecutor().HasBeenInterrupted();
497 
498  // Mark that we've visited these outputs in our parent
499  // executor. We need to tell the parent executor that
500  // we've visited this node so that we receive invalidation
501  // the next time it is required. If we don't mark the
502  // output as needing invalidation and the main executor
503  // never needs to execute it, then it will never get
504  // invalidated.
505  // Also write back any computed or pass-through data to the
506  // write back executor, so that the data can be picked up by
507  // another executor. Note, that we do NOT want to write back
508  // any data after interruption, because the buffers may
509  // contain junk.
510  VDF_FOR_EACH_SCHEDULED_OUTPUT_ID(outputId, schedule, node) {
511  const VdfOutput& output = *schedule.GetOutput(outputId);
512  if (_writeBackExecutor &&
513  !hasBeenInterrupted &&
514  !outputsStack.back().inputsSpeculate) {
515  _WriteBackComputedOutput(output, outputId, schedule);
516  }
517  Base::_GetExecutor()._TouchOutput(output);
518  }
519 
520  outputsStack.pop_back();
521  }
522  }
523 
524  TF_DEBUG(VDF_SEE_TRACE).Msg("SpeculationOutputExecuteEnd(); }\n");
525 }
526 
527 template <typename DataManagerType>
528 void
530  const VdfOutput &output,
531  const VdfSchedule::OutputId &outputId,
532  const VdfSchedule &schedule)
533 {
534  // Retrieve the data handle.
535  const typename Base::_DataHandle dataHandle =
536  Base::_GetDataManager()->GetDataHandle(output.GetId());
537  if (!Base::_GetDataManager()->IsValidDataHandle(dataHandle)) {
538  return;
539  }
540 
541  // Get the buffer data associated with the data handle.
542  VdfExecutorBufferData *bufferData =
543  Base::_GetDataManager()->GetBufferData(dataHandle);
544 
545  // Get the output vector and computed output mask
546  const VdfVector *value = bufferData->GetExecutorCache();
547 
548  // If the data is not available we are done. This can happen with
549  // node that manage their own buffers and choose to leave them empty.
550  if (!value) {
551  return;
552  }
553 
554  const VdfMask &computedMask = bufferData->GetExecutorCacheMask();
555 
556  // If the computed output mask is empty, we can bail out early. This may
557  // happen if, for example, the executor was interrupted and opted for not
558  // writing a computed output mask for the current node.
559  // Don't even bother writing back an all-zeros mask.
560  if (computedMask.IsEmpty() || computedMask.IsAllZeros()) {
561  return;
562  }
563 
564  // If the output does not pass its data, we can write the full output value
565  // back to the write executor.
566  if (!output.GetAssociatedInput()) {
567  _writeBackExecutor->SetOutputValue(output, *value, computedMask);
568 
569  // Reclaim locally, so that future cache lookups result in hits on
570  // the parent executor, but not the local executor.
571  //
572  // XXX
573  // This guards against client callbacks that mutate cached values (which
574  // is something we have encountered in practice), causing output values
575  // to change after the node has already run. By removing the buffer
576  // locally, we ensure that the next time we access the buffer we get it
577  // from the parent executor and modify it there. We would prefer to not
578  // support this client behavior, but for now, we choose to keep this,
579  // since it's not expensive, and safer.
580  bufferData->ResetExecutorCache();
581  }
582 
583  // If the output passes its data, we may still be able to write back some
584  // or all of it.
585  else {
586  // If this output is not scheduled to pass its data, we can simply copy
587  // the entire executor cache. Alternatively, if the output is scheduled
588  // to pass its data, we can at least copy anything that will be kept at
589  // the output. Unless, however, invalidation entered somewhere between
590  // this output, and the output we are going to pass the data to. If this
591  // is the case, we want to write back the entire cache to the write back
592  // executor, making this algorithm the equivalent of mung buffer locking
593  // on the main executor!
594  const VdfMask *writeBackMask = &computedMask;
595  const VdfOutput *passToOutput = schedule.GetPassToOutput(outputId);
596  if (passToOutput &&
597  !_writeBackExecutor->HasInvalidationTimestampMismatch(
598  output, *passToOutput)) {
599  writeBackMask = &schedule.GetKeepMask(outputId);
600  }
601 
602  if (!writeBackMask->IsEmpty()) {
603  _writeBackExecutor->SetOutputValue(output, *value, *writeBackMask);
604  }
605  }
606 }
607 
609 
610 #endif
size_t GetSize() const
Definition: request.h:174
VDF_API bool IsAffective(const OutputId &outputId) const
virtual VDF_API VdfRequiredInputsPredicate GetRequiredInputsPredicate(const VdfContext &context) const
VDF_API const VdfMask & GetKeepMask(const OutputId &outputId) const
void RunSchedule(const VdfSchedule &schedule, const VdfRequest &computeRequest, VdfExecutorErrorLogger *errorLogger)
bool IsEmpty() const
Definition: mask.h:168
VDF_API const VdfOutput * GetPassToOutput(const OutputId &outputId) const
const VdfNetwork * GetNetwork() const
Definition: schedule.h:178
ScheduleNodeVector & GetScheduleNodeVector()
Definition: schedule.h:503
#define VDF_FOR_EACH_SCHEDULED_OUTPUT_ID(OUTPUT_ID_NAME, VDF_SCHEDULE, VDF_NODE)
Definition: schedule.h:652
VDF_API const VdfOutput * GetFromBufferOutput(const OutputId &outputId) const
#define PXR_NAMESPACE_OPEN_SCOPE
Definition: pxr.h:73
GLsizei const GLfloat * value
Definition: glcorearb.h:824
VDF_API OutputId GetOutputId(const VdfOutput &output) const
VDF_API const std::string GetDebugName() const
Definition: node.h:52
VdfId GetId() const
Definition: output.h:100
const VdfMask & GetMask() const
Definition: maskedOutput.h:64
A VdfMask is placed on connections to specify the data flowing through them.
Definition: mask.h:36
VdfSpeculationExecutorEngine(const VdfSpeculationExecutorBase &speculationExecutor, DataManagerType *dataManager)
VDF_API InputsRange GetInputs(const VdfNode &node) const
const VdfInput * GetAssociatedInput() const
Definition: output.h:76
VDF_API const VdfOutput * GetOutput(const OutputId &outputId) const
#define TF_DEV_AXIOM(cond)
Fast bit array that keeps track of the number of bits set and can find the next set in a timely manne...
Definition: bits.h:48
void ResetExecutorCache(const VdfMask &mask)
int GetScheduleNodeIndex(const OutputId &outputId) const
Definition: schedule.h:514
Contains a specification of how to execute a particular VdfNetwork.
Definition: schedule.h:40
const VdfMaskedOutput * Get(const size_t i) const
Definition: request.h:182
VDF_API const VdfNode * GetNode(const OutputId &outputId) const
GLint GLuint mask
Definition: glcorearb.h:124
const VdfSchedule & GetSchedule() const
bool IsRequiredRead(const VdfInput &input) const
#define TRACE_FUNCTION()
Definition: trace.h:30
This object is responsible for storing the executor buffer data, comprised of the executor cache vect...
#define TF_DEBUG(enumVal)
Definition: debug.h:501
bool IsAllZeros() const
Definition: mask.h:206
Class to hold on to an externally owned output and a mask.
Definition: maskedOutput.h:31
void Set(size_t index)
Definition: bits.h:377
const VdfMask & GetExecutorCacheMask() const
VDF_API const VdfMask & GetRequestMask(const OutputId &outputId) const
bool IsValid() const
Definition: schedule.h:97
#define PXR_NAMESPACE_CLOSE_SCOPE
Definition: pxr.h:74
This predicate determines whether a given input value is needed to fulfill the input dependencies req...
This class provides an executor engine to the speculation executor.
This class is a collection of common functions used by pulled based executors.
Abstract base class for classes that execute a VdfNetwork to compute a requested set of values...
VdfVector * GetExecutorCache() const
bool IsSet(size_t index) const
Definition: bits.h:412
VdfOutput * GetOutput() const
Definition: maskedOutput.h:52
state
Definition: core.h:2289