7 #ifndef PXR_EXEC_VDF_PARALLEL_EXECUTOR_ENGINE_BASE_H
8 #define PXR_EXEC_VDF_PARALLEL_EXECUTOR_ENGINE_BASE_H
38 #include <tbb/concurrent_vector.h>
43 #define PEE_TRACE_SCOPE(x)
57 template <
typename Derived,
typename DataManager >
75 DataManager *dataManager);
89 schedule, computeRequest, errorLogger,
98 template <
typename Callback >
103 Callback &&callback);
113 template <
typename Callback >
121 const size_t requestedIndex,
122 Callback &callback) :
126 _requestedIndex(requestedIndex),
138 const size_t _requestedIndex;
155 _taskIndex(taskIndex),
182 _taskIndex(taskIndex),
209 _taskIndex(taskIndex),
276 template <
typename Callback >
280 const size_t requestedIndex,
306 template <
typename Callback >
311 const size_t requestedIndex,
381 template <
typename Iterable >
383 const Iterable &tasks,
516 const bool hasAssociatedInput,
585 return *
static_cast<Derived *
>(
this);
615 tbb::concurrent_vector<TfErrorTransport>
_errors;
623 template <
typename Derived,
typename DataManager >
627 DataManager *dataManager) :
629 _dataManager(dataManager),
631 _computeTasks(&_taskGraph),
632 _inputsTasks(&_taskGraph),
633 _prepTasks(&_taskGraph),
634 _keepTasks(&_taskGraph),
639 template <
typename Derived,
typename DataManager >
645 template <
typename Derived,
typename DataManager >
646 template <
typename Callback >
654 TRACE_SCOPE(
"VdfParallelExecutorEngineBase::RunSchedule");
663 _resetState.store(
false, std::memory_order_relaxed);
676 _isolatingDispatcher.Run([engine, &state, &view, &callback] {
685 [engine, &
state, &view, &callback, &taskLists]
686 (
size_t b,
size_t e) {
688 for (
size_t i = b; i != e; ++i) {
691 state, *maskedOutput, i, callback, taskList);
705 "VdfParallelExecutorEngineBase::RunSchedule "
706 "(wait for parallel tasks)");
711 _isolatingDispatcher.Wait();
715 _Self()._FinalizeEvaluation();
718 _isInterrupted.store(
false, std::memory_order_relaxed);
721 _PostTransportedErrors();
724 template <
typename Derived,
typename DataManager >
736 _dependencyState.reset(
new std::atomic<uint8_t>[numUniqueDeps]);
737 char *
const dependencyState =
738 reinterpret_cast<char*
>(_dependencyState.get());
739 memset(dependencyState, 0,
740 sizeof(std::atomic<uint8_t>) * numUniqueDeps);
750 template <
typename Derived,
typename DataManager >
751 template <
typename Callback >
756 const size_t requestedIndex,
768 if (_executor.GetOutputValue(output, mask)) {
769 callback(maskedOutput, requestedIndex);
776 bool isReset = _resetState.load(std::memory_order_relaxed);
777 if (!isReset && _resetState.compare_exchange_strong(isReset,
true)) {
785 this,
state, maskedOutput, requestedIndex, callback);
786 taskList->push_back(task);
789 template <
typename Derived,
typename DataManager >
790 template <
typename Callback >
805 if (_engine->_ProcessLeafTask(
806 this, _state, _output, _requestedIndex, _callback, &_evaluationStage,
808 _RecycleAsContinuation();
815 RemoveChildReference();
822 template <
typename Derived,
typename DataManager >
845 this, _state, _node, scheduleTask, &_evaluationStage, &bypass)) {
867 template <
typename Derived,
typename DataManager >
881 _state.GetSchedule().GetInputsTask(_taskIndex);
885 if (_engine->_ProcessInputsTask(
886 this, _state, _node, scheduleTask, &_evaluationStage, &bypass)) {
887 _RecycleAsContinuation();
893 _engine->_inputsTasks.MarkDone(_taskIndex);
894 RemoveChildReference();
901 template <
typename Derived,
typename DataManager >
915 if (_engine->_ProcessKeepTask(
916 this, _state, _node, &_evaluationStage, &bypass)) {
917 _RecycleAsContinuation();
923 _engine->_keepTasks.MarkDone(_taskIndex);
924 RemoveChildReference();
931 template <
typename Derived,
typename DataManager >
938 while (output && output != &_source) {
939 _engine->_Self()._Touch(*output);
947 template <
typename Derived,
typename DataManager >
961 const bool invoked = _engine->_InvokeComputeTasks(
962 _state.GetSchedule().GetComputeTaskIds(_node),
963 _state, _node,
this,
nullptr);
970 _RecycleAsContinuation();
977 RemoveChildReference();
984 template <
typename Derived,
typename DataManager >
1006 if (tasks.
size() > 1) {
1014 this,
state, node, keepTaskIndex);
1027 this,
state, node, computeTaskIndex);
1033 template <
typename Derived,
typename DataManager >
1043 if (!bypass || *bypass) {
1050 template <
typename Derived,
typename DataManager >
1051 template <
typename Callback >
1057 const size_t requestedIndex,
1064 EvaluationStageSpawn,
1065 EvaluationStageCallback
1069 switch (*evaluationStage) {
1074 case EvaluationStageSpawn: {
1077 *evaluationStage = EvaluationStageCallback;
1083 case EvaluationStageCallback: {
1084 callback(maskedOutput, requestedIndex);
1091 template <
typename Derived,
typename DataManager >
1103 EvaluationStageInputs,
1104 EvaluationStagePrepNode,
1105 EvaluationStageEvaluateNode
1109 switch (*evaluationStage) {
1112 case EvaluationStageInputs: {
1126 const bool invokedRequireds =
1128 const bool invokedInputsTask =
1133 if (invokedRequireds || invokedInputsTask) {
1134 *evaluationStage = EvaluationStagePrepNode;
1140 case EvaluationStagePrepNode: {
1152 *evaluationStage = EvaluationStageEvaluateNode;
1158 case EvaluationStageEvaluateNode: {
1176 template <
typename Derived,
typename DataManager >
1188 EvaluationStagePrereqs,
1189 EvaluationStageOptionals,
1199 switch (*evaluationStage) {
1202 case EvaluationStagePrereqs: {
1207 scheduleTask, state, task, bypass)) {
1208 *evaluationStage = EvaluationStageOptionals;
1214 case EvaluationStageOptionals: {
1228 scheduleTask, state, node, task, bypass)) {
1229 *evaluationStage = EvaluationStageDone;
1239 template <
typename Derived,
typename DataManager >
1250 EvaluationStageKeep,
1251 EvaluationStagePublish
1259 switch (*evaluationStage) {
1262 case EvaluationStageKeep: {
1269 bool invoked =
false;
1278 taskId, state, node, task, bypass);
1285 *evaluationStage = EvaluationStagePublish;
1291 case EvaluationStagePublish: {
1305 template <
typename Derived,
typename DataManager >
1321 this,
state, node, idx);
1330 template <
typename Derived,
typename DataManager >
1345 template <
typename Derived,
typename DataManager >
1363 this,
state, node, taskIndex);
1372 template <
typename Derived,
typename DataManager >
1373 template <
typename Iterable >
1376 const Iterable &tasks,
1383 bool invoked =
false;
1392 template <
typename Derived,
typename DataManager >
1407 uint8_t currentState = state->load(std::memory_order_relaxed);
1411 if (currentState == StateUndecided) {
1414 const uint8_t newState = isCached ? StateCached : StateUncached;
1419 if (state->compare_exchange_strong(currentState, newState)) {
1425 return currentState == StateCached;
1428 template <
typename Derived,
typename DataManager >
1473 template <
typename Derived,
typename DataManager >
1517 template <
typename Derived,
typename DataManager >
1525 PEE_TRACE_SCOPE(
"VdfParallelExecutorEngineBase::_InvokePrereqInputs");
1538 bool invoked =
false;
1547 template <
typename Derived,
typename DataManager >
1556 PEE_TRACE_SCOPE(
"VdfParallelExecutorEngineBase::_InvokeOptionalInputs");
1580 bool invoked =
false;
1604 *scheduleInput.source, state, successor, bypass);
1613 template <
typename Derived,
typename DataManager >
1621 PEE_TRACE_SCOPE(
"VdfParallelExecutorEngineBase::_InvokeRequiredInputs");
1631 bool invoked =
false;
1640 template <
typename Derived,
typename DataManager >
1667 this,
state, node, inputsTaskIndex);
1676 template <
typename Derived,
typename DataManager >
1699 PEE_TRACE_SCOPE(
"VdfParallelExecutorEngineBase::_InvokePrepTask (task)");
1721 template <
typename Derived,
typename DataManager >
1741 template <
typename Derived,
typename DataManager >
1753 _Self()._Touch(output);
1770 output, outputId, requestMask, schedule, privateBuffer);
1789 template <
typename Derived,
typename DataManager >
1815 _dataManager->CreateOutputCache(output, scratchBuffer, unionBits);
1819 template <
typename Derived,
typename DataManager >
1846 template <
typename Derived,
typename DataManager >
1893 if (!privateBuffer->GetExecutorCache()) {
1905 output, dataHandle));
1913 scheduleTask, state, output, outputId, dataHandle,
1914 hasAssociatedInput, privateBuffer);
1918 template <
typename Derived,
typename DataManager >
1946 scheduleTask, state, output, outputId, dataHandle,
1947 hasAssociatedInput, privateBuffer);
1951 template <
typename Derived,
typename DataManager >
1959 const bool hasAssociatedInput,
1965 const bool hasMultipleInvocations =
1974 _Self()._FinalizeOutput(
1975 state, output, outputId, dataHandle, invocationIndex, passToOutput);
1980 if (!passToOutput) {
1998 if (scratchBuffer) {
1999 scratchBuffer->
Merge(
2022 const VdfMask &keepMask = hasMultipleInvocations
2033 "VdfParallelExecutorEngineBase::_FinalizeOutput (keep)");
2034 scratchValue->Merge(
2041 if (!hasMultipleInvocations) {
2050 template <
typename Derived,
typename DataManager >
2075 if (numInputNodes == 1 && !(*input)[0].GetMask().IsAllZeros()) {
2083 _dataManager->CreateOutputCache(output, privateBuffer);
2086 template <
typename Derived,
typename DataManager >
2096 bool passBuffer =
false;
2114 uniqueIndex, source, inputMask);
2127 _CopyBuffer(output, source, inputMask, privateBuffer);
2131 template <
typename Derived,
typename DataManager >
2146 template <
typename Derived,
typename DataManager >
2161 if (!sourceVector) {
2169 destValue->
Copy(*sourceVector, fromMask);
2172 template <
typename Derived,
typename DataManager >
2193 if (
const VdfVector*
value = scratchBuffer->GetExecutorCache()) {
2195 output, dataHandle, scratchBuffer->GetExecutorCacheMask());
2201 template <
typename Derived,
typename DataManager >
2216 if (!publicValue || publicMask.
IsEmpty() || publicMask == haveMask) {
2234 const VdfMask extendedMask = publicMask | haveMask;
2235 if (!scratchValue) {
2237 output, scratchBuffer, extendedMask.
GetBits());
2243 scratchValue->
Merge(*publicValue, mergeBits);
2245 return scratchValue;
2248 template <
typename Derived,
typename DataManager >
2257 const bool hasCycle =
_Self()._DetectCycle(state, node);
2273 template <
typename Derived,
typename DataManager >
2281 template <
typename Derived,
typename DataManager >
2287 _errors.grow_by(1)->swap(transport);
2290 template <
typename Derived,
typename DataManager >
2300 errorTransport.Post();
std::atomic< bool > _isInterrupted
void _TransportErrors(const TfErrorMark &errorMark)
void _PublishScratchBuffers(const VdfSchedule &schedule, const VdfNode &node)
virtual VDF_API VdfRequiredInputsPredicate GetRequiredInputsPredicate(const VdfContext &context) const
int RemoveChildReference()
VdfExecutionStats * GetExecutionStats() const
VDF_API const VdfMask & GetKeepMask(const OutputId &outputId) const
size_t GetNumInputsTasks() const
bool _InvokeRequiredInputs(const VdfScheduleComputeTask &scheduleTask, const VdfEvaluationState &state, WorkTaskGraph::BaseTask *successor, WorkTaskGraph::BaseTask **bypass)
WorkTaskGraph::BaseTask * execute()
InputDependencyRange GetRequiredInputDependencies(const VdfScheduleComputeTask &task) const
#define TRACE_SCOPE(name)
WORK_API void RunLists(const TaskLists &taskLists)
size_t GetFirstSet() const
void TakeOwnership(VdfVector *v)
void _CopyBuffer(const VdfOutput &output, const VdfOutput &source, const VdfMask &fromMask, VdfExecutorBufferData *toData) const
std::atomic< bool > _resetState
VDF_API const VdfOutput * GetPassToOutput(const OutputId &outputId) const
const VdfNetwork * GetNetwork() const
#define VDF_FOR_EACH_SCHEDULED_OUTPUT_ID(OUTPUT_ID_NAME, VDF_SCHEDULE, VDF_NODE)
VDF_API const VdfOutput * GetFromBufferOutput(const OutputId &outputId) const
bool _InvokeOptionalInputs(const VdfScheduleInputsTask &scheduleTask, const VdfEvaluationState &state, const VdfNode &node, WorkTaskGraph::BaseTask *successor, WorkTaskGraph::BaseTask **bypass)
#define PXR_NAMESPACE_OPEN_SCOPE
GLsizei const GLfloat * value
VDF_API OutputId GetOutputId(const VdfOutput &output) const
void _ProcessOutput(const VdfScheduleComputeTask &scheduleTask, const VdfEvaluationState &state, const VdfOutput &output, const VdfSchedule::OutputId outputId, const _DataHandle dataHandle, const bool hasAssociatedInput, VdfExecutorBufferData *privateBuffer)
DataManager * _dataManager
void RunSchedule(const VdfSchedule &schedule, const VdfRequest &computeRequest, VdfExecutorErrorLogger *errorLogger)
_KeepTask(This *engine, const VdfEvaluationState &state, const VdfNode &node, VdfScheduleTaskIndex taskIndex)
void _ResetState(const VdfSchedule &schedule)
std::string const & GetString() const
Return the string that this token represents.
const VdfScheduleComputeTask & GetComputeTask(const VdfScheduleTaskIndex index) const
const VdfMask & GetMask() const
VdfParallelExecutorEngineBase & operator=(const VdfParallelExecutorEngineBase &)=delete
ImageBuf OIIO_API min(Image_or_Const A, Image_or_Const B, ROI roi={}, int nthreads=0)
A VdfMask is placed on connections to specify the data flowing through them.
WorkTaskGraph::BaseTask * execute() override
const VdfNode & GetNode() const
virtual void Compute(const VdfContext &context) const =0
State Claim(const size_t idx, WorkTaskGraph::BaseTask *successor)
size_t GetNumComputeTasks() const
VdfParallelTaskSync _inputsTasks
VDF_API const VdfOutputSpec & GetSpec() const
void _PrepareOutput(const VdfSchedule &schedule, const VdfSchedule::OutputId outputId)
static VDF_API void FillVector(TfType type, size_t numElements, VdfVector *vector)
void _InvokeTouchTask(const VdfOutput &dest, const VdfOutput &source)
_TouchTask(This *engine, const VdfOutput &dest, const VdfOutput &source)
TfErrorTransport Transport() const
VdfMask::Bits const & GetBits() const
Fast, compressed bit array which is capable of performing logical operations without first decompress...
bool _ProcessComputeTask(WorkTaskGraph::BaseTask *task, const VdfEvaluationState &state, const VdfNode &node, const VdfScheduleComputeTask &scheduleTask, _EvaluationStage *evaluationStage, WorkTaskGraph::BaseTask **bypass)
bool _InvokePrereqInputs(const VdfScheduleInputsTask &scheduleTask, const VdfEvaluationState &state, WorkTaskGraph::BaseTask *successor, WorkTaskGraph::BaseTask **bypass)
VdfVector * _AbsorbPublicBuffer(const VdfOutput &output, const _DataHandle dataHandle, const VdfMask &haveMask)
VDF_API InputsRange GetInputs(const VdfNode &node) const
_LeafTask(This *engine, const VdfEvaluationState &state, const VdfMaskedOutput &output, const size_t requestedIndex, Callback &callback)
const VdfInput * GetAssociatedInput() const
The task is already done.
void _RunOutput(const VdfEvaluationState &state, const VdfMaskedOutput &maskedOutput, const size_t requestedIndex, Callback &callback, WorkTaskGraph::TaskList *taskList)
void _PassBuffer(VdfExecutorBufferData *fromBuffer, VdfExecutorBufferData *toBuffer) const
void WorkParallelForN(size_t n, Fn &&callback, size_t grainSize)
VDF_API const VdfOutput * GetOutput(const OutputId &outputId) const
#define TF_DEV_AXIOM(cond)
VdfParallelTaskSync _keepTasks
void Copy(const VdfVector &rhs, const VdfMask &mask)
void Wait()
Wait on all the running tasks to complete.
const TaskIdRange GetComputeTaskIds(const VdfNode &node) const
InputDependencyRange GetPrereqInputDependencies(const VdfScheduleInputsTask &task) const
void ResetExecutorCache(const VdfMask &mask)
void _SpawnOrBypass(WorkTaskGraph::BaseTask *task, WorkTaskGraph::BaseTask **bypass)
#define PEE_TRACE_SCOPE(x)
Contains a specification of how to execute a particular VdfNetwork.
const VdfExecutorInterface & _executor
size_t GetNumUniqueInputDependencies() const
bool HasBeenInterrupted() const
void MarkDone(const size_t idx)
bool _InvokePrepTask(const VdfScheduleComputeTask &scheduleTask, const VdfEvaluationState &state, const VdfNode &node, WorkTaskGraph::BaseTask *successor)
VDF_API const TfToken & GetName() const
uint32_t VdfScheduleTaskId
GLsizei GLsizei GLchar * source
_ComputeTask(This *engine, const VdfEvaluationState &state, const VdfNode &node, VdfScheduleTaskId taskIndex)
VdfScheduleTaskIndex prepTaskIndex
const VdfSchedule & GetSchedule() const
bool _InvokeInputsTask(const VdfScheduleComputeTask &scheduleTask, const VdfEvaluationState &state, const VdfNode &node, WorkTaskGraph::BaseTask *successor, WorkTaskGraph::BaseTask **bypass)
VDF_API void Merge(const VdfVector &rhs, const VdfMask::Bits &bits)
void SetExecutorCacheMask(const VdfMask &mask)
void _ComputeNode(const VdfScheduleComputeTask &scheduleTask, const VdfEvaluationState &state, const VdfNode &node)
void _PrepareNode(const VdfEvaluationState &state, const VdfNode &node)
size_t GetLastSet() const
This object is responsible for storing the executor buffer data, comprised of the executor cache vect...
size_t GetNumPrepTasks() const
VdfParallelExecutorEngineBase(const VdfParallelExecutorEngineBase &)=delete
bool _InvokeKeepTask(const VdfScheduleTaskIndex idx, const VdfNode &node, const VdfEvaluationState &state, WorkTaskGraph::BaseTask *successor, WorkTaskGraph::BaseTask **bypass)
void _CreateScratchCache(const VdfOutput &output, const _DataHandle dataHandle, const VdfMask &mask, VdfExecutorBufferData *scratchBuffer)
F * AllocateTask(Args &&...args)
VDF_API std::string GetDebugName() const
GLboolean GLboolean GLboolean b
bool _ProcessKeepTask(WorkTaskGraph::BaseTask *task, const VdfEvaluationState &state, const VdfNode &node, _EvaluationStage *evaluationStage, WorkTaskGraph::BaseTask **bypass)
void _SpawnRequestedTasks(const VdfEvaluationState &state, const VdfNode &node, WorkTaskGraph::BaseTask *successor, WorkTaskGraph::BaseTask **bypass)
TfType GetType() const
Returns the type of this spec.
bool _HasDetectedInterruption() const
bool _IsInputDependencyCached(VdfScheduleInputDependencyUniqueIndex uniqueIndex, const VdfOutput &output, const VdfMask &mask)
void _RecycleAsContinuation()
Class to hold on to an externally owned output and a mask.
const VdfScheduleTaskIndex GetKeepTaskIndex(const VdfNode &node) const
DataManager::DataHandle _DataHandle
size_t GetNumKeepTasks() const
_ComputeAllTask(This *engine, const VdfEvaluationState &state, const VdfNode &node)
void _EvaluateNode(const VdfScheduleComputeTask &scheduleTask, const VdfEvaluationState &state, const VdfNode &node, WorkTaskGraph::BaseTask *successor)
const VdfMask & GetExecutorCacheMask() const
#define TF_PY_ALLOW_THREADS_IN_SCOPE()
uint32_t _EvaluationStage
WorkTaskGraph::BaseTask * execute() override
VDF_API const VdfMask & GetRequestMask(const OutputId &outputId) const
TF_API const std::string & GetTypeName() const
void _PassThroughNode(const VdfScheduleComputeTask &scheduleTask, const VdfEvaluationState &state, const VdfNode &node)
bool _InvokeComputeTasks(const Iterable &tasks, const VdfEvaluationState &state, const VdfNode &node, WorkTaskGraph::BaseTask *successor, WorkTaskGraph::BaseTask **bypass)
bool _InvokeComputeOrKeepTasks(const VdfScheduleInputDependency &input, const VdfEvaluationState &state, WorkTaskGraph::BaseTask *successor, WorkTaskGraph::BaseTask **bypass)
WorkTaskGraph::BaseTask * execute()
ImageBuf OIIO_API max(Image_or_Const A, Image_or_Const B, ROI roi={}, int nthreads=0)
bool _InvokeComputeTask(const VdfScheduleTaskId taskIndex, const VdfEvaluationState &state, const VdfNode &node, WorkTaskGraph::BaseTask *successor, WorkTaskGraph::BaseTask **bypass)
const VdfVector * GetOutputValue(const VdfOutput &output, const VdfMask &mask) const
#define PXR_NAMESPACE_CLOSE_SCOPE
bool VdfScheduleTaskIsInvalid(uint32_t task)
VDF_API const VdfOutput * VdfGetAssociatedSourceOutput(const VdfOutput &output)
void _PrepareReadWriteBuffer(const VdfOutput &output, const VdfSchedule::OutputId outputId, const VdfMask &mask, const VdfSchedule &schedule, VdfExecutorBufferData *privateBuffer)
F * AllocateChild(Args &&...args)
uint32_t VdfScheduleTaskIndex
WorkTaskGraph::BaseTask * execute()
bool _ProcessInputsTask(WorkTaskGraph::BaseTask *task, const VdfEvaluationState &state, const VdfNode &node, const VdfScheduleInputsTask &scheduleTask, _EvaluationStage *evaluationStage, WorkTaskGraph::BaseTask **bypass)
VdfScheduleTaskIndex inputsTaskIndex
uint32_t VdfScheduleInputDependencyUniqueIndex
virtual ~VdfParallelExecutorEngineBase()
void _PassOrCopyBuffer(const VdfOutput &output, const VdfOutput &source, const VdfMask &inputMask, const VdfSchedule &schedule, VdfExecutorBufferData *privateBuffer)
Abstract base class for classes that execute a VdfNetwork to compute a requested set of values...
std::unique_ptr< std::atomic< uint8_t >[]> _dependencyState
VdfVector * GetExecutorCache() const
bool _DetectInterruption(const VdfEvaluationState &state, const VdfNode &node)
VdfOutput * GetOutput() const
std::vector< BaseTask * > TaskList
Container for allocated tasks to be spawned.
tbb::concurrent_vector< TfErrorTransport > _errors
VdfParallelTaskSync _computeTasks
InputDependencyRange GetOptionalInputDependencies(const VdfScheduleInputsTask &task) const
VdfScheduleComputeTaskFlags flags
Instances of this class are used to synchronize dynamic, acyclic task graphs, allowing tasks to claim...
WorkIsolatingDispatcher _isolatingDispatcher
VdfScheduleTaskIndex invocationIndex
bool _ProcessLeafTask(WorkTaskGraph::BaseTask *task, const VdfEvaluationState &state, const VdfMaskedOutput &maskedOutput, const size_t requestedIndex, Callback &callback, _EvaluationStage *evaluationStage, WorkTaskGraph::BaseTask **bypass)
VdfParallelTaskSync _prepTasks
VDF_API VdfScheduleInputDependencyUniqueIndex GetUniqueIndex(const OutputId outputId) const
void _PostTransportedErrors()
tbb::enumerable_thread_specific< TaskList > TaskLists
Thread-local storage for allocated tasks to be spawned.