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
9 #include <openvdb/util/NodeMasks.h>
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 {
22 OPENVDB_USE_VERSION_NAMESPACE
23 namespace OPENVDB_VERSION_NAME {
24 namespace tree {
25
26 namespace internal {
27
39 template<typename T>
40 struct LeafBufferFlags
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>
74 class LeafBuffer
75 {
76 public:
77 using ValueType = T;
78 using StorageType = ValueType;
79 using NodeMaskType = util::NodeMask<Log2Dim>;
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;
87 io::MappedFile::Ptr mapping;
88 SharedPtr<io::StreamMetadata> meta;
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 {
169 ValueType* mData;
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
193 LeafBuffer<T, Log2Dim>::LeafBuffer(const ValueType& val)
194 : mData(new ValueType[SIZE])
195 {
196 mOutOfCore = 0;
197 this->fill(val);
198 }
199
200
201 template<typename T, Index Log2Dim>
202 inline
203 LeafBuffer<T, Log2Dim>::~LeafBuffer()
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
215 LeafBuffer<T, Log2Dim>::LeafBuffer(const LeafBuffer& other)
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
233 LeafBuffer<T, Log2Dim>::setValue(Index i, const ValueType& val)
234 {
235 assert(i < SIZE);
236 this->loadValues();
237 if (mData) mData[i] = val;
238 }
239
240
241 template<typename T, Index Log2Dim>
242 inline LeafBuffer<T, Log2Dim>&
243 LeafBuffer<T, Log2Dim>::operator=(const LeafBuffer& other)
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
268 LeafBuffer<T, Log2Dim>::fill(const ValueType& val)
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
281 LeafBuffer<T, Log2Dim>::operator==(const LeafBuffer& other) const
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
296 LeafBuffer<T, Log2Dim>::swap(LeafBuffer& other)
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
305 LeafBuffer<T, Log2Dim>::memUsage() const
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*
316 LeafBuffer<T, Log2Dim>::data() const
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*
330 LeafBuffer<T, Log2Dim>::data()
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&
344 LeafBuffer<T, Log2Dim>::at(Index i) const
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
356 LeafBuffer<T, Log2Dim>::deallocate()
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
369 LeafBuffer<T, Log2Dim>::doLoad() const
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
407 LeafBuffer<T, Log2Dim>::detachFromFile()
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:
427 using NodeMaskType = util::NodeMask<Log2Dim>;
428 using WordType = typename NodeMaskType::Word;
429 using ValueType = bool;
430 using StorageType = WordType;
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
439 LeafBuffer() {}
440 LeafBuffer(bool on): mData(on) {}
441 LeafBuffer(const NodeMaskType& other): mData(other) {}
442 LeafBuffer(const LeafBuffer& other): mData(other.mData) {}
443 ~LeafBuffer() {}
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
477 NodeMaskType mData;
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
Compression.h
openvdb::v7_0::tree::LeafBuffer
Array of fixed size 23Log2Dim that stores the voxel values of a LeafNode.
Definition: LeafBuffer.h:74
openvdb::v7_0::tree::LeafBuffer< bool, Log2Dim >::operator==
bool operator==(const LeafBuffer &other) const
Definition: LeafBuffer.h:456
openvdb::v7_0::tree::LeafBuffer::FileInfo::meta
SharedPtr< io::StreamMetadata > meta
Definition: LeafBuffer.h:88
NodeMasks.h
openvdb::v7_0::io::getHalfFloat
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 ...
Types.h
openvdb::v7_0::tree::LeafBuffer< bool, Log2Dim >::data
const WordType * data() const
Return a const pointer to the C-style array of words encoding the bits.
Definition: LeafBuffer.h:471
openvdb::v7_0::io::MappedFile::Ptr
SharedPtr< MappedFile > Ptr
Definition: io.h:136
openvdb::v7_0::tree::LeafBuffer< bool, Log2Dim >::data
WordType * data()
Return a pointer to the C-style array of words encoding the bits.
Definition: LeafBuffer.h:468
openvdb::v7_0::tree::LeafBuffer::allocate
bool allocate()
Allocate memory for this buffer if it has not already been allocated.
Definition: LeafBuffer.h:107
openvdb::v7_0::tree::LeafBuffer::FileInfo::bufpos
std::streamoff bufpos
Definition: LeafBuffer.h:85
openvdb::v7_0::tree::LeafBuffer< bool, Log2Dim >::StorageType
WordType StorageType
Definition: LeafBuffer.h:430
openvdb::v7_0::tree::LeafBuffer< bool, Log2Dim >::~LeafBuffer
~LeafBuffer()
Definition: LeafBuffer.h:443
openvdb::v7_0::tree::LeafBuffer< bool, Log2Dim >::LeafBuffer
LeafBuffer()
Definition: LeafBuffer.h:439
openvdb::v7_0::tree::LeafBuffer< bool, Log2Dim >::WordType
typename NodeMaskType::Word WordType
Definition: LeafBuffer.h:428
openvdb::v7_0::SharedPtr
std::shared_ptr< T > SharedPtr
Definition: Types.h:91
openvdb::v7_0::tree::LeafBuffer< bool, Log2Dim >::operator[]
const bool & operator[](Index i) const
Definition: LeafBuffer.h:454
openvdb::v7_0::tree::internal::LeafBufferFlags::type
tbb::atomic< Index32 > type
The type of LeafBuffer::mOutOfCore.
Definition: LeafBuffer.h:44
openvdb::v7_0::tree::LeafBuffer::LeafBuffer
LeafBuffer()
Default constructor.
Definition: LeafBuffer.h:92
openvdb::v7_0::tree::LeafBuffer::mFileInfo
FileInfo * mFileInfo
Definition: LeafBuffer.h:170
openvdb::v7_0::tree::LeafBuffer< bool, Log2Dim >::setValue
void setValue(Index i, bool val)
Definition: LeafBuffer.h:459
openvdb::v7_0::io::setStreamMetadataPtr
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,...
openvdb::v7_0::tree::LeafBuffer< bool, Log2Dim >::sOff
static const bool sOff
Definition: LeafBuffer.h:437
openvdb::v7_0::tree::LeafBuffer::empty
bool empty() const
Return true if memory for this buffer has not yet been allocated.
Definition: LeafBuffer.h:105
openvdb::v7_0::tree::LeafBuffer< bool, Log2Dim >::fill
void fill(bool val)
Definition: LeafBuffer.h:444
openvdb::v7_0::tree::LeafBuffer::FileInfo
Definition: LeafBuffer.h:82
openvdb::v7_0::util::NodeMask
Bit mask for the internal and leaf nodes of VDB. This is a 64-bit implementation.
Definition: NodeMasks.h:288
openvdb::v7_0::io::readCompressedValues
void readCompressedValues(std::istream &is, ValueT *destBuf, Index destCount, const MaskT &valueMask, bool fromHalf)
Definition: Compression.h:465
openvdb::v7_0::math::operator==
bool operator==(const Vec3< T0 > &v0, const Vec3< T1 > &v1)
Equality operator, does exact floating point comparisons.
Definition: Vec3.h:471
openvdb::v7_0::tree::LeafBuffer::mData
ValueType * mData
Definition: LeafBuffer.h:169
openvdb::v7_0::tree::LeafBuffer< bool, Log2Dim >::LeafBuffer
LeafBuffer(const NodeMaskType &other)
Definition: LeafBuffer.h:441
openvdb::v7_0::tree::LeafNode
Templated block class to hold specific data types and a fixed number of values determined by Log2Dim....
Definition: LeafNode.h:37
openvdb::v7_0::tree::LeafBuffer::FileInfo::mapping
io::MappedFile::Ptr mapping
Definition: LeafBuffer.h:87
openvdb::v7_0::tree::LeafBuffer< bool, Log2Dim >::ValueType
bool ValueType
Definition: LeafBuffer.h:429
OPENVDB_USE_VERSION_NAMESPACE
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h:154
openvdb::v7_0::tree::LeafBuffer< bool, Log2Dim >::LeafBuffer
LeafBuffer(const LeafBuffer &other)
Definition: LeafBuffer.h:442
openvdb::v7_0::tree::LeafBuffer< ValueType, Log2Dim >::ValueType
ValueType ValueType
Definition: LeafBuffer.h:77
openvdb::v7_0::tree::LeafBuffer< bool, Log2Dim >::LeafBuffer
LeafBuffer(bool on)
Definition: LeafBuffer.h:440
openvdb::v7_0::tree::LeafBuffer< bool, Log2Dim >::size
static Index size()
Definition: LeafBuffer.h:464
openvdb::v7_0::Index
Index32 Index
Definition: Types.h:31
openvdb::v7_0::tree::LeafBuffer::LeafBuffer
LeafBuffer(PartialCreate, const ValueType &)
Construct a buffer but don't allocate memory for the full array of values.
Definition: LeafBuffer.h:98
openvdb::v7_0::math::isExactlyEqual
bool isExactlyEqual(const T0 &a, const T1 &b)
Return true if a is exactly equal to b.
Definition: Math.h:388
openvdb::v7_0::tree::LeafBuffer::FileInfo::maskpos
std::streamoff maskpos
Definition: LeafBuffer.h:86
OPENVDB_VERSION_NAME
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h:102
openvdb::v7_0::tree::LeafBuffer::operator!=
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
openvdb::v7_0::Index32
uint32_t Index32
Definition: Types.h:29
openvdb::v7_0::tree::LeafBuffer< bool, Log2Dim >::sOn
static const bool sOn
Definition: LeafBuffer.h:436
openvdb::v7_0::tree::internal::LeafBufferFlags
Definition: LeafBuffer.h:40
openvdb::v7_0::PartialCreate
Tag dispatch class that distinguishes constructors during file input.
Definition: Types.h:683
openvdb::v7_0::tree::LeafBuffer::getValue
const ValueType & getValue(Index i) const
Return a const reference to the i'th element of this buffer.
Definition: LeafBuffer.h:113
openvdb::v7_0::tree::LeafBuffer::FileInfo::FileInfo
FileInfo()
Definition: LeafBuffer.h:84
openvdb
Definition: Exceptions.h:13
openvdb::v7_0::tree::LeafBuffer::isOutOfCore
bool isOutOfCore() const
Return true if this buffer's values have not yet been read from disk.
Definition: LeafBuffer.h:103
openvdb::v7_0::tree::LeafBuffer::operator[]
const ValueType & operator[](Index i) const
Return a const reference to the i'th element of this buffer.
Definition: LeafBuffer.h:115
openvdb::v7_0::tree::LeafBuffer< bool, Log2Dim >::getValue
const bool & getValue(Index i) const
Definition: LeafBuffer.h:447
openvdb::v7_0::tree::LeafBuffer< ValueType, Log2Dim >::StorageType
ValueType StorageType
Definition: LeafBuffer.h:78
openvdb::v7_0::tree::LeafBuffer< bool, Log2Dim >::swap
void swap(LeafBuffer &other)
Definition: LeafBuffer.h:461
openvdb::v7_0::tree::LeafBuffer< bool, Log2Dim >::operator!=
bool operator!=(const LeafBuffer &other) const
Definition: LeafBuffer.h:457
openvdb::v7_0::tree::LeafBuffer< bool, Log2Dim >::memUsage
Index memUsage() const
Definition: LeafBuffer.h:463
openvdb::v7_0::tree::LeafBuffer::size
static Index size()
Return the number of values contained in this buffer.
Definition: LeafBuffer.h:135
openvdb::v7_0::util::NodeMask::Word
Index64 Word
Definition: NodeMasks.h:297
openvdb::v7_0::tree::LeafBuffer< bool, Log2Dim >::operator=
LeafBuffer & operator=(const LeafBuffer &b)
Definition: LeafBuffer.h:445