HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
varyingref.h
Go to the documentation of this file.
1 // Copyright 2008-present Contributors to the OpenImageIO project.
2 // SPDX-License-Identifier: BSD-3-Clause
3 // https://github.com/OpenImageIO/oiio
4 
5 
6 #pragma once
7 
9 
11 
12 /// VaryingRef is a templated class (on class T) that holds either a
13 /// pointer to a single T value, or an "array" of T values, each
14 /// separated by a certain number of bytes. For those versed in the
15 /// lingo of SIMD shading, this encapsulates 'uniform' and 'varying'
16 /// references.
17 ///
18 /// Suppose you have a computation 'kernel' that is performing an
19 /// operation while looping over several computation 'points.' Each of
20 /// the several operands of the kernel may either be a 'uniform' value
21 /// (identical for each point), or 'varying' (having a potentially
22 /// different value for each point).
23 ///
24 /// Here is a concrete example. Suppose you have the following function:
25 ///
26 /// void add (int n, float *a, float *b, float *result) {
27 /// for (int i = 0; i < n; ++i)
28 /// result[i] = a[i] + b[i];
29 /// }
30 ///
31 /// But if the caller of this function has only a single b value (let's
32 /// say, you always want to add 3 to every a[i]), you would be forced
33 /// to replicate an entire array full of 3's in order to call the function.
34 ///
35 /// Instead, we may wish to generalize the function so that each operand
36 /// may refer to EITHER a single value or an array of values, without
37 /// making the code more complicated. We can do this with VaryingRef:
38 ///
39 /// void add (int n, VaryingRef<float> a, VaryingRef<float> b,
40 /// float *result) {
41 /// for (int i = 0; i < n; ++i)
42 /// result[i] = a[i] + b[i];
43 /// }
44 ///
45 /// VaryingRef overloads operator [] to properly decode whether it is
46 /// uniform (point to the one value) or varying (index the right array
47 /// element). It also overloads the increment operator ++ and the pointer
48 /// indirection operator '*', so you could also write the function
49 /// equivalently as:
50 ///
51 /// void add (int n, VaryingRef<float> a, VaryingRef<float> b,
52 /// float *result) {
53 /// for (int i = 0; i < n; ++i, ++a, ++b) // note increments
54 /// result[i] = (*a) + (*b);
55 /// }
56 ///
57 /// An example of calling this function would be:
58 ///
59 /// float a[n];
60 /// float b; // just 1 value
61 /// float result[n];
62 /// add (n, VaryingRef<float>(a,sizeof(a[0])),
63 /// VaryingRef<float>(b), result);
64 ///
65 /// In this example, we're passing a truly varying 'a' (signified by
66 /// giving a step size from element to element), but a uniform 'b' (signified
67 /// by no step size, or a step size of zero).
68 ///
69 /// There are Varying() and Uniform() templated functions that provide
70 /// a helpful shorthand:
71 ///
72 /// add (n, Varying(a), Uniform(b), result);
73 ///
74 /// Now let's take it a step further and fully optimize the 'add' function
75 /// for when both operands are uniform:
76 ///
77 /// void add (int n, VaryingRef<float> a, VaryingRef<float> b,
78 /// VaryingRef<float> result) {
79 /// if (a.is_uniform() && b.is_uniform()) {
80 /// float r = (*a) + (*b);
81 /// for (int i = 0; i < n; ++i)
82 /// result[i] = r;
83 /// } else {
84 /// // One or both are varying
85 /// for (int i = 0; i < n; ++i, ++a, ++b)
86 /// result[i] = (*a) + (*b);
87 /// }
88 /// }
89 ///
90 /// This is the basis for handling uniform and varying values efficiently
91 /// inside a SIMD shading system.
92 
93 template<class T> class VaryingRef {
94 public:
95  VaryingRef() { init(0, 0); }
96 
97  /// Construct a VaryingRef either of a single value pointed to by ptr
98  /// (if step == 0 or no step is provided), or of a varying set of
99  /// values beginning with ptr and with successive values every 'step'
100  /// bytes.
101  VaryingRef(void* ptr_, int step_ = 0) { init((T*)ptr_, step_); }
102 
103  /// Construct a uniform VaryingRef from a single value.
104  ///
105  VaryingRef(T& ptr_) { init(&ptr_, 0); }
106 
107  /// Initialize this VaryingRef to either of a single value pointed
108  /// to by ptr (if step == 0 or no step is provided), or of a varying
109  /// set of values beginning with ptr and with successive values
110  /// every 'step' bytes.
111  void init(T* ptr_, int step_ = 0)
112  {
113  m_ptr = ptr_;
114  m_step = step_;
115  }
116 
117  /// Initialize this VaryingRef to be uniform and point to a particular
118  /// value reference.
119  const VaryingRef& operator=(T& ptr_)
120  {
121  init(&ptr_);
122  return *this;
123  }
124 
125  /// Is this reference pointing nowhere?
126  ///
127  bool is_null() const { return (m_ptr == 0); }
128 
129  /// Cast to void* returns the pointer, but the real purpose is so
130  /// you can use a VaryingRef as if it were a 'bool' value in a test.
131  operator void*() const { return m_ptr; }
132 
133  /// Is this VaryingRef referring to a varying value, signified by
134  /// having a nonzero step size between elements?
135  bool is_varying() const { return (m_step != 0); }
136 
137  /// Is this VaryingRef referring to a uniform value, signified by
138  /// having a step size of zero between elements?
139  bool is_uniform() const { return (m_step == 0); }
140 
141  /// Pre-increment: If this VaryingRef is varying, increment its
142  /// pointer to the next element in the series, but don't change
143  /// anything if it's uniform. In either case, return a reference to
144  /// its new state.
146  { // Prefix form ++i
147  char* p = (char*)m_ptr;
148  p += m_step;
149  m_ptr = (T*)p;
150  return *this;
151  }
152  /// Post-increment: If this VaryingRef is varying, increment its
153  /// pointer to the next element in the series, but don't change
154  /// anything if it's uniform. No value is returned, so it's not
155  /// legal to do 'bar = foo++' if foo and bar are VaryingRef's.
156  void operator++(int)
157  { // Postfix form i++ : return nothing to avoid copy
158  // VaryingRef<T> tmp = *this;
159  char* p = (char*)m_ptr;
160  p += m_step;
161  m_ptr = (T*)p;
162  // return tmp;
163  }
164 
165  /// Pointer indirection will return the first value currently
166  /// pointed to by this VaryingRef.
167  T& operator*() const { return *m_ptr; }
168 
169  /// Array indexing operator will return a reference to the single
170  /// element if *this is uniform, or to the i-th element of the
171  /// series if *this is varying.
172  T& operator[](int i) const { return *(T*)((char*)m_ptr + i * m_step); }
173 
174  /// Return the raw pointer underneath.
175  ///
176  T* ptr() const { return m_ptr; }
177 
178  /// Return the raw step underneath.
179  ///
180  int step() const { return m_step; }
181 
182 private:
183  T* m_ptr; ///< Pointer to value
184  int m_step; ///< Distance between successive values -- in BYTES!
185 };
186 
187 
188 
189 /// Helper function wraps a varying reference with default step size.
190 ///
191 template<class T>
194 {
195  return VaryingRef<T>(x, sizeof(T));
196 }
197 
198 /// Helper function wraps a uniform reference.
199 ///
200 template<class T>
203 {
204  return VaryingRef<T>(x, 0);
205 }
206 
207 /// Helper function wraps a uniform reference.
208 ///
209 template<class T>
212 {
213  return VaryingRef<T>(&x, 0);
214 }
215 
216 
VaryingRef< T > Uniform(T *x)
Definition: varyingref.h:202
VaryingRef< T > Varying(T *x)
Definition: varyingref.h:193
bool is_null() const
Definition: varyingref.h:127
VaryingRef(T &ptr_)
Definition: varyingref.h:105
T * ptr() const
Definition: varyingref.h:176
GLint GLenum GLint x
Definition: glcorearb.h:409
VaryingRef(void *ptr_, int step_=0)
Definition: varyingref.h:101
VaryingRef & operator++()
Definition: varyingref.h:145
void operator++(int)
Definition: varyingref.h:156
T & operator[](int i) const
Definition: varyingref.h:172
T & operator*() const
Definition: varyingref.h:167
void init(T *ptr_, int step_=0)
Definition: varyingref.h:111
bool is_uniform() const
Definition: varyingref.h:139
#define OIIO_NAMESPACE_END
Definition: oiioversion.h:94
const VaryingRef & operator=(T &ptr_)
Definition: varyingref.h:119
int step() const
Definition: varyingref.h:180
#define OIIO_NAMESPACE_BEGIN
Definition: oiioversion.h:93
bool is_varying() const
Definition: varyingref.h:135