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