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