pageCacheBasedExecutor.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_EF_PAGE_CACHE_BASED_EXECUTOR_H
8 #define PXR_EXEC_EF_PAGE_CACHE_BASED_EXECUTOR_H
9 
11 
12 #include "pxr/pxr.h"
13 
14 #include "pxr/exec/ef/loftedOutputSet.h"
17 
18 #include "pxr/base/tf/mallocTag.h"
19 #include "pxr/base/trace/trace.h"
23 #include "pxr/exec/vdf/output.h"
24 #include "pxr/exec/vdf/schedule.h"
25 #include "pxr/exec/vdf/types.h"
26 
27 PXR_NAMESPACE_OPEN_SCOPE
28 
30 
40 template <
41  template <typename> class EngineType,
42  typename DataManagerType>
44  public VdfDataManagerBasedExecutor<DataManagerType, VdfExecutorInterface>
45 {
46  typedef
48  Base;
49 
50 public:
54  EfPageCacheStorage *cacheStorage) :
55  _engine(*this, &this->_dataManager),
56  _cacheStorage(cacheStorage),
57  _currentCache(NULL)
58  {
59  // The pointer to the page cache storage container must always be valid.
60  TF_VERIFY(_cacheStorage);
61  }
62 
66 
72  virtual void SetOutputValue(
73  const VdfOutput &output,
74  const VdfVector &value,
75  const VdfMask &mask) override;
76 
80  void WillDeleteNode(const VdfNode &node);
81 
82 protected:
83  // Returns a value for the cache that flows across \p connection.
84  virtual const VdfVector *_GetInputValue(
85  const VdfConnection &connection,
86  const VdfMask &mask) const override;
87 
88  // Returns an output value for reading.
89  virtual const VdfVector *_GetOutputValueForReading(
90  const VdfOutput &output,
91  const VdfMask &mask) const override;
92 
93  // Clear all data in the local data manager.
94  virtual void _ClearData() override;
95 
96 private:
97  // Run the specified schedule.
98  virtual void _Run(
99  const VdfSchedule &schedule,
100  const VdfRequest &computeRequest,
101  VdfExecutorErrorLogger *errorLogger) override;
102 
103  // Returns \c true if the values resulting from evaluation should be stored
104  // away in the page cache.
105  bool _IsCaching() const;
106 
107  // Query the page cache for a value.
108  const VdfVector *_GetPageCacheValue(
109  const VdfOutput &output,
110  const VdfMask &mask) const;
111 
112  // Executor data invalidation
113  bool _InvalidateOutput(
114  const VdfOutput &output,
115  const VdfMask &invalidationMask) override;
116 
117  // Pre-process executor invalidation by augmenting the invalidation
118  // request to also invalidate any lofted outputs.
119  virtual bool _PreProcessInvalidation(
120  const VdfMaskedOutputVector &invalidationRequest,
121  VdfMaskedOutputVector *processedRequest) override;
122 
123  // Clear data at a specified output.
124  void _ClearDataForOutput(
125  VdfId outputId, VdfId nodeId) override;
126 
127 private:
128  // The executor engine.
129  EngineType<DataManagerType> _engine;
130 
131  // The page cache storage container.
132  EfPageCacheStorage *_cacheStorage;
133 
134  // The output value cache for the currently selected page.
135  Ef_OutputValueCache *_currentCache;
136 
137  // A set of outputs, which had their values sourced from the page cache
138  // during evaluation (or getting of output values.) We need to keep track
139  // of these outputs in order to allows us to later properly invalidate them.
140  mutable Ef_LoftedOutputSet _loftedOutputs;
141 };
142 
144 
145 template <template <typename> class EngineType, typename DataManagerType>
146 void
148  const VdfOutput &output,
149  const VdfVector &value,
150  const VdfMask &mask)
151 {
152  // Call through to the base class, to set the output value in the
153  // local data manager.
154  Base::SetOutputValue(output, value, mask);
155 
156  // If the output we are setting a new value on is the key output, then
157  // also make sure to set the currently selected page in the page cache
158  // storage container.
159  if (_cacheStorage->_IsKeyOutput(output, mask)) {
160  _currentCache = _cacheStorage->_GetOrCreateCache(value);
161  }
162 }
163 
164 template <template <typename> class EngineType, typename DataManagerType>
165 void
167  const VdfNode &node)
168 {
169  _loftedOutputs.RemoveAllOutputsForNode(node);
170 }
171 
172 /* virtual */
173 template <template <typename> class EngineType, typename DataManagerType>
174 const VdfVector *
176  const VdfConnection &connection,
177  const VdfMask &mask) const
178 {
179  // Note, this method will be called concurrently, if the engine type is
180  // a parallel engine.
181 
182  // First, look for the value in the local data manager.
183  if (const VdfVector *value =
184  Base::_dataManager.GetInputValue(connection, mask)) {
185  return value;
186  }
187 
188  // Then, query the page cache storage.
189  return _GetPageCacheValue(connection.GetSourceOutput(), mask);
190 }
191 
192 /* virtual */
193 template <template <typename> class EngineType, typename DataManagerType>
194 const VdfVector *
197  const VdfOutput &output,
198  const VdfMask &mask) const
199 {
200  // Note, this method will be called concurrently, if the engine type is
201  // a parallel engine.
202 
203  // First, look for the value in the local data manager.
204  if (const VdfVector *value =
205  Base::_dataManager.GetOutputValueForReading(
206  Base::_dataManager.GetDataHandle(output.GetId()), mask)) {
207  return value;
208  }
209 
210  // Then, query the page cache storage.
211  return _GetPageCacheValue(output, mask);
212 }
213 
214 template <template <typename> class EngineType, typename DataManagerType>
215 void
217  const VdfSchedule &schedule,
218  const VdfRequest &computeRequest,
219  VdfExecutorErrorLogger *errorLogger)
220 {
221  // If nothing has been requested, bail out early.
222  if (computeRequest.IsEmpty()) {
223  return;
224  }
225 
226  TRACE_FUNCTION();
227  TfAutoMallocTag tag("Ef", "EfPageCacheBasedExecutor::_Run");
228 
229  // Make sure the lofted node references array is sufficiently large.
230  _loftedOutputs.Resize(*schedule.GetNetwork());
231 
232  // If caching is enabled, run the schedule with a callback that writes each
233  // computed output value to the page cache.
234  if (TF_VERIFY(_currentCache) && _IsCaching()) {
235  const DataManagerType &dataManager = Base::_dataManager;
236  const VdfRequest &cacheableRequest =
237  _cacheStorage->GetCacheableRequest(computeRequest);
238  VdfRequest::IndexedView cacheableView(cacheableRequest);
239  Ef_OutputValueCache *cache = _currentCache;
240  EfPageCacheStorage *storage = _cacheStorage;
241 
242  _engine.RunSchedule(
243  schedule, computeRequest, errorLogger,
244  [&dataManager, &cacheableView, cache, storage]
245  (const VdfMaskedOutput &mo, size_t requestedIndex) {
246  // Lookup the value in the local data manager. This is the
247  // value to store away in the page cache. Bail out if no such
248  // value exists, or if the output is not cacheable.
249  const VdfVector *value = dataManager.GetOutputValueForReading(
250  dataManager.GetDataHandle(mo.GetOutput()->GetId()),
251  mo.GetMask());
252  if (!value || !cacheableView.Get(requestedIndex)) {
253  return;
254  }
255 
256  // If the output has already been cached, bail out.
257  {
258  Ef_OutputValueCache::SharedAccess access(cache);
259  if (access.GetValue(*mo.GetOutput(), mo.GetMask())) {
260  return;
261  }
262  }
263 
264  // Attempt to cache the value in the page cache.
266  storage->_Commit(mo, *value, &access);
267  });
268  }
269 
270  // If caching is not enabled, run the schedule without a callback.
271  else {
272  _engine.RunSchedule(schedule, computeRequest, errorLogger);
273  }
274 }
275 
276 template <template <typename> class EngineType, typename DataManagerType>
277 bool
279 {
280  return
281  _cacheStorage->IsEnabled() &&
283 }
284 
285 template <template <typename> class EngineType, typename DataManagerType>
286 const VdfVector *
288  const VdfOutput &output,
289  const VdfMask &mask) const
290 {
291  // Note, this method will be called concurrently, if the engine type is
292  // a parallel engine.
293 
294  // We only do this when the executor is running, because we never want
295  // external clients to receive pointers to data in the page cache, in order
296  // to avoid data races.
297  if (!_currentCache) {
298  return nullptr;
299  }
300 
301  // Obtain shared read access to the current cache.
302  Ef_OutputValueCache::SharedAccess cacheAccess(_currentCache);
303 
304  // Lookup the value in the current cache.
305  if (const VdfVector *cachedValue = cacheAccess.GetValue(output, mask)) {
306  // Mark this output as having been lofted into the data manager. We
307  // cannot return a cache hit if this fails.
308  if (_loftedOutputs.Add(output, mask)) {
309  // Touch the output, so that invalidation will be able to
310  // propagate down in the network.
311  Base::_TouchOutput(output);
312 
313  // Return the cached value.
314  return cachedValue;
315  }
316  }
317 
318  // No value found.
319  return nullptr;
320 }
321 
322 /* virtual */
323 template <template <typename> class EngineType, typename DataManagerType>
324 bool
326  const VdfOutput &output,
327  const VdfMask &invalidationMask)
328 {
329  // Call into the base class for output invalidation.
330  if (Base::_InvalidateOutput(output, invalidationMask)) {
331  // If some data has been invalidated, make sure to also remove the
332  // bits from the lofted output.
333  _loftedOutputs.Remove(
334  output.GetId(), output.GetNode().GetId(), invalidationMask);
335 
336  // Some data has been invalidated.
337  return true;
338  }
339 
340  // Nothing has been invalidated.
341  return false;
342 }
343 
344 /* virtual */
345 template <template <typename> class EngineType, typename DataManagerType>
346 bool
348  const VdfMaskedOutputVector &invalidationRequest,
349  VdfMaskedOutputVector *processedRequest)
350 {
351  // Bail out, if there are no lofted outputs. Return false to indicate
352  // that the originally supplied invalidationRequest shall be used.
353  const size_t numLoftedOutputs = _loftedOutputs.GetSize();
354  if (numLoftedOutputs == 0) {
355  return false;
356  }
357 
358  TRACE_FUNCTION();
359 
360  // Find all outputs depenending on the originally supplied request.
361  const VdfOutputToMaskMap &deps =
362  _cacheStorage->_FindDependencies(invalidationRequest);
363 
364  // If there are no dependent outputs, bail out.
365  if (deps.empty()) {
366  return false;
367  }
368 
369  // Reserve space to guarantee that all outputs will fit without needing to
370  // reallocate.
371  processedRequest->reserve(
372  invalidationRequest.size() +
373  std::min(numLoftedOutputs, deps.size()));
374 
375  // If the invalidation request has not been augmented with lofted outputs,
376  // we can simply bail out an instead use the originally supplied
377  // invalidationRequest.
378  _loftedOutputs.CollectLoftedDependencies(deps, processedRequest);
379  if (processedRequest->empty()) {
380  return false;
381  }
382 
383  // Otherwise, add all the outputs from the original invalidationRequest
384  // to the new invalidation request, which now also contains all the
385  // dependent lofted outputs.
386  processedRequest->insert(
387  processedRequest->end(),
388  invalidationRequest.begin(), invalidationRequest.end());
389 
390  // The invalidation request has been modified. Return true to tell
391  // executor invalidation to use the processed request instead of the
392  // originally supplied request.
393  return true;
394 }
395 
396 /* virtual */
397 template <template <typename> class EngineType, typename DataManagerType>
398 void
400  const VdfId outputId, const VdfId nodeId)
401 {
402  Base::_ClearDataForOutput(outputId, nodeId);
403  _loftedOutputs.Remove(outputId, nodeId, VdfMask());
404 }
405 
406 /* virtual */
407 template <template <typename> class EngineType, typename DataManagerType>
408 void
410 {
411  _loftedOutputs.Clear();
412 }
413 
414 PXR_NAMESPACE_CLOSE_SCOPE
415 
416 #endif
A class that fully represents a connection between two VdfNodes.
Definition: connection.h:29
const VdfMask & GetMask() const
Returns the VdfMask.
Definition: maskedOutput.h:64
A client may instantiate an object of this class and set it in an executor, to collect errors that ma...
Base class for executors that use a data manager.
Manages a page cache and provides methods for invalidation of cached values.
VdfId GetId() const
The unique id of this output.
Definition: output.h:100
virtual const VdfVector * _GetOutputValueForReading(const VdfOutput &output, const VdfMask &mask) const override
Returns an output value for reading.
This is the base class for all nodes in a VdfNetwork.
Definition: node.h:52
VdfId GetId() const
Returns the unique id of this node in its network.
Definition: node.h:116
A VdfMask is placed on connections to specify the data flowing through them.
Definition: mask.h:36
This accessor grants shared read access to the cache, preventing any concurrent write access.
An output-to-value storage for caching.
This class is used to abstract away knowledge of the cache data used for each node.
Definition: vector.h:58
virtual ~EfPageCacheBasedExecutor()
Destructor.
Executes a VdfNetwork to compute a requested set of values.
const VdfOutput & GetSourceOutput() const
Returns the output (ie. source) for this connection.
Definition: connection.h:63
const VdfNetwork * GetNetwork() const
Returns the network for this schedule.
Definition: schedule.h:178
static EF_API bool HasReachedMemoryLimit()
Returns true, if the upper memory limit has been reached, and the object is no longer allowed to allo...
Contains a specification of how to execute a particular VdfNetwork.
Definition: schedule.h:40
#define TF_VERIFY(cond, format,...)
Checks a condition and reports an error if it evaluates false.
Definition: diagnostic.h:267
virtual void _ClearData() override
Virtual implementation of ClearData, which can optionally be overridden by derived classes.
#define TRACE_FUNCTION()
Records a timestamp when constructed and a timespan event when destructed, using the name of the func...
Definition: trace.h:30
Scoped (i.e.
Definition: mallocTag.h:251
An accessor that provides exclusive read/write access to the cache.
std::unordered_map< const VdfOutput *, VdfMask, TfHash > VdfOutputToMaskMap
A map from output pointer to mask.
Definition: types.h:104
Class to hold on to an externally owned output and a mask.
Definition: maskedOutput.h:31
A VdfOutput represents an output on a node.
Definition: output.h:31
const VdfNode & GetNode() const
Returns the owning node for this output.
Definition: output.h:57
VdfOutput * GetOutput() const
Returns the VdfOutput.
Definition: maskedOutput.h:52
virtual const VdfVector * _GetInputValue(const VdfConnection &connection, const VdfMask &mask) const override
Returns value for the cache that flows across connection.
EfPageCacheBasedExecutor(EfPageCacheStorage *cacheStorage)
Constructor.
void WillDeleteNode(const VdfNode &node)
Removes any internal references to node upon deleting the node from the VdfNetwork.
Tracks the set of lofted outputs (outputs whose values were sourced from the page cache during evalua...
virtual void SetOutputValue(const VdfOutput &output, const VdfVector &value, const VdfMask &mask) override
Set an output value.
uint64_t VdfId
The unique identifier type for Vdf objects.
Definition: types.h:107