OpenVDB 12.0.0
 
Loading...
Searching...
No Matches
LeafManager.h
Go to the documentation of this file.
1// Copyright Contributors to the OpenVDB Project
2// SPDX-License-Identifier: Apache-2.0
3
4/// @file LeafManager.h
5///
6/// @brief A LeafManager manages a linear array of pointers to a given tree's
7/// leaf nodes, as well as optional auxiliary buffers (one or more per leaf)
8/// that can be swapped with the leaf nodes' voxel data buffers.
9/// @details The leaf array is useful for multithreaded computations over
10/// leaf voxels in a tree with static topology but varying voxel values.
11/// The auxiliary buffers are convenient for temporal integration.
12/// Efficient methods are provided for multithreaded swapping and synching
13/// (i.e., copying the contents) of these buffers.
14
15#ifndef OPENVDB_TREE_LEAFMANAGER_HAS_BEEN_INCLUDED
16#define OPENVDB_TREE_LEAFMANAGER_HAS_BEEN_INCLUDED
17
18#include <openvdb/Types.h>
19#include <openvdb/util/Assert.h>
20#include "RootNode.h" // for NodeChain
21#include <tbb/blocked_range.h>
22#include <tbb/parallel_for.h>
23#include <tbb/parallel_reduce.h>
24#include <deque>
25#include <functional>
26#include <type_traits>
27
28
29namespace openvdb {
31namespace OPENVDB_VERSION_NAME {
32namespace tree {
33
34namespace leafmgr {
35
36//@{
37/// Useful traits for Tree types
38template<typename TreeT> struct TreeTraits {
39 static const bool IsConstTree = false;
40 using LeafIterType = typename TreeT::LeafIter;
41};
42template<typename TreeT> struct TreeTraits<const TreeT> {
43 static const bool IsConstTree = true;
44 using LeafIterType = typename TreeT::LeafCIter;
45};
46//@}
47
48} // namespace leafmgr
49
50
51/// This helper class implements LeafManager methods that need to be
52/// specialized for const vs. non-const trees.
53template<typename ManagerT>
55{
56 using RangeT = typename ManagerT::RangeType;
57 using LeafT = typename ManagerT::LeafType;
58 using BufT = typename ManagerT::BufferType;
59
60 static inline void doSwapLeafBuffer(const RangeT& r, size_t auxBufferIdx,
61 LeafT** leafs, BufT* bufs, size_t bufsPerLeaf)
62 {
63 for (size_t n = r.begin(), m = r.end(), N = bufsPerLeaf; n != m; ++n) {
64 leafs[n]->swap(bufs[n * N + auxBufferIdx]);
65 }
66 }
67};
68
69
70////////////////////////////////////////
71
72
73/// @brief This class manages a linear array of pointers to a given tree's
74/// leaf nodes, as well as optional auxiliary buffers (one or more per leaf)
75/// that can be swapped with the leaf nodes' voxel data buffers.
76/// @details The leaf array is useful for multithreaded computations over
77/// leaf voxels in a tree with static topology but varying voxel values.
78/// The auxiliary buffers are convenient for temporal integration.
79/// Efficient methods are provided for multithreaded swapping and sync'ing
80/// (i.e., copying the contents) of these buffers.
81///
82/// @note Buffer index 0 denotes a leaf node's internal voxel data buffer.
83/// Any auxiliary buffers are indexed starting from one.
84template<typename TreeT>
86{
87public:
88 using TreeType = TreeT;
89 using ValueType = typename TreeT::ValueType;
90 using RootNodeType = typename TreeT::RootNodeType;
91 using NonConstLeafType = typename TreeType::LeafNodeType;
95 using NonConstBufferType = typename LeafType::Buffer;
97 using RangeType = tbb::blocked_range<size_t>; // leaf index range
98 static const Index DEPTH = 2; // root + leaf nodes
99
101
103 {
104 public:
106 {
107 public:
108 Iterator(const LeafRange& range, size_t pos): mRange(range), mPos(pos)
109 {
110 OPENVDB_ASSERT(this->isValid());
111 }
112 Iterator(const Iterator&) = default;
113 Iterator& operator=(const Iterator&) = default;
114 /// Advance to the next leaf node.
115 Iterator& operator++() { ++mPos; return *this; }
116 /// Return a reference to the leaf node to which this iterator is pointing.
117 LeafType& operator*() const { return mRange.mLeafManager.leaf(mPos); }
118 /// Return a pointer to the leaf node to which this iterator is pointing.
119 LeafType* operator->() const { return &(this->operator*()); }
120 /// @brief Return the nth buffer for the leaf node to which this iterator is pointing,
121 /// where n = @a bufferIdx and n = 0 corresponds to the leaf node's own buffer.
122 BufferType& buffer(size_t bufferIdx)
123 {
124 return mRange.mLeafManager.getBuffer(mPos, bufferIdx);
125 }
126 /// Return the index into the leaf array of the current leaf node.
127 size_t pos() const { return mPos; }
128 /// Return @c true if the position of this iterator is in a valid range.
129 bool isValid() const { return mPos>=mRange.mBegin && mPos<=mRange.mEnd; }
130 /// Return @c true if this iterator is not yet exhausted.
131 bool test() const { return mPos < mRange.mEnd; }
132 /// Return @c true if this iterator is not yet exhausted.
133 operator bool() const { return this->test(); }
134 /// Return @c true if this iterator is exhausted.
135 bool empty() const { return !this->test(); }
136 bool operator!=(const Iterator& other) const
137 {
138 return (mPos != other.mPos) || (&mRange != &other.mRange);
139 }
140 bool operator==(const Iterator& other) const { return !(*this != other); }
141 const LeafRange& leafRange() const { return mRange; }
142
143 private:
144 const LeafRange& mRange;
145 size_t mPos;
146 };// end Iterator
147
148 LeafRange(size_t begin, size_t end, const LeafManager& leafManager, size_t grainSize=1)
149 : mEnd(end)
150 , mBegin(begin)
151 , mGrainSize(grainSize)
152 , mLeafManager(leafManager)
153 {
154 }
155
156 Iterator begin() const {return Iterator(*this, mBegin);}
157
158 Iterator end() const {return Iterator(*this, mEnd);}
159
160 size_t size() const { return mEnd - mBegin; }
161
162 size_t grainsize() const { return mGrainSize; }
163
164 const LeafManager& leafManager() const { return mLeafManager; }
165
166 bool empty() const {return !(mBegin < mEnd);}
167
168 bool is_divisible() const {return mGrainSize < this->size();}
169
170 LeafRange(LeafRange& r, tbb::split)
171 : mEnd(r.mEnd)
172 , mBegin(doSplit(r))
173 , mGrainSize(r.mGrainSize)
174 , mLeafManager(r.mLeafManager)
175 {
176 }
177
178 private:
179 size_t mEnd, mBegin, mGrainSize;
180 const LeafManager& mLeafManager;
181
182 static size_t doSplit(LeafRange& r)
183 {
185 size_t middle = r.mBegin + (r.mEnd - r.mBegin) / 2u;
186 r.mEnd = middle;
187 return middle;
188 }
189 };// end of LeafRange
190
191 /// @brief Constructor from a tree reference and an auxiliary buffer count
192 /// @note The default is no auxiliary buffers
193 LeafManager(TreeType& tree, size_t auxBuffersPerLeaf=0, bool serial=false)
194 : mTree(&tree)
195 , mLeafCount(0)
196 , mAuxBufferCount(0)
197 , mAuxBuffersPerLeaf(auxBuffersPerLeaf)
198 {
199 this->rebuild(serial);
200 }
201
202 /// @brief Construct directly from an existing array of leafnodes.
203 /// @warning The leafnodes are implicitly assumed to exist in the
204 /// input @a tree.
206 size_t auxBuffersPerLeaf=0, bool serial=false)
207 : mTree(&tree)
208 , mLeafCount(end-begin)
209 , mAuxBufferCount(0)
210 , mAuxBuffersPerLeaf(auxBuffersPerLeaf)
211 , mLeafPtrs(new LeafType*[mLeafCount])
212 , mLeafs(mLeafPtrs.get())
213 {
214 size_t n = mLeafCount;
215 LeafType **target = mLeafs, **source = begin;
216 while (n--) *target++ = *source++;
217 if (auxBuffersPerLeaf) this->initAuxBuffers(serial);
218 }
219
220 /// Shallow copy constructor called by tbb::parallel_for() threads
221 ///
222 /// @note This should never get called directly
224 : mTree(other.mTree)
225 , mLeafCount(other.mLeafCount)
226 , mAuxBufferCount(other.mAuxBufferCount)
227 , mAuxBuffersPerLeaf(other.mAuxBuffersPerLeaf)
228 , mLeafs(other.mLeafs)
229 , mAuxBuffers(other.mAuxBuffers)
230 , mTask(other.mTask)
231 {
232 }
233
234 /// @brief (Re)initialize by resizing (if necessary) and repopulating the leaf array
235 /// and by deleting existing auxiliary buffers and allocating new ones.
236 /// @details Call this method if the tree's topology, and therefore the number
237 /// of leaf nodes, changes. New auxiliary buffers are initialized with copies
238 /// of corresponding leaf node buffers.
239 void rebuild(bool serial=false)
240 {
241 this->initLeafArray(serial);
242 this->initAuxBuffers(serial);
243 }
244 //@{
245 /// Repopulate the leaf array and delete and reallocate auxiliary buffers.
246 void rebuild(size_t auxBuffersPerLeaf, bool serial=false)
247 {
248 mAuxBuffersPerLeaf = auxBuffersPerLeaf;
249 this->rebuild(serial);
250 }
251 void rebuild(TreeType& tree, bool serial=false)
252 {
253 mTree = &tree;
254 this->rebuild(serial);
255 }
256 void rebuild(TreeType& tree, size_t auxBuffersPerLeaf, bool serial=false)
257 {
258 mTree = &tree;
259 mAuxBuffersPerLeaf = auxBuffersPerLeaf;
260 this->rebuild(serial);
261 }
262 //@}
263 /// @brief Change the number of auxiliary buffers.
264 /// @details If auxBuffersPerLeaf is 0, all existing auxiliary buffers are deleted.
265 /// New auxiliary buffers are initialized with copies of corresponding leaf node buffers.
266 /// This method does not rebuild the leaf array.
267 void rebuildAuxBuffers(size_t auxBuffersPerLeaf, bool serial=false)
268 {
269 mAuxBuffersPerLeaf = auxBuffersPerLeaf;
270 this->initAuxBuffers(serial);
271 }
272 /// @brief Remove the auxiliary buffers, but don't rebuild the leaf array.
274
275 /// @brief Remove the auxiliary buffers and rebuild the leaf array.
276 void rebuildLeafArray(bool serial = false)
277 {
278 this->removeAuxBuffers();
279 this->initLeafArray(serial);
280 }
281
282 /// @brief Return the total number of allocated auxiliary buffers.
283 size_t auxBufferCount() const { return mAuxBufferCount; }
284 /// @brief Return the number of auxiliary buffers per leaf node.
285 size_t auxBuffersPerLeaf() const { return mAuxBuffersPerLeaf; }
286
287 /// @brief Return the number of leaf nodes.
288 size_t leafCount() const { return mLeafCount; }
289
290 /// @brief Return the number of active voxels in the leaf nodes.
291 /// @note Multi-threaded for better performance than Tree::activeLeafVoxelCount
293 {
294 return tbb::parallel_reduce(this->leafRange(), Index64(0),
295 [] (const LeafRange& range, Index64 sum) -> Index64 {
296 for (const auto& leaf: range) { sum += leaf.onVoxelCount(); }
297 return sum;
298 },
299 [] (Index64 n, Index64 m) -> Index64 { return n + m; });
300 }
301
302 /// Return a const reference to tree associated with this manager.
303 const TreeType& tree() const { return *mTree; }
304
305 /// Return a reference to the tree associated with this manager.
306 TreeType& tree() { return *mTree; }
307
308 /// Return a const reference to root node associated with this manager.
309 const RootNodeType& root() const { return mTree->root(); }
310
311 /// Return a reference to the root node associated with this manager.
312 RootNodeType& root() { return mTree->root(); }
313
314 /// Return @c true if the tree associated with this manager is immutable.
315 bool isConstTree() const { return this->IsConstTree; }
316
317 /// @brief Return a pointer to the leaf node at index @a leafIdx in the array.
318 /// @note For performance reasons no range check is performed (other than an assertion)!
319 LeafType& leaf(size_t leafIdx) const { OPENVDB_ASSERT(leafIdx<mLeafCount); return *mLeafs[leafIdx]; }
320
321 /// @brief Return the leaf or auxiliary buffer for the leaf node at index @a leafIdx.
322 /// If @a bufferIdx is zero, return the leaf buffer, otherwise return the nth
323 /// auxiliary buffer, where n = @a bufferIdx - 1.
324 ///
325 /// @note For performance reasons no range checks are performed on the inputs
326 /// (other than assertions)! Since auxiliary buffers, unlike leaf buffers,
327 /// might not exist, be especially careful when specifying the @a bufferIdx.
328 /// @note For const trees, this method always returns a reference to a const buffer.
329 /// It is safe to @c const_cast and modify any auxiliary buffer (@a bufferIdx > 0),
330 /// but it is not safe to modify the leaf buffer (@a bufferIdx = 0).
331 BufferType& getBuffer(size_t leafIdx, size_t bufferIdx) const
332 {
333 OPENVDB_ASSERT(leafIdx < mLeafCount);
334 OPENVDB_ASSERT(bufferIdx == 0 || bufferIdx - 1 < mAuxBuffersPerLeaf);
335 return bufferIdx == 0 ? mLeafs[leafIdx]->buffer()
336 : mAuxBuffers[leafIdx * mAuxBuffersPerLeaf + bufferIdx - 1];
337 }
338
339 /// @brief Return a @c tbb::blocked_range of leaf array indices.
340 ///
341 /// @note Consider using leafRange() instead, which provides access methods
342 /// to leaf nodes and buffers.
343 RangeType getRange(size_t grainsize = 1) const { return RangeType(0, mLeafCount, grainsize); }
344
345 /// Return a TBB-compatible LeafRange.
346 LeafRange leafRange(size_t grainsize = 1) const
347 {
348 return LeafRange(0, mLeafCount, *this, grainsize);
349 }
350
351 /// @brief Swap each leaf node's buffer with the nth corresponding auxiliary buffer,
352 /// where n = @a bufferIdx.
353 /// @return @c true if the swap was successful
354 /// @param bufferIdx index of the buffer that will be swapped with
355 /// the corresponding leaf node buffer
356 /// @param serial if false, swap buffers in parallel using multiple threads.
357 /// @note Recall that the indexing of auxiliary buffers is 1-based, since
358 /// buffer index 0 denotes the leaf node buffer. So buffer index 1 denotes
359 /// the first auxiliary buffer.
360 bool swapLeafBuffer(size_t bufferIdx, bool serial = false)
361 {
362 namespace ph = std::placeholders;
363 if (bufferIdx == 0 || bufferIdx > mAuxBuffersPerLeaf || this->isConstTree()) return false;
364 mTask = std::bind(&LeafManager::doSwapLeafBuffer, ph::_1, ph::_2, bufferIdx - 1);
365 this->cook(serial ? 0 : 512);
366 return true;//success
367 }
368 /// @brief Swap any two buffers for each leaf node.
369 /// @note Recall that the indexing of auxiliary buffers is 1-based, since
370 /// buffer index 0 denotes the leaf node buffer. So buffer index 1 denotes
371 /// the first auxiliary buffer.
372 bool swapBuffer(size_t bufferIdx1, size_t bufferIdx2, bool serial = false)
373 {
374 namespace ph = std::placeholders;
375 const size_t b1 = std::min(bufferIdx1, bufferIdx2);
376 const size_t b2 = std::max(bufferIdx1, bufferIdx2);
377 if (b1 == b2 || b2 > mAuxBuffersPerLeaf) return false;
378 if (b1 == 0) {
379 if (this->isConstTree()) return false;
380 mTask = std::bind(&LeafManager::doSwapLeafBuffer, ph::_1, ph::_2, b2-1);
381 } else {
382 mTask = std::bind(&LeafManager::doSwapAuxBuffer, ph::_1, ph::_2, b1-1, b2-1);
383 }
384 this->cook(serial ? 0 : 512);
385 return true;//success
386 }
387
388 /// @brief Sync up the specified auxiliary buffer with the corresponding leaf node buffer.
389 /// @return @c true if the sync was successful
390 /// @param bufferIdx index of the buffer that will contain a
391 /// copy of the corresponding leaf node buffer
392 /// @param serial if false, sync buffers in parallel using multiple threads.
393 /// @note Recall that the indexing of auxiliary buffers is 1-based, since
394 /// buffer index 0 denotes the leaf node buffer. So buffer index 1 denotes
395 /// the first auxiliary buffer.
396 bool syncAuxBuffer(size_t bufferIdx, bool serial = false)
397 {
398 namespace ph = std::placeholders;
399 if (bufferIdx == 0 || bufferIdx > mAuxBuffersPerLeaf) return false;
400 mTask = std::bind(&LeafManager::doSyncAuxBuffer, ph::_1, ph::_2, bufferIdx - 1);
401 this->cook(serial ? 0 : 64);
402 return true;//success
403 }
404
405 /// @brief Sync up all auxiliary buffers with their corresponding leaf node buffers.
406 /// @return true if the sync was successful
407 /// @param serial if false, sync buffers in parallel using multiple threads.
408 bool syncAllBuffers(bool serial = false)
409 {
410 namespace ph = std::placeholders;
411 switch (mAuxBuffersPerLeaf) {
412 case 0: return false;//nothing to do
413 case 1: mTask = std::bind(&LeafManager::doSyncAllBuffers1, ph::_1, ph::_2); break;
414 case 2: mTask = std::bind(&LeafManager::doSyncAllBuffers2, ph::_1, ph::_2); break;
415 default: mTask = std::bind(&LeafManager::doSyncAllBuffersN, ph::_1, ph::_2); break;
416 }
417 this->cook(serial ? 0 : 64);
418 return true;//success
419 }
420
421 /// @brief Threaded method that applies a user-supplied functor
422 /// to each leaf node in the LeafManager.
423 ///
424 /// @details The user-supplied functor needs to define the methods
425 /// required for tbb::parallel_for.
426 ///
427 /// @param op user-supplied functor, see examples for interface details.
428 /// @param threaded optional toggle to disable threading, on by default.
429 /// @param grainSize optional parameter to specify the grainsize
430 /// for threading, one by default.
431 ///
432 /// @warning The functor object is deep-copied to create TBB tasks.
433 /// This allows the function to use non-thread-safe members
434 /// like a ValueAccessor.
435 ///
436 /// @par Example:
437 /// @code
438 /// // Functor to offset a tree's voxel values with values from another tree.
439 /// template<typename TreeType>
440 /// struct OffsetOp
441 /// {
442 /// using Accessor = tree::ValueAccessor<const TreeType>;
443 ///
444 /// OffsetOp(const TreeType& tree): mRhsTreeAcc(tree) {}
445 ///
446 /// template <typename LeafNodeType>
447 /// void operator()(LeafNodeType &lhsLeaf, size_t) const
448 /// {
449 /// const LeafNodeType *rhsLeaf = mRhsTreeAcc.probeConstLeaf(lhsLeaf.origin());
450 /// if (rhsLeaf) {
451 /// typename LeafNodeType::ValueOnIter iter = lhsLeaf.beginValueOn();
452 /// for (; iter; ++iter) {
453 /// iter.setValue(iter.getValue() + rhsLeaf->getValue(iter.pos()));
454 /// }
455 /// }
456 /// }
457 /// Accessor mRhsTreeAcc;
458 /// };
459 ///
460 /// // usage:
461 /// tree::LeafManager<FloatTree> leafNodes(lhsTree);
462 /// leafNodes.foreach(OffsetOp<FloatTree>(rhsTree));
463 ///
464 /// // A functor that performs a min operation between different auxiliary buffers.
465 /// template<typename LeafManagerType>
466 /// struct MinOp
467 /// {
468 /// using BufferType = typename LeafManagerType::BufferType;
469 ///
470 /// MinOp(LeafManagerType& leafNodes): mLeafs(leafNodes) {}
471 ///
472 /// template <typename LeafNodeType>
473 /// void operator()(LeafNodeType &leaf, size_t leafIndex) const
474 /// {
475 /// // get the first buffer
476 /// BufferType& buffer = mLeafs.getBuffer(leafIndex, 1);
477 ///
478 /// // min ...
479 /// }
480 /// LeafManagerType& mLeafs;
481 /// };
482 /// @endcode
483 template<typename LeafOp>
484 void foreach(const LeafOp& op, bool threaded = true, size_t grainSize=1)
485 {
486 LeafTransformer<LeafOp> transform(op);
487 transform.run(this->leafRange(grainSize), threaded);
488 }
489
490 /// @brief Threaded method that applies a user-supplied functor
491 /// to each leaf node in the LeafManager. Unlike foreach
492 /// (defined above) this method performs a reduction on
493 /// all the leaf nodes.
494 ///
495 /// @details The user-supplied functor needs to define the methods
496 /// required for tbb::parallel_reduce.
497 ///
498 /// @param op user-supplied functor, see examples for interface details.
499 /// @param threaded optional toggle to disable threading, on by default.
500 /// @param grainSize optional parameter to specify the grainsize
501 /// for threading, one by default.
502 ///
503 /// @warning The functor object is deep-copied to create TBB tasks.
504 /// This allows the function to use non-thread-safe members
505 /// like a ValueAccessor.
506 ///
507 /// @par Example:
508 /// @code
509 /// // Functor to count the number of negative (active) leaf values
510 /// struct CountOp
511 /// {
512 /// CountOp() : mCounter(0) {}
513 /// CountOp(const CountOp &other) : mCounter(other.mCounter) {}
514 /// CountOp(const CountOp &other, tbb::split) : mCounter(0) {}
515 /// template <typename LeafNodeType>
516 /// void operator()(LeafNodeType &leaf, size_t)
517 /// {
518 /// typename LeafNodeType::ValueOnIter iter = leaf.beginValueOn();
519 /// for (; iter; ++iter) if (*iter < 0.0f) ++mCounter;
520 /// }
521 /// void join(const CountOp &other) {mCounter += other.mCounter;}
522 /// size_t mCounter;
523 /// };
524 ///
525 /// // usage:
526 /// tree::LeafManager<FloatTree> leafNodes(tree);
527 /// MinValueOp min;
528 /// leafNodes.reduce(min);
529 /// std::cerr << "Number of negative active voxels = " << min.mCounter << std::endl;
530 ///
531 /// @endcode
532 template<typename LeafOp>
533 void reduce(LeafOp& op, bool threaded = true, size_t grainSize=1)
534 {
535 LeafReducer<LeafOp> transform(op);
536 transform.run(this->leafRange(grainSize), threaded);
537 }
538
539 /// @brief Generate a linear array of prefix sums of offsets into the
540 /// active voxels in the leafs. So @a offsets[n]+m is the offset to the
541 /// mth active voxel in the nth leaf node (useful for
542 /// user-managed value buffers, e.g. in tools/LevelSetAdvect.h).
543 /// @return The total number of active values in the leaf nodes
544 /// @param offsets array of prefix sums of offsets to active voxels
545 /// @param size on input, the size of @a offsets; on output, its new size
546 /// @param grainSize optional grain size for threading
547 /// @details If @a offsets is @c nullptr or @a size is smaller than the
548 /// total number of active voxels (the return value) then @a offsets
549 /// is reallocated and @a size equals the total number of active voxels.
550 size_t getPrefixSum(size_t*& offsets, size_t& size, size_t grainSize=1) const
551 {
552 if (offsets == nullptr || size < mLeafCount) {
553 delete [] offsets;
554 offsets = new size_t[mLeafCount];
555 size = mLeafCount;
556 }
557 size_t prefix = 0;
558 if ( grainSize > 0 ) {
559 PrefixSum tmp(this->leafRange( grainSize ), offsets, prefix);
560 } else {// serial
561 for (size_t i=0; i<mLeafCount; ++i) {
562 offsets[i] = prefix;
563 prefix += mLeafs[i]->onVoxelCount();
564 }
565 }
566 return prefix;
567 }
568
569 ////////////////////////////////////////////////////////////////////////////////////
570 // All methods below are for internal use only and should never be called directly
571
572 /// Used internally by tbb::parallel_for() - never call it directly!
573 void operator()(const RangeType& r) const
574 {
575 if (mTask) mTask(const_cast<LeafManager*>(this), r);
576 else OPENVDB_THROW(ValueError, "task is undefined");
577 }
578
579private:
580
581 void initLeafArray(bool serial = false)
582 {
583 // Build an array of all nodes that have leaf nodes as their immediate children
584
585 using NodeChainT = typename NodeChain<RootNodeType, RootNodeType::LEVEL>::Type;
586 using NonConstLeafParentT = typename NodeChainT::template Get</*Level=*/1>;
587 using LeafParentT = typename CopyConstness<TreeType, NonConstLeafParentT>::Type;
588
589 std::deque<LeafParentT*> leafParents;
590 if constexpr(std::is_same<NonConstLeafParentT, RootNodeType>::value) {
591 leafParents.emplace_back(&mTree->root());
592 }
593 else {
594 mTree->getNodes(leafParents);
595 }
596
597 // Compute the leaf counts for each node
598
599 std::vector<Index64> leafCounts;
600 if (serial) {
601 leafCounts.reserve(leafParents.size());
602 for (LeafParentT* leafParent : leafParents) {
603 leafCounts.push_back(leafParent->childCount());
604 }
605 } else {
606 leafCounts.resize(leafParents.size());
607 tbb::parallel_for(
608 // with typical node sizes and SSE enabled, there are only a handful
609 // of instructions executed per-operation with a default grainsize
610 // of 1, so increase to 64 to reduce parallel scheduling overhead
611 tbb::blocked_range<size_t>(0, leafParents.size(), /*grainsize=*/64),
612 [&](tbb::blocked_range<size_t>& range)
613 {
614 for (size_t i = range.begin(); i < range.end(); i++) {
615 leafCounts[i] = leafParents[i]->childCount();
616 }
617 }
618 );
619 }
620
621 // Turn leaf counts into a cumulative histogram and obtain total leaf count
622
623 for (size_t i = 1; i < leafCounts.size(); i++) {
624 leafCounts[i] += leafCounts[i-1];
625 }
626
627 const size_t leafCount = leafCounts.empty() ? 0 : leafCounts.back();
628
629 // Allocate (or deallocate) the leaf pointer array
630
631 if (leafCount != mLeafCount) {
632 if (leafCount > 0) {
633 mLeafPtrs.reset(new LeafType*[leafCount]);
634 mLeafs = mLeafPtrs.get();
635 } else {
636 mLeafPtrs.reset();
637 mLeafs = nullptr;
638 }
639 mLeafCount = leafCount;
640 }
641
642 if (mLeafCount == 0) return;
643
644 // Populate the leaf node pointers
645
646 if (serial) {
647 LeafType** leafPtr = mLeafs;
648 for (LeafParentT* leafParent : leafParents) {
649 for (auto iter = leafParent->beginChildOn(); iter; ++iter) {
650 *leafPtr++ = &iter.getValue();
651 }
652 }
653 } else {
654 tbb::parallel_for(
655 tbb::blocked_range<size_t>(0, leafParents.size()),
656 [&](tbb::blocked_range<size_t>& range)
657 {
658 size_t i = range.begin();
659 LeafType** leafPtr = mLeafs;
660 if (i > 0) leafPtr += leafCounts[i-1];
661 for ( ; i < range.end(); i++) {
662 for (auto iter = leafParents[i]->beginChildOn(); iter; ++iter) {
663 *leafPtr++ = &iter.getValue();
664 }
665 }
666 }
667 );
668 }
669 }
670
671 void initAuxBuffers(bool serial)
672 {
673 const size_t auxBufferCount = mLeafCount * mAuxBuffersPerLeaf;
674 if (auxBufferCount != mAuxBufferCount) {
675 if (auxBufferCount > 0) {
676 mAuxBufferPtrs.reset(new NonConstBufferType[auxBufferCount]);
677 mAuxBuffers = mAuxBufferPtrs.get();
678 } else {
679 mAuxBufferPtrs.reset();
680 mAuxBuffers = nullptr;
681 }
682 mAuxBufferCount = auxBufferCount;
683 }
684 this->syncAllBuffers(serial);
685 }
686
687 void cook(size_t grainsize)
688 {
689 if (grainsize>0) {
690 tbb::parallel_for(this->getRange(grainsize), *this);
691 } else {
692 (*this)(this->getRange());
693 }
694 }
695
696 void doSwapLeafBuffer(const RangeType& r, size_t auxBufferIdx)
697 {
698 LeafManagerImpl<LeafManager>::doSwapLeafBuffer(
699 r, auxBufferIdx, mLeafs, mAuxBuffers, mAuxBuffersPerLeaf);
700 }
701
702 void doSwapAuxBuffer(const RangeType& r, size_t auxBufferIdx1, size_t auxBufferIdx2)
703 {
704 for (size_t N = mAuxBuffersPerLeaf, n = N*r.begin(), m = N*r.end(); n != m; n+=N) {
705 mAuxBuffers[n + auxBufferIdx1].swap(mAuxBuffers[n + auxBufferIdx2]);
706 }
707 }
708
709 void doSyncAuxBuffer(const RangeType& r, size_t auxBufferIdx)
710 {
711 for (size_t n = r.begin(), m = r.end(), N = mAuxBuffersPerLeaf; n != m; ++n) {
712 mAuxBuffers[n*N + auxBufferIdx] = mLeafs[n]->buffer();
713 }
714 }
715
716 void doSyncAllBuffers1(const RangeType& r)
717 {
718 for (size_t n = r.begin(), m = r.end(); n != m; ++n) {
719 mAuxBuffers[n] = mLeafs[n]->buffer();
720 }
721 }
722
723 void doSyncAllBuffers2(const RangeType& r)
724 {
725 for (size_t n = r.begin(), m = r.end(); n != m; ++n) {
726 const BufferType& leafBuffer = mLeafs[n]->buffer();
727 mAuxBuffers[2*n ] = leafBuffer;
728 mAuxBuffers[2*n+1] = leafBuffer;
729 }
730 }
731
732 void doSyncAllBuffersN(const RangeType& r)
733 {
734 for (size_t n = r.begin(), m = r.end(), N = mAuxBuffersPerLeaf; n != m; ++n) {
735 const BufferType& leafBuffer = mLeafs[n]->buffer();
736 for (size_t i=n*N, j=i+N; i!=j; ++i) mAuxBuffers[i] = leafBuffer;
737 }
738 }
739
740 /// @brief Private member class that applies a user-defined
741 /// functor to perform parallel_for on all the leaf nodes.
742 template<typename LeafOp>
743 struct LeafTransformer
744 {
745 LeafTransformer(const LeafOp &leafOp) : mLeafOp(leafOp)
746 {
747 }
748 void run(const LeafRange &range, bool threaded) const
749 {
750 threaded ? tbb::parallel_for(range, *this) : (*this)(range);
751 }
752 void operator()(const LeafRange &range) const
753 {
754 for (typename LeafRange::Iterator it = range.begin(); it; ++it) mLeafOp(*it, it.pos());
755 }
756 const LeafOp mLeafOp;
757 };// LeafTransformer
758
759 /// @brief Private member class that applies a user-defined
760 /// functor to perform parallel_reduce on all the leaf nodes.
761 template<typename LeafOp>
762 struct LeafReducer
763 {
764 LeafReducer(LeafOp &leafOp) : mLeafOp(&leafOp)
765 {
766 }
767 LeafReducer(const LeafReducer &other, tbb::split)
768 : mLeafOpPtr(std::make_unique<LeafOp>(*(other.mLeafOp), tbb::split()))
769 , mLeafOp(mLeafOpPtr.get())
770 {
771 }
772 void run(const LeafRange& range, bool threaded)
773 {
774 threaded ? tbb::parallel_reduce(range, *this) : (*this)(range);
775 }
776 void operator()(const LeafRange& range)
777 {
778 LeafOp &op = *mLeafOp;//local registry
779 for (typename LeafRange::Iterator it = range.begin(); it; ++it) op(*it, it.pos());
780 }
781 void join(const LeafReducer& other) { mLeafOp->join(*(other.mLeafOp)); }
782 std::unique_ptr<LeafOp> mLeafOpPtr;
783 LeafOp *mLeafOp = nullptr;
784 };// LeafReducer
785
786 // Helper class to compute a prefix sum of offsets to active voxels
787 struct PrefixSum
788 {
789 PrefixSum(const LeafRange& r, size_t* offsets, size_t& prefix)
790 : mOffsets(offsets)
791 {
792 tbb::parallel_for( r, *this);
793 for (size_t i=0, leafCount = r.size(); i<leafCount; ++i) {
794 size_t tmp = offsets[i];
795 offsets[i] = prefix;
796 prefix += tmp;
797 }
798 }
799 inline void operator()(const LeafRange& r) const {
800 for (typename LeafRange::Iterator i = r.begin(); i; ++i) {
801 mOffsets[i.pos()] = i->onVoxelCount();
802 }
803 }
804 size_t* mOffsets;
805 };// PrefixSum
806
807 using FuncType = typename std::function<void (LeafManager*, const RangeType&)>;
808
809 TreeType* mTree;
810 size_t mLeafCount, mAuxBufferCount, mAuxBuffersPerLeaf;
811 std::unique_ptr<LeafType*[]> mLeafPtrs;
812 LeafType** mLeafs = nullptr;//array of LeafNode pointers
813 std::unique_ptr<NonConstBufferType[]> mAuxBufferPtrs;
814 NonConstBufferType* mAuxBuffers = nullptr;//array of auxiliary buffers
815 FuncType mTask = nullptr;
816};//end of LeafManager class
817
818
819// Partial specializations of LeafManager methods for const trees
820template<typename TreeT>
821struct LeafManagerImpl<LeafManager<const TreeT> >
822{
824 using RangeT = typename ManagerT::RangeType;
825 using LeafT = typename ManagerT::LeafType;
826 using BufT = typename ManagerT::BufferType;
827
828 static inline void doSwapLeafBuffer(const RangeT&, size_t /*auxBufferIdx*/,
829 LeafT**, BufT*, size_t /*bufsPerLeaf*/)
830 {
831 // Buffers can't be swapped into const trees.
832 }
833};
834
835} // namespace tree
836} // namespace OPENVDB_VERSION_NAME
837} // namespace openvdb
838
839#endif // OPENVDB_TREE_LEAFMANAGER_HAS_BEEN_INCLUDED
#define OPENVDB_ASSERT(X)
Definition Assert.h:41
The root node of an OpenVDB tree.
Definition Exceptions.h:65
bool test() const
Return true if this iterator is not yet exhausted.
Definition LeafManager.h:131
Iterator & operator=(const Iterator &)=default
bool isValid() const
Return true if the position of this iterator is in a valid range.
Definition LeafManager.h:129
bool empty() const
Return true if this iterator is exhausted.
Definition LeafManager.h:135
Iterator(const LeafRange &range, size_t pos)
Definition LeafManager.h:108
LeafType * operator->() const
Return a pointer to the leaf node to which this iterator is pointing.
Definition LeafManager.h:119
bool operator!=(const Iterator &other) const
Definition LeafManager.h:136
LeafType & operator*() const
Return a reference to the leaf node to which this iterator is pointing.
Definition LeafManager.h:117
size_t pos() const
Return the index into the leaf array of the current leaf node.
Definition LeafManager.h:127
BufferType & buffer(size_t bufferIdx)
Return the nth buffer for the leaf node to which this iterator is pointing, where n = bufferIdx and n...
Definition LeafManager.h:122
Iterator & operator++()
Advance to the next leaf node.
Definition LeafManager.h:115
const LeafRange & leafRange() const
Definition LeafManager.h:141
bool operator==(const Iterator &other) const
Definition LeafManager.h:140
LeafRange(size_t begin, size_t end, const LeafManager &leafManager, size_t grainSize=1)
Definition LeafManager.h:148
Iterator begin() const
Definition LeafManager.h:156
size_t size() const
Definition LeafManager.h:160
bool is_divisible() const
Definition LeafManager.h:168
const LeafManager & leafManager() const
Definition LeafManager.h:164
Iterator end() const
Definition LeafManager.h:158
bool empty() const
Definition LeafManager.h:166
LeafRange(LeafRange &r, tbb::split)
Definition LeafManager.h:170
size_t grainsize() const
Definition LeafManager.h:162
This class manages a linear array of pointers to a given tree's leaf nodes, as well as optional auxil...
Definition LeafManager.h:86
const TreeType & tree() const
Definition LeafManager.h:303
BufferType & getBuffer(size_t leafIdx, size_t bufferIdx) const
Return the leaf or auxiliary buffer for the leaf node at index leafIdx. If bufferIdx is zero,...
Definition LeafManager.h:331
size_t getPrefixSum(size_t *&offsets, size_t &size, size_t grainSize=1) const
Generate a linear array of prefix sums of offsets into the active voxels in the leafs....
Definition LeafManager.h:550
LeafType & leaf(size_t leafIdx) const
Definition LeafManager.h:319
size_t auxBufferCount() const
Return the total number of allocated auxiliary buffers.
Definition LeafManager.h:283
typename CopyConstness< TreeType, NonConstBufferType >::Type BufferType
Definition LeafManager.h:96
RootNodeType & root()
Return a reference to the root node associated with this manager.
Definition LeafManager.h:312
TreeType & tree()
Return a reference to the tree associated with this manager.
Definition LeafManager.h:306
typename TopologyT::ValueType ValueType
Definition LeafManager.h:89
typename leafmgr::TreeTraits< TopologyT >::LeafIterType LeafIterType
Definition LeafManager.h:94
bool swapLeafBuffer(size_t bufferIdx, bool serial=false)
Swap each leaf node's buffer with the nth corresponding auxiliary buffer, where n = bufferIdx.
Definition LeafManager.h:360
void rebuild(TreeType &tree, bool serial=false)
Definition LeafManager.h:251
void operator()(const RangeType &r) const
Used internally by tbb::parallel_for() - never call it directly!
Definition LeafManager.h:573
void rebuild(bool serial=false)
Definition LeafManager.h:239
bool swapBuffer(size_t bufferIdx1, size_t bufferIdx2, bool serial=false)
Swap any two buffers for each leaf node.
Definition LeafManager.h:372
RangeType getRange(size_t grainsize=1) const
Return a tbb::blocked_range of leaf array indices.
Definition LeafManager.h:343
bool syncAuxBuffer(size_t bufferIdx, bool serial=false)
Sync up the specified auxiliary buffer with the corresponding leaf node buffer.
Definition LeafManager.h:396
static const Index DEPTH
Definition LeafManager.h:98
bool syncAllBuffers(bool serial=false)
Sync up all auxiliary buffers with their corresponding leaf node buffers.
Definition LeafManager.h:408
LeafManager(const LeafManager &other)
Definition LeafManager.h:223
void reduce(LeafOp &op, bool threaded=true, size_t grainSize=1)
Threaded method that applies a user-supplied functor to each leaf node in the LeafManager....
Definition LeafManager.h:533
bool isConstTree() const
Return true if the tree associated with this manager is immutable.
Definition LeafManager.h:315
typename TopologyT::RootNodeType RootNodeType
Definition LeafManager.h:90
LeafManager(TreeType &tree, size_t auxBuffersPerLeaf=0, bool serial=false)
Constructor from a tree reference and an auxiliary buffer count.
Definition LeafManager.h:193
size_t auxBuffersPerLeaf() const
Definition LeafManager.h:285
typename TreeType::LeafNodeType NonConstLeafType
Definition LeafManager.h:91
LeafManager(TreeType &tree, LeafType **begin, LeafType **end, size_t auxBuffersPerLeaf=0, bool serial=false)
Construct directly from an existing array of leafnodes.
Definition LeafManager.h:205
void rebuildLeafArray(bool serial=false)
Remove the auxiliary buffers and rebuild the leaf array.
Definition LeafManager.h:276
size_t leafCount() const
Return the number of leaf nodes.
Definition LeafManager.h:288
void rebuild(TreeType &tree, size_t auxBuffersPerLeaf, bool serial=false)
Definition LeafManager.h:256
tbb::blocked_range< size_t > RangeType
Definition LeafManager.h:97
void rebuildAuxBuffers(size_t auxBuffersPerLeaf, bool serial=false)
Change the number of auxiliary buffers.
Definition LeafManager.h:267
void rebuild(size_t auxBuffersPerLeaf, bool serial=false)
Repopulate the leaf array and delete and reallocate auxiliary buffers.
Definition LeafManager.h:246
typename LeafType::Buffer NonConstBufferType
Definition LeafManager.h:95
LeafRange leafRange(size_t grainsize=1) const
Definition LeafManager.h:346
static const bool IsConstTree
Definition LeafManager.h:100
TopologyT TreeType
Definition LeafManager.h:88
const RootNodeType & root() const
Return a const reference to root node associated with this manager.
Definition LeafManager.h:309
typename CopyConstness< TreeType, NonConstLeafType >::Type LeafType
Definition LeafManager.h:92
Index64 activeLeafVoxelCount() const
Return the number of active voxels in the leaf nodes.
Definition LeafManager.h:292
LeafType LeafNodeType
Definition LeafManager.h:93
void removeAuxBuffers()
Remove the auxiliary buffers, but don't rebuild the leaf array.
Definition LeafManager.h:273
OPENVDB_AX_API void run(const char *ax, openvdb::GridBase &grid, const AttributeBindings &bindings={})
Run a full AX pipeline (parse, compile and execute) on a single OpenVDB Grid.
void split(ContainerT &out, const std::string &in, const char delim)
Definition Name.h:43
OutGridT XformOp & op
Definition ValueTransformer.h:140
OutGridT XformOp bool threaded
Definition ValueTransformer.h:140
Definition LeafManager.h:34
Index32 Index
Definition Types.h:54
uint64_t Index64
Definition Types.h:53
Definition Exceptions.h:13
#define OPENVDB_THROW(exception, message)
Definition Exceptions.h:74
typename std::remove_const< ToType >::type Type
Definition Types.h:439
LeafManager< const TreeT > ManagerT
Definition LeafManager.h:823
typename ManagerT::LeafType LeafT
Definition LeafManager.h:825
typename ManagerT::RangeType RangeT
Definition LeafManager.h:824
typename ManagerT::BufferType BufT
Definition LeafManager.h:826
static void doSwapLeafBuffer(const RangeT &, size_t, LeafT **, BufT *, size_t)
Definition LeafManager.h:828
Definition LeafManager.h:55
static void doSwapLeafBuffer(const RangeT &r, size_t auxBufferIdx, LeafT **leafs, BufT *bufs, size_t bufsPerLeaf)
Definition LeafManager.h:60
typename ManagerT::LeafType LeafT
Definition LeafManager.h:57
typename ManagerT::RangeType RangeT
Definition LeafManager.h:56
typename ManagerT::BufferType BufT
Definition LeafManager.h:58
typename SubtreeT::template Append< HeadT > Type
Definition RootNode.h:1039
typename TreeT::LeafCIter LeafIterType
Definition LeafManager.h:44
static const bool IsConstTree
Definition LeafManager.h:43
Useful traits for Tree types.
Definition LeafManager.h:38
typename TreeT::LeafIter LeafIterType
Definition LeafManager.h:40
static const bool IsConstTree
Definition LeafManager.h:39
#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