HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
LeafBuffer.h
Go to the documentation of this file.
1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: MPL-2.0
3 
4 #ifndef OPENVDB_TREE_LEAFBUFFER_HAS_BEEN_INCLUDED
5 #define OPENVDB_TREE_LEAFBUFFER_HAS_BEEN_INCLUDED
6 
7 #include <openvdb/Types.h>
8 #include <openvdb/io/Compression.h> // for io::readCompressedValues(), etc
10 #include <tbb/atomic.h>
11 #include <tbb/spin_mutex.h>
12 #include <algorithm> // for std::swap
13 #include <cstddef> // for offsetof()
14 #include <iostream>
15 #include <memory>
16 #include <type_traits>
17 
18 
19 class TestLeaf;
20 
21 namespace openvdb {
23 namespace OPENVDB_VERSION_NAME {
24 namespace tree {
25 
26 namespace internal {
27 
28 /// @internal For delayed loading to be threadsafe, LeafBuffer::mOutOfCore must be
29 /// memory-fenced when it is set in LeafBuffer::doLoad(), otherwise that operation
30 /// could be reordered ahead of others in doLoad(), with the possible result that
31 /// other threads could see the buffer as in-core before it has been fully loaded.
32 /// Making mOutOfCore a TBB atomic solves the problem, since TBB atomics are release-fenced
33 /// by default (unlike STL atomics, which are not even guaranteed to be lock-free).
34 /// However, TBB atomics have stricter alignment requirements than their underlying value_types,
35 /// so a LeafBuffer with an atomic mOutOfCore is potentially ABI-incompatible with
36 /// its non-atomic counterpart.
37 /// This helper class conditionally declares mOutOfCore as an atomic only if doing so
38 /// doesn't break ABI compatibility.
39 template<typename T>
41 {
42  /// The type of LeafBuffer::mOutOfCore
43  using type = tbb::atomic<Index32>;
44  static constexpr bool IsAtomic = true;
45 };
46 
47 } // namespace internal
48 
49 
50 /// @brief Array of fixed size 2<SUP>3<I>Log2Dim</I></SUP> that stores
51 /// the voxel values of a LeafNode
52 template<typename T, Index Log2Dim>
54 {
55 public:
56  using ValueType = T;
59  static const Index SIZE = 1 << 3 * Log2Dim;
60 
61  struct FileInfo
62  {
63  FileInfo(): bufpos(0) , maskpos(0) {}
64  std::streamoff bufpos;
65  std::streamoff maskpos;
68  };
69 
70  /// Default constructor
71  inline LeafBuffer(): mData(new ValueType[SIZE]) { mOutOfCore = 0; }
72  /// Construct a buffer populated with the specified value.
73  explicit inline LeafBuffer(const ValueType&);
74  /// Copy constructor
75  inline LeafBuffer(const LeafBuffer&);
76  /// Construct a buffer but don't allocate memory for the full array of values.
77  LeafBuffer(PartialCreate, const ValueType&): mData(nullptr) { mOutOfCore = 0; }
78  /// Destructor
79  inline ~LeafBuffer();
80 
81  /// Return @c true if this buffer's values have not yet been read from disk.
82  bool isOutOfCore() const { return bool(mOutOfCore); }
83  /// Return @c true if memory for this buffer has not yet been allocated.
84  bool empty() const { return !mData || this->isOutOfCore(); }
85  /// Allocate memory for this buffer if it has not already been allocated.
86  bool allocate() { if (mData == nullptr) mData = new ValueType[SIZE]; return true; }
87 
88  /// Populate this buffer with a constant value.
89  inline void fill(const ValueType&);
90 
91  /// Return a const reference to the i'th element of this buffer.
92  const ValueType& getValue(Index i) const { return this->at(i); }
93  /// Return a const reference to the i'th element of this buffer.
94  const ValueType& operator[](Index i) const { return this->at(i); }
95  /// Set the i'th value of this buffer to the specified value.
96  inline void setValue(Index i, const ValueType&);
97 
98  /// Copy the other buffer's values into this buffer.
99  inline LeafBuffer& operator=(const LeafBuffer&);
100 
101  /// @brief Return @c true if the contents of the other buffer
102  /// exactly equal the contents of this buffer.
103  inline bool operator==(const LeafBuffer&) const;
104  /// @brief Return @c true if the contents of the other buffer
105  /// are not exactly equal to the contents of this buffer.
106  inline bool operator!=(const LeafBuffer& other) const { return !(other == *this); }
107 
108  /// Exchange this buffer's values with the other buffer's values.
109  inline void swap(LeafBuffer&);
110 
111  /// Return the memory footprint of this buffer in bytes.
112  inline Index memUsage() const;
113  /// Return the number of values contained in this buffer.
114  static Index size() { return SIZE; }
115 
116  /// @brief Return a const pointer to the array of voxel values.
117  /// @details This method guarantees that the buffer is allocated and loaded.
118  /// @warning This method should only be used by experts seeking low-level optimizations.
119  const ValueType* data() const;
120  /// @brief Return a pointer to the array of voxel values.
121  /// @details This method guarantees that the buffer is allocated and loaded.
122  /// @warning This method should only be used by experts seeking low-level optimizations.
123  ValueType* data();
124 
125 private:
126  /// If this buffer is empty, return zero, otherwise return the value at index @ i.
127  inline const ValueType& at(Index i) const;
128 
129  /// @brief Return a non-const reference to the value at index @a i.
130  /// @details This method is private since it makes assumptions about the
131  /// buffer's memory layout. LeafBuffers associated with custom leaf node types
132  /// (e.g., a bool buffer implemented as a bitmask) might not be able to
133  /// return non-const references to their values.
134  ValueType& operator[](Index i) { return const_cast<ValueType&>(this->at(i)); }
135 
136  bool deallocate();
137 
138  inline void setOutOfCore(bool b) { mOutOfCore = b; }
139  // To facilitate inlining in the common case in which the buffer is in-core,
140  // the loading logic is split into a separate function, doLoad().
141  inline void loadValues() const { if (this->isOutOfCore()) this->doLoad(); }
142  inline void doLoad() const;
143  inline bool detachFromFile();
144 
145  using FlagsType = typename internal::LeafBufferFlags<ValueType>::type;
146 
147  union {
149  FileInfo* mFileInfo;
150  };
151  FlagsType mOutOfCore; // interpreted as bool; extra bits reserved for future use
152  tbb::spin_mutex mMutex; // 1 byte
153  //int8_t mReserved[3]; // padding for alignment
154 
155  static const ValueType sZero;
156 
157  friend class ::TestLeaf;
158  // Allow the parent LeafNode to access this buffer's data pointer.
159  template<typename, Index> friend class LeafNode;
160 }; // class LeafBuffer
161 
162 
163 ////////////////////////////////////////
164 
165 
166 template<typename T, Index Log2Dim>
167 const T LeafBuffer<T, Log2Dim>::sZero = zeroVal<T>();
168 
169 
170 template<typename T, Index Log2Dim>
171 inline
173  : mData(new ValueType[SIZE])
174 {
175  mOutOfCore = 0;
176  this->fill(val);
177 }
178 
179 
180 template<typename T, Index Log2Dim>
181 inline
183 {
184  if (this->isOutOfCore()) {
185  this->detachFromFile();
186  } else {
187  this->deallocate();
188  }
189 }
190 
191 
192 template<typename T, Index Log2Dim>
193 inline
195  : mData(nullptr)
196  , mOutOfCore(other.mOutOfCore)
197 {
198  if (other.isOutOfCore()) {
199  mFileInfo = new FileInfo(*other.mFileInfo);
200  } else if (other.mData != nullptr) {
201  this->allocate();
203  const ValueType* source = other.mData;
204  Index n = SIZE;
205  while (n--) *target++ = *source++;
206  }
207 }
208 
209 
210 template<typename T, Index Log2Dim>
211 inline void
213 {
214  assert(i < SIZE);
215  this->loadValues();
216  if (mData) mData[i] = val;
217 }
218 
219 
220 template<typename T, Index Log2Dim>
223 {
224  if (&other != this) {
225  if (this->isOutOfCore()) {
226  this->detachFromFile();
227  } else {
228  if (other.isOutOfCore()) this->deallocate();
229  }
230  if (other.isOutOfCore()) {
231  mOutOfCore = other.mOutOfCore;
232  mFileInfo = new FileInfo(*other.mFileInfo);
233  } else if (other.mData != nullptr) {
234  this->allocate();
235  ValueType* target = mData;
236  const ValueType* source = other.mData;
237  Index n = SIZE;
238  while (n--) *target++ = *source++;
239  }
240  }
241  return *this;
242 }
243 
244 
245 template<typename T, Index Log2Dim>
246 inline void
248 {
249  this->detachFromFile();
250  if (mData != nullptr) {
251  ValueType* target = mData;
252  Index n = SIZE;
253  while (n--) *target++ = val;
254  }
255 }
256 
257 
258 template<typename T, Index Log2Dim>
259 inline bool
261 {
262  this->loadValues();
263  other.loadValues();
264  const ValueType *target = mData, *source = other.mData;
265  if (!target && !source) return true;
266  if (!target || !source) return false;
267  Index n = SIZE;
268  while (n && math::isExactlyEqual(*target++, *source++)) --n;
269  return n == 0;
270 }
271 
272 
273 template<typename T, Index Log2Dim>
274 inline void
276 {
277  std::swap(mData, other.mData);
278  std::swap(mOutOfCore, other.mOutOfCore);
279 }
280 
281 
282 template<typename T, Index Log2Dim>
283 inline Index
285 {
286  size_t n = sizeof(*this);
287  if (this->isOutOfCore()) n += sizeof(FileInfo);
288  else if (mData) n += SIZE * sizeof(ValueType);
289  return static_cast<Index>(n);
290 }
291 
292 
293 template<typename T, Index Log2Dim>
294 inline const typename LeafBuffer<T, Log2Dim>::ValueType*
296 {
297  this->loadValues();
298  if (mData == nullptr) {
299  LeafBuffer* self = const_cast<LeafBuffer*>(this);
300  // This lock will be contended at most once.
301  tbb::spin_mutex::scoped_lock lock(self->mMutex);
302  if (mData == nullptr) self->mData = new ValueType[SIZE];
303  }
304  return mData;
305 }
306 
307 template<typename T, Index Log2Dim>
308 inline typename LeafBuffer<T, Log2Dim>::ValueType*
310 {
311  this->loadValues();
312  if (mData == nullptr) {
313  // This lock will be contended at most once.
314  tbb::spin_mutex::scoped_lock lock(mMutex);
315  if (mData == nullptr) mData = new ValueType[SIZE];
316  }
317  return mData;
318 }
319 
320 
321 template<typename T, Index Log2Dim>
322 inline const typename LeafBuffer<T, Log2Dim>::ValueType&
324 {
325  assert(i < SIZE);
326  this->loadValues();
327  // We can't use the ternary operator here, otherwise Visual C++ returns
328  // a reference to a temporary.
329  if (mData) return mData[i]; else return sZero;
330 }
331 
332 
333 template<typename T, Index Log2Dim>
334 inline bool
335 LeafBuffer<T, Log2Dim>::deallocate()
336 {
337  if (mData != nullptr && !this->isOutOfCore()) {
338  delete[] mData;
339  mData = nullptr;
340  return true;
341  }
342  return false;
343 }
344 
345 
346 template<typename T, Index Log2Dim>
347 inline void
348 LeafBuffer<T, Log2Dim>::doLoad() const
349 {
350  if (!this->isOutOfCore()) return;
351 
352  LeafBuffer<T, Log2Dim>* self = const_cast<LeafBuffer<T, Log2Dim>*>(this);
353 
354  // This lock will be contended at most once, after which this buffer
355  // will no longer be out-of-core.
356  tbb::spin_mutex::scoped_lock lock(self->mMutex);
357  if (!this->isOutOfCore()) return;
358 
359  std::unique_ptr<FileInfo> info(self->mFileInfo);
360  assert(info.get() != nullptr);
361  assert(info->mapping.get() != nullptr);
362  assert(info->meta.get() != nullptr);
363 
364  /// @todo For now, we have to clear the mData pointer in order for allocate() to take effect.
365  self->mData = nullptr;
366  self->allocate();
367 
368  SharedPtr<std::streambuf> buf = info->mapping->createBuffer();
369  std::istream is(buf.get());
370 
371  io::setStreamMetadataPtr(is, info->meta, /*transfer=*/true);
372 
373  NodeMaskType mask;
374  is.seekg(info->maskpos);
375  mask.load(is);
376 
377  is.seekg(info->bufpos);
378  io::readCompressedValues(is, self->mData, SIZE, mask, io::getHalfFloat(is));
379 
380  self->setOutOfCore(false);
381 }
382 
383 
384 template<typename T, Index Log2Dim>
385 inline bool
386 LeafBuffer<T, Log2Dim>::detachFromFile()
387 {
388  if (this->isOutOfCore()) {
389  delete mFileInfo;
390  mFileInfo = nullptr;
391  this->setOutOfCore(false);
392  return true;
393  }
394  return false;
395 }
396 
397 
398 ////////////////////////////////////////
399 
400 
401 // Partial specialization for bool ValueType
402 template<Index Log2Dim>
403 class LeafBuffer<bool, Log2Dim>
404 {
405 public:
407  using WordType = typename NodeMaskType::Word;
408  using ValueType = bool;
410 
411  static const Index WORD_COUNT = NodeMaskType::WORD_COUNT;
412  static const Index SIZE = 1 << 3 * Log2Dim;
413 
414  // These static declarations must be on separate lines to avoid VC9 compiler errors.
415  static const bool sOn;
416  static const bool sOff;
417 
419  LeafBuffer(bool on): mData(on) {}
420  LeafBuffer(const NodeMaskType& other): mData(other) {}
421  LeafBuffer(const LeafBuffer& other): mData(other.mData) {}
423  void fill(bool val) { mData.set(val); }
424  LeafBuffer& operator=(const LeafBuffer& b) { if (&b != this) { mData=b.mData; } return *this; }
425 
426  const bool& getValue(Index i) const
427  {
428  assert(i < SIZE);
429  // We can't use the ternary operator here, otherwise Visual C++ returns
430  // a reference to a temporary.
431  if (mData.isOn(i)) return sOn; else return sOff;
432  }
433  const bool& operator[](Index i) const { return this->getValue(i); }
434 
435  bool operator==(const LeafBuffer& other) const { return mData == other.mData; }
436  bool operator!=(const LeafBuffer& other) const { return mData != other.mData; }
437 
438  void setValue(Index i, bool val) { assert(i < SIZE); mData.set(i, val); }
439 
440  void swap(LeafBuffer& other) { if (&other != this) std::swap(mData, other.mData); }
441 
442  Index memUsage() const { return sizeof(*this); }
443  static Index size() { return SIZE; }
444 
445  /// @brief Return a pointer to the C-style array of words encoding the bits.
446  /// @warning This method should only be used by experts seeking low-level optimizations.
447  WordType* data() { return &(mData.template getWord<WordType>(0)); }
448  /// @brief Return a const pointer to the C-style array of words encoding the bits.
449  /// @warning This method should only be used by experts seeking low-level optimizations.
450  const WordType* data() const { return const_cast<LeafBuffer*>(this)->data(); }
451 
452 private:
453  // Allow the parent LeafNode to access this buffer's data.
454  template<typename, Index> friend class LeafNode;
455 
457 }; // class LeafBuffer
458 
459 
460 /// @internal For consistency with other nodes and with iterators, methods like
461 /// LeafNode::getValue() return a reference to a value. Since it's not possible
462 /// to return a reference to a bit in a node mask, we return a reference to one
463 /// of the following static values instead.
464 template<Index Log2Dim> const bool LeafBuffer<bool, Log2Dim>::sOn = true;
465 template<Index Log2Dim> const bool LeafBuffer<bool, Log2Dim>::sOff = false;
466 
467 } // namespace tree
468 } // namespace OPENVDB_VERSION_NAME
469 } // namespace openvdb
470 
471 #endif // OPENVDB_TREE_LEAFBUFFER_HAS_BEEN_INCLUDED
LeafBuffer & operator=(const LeafBuffer &)
Copy the other buffer's values into this buffer.
Definition: LeafBuffer.h:222
bool isExactlyEqual(const T0 &a, const T1 &b)
Return true if a is exactly equal to b.
Definition: Math.h:434
void swap(UT::ArraySet< Key, MULTI, MAX_LOAD_FACTOR_256, Clearer, Hash, KeyEqual > &a, UT::ArraySet< Key, MULTI, MAX_LOAD_FACTOR_256, Clearer, Hash, KeyEqual > &b)
Definition: UT_ArraySet.h:1629
GLenum target
Definition: glew.h:2865
GLuint const GLfloat * val
Definition: glew.h:2794
LeafBuffer(PartialCreate, const ValueType &)
Construct a buffer but don't allocate memory for the full array of values.
Definition: LeafBuffer.h:77
OPENVDB_API bool getHalfFloat(std::ios_base &)
Return true if floating-point values should be quantized to 16 bits when writing to the given stream ...
GLsizei GLsizei GLchar * source
Definition: glew.h:1832
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h:167
SharedPtr< MappedFile > Ptr
Definition: io.h:136
tbb::atomic< Index32 > type
The type of LeafBuffer::mOutOfCore.
Definition: LeafBuffer.h:43
bool operator==(const LeafBuffer &) const
Return true if the contents of the other buffer exactly equal the contents of this buffer...
Definition: LeafBuffer.h:260
GLenum GLint GLuint mask
Definition: glew.h:1845
bool empty() const
Return true if memory for this buffer has not yet been allocated.
Definition: LeafBuffer.h:84
Tag dispatch class that distinguishes constructors during file input.
Definition: Types.h:548
void readCompressedValues(std::istream &is, ValueT *destBuf, Index destCount, const MaskT &valueMask, bool fromHalf)
Definition: Compression.h:465
std::shared_ptr< T > SharedPtr
Definition: Types.h:92
Bit mask for the internal and leaf nodes of VDB. This is a 64-bit implementation. ...
Definition: NodeMasks.h:307
void swap(LeafBuffer &)
Exchange this buffer's values with the other buffer's values.
Definition: LeafBuffer.h:275
Templated block class to hold specific data types and a fixed number of values determined by Log2Dim...
Definition: LeafNode.h:37
bool allocate()
Allocate memory for this buffer if it has not already been allocated.
Definition: LeafBuffer.h:86
static Index size()
Return the number of values contained in this buffer.
Definition: LeafBuffer.h:114
const ValueType & getValue(Index i) const
Return a const reference to the i'th element of this buffer.
Definition: LeafBuffer.h:92
GLsizei n
Definition: glew.h:4040
Index memUsage() const
Return the memory footprint of this buffer in bytes.
Definition: LeafBuffer.h:284
WordType * data()
Return a pointer to the C-style array of words encoding the bits.
Definition: LeafBuffer.h:447
OPENVDB_API void setStreamMetadataPtr(std::ios_base &, SharedPtr< StreamMetadata > &, bool transfer=true)
Associate the given stream with (a shared pointer to) an object that stores metadata (file format...
GLdouble GLdouble GLdouble b
Definition: glew.h:9122
const ValueType * data() const
Return a const pointer to the array of voxel values.
Definition: LeafBuffer.h:295
const ValueType & operator[](Index i) const
Return a const reference to the i'th element of this buffer.
Definition: LeafBuffer.h:94
void fill(const ValueType &)
Populate this buffer with a constant value.
Definition: LeafBuffer.h:247
Allocator::value_type * allocate(Allocator &alloc, std::size_t n)
Definition: format.h:282
bool isOutOfCore() const
Return true if this buffer's values have not yet been read from disk.
Definition: LeafBuffer.h:82
Array of fixed size 23Log2Dim that stores the voxel values of a LeafNode.
Definition: LeafBuffer.h:53
#define SIZE
Definition: simple.C:40
void setValue(Index i, const ValueType &)
Set the i'th value of this buffer to the specified value.
Definition: LeafBuffer.h:212
bool operator!=(const LeafBuffer &other) const
Return true if the contents of the other buffer are not exactly equal to the contents of this buffer...
Definition: LeafBuffer.h:106
GLenum GLuint GLsizei const GLchar * buf
Definition: glew.h:2580
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h:113
const WordType * data() const
Return a const pointer to the C-style array of words encoding the bits.
Definition: LeafBuffer.h:450