HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
APEX_Buffer.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_Buffer.h (APEX Library, C++)
7  *
8  * COMMENTS:
9  */
10 
11 #ifndef __APEX_BUFFER_H__
12 #define __APEX_BUFFER_H__
13 
14 #include "APEX_API.h"
15 #include "APEX_COW.h"
16 #include "APEX_Types.h"
17 
18 #include <UT/UT_Array.h>
19 #include <UT/UT_Assert.h>
20 #include <UT/UT_Debug.h>
21 #include <UT/UT_Format.h>
22 #include <UT/UT_NonCopyable.h>
23 #include <UT/UT_StringArray.h>
24 #include <UT/UT_StringHolder.h>
25 #include <UT/UT_WorkBuffer.h>
26 #include <SYS/SYS_Types.h>
27 
28 #include <utility>
29 
30 /*
31 
32 We currently support data on the buffer that requires a node compilation
33 to initialize (such as SOP_VerbParms etc.), this could very likely be
34 refactored to be placed in a node's callback::RunData argument, but still yet
35 to investigate fully.
36 
37 A possible thought here is to have a special tag for data types such as this
38 and separate out their storage so we keep the data that cannot be mem copied
39 away from those that can.
40 
41 */
42 
43 namespace apex
44 {
45 
46 class APEX_Argument;
47 
48 /*
49 Any type information (size_t) is taken directly from the APEX_TypeDefinition subclass.
50 */
51 
52 /// Expandable storage of arbitrary runtime types for APEX program state. Indexed by @ref APEX_Buffer.
54 {
55 public:
57  {
58  myTypeDefinition = typedfn;
59  }
60 
62  {
63  clear();
64  }
65  void clear();
66  void allocate(exint size);
67 
68  void *storage() { return myData; }
69  void *data(exint index = 0);
70  const APEX_TypeDefinitionBase *typeDefinition() { return myTypeDefinition; }
71 
72 private:
73  // There's some redundancy here if the parent APEX_Buffer
74  // stored these in a map... we could just store
75  // a flat array of APEX_TypedBuffer's on the APEX_Buffer
76  // and do any lookups using this member.
77  const APEX_TypeDefinitionBase *myTypeDefinition = nullptr;
78 
79  // Our actual data storage - std::pmr::monotonic_allocator is maybe worth looking into?
80  // as these buffers will only grow during graph compilation and will not ever resize
81  // until they are wiped clean by a graph definition change.
82  void *myData = nullptr;
83 };
84 
86 #define APEX_INVALID_DATAID -1
87 /*
88 The buffer should not deal with typeIds?
89 These are not guaranteed and so can only be resolved by a given
90 APEX_Registry, or collection thereof. So any APIs dealing with typeIds
91 need to resolve the APEX_TypeDefinition subclass against a registry before
92 querying the buffer.
93 */
94 
95 /// Holds all of the memory for execution state of a compiled APEX graph.
97 {
98 public:
100  {}
101 
102  /// Get a new unique data ID.
103  static APEX_DataID nextDataId();
104 
105  void clear()
106  {
107  myTypedBuffers.clear();
108  myIsLocked = false;
109  }
110 
111  /// Get the type-specific storage for a given type, or @c nullptr if not found.
113  {
114  for (auto &buf : myTypedBuffers)
115  {
116  if (buf.typeDefinition() == type_defn)
117  return &buf;
118  }
119  return nullptr;
120  }
121 
123  {
124  if (index < 0 || index >= myTypedBuffers.size())
125  return nullptr;
126  return &myTypedBuffers[index];
127  }
128 
129  /// Get the index at which a given type is stored in this buffer, or -1 if not present.
131  {
132  exint result = 0;
133  for (auto &buf : myTypedBuffers)
134  {
135  if (buf.typeDefinition() == type_defn)
136  {
137  break;
138  }
139  result++;
140  }
141  if (result >= myTypedBuffers.size())
142  return -1;
143  return result;
144  }
145 
146  /// Allocate a type-specific storage buffer of the given type of the given size. Returns the index
147  /// of the new storage.
149  {
150  exint type_index = typeIndex(type_defn);
151  if (type_index > -1)
152  {
153  UTdebugPrint("Attempt to allocate multiple buffers of the same type!");
154  return type_index;
155  }
156  exint t_buf_idx = myTypedBuffers.emplace_back(type_defn);
157  myTypedBuffers[t_buf_idx].allocate(size);
158  return t_buf_idx;
159  }
160 
161  /// Extend the type-specific storage buffer of the given type by one element and return an ApexArgument tracking it.
162  APEX_Argument append(const APEX_TypeDefinitionBase *type_defn);
163 
164  /// Set the value of the given argument in this buffer to the value. The type of the argument must support copying.
165  void set(APEX_Argument &arg, void *value);
166  /// Prevent further modifications to the layout of this buffer.
167  void lock() { myIsLocked = true; }
168  // This relies on the source of the APEX_TypeDefinition to be static
169  // UT_Map<APEX_TypeDefinitionBase *, APEX_TypedBuffer> myTypedBuffers;
170 private:
171  UT_Array<APEX_TypedBuffer> myTypedBuffers;
172  bool myIsLocked = false;
173 };
174 
175 /// Represents a location for a single object inside of an APEX_Buffer.
177 {
178 public:
179  APEX_Buffer *buffer = nullptr;
180  const APEX_TypeDefinitionBase *type_defn = nullptr;
181  exint offset = -1;
182 
183  bool isValid() const
184  {
185  return buffer && type_defn && (offset > -1);
186  }
187 
188  void *getVoidPtr()
189  {
190  if (!buffer)
191  return nullptr;
192  APEX_TypedBuffer *typed_buffer = buffer->findTypedBuffer(type_defn);
193  if (!typed_buffer || offset < 0)
194  return nullptr;
195  return typed_buffer->data(offset);
196  }
197 
198  const void *getVoidPtr() const
199  {
200  if (!buffer)
201  return nullptr;
202  APEX_TypedBuffer *typed_buffer = buffer->findTypedBuffer(type_defn);
203  if (!typed_buffer || offset < 0)
204  return nullptr;
205  return typed_buffer->data(offset);
206  }
207 };
208 
209 /// Represents a location for a single object inside of an APEX_Buffer while keeping track of some notion
210 /// of the data's freshness. Used by parameter dictionaries for cheap output caching.
212 {
213 public:
215 
216  APEX_DataID dataId() const { return myDataId; }
217  void bumpDataId() { myDataId = APEX_Buffer::nextDataId(); }
218  void copyDataId(const APEX_TrackedArgument &other) { myDataId = other.dataId(); }
219 };
220 
221 /// Check if a callback argument holds an instance of the given type.
222 template <typename T>
223 static bool
224 isType(const APEX_Argument &arg)
225 {
226  const APEX_TypeDefinitionBase *test_type = findTypeDef<T>();
227  return arg.type_defn == test_type;
228 }
229 
230 /// Return the address of the data contained in the passed argument if it is of the provided type,
231 /// otherwise return nullptr. APEX equivalent of @c dynamic_cast.
232 template <typename T>
233 static T *
234 castArg(APEX_Argument *arg)
235 {
236  if (!isType<T>(*arg))
237  return nullptr;
238  void *arg_ptr = arg->getVoidPtr();
239  return reinterpret_cast<T *>(arg_ptr);
240 }
241 
242 /// Return the address of the data contained in the passed argument if it is of the provided type,
243 /// otherwise return nullptr. APEX equivalent of @c dynamic_cast.
244 template <typename T>
245 static const T *
246 castArg(const APEX_Argument *arg)
247 {
248  if (!isType<T>(*arg))
249  return nullptr;
250  const void *arg_ptr = arg->getVoidPtr();
251  return reinterpret_cast<const T *>(arg_ptr);
252 }
253 
254 /// Safely copy data from one argument to another. Returns true if the copy actually occurred.
255 static bool
256 copyArgData(APEX_Argument *dst, const APEX_Argument *src, bool allow_implicit_conversion=false)
257 {
258  UT_ASSERT(dst->isValid());
259  UT_ASSERT(src->isValid());
260 
261  void *dst_ptr = dst->getVoidPtr();
262  const void *src_ptr = src->getVoidPtr();
263  if (dst->type_defn != src->type_defn)
264  {
265  if (!allow_implicit_conversion)
266  return false;
267 
268  if (isType<Bool>(*dst) && isType<Int>(*src))
269  {
270  // Int to bool conversion
271  Int val_int = *castArg<Int>(src);
272  Bool val_bool = val_int != 0;
273  Bool &dst_val = *castArg<Bool>(dst);
274  dst_val = val_bool;
275  return true;
276  }
277  else if (isType<Int>(*dst) && isType<Bool>(*src))
278  {
279  // Bool to int conversion
280  Bool val_bool = *castArg<Bool>(src);
281  Int val_int = val_bool? 1 : 0;
282  Int &dst_val = *castArg<Int>(dst);
283  dst_val = val_int;
284  return true;
285  }
286 
287  return false;
288  }
289  if (!dst->type_defn->isCopyable())
290  return false;
291  if (dst_ptr == src_ptr)
292  return false;
293  dst->type_defn->setData(dst_ptr, src_ptr);
294  return true;
295 }
296 
297 template <typename T>
298 static inline bool
299 APEXformatArgT(
300  UT::Format::ArgValue &fmt_arg,
301  const APEX_Argument *arg)
302 {
303  {
304  const T *val = castArg<T>(arg);
305  if (val)
306  {
307  fmt_arg = *val;
308  return true;
309  }
310  }
311 
312  {
313  using ArrayT = ApexArray<T>;
314  const ArrayT *val = castArg<ArrayT>(arg);
315  if (val)
316  {
317  fmt_arg = *val;
318  return true;
319  }
320  }
321 
322  return false;
323 }
324 
325 static inline bool
326 APEXformatArg(
327  UT::Format::ArgValue &fmt_arg,
328  const APEX_Argument *arg)
329 {
330  if (!arg)
331  return false;
332 
333  if (APEXformatArgT<String>(fmt_arg, arg))
334  return true;
335  if (APEXformatArgT<Bool>(fmt_arg, arg))
336  return true;
337  if (APEXformatArgT<Int>(fmt_arg, arg))
338  return true;
339  if (APEXformatArgT<Float>(fmt_arg, arg))
340  return true;
341  if (APEXformatArgT<Vector2>(fmt_arg, arg))
342  return true;
343  if (APEXformatArgT<Vector3>(fmt_arg, arg))
344  return true;
345  if (APEXformatArgT<Vector4>(fmt_arg, arg))
346  return true;
347  if (APEXformatArgT<Matrix3>(fmt_arg, arg))
348  return true;
349  if (APEXformatArgT<Matrix4>(fmt_arg, arg))
350  return true;
351  if (APEXformatArgT<Geometry>(fmt_arg, arg))
352  return true;
353 
354  return false;
355 }
356 
357 static inline UT_StringHolder
358 APEXformatArg(const APEX_Argument *arg)
359 {
360  UT::Format::ArgValue fmt_arg(0);
361  if (!APEXformatArg(fmt_arg, arg))
362  return "";
363 
366  return buf.buffer();
367 }
368 
369 static inline void
370 APEXformatString(
371  String &result,
372  const String &format_str,
374 {
375  UT_StringArray tmp_args;
376  UT_WorkBuffer tmp;
377 
379  for (const APEX_Argument *arg : args)
380  {
381  // We might get invalid args, if our input was promoted to a subnet input that
382  // has not been connected. We skip these.
383  if (!arg->isValid())
384  continue;
385 
386  UT::Format::ArgValue fmt_arg(0);
387  if (APEXformatArg(fmt_arg, arg))
388  {
389  fmt_args.emplace_back(fmt_arg);
390  continue;
391  }
392 
393  // Fallback to just printing the type name.
394  tmp = "<";
395  tmp += arg->type_defn->repr();
396  tmp += ">";
397  tmp_args.emplace_back(std::move(tmp));
398  fmt_args.emplace_back(tmp_args.last());
399  }
400 
401  UT_WorkBuffer output;
402  output.appendFormatByArray(format_str, fmt_args, /*report_errors*/false);
403  result = std::move(output);
404 }
405 
406 } // end namespace apex
407 
408 #endif // __APEX_BUFFER_H__
T & last()
Definition: UT_Array.h:823
#define UTdebugPrint(...)
Definition: UT_Debug.h:146
GLenum GLuint GLenum GLsizei const GLchar * buf
Definition: glcorearb.h:2540
APEX_DataID dataId() const
Definition: APEX_Buffer.h:216
static APEX_DataID nextDataId()
Get a new unique data ID.
const APEX_TypeDefinitionBase * typeDefinition()
Definition: APEX_Buffer.h:70
GLboolean * data
Definition: glcorearb.h:131
GLsizei const GLfloat * value
Definition: glcorearb.h:824
Expandable storage of arbitrary runtime types for APEX program state. Indexed by APEX_Buffer.
Definition: APEX_Buffer.h:53
#define APEX_API
Definition: APEX_API.h:21
int64 exint
Definition: SYS_Types.h:125
SYS_FORCE_INLINE const char * buffer() const
**But if you need a result
Definition: thread.h:622
void * data(exint index=0)
auto arg(const Char *name, const T &arg) -> detail::named_arg< Char, T >
Definition: core.h:1859
GLuint buffer
Definition: glcorearb.h:660
Holds all of the memory for execution state of a compiled APEX graph.
Definition: APEX_Buffer.h:96
GLintptr offset
Definition: glcorearb.h:665
exint emplace_back(S &&...s)
Definition: UT_ArrayImpl.h:769
int64 APEX_DataID
Definition: APEX_Buffer.h:85
constexpr auto set(type rhs) -> int
Definition: core.h:610
APEX_TypedBuffer(const APEX_TypeDefinitionBase *typedfn)
Definition: APEX_Buffer.h:56
long long int64
Definition: SYS_Types.h:116
UT_StringHolder String
Definition: APEX_Include.h:63
void copyDataId(const APEX_TrackedArgument &other)
Definition: APEX_Buffer.h:218
APEX_TypedBuffer * findTypedBuffer(const APEX_TypeDefinitionBase *type_defn)
Get the type-specific storage for a given type, or nullptr if not found.
Definition: APEX_Buffer.h:112
GLsizeiptr size
Definition: glcorearb.h:664
const void * getVoidPtr() const
Definition: APEX_Buffer.h:198
GLenum GLenum dst
Definition: glcorearb.h:1793
exint Int
Definition: APEX_Include.h:61
void lock()
Prevent further modifications to the layout of this buffer.
Definition: APEX_Buffer.h:167
GLuint index
Definition: glcorearb.h:786
GLuint GLfloat * val
Definition: glcorearb.h:1608
bool Bool
Definition: APEX_Include.h:60
Type-safe formatting, modeled on the Python str.format function.
exint typeIndex(const APEX_TypeDefinitionBase *type_defn)
Get the index at which a given type is stored in this buffer, or -1 if not present.
Definition: APEX_Buffer.h:130
**If you just want to fire and args
Definition: thread.h:618
exint allocate(const APEX_TypeDefinitionBase *type_defn, exint size)
Definition: APEX_Buffer.h:148
#define UT_ASSERT(ZZ)
Definition: UT_Assert.h:156
Represents a location for a single object inside of an APEX_Buffer.
Definition: APEX_Buffer.h:176
bool isValid() const
Definition: APEX_Buffer.h:183
GLenum src
Definition: glcorearb.h:1793
size_t appendFormatByArray(const char *fmt, const UT_Array< UT::Format::ArgValue > &args, bool report_errors=true)
Like appendFormat() except it that uses an array of arguments.
APEX_TypedBuffer * getTypedBuffer(exint index)
Definition: APEX_Buffer.h:122