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