executorBufferData.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_EXECUTOR_BUFFER_DATA_H
8 #define PXR_EXEC_VDF_EXECUTOR_BUFFER_DATA_H
9 
11 
12 #include "pxr/pxr.h"
13 
14 #include "pxr/exec/vdf/api.h"
15 #include "pxr/exec/vdf/mask.h"
16 #include "pxr/exec/vdf/smblData.h"
17 
18 #include <atomic>
19 
20 PXR_NAMESPACE_OPEN_SCOPE
21 
22 class VdfOutputSpec;
23 class VdfVector;
24 
32 {
33 public:
37  VdfExecutorBufferData &operator=(const VdfExecutorBufferData &) = delete;
38 
41  VdfExecutorBufferData() : _cacheAndFlags() {}
42 
45  VDF_API
47 
50  VDF_API
51  void Reset();
52 
55  VDF_API
56  void Clone(VdfExecutorBufferData *dest) const;
57 
60  inline VdfVector *CreateExecutorCache(const VdfOutputSpec &spec);
61 
66  const VdfOutputSpec &spec,
67  const VdfMask::Bits &bits);
68 
72 
75  inline VdfVector *GetExecutorCache() const;
76 
79  inline const VdfMask &GetExecutorCacheMask() const;
80 
83  inline void SetExecutorCacheMask(const VdfMask &mask);
84 
88  inline void ResetExecutorCache(const VdfMask &mask);
89 
92  inline void ResetExecutorCache();
93 
97  VDF_API
98  void RetainExecutorCache(const VdfOutputSpec &spec, VdfSMBLData *smblData);
99 
104  VDF_API
106 
110  inline bool HasOwnership() const;
111 
115  inline void YieldOwnership();
116 
120  inline void YieldOwnership(VdfVector *v);
121 
127  inline void TakeOwnership(VdfVector *v);
128 
129 private:
130 
131  // Flag denotes whether the VdfVector is owned by this buffer.
132  static constexpr uintptr_t _IsOwnedFlag = 1 << 0;
133 
134  // Flag denotes whether the cache is occupied, rather than merely allocated.
135  static constexpr uintptr_t _IsOccupiedFlag = 1 << 1;
136 
137  // Masks the flags in the cache pointer.
138  static constexpr uintptr_t _FlagsMask = _IsOwnedFlag | _IsOccupiedFlag;
139 
140  // Casts the integer value to a pointer value.
141  static VdfVector *_CastToPointer(uintptr_t v) {
142  return reinterpret_cast<VdfVector *>(reinterpret_cast<void *>(v));
143  }
144 
145  // Casts the pointer value to an integer value.
146  static uintptr_t _CastToInteger(VdfVector *v) {
147  return reinterpret_cast<uintptr_t>(reinterpret_cast<void *>(v));
148  }
149 
150  // Returns the cache pointer from the cache-with-flags value.
151  static VdfVector *_GetCache(VdfVector *cacheAndFlags) {
152  return _CastToPointer(_CastToInteger(cacheAndFlags) & ~_FlagsMask);
153  }
154 
155  // Retuns the flags from the cache-with-flags value.
156  static uintptr_t _GetFlags(VdfVector *cacheAndFlags) {
157  return _CastToInteger(cacheAndFlags) & _FlagsMask;
158  }
159 
160  // Sets the flags on the cache-with-flags value.
161  static VdfVector *_SetFlags(
162  VdfVector *cacheAndFlags, uintptr_t flags) {
163  return _CastToPointer(_CastToInteger(cacheAndFlags) | flags);
164  }
165 
166  // Unsets the flags on the cache-with-flags value.
167  static VdfVector *_UnsetFlags(
168  VdfVector *cacheAndFlags, uintptr_t flags) {
169  return _CastToPointer(_CastToInteger(cacheAndFlags) & ~flags);
170  }
171 
172  // Free all the data allocated by this object
173  VDF_API
174  void _Free();
175 
176  // The VdfVector, as well as two bits denoting ownership and cache
177  // occupation.
178  // Note, even though this is an atomic, VdfExecutorBufferData makes no
179  // thread-safety guarantees beyond concurrent read access to the data. The
180  // only reason this is an atomic is so that the flags can be stored along
181  // with the cache pointer, while still being able to modify the flags
182  // concurrently to reading the cache pointer (as if those were two separate
183  // member variables) without technically triggering undefined behavior.
184  std::atomic<VdfVector *> _cacheAndFlags;
185 
186  // Mask of the entries computed in _cacheAndFlags.
187  VdfMask _mask;
188 
189 };
190 
192 
193 VdfVector *
195 {
196  VdfVector *cacheAndFlags = _cacheAndFlags.load(std::memory_order_acquire);
197 
198  // If this buffer maintains ownership over a previously allocated vector,
199  // make it occupy the executor cache.
200  if (_GetCache(cacheAndFlags) && _GetFlags(cacheAndFlags) & _IsOwnedFlag) {
201  _cacheAndFlags.store(
202  _SetFlags(cacheAndFlags, _IsOccupiedFlag),
203  std::memory_order_release);
204  return _GetCache(cacheAndFlags);
205  }
206 
207  // If the buffer does not have ownership of the cache, or has not
208  // previously allocated a cache, allocate a new one. Take ownership
209  // of the new buffer.
210  VdfVector *newCache = spec.AllocateCache();
211  _cacheAndFlags.store(
212  _SetFlags(newCache, _IsOwnedFlag | _IsOccupiedFlag),
213  std::memory_order_release);
214 
215  // Return the newly allocated cache.
216  return newCache;
217 }
218 
219 VdfVector *
221  const VdfOutputSpec &spec,
222  const VdfMask::Bits &bits)
223 {
224  VdfVector *v = CreateExecutorCache(spec);
225  spec.ResizeCache(v, bits);
226  return v;
227 }
228 
229 VdfVector *
231 {
232  VdfVector *cacheAndFlags = _cacheAndFlags.load(std::memory_order_acquire);
233  VdfVector *rhsCacheAndFlags =
234  rhs->_cacheAndFlags.load(std::memory_order_acquire);
235 
236  _cacheAndFlags.store(rhsCacheAndFlags, std::memory_order_release);
237  rhs->_cacheAndFlags.store(cacheAndFlags, std::memory_order_release);
238 
239  return _GetCache(rhsCacheAndFlags);
240 }
241 
242 VdfVector *
244 {
245  VdfVector *cacheAndFlags = _cacheAndFlags.load(std::memory_order_acquire);
246  return (_GetFlags(cacheAndFlags) & _IsOccupiedFlag)
247  ? _GetCache(cacheAndFlags)
248  : nullptr;
249 }
250 
251 const VdfMask &
253 {
254  return _mask;
255 }
256 
257 void
259 {
260  // Untoggle the occupation flag of the executor cache without
261  // modifying the ownership flag.
262  VdfVector *cacheAndFlags = _cacheAndFlags.load(std::memory_order_acquire);
263  if (_GetFlags(cacheAndFlags) & _IsOccupiedFlag) {
264  _cacheAndFlags.store(
265  _UnsetFlags(cacheAndFlags, _IsOccupiedFlag),
266  std::memory_order_release);
267  }
268 
269  // Reset the executor cache mask to the mask provided.
270  if (_mask != mask) {
271  _mask = mask;
272  }
273 }
274 
275 void
277 {
279 }
280 
281 void
283 {
284  _mask = mask;
285 }
286 
287 bool
289 {
290  VdfVector *cacheAndFlags = _cacheAndFlags.load(std::memory_order_relaxed);
291  return (_GetFlags(cacheAndFlags) & _IsOwnedFlag) != 0;
292 }
293 
294 void
296 {
297  VdfVector *cacheAndFlags = _cacheAndFlags.load(std::memory_order_acquire);
298  _cacheAndFlags.store(
299  _UnsetFlags(cacheAndFlags, _IsOwnedFlag), std::memory_order_release);
300 }
301 
302 void
304 {
305  // The call to _Free won't actually deallocate the cache if it's not
306  // owned by this instance, so it's okay to "self-assign" in that case.
308 
309  _Free();
310  _cacheAndFlags.store(
311  _SetFlags(v, _IsOccupiedFlag), std::memory_order_release);
312 }
313 
314 void
316 {
317  // The call to _Free won't actually deallocate the cache if it's not
318  // owned by this instance, so it's okay to "self-assign" in that case.
320 
321  _Free();
322  _cacheAndFlags.store(
323  _SetFlags(v, _IsOccupiedFlag | _IsOwnedFlag),
324  std::memory_order_release);
325 }
326 
327 PXR_NAMESPACE_CLOSE_SCOPE
328 
329 #endif
#define TF_DEV_AXIOM(cond)
The same as TF_AXIOM, but compiled only in dev builds.
Definition: diagnostic.h:206
void TakeOwnership(VdfVector *v)
Assumes ownership of the given vector.
VDF_API VdfVector * AllocateCache() const
Allocate a new VdfVector with this spec's type.
VDF_API ~VdfExecutorBufferData()
Destructor.
A VdfMask is placed on connections to specify the data flowing through them.
Definition: mask.h:36
Fast, compressed bit array which is capable of performing logical operations without first decompress...
This class is used to abstract away knowledge of the cache data used for each node.
Definition: vector.h:58
const VdfMask & GetExecutorCacheMask() const
Get the available mask.
void ResetExecutorCache()
Reset the executor cache without releasing any memory.
VdfVector * GetExecutorCache() const
Returns the executor cache stored at this buffer data instance.
VDF_API VdfMask ReleaseExecutorCache(VdfSMBLData *smblData)
Merges the executor cache previously retained in smblData into this cache and releases the SMBL data.
VdfSMBLData holds per-output data that is meant to be consumed by the executor.
Definition: smblData.h:30
VDF_API void RetainExecutorCache(const VdfOutputSpec &spec, VdfSMBLData *smblData)
Takes the existing executor cache and retains it within the existing VdfSMBLData object.
This object is responsible for storing the executor buffer data, comprised of the executor cache vect...
void YieldOwnership()
Yields ownership of the internal vector, i.e.
VdfVector * SwapExecutorCache(VdfExecutorBufferData *rhs)
Swaps the executor cache at this buffer, with that of another buffer.
void SetExecutorCacheMask(const VdfMask &mask)
Sets the available mask.
VDF_API void Clone(VdfExecutorBufferData *dest) const
Clones this VdfExecutorBufferData instance to dest.
VdfVector * CreateExecutorCache(const VdfOutputSpec &spec)
Creates a new executor cache for this buffer.
bool HasOwnership() const
Returns true if the buffer owns the executor cache.
VDF_API void ResizeCache(VdfVector *vector, const VdfMask::Bits &bits) const
Resize an existing VdfVector to accomodate all the data set in the bits.
A VdfOuptutSpec describes an output connector.
Definition: outputSpec.h:39
VDF_API void Reset()
Reset the instance to its original, newly constructed state.
VdfExecutorBufferData()
Constructor.