OpenVDB  12.1.0
LeafBuffer.h
Go to the documentation of this file.
1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: Apache-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 <openvdb/util/Assert.h>
11 #include <tbb/spin_mutex.h>
12 #include <algorithm> // for std::swap
13 #include <atomic>
14 #include <cstddef> // for offsetof()
15 #include <iostream>
16 #include <memory>
17 #include <type_traits>
18 
19 
20 class TestLeaf;
21 
22 namespace openvdb {
24 namespace OPENVDB_VERSION_NAME {
25 namespace tree {
26 
27 
28 /// @brief Array of fixed size 2<SUP>3<I>Log2Dim</I></SUP> that stores
29 /// the voxel values of a LeafNode
30 template<typename T, Index Log2Dim>
32 {
33 public:
34  using ValueType = T;
37  static const Index SIZE = 1 << 3 * Log2Dim;
38 
39 #ifdef OPENVDB_USE_DELAYED_LOADING
40  struct FileInfo
41  {
42  FileInfo(): bufpos(0) , maskpos(0) {}
43  std::streamoff bufpos;
44  std::streamoff maskpos;
45  io::MappedFile::Ptr mapping;
47  };
48 #endif
49 
50  /// Default constructor
51  inline LeafBuffer(): mData(new ValueType[SIZE])
52  {
53 #ifdef OPENVDB_USE_DELAYED_LOADING
54  mOutOfCore = 0;
55 #endif
56  }
57  /// Construct a buffer populated with the specified value.
58  explicit inline LeafBuffer(const ValueType&);
59  /// Copy constructor
60  inline LeafBuffer(const LeafBuffer&);
61  /// Construct a buffer but don't allocate memory for the full array of values.
62  LeafBuffer(PartialCreate, const ValueType&): mData(nullptr)
63  {
64 #ifdef OPENVDB_USE_DELAYED_LOADING
65  mOutOfCore = 0;
66 #endif
67  }
68  /// Destructor
69  inline ~LeafBuffer();
70 
71  /// Return @c true if this buffer's values have not yet been read from disk.
72  bool isOutOfCore() const
73  {
74 #ifdef OPENVDB_USE_DELAYED_LOADING
75  return bool(mOutOfCore);
76 #else
77  return false;
78 #endif
79  }
80  /// Return @c true if memory for this buffer has not yet been allocated.
81  bool empty() const { return !mData || this->isOutOfCore(); }
82  /// Allocate memory for this buffer if it has not already been allocated.
83  bool allocate() { if (mData == nullptr) mData = new ValueType[SIZE]; return true; }
84 
85  /// Populate this buffer with a constant value.
86  inline void fill(const ValueType&);
87 
88  /// Return a const reference to the i'th element of this buffer.
89  const ValueType& getValue(Index i) const { return this->at(i); }
90  /// Return a const reference to the i'th element of this buffer.
91  const ValueType& operator[](Index i) const { return this->at(i); }
92  /// Set the i'th value of this buffer to the specified value.
93  inline void setValue(Index i, const ValueType&);
94 
95  /// Copy the other buffer's values into this buffer.
96  inline LeafBuffer& operator=(const LeafBuffer&);
97 
98  /// @brief Return @c true if the contents of the other buffer
99  /// exactly equal the contents of this buffer.
100  inline bool operator==(const LeafBuffer&) const;
101  /// @brief Return @c true if the contents of the other buffer
102  /// are not exactly equal to the contents of this buffer.
103  inline bool operator!=(const LeafBuffer& other) const { return !(other == *this); }
104 
105  /// Exchange this buffer's values with the other buffer's values.
106  inline void swap(LeafBuffer&);
107 
108  /// Return the memory footprint of this buffer in bytes.
109  inline Index memUsage() const;
110  inline Index memUsageIfLoaded() const;
111  /// Return the number of values contained in this buffer.
112  static Index size() { return SIZE; }
113 
114  /// @brief Return a const pointer to the array of voxel values.
115  /// @details This method guarantees that the buffer is allocated and loaded.
116  /// @warning This method should only be used by experts seeking low-level optimizations.
117  const ValueType* data() const;
118  /// @brief Return a pointer to the array of voxel values.
119  /// @details This method guarantees that the buffer is allocated and loaded.
120  /// @warning This method should only be used by experts seeking low-level optimizations.
121  ValueType* data();
122 
123 private:
124  /// If this buffer is empty, return zero, otherwise return the value at index @ i.
125  inline const ValueType& at(Index i) const;
126 
127  /// @brief Return a non-const reference to the value at index @a i.
128  /// @details This method is private since it makes assumptions about the
129  /// buffer's memory layout. LeafBuffers associated with custom leaf node types
130  /// (e.g., a bool buffer implemented as a bitmask) might not be able to
131  /// return non-const references to their values.
132  ValueType& operator[](Index i) { return const_cast<ValueType&>(this->at(i)); }
133 
134  bool deallocate();
135 
136  inline void setOutOfCore(bool b)
137  {
138  (void) b;
139 #ifdef OPENVDB_USE_DELAYED_LOADING
140  mOutOfCore = b;
141 #endif
142  }
143  // To facilitate inlining in the common case in which the buffer is in-core,
144  // the loading logic is split into a separate function, doLoad().
145  inline void loadValues() const
146  {
147 #ifdef OPENVDB_USE_DELAYED_LOADING
148  if (this->isOutOfCore()) this->doLoad();
149 #endif
150  }
151  inline void doLoad() const;
152  inline bool detachFromFile();
153 
154  using FlagsType = std::atomic<Index32>;
155 
156 #ifdef OPENVDB_USE_DELAYED_LOADING
157  union {
158  ValueType* mData;
159  FileInfo* mFileInfo;
160  };
161 #else
162  ValueType* mData;
163 #endif
164  FlagsType mOutOfCore; // interpreted as bool; extra bits reserved for future use
165  tbb::spin_mutex mMutex; // 1 byte
166  //int8_t mReserved[3]; // padding for alignment
167 
168  friend class ::TestLeaf;
169  // Allow the parent LeafNode to access this buffer's data pointer.
170  template<typename, Index> friend class LeafNode;
171 }; // class LeafBuffer
172 
173 
174 ////////////////////////////////////////
175 
176 
177 template<typename T, Index Log2Dim>
178 inline
180  : mData(new ValueType[SIZE])
181 {
182 #ifdef OPENVDB_USE_DELAYED_LOADING
183  mOutOfCore = 0;
184 #endif
185  this->fill(val);
186 }
187 
188 
189 template<typename T, Index Log2Dim>
190 inline
192 {
193 #ifdef OPENVDB_USE_DELAYED_LOADING
194  if (this->isOutOfCore()) {
195  this->detachFromFile();
196  } else {
197  this->deallocate();
198  }
199 #else
200  this->deallocate();
201 #endif
202 }
203 
204 
205 template<typename T, Index Log2Dim>
206 inline
208  : mData(nullptr)
210  , mOutOfCore(other.mOutOfCore.load())
211 #endif
212 {
213 #ifdef OPENVDB_USE_DELAYED_LOADING
214  if (other.isOutOfCore()) {
215  mFileInfo = new FileInfo(*other.mFileInfo);
216  } else {
217 #endif
218  if (other.mData != nullptr) {
219  this->allocate();
220  ValueType* target = mData;
221  const ValueType* source = other.mData;
222  Index n = SIZE;
223  while (n--) *target++ = *source++;
224  }
225 #ifdef OPENVDB_USE_DELAYED_LOADING
226  }
227 #endif
228 }
229 
230 
231 template<typename T, Index Log2Dim>
232 inline void
234 {
235  OPENVDB_ASSERT(i < SIZE);
236  this->loadValues();
237  if (mData) mData[i] = val;
238 }
239 
240 
241 template<typename T, Index Log2Dim>
244 {
245  if (&other != this) {
246 #ifdef OPENVDB_USE_DELAYED_LOADING
247  if (this->isOutOfCore()) {
248  this->detachFromFile();
249  } else {
250  if (other.isOutOfCore()) this->deallocate();
251  }
252  if (other.isOutOfCore()) {
253  mOutOfCore.store(other.mOutOfCore.load(std::memory_order_acquire),
254  std::memory_order_release);
255  mFileInfo = new FileInfo(*other.mFileInfo);
256  } else {
257 #endif
258  if (other.mData != nullptr) {
259  this->allocate();
260  ValueType* target = mData;
261  const ValueType* source = other.mData;
262  Index n = SIZE;
263  while (n--) *target++ = *source++;
264  }
265 #ifdef OPENVDB_USE_DELAYED_LOADING
266  }
267 #endif
268  }
269  return *this;
270 }
271 
272 
273 template<typename T, Index Log2Dim>
274 inline void
276 {
277  this->detachFromFile();
278  if (mData != nullptr) {
279  ValueType* target = mData;
280  Index n = SIZE;
281  while (n--) *target++ = val;
282  }
283 }
284 
285 
286 template<typename T, Index Log2Dim>
287 inline bool
289 {
290  this->loadValues();
291  other.loadValues();
292  const ValueType *target = mData, *source = other.mData;
293  if (!target && !source) return true;
294  if (!target || !source) return false;
295  Index n = SIZE;
296  while (n && math::isExactlyEqual(*target++, *source++)) --n;
297  return n == 0;
298 }
299 
300 
301 template<typename T, Index Log2Dim>
302 inline void
304 {
305  std::swap(mData, other.mData);
306 #ifdef OPENVDB_USE_DELAYED_LOADING
307  // Two atomics can't be swapped because it would require hardware support:
308  // https://en.wikipedia.org/wiki/Double_compare-and-swap
309  // Note that there's a window in which other.mOutOfCore could be written
310  // between our load from it and our store to it.
311  auto tmp = other.mOutOfCore.load(std::memory_order_acquire);
312  tmp = mOutOfCore.exchange(std::move(tmp));
313  other.mOutOfCore.store(std::move(tmp), std::memory_order_release);
314 #endif
315 }
316 
317 
318 template<typename T, Index Log2Dim>
319 inline Index
321 {
322  size_t n = sizeof(*this);
323 #ifdef OPENVDB_USE_DELAYED_LOADING
324  if (this->isOutOfCore()) n += sizeof(FileInfo);
325  else {
326 #endif
327  if (mData) n += SIZE * sizeof(ValueType);
328 #ifdef OPENVDB_USE_DELAYED_LOADING
329  }
330 #endif
331  return static_cast<Index>(n);
332 }
333 
334 
335 template<typename T, Index Log2Dim>
336 inline Index
338 {
339  size_t n = sizeof(*this);
340  n += SIZE * sizeof(ValueType);
341  return static_cast<Index>(n);
342 }
343 
344 
345 template<typename T, Index Log2Dim>
346 inline const typename LeafBuffer<T, Log2Dim>::ValueType*
348 {
349  this->loadValues();
350  if (mData == nullptr) {
351  LeafBuffer* self = const_cast<LeafBuffer*>(this);
352 #ifdef OPENVDB_USE_DELAYED_LOADING
353  // This lock will be contended at most once.
354  tbb::spin_mutex::scoped_lock lock(self->mMutex);
355 #endif
356  if (mData == nullptr) self->mData = new ValueType[SIZE];
357  }
358  return mData;
359 }
360 
361 template<typename T, Index Log2Dim>
362 inline typename LeafBuffer<T, Log2Dim>::ValueType*
364 {
365  this->loadValues();
366  if (mData == nullptr) {
367 #ifdef OPENVDB_USE_DELAYED_LOADING
368  // This lock will be contended at most once.
369  tbb::spin_mutex::scoped_lock lock(mMutex);
370 #endif
371  if (mData == nullptr) mData = new ValueType[SIZE];
372  }
373  return mData;
374 }
375 
376 
377 template<typename T, Index Log2Dim>
378 inline const typename LeafBuffer<T, Log2Dim>::ValueType&
380 {
381  static const ValueType sZero = zeroVal<T>();
382  OPENVDB_ASSERT(i < SIZE);
383  this->loadValues();
384  // We can't use the ternary operator here, otherwise Visual C++ returns
385  // a reference to a temporary.
386  if (mData) return mData[i]; else return sZero;
387 }
388 
389 
390 template<typename T, Index Log2Dim>
391 inline bool
393 {
394 
395  if (mData != nullptr) {
396 #ifdef OPENVDB_USE_DELAYED_LOADING
397  if (this->isOutOfCore()) return false;
398 #endif
399  delete[] mData;
400  mData = nullptr;
401  return true;
402  }
403  return false;
404 }
405 
406 
407 template<typename T, Index Log2Dim>
408 inline void
410 {
411 #ifdef OPENVDB_USE_DELAYED_LOADING
412  if (!this->isOutOfCore()) return;
413 
414  LeafBuffer<T, Log2Dim>* self = const_cast<LeafBuffer<T, Log2Dim>*>(this);
415 
416  // This lock will be contended at most once, after which this buffer
417  // will no longer be out-of-core.
418  tbb::spin_mutex::scoped_lock lock(self->mMutex);
419  if (!this->isOutOfCore()) return;
420 
421  std::unique_ptr<FileInfo> info(self->mFileInfo);
422  OPENVDB_ASSERT(info.get() != nullptr);
423  OPENVDB_ASSERT(info->mapping.get() != nullptr);
424  OPENVDB_ASSERT(info->meta.get() != nullptr);
425 
426  /// @todo For now, we have to clear the mData pointer in order for allocate() to take effect.
427  self->mData = nullptr;
428  self->allocate();
429 
430  SharedPtr<std::streambuf> buf = info->mapping->createBuffer();
431  std::istream is(buf.get());
432 
433  io::setStreamMetadataPtr(is, info->meta, /*transfer=*/true);
434 
435  NodeMaskType mask;
436  is.seekg(info->maskpos);
437  mask.load(is);
438 
439  is.seekg(info->bufpos);
440  io::readCompressedValues(is, self->mData, SIZE, mask, io::getHalfFloat(is));
441 
442  self->setOutOfCore(false);
443 #endif
444 }
445 
446 
447 template<typename T, Index Log2Dim>
448 inline bool
450 {
451 #ifdef OPENVDB_USE_DELAYED_LOADING
452  if (this->isOutOfCore()) {
453  delete mFileInfo;
454  mFileInfo = nullptr;
455  this->setOutOfCore(false);
456  return true;
457  }
458 #endif
459  return false;
460 }
461 
462 
463 ////////////////////////////////////////
464 
465 
466 // Partial specialization for bool ValueType
467 template<Index Log2Dim>
468 class LeafBuffer<bool, Log2Dim>
469 {
470 public:
472  using WordType = typename NodeMaskType::Word;
473  using ValueType = bool;
475 
476  static const Index WORD_COUNT = NodeMaskType::WORD_COUNT;
477  static const Index SIZE = 1 << 3 * Log2Dim;
478 
479  static inline const bool sOn = true;
480  static inline const bool sOff = false;
481 
483  LeafBuffer(bool on): mData(on) {}
484  LeafBuffer(const NodeMaskType& other): mData(other) {}
485  LeafBuffer(const LeafBuffer& other): mData(other.mData) {}
487  void fill(bool val) { mData.set(val); }
488  LeafBuffer& operator=(const LeafBuffer& b) { if (&b != this) { mData=b.mData; } return *this; }
489 
490  const bool& getValue(Index i) const
491  {
492  OPENVDB_ASSERT(i < SIZE);
493  // We can't use the ternary operator here, otherwise Visual C++ returns
494  // a reference to a temporary.
495  if (mData.isOn(i)) return sOn; else return sOff;
496  }
497  const bool& operator[](Index i) const { return this->getValue(i); }
498 
499  bool operator==(const LeafBuffer& other) const { return mData == other.mData; }
500  bool operator!=(const LeafBuffer& other) const { return mData != other.mData; }
501 
502  void setValue(Index i, bool val) { OPENVDB_ASSERT(i < SIZE); mData.set(i, val); }
503 
504  void swap(LeafBuffer& other) { if (&other != this) std::swap(mData, other.mData); }
505 
506  Index memUsage() const { return sizeof(*this); }
507  Index memUsageIfLoaded() const { return sizeof(*this); }
508  static Index size() { return SIZE; }
509 
510  /// @brief Return a pointer to the C-style array of words encoding the bits.
511  /// @warning This method should only be used by experts seeking low-level optimizations.
512  WordType* data() { return &(mData.template getWord<WordType>(0)); }
513  /// @brief Return a const pointer to the C-style array of words encoding the bits.
514  /// @warning This method should only be used by experts seeking low-level optimizations.
515  const WordType* data() const { return const_cast<LeafBuffer*>(this)->data(); }
516 
517 private:
518  // Allow the parent LeafNode to access this buffer's data.
519  template<typename, Index> friend class LeafNode;
520 
521  NodeMaskType mData;
522 }; // class LeafBuffer
523 
524 } // namespace tree
525 } // namespace OPENVDB_VERSION_NAME
526 } // namespace openvdb
527 
528 #endif // OPENVDB_TREE_LEAFBUFFER_HAS_BEEN_INCLUDED
LeafBuffer()
Default constructor.
Definition: LeafBuffer.h:51
Templated block class to hold specific data types and a fixed number of values determined by Log2Dim...
Definition: LeafNode.h:38
WordType StorageType
Definition: LeafBuffer.h:474
const WordType * data() const
Return a const pointer to the C-style array of words encoding the bits.
Definition: LeafBuffer.h:515
Index64 Word
Definition: NodeMasks.h:316
LeafBuffer(const NodeMaskType &other)
Definition: LeafBuffer.h:484
void load(std::istream &is)
Definition: NodeMasks.h:569
bool empty() const
Return true if memory for this buffer has not yet been allocated.
Definition: LeafBuffer.h:81
bool operator==(const Vec3< T0 > &v0, const Vec3< T1 > &v1)
Equality operator, does exact floating point comparisons.
Definition: Vec3.h:474
void swap(LeafBuffer &)
Exchange this buffer&#39;s values with the other buffer&#39;s values.
Definition: LeafBuffer.h:303
bool operator==(const LeafBuffer &) const
Return true if the contents of the other buffer exactly equal the contents of this buffer...
Definition: LeafBuffer.h:288
LeafBuffer(PartialCreate, const ValueType &)
Construct a buffer but don&#39;t allocate memory for the full array of values.
Definition: LeafBuffer.h:62
Index memUsageIfLoaded() const
Definition: LeafBuffer.h:337
Bit mask for the internal and leaf nodes of VDB. This is a 64-bit implementation. ...
Definition: NodeMasks.h:307
void swap(LeafBuffer &other)
Definition: LeafBuffer.h:504
std::shared_ptr< T > SharedPtr
Definition: Types.h:114
LeafBuffer & operator=(const LeafBuffer &)
Copy the other buffer&#39;s values into this buffer.
Definition: LeafBuffer.h:243
Index32 Index
Definition: Types.h:54
bool operator==(const LeafBuffer &other) const
Definition: LeafBuffer.h:499
Index memUsage() const
Return the memory footprint of this buffer in bytes.
Definition: LeafBuffer.h:320
bool isExactlyEqual(const T0 &a, const T1 &b)
Return true if a is exactly equal to b.
Definition: Math.h:443
const bool & getValue(Index i) const
Definition: LeafBuffer.h:490
static const Index SIZE
Definition: LeafBuffer.h:37
const ValueType & getValue(Index i) const
Return a const reference to the i&#39;th element of this buffer.
Definition: LeafBuffer.h:89
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...
Index64 memUsage(const TreeT &tree, bool threaded=true)
Return the total amount of memory in bytes occupied by this tree.
Definition: Count.h:493
static Index size()
Definition: LeafBuffer.h:508
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:103
Index memUsageIfLoaded() const
Definition: LeafBuffer.h:507
void fill(bool val)
Definition: LeafBuffer.h:487
static Index size()
Return the number of values contained in this buffer.
Definition: LeafBuffer.h:112
bool operator!=(const LeafBuffer &other) const
Definition: LeafBuffer.h:500
#define OPENVDB_ASSERT(X)
Definition: Assert.h:41
bool isOutOfCore() const
Return true if this buffer&#39;s values have not yet been read from disk.
Definition: LeafBuffer.h:72
#define OPENVDB_USE_DELAYED_LOADING
Definition: version.h.in:143
Array of fixed size 23Log2Dim that stores the voxel values of a LeafNode.
Definition: LeafBuffer.h:31
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 ...
Definition: Exceptions.h:13
void setValue(Index i, const ValueType &)
Set the i&#39;th value of this buffer to the specified value.
Definition: LeafBuffer.h:233
OutGridT const XformOp bool bool
Definition: ValueTransformer.h:609
static const Index32 WORD_COUNT
Definition: NodeMasks.h:315
Tag dispatch class that distinguishes constructors during file input.
Definition: Types.h:760
~LeafBuffer()
Destructor.
Definition: LeafBuffer.h:191
const bool & operator[](Index i) const
Definition: LeafBuffer.h:497
void readCompressedValues(std::istream &is, ValueT *destBuf, Index destCount, const MaskT &valueMask, bool fromHalf)
Definition: Compression.h:466
bool ValueType
Definition: LeafBuffer.h:473
typename NodeMaskType::Word WordType
Definition: LeafBuffer.h:472
Index memUsage() const
Definition: LeafBuffer.h:506
ValueType StorageType
Definition: LeafBuffer.h:35
WordType * data()
Return a pointer to the C-style array of words encoding the bits.
Definition: LeafBuffer.h:512
bool allocate()
Allocate memory for this buffer if it has not already been allocated.
Definition: LeafBuffer.h:83
const ValueType & operator[](Index i) const
Return a const reference to the i&#39;th element of this buffer.
Definition: LeafBuffer.h:91
LeafBuffer(bool on)
Definition: LeafBuffer.h:483
const ValueType * data() const
Return a const pointer to the array of voxel values.
Definition: LeafBuffer.h:347
LeafBuffer(const LeafBuffer &other)
Definition: LeafBuffer.h:485
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h.in:121
LeafBuffer & operator=(const LeafBuffer &b)
Definition: LeafBuffer.h:488
void setValue(Index i, bool val)
Definition: LeafBuffer.h:502
Index64 memUsageIfLoaded(const TreeT &tree, bool threaded=true)
Return the deserialized memory usage of this tree. This is not necessarily equal to the current memor...
Definition: Count.h:502
ValueType ValueType
Definition: LeafBuffer.h:34
void fill(const ValueType &)
Populate this buffer with a constant value.
Definition: LeafBuffer.h:275
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h.in:218