OpenVDB  7.0.0
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/atomic.h>
11 #include <tbb/spin_mutex.h>
12 #include <algorithm> // for std::swap
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 namespace internal {
27 
39 template<typename T>
41 {
42 #if OPENVDB_ABI_VERSION_NUMBER >= 5
43  using type = tbb::atomic<Index32>;
45  static constexpr bool IsAtomic = true;
46 #else // OPENVDB_ABI_VERSION_NUMBER < 5
47  // These structs need to have the same data members as LeafBuffer.
48  struct Atomic { union { T* data; void* ptr; }; tbb::atomic<Index32> i; tbb::spin_mutex mutex; };
49  struct NonAtomic { union { T* data; void* ptr; }; Index32 i; tbb::spin_mutex mutex; };
50 
51 #ifndef __INTEL_COMPILER
52  static constexpr bool IsAtomic = ((sizeof(Atomic) == sizeof(NonAtomic))
54  && (offsetof(Atomic, i) == offsetof(NonAtomic, i)));
55 #else
56  // We can't use offsetof() with ICC, because it requires the arguments
57  // to be POD types. (C++11 requires only that they be standard layout types,
58  // which Atomic and NonAtomic are.)
59  static constexpr bool IsAtomic = (sizeof(Atomic) == sizeof(NonAtomic));
60 #endif
61  static constexpr size_t size = sizeof(Atomic);
64  using type = typename std::conditional<IsAtomic, tbb::atomic<Index32>, Index32>::type;
65 #endif
66 };
67 
68 } // namespace internal
69 
70 
73 template<typename T, Index Log2Dim>
75 {
76 public:
77  using ValueType = T;
80  static const Index SIZE = 1 << 3 * Log2Dim;
81 
82  struct FileInfo
83  {
84  FileInfo(): bufpos(0) , maskpos(0) {}
85  std::streamoff bufpos;
86  std::streamoff maskpos;
89  };
90 
92  inline LeafBuffer(): mData(new ValueType[SIZE]) { mOutOfCore = 0; }
94  explicit inline LeafBuffer(const ValueType&);
96  inline LeafBuffer(const LeafBuffer&);
98  LeafBuffer(PartialCreate, const ValueType&): mData(nullptr) { mOutOfCore = 0; }
100  inline ~LeafBuffer();
101 
103  bool isOutOfCore() const { return bool(mOutOfCore); }
105  bool empty() const { return !mData || this->isOutOfCore(); }
107  bool allocate() { if (mData == nullptr) mData = new ValueType[SIZE]; return true; }
108 
110  inline void fill(const ValueType&);
111 
113  const ValueType& getValue(Index i) const { return this->at(i); }
115  const ValueType& operator[](Index i) const { return this->at(i); }
117  inline void setValue(Index i, const ValueType&);
118 
120  inline LeafBuffer& operator=(const LeafBuffer&);
121 
124  inline bool operator==(const LeafBuffer&) const;
127  inline bool operator!=(const LeafBuffer& other) const { return !(other == *this); }
128 
130  inline void swap(LeafBuffer&);
131 
133  inline Index memUsage() const;
135  static Index size() { return SIZE; }
136 
140  const ValueType* data() const;
144  ValueType* data();
145 
146 private:
148  inline const ValueType& at(Index i) const;
149 
155  ValueType& operator[](Index i) { return const_cast<ValueType&>(this->at(i)); }
156 
157  bool deallocate();
158 
159  inline void setOutOfCore(bool b) { mOutOfCore = b; }
160  // To facilitate inlining in the common case in which the buffer is in-core,
161  // the loading logic is split into a separate function, doLoad().
162  inline void loadValues() const { if (this->isOutOfCore()) this->doLoad(); }
163  inline void doLoad() const;
164  inline bool detachFromFile();
165 
166  using FlagsType = typename internal::LeafBufferFlags<ValueType>::type;
167 
168  union {
170  FileInfo* mFileInfo;
171  };
172  FlagsType mOutOfCore; // interpreted as bool; extra bits reserved for future use
173  tbb::spin_mutex mMutex; // 1 byte
174  //int8_t mReserved[3]; // padding for alignment
175 
176  static const ValueType sZero;
177 
178  friend class ::TestLeaf;
179  // Allow the parent LeafNode to access this buffer's data pointer.
180  template<typename, Index> friend class LeafNode;
181 }; // class LeafBuffer
182 
183 
185 
186 
187 template<typename T, Index Log2Dim>
188 const T LeafBuffer<T, Log2Dim>::sZero = zeroVal<T>();
189 
190 
191 template<typename T, Index Log2Dim>
192 inline
194  : mData(new ValueType[SIZE])
195 {
196  mOutOfCore = 0;
197  this->fill(val);
198 }
199 
200 
201 template<typename T, Index Log2Dim>
202 inline
204 {
205  if (this->isOutOfCore()) {
206  this->detachFromFile();
207  } else {
208  this->deallocate();
209  }
210 }
211 
212 
213 template<typename T, Index Log2Dim>
214 inline
216  : mData(nullptr)
217  , mOutOfCore(other.mOutOfCore)
218 {
219  if (other.isOutOfCore()) {
220  mFileInfo = new FileInfo(*other.mFileInfo);
221  } else if (other.mData != nullptr) {
222  this->allocate();
223  ValueType* target = mData;
224  const ValueType* source = other.mData;
225  Index n = SIZE;
226  while (n--) *target++ = *source++;
227  }
228 }
229 
230 
231 template<typename T, Index Log2Dim>
232 inline void
234 {
235  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  if (this->isOutOfCore()) {
247  this->detachFromFile();
248  } else {
249  if (other.isOutOfCore()) this->deallocate();
250  }
251  if (other.isOutOfCore()) {
252  mOutOfCore = other.mOutOfCore;
253  mFileInfo = new FileInfo(*other.mFileInfo);
254  } else if (other.mData != nullptr) {
255  this->allocate();
256  ValueType* target = mData;
257  const ValueType* source = other.mData;
258  Index n = SIZE;
259  while (n--) *target++ = *source++;
260  }
261  }
262  return *this;
263 }
264 
265 
266 template<typename T, Index Log2Dim>
267 inline void
269 {
270  this->detachFromFile();
271  if (mData != nullptr) {
272  ValueType* target = mData;
273  Index n = SIZE;
274  while (n--) *target++ = val;
275  }
276 }
277 
278 
279 template<typename T, Index Log2Dim>
280 inline bool
282 {
283  this->loadValues();
284  other.loadValues();
285  const ValueType *target = mData, *source = other.mData;
286  if (!target && !source) return true;
287  if (!target || !source) return false;
288  Index n = SIZE;
289  while (n && math::isExactlyEqual(*target++, *source++)) --n;
290  return n == 0;
291 }
292 
293 
294 template<typename T, Index Log2Dim>
295 inline void
297 {
298  std::swap(mData, other.mData);
299  std::swap(mOutOfCore, other.mOutOfCore);
300 }
301 
302 
303 template<typename T, Index Log2Dim>
304 inline Index
306 {
307  size_t n = sizeof(*this);
308  if (this->isOutOfCore()) n += sizeof(FileInfo);
309  else if (mData) n += SIZE * sizeof(ValueType);
310  return static_cast<Index>(n);
311 }
312 
313 
314 template<typename T, Index Log2Dim>
315 inline const typename LeafBuffer<T, Log2Dim>::ValueType*
317 {
318  this->loadValues();
319  if (mData == nullptr) {
320  LeafBuffer* self = const_cast<LeafBuffer*>(this);
321  // This lock will be contended at most once.
322  tbb::spin_mutex::scoped_lock lock(self->mMutex);
323  if (mData == nullptr) self->mData = new ValueType[SIZE];
324  }
325  return mData;
326 }
327 
328 template<typename T, Index Log2Dim>
329 inline typename LeafBuffer<T, Log2Dim>::ValueType*
331 {
332  this->loadValues();
333  if (mData == nullptr) {
334  // This lock will be contended at most once.
335  tbb::spin_mutex::scoped_lock lock(mMutex);
336  if (mData == nullptr) mData = new ValueType[SIZE];
337  }
338  return mData;
339 }
340 
341 
342 template<typename T, Index Log2Dim>
343 inline const typename LeafBuffer<T, Log2Dim>::ValueType&
345 {
346  assert(i < SIZE);
347  this->loadValues();
348  // We can't use the ternary operator here, otherwise Visual C++ returns
349  // a reference to a temporary.
350  if (mData) return mData[i]; else return sZero;
351 }
352 
353 
354 template<typename T, Index Log2Dim>
355 inline bool
357 {
358  if (mData != nullptr && !this->isOutOfCore()) {
359  delete[] mData;
360  mData = nullptr;
361  return true;
362  }
363  return false;
364 }
365 
366 
367 template<typename T, Index Log2Dim>
368 inline void
370 {
371  if (!this->isOutOfCore()) return;
372 
373  LeafBuffer<T, Log2Dim>* self = const_cast<LeafBuffer<T, Log2Dim>*>(this);
374 
375  // This lock will be contended at most once, after which this buffer
376  // will no longer be out-of-core.
377  tbb::spin_mutex::scoped_lock lock(self->mMutex);
378  if (!this->isOutOfCore()) return;
379 
380  std::unique_ptr<FileInfo> info(self->mFileInfo);
381  assert(info.get() != nullptr);
382  assert(info->mapping.get() != nullptr);
383  assert(info->meta.get() != nullptr);
384 
386  self->mData = nullptr;
387  self->allocate();
388 
389  SharedPtr<std::streambuf> buf = info->mapping->createBuffer();
390  std::istream is(buf.get());
391 
392  io::setStreamMetadataPtr(is, info->meta, /*transfer=*/true);
393 
394  NodeMaskType mask;
395  is.seekg(info->maskpos);
396  mask.load(is);
397 
398  is.seekg(info->bufpos);
399  io::readCompressedValues(is, self->mData, SIZE, mask, io::getHalfFloat(is));
400 
401  self->setOutOfCore(false);
402 }
403 
404 
405 template<typename T, Index Log2Dim>
406 inline bool
408 {
409  if (this->isOutOfCore()) {
410  delete mFileInfo;
411  mFileInfo = nullptr;
412  this->setOutOfCore(false);
413  return true;
414  }
415  return false;
416 }
417 
418 
420 
421 
422 // Partial specialization for bool ValueType
423 template<Index Log2Dim>
424 class LeafBuffer<bool, Log2Dim>
425 {
426 public:
428  using WordType = typename NodeMaskType::Word;
429  using ValueType = bool;
431 
432  static const Index WORD_COUNT = NodeMaskType::WORD_COUNT;
433  static const Index SIZE = 1 << 3 * Log2Dim;
434 
435  // These static declarations must be on separate lines to avoid VC9 compiler errors.
436  static const bool sOn;
437  static const bool sOff;
438 
440  LeafBuffer(bool on): mData(on) {}
441  LeafBuffer(const NodeMaskType& other): mData(other) {}
442  LeafBuffer(const LeafBuffer& other): mData(other.mData) {}
444  void fill(bool val) { mData.set(val); }
445  LeafBuffer& operator=(const LeafBuffer& b) { if (&b != this) { mData=b.mData; } return *this; }
446 
447  const bool& getValue(Index i) const
448  {
449  assert(i < SIZE);
450  // We can't use the ternary operator here, otherwise Visual C++ returns
451  // a reference to a temporary.
452  if (mData.isOn(i)) return sOn; else return sOff;
453  }
454  const bool& operator[](Index i) const { return this->getValue(i); }
455 
456  bool operator==(const LeafBuffer& other) const { return mData == other.mData; }
457  bool operator!=(const LeafBuffer& other) const { return mData != other.mData; }
458 
459  void setValue(Index i, bool val) { assert(i < SIZE); mData.set(i, val); }
460 
461  void swap(LeafBuffer& other) { if (&other != this) std::swap(mData, other.mData); }
462 
463  Index memUsage() const { return sizeof(*this); }
464  static Index size() { return SIZE; }
465 
468  WordType* data() { return &(mData.template getWord<WordType>(0)); }
471  const WordType* data() const { return const_cast<LeafBuffer*>(this)->data(); }
472 
473 private:
474  // Allow the parent LeafNode to access this buffer's data.
475  template<typename, Index> friend class LeafNode;
476 
478 }; // class LeafBuffer
479 
480 
485 template<Index Log2Dim> const bool LeafBuffer<bool, Log2Dim>::sOn = true;
486 template<Index Log2Dim> const bool LeafBuffer<bool, Log2Dim>::sOff = false;
487 
488 } // namespace tree
489 } // namespace OPENVDB_VERSION_NAME
490 } // namespace openvdb
491 
492 #endif // OPENVDB_TREE_LEAFBUFFER_HAS_BEEN_INCLUDED
FileInfo()
Definition: LeafBuffer.h:84
WordType StorageType
Definition: LeafBuffer.h:430
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:127
tbb::atomic< Index32 > type
The type of LeafBuffer::mOutOfCore.
Definition: LeafBuffer.h:44
void setValue(Index i, const ValueType &)
Set the i&#39;th value of this buffer to the specified value.
Definition: LeafBuffer.h:233
SharedPtr< MappedFile > Ptr
Definition: io.h:136
uint32_t Index32
Definition: Types.h:29
static const Index32 WORD_COUNT
Definition: NodeMasks.h:296
std::shared_ptr< T > SharedPtr
Definition: Types.h:91
LeafBuffer(const NodeMaskType &other)
Definition: LeafBuffer.h:441
typename NodeMaskType::Word WordType
Definition: LeafBuffer.h:428
Templated block class to hold specific data types and a fixed number of values determined by Log2Dim...
Definition: LeafNode.h:37
const WordType * data() const
Return a const pointer to the C-style array of words encoding the bits.
Definition: LeafBuffer.h:471
const ValueType & operator[](Index i) const
Return a const reference to the i&#39;th element of this buffer.
Definition: LeafBuffer.h:115
~LeafBuffer()
Definition: LeafBuffer.h:443
bool ValueType
Definition: LeafBuffer.h:429
bool allocate()
Allocate memory for this buffer if it has not already been allocated.
Definition: LeafBuffer.h:107
Bit mask for the internal and leaf nodes of VDB. This is a 64-bit implementation. ...
Definition: NodeMasks.h:288
LeafBuffer()
Default constructor.
Definition: LeafBuffer.h:92
const bool & operator[](Index i) const
Definition: LeafBuffer.h:454
void readCompressedValues(std::istream &is, ValueT *destBuf, Index destCount, const MaskT &valueMask, bool fromHalf)
Definition: Compression.h:465
bool operator==(const LeafBuffer &other) const
Definition: LeafBuffer.h:456
ValueType ValueType
Definition: LeafBuffer.h:77
LeafBuffer(const LeafBuffer &other)
Definition: LeafBuffer.h:442
~LeafBuffer()
Destructor.
Definition: LeafBuffer.h:203
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...
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h:102
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 ...
WordType * data()
Return a pointer to the C-style array of words encoding the bits.
Definition: LeafBuffer.h:468
LeafBuffer & operator=(const LeafBuffer &b)
Definition: LeafBuffer.h:445
LeafBuffer(PartialCreate, const ValueType &)
Construct a buffer but don&#39;t allocate memory for the full array of values.
Definition: LeafBuffer.h:98
static Index size()
Definition: LeafBuffer.h:464
void swap(LeafBuffer &other)
Definition: LeafBuffer.h:461
Definition: Exceptions.h:13
void swap(LeafBuffer &)
Exchange this buffer&#39;s values with the other buffer&#39;s values.
Definition: LeafBuffer.h:296
std::streamoff maskpos
Definition: LeafBuffer.h:86
const bool & getValue(Index i) const
Definition: LeafBuffer.h:447
io::MappedFile::Ptr mapping
Definition: LeafBuffer.h:87
bool operator!=(const LeafBuffer &other) const
Definition: LeafBuffer.h:457
static Index size()
Return the number of values contained in this buffer.
Definition: LeafBuffer.h:135
const ValueType & getValue(Index i) const
Return a const reference to the i&#39;th element of this buffer.
Definition: LeafBuffer.h:113
ValueType * mData
Definition: LeafBuffer.h:169
LeafBuffer & operator=(const LeafBuffer &)
Copy the other buffer&#39;s values into this buffer.
Definition: LeafBuffer.h:243
bool operator==(const Vec3< T0 > &v0, const Vec3< T1 > &v1)
Equality operator, does exact floating point comparisons.
Definition: Vec3.h:471
Index memUsage() const
Return the memory footprint of this buffer in bytes.
Definition: LeafBuffer.h:305
Index32 Index
Definition: Types.h:31
SharedPtr< io::StreamMetadata > meta
Definition: LeafBuffer.h:88
static const bool sOn
Definition: LeafBuffer.h:436
Index64 Word
Definition: NodeMasks.h:297
bool empty() const
Return true if memory for this buffer has not yet been allocated.
Definition: LeafBuffer.h:105
bool operator==(const LeafBuffer &) const
Return true if the contents of the other buffer exactly equal the contents of this buffer...
Definition: LeafBuffer.h:281
bool isOutOfCore() const
Return true if this buffer&#39;s values have not yet been read from disk.
Definition: LeafBuffer.h:103
Tag dispatch class that distinguishes constructors during file input.
Definition: Types.h:683
bool isExactlyEqual(const T0 &a, const T1 &b)
Return true if a is exactly equal to b.
Definition: Math.h:388
Index memUsage() const
Definition: LeafBuffer.h:463
void fill(const ValueType &)
Populate this buffer with a constant value.
Definition: LeafBuffer.h:268
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h:154
ValueType StorageType
Definition: LeafBuffer.h:78
static const bool sOff
Definition: LeafBuffer.h:437
FileInfo * mFileInfo
Definition: LeafBuffer.h:170
Array of fixed size 23Log2Dim that stores the voxel values of a LeafNode.
Definition: LeafBuffer.h:74
void load(std::istream &is)
Definition: NodeMasks.h:550
LeafBuffer(bool on)
Definition: LeafBuffer.h:440
std::streamoff bufpos
Definition: LeafBuffer.h:85
static const Index SIZE
Definition: LeafBuffer.h:80
const ValueType * data() const
Return a const pointer to the array of voxel values.
Definition: LeafBuffer.h:316
void setValue(Index i, bool val)
Definition: LeafBuffer.h:459
LeafBuffer()
Definition: LeafBuffer.h:439
void fill(bool val)
Definition: LeafBuffer.h:444