OpenVDB 13.0.1
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#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.
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.
128
129private:
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
183template<typename T, Index Log2Dim>
184inline
186 : mData(new ValueType[SIZE])
187{
188#ifdef OPENVDB_USE_DELAYED_LOADING
189 mOutOfCore = 0;
190#endif
191 this->fill(val);
192}
193
194
195template<typename T, Index Log2Dim>
196inline
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
211template<typename T, Index Log2Dim>
212inline
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 }
232 }
233#endif
234}
235
236
237template<typename T, Index Log2Dim>
238inline void
240{
241 OPENVDB_ASSERT(i < SIZE);
242 this->loadValues();
243 if (mData) mData[i] = val;
244}
245
246
247template<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
279template<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
293template<typename T, Index Log2Dim>
294inline 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
306template<typename T, Index Log2Dim>
307inline 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
321template<typename T, Index Log2Dim>
322inline 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
338template<typename T, Index Log2Dim>
339inline 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
355template<typename T, Index Log2Dim>
356inline Index
358{
359 size_t n = sizeof(*this);
360 n += SIZE * sizeof(ValueType);
361 return static_cast<Index>(n);
362}
363
364
365template<typename T, Index Log2Dim>
366inline 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
381template<typename T, Index Log2Dim>
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
397template<typename T, Index Log2Dim>
398inline const typename LeafBuffer<T, Log2Dim>::ValueType&
399LeafBuffer<T, Log2Dim>::at(Index i) const
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
410template<typename T, Index Log2Dim>
411inline bool
412LeafBuffer<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
427template<typename T, Index Log2Dim>
428inline void
429LeafBuffer<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
467template<typename T, Index Log2Dim>
468inline bool
469LeafBuffer<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
487template<Index Log2Dim>
488class LeafBuffer<bool, Log2Dim>
489{
490public:
492 using WordType = typename NodeMaskType::Word;
493 using ValueType = bool;
495
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
540private:
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
#define OPENVDB_ASSERT(X)
Definition Assert.h:41
Tag dispatch class that distinguishes constructors during file input.
Definition Types.h:760
LeafBuffer(const NodeMaskType &other)
Definition LeafBuffer.h:504
WordType StorageType
Definition LeafBuffer.h:494
util::NodeMask< Log2Dim > NodeMaskType
Definition LeafBuffer.h:491
bool operator!=(const LeafBuffer &other) const
Definition LeafBuffer.h:520
static const Index WORD_COUNT
Definition LeafBuffer.h:496
static const bool sOff
Definition LeafBuffer.h:500
typename NodeMaskType::Word WordType
Definition LeafBuffer.h:492
Index memUsage() const
Definition LeafBuffer.h:526
void swap(LeafBuffer &other)
Definition LeafBuffer.h:524
const WordType * data() const
Return a const pointer to the C-style array of words encoding the bits.
Definition LeafBuffer.h:535
LeafBuffer & operator=(const LeafBuffer &b)
Definition LeafBuffer.h:508
bool operator==(const LeafBuffer &other) const
Definition LeafBuffer.h:519
Index memUsageIfLoaded() const
Definition LeafBuffer.h:527
bool ValueType
Definition LeafBuffer.h:493
LeafBuffer(const LeafBuffer &other)
Definition LeafBuffer.h:505
LeafBuffer(bool on)
Definition LeafBuffer.h:503
const bool & operator[](Index i) const
Definition LeafBuffer.h:517
void fill(bool val)
Definition LeafBuffer.h:507
const bool & getValue(Index i) const
Definition LeafBuffer.h:510
const NodeMaskType & storage() const
Return raw LeafBuffer data.
Definition LeafBuffer.h:538
friend class LeafNode
Definition LeafBuffer.h:542
static Index size()
Definition LeafBuffer.h:528
static const Index SIZE
Definition LeafBuffer.h:497
static const bool sOn
Definition LeafBuffer.h:499
WordType * data()
Return a pointer to the C-style array of words encoding the bits.
Definition LeafBuffer.h:532
void setValue(Index i, bool val)
Definition LeafBuffer.h:522
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:249
~LeafBuffer()
Destructor.
Definition LeafBuffer.h:197
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:185
LeafBuffer(const LeafBuffer &)
Copy constructor.
Definition LeafBuffer.h:213
void fill(const ValueType &)
Populate this buffer with a constant value.
Definition LeafBuffer.h:295
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:109
void swap(LeafBuffer &)
Exchange this buffer's values with the other buffer's values.
Definition LeafBuffer.h:323
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:340
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:357
friend class LeafNode
Definition LeafBuffer.h:176
void setValue(Index i, const ValueType &)
Set the i'th value of this buffer to the specified value.
Definition LeafBuffer.h:239
static Index size()
Return the number of values contained in this buffer.
Definition LeafBuffer.h:118
const ValueType * data() const
Return a const pointer to the array of voxel values.
Definition LeafBuffer.h:367
static const Index SIZE
Definition LeafBuffer.h:37
ValueType * data()
Return a pointer to the array of voxel values.
Definition LeafBuffer.h:383
const ValueType & getValue(Index i) const
Return a const reference to the i'th element of this buffer.
Definition LeafBuffer.h:95
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:97
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
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:468
Index32 Index
Definition Types.h:34
constexpr T zeroVal()
Return the value of type T that corresponds to zero.
Definition Math.h:71
std::shared_ptr< T > SharedPtr
Definition Types.h:95
Definition Exceptions.h:13
#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