OpenVDB 12.0.0
 
Loading...
Searching...
No Matches
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
20class TestLeaf;
21
22namespace openvdb {
24namespace OPENVDB_VERSION_NAME {
25namespace 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
30template<typename T, Index Log2Dim>
32{
33public:
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.
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.
122
123private:
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
177template<typename T, Index Log2Dim>
178inline
180 : mData(new ValueType[SIZE])
181{
182#ifdef OPENVDB_USE_DELAYED_LOADING
183 mOutOfCore = 0;
184#endif
185 this->fill(val);
186}
187
188
189template<typename T, Index Log2Dim>
190inline
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
205template<typename T, Index Log2Dim>
206inline
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 }
226 }
227#endif
228}
229
230
231template<typename T, Index Log2Dim>
232inline void
234{
235 OPENVDB_ASSERT(i < SIZE);
236 this->loadValues();
237 if (mData) mData[i] = val;
238}
239
240
241template<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
273template<typename T, Index Log2Dim>
274inline 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
286template<typename T, Index Log2Dim>
287inline 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
301template<typename T, Index Log2Dim>
302inline 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
318template<typename T, Index Log2Dim>
319inline 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
335template<typename T, Index Log2Dim>
336inline Index
338{
339 size_t n = sizeof(*this);
340 n += SIZE * sizeof(ValueType);
341 return static_cast<Index>(n);
342}
343
344
345template<typename T, Index Log2Dim>
346inline 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
361template<typename T, Index Log2Dim>
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
377template<typename T, Index Log2Dim>
378inline const typename LeafBuffer<T, Log2Dim>::ValueType&
379LeafBuffer<T, Log2Dim>::at(Index i) const
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
390template<typename T, Index Log2Dim>
391inline bool
392LeafBuffer<T, Log2Dim>::deallocate()
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
407template<typename T, Index Log2Dim>
408inline void
409LeafBuffer<T, Log2Dim>::doLoad() const
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
447template<typename T, Index Log2Dim>
448inline bool
449LeafBuffer<T, Log2Dim>::detachFromFile()
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
467template<Index Log2Dim>
468class LeafBuffer<bool, Log2Dim>
469{
470public:
472 using WordType = typename NodeMaskType::Word;
473 using ValueType = bool;
475
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
517private:
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
#define OPENVDB_ASSERT(X)
Definition Assert.h:41
Tag dispatch class that distinguishes constructors during file input.
Definition Types.h:689
LeafBuffer(const NodeMaskType &other)
Definition LeafBuffer.h:484
WordType StorageType
Definition LeafBuffer.h:474
util::NodeMask< Log2Dim > NodeMaskType
Definition LeafBuffer.h:471
bool operator!=(const LeafBuffer &other) const
Definition LeafBuffer.h:500
static const Index WORD_COUNT
Definition LeafBuffer.h:476
static const bool sOff
Definition LeafBuffer.h:480
typename NodeMaskType::Word WordType
Definition LeafBuffer.h:472
Index memUsage() const
Definition LeafBuffer.h:506
void swap(LeafBuffer &other)
Definition LeafBuffer.h:504
const WordType * data() const
Return a const pointer to the C-style array of words encoding the bits.
Definition LeafBuffer.h:515
LeafBuffer & operator=(const LeafBuffer &b)
Definition LeafBuffer.h:488
bool operator==(const LeafBuffer &other) const
Definition LeafBuffer.h:499
Index memUsageIfLoaded() const
Definition LeafBuffer.h:507
bool ValueType
Definition LeafBuffer.h:473
LeafBuffer(const LeafBuffer &other)
Definition LeafBuffer.h:485
LeafBuffer(bool on)
Definition LeafBuffer.h:483
const bool & operator[](Index i) const
Definition LeafBuffer.h:497
void fill(bool val)
Definition LeafBuffer.h:487
const bool & getValue(Index i) const
Definition LeafBuffer.h:490
friend class LeafNode
Definition LeafBuffer.h:519
static Index size()
Definition LeafBuffer.h:508
static const Index SIZE
Definition LeafBuffer.h:477
static const bool sOn
Definition LeafBuffer.h:479
WordType * data()
Return a pointer to the C-style array of words encoding the bits.
Definition LeafBuffer.h:512
void setValue(Index i, bool val)
Definition LeafBuffer.h:502
Array of fixed size 23Log2Dim that stores the voxel values of a LeafNode.
Definition LeafBuffer.h:32
LeafBuffer & operator=(const LeafBuffer &)
Copy the other buffer's values into this buffer.
Definition LeafBuffer.h:243
~LeafBuffer()
Destructor.
Definition LeafBuffer.h:191
LeafBuffer(PartialCreate, const ValueType &)
Construct a buffer but don't allocate memory for the full array of values.
Definition LeafBuffer.h:62
ValueType StorageType
Definition LeafBuffer.h:35
LeafBuffer(const ValueType &)
Construct a buffer populated with the specified value.
Definition LeafBuffer.h:179
LeafBuffer(const LeafBuffer &)
Copy constructor.
Definition LeafBuffer.h:207
void fill(const ValueType &)
Populate this buffer with a constant value.
Definition LeafBuffer.h:275
util::NodeMask< Log2Dim > NodeMaskType
Definition LeafBuffer.h:36
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
void swap(LeafBuffer &)
Exchange this buffer's values with the other buffer's values.
Definition LeafBuffer.h:303
bool isOutOfCore() const
Return true if this buffer's values have not yet been read from disk.
Definition LeafBuffer.h:72
Index memUsage() const
Return the memory footprint of this buffer in bytes.
Definition LeafBuffer.h:320
bool empty() const
Return true if memory for this buffer has not yet been allocated.
Definition LeafBuffer.h:81
Index memUsageIfLoaded() const
Definition LeafBuffer.h:337
friend class LeafNode
Definition LeafBuffer.h:170
void setValue(Index i, const ValueType &)
Set the i'th value of this buffer to the specified value.
Definition LeafBuffer.h:233
static Index size()
Return the number of values contained in this buffer.
Definition LeafBuffer.h:112
const ValueType * data() const
Return a const pointer to the array of voxel values.
Definition LeafBuffer.h:347
static const Index SIZE
Definition LeafBuffer.h:37
ValueType * data()
Return a pointer to the array of voxel values.
Definition LeafBuffer.h:363
const ValueType & getValue(Index i) const
Return a const reference to the i'th element of this buffer.
Definition LeafBuffer.h:89
bool allocate()
Allocate memory for this buffer if it has not already been allocated.
Definition LeafBuffer.h:83
LeafBuffer()
Default constructor.
Definition LeafBuffer.h:51
T ValueType
Definition LeafBuffer.h:34
const ValueType & operator[](Index i) const
Return a const reference to the i'th element of 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:288
Bit mask for the internal and leaf nodes of VDB. This is a 64-bit implementation.
Definition NodeMasks.h:308
Index64 Word
Definition NodeMasks.h:316
static const Index32 WORD_COUNT
Definition NodeMasks.h:315
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 readCompressedValues(std::istream &is, ValueT *destBuf, Index destCount, const MaskT &valueMask, bool fromHalf)
Definition Compression.h:466
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 isExactlyEqual(const T0 &a, const T1 &b)
Return true if a is exactly equal to b.
Definition Math.h:443
Index32 Index
Definition Types.h:54
constexpr T zeroVal()
Return the value of type T that corresponds to zero.
Definition Math.h:70
std::shared_ptr< T > SharedPtr
Definition Types.h:114
Definition Exceptions.h:13
static pnanovdb_uint32_t allocate(pnanovdb_uint32_t *poffset, pnanovdb_uint32_t size, pnanovdb_uint32_t alignment)
Definition pnanovdb_validate_strides.h:20
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition version.h.in:121
#define OPENVDB_USE_VERSION_NAMESPACE
Definition version.h.in:218
#define OPENVDB_USE_DELAYED_LOADING
Definition version.h.in:143