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