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