HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
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 
10 ///\file
11 
12 #include "pxr/pxr.h"
13 
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 
28 
30 
31 ///////////////////////////////////////////////////////////////////////////////
32 ///
33 /// \class EfPageCacheBasedExecutor
34 ///
35 /// \brief Executes a VdfNetwork to compute a requested set of values. Caches
36 /// the computed data in a EfPageCacheStorage container and recalls
37 /// existing data using a page specified via the currently set value
38 /// on the key output.
39 ///
40 template <
41  template <typename> class EngineType,
42  typename DataManagerType>
44  public VdfDataManagerBasedExecutor<DataManagerType, VdfExecutorInterface>
45 {
46  typedef
48  Base;
49 
50 public:
51  /// Constructor.
52  ///
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 
63  /// Destructor.
64  ///
66 
67  /// Set an output value.
68  ///
69  /// Changes the currently selected page in the page cache, if \p output
70  /// is a key output in the EfPageCacheStorage container.
71  ///
72  virtual void SetOutputValue(
73  const VdfOutput &output,
74  const VdfVector &value,
75  const VdfMask &mask) override;
76 
77  /// Removes any internal references to \p node upon deleting the
78  /// node from the VdfNetwork.
79  ///
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 
143 ///////////////////////////////////////////////////////////////////////////////
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  TfAutoMallocTag2 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  {
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 
415 
416 #endif
EfPageCacheBasedExecutor(EfPageCacheStorage *cacheStorage)
virtual void _ClearData() override
const VdfNetwork * GetNetwork() const
Definition: schedule.h:178
Manages a page cache and provides methods for invalidation of cached values.
getFileOption("OpenEXR:storage") storage
Definition: HDK_Image.dox:276
#define PXR_NAMESPACE_OPEN_SCOPE
Definition: pxr.h:73
GLsizei const GLfloat * value
Definition: glcorearb.h:824
VDF_API bool IsEmpty() const
Definition: node.h:52
VdfId GetId() const
Definition: output.h:100
const VdfMask & GetMask() const
Definition: maskedOutput.h:64
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.
Definition: mask.h:36
**But if you need a or simply need to know when the task has note that the like this
Definition: thread.h:626
const VdfNode & GetNode() const
Definition: output.h:57
An output-to-value storage for caching. The class provides accessor types for thread-safe, as well as unprotected access.
virtual const VdfVector * _GetInputValue(const VdfConnection &connection, const VdfMask &mask) const override
Executes a VdfNetwork to compute a requested set of values. Caches the computed data in a EfPageCache...
Contains a specification of how to execute a particular VdfNetwork.
Definition: schedule.h:40
GLuint GLint GLboolean GLint GLenum access
Definition: glcorearb.h:2222
VdfId GetId() const
Definition: node.h:116
GLint GLuint mask
Definition: glcorearb.h:124
#define TRACE_FUNCTION()
Definition: trace.h:30
static EF_API bool HasReachedMemoryLimit()
void WillDeleteNode(const VdfNode &node)
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
const VdfOutput & GetSourceOutput() const
Returns the output (ie. source) for this connection.
Definition: connection.h:63
#define PXR_NAMESPACE_CLOSE_SCOPE
Definition: pxr.h:74
virtual const VdfVector * _GetOutputValueForReading(const VdfOutput &output, const VdfMask &mask) const override
std::vector< VdfMaskedOutput > VdfMaskedOutputVector
virtual void SetOutputValue(const VdfOutput &output, const VdfVector &value, const VdfMask &mask) override
VdfOutput * GetOutput() const
Definition: maskedOutput.h:52
uint64_t VdfId
The unique identifier type for Vdf objects.
Definition: types.h:107