88
99#include " pxr/exec/exec/authoredValueInvalidationResult.h"
1010#include " pxr/exec/exec/cacheView.h"
11+ #include " pxr/exec/exec/debugCodes.h"
1112#include " pxr/exec/exec/definitionRegistry.h"
1213#include " pxr/exec/exec/disconnectedInputsInvalidationResult.h"
1314#include " pxr/exec/exec/program.h"
3334#include " pxr/exec/vdf/scheduler.h"
3435#include " pxr/exec/vdf/types.h"
3536
37+ #include < string_view>
38+
3639PXR_NAMESPACE_OPEN_SCOPE
3740
3841Exec_RequestImpl::Exec_RequestImpl (
@@ -56,11 +59,46 @@ Exec_RequestImpl::~Exec_RequestImpl()
5659 }
5760}
5861
62+ static void
63+ _OutputInvalidationResultDebugMsg (
64+ const std::string_view label,
65+ const ExecRequestIndexSet &indices)
66+ {
67+ TF_DEBUG (EXEC_REQUEST_INVALIDATION).Msg (
68+ " [%.*s]\n " ,
69+ static_cast <int >(label.size ()), label.data ());
70+
71+ std::vector<int > sortedIndices (indices.begin (), indices.end ());
72+ std::sort (sortedIndices.begin (), sortedIndices.end ());
73+ TF_DEBUG (EXEC_REQUEST_INVALIDATION).Msg (" indices:" );
74+ for (const int index : sortedIndices) {
75+ TF_DEBUG (EXEC_REQUEST_INVALIDATION).Msg (" %d" , index);
76+ }
77+ }
78+
79+ static void
80+ _OutputInvalidationResultDebugMsg (
81+ const std::string_view label,
82+ const ExecRequestIndexSet &indices,
83+ const EfTimeInterval &interval)
84+ {
85+ _OutputInvalidationResultDebugMsg (label, indices);
86+
87+ TF_DEBUG (EXEC_REQUEST_INVALIDATION).Msg (
88+ " \n interval: %s\n " ,
89+ interval.GetAsString ().c_str ());
90+ }
91+
5992void
6093Exec_RequestImpl::DidInvalidateComputedValues (
6194 const Exec_AuthoredValueInvalidationResult &invalidationResult)
6295{
6396 if (!_valueCallback || _leafOutputs.empty ()) {
97+ TF_DEBUG (EXEC_REQUEST_INVALIDATION).Msg (
98+ " [%s] %s\n " , TF_FUNC_NAME ().c_str (),
99+ !_valueCallback
100+ ? " No value invalidation callback"
101+ : " Request has not been prepared" );
64102 return ;
65103 }
66104
@@ -91,6 +129,10 @@ Exec_RequestImpl::DidInvalidateComputedValues(
91129 // Only invoke the invalidation callback if there are any invalid indices
92130 // from this request.
93131 if (!invalidIndices.empty ()) {
132+ if (ARCH_UNLIKELY (TfDebug::IsEnabled (EXEC_REQUEST_INVALIDATION))) {
133+ _OutputInvalidationResultDebugMsg (
134+ TF_FUNC_NAME (), invalidIndices, invalidInterval);
135+ }
94136 TRACE_FUNCTION_SCOPE (" value invalidation callback" );
95137 _valueCallback (invalidIndices, invalidInterval);
96138 }
@@ -101,6 +143,11 @@ Exec_RequestImpl::DidInvalidateComputedValues(
101143 const Exec_DisconnectedInputsInvalidationResult &invalidationResult)
102144{
103145 if (!_valueCallback || _leafOutputs.empty ()) {
146+ TF_DEBUG (EXEC_REQUEST_INVALIDATION).Msg (
147+ " [%s] %s\n " , TF_FUNC_NAME ().c_str (),
148+ !_valueCallback
149+ ? " No value invalidation callback"
150+ : " Request has not been prepared" );
104151 return ;
105152 }
106153
@@ -130,6 +177,10 @@ Exec_RequestImpl::DidInvalidateComputedValues(
130177 // Only invoke the invalidation callback if there are any invalid indices
131178 // from this request.
132179 if (!invalidIndices.empty ()) {
180+ if (ARCH_UNLIKELY (TfDebug::IsEnabled (EXEC_REQUEST_INVALIDATION))) {
181+ _OutputInvalidationResultDebugMsg (
182+ TF_FUNC_NAME (), invalidIndices, invalidInterval);
183+ }
133184 TRACE_FUNCTION_SCOPE (" value invalidation callback" );
134185 _valueCallback (invalidIndices, invalidInterval);
135186 }
@@ -140,6 +191,11 @@ Exec_RequestImpl::DidChangeTime(
140191 const Exec_TimeChangeInvalidationResult &invalidationResult)
141192{
142193 if (!_timeCallback || _leafOutputs.empty ()) {
194+ TF_DEBUG (EXEC_REQUEST_INVALIDATION).Msg (
195+ " [%s] %s\n " , TF_FUNC_NAME ().c_str (),
196+ !_valueCallback
197+ ? " No time invalidation callback"
198+ : " Request has not been prepared" );
143199 return ;
144200 }
145201
@@ -168,11 +224,48 @@ Exec_RequestImpl::DidChangeTime(
168224 // Only invoke the invalidation callback if there are any invalid indices
169225 // from this request.
170226 if (!invalidIndices.empty ()) {
227+ if (ARCH_UNLIKELY (TfDebug::IsEnabled (EXEC_REQUEST_INVALIDATION))) {
228+ _OutputInvalidationResultDebugMsg (
229+ TF_FUNC_NAME (), invalidIndices);
230+ }
171231 TRACE_FUNCTION_SCOPE (" time change callback" );
172232 _timeCallback (invalidIndices);
173233 }
174234}
175235
236+ void
237+ Exec_RequestImpl::Expire ()
238+ {
239+ TF_DEBUG (EXEC_REQUEST_EXPIRATION)
240+ .Msg (" [%s] Expiring request %p\n " , TF_FUNC_NAME ().c_str (), this );
241+
242+ if (!TF_VERIFY (_system, " Attempted to expire an expired request" )) {
243+ return ;
244+ }
245+
246+ TRACE_FUNCTION ();
247+
248+ // If the request has never been prepared (or just contains no values to
249+ // compute) there's no need push index-specific expiration.
250+ if (!_leafOutputs.empty ()) {
251+ TRACE_FUNCTION_SCOPE (" Expiring all indices" );
252+
253+ ExecRequestIndexSet allIndices;
254+ const size_t numLeafOutputs = _leafOutputs.size ();
255+ allIndices.reserve (numLeafOutputs);
256+ for (size_t i=0 ; i<numLeafOutputs; ++i) {
257+ allIndices.insert (i);
258+ }
259+ _ExpireIndices (allIndices);
260+ }
261+
262+ // Because we're expiring the whole request, we do more than just expiring
263+ // all the indices. It is guaranteed that no more invalidation can occur
264+ // so the request removes itself from the system's tracker and drops all
265+ // its data structures.
266+ _Discard ();
267+ }
268+
176269// Returns a value extractor suitable for the given value key according to its
177270// computation definition.
178271//
@@ -271,7 +364,9 @@ Exec_RequestImpl::_Compile(
271364 _computeRequest.reset ();
272365 _schedule.reset ();
273366 _lastInvalidatedIndices.Resize (_leafOutputs.size ());
274- _lastInvalidatedIndices.ClearAll ();
367+ // These bits are set instead of cleared because clients are notified of
368+ // invalidation only after computing values.
369+ _lastInvalidatedIndices.SetAll ();
275370
276371 // We must greedily build the leaf node to index map. When requests are
277372 // informed of network edits, some leaf nodes may have already been
@@ -284,6 +379,12 @@ Exec_RequestImpl::_Schedule()
284379{
285380 TfAutoMallocTag tag (" Exec" , __ARCH_PRETTY_FUNCTION__);
286381
382+ // If there's nothing to compute, there's nothing to schedule.
383+ if (_leafOutputs.empty ()) {
384+ _schedule.reset ();
385+ return ;
386+ }
387+
287388 // The compute request only needs to be rebuilt if the compiled outputs
288389 // change.
289390 if (!_computeRequest) {
@@ -312,7 +413,7 @@ Exec_RequestImpl::_Compute()
312413{
313414 TfAutoMallocTag tag (" Exec" , __ARCH_PRETTY_FUNCTION__);
314415
315- if (!TF_VERIFY (_system)) {
416+ if (!TF_VERIFY (_system) || !_schedule ) {
316417 return Exec_CacheView ();
317418 }
318419
@@ -338,6 +439,60 @@ Exec_RequestImpl::_RequiresCompilation() const
338439 || (TF_VERIFY (_system) && _system->_HasPendingRecompilation ());
339440}
340441
442+ void
443+ Exec_RequestImpl::_ExpireIndices (const ExecRequestIndexSet &expired)
444+ {
445+ // If the request has never been prepared, there's no invalidation to push
446+ // to clients.
447+ if (_leafOutputs.empty ()) {
448+ return ;
449+ }
450+
451+ TRACE_FUNCTION ();
452+
453+ TF_DEBUG (EXEC_REQUEST_EXPIRATION)
454+ .Msg (" [%s] Expiring %zu indices\n " ,
455+ TF_FUNC_NAME ().c_str (), expired.size ());
456+
457+ const bool isNewlyInvalidInterval =
458+ !_lastInvalidatedInterval.IsFullInterval ();
459+ ExecRequestIndexSet newlyInvalidIndices;
460+ newlyInvalidIndices.reserve (expired.size ());
461+ for (const int idx : expired) {
462+ if (isNewlyInvalidInterval || !_lastInvalidatedIndices.IsSet (idx)) {
463+ newlyInvalidIndices.insert (idx);
464+ _lastInvalidatedIndices.Set (idx);
465+ }
466+ _leafOutputs[idx] = VdfMaskedOutput ();
467+ _extractors[idx] = Exec_ValueExtractor ();
468+ }
469+ _schedule.reset ();
470+ _computeRequest.reset ();
471+
472+ if (_valueCallback && !newlyInvalidIndices.empty ()) {
473+ if (ARCH_UNLIKELY (TfDebug::IsEnabled (EXEC_REQUEST_INVALIDATION))) {
474+ _OutputInvalidationResultDebugMsg (
475+ TF_FUNC_NAME (), expired, EfTimeInterval::GetFullInterval ());
476+ }
477+ TRACE_FUNCTION_SCOPE (" value invalidation callback" );
478+ _valueCallback (expired, EfTimeInterval::GetFullInterval ());
479+ }
480+
481+ _lastInvalidatedInterval = EfTimeInterval::GetFullInterval ();
482+ }
483+
484+ void
485+ Exec_RequestImpl::_Discard ()
486+ {
487+ _system->_requestTracker ->Remove (this );
488+ _system = nullptr ;
489+ TfReset (_leafOutputs);
490+ TfReset (_extractors);
491+ TfReset (_leafNodeToIndex);
492+ _valueCallback = nullptr ;
493+ _timeCallback = nullptr ;
494+ }
495+
341496void
342497Exec_RequestImpl::_BuildLeafNodeToIndexMap ()
343498{
@@ -355,6 +510,9 @@ Exec_RequestImpl::_BuildLeafNodeToIndexMap()
355510 _leafNodeToIndex.reserve (_leafOutputs.size ());
356511 for (size_t i = 0 ; i < _leafOutputs.size (); ++i) {
357512 const VdfMaskedOutput &sourceOutput = _leafOutputs[i];
513+ if (!sourceOutput) {
514+ continue ;
515+ }
358516 for (const VdfConnection *const connection :
359517 sourceOutput.GetOutput ()->GetConnections ()) {
360518 const VdfNode &targetNode = connection->GetTargetNode ();
0 commit comments