OpenVDB  12.0.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
bool isExactlyEqual(const T0 &a, const T1 &b)
Return true if a is exactly equal to b.
Definition: Math.h:443
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
LeafBuffer(const LeafBuffer &other)
Definition: LeafBuffer.h:485
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 ...
bool empty() const
Return true if memory for this buffer has not yet been allocated.
Definition: LeafBuffer.h:81
void swap(LeafBuffer &)
Exchange this buffer&#39;s values with the other buffer&#39;s values.
Definition: LeafBuffer.h:303
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
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
WordType StorageType
Definition: LeafBuffer.h:474
Index memUsageIfLoaded() const
Definition: LeafBuffer.h:337
LeafBuffer(const NodeMaskType &other)
Definition: LeafBuffer.h:484
Bit mask for the internal and leaf nodes of VDB. This is a 64-bit implementation. ...
Definition: NodeMasks.h:307
const bool & getValue(Index i) const
Definition: LeafBuffer.h:490
LeafBuffer & operator=(const LeafBuffer &)
Copy the other buffer&#39;s values into this buffer.
Definition: LeafBuffer.h:243
Index memUsageIfLoaded() const
Definition: LeafBuffer.h:507
Index memUsage() const
Return the memory footprint of this buffer in bytes.
Definition: LeafBuffer.h:320
bool operator!=(const LeafBuffer &other) const
Definition: LeafBuffer.h:500
Index32 Index
Definition: Types.h:54
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 Word
Definition: NodeMasks.h:316
bool operator==(const LeafBuffer &other) const
Definition: LeafBuffer.h:499
static const Index SIZE
Definition: LeafBuffer.h:37
void fill(bool val)
Definition: LeafBuffer.h:487
const ValueType & getValue(Index i) const
Return a const reference to the i&#39;th element of this buffer.
Definition: LeafBuffer.h:89
void swap(LeafBuffer &other)
Definition: LeafBuffer.h:504
bool operator==(const Vec3< T0 > &v0, const Vec3< T1 > &v1)
Equality operator, does exact floating point comparisons.
Definition: Vec3.h:474
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
static Index size()
Return the number of values contained in this buffer.
Definition: LeafBuffer.h:112
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
void readCompressedValues(std::istream &is, ValueT *destBuf, Index destCount, const MaskT &valueMask, bool fromHalf)
Definition: Compression.h:466
static const Index32 WORD_COUNT
Definition: NodeMasks.h:315
std::shared_ptr< T > SharedPtr
Definition: Types.h:114
#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
void setValue(Index i, const ValueType &)
Set the i&#39;th value of this buffer to the specified value.
Definition: LeafBuffer.h:233
Definition: Exceptions.h:13
Array of fixed size 23Log2Dim that stores the voxel values of a LeafNode.
Definition: LeafBuffer.h:31
OutGridT const XformOp bool bool
Definition: ValueTransformer.h:609
WordType * data()
Return a pointer to the C-style array of words encoding the bits.
Definition: LeafBuffer.h:512
~LeafBuffer()
Destructor.
Definition: LeafBuffer.h:191
typename NodeMaskType::Word WordType
Definition: LeafBuffer.h:472
Index memUsage() const
Definition: LeafBuffer.h:506
LeafBuffer & operator=(const LeafBuffer &b)
Definition: LeafBuffer.h:488
bool ValueType
Definition: LeafBuffer.h:473
ValueType StorageType
Definition: LeafBuffer.h:35
bool allocate()
Allocate memory for this buffer if it has not already been allocated.
Definition: LeafBuffer.h:83
const bool & operator[](Index i) const
Definition: LeafBuffer.h:497
const ValueType & operator[](Index i) const
Return a const reference to the i&#39;th element of this buffer.
Definition: LeafBuffer.h:91
void setValue(Index i, bool val)
Definition: LeafBuffer.h:502
const ValueType * data() const
Return a const pointer to the array of voxel values.
Definition: LeafBuffer.h:347
const WordType * data() const
Return a const pointer to the C-style array of words encoding the bits.
Definition: LeafBuffer.h:515
void load(std::istream &is)
Definition: NodeMasks.h:569
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h.in:121
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
Tag dispatch class that distinguishes constructors during file input.
Definition: Types.h:689
LeafBuffer(bool on)
Definition: LeafBuffer.h:483