HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
APEX_COW.h
Go to the documentation of this file.
1 /*
2  * PROPRIETARY INFORMATION. This software is proprietary to
3  * Side Effects Software Inc., and is not to be reproduced,
4  * transmitted, or disclosed in any way without written permission.
5  *
6  * NAME: APEX_COW.h (APEX Library, C++)
7  *
8  * COMMENTS:
9  */
10 
11 #ifndef __APEX_COW_H__
12 #define __APEX_COW_H__
13 
14 #include "APEX_API.h"
15 
16 #include <GU/GU_Detail.h>
17 #include <GU/GU_DetailHandle.h>
18 #include <GA/GA_Types.h>
19 
20 #include <UT/UT_Array.h>
21 #include <UT/UT_COWValue.h> // IWYU pragma: export
22 #include <UT/UT_Tracing.h>
23 
24 #include <SYS/SYS_TypeDecorate.h>
25 #include <SYS/SYS_Types.h>
26 
27 #include <utility>
28 #include <stddef.h>
29 
30 namespace apex
31 {
32 template <typename T>
33 class APEX_COWHandle : public UT_COWValue<T>
34 {
35 public:
37 
38  using Parent::Parent;
39 
41  {
42  return static_cast<const APEX_COWHandle<T>&>(Parent::getStaticEmpty());
43  }
44 };
45 
46 template <typename T>
47 class ApexArray : public APEX_COWHandle<UT_Array<T>>
48 {
49 public:
50  using value_type = T;
51 
52  void operator=(const UT_Array<T> &other)
53  {
54  clear();
55  append(other.data(), other.size());
56  }
57 
58  exint append(const T &v)
59  {
60  return (**this).append(v);
61  }
62 
63  exint append(const T &v, bool check_dup)
64  {
65  return (**this).append(v, check_dup);
66  }
67 
68  void append(const T *v, exint num_entries)
69  {
70  // Avoid possibly deduplicating from the global empty if
71  // this operation would do nothing.
72  if (num_entries == 0)
73  return;
74  return (**this).append(v, num_entries);
75  }
76 
77  template <typename... S>
79  {
80  return (**this).emplace_back(s...);
81  }
82 
84  {
85  if (capacity == 0)
86  clear();
87  else
88  (**this).setCapacity(capacity);
89  }
90  void setSize(exint newsize) { (**this).setSize(newsize); }
91  exint size() const { return (**this).size(); }
92  exint capacity() const { return (**this).capacity(); }
93 
94  T *data() { return (**this).data(); }
95  const T *data() const { return (**this).data(); }
96 
97  T &operator[](exint index) { return (**this)[index]; }
98  const T &operator[](exint index) const { return (**this)[index]; }
99 
100  void clear()
101  {
102  // Directly assign to the global empty array to avoid a makeUnique call just to
103  // throw away the resulting copy.
104  APEX_COWHandle<UT_Array<T>>::operator=({});
105  }
106  bool isEmpty() const { return (**this).isEmpty(); }
107 
108  exint find(const T &val, exint start = 0) const { return (**this).find(val, start); }
109 
110  using iterator = typename UT_Array<T>::iterator;
114 
115  iterator begin() { return (**this).begin(); }
116  iterator end() { return (**this).end(); }
117  const_iterator begin() const { return (**this).begin(); }
118  const_iterator end() const { return (**this).end(); }
119  iterator rbegin() { return (**this).rbegin(); }
120  iterator rend() { return (**this).rend(); }
121  const_iterator rbegin() const { return (**this).rbegin(); }
122  const_iterator rend() const { return (**this).rend(); }
123 };
124 
125 // Overload for custom formatting of ApexArray<T> with UTformat. It's will be found via ADL.
126 template <typename T>
127 inline size_t
128 format(char *buffer, size_t bufsize, const ApexArray<T> &v)
129 {
130  return format(buffer, bufsize, v.peek());
131 }
132 
133 /// APEX wrapper for geometry via GU_DetailHandle. Keeps a much stronger COW
134 /// guarantee than a raw GU_DetailHandle by using preserve requests to track
135 /// active APEX-side references to geo. Additionally, it lazily clears geo
136 /// when reset to its default state via operator= to keep operations like
137 /// replaceWith and stashAll fast.
139 {
140 public:
141  /// Construct an empty ApexGeometry.
143  : // Simply setting stashed to true lets the rest of the class act
144  // as if the geo is empty - this also means we don't have to allocate
145  // a detail upfront as one will be created on-demand.
146  myIsStashed(true)
147  {
148  }
149  /// Construct an ApexGeometry referencing a GU_DetailHandle
150  ApexGeometry(const GU_DetailHandle &gdh) : myHandle(gdh)
151  {
152  // Since we're not stashed, ensure we've added a preserveRequest.
153  myHandle.addPreserveRequest();
154  }
155  /// Construct an ApexGeometry referencing a GU_DetailHandle
156  ApexGeometry(GU_DetailHandle &&gdh) : myHandle(std::move(gdh))
157  {
158  // Since we're not stashed, ensure we've added a preserveRequest.
159  // NOTE: we still do this for move-construct on a raw GU_DetailHandle because
160  // we don't know if the caller had a preserveRequest (and regardless, cannot tell
161  // it to discard its request directly).
162  myHandle.addPreserveRequest();
163  }
165  {
166  if (other.myIsStashed)
167  // If constructing from an empty geometry, just make another empty
168  // geometry.
169  myIsStashed = true;
170  else
171  {
172  // Otherwise, reference the incoming geometry and add a new preserveRequest
173  // to add another APEX reference to it.
174  myHandle = other.myHandle;
175  myHandle.addPreserveRequest();
176  }
177  }
178 
179  ApexGeometry(ApexGeometry &&other) noexcept
180  {
181  if (other.myIsStashed)
182  // If constructing from an empty geometry, just make another empty
183  // geometry.
184  myIsStashed = true;
185  else
186  {
187  // Otherwise steal the handle from the incoming object, setting
188  // it as stashed to ensure it doesn't try to remove a preserveRequest.
189  myHandle = std::move(other.myHandle);
190  other.myIsStashed = true;
191  }
192  }
193 
195  {
196  if (&other == this)
197  return *this;
198  // If we're assigning a stashed geo (i.e. empty), and
199  // we last were modified (so want to keep stale data),
200  // just remove the preserveRequest if one was present.
201  if (other.myIsStashed && myLastModified)
202  {
203  if (!std::exchange(myIsStashed, true))
204  myHandle.removePreserveRequest();
205  }
206  // Otherwise, if assigning a stashed geo in general, mark
207  // us as stashed, remove any preserveRequest if we weren't
208  // already stashed, and clear the last modified and handle.
209  else if (other.myIsStashed)
210  {
211  if (!std::exchange(myIsStashed, true))
212  myHandle.removePreserveRequest();
213  myHandle = {};
214  myLastModified = false;
215  }
216  // Otherwise, we're assining an actual geometry, so we should
217  // reference it. Ensure we remove the preserveRequest if we had
218  // one, mark us no longer stashed, then alias the handle and add
219  // a new preserveRequest since we're not stashed.
220  else
221  {
222  if (!std::exchange(myIsStashed, false))
223  myHandle.removePreserveRequest();
224  myHandle = other.myHandle;
225  myHandle.addPreserveRequest();
226  myLastModified = false;
227  }
228  return *this;
229  }
230 
232  {
233  if (&other == this)
234  return *this;
235  // If we're assigning a stashed geo (i.e. empty), and
236  // we last were modified (so want to keep stale data),
237  // just remove the preserveRequest if one was present.
238  if (other.myIsStashed && myLastModified)
239  {
240  if (!std::exchange(myIsStashed, true))
241  myHandle.removePreserveRequest();
242  }
243  // Otherwise, if assigning a stashed geo in general, mark
244  // us as stashed, remove any preserveRequest if we weren't
245  // already stashed, and clear the last modified and handle.
246  else if (other.myIsStashed)
247  {
248  if (!std::exchange(myIsStashed, true))
249  myHandle.removePreserveRequest();
250  myHandle = {};
251  myLastModified = false;
252  }
253  // Otherwise, we're assigning an actual geometry, so we should
254  // reference it. Ensure we remove the preserveRequest if
255  // we had one, mark us as no longer stashed, steal the old handle
256  // (which should already have a preserveRequest as the other geo
257  // is not stashed), and set the provided geometry to be stashed
258  // so it does not try to remove a preserveRequest when it goes
259  // out of scope.
260  else
261  {
262  if (!std::exchange(myIsStashed, false))
263  myHandle.removePreserveRequest();
264  myHandle = std::move(other.myHandle);
265  other.myIsStashed = true;
266  myLastModified = false;
267  }
268  return *this;
269  }
270 
272  {
273  if (!myIsStashed) // The handle only has a preserve request if we are not stashed.
274  myHandle.removePreserveRequest();
275  }
276 
277  /// Returns whether the geometry is unique - if so, asUnsafeHandle can be used safely.
278  bool isUnique() const { return myIsStashed || myHandle.getPreserveRequest() <= 1; }
279 
280  /// Ensures the underlying GU_DetailHandle is not being aliased. If for_overwrite is passed,
281  /// the caller does not care about the actual contents of the geometry - it can be cleared if
282  /// that would be faster than performing a copy, and stale data should be kept if doing so would
283  /// not cause aliasing.
284  void makeUnique(bool for_overwrite = false)
285  {
286  // First, ensure we know this is no longer a referenced geo.
287  myLastModified = true;
288  // Also, set myIsStashed back to false immediately as we're going to create an actual
289  // geometry object.
290  bool was_stashed = std::exchange(myIsStashed, false);
291  // If we were stashed and had an invalid detail (such as from default construction), we can
292  // immediately just create a new empty one and be finished.
293  if (was_stashed && myHandle.isNull())
294  {
295  myHandle.allocateAndSet(new GU_Detail());
296  myHandle.addPreserveRequest();
297  return;
298  }
299  // If we were originally stashed, we need to un-stash: re-reference whatever geo we had
300  // originally so that there are the correct number of references to it for the unique check.
301  if (was_stashed)
302  {
303  myHandle.addPreserveRequest();
304  if (!isUnique())
305  {
306  // If we're not unique, our stashed geometry has a preserve request
307  // somewhere else so we need to start from a new geometry
308  utZoneScopedN("ApexGeometry::makeUnique::stash::allocateGeoemtry");
309  myHandle.removePreserveRequest();
310  myHandle.allocateAndSet(new GU_Detail());
311  myHandle.addPreserveRequest();
312  }
313  else if (!for_overwrite)
314  {
315  // Otherwise, we clear the geometry if it's note being overwritten
316  utZoneScopedN("ApexGeometry::makeUnique::stash::reset");
317  myHandle.gdpNC()->clear();
318  }
319 
320  return;
321  }
322 
323  // Otherwise, ensure we're unique
324  if (isUnique())
325  return;
326 
327  // If we're not unique, we need to make unique.
328  if (for_overwrite)
329  {
330  // If for_overwrite was requested, we can do this cheeply by
331  // creating a new geometry.
332  utZoneScopedN("ApexGeometry::makeUnique::allocateGeoemtry");
333  myHandle.removePreserveRequest();
334  myHandle.allocateAndSet(new GU_Detail());
335  myHandle.addPreserveRequest();
336  }
337  else
338  {
339  // Otherwise, if none of that shortcuts the slow copy, perform
340  // a duplicateGeometry - remove the old preserveRequest and
341  // add a new one to the fresh detail.
342  utZoneScopedN("ApexGeometry::makeUnique");
344  .removePreserveRequest();
345  myHandle.addPreserveRequest();
346  }
347 
348  }
349  /// Return a reference to the underlying GU_Detail, ensuring uniqueness.
351  {
352  makeUnique();
353  return *myHandle.gdpNC();
354  }
355  /// Return a const reference to the underlying GU_Detail, without ensuring uniqueness.
356  inline const GU_Detail &operator*() const
357  {
358  if (myIsStashed)
359  return *getStaticEmpty();
360  return *myHandle.gdp();
361  }
362  /// Access the underlying GU_Detail, ensuring uniqueness
364  {
365  makeUnique();
366  return myHandle.gdpNC();
367  }
368 
369  /// Access the underlying const GU_Detail, without ensuring uniqueness
370  inline const GU_Detail *operator->() const
371  {
372  if (myIsStashed)
373  return getStaticEmpty();
374  return myHandle.gdp();
375  }
376 
377  /// Access the underlying GU_Detail, ensuring uniqueness.
378  GU_Detail *gdp() { return operator->(); }
379 
380  /// Access the underlying GU_Detail. If the geometry is currently stashed, this can return the
381  /// old stale geometry so long as it is unique. Otherwise, in any case where the normal access
382  /// logic would perform a copy, this will return an empty geometry object instead - only
383  /// returning the actual current data for an unstashed geometry if it is already unique.
384  ///
385  /// This is useful for operations that will completely overwrite the contained geometry.
387  {
388  makeUnique(true);
389  return myHandle.gdpNC();
390  }
391 
392  /// Access the underlying const GU_Detail, without ensuring uniqueness
393  inline const GU_Detail *gdp() const { return operator->(); }
394 
395  /// Access the underlying const GU_Detail, without ensuring uniqueness
396  inline const GU_Detail &peek() const { return **this; }
397 
398  /// Access the underlying const GU_Detail *, without ensuring uniqueness
399  inline const GU_Detail *peekPtr() const { return operator->(); }
400 
401  /// Access the underlying detail handle, while only ensuring it is valid (i.e. without ensuring
402  /// there are no other APEX references to it). Calling code should respect the rule that
403  /// >1 preserveRequest means that other ApexGeometry objects are expecting the geo not to
404  /// change.
405  GU_DetailHandle &asUnsafeHandle(bool for_overwrite = false)
406  {
407  if (myIsStashed)
408  {
409  myIsStashed = false;
410  if (myHandle.isNull())
411  myHandle.allocateAndSet(new GU_Detail());
412  else if (!for_overwrite)
413  myHandle.gdpNC()->clear();
414  myHandle.addPreserveRequest();
415  }
416  return myHandle;
417  }
418 
419  /// Access the underlying GU_DetailHandle while first ensuring it is unique.
421  {
422  makeUnique();
423  return myHandle;
424  }
425 
426  /// Create a GU_ConstDetailHandle aliasing the current handle; if the calling code will cast
427  /// away the const, force_copy can be set to call duplicateGeometry first (with the default
428  /// clone data ID strategy).
430  bool force_copy = false,
431  GA_DataIdStrategy explicit_strategy = GA_DATA_ID_CLONE) const
432  {
433  if (myIsStashed)
434  return *getStaticEmptyHandle();
435  if (force_copy)
436  {
437  utZoneScoped;
438  return myHandle.duplicateGeometry(explicit_strategy);
439  }
440  else
441  return myHandle;
442  }
443 
445 
446 private:
447  // Provide a statically allocated empty geometry for gdp() on a const, stashed
448  // geometry object.
449  static const GU_Detail *getStaticEmpty()
450  {
451  static const GU_Detail theEmpty{};
452  return &theEmpty;
453  }
454 
455  // Ditto, but for a detail handle.
456  static const GU_DetailHandle *getStaticEmptyHandle()
457  {
458  struct HandleInit
459  {
460  HandleInit()
461  {
462  // Initialize here to use the C++ guaranteed
463  // locking on static initialization (instead of possibly
464  // repeatedly creating detail handles from separate threads)
465  handle.allocateAndSet(new GU_Detail());
466  }
467 
468  GU_DetailHandle handle;
469  };
470 
471  static const HandleInit theEmpty;
472  return &theEmpty.handle;
473  }
474 
475  // The current geometry. We keep a preserveRequest alive on this so long as
476  // myIsStashed is not set. Additionally, it is only allowed to be invalid if
477  // myIsStashed is set - otherwise it must point to _some_ geometry.
478  GU_DetailHandle myHandle;
479 
480  bool myLastModified = false; // If makeUnique was called and we have not been referenced since,
481  // this is set. Keeps track of buffers we're interested in stashing.
482 
483  bool myIsStashed = false; // If set, the geo is considered 'stashed': if the underlying handle
484  // is unique, we can return it instead of an actually empty geometry
485  // when requested.
486 };
487 
488 // Overload for custom formatting of ApexGeometry with UTformat. It's will be found via ADL.
489 APEX_API size_t format(char *buffer, size_t bufsize, const ApexGeometry &v);
490 
491 // Overload for custom formatting of ApexArray<ApexGeometry> with UTformat for ADL searches.
492 template <>
493 APEX_API size_t
494 format<ApexGeometry>(char *buffer, size_t bufsize, const ApexArray<ApexGeometry> &v);
495 
496 } // end namespace apex
497 
498 // The move constructor is only non-trivial because it needs to ensure the destructor of the
499 // moved-out-of object doesn't try to clean anything up - if it's guaranteed to never be called
500 // a memmove can be used instead.
502 
503 #endif // header guard
ApexGeometry(GU_DetailHandle &&gdh)
Construct an ApexGeometry referencing a GU_DetailHandle.
Definition: APEX_COW.h:156
exint append(const T &v, bool check_dup)
Definition: APEX_COW.h:63
iterator begin()
Definition: APEX_COW.h:115
ApexGeometry()
Construct an empty ApexGeometry.
Definition: APEX_COW.h:142
int getPreserveRequest() const
typename UT_Array< T >::const_reverse_iterator const_reverse_iterator
Definition: APEX_COW.h:113
GLenum GLuint GLsizei bufsize
Definition: glcorearb.h:1818
bool isEmpty() const
Definition: APEX_COW.h:106
typename UT_Array< T >::reverse_iterator reverse_iterator
Definition: APEX_COW.h:112
GU_Detail * gdpForOverwrite()
Definition: APEX_COW.h:386
size_t format(char *buffer, size_t bufsize, const ApexArray< T > &v)
Definition: APEX_COW.h:128
GA_DataIdStrategy
Definition: GA_Types.h:211
const GLdouble * v
Definition: glcorearb.h:837
bool isUnique() const
Returns whether the geometry is unique - if so, asUnsafeHandle can be used safely.
Definition: APEX_COW.h:278
GLuint start
Definition: glcorearb.h:475
#define utZoneScoped
Definition: UT_Tracing.h:211
static const UT_COWValue< T > & getStaticEmpty()
Definition: UT_COWValue.h:138
#define APEX_API
Definition: APEX_API.h:21
int64 exint
Definition: SYS_Types.h:125
GLdouble s
Definition: glad.h:3009
const_iterator begin() const
Definition: APEX_COW.h:117
const GU_Detail * gdp() const
GU_ConstDetailHandle asConstHandle(bool force_copy=false, GA_DataIdStrategy explicit_strategy=GA_DATA_ID_CLONE) const
Definition: APEX_COW.h:429
typename UT_Array< T >::iterator iterator
Definition: APEX_COW.h:110
GLuint buffer
Definition: glcorearb.h:660
exint size() const
Definition: UT_Array.h:653
GU_DetailHandle duplicateGeometry(GA_DataIdStrategy data_id_strategy=GA_DATA_ID_BUMP) const
const GU_Detail & operator*() const
Return a const reference to the underlying GU_Detail, without ensuring uniqueness.
Definition: APEX_COW.h:356
void setCapacity(exint capacity)
Definition: APEX_COW.h:83
ApexGeometry(ApexGeometry &&other) noexcept
Definition: APEX_COW.h:179
void allocateAndSet(GU_Detail *gdp, bool own=true)
void addPreserveRequest()
typename UT_Array< T >::const_iterator const_iterator
Definition: APEX_COW.h:111
void setSize(exint newsize)
Definition: APEX_COW.h:90
#define utZoneScopedN(name)
Definition: UT_Tracing.h:212
GU_DetailHandle & asUnsafeHandle(bool for_overwrite=false)
Definition: APEX_COW.h:405
iterator rend()
Definition: APEX_COW.h:120
const GU_Detail * gdp() const
Access the underlying const GU_Detail, without ensuring uniqueness.
Definition: APEX_COW.h:393
GU_Detail * operator->()
Access the underlying GU_Detail, ensuring uniqueness.
Definition: APEX_COW.h:363
const GU_Detail * peekPtr() const
Access the underlying const GU_Detail *, without ensuring uniqueness.
Definition: APEX_COW.h:399
exint emplace_back(S &&...s)
Definition: APEX_COW.h:78
T & operator[](exint index)
Definition: APEX_COW.h:97
GLint GLint GLsizei GLint GLenum format
Definition: glcorearb.h:108
ApexGeometry & operator=(const ApexGeometry &other)
Definition: APEX_COW.h:194
GU_DetailHandle & asSafeHandle()
Access the underlying GU_DetailHandle while first ensuring it is unique.
Definition: APEX_COW.h:420
exint append(const T &v)
Definition: APEX_COW.h:58
const GU_Detail * operator->() const
Access the underlying const GU_Detail, without ensuring uniqueness.
Definition: APEX_COW.h:370
exint size() const
Definition: APEX_COW.h:91
ApexGeometry & operator=(ApexGeometry &&other) noexcept
Definition: APEX_COW.h:231
iterator rbegin()
Definition: APEX_COW.h:119
iterator end()
Definition: APEX_COW.h:116
const_iterator rend() const
Definition: APEX_COW.h:122
GU_Detail * gdpNC()
static APEX_API const ApexGeometry & getStaticEmptyGeometry()
void clear()
Definition: GA_Detail.h:127
void append(const T *v, exint num_entries)
Definition: APEX_COW.h:68
ApexGeometry(const GU_DetailHandle &gdh)
Construct an ApexGeometry referencing a GU_DetailHandle.
Definition: APEX_COW.h:150
GU_Detail & operator*()
Return a reference to the underlying GU_Detail, ensuring uniqueness.
Definition: APEX_COW.h:350
const T * data() const
Definition: APEX_COW.h:95
T * data()
Definition: UT_Array.h:849
VULKAN_HPP_CONSTEXPR_14 VULKAN_HPP_INLINE T exchange(T &obj, U &&newValue)
Definition: vulkan_raii.hpp:25
const GU_Detail & peek() const
Access the underlying const GU_Detail, without ensuring uniqueness.
Definition: APEX_COW.h:396
const T & operator[](exint index) const
Definition: APEX_COW.h:98
GLuint index
Definition: glcorearb.h:786
GLuint GLfloat * val
Definition: glcorearb.h:1608
SYS_DECLARE_IS_TR(apex::ApexGeometry)
ApexGeometry(const ApexGeometry &other)
Definition: APEX_COW.h:164
exint find(const T &val, exint start=0) const
Definition: APEX_COW.h:108
const_iterator rbegin() const
Definition: APEX_COW.h:121
void operator=(const UT_Array< T > &other)
Definition: APEX_COW.h:52
static const APEX_COWHandle< T > & getAPEXStaticEmpty()
Definition: APEX_COW.h:40
void clear()
Definition: APEX_COW.h:100
void removePreserveRequest()
exint capacity() const
Definition: APEX_COW.h:92
GU_Detail * gdp()
Access the underlying GU_Detail, ensuring uniqueness.
Definition: APEX_COW.h:378
void makeUnique(bool for_overwrite=false)
Definition: APEX_COW.h:284
const_iterator end() const
Definition: APEX_COW.h:118
const UT_Array< T > & peek() const
Definition: UT_COWValue.h:129
bool isNull() const