Line | Branch | Exec | Source |
---|---|---|---|
1 | // Copyright Contributors to the OpenVDB Project | ||
2 | // SPDX-License-Identifier: MPL-2.0 | ||
3 | |||
4 | /// @file PointPartitioner.h | ||
5 | /// | ||
6 | /// @brief Spatially partitions points using a parallel radix-based | ||
7 | /// sorting algorithm. | ||
8 | /// | ||
9 | /// @details Performs a stable deterministic sort; partitioning the same | ||
10 | /// point sequence will produce the same result each time. | ||
11 | /// @details The algorithm is unbounded meaning that points may be | ||
12 | /// distributed anywhere in index space. | ||
13 | /// @details The actual points are never stored in the tool, only | ||
14 | /// offsets into an external array. | ||
15 | /// | ||
16 | /// @author Mihai Alden | ||
17 | |||
18 | #ifndef OPENVDB_TOOLS_POINT_PARTITIONER_HAS_BEEN_INCLUDED | ||
19 | #define OPENVDB_TOOLS_POINT_PARTITIONER_HAS_BEEN_INCLUDED | ||
20 | |||
21 | #include <openvdb/Types.h> | ||
22 | #include <openvdb/math/Transform.h> | ||
23 | |||
24 | #include <tbb/blocked_range.h> | ||
25 | #include <tbb/parallel_for.h> | ||
26 | #include <tbb/task_arena.h> | ||
27 | |||
28 | #include <algorithm> | ||
29 | #include <cmath> // for std::isfinite() | ||
30 | #include <deque> | ||
31 | #include <map> | ||
32 | #include <set> | ||
33 | #include <utility> // std::pair | ||
34 | #include <vector> | ||
35 | |||
36 | |||
37 | namespace openvdb { | ||
38 | OPENVDB_USE_VERSION_NAMESPACE | ||
39 | namespace OPENVDB_VERSION_NAME { | ||
40 | namespace tools { | ||
41 | |||
42 | |||
43 | //////////////////////////////////////// | ||
44 | |||
45 | |||
46 | /// @brief Partitions points into @c BucketLog2Dim aligned buckets | ||
47 | /// using a parallel radix-based sorting algorithm. | ||
48 | /// | ||
49 | /// @interface PointArray | ||
50 | /// Expected interface for the PointArray container: | ||
51 | /// @code | ||
52 | /// template<typename VectorType> | ||
53 | /// struct PointArray | ||
54 | /// { | ||
55 | /// // The type used to represent world-space point positions | ||
56 | /// using PosType = VectorType; | ||
57 | /// | ||
58 | /// // Return the number of points in the array | ||
59 | /// size_t size() const; | ||
60 | /// | ||
61 | /// // Return the world-space position of the nth point in the array. | ||
62 | /// void getPos(size_t n, PosType& xyz) const; | ||
63 | /// }; | ||
64 | /// @endcode | ||
65 | /// | ||
66 | /// @details Performs a stable deterministic sort; partitioning the same | ||
67 | /// point sequence will produce the same result each time. | ||
68 | /// @details The algorithm is unbounded meaning that points may be | ||
69 | /// distributed anywhere in index space. | ||
70 | /// @details The actual points are never stored in the tool, only | ||
71 | /// offsets into an external array. | ||
72 | /// @details @c BucketLog2Dim defines the bucket coordinate dimensions, | ||
73 | /// i.e. BucketLog2Dim = 3 corresponds to a bucket that spans | ||
74 | /// a (2^3)^3 = 8^3 voxel region. | ||
75 | template<typename PointIndexType = uint32_t, Index BucketLog2Dim = 3> | ||
76 | class PointPartitioner | ||
77 | { | ||
78 | public: | ||
79 | enum { LOG2DIM = BucketLog2Dim }; | ||
80 | |||
81 | using Ptr = SharedPtr<PointPartitioner>; | ||
82 | using ConstPtr = SharedPtr<const PointPartitioner>; | ||
83 | |||
84 | using IndexType = PointIndexType; | ||
85 | |||
86 | static constexpr Index bits = 1 + (3 * BucketLog2Dim); | ||
87 | // signed, so if bits is exactly 16, int32 is required | ||
88 | using VoxelOffsetType = typename std::conditional<(bits < 16), | ||
89 | int16_t, typename std::conditional<(bits < 32), int32_t, int64_t>::type>::type; | ||
90 | |||
91 | using VoxelOffsetArray = std::unique_ptr<VoxelOffsetType[]>; | ||
92 | |||
93 | class IndexIterator; | ||
94 | |||
95 | ////////// | ||
96 | |||
97 | PointPartitioner(); | ||
98 | |||
99 | /// @brief Partitions point indices into @c BucketLog2Dim aligned buckets. | ||
100 | /// | ||
101 | /// @param points list of world space points. | ||
102 | /// @param xform world to index space transform. | ||
103 | /// @param voxelOrder sort point indices by local voxel offsets. | ||
104 | /// @param recordVoxelOffsets construct local voxel offsets | ||
105 | /// @param cellCenteredTransform toggle the cell-centered interpretation that imagines world | ||
106 | /// space as divided into discrete cells (e.g., cubes) centered | ||
107 | /// on the image of the index-space lattice points. | ||
108 | template<typename PointArray> | ||
109 | void construct(const PointArray& points, const math::Transform& xform, | ||
110 | bool voxelOrder = false, bool recordVoxelOffsets = false, | ||
111 | bool cellCenteredTransform = true); | ||
112 | |||
113 | |||
114 | /// @brief Partitions point indices into @c BucketLog2Dim aligned buckets. | ||
115 | /// | ||
116 | /// @param points list of world space points. | ||
117 | /// @param xform world to index space transform. | ||
118 | /// @param voxelOrder sort point indices by local voxel offsets. | ||
119 | /// @param recordVoxelOffsets construct local voxel offsets | ||
120 | /// @param cellCenteredTransform toggle the cell-centered interpretation that imagines world | ||
121 | /// space as divided into discrete cells (e.g., cubes) centered | ||
122 | /// on the image of the index-space lattice points. | ||
123 | template<typename PointArray> | ||
124 | static Ptr create(const PointArray& points, const math::Transform& xform, | ||
125 | bool voxelOrder = false, bool recordVoxelOffsets = false, | ||
126 | bool cellCenteredTransform = true); | ||
127 | |||
128 | |||
129 | /// @brief Returns the number of buckets. | ||
130 |
6/9✓ Branch 0 taken 3 times.
✓ Branch 1 taken 709 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 5 times.
✓ Branch 7 taken 9 times.
✗ Branch 8 not taken.
|
732 | size_t size() const { return mPageCount; } |
131 | |||
132 | /// @brief true if the container size is 0, false otherwise. | ||
133 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | bool empty() const { return mPageCount == 0; } |
134 | |||
135 | /// @brief Removes all data and frees up memory. | ||
136 | void clear(); | ||
137 | |||
138 | /// @brief Exchanges the content of the container by another. | ||
139 | void swap(PointPartitioner&); | ||
140 | |||
141 | /// @brief Returns the point indices for bucket @a n | ||
142 | IndexIterator indices(size_t n) const; | ||
143 | |||
144 | /// @brief Returns the coordinate-aligned bounding box for bucket @a n | ||
145 | CoordBBox getBBox(size_t n) const { | ||
146 | return CoordBBox::createCube(mPageCoordinates[n], (1u << BucketLog2Dim)); | ||
147 | } | ||
148 | |||
149 | /// @brief Returns the origin coordinate for bucket @a n | ||
150 | const Coord& origin(size_t n) const { return mPageCoordinates[n]; } | ||
151 | |||
152 | /// @brief Returns a list of @c LeafNode voxel offsets for the points. | ||
153 | /// @note The list is optionally constructed. | ||
154 | const VoxelOffsetArray& voxelOffsets() const { return mVoxelOffsets; } | ||
155 | |||
156 | /// @brief Returns @c true if this point partitioning was constructed | ||
157 | /// using a cell-centered transform. | ||
158 | /// @note Cell-centered interpretation is the default behavior. | ||
159 |
3/6✗ Branch 0 not taken.
✓ Branch 1 taken 709 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 9 times.
|
720 | bool usingCellCenteredTransform() const { return mUsingCellCenteredTransform; } |
160 | |||
161 | private: | ||
162 | // Disallow copying | ||
163 | PointPartitioner(const PointPartitioner&); | ||
164 | PointPartitioner& operator=(const PointPartitioner&); | ||
165 | |||
166 | std::unique_ptr<IndexType[]> mPointIndices; | ||
167 | VoxelOffsetArray mVoxelOffsets; | ||
168 | |||
169 | std::unique_ptr<IndexType[]> mPageOffsets; | ||
170 | std::unique_ptr<Coord[]> mPageCoordinates; | ||
171 | IndexType mPageCount; | ||
172 | bool mUsingCellCenteredTransform; | ||
173 | }; // class PointPartitioner | ||
174 | |||
175 | |||
176 | using UInt32PointPartitioner = PointPartitioner<uint32_t, 3>; | ||
177 | |||
178 | |||
179 | template<typename PointIndexType, Index BucketLog2Dim> | ||
180 | class PointPartitioner<PointIndexType, BucketLog2Dim>::IndexIterator | ||
181 | { | ||
182 | public: | ||
183 | using IndexType = PointIndexType; | ||
184 | |||
185 | 86895 | IndexIterator(IndexType* begin = nullptr, IndexType* end = nullptr) | |
186 | 86895 | : mBegin(begin), mEnd(end), mItem(begin) {} | |
187 | |||
188 | /// @brief Rewind to first item. | ||
189 | 2 | void reset() { mItem = mBegin; } | |
190 | |||
191 | /// @brief Number of point indices in the iterator range. | ||
192 |
3/5✓ Branch 0 taken 6951 times.
✓ Branch 1 taken 36449 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
|
43401 | size_t size() const { return mEnd - mBegin; } |
193 | |||
194 | /// @brief Returns the item to which this iterator is currently pointing. | ||
195 |
10/18✓ Branch 0 taken 43399 times.
✓ Branch 1 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 10 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 10 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 10 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 4 times.
✗ Branch 18 not taken.
✓ Branch 19 taken 15 times.
✗ Branch 21 not taken.
✓ Branch 22 taken 23 times.
✗ Branch 24 not taken.
✓ Branch 25 taken 23 times.
|
43530 | IndexType& operator*() { assert(mItem != nullptr); return *mItem; } |
196 | const IndexType& operator*() const { assert(mItem != nullptr); return *mItem; } | ||
197 | |||
198 | /// @brief Return @c true if this iterator is not yet exhausted. | ||
199 |
18/18✓ Branch 0 taken 10 times.
✓ Branch 1 taken 18 times.
✓ Branch 2 taken 11 times.
✓ Branch 3 taken 18 times.
✓ Branch 4 taken 11 times.
✓ Branch 5 taken 10 times.
✓ Branch 6 taken 10 times.
✓ Branch 7 taken 10 times.
✓ Branch 8 taken 10 times.
✓ Branch 9 taken 10 times.
✓ Branch 10 taken 4 times.
✓ Branch 11 taken 3 times.
✓ Branch 12 taken 15 times.
✓ Branch 13 taken 14 times.
✓ Branch 14 taken 23 times.
✓ Branch 15 taken 14 times.
✓ Branch 16 taken 23 times.
✓ Branch 17 taken 14 times.
|
228 | operator bool() const { return mItem < mEnd; } |
200 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
116 | bool test() const { return mItem < mEnd; } |
201 | |||
202 | /// @brief Advance to the next item. | ||
203 |
5/10✗ Branch 0 not taken.
✓ Branch 1 taken 131 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 times.
✓ Branch 10 taken 1 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✓ Branch 13 taken 1 times.
|
133 | IndexIterator& operator++() { assert(this->test()); ++mItem; return *this; } |
204 | |||
205 | /// @brief Advance to the next item. | ||
206 | bool next() { this->operator++(); return this->test(); } | ||
207 | bool increment() { this->next(); return this->test(); } | ||
208 | |||
209 | /// @brief Equality operators | ||
210 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | bool operator==(const IndexIterator& other) const { return mItem == other.mItem; } |
211 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | bool operator!=(const IndexIterator& other) const { return !this->operator==(other); } |
212 | |||
213 | private: | ||
214 | IndexType * const mBegin, * const mEnd; | ||
215 | IndexType * mItem; | ||
216 | }; // class PointPartitioner::IndexIterator | ||
217 | |||
218 | |||
219 | //////////////////////////////////////// | ||
220 | //////////////////////////////////////// | ||
221 | |||
222 | // Implementation details | ||
223 | |||
224 | /// @cond OPENVDB_DOCS_INTERNAL | ||
225 | |||
226 | namespace point_partitioner_internal { | ||
227 | |||
228 | |||
229 | template<typename PointIndexType> | ||
230 | struct ComputePointOrderOp | ||
231 | { | ||
232 | 1670 | ComputePointOrderOp(PointIndexType* pointOrder, | |
233 | const PointIndexType* bucketCounters, const PointIndexType* bucketOffsets) | ||
234 | : mPointOrder(pointOrder) | ||
235 | , mBucketCounters(bucketCounters) | ||
236 | 1670 | , mBucketOffsets(bucketOffsets) | |
237 | { | ||
238 | } | ||
239 | |||
240 | void operator()(const tbb::blocked_range<size_t>& range) const { | ||
241 |
4/4✓ Branch 0 taken 168492 times.
✓ Branch 1 taken 8998 times.
✓ Branch 2 taken 2494744 times.
✓ Branch 3 taken 89985 times.
|
2762219 | for (size_t n = range.begin(), N = range.end(); n != N; ++n) { |
242 | 2663236 | mPointOrder[n] += mBucketCounters[mBucketOffsets[n]]; | |
243 | } | ||
244 | } | ||
245 | |||
246 | PointIndexType * const mPointOrder; | ||
247 | PointIndexType const * const mBucketCounters; | ||
248 | PointIndexType const * const mBucketOffsets; | ||
249 | }; // struct ComputePointOrderOp | ||
250 | |||
251 | |||
252 | template<typename PointIndexType> | ||
253 | struct CreateOrderedPointIndexArrayOp | ||
254 | { | ||
255 | 1670 | CreateOrderedPointIndexArrayOp(PointIndexType* orderedIndexArray, | |
256 | const PointIndexType* pointOrder, const PointIndexType* indices) | ||
257 | : mOrderedIndexArray(orderedIndexArray) | ||
258 | , mPointOrder(pointOrder) | ||
259 | 1670 | , mIndices(indices) | |
260 | { | ||
261 | } | ||
262 | |||
263 | void operator()(const tbb::blocked_range<size_t>& range) const { | ||
264 |
4/4✓ Branch 0 taken 168707 times.
✓ Branch 1 taken 8974 times.
✓ Branch 2 taken 2494529 times.
✓ Branch 3 taken 89747 times.
|
2761957 | for (size_t n = range.begin(), N = range.end(); n != N; ++n) { |
265 | 2663236 | mOrderedIndexArray[mPointOrder[n]] = mIndices[n]; | |
266 | } | ||
267 | } | ||
268 | |||
269 | PointIndexType * const mOrderedIndexArray; | ||
270 | PointIndexType const * const mPointOrder; | ||
271 | PointIndexType const * const mIndices; | ||
272 | }; // struct CreateOrderedPointIndexArrayOp | ||
273 | |||
274 | |||
275 | template<typename PointIndexType, Index BucketLog2Dim> | ||
276 | struct VoxelOrderOp | ||
277 | { | ||
278 | static constexpr Index bits = 1 + (3 * BucketLog2Dim); | ||
279 | // signed, so if bits is exactly 16, int32 is required | ||
280 | using VoxelOffsetType = typename std::conditional<(bits < 16), | ||
281 | int16_t, typename std::conditional<(bits < 32), int32_t, int64_t>::type>::type; | ||
282 | |||
283 | using VoxelOffsetArray = std::unique_ptr<VoxelOffsetType[]>; | ||
284 | using IndexArray = std::unique_ptr<PointIndexType[]>; | ||
285 | |||
286 | ✗ | VoxelOrderOp(IndexArray& indices, const IndexArray& pages,const VoxelOffsetArray& offsets) | |
287 | : mIndices(indices.get()) | ||
288 | , mPages(pages.get()) | ||
289 | ✗ | , mVoxelOffsets(offsets.get()) | |
290 | { | ||
291 | } | ||
292 | |||
293 | ✗ | void operator()(const tbb::blocked_range<size_t>& range) const { | |
294 | |||
295 | ✗ | PointIndexType pointCount = 0; | |
296 | ✗ | for (size_t n(range.begin()), N(range.end()); n != N; ++n) { | |
297 | ✗ | pointCount = std::max(pointCount, (mPages[n + 1] - mPages[n])); | |
298 | } | ||
299 | |||
300 | const PointIndexType voxelCount = 1 << (3 * BucketLog2Dim); | ||
301 | |||
302 | // allocate histogram buffers | ||
303 | ✗ | std::unique_ptr<VoxelOffsetType[]> offsets(new VoxelOffsetType[pointCount]); | |
304 | ✗ | std::unique_ptr<PointIndexType[]> sortedIndices(new PointIndexType[pointCount]); | |
305 | ✗ | std::unique_ptr<PointIndexType[]> histogram(new PointIndexType[voxelCount]); | |
306 | |||
307 | ✗ | for (size_t n(range.begin()), N(range.end()); n != N; ++n) { | |
308 | |||
309 | ✗ | PointIndexType * const indices = mIndices + mPages[n]; | |
310 | ✗ | pointCount = mPages[n + 1] - mPages[n]; | |
311 | |||
312 | // local copy of voxel offsets. | ||
313 | ✗ | for (PointIndexType i = 0; i < pointCount; ++i) { | |
314 | ✗ | offsets[i] = mVoxelOffsets[ indices[i] ]; | |
315 | } | ||
316 | |||
317 | // reset histogram | ||
318 | ✗ | memset(&histogram[0], 0, voxelCount * sizeof(PointIndexType)); | |
319 | |||
320 | // compute histogram | ||
321 | ✗ | for (PointIndexType i = 0; i < pointCount; ++i) { | |
322 | ✗ | ++histogram[ offsets[i] ]; | |
323 | } | ||
324 | |||
325 | PointIndexType count = 0, startOffset; | ||
326 | ✗ | for (int i = 0; i < int(voxelCount); ++i) { | |
327 | ✗ | if (histogram[i] > 0) { | |
328 | startOffset = count; | ||
329 | ✗ | count += histogram[i]; | |
330 | ✗ | histogram[i] = startOffset; | |
331 | } | ||
332 | } | ||
333 | |||
334 | // sort indices based on voxel offset | ||
335 | ✗ | for (PointIndexType i = 0; i < pointCount; ++i) { | |
336 | ✗ | sortedIndices[ histogram[ offsets[i] ]++ ] = indices[i]; | |
337 | } | ||
338 | |||
339 | ✗ | memcpy(&indices[0], &sortedIndices[0], sizeof(PointIndexType) * pointCount); | |
340 | } | ||
341 | } | ||
342 | |||
343 | PointIndexType * const mIndices; | ||
344 | PointIndexType const * const mPages; | ||
345 | VoxelOffsetType const * const mVoxelOffsets; | ||
346 | }; // struct VoxelOrderOp | ||
347 | |||
348 | |||
349 | //////////////////////////////////////// | ||
350 | |||
351 | |||
352 | template<typename T> | ||
353 | 3340 | struct Array | |
354 | { | ||
355 | using Ptr = std::unique_ptr<Array>; | ||
356 | |||
357 |
1/2✓ Branch 0 taken 3340 times.
✗ Branch 1 not taken.
|
3340 | Array(size_t size) : mSize(size), mData(new T[size]) { } |
358 | |||
359 |
2/2✓ Branch 0 taken 1319 times.
✓ Branch 1 taken 351 times.
|
3340 | size_t size() const { return mSize; } |
360 | |||
361 | T* data() { return mData.get(); } | ||
362 | const T* data() const { return mData.get(); } | ||
363 | |||
364 |
1/2✓ Branch 0 taken 1670 times.
✗ Branch 1 not taken.
|
1670 | void clear() { mSize = 0; mData.reset(); } |
365 | |||
366 | private: | ||
367 | size_t mSize; | ||
368 | std::unique_ptr<T[]> mData; | ||
369 | }; // struct Array | ||
370 | |||
371 | |||
372 | template<typename PointIndexType> | ||
373 | struct MoveSegmentDataOp | ||
374 | { | ||
375 | using SegmentPtr = typename Array<PointIndexType>::Ptr; | ||
376 | |||
377 |
3/6✓ Branch 1 taken 721 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 9 times.
✗ Branch 8 not taken.
|
732 | MoveSegmentDataOp(std::vector<PointIndexType*>& indexLists, SegmentPtr* segments) |
378 | 732 | : mIndexLists(&indexLists[0]), mSegments(segments) | |
379 | { | ||
380 | } | ||
381 | |||
382 | 1231 | void operator()(const tbb::blocked_range<size_t>& range) const { | |
383 |
2/2✓ Branch 0 taken 1670 times.
✓ Branch 1 taken 1231 times.
|
2901 | for (size_t n(range.begin()), N(range.end()); n != N; ++n) { |
384 | 1670 | PointIndexType* indices = mIndexLists[n]; | |
385 | 1670 | SegmentPtr& segment = mSegments[n]; | |
386 | |||
387 |
1/2✓ Branch 1 taken 1670 times.
✗ Branch 2 not taken.
|
1670 | tbb::parallel_for(tbb::blocked_range<size_t>(0, segment->size()), |
388 | CopyData(indices, segment->data())); | ||
389 | |||
390 | segment.reset(); // clear data | ||
391 | } | ||
392 | 1231 | } | |
393 | |||
394 | private: | ||
395 | |||
396 | struct CopyData | ||
397 | { | ||
398 | 1670 | CopyData(PointIndexType* lhs, const PointIndexType* rhs) : mLhs(lhs), mRhs(rhs) { } | |
399 | |||
400 | void operator()(const tbb::blocked_range<size_t>& range) const { | ||
401 |
4/4✓ Branch 0 taken 164269 times.
✓ Branch 1 taken 8976 times.
✓ Branch 2 taken 2498967 times.
✓ Branch 3 taken 89805 times.
|
2762017 | for (size_t n = range.begin(), N = range.end(); n != N; ++n) { |
402 | 2663236 | mLhs[n] = mRhs[n]; | |
403 | } | ||
404 | } | ||
405 | |||
406 | PointIndexType * const mLhs; | ||
407 | PointIndexType const * const mRhs; | ||
408 | }; | ||
409 | |||
410 | PointIndexType * const * const mIndexLists; | ||
411 | SegmentPtr * const mSegments; | ||
412 | }; // struct MoveSegmentDataOp | ||
413 | |||
414 | |||
415 | template<typename PointIndexType> | ||
416 | struct MergeBinsOp | ||
417 | { | ||
418 | using Segment = Array<PointIndexType>; | ||
419 | using SegmentPtr = typename Segment::Ptr; | ||
420 | |||
421 | using IndexPair = std::pair<PointIndexType, PointIndexType>; | ||
422 | using IndexPairList = std::deque<IndexPair>; | ||
423 | using IndexPairListPtr = std::shared_ptr<IndexPairList>; | ||
424 | using IndexPairListMap = std::map<Coord, IndexPairListPtr>; | ||
425 | using IndexPairListMapPtr = std::shared_ptr<IndexPairListMap>; | ||
426 | |||
427 | 732 | MergeBinsOp(IndexPairListMapPtr* bins, | |
428 | SegmentPtr* indexSegments, | ||
429 | SegmentPtr* offsetSegments, | ||
430 | Coord* coords, | ||
431 | size_t numSegments) | ||
432 | : mBins(bins) | ||
433 | , mIndexSegments(indexSegments) | ||
434 | , mOffsetSegments(offsetSegments) | ||
435 | , mCoords(coords) | ||
436 |
3/6✓ Branch 1 taken 721 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 9 times.
✗ Branch 8 not taken.
|
732 | , mNumSegments(numSegments) |
437 | { | ||
438 | } | ||
439 | |||
440 | 1217 | void operator()(const tbb::blocked_range<size_t>& range) const { | |
441 | |||
442 | std::vector<IndexPairListPtr*> data; | ||
443 | std::vector<PointIndexType> arrayOffsets; | ||
444 | |||
445 |
2/2✓ Branch 0 taken 1670 times.
✓ Branch 1 taken 1217 times.
|
2887 | for (size_t n = range.begin(), N = range.end(); n != N; ++n) { |
446 | |||
447 |
2/2✓ Branch 0 taken 453 times.
✓ Branch 1 taken 1217 times.
|
1670 | const Coord& ijk = mCoords[n]; |
448 | size_t numIndices = 0; | ||
449 | |||
450 | data.clear(); | ||
451 | |||
452 |
2/2✓ Branch 0 taken 5011 times.
✓ Branch 1 taken 1670 times.
|
6681 | for (size_t i = 0, I = mNumSegments; i < I; ++i) { |
453 | |||
454 | 5011 | IndexPairListMap& idxMap = *mBins[i]; | |
455 |
2/2✓ Branch 0 taken 2365 times.
✓ Branch 1 taken 2646 times.
|
5011 | typename IndexPairListMap::iterator iter = idxMap.find(ijk); |
456 | |||
457 |
3/4✓ Branch 0 taken 2365 times.
✓ Branch 1 taken 2646 times.
✓ Branch 2 taken 2365 times.
✗ Branch 3 not taken.
|
5011 | if (iter != idxMap.end() && iter->second) { |
458 | 2365 | IndexPairListPtr& idxListPtr = iter->second; | |
459 | |||
460 |
1/2✓ Branch 1 taken 2365 times.
✗ Branch 2 not taken.
|
2365 | data.push_back(&idxListPtr); |
461 | 2365 | numIndices += idxListPtr->size(); | |
462 | } | ||
463 | } | ||
464 | |||
465 |
2/4✓ Branch 0 taken 1670 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1670 times.
✗ Branch 3 not taken.
|
1670 | if (data.empty() || numIndices == 0) continue; |
466 | |||
467 | 1670 | SegmentPtr& indexSegment = mIndexSegments[n]; | |
468 | 1670 | SegmentPtr& offsetSegment = mOffsetSegments[n]; | |
469 | |||
470 |
2/4✓ Branch 1 taken 1670 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1670 times.
✗ Branch 5 not taken.
|
1670 | indexSegment.reset(new Segment(numIndices)); |
471 |
2/4✓ Branch 1 taken 1670 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1670 times.
✗ Branch 5 not taken.
|
1670 | offsetSegment.reset(new Segment(numIndices)); |
472 | |||
473 | arrayOffsets.clear(); | ||
474 |
1/2✓ Branch 1 taken 1670 times.
✗ Branch 2 not taken.
|
1670 | arrayOffsets.reserve(data.size()); |
475 | |||
476 |
2/2✓ Branch 0 taken 2365 times.
✓ Branch 1 taken 1670 times.
|
4035 | for (size_t i = 0, count = 0, I = data.size(); i < I; ++i) { |
477 |
1/2✓ Branch 1 taken 2365 times.
✗ Branch 2 not taken.
|
2365 | arrayOffsets.push_back(PointIndexType(count)); |
478 | 2365 | count += (*data[i])->size(); | |
479 | } | ||
480 | |||
481 |
1/4✓ Branch 1 taken 1670 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
1670 | tbb::parallel_for(tbb::blocked_range<size_t>(0, data.size()), |
482 | CopyData(&data[0], &arrayOffsets[0], indexSegment->data(), offsetSegment->data())); | ||
483 | } | ||
484 | 1217 | } | |
485 | |||
486 | private: | ||
487 | |||
488 | struct CopyData | ||
489 | { | ||
490 | 1670 | CopyData(IndexPairListPtr** indexLists, | |
491 | const PointIndexType* arrayOffsets, | ||
492 | PointIndexType* indices, | ||
493 | PointIndexType* offsets) | ||
494 | : mIndexLists(indexLists) | ||
495 | , mArrayOffsets(arrayOffsets) | ||
496 | , mIndices(indices) | ||
497 |
1/2✓ Branch 1 taken 1670 times.
✗ Branch 2 not taken.
|
1670 | , mOffsets(offsets) |
498 | { | ||
499 | } | ||
500 | |||
501 | 2365 | void operator()(const tbb::blocked_range<size_t>& range) const { | |
502 | |||
503 | using CIter = typename IndexPairList::const_iterator; | ||
504 | |||
505 |
2/2✓ Branch 0 taken 2365 times.
✓ Branch 1 taken 2365 times.
|
4730 | for (size_t n = range.begin(), N = range.end(); n != N; ++n) { |
506 | |||
507 | 2365 | const PointIndexType arrayOffset = mArrayOffsets[n]; | |
508 | 2365 | PointIndexType* indexPtr = &mIndices[arrayOffset]; | |
509 | 2365 | PointIndexType* offsetPtr = &mOffsets[arrayOffset]; | |
510 | |||
511 | 2365 | IndexPairListPtr& list = *mIndexLists[n]; | |
512 | |||
513 |
2/2✓ Branch 0 taken 2663236 times.
✓ Branch 1 taken 2365 times.
|
2665601 | for (CIter it = list->begin(), end = list->end(); it != end; ++it) { |
514 | const IndexPair& data = *it; | ||
515 | 2663236 | *indexPtr++ = data.first; | |
516 |
2/2✓ Branch 0 taken 2622005 times.
✓ Branch 1 taken 41231 times.
|
2663236 | *offsetPtr++ = data.second; |
517 | } | ||
518 | |||
519 | list.reset(); // clear data | ||
520 | } | ||
521 | 2365 | } | |
522 | |||
523 | IndexPairListPtr * const * const mIndexLists; | ||
524 | PointIndexType const * const mArrayOffsets; | ||
525 | PointIndexType * const mIndices; | ||
526 | PointIndexType * const mOffsets; | ||
527 | }; // struct CopyData | ||
528 | |||
529 | IndexPairListMapPtr * const mBins; | ||
530 | SegmentPtr * const mIndexSegments; | ||
531 | SegmentPtr * const mOffsetSegments; | ||
532 | Coord const * const mCoords; | ||
533 | size_t const mNumSegments; | ||
534 | }; // struct MergeBinsOp | ||
535 | |||
536 | |||
537 | template<typename PointArray, typename PointIndexType, typename VoxelOffsetType> | ||
538 |
4/12✓ Branch 4 taken 717 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 4 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 2 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✓ Branch 16 taken 9 times.
✗ Branch 17 not taken.
|
1270 | struct BinPointIndicesOp |
539 | { | ||
540 | using PosType = typename PointArray::PosType; | ||
541 | using IndexPair = std::pair<PointIndexType, PointIndexType>; | ||
542 | using IndexPairList = std::deque<IndexPair>; | ||
543 | using IndexPairListPtr = std::shared_ptr<IndexPairList>; | ||
544 | using IndexPairListMap = std::map<Coord, IndexPairListPtr>; | ||
545 | using IndexPairListMapPtr = std::shared_ptr<IndexPairListMap>; | ||
546 | |||
547 | 732 | BinPointIndicesOp(IndexPairListMapPtr* data, | |
548 | const PointArray& points, | ||
549 | VoxelOffsetType* voxelOffsets, | ||
550 | const math::Transform& m, | ||
551 | Index binLog2Dim, | ||
552 | Index bucketLog2Dim, | ||
553 | size_t numSegments, | ||
554 | bool cellCenteredTransform) | ||
555 | : mData(data) | ||
556 | , mPoints(&points) | ||
557 | , mVoxelOffsets(voxelOffsets) | ||
558 | , mXForm(m) | ||
559 | , mBinLog2Dim(binLog2Dim) | ||
560 | , mBucketLog2Dim(bucketLog2Dim) | ||
561 | , mNumSegments(numSegments) | ||
562 |
6/12✓ Branch 1 taken 721 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 721 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 2 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 9 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 9 times.
✗ Branch 17 not taken.
|
732 | , mCellCenteredTransform(cellCenteredTransform) |
563 | { | ||
564 | } | ||
565 | |||
566 | 1326 | void operator()(const tbb::blocked_range<size_t>& range) const { | |
567 | |||
568 | 1326 | const Index log2dim = mBucketLog2Dim; | |
569 | 1326 | const Index log2dim2 = 2 * log2dim; | |
570 | 1326 | const Index bucketMask = (1u << log2dim) - 1u; | |
571 | |||
572 | 1326 | const Index binLog2dim = mBinLog2Dim; | |
573 | 1326 | const Index binLog2dim2 = 2 * binLog2dim; | |
574 | |||
575 | 1326 | const Index binMask = (1u << (log2dim + binLog2dim)) - 1u; | |
576 | const Index invBinMask = ~binMask; | ||
577 | |||
578 | IndexPairList * idxList = nullptr; | ||
579 | Coord ijk(0, 0, 0), loc(0, 0, 0), binCoord(0, 0, 0), lastBinCoord(1, 2, 3); | ||
580 | PosType pos; | ||
581 | |||
582 | PointIndexType bucketOffset = 0; | ||
583 | VoxelOffsetType voxelOffset = 0; | ||
584 | |||
585 | 1326 | const bool cellCentered = mCellCenteredTransform; | |
586 | |||
587 | 1326 | const size_t numPoints = mPoints->size(); | |
588 | 1326 | const size_t segmentSize = numPoints / mNumSegments; | |
589 | |||
590 |
2/2✓ Branch 0 taken 1270 times.
✓ Branch 1 taken 1270 times.
|
2652 | for (size_t n = range.begin(), N = range.end(); n != N; ++n) { |
591 | |||
592 |
1/2✓ Branch 0 taken 1270 times.
✗ Branch 1 not taken.
|
1326 | IndexPairListMapPtr& dataPtr = mData[n]; |
593 |
1/2✓ Branch 0 taken 1270 times.
✗ Branch 1 not taken.
|
1326 | if (!dataPtr) dataPtr.reset(new IndexPairListMap()); |
594 | IndexPairListMap& idxMap = *dataPtr; | ||
595 | |||
596 | 1326 | const bool isLastSegment = (n + 1) >= mNumSegments; | |
597 | |||
598 | 1326 | const size_t start = n * segmentSize; | |
599 |
2/2✓ Branch 0 taken 538 times.
✓ Branch 1 taken 732 times.
|
1326 | const size_t end = isLastSegment ? numPoints : (start + segmentSize); |
600 | |||
601 |
2/2✓ Branch 0 taken 1270 times.
✓ Branch 1 taken 2663240 times.
|
4182599 | for (size_t i = start; i != end; ++i) { |
602 | |||
603 |
2/2✓ Branch 0 taken 2663236 times.
✓ Branch 1 taken 4 times.
|
4181273 | mPoints->getPos(i, pos); |
604 | |||
605 |
4/6✓ Branch 0 taken 2663236 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 2663236 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2663236 times.
✗ Branch 5 not taken.
|
4181273 | if (std::isfinite(pos[0]) && std::isfinite(pos[1]) && std::isfinite(pos[2])) { |
606 |
2/6✓ Branch 0 taken 2663236 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 1401704 times.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
|
4181265 | ijk = cellCentered ? mXForm.worldToIndexCellCentered(pos) : |
607 | mXForm.worldToIndexNodeCentered(pos); | ||
608 | |||
609 |
2/2✓ Branch 0 taken 2653121 times.
✓ Branch 1 taken 10115 times.
|
4181265 | if (mVoxelOffsets) { |
610 | 4171150 | loc[0] = ijk[0] & bucketMask; | |
611 | 4171150 | loc[1] = ijk[1] & bucketMask; | |
612 | 4171150 | loc[2] = ijk[2] & bucketMask; | |
613 | 4171150 | voxelOffset = VoxelOffsetType( | |
614 | 4171150 | (loc[0] << log2dim2) + (loc[1] << log2dim) + loc[2]); | |
615 | } | ||
616 | |||
617 | 4181265 | binCoord[0] = ijk[0] & invBinMask; | |
618 | 4181265 | binCoord[1] = ijk[1] & invBinMask; | |
619 | 4181265 | binCoord[2] = ijk[2] & invBinMask; | |
620 | |||
621 | 4181265 | ijk[0] &= binMask; | |
622 | 4181265 | ijk[1] &= binMask; | |
623 | 4181265 | ijk[2] &= binMask; | |
624 | |||
625 | 4181265 | ijk[0] >>= log2dim; | |
626 | 4181265 | ijk[1] >>= log2dim; | |
627 | 4181265 | ijk[2] >>= log2dim; | |
628 | |||
629 | bucketOffset = PointIndexType( | ||
630 |
2/2✓ Branch 0 taken 2653266 times.
✓ Branch 1 taken 9970 times.
|
4181265 | (ijk[0] << binLog2dim2) + (ijk[1] << binLog2dim) + ijk[2]); |
631 | |||
632 | if (lastBinCoord != binCoord) { | ||
633 | 34405 | lastBinCoord = binCoord; | |
634 | 34405 | IndexPairListPtr& idxListPtr = idxMap[lastBinCoord]; | |
635 |
2/2✓ Branch 0 taken 2365 times.
✓ Branch 1 taken 16896 times.
|
37765 | if (!idxListPtr) idxListPtr.reset(new IndexPairList()); |
636 | idxList = idxListPtr.get(); | ||
637 | } | ||
638 | |||
639 | 4181265 | idxList->push_back(IndexPair(PointIndexType(i), bucketOffset)); | |
640 |
2/2✓ Branch 0 taken 2653121 times.
✓ Branch 1 taken 10115 times.
|
4181265 | if (mVoxelOffsets) mVoxelOffsets[i] = voxelOffset; |
641 | } | ||
642 | } | ||
643 | } | ||
644 | 1326 | } | |
645 | |||
646 | IndexPairListMapPtr * const mData; | ||
647 | PointArray const * const mPoints; | ||
648 | VoxelOffsetType * const mVoxelOffsets; | ||
649 | math::Transform const mXForm; | ||
650 | Index const mBinLog2Dim; | ||
651 | Index const mBucketLog2Dim; | ||
652 | size_t const mNumSegments; | ||
653 | bool const mCellCenteredTransform; | ||
654 | }; // struct BinPointIndicesOp | ||
655 | |||
656 | |||
657 | template<typename PointIndexType> | ||
658 | struct OrderSegmentsOp | ||
659 | { | ||
660 | using IndexArray = std::unique_ptr<PointIndexType[]>; | ||
661 | using SegmentPtr = typename Array<PointIndexType>::Ptr; | ||
662 | |||
663 | 732 | OrderSegmentsOp(SegmentPtr* indexSegments, SegmentPtr* offsetSegments, | |
664 | IndexArray* pageOffsetArrays, IndexArray* pageIndexArrays, Index binVolume) | ||
665 | : mIndexSegments(indexSegments) | ||
666 | , mOffsetSegments(offsetSegments) | ||
667 | , mPageOffsetArrays(pageOffsetArrays) | ||
668 | , mPageIndexArrays(pageIndexArrays) | ||
669 | 732 | , mBinVolume(binVolume) | |
670 | { | ||
671 | } | ||
672 | |||
673 | 1212 | void operator()(const tbb::blocked_range<size_t>& range) const { | |
674 | |||
675 | 1212 | const size_t bucketCountersSize = size_t(mBinVolume); | |
676 | 1212 | IndexArray bucketCounters(new PointIndexType[bucketCountersSize]); | |
677 | |||
678 | 1212 | size_t maxSegmentSize = 0; | |
679 |
2/2✓ Branch 0 taken 1670 times.
✓ Branch 1 taken 1212 times.
|
2882 | for (size_t n = range.begin(), N = range.end(); n != N; ++n) { |
680 |
2/2✓ Branch 0 taken 1319 times.
✓ Branch 1 taken 351 times.
|
2989 | maxSegmentSize = std::max(maxSegmentSize, mIndexSegments[n]->size()); |
681 | } | ||
682 | |||
683 |
2/4✓ Branch 0 taken 1212 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 1212 times.
✗ Branch 4 not taken.
|
1212 | IndexArray bucketIndices(new PointIndexType[maxSegmentSize]); |
684 | |||
685 |
2/2✓ Branch 0 taken 1670 times.
✓ Branch 1 taken 1212 times.
|
2882 | for (size_t n = range.begin(), N = range.end(); n != N; ++n) { |
686 | |||
687 | 1670 | memset(bucketCounters.get(), 0, sizeof(PointIndexType) * bucketCountersSize); | |
688 | |||
689 | 1670 | const size_t segmentSize = mOffsetSegments[n]->size(); | |
690 | PointIndexType* offsets = mOffsetSegments[n]->data(); | ||
691 | |||
692 | // Count the number of points per bucket and assign a local bucket index | ||
693 | // to each point. | ||
694 |
2/2✓ Branch 0 taken 1670 times.
✓ Branch 1 taken 2663236 times.
|
2664906 | for (size_t i = 0; i < segmentSize; ++i) { |
695 | 2663236 | bucketIndices[i] = bucketCounters[offsets[i]]++; | |
696 | } | ||
697 | |||
698 | PointIndexType nonemptyBucketCount = 0; | ||
699 |
2/2✓ Branch 0 taken 54722560 times.
✓ Branch 1 taken 1670 times.
|
54724230 | for (size_t i = 0; i < bucketCountersSize; ++i) { |
700 | 54722560 | nonemptyBucketCount += static_cast<PointIndexType>(bucketCounters[i] != 0); | |
701 | } | ||
702 | |||
703 | |||
704 | 1670 | IndexArray& pageOffsets = mPageOffsetArrays[n]; | |
705 |
1/2✓ Branch 1 taken 1670 times.
✗ Branch 2 not taken.
|
1670 | pageOffsets.reset(new PointIndexType[nonemptyBucketCount + 1]); |
706 | 1670 | pageOffsets[0] = nonemptyBucketCount + 1; // stores array size in first element | |
707 | |||
708 | 1670 | IndexArray& pageIndices = mPageIndexArrays[n]; | |
709 |
1/2✓ Branch 1 taken 1670 times.
✗ Branch 2 not taken.
|
1670 | pageIndices.reset(new PointIndexType[nonemptyBucketCount]); |
710 | |||
711 | // Compute bucket counter prefix sum | ||
712 | PointIndexType count = 0, idx = 0; | ||
713 |
2/2✓ Branch 0 taken 54722560 times.
✓ Branch 1 taken 1670 times.
|
54724230 | for (size_t i = 0; i < bucketCountersSize; ++i) { |
714 |
2/2✓ Branch 0 taken 44744 times.
✓ Branch 1 taken 54677816 times.
|
54722560 | if (bucketCounters[i] != 0) { |
715 | 44744 | pageIndices[idx] = static_cast<PointIndexType>(i); | |
716 | 44744 | pageOffsets[idx+1] = bucketCounters[i]; | |
717 | 44744 | bucketCounters[i] = count; | |
718 | 44744 | count += pageOffsets[idx+1]; | |
719 | ++idx; | ||
720 | } | ||
721 | } | ||
722 | |||
723 |
1/2✓ Branch 1 taken 1670 times.
✗ Branch 2 not taken.
|
1670 | PointIndexType* indices = mIndexSegments[n]->data(); |
724 | const tbb::blocked_range<size_t> segmentRange(0, segmentSize); | ||
725 | |||
726 | // Compute final point order by incrementing the local bucket point index | ||
727 | // with the prefix sum offset. | ||
728 |
1/2✓ Branch 1 taken 1670 times.
✗ Branch 2 not taken.
|
1670 | tbb::parallel_for(segmentRange, ComputePointOrderOp<PointIndexType>( |
729 | bucketIndices.get(), bucketCounters.get(), offsets)); | ||
730 | |||
731 |
1/4✓ Branch 1 taken 1670 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
1670 | tbb::parallel_for(segmentRange, CreateOrderedPointIndexArrayOp<PointIndexType>( |
732 | offsets, bucketIndices.get(), indices)); | ||
733 | |||
734 |
1/2✓ Branch 0 taken 1670 times.
✗ Branch 1 not taken.
|
1670 | mIndexSegments[n]->clear(); // clear data |
735 | } | ||
736 | 1212 | } | |
737 | |||
738 | SegmentPtr * const mIndexSegments; | ||
739 | SegmentPtr * const mOffsetSegments; | ||
740 | IndexArray * const mPageOffsetArrays; | ||
741 | IndexArray * const mPageIndexArrays; | ||
742 | Index const mBinVolume; | ||
743 | }; // struct OrderSegmentsOp | ||
744 | |||
745 | |||
746 | //////////////////////////////////////// | ||
747 | |||
748 | |||
749 | /// @brief Segment points using one level of least significant digit radix bins. | ||
750 | template<typename PointIndexType, typename VoxelOffsetType, typename PointArray> | ||
751 | 747 | inline void binAndSegment( | |
752 | const PointArray& points, | ||
753 | const math::Transform& xform, | ||
754 | std::unique_ptr<typename Array<PointIndexType>::Ptr[]>& indexSegments, | ||
755 | std::unique_ptr<typename Array<PointIndexType>::Ptr[]>& offsetSegments, | ||
756 | std::vector<Coord>& coords, | ||
757 | const Index binLog2Dim, | ||
758 | const Index bucketLog2Dim, | ||
759 | VoxelOffsetType* voxelOffsets = nullptr, | ||
760 | bool cellCenteredTransform = true) | ||
761 | { | ||
762 | using IndexPair = std::pair<PointIndexType, PointIndexType>; | ||
763 | using IndexPairList = std::deque<IndexPair>; | ||
764 | using IndexPairListPtr = std::shared_ptr<IndexPairList>; | ||
765 | using IndexPairListMap = std::map<Coord, IndexPairListPtr>; | ||
766 | using IndexPairListMapPtr = std::shared_ptr<IndexPairListMap>; | ||
767 | |||
768 |
2/2✓ Branch 0 taken 660 times.
✓ Branch 1 taken 72 times.
|
747 | size_t numTasks = 1, numThreads = size_t(tbb::this_task_arena::max_concurrency()); |
769 |
2/2✓ Branch 0 taken 660 times.
✓ Branch 1 taken 72 times.
|
747 | if (points.size() > (numThreads * 2)) numTasks = numThreads * 2; |
770 |
2/2✓ Branch 0 taken 322 times.
✓ Branch 1 taken 338 times.
|
662 | else if (points.size() > numThreads) numTasks = numThreads; |
771 | |||
772 |
3/4✓ Branch 0 taken 394 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 1270 times.
✓ Branch 4 taken 732 times.
|
2073 | std::unique_ptr<IndexPairListMapPtr[]> bins(new IndexPairListMapPtr[numTasks]); |
773 | |||
774 | using BinOp = BinPointIndicesOp<PointArray, PointIndexType, VoxelOffsetType>; | ||
775 | |||
776 |
2/8✓ Branch 1 taken 732 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 732 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
|
1494 | tbb::parallel_for(tbb::blocked_range<size_t>(0, numTasks), |
777 | BinOp(bins.get(), points, voxelOffsets, xform, binLog2Dim, bucketLog2Dim, | ||
778 | numTasks, cellCenteredTransform)); | ||
779 | |||
780 | std::set<Coord> uniqueCoords; | ||
781 | |||
782 |
2/2✓ Branch 0 taken 1270 times.
✓ Branch 1 taken 732 times.
|
2073 | for (size_t i = 0; i < numTasks; ++i) { |
783 | IndexPairListMap& idxMap = *bins[i]; | ||
784 |
2/2✓ Branch 0 taken 2365 times.
✓ Branch 1 taken 1270 times.
|
4686 | for (typename IndexPairListMap::iterator it = idxMap.begin(); it != idxMap.end(); ++it) { |
785 |
1/2✓ Branch 1 taken 2365 times.
✗ Branch 2 not taken.
|
3360 | uniqueCoords.insert(it->first); |
786 | } | ||
787 | } | ||
788 | |||
789 | 747 | coords.assign(uniqueCoords.begin(), uniqueCoords.end()); | |
790 | uniqueCoords.clear(); | ||
791 | |||
792 | size_t segmentCount = coords.size(); | ||
793 | |||
794 | using SegmentPtr = typename Array<PointIndexType>::Ptr; | ||
795 | |||
796 |
4/6✓ Branch 0 taken 732 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 732 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1670 times.
✓ Branch 6 taken 732 times.
|
3197 | indexSegments.reset(new SegmentPtr[segmentCount]); |
797 |
3/4✓ Branch 1 taken 732 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1670 times.
✓ Branch 4 taken 732 times.
|
3197 | offsetSegments.reset(new SegmentPtr[segmentCount]); |
798 | |||
799 | using MergeOp = MergeBinsOp<PointIndexType>; | ||
800 | |||
801 |
1/2✓ Branch 1 taken 732 times.
✗ Branch 2 not taken.
|
747 | tbb::parallel_for(tbb::blocked_range<size_t>(0, segmentCount), |
802 | MergeOp(bins.get(), indexSegments.get(), offsetSegments.get(), &coords[0], numTasks)); | ||
803 | 747 | } | |
804 | |||
805 | |||
806 | template<typename PointIndexType, typename VoxelOffsetType, typename PointArray> | ||
807 | 747 | inline void partition( | |
808 | const PointArray& points, | ||
809 | const math::Transform& xform, | ||
810 | const Index bucketLog2Dim, | ||
811 | std::unique_ptr<PointIndexType[]>& pointIndices, | ||
812 | std::unique_ptr<PointIndexType[]>& pageOffsets, | ||
813 | std::unique_ptr<Coord[]>& pageCoordinates, | ||
814 | PointIndexType& pageCount, | ||
815 | std::unique_ptr<VoxelOffsetType[]>& voxelOffsets, | ||
816 | bool recordVoxelOffsets, | ||
817 | bool cellCenteredTransform) | ||
818 | { | ||
819 | using SegmentPtr = typename Array<PointIndexType>::Ptr; | ||
820 | |||
821 |
3/4✓ Branch 0 taken 719 times.
✓ Branch 1 taken 13 times.
✓ Branch 2 taken 719 times.
✗ Branch 3 not taken.
|
747 | if (recordVoxelOffsets) voxelOffsets.reset(new VoxelOffsetType[points.size()]); |
822 | else voxelOffsets.reset(); | ||
823 | |||
824 |
1/2✓ Branch 1 taken 732 times.
✗ Branch 2 not taken.
|
747 | const Index binLog2Dim = 5u; |
825 | // note: Bins span a (2^(binLog2Dim + bucketLog2Dim))^3 voxel region, | ||
826 | // i.e. bucketLog2Dim = 3 and binLog2Dim = 5 corresponds to a | ||
827 | // (2^8)^3 = 256^3 voxel region. | ||
828 | |||
829 | |||
830 | std::vector<Coord> segmentCoords; | ||
831 | |||
832 | 747 | std::unique_ptr<SegmentPtr[]> indexSegments; | |
833 | 747 | std::unique_ptr<SegmentPtr[]> offsetSegments; | |
834 | |||
835 |
1/2✓ Branch 1 taken 732 times.
✗ Branch 2 not taken.
|
747 | binAndSegment<PointIndexType, VoxelOffsetType, PointArray>(points, xform, |
836 | indexSegments, offsetSegments, segmentCoords, binLog2Dim, bucketLog2Dim, | ||
837 | voxelOffsets.get(), cellCenteredTransform); | ||
838 | |||
839 | size_t numSegments = segmentCoords.size(); | ||
840 | |||
841 | const tbb::blocked_range<size_t> segmentRange(0, numSegments); | ||
842 | |||
843 | using IndexArray = std::unique_ptr<PointIndexType[]>; | ||
844 |
4/6✓ Branch 0 taken 732 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 732 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1670 times.
✓ Branch 6 taken 732 times.
|
3197 | std::unique_ptr<IndexArray[]> pageOffsetArrays(new IndexArray[numSegments]); |
845 |
3/4✓ Branch 1 taken 732 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1670 times.
✓ Branch 4 taken 732 times.
|
3197 | std::unique_ptr<IndexArray[]> pageIndexArrays(new IndexArray[numSegments]); |
846 | |||
847 | const Index binVolume = 1u << (3u * binLog2Dim); | ||
848 | |||
849 |
2/6✓ Branch 1 taken 732 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 732 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
|
747 | tbb::parallel_for(segmentRange, OrderSegmentsOp<PointIndexType> |
850 | (indexSegments.get(), offsetSegments.get(), | ||
851 | pageOffsetArrays.get(), pageIndexArrays.get(), binVolume)); | ||
852 | |||
853 | indexSegments.reset(); | ||
854 | |||
855 | std::vector<Index> segmentOffsets; | ||
856 |
1/2✓ Branch 1 taken 732 times.
✗ Branch 2 not taken.
|
747 | segmentOffsets.reserve(numSegments); |
857 | |||
858 | 747 | pageCount = 0; | |
859 |
2/2✓ Branch 0 taken 1670 times.
✓ Branch 1 taken 732 times.
|
3197 | for (size_t n = 0; n < numSegments; ++n) { |
860 |
1/2✓ Branch 1 taken 1670 times.
✗ Branch 2 not taken.
|
2450 | segmentOffsets.push_back(pageCount); |
861 | 2450 | pageCount += pageOffsetArrays[n][0] - 1; | |
862 | } | ||
863 | |||
864 |
1/2✓ Branch 1 taken 732 times.
✗ Branch 2 not taken.
|
747 | pageOffsets.reset(new PointIndexType[pageCount + 1]); |
865 | |||
866 | PointIndexType count = 0; | ||
867 |
2/2✓ Branch 0 taken 1670 times.
✓ Branch 1 taken 732 times.
|
3197 | for (size_t n = 0, idx = 0; n < numSegments; ++n) { |
868 | |||
869 | PointIndexType* offsets = pageOffsetArrays[n].get(); | ||
870 | 2450 | size_t size = size_t(offsets[0]); | |
871 | |||
872 |
2/2✓ Branch 0 taken 44744 times.
✓ Branch 1 taken 1670 times.
|
78422 | for (size_t i = 1; i < size; ++i) { |
873 | 75972 | pageOffsets[idx++] = count; | |
874 | 75972 | count += offsets[i]; | |
875 | } | ||
876 | } | ||
877 | |||
878 |
1/2✓ Branch 0 taken 732 times.
✗ Branch 1 not taken.
|
747 | pageOffsets[pageCount] = count; |
879 | |||
880 |
2/4✓ Branch 0 taken 732 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 732 times.
✗ Branch 4 not taken.
|
747 | pointIndices.reset(new PointIndexType[points.size()]); |
881 | |||
882 | std::vector<PointIndexType*> indexArray; | ||
883 |
1/2✓ Branch 1 taken 732 times.
✗ Branch 2 not taken.
|
747 | indexArray.reserve(numSegments); |
884 | |||
885 | 747 | PointIndexType* index = pointIndices.get(); | |
886 |
2/2✓ Branch 0 taken 1670 times.
✓ Branch 1 taken 732 times.
|
3197 | for (size_t n = 0; n < numSegments; ++n) { |
887 |
1/2✓ Branch 1 taken 1670 times.
✗ Branch 2 not taken.
|
2450 | indexArray.push_back(index); |
888 | 2450 | index += offsetSegments[n]->size(); | |
889 | } | ||
890 | |||
891 | // compute leaf node origin for each page | ||
892 | |||
893 |
3/4✓ Branch 1 taken 732 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 44744 times.
✓ Branch 4 taken 732 times.
|
76719 | pageCoordinates.reset(new Coord[pageCount]); |
894 | |||
895 |
2/4✓ Branch 1 taken 732 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 732 times.
✗ Branch 5 not taken.
|
747 | tbb::parallel_for(segmentRange, |
896 | 2882 | [&](tbb::blocked_range<size_t>& range) | |
897 | { | ||
898 |
6/6✓ Branch 0 taken 916 times.
✓ Branch 1 taken 916 times.
✓ Branch 2 taken 12 times.
✓ Branch 3 taken 12 times.
✓ Branch 4 taken 742 times.
✓ Branch 5 taken 284 times.
|
2882 | for (size_t n = range.begin(); n < range.end(); n++) |
899 | { | ||
900 | 1670 | Index segmentOffset = segmentOffsets[n]; | |
901 | 1670 | PointIndexType* indices = pageIndexArrays[n].get(); | |
902 | |||
903 | const Coord& segmentCoord = segmentCoords[n]; | ||
904 | |||
905 | // segment size stored in the first value of the offset array | ||
906 | 1670 | const size_t segmentSize = pageOffsetArrays[n][0] - 1; | |
907 | tbb::blocked_range<size_t> copyRange(0, segmentSize); | ||
908 | 1670 | tbb::parallel_for(copyRange, | |
909 | 32296 | [&](tbb::blocked_range<size_t>& r) | |
910 | { | ||
911 |
6/6✓ Branch 0 taken 16372 times.
✓ Branch 1 taken 8288 times.
✓ Branch 2 taken 1761 times.
✓ Branch 3 taken 913 times.
✓ Branch 4 taken 26611 times.
✓ Branch 5 taken 23095 times.
|
77040 | for (size_t i = r.begin(); i < r.end(); i++) |
912 | { | ||
913 | 44744 | Index pageIndex = indices[i]; | |
914 | 44744 | Coord& ijk = pageCoordinates[segmentOffset+i]; | |
915 | |||
916 | 44744 | ijk[0] = pageIndex >> (2 * binLog2Dim); | |
917 | 44744 | Index pageIndexModulo = pageIndex - (ijk[0] << (2 * binLog2Dim)); | |
918 | 44744 | ijk[1] = pageIndexModulo >> binLog2Dim; | |
919 | 44744 | ijk[2] = pageIndexModulo - (ijk[1] << binLog2Dim); | |
920 | |||
921 | 44744 | ijk = (ijk << bucketLog2Dim) + segmentCoord; | |
922 | } | ||
923 | } | ||
924 | ); | ||
925 | } | ||
926 | } | ||
927 | ); | ||
928 | |||
929 | // move segment data | ||
930 | |||
931 |
2/6✓ Branch 1 taken 732 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 732 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
|
747 | tbb::parallel_for(segmentRange, |
932 | MoveSegmentDataOp<PointIndexType>(indexArray, offsetSegments.get())); | ||
933 | 747 | } | |
934 | |||
935 | |||
936 | } // namespace point_partitioner_internal | ||
937 | |||
938 | /// @endcond | ||
939 | |||
940 | //////////////////////////////////////// | ||
941 | |||
942 | |||
943 | template<typename PointIndexType, Index BucketLog2Dim> | ||
944 |
4/8✓ Branch 1 taken 711 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 12 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 5 times.
✗ Branch 11 not taken.
|
732 | inline PointPartitioner<PointIndexType, BucketLog2Dim>::PointPartitioner() |
945 | : mPointIndices(nullptr) | ||
946 | , mVoxelOffsets(nullptr) | ||
947 | , mPageOffsets(nullptr) | ||
948 | , mPageCoordinates(nullptr) | ||
949 | , mPageCount(0) | ||
950 |
4/8✓ Branch 1 taken 711 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 12 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 5 times.
✗ Branch 11 not taken.
|
732 | , mUsingCellCenteredTransform(true) |
951 | { | ||
952 | } | ||
953 | |||
954 | |||
955 | template<typename PointIndexType, Index BucketLog2Dim> | ||
956 | inline void | ||
957 | PointPartitioner<PointIndexType, BucketLog2Dim>::clear() | ||
958 | { | ||
959 | mPageCount = 0; | ||
960 | mUsingCellCenteredTransform = true; | ||
961 | mPointIndices.reset(); | ||
962 | mVoxelOffsets.reset(); | ||
963 | mPageOffsets.reset(); | ||
964 | mPageCoordinates.reset(); | ||
965 | } | ||
966 | |||
967 | |||
968 | template<typename PointIndexType, Index BucketLog2Dim> | ||
969 | inline void | ||
970 | PointPartitioner<PointIndexType, BucketLog2Dim>::swap(PointPartitioner& rhs) | ||
971 | { | ||
972 | const IndexType tmpLhsPageCount = mPageCount; | ||
973 | mPageCount = rhs.mPageCount; | ||
974 | rhs.mPageCount = tmpLhsPageCount; | ||
975 | |||
976 | mPointIndices.swap(rhs.mPointIndices); | ||
977 | mVoxelOffsets.swap(rhs.mVoxelOffsets); | ||
978 | mPageOffsets.swap(rhs.mPageOffsets); | ||
979 | mPageCoordinates.swap(rhs.mPageCoordinates); | ||
980 | |||
981 | bool lhsCellCenteredTransform = mUsingCellCenteredTransform; | ||
982 | mUsingCellCenteredTransform = rhs.mUsingCellCenteredTransform; | ||
983 | rhs.mUsingCellCenteredTransform = lhsCellCenteredTransform; | ||
984 | } | ||
985 | |||
986 | |||
987 | template<typename PointIndexType, Index BucketLog2Dim> | ||
988 | inline typename PointPartitioner<PointIndexType, BucketLog2Dim>::IndexIterator | ||
989 |
1/2✓ Branch 0 taken 86895 times.
✗ Branch 1 not taken.
|
86895 | PointPartitioner<PointIndexType, BucketLog2Dim>::indices(size_t n) const |
990 | { | ||
991 |
2/4✓ Branch 0 taken 86895 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 86895 times.
|
86895 | assert(bool(mPointIndices) && bool(mPageCount)); |
992 | return IndexIterator( | ||
993 | 86895 | mPointIndices.get() + mPageOffsets[n], | |
994 | 86895 | mPointIndices.get() + mPageOffsets[n + 1]); | |
995 | } | ||
996 | |||
997 | |||
998 | template<typename PointIndexType, Index BucketLog2Dim> | ||
999 | template<typename PointArray> | ||
1000 | inline void | ||
1001 | 747 | PointPartitioner<PointIndexType, BucketLog2Dim>::construct( | |
1002 | const PointArray& points, | ||
1003 | const math::Transform& xform, | ||
1004 | bool voxelOrder, | ||
1005 | bool recordVoxelOffsets, | ||
1006 | bool cellCenteredTransform) | ||
1007 | { | ||
1008 | 747 | mUsingCellCenteredTransform = cellCenteredTransform; | |
1009 | |||
1010 | 747 | point_partitioner_internal::partition(points, xform, BucketLog2Dim, | |
1011 | 747 | mPointIndices, mPageOffsets, mPageCoordinates, mPageCount, mVoxelOffsets, | |
1012 | 747 | (voxelOrder || recordVoxelOffsets), cellCenteredTransform); | |
1013 | |||
1014 |
2/2✓ Branch 0 taken 719 times.
✓ Branch 1 taken 13 times.
|
747 | const tbb::blocked_range<size_t> pageRange(0, mPageCount); |
1015 | |||
1016 |
3/4✓ Branch 0 taken 719 times.
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 719 times.
|
747 | if (mVoxelOffsets && voxelOrder) { |
1017 | ✗ | tbb::parallel_for(pageRange, point_partitioner_internal::VoxelOrderOp< | |
1018 | IndexType, BucketLog2Dim>(mPointIndices, mPageOffsets, mVoxelOffsets)); | ||
1019 | } | ||
1020 | |||
1021 |
3/4✓ Branch 0 taken 719 times.
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 719 times.
|
747 | if (mVoxelOffsets && !recordVoxelOffsets) { |
1022 | mVoxelOffsets.reset(); | ||
1023 | } | ||
1024 | 747 | } | |
1025 | |||
1026 | |||
1027 | template<typename PointIndexType, Index BucketLog2Dim> | ||
1028 | template<typename PointArray> | ||
1029 | inline typename PointPartitioner<PointIndexType, BucketLog2Dim>::Ptr | ||
1030 | 1 | PointPartitioner<PointIndexType, BucketLog2Dim>::create( | |
1031 | const PointArray& points, | ||
1032 | const math::Transform& xform, | ||
1033 | bool voxelOrder, | ||
1034 | bool recordVoxelOffsets, | ||
1035 | bool cellCenteredTransform) | ||
1036 | { | ||
1037 | 1 | Ptr ret(new PointPartitioner()); | |
1038 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | ret->construct(points, xform, voxelOrder, recordVoxelOffsets, cellCenteredTransform); |
1039 | 1 | return ret; | |
1040 | } | ||
1041 | |||
1042 | |||
1043 | //////////////////////////////////////// | ||
1044 | |||
1045 | |||
1046 | } // namespace tools | ||
1047 | } // namespace OPENVDB_VERSION_NAME | ||
1048 | } // namespace openvdb | ||
1049 | |||
1050 | |||
1051 | #endif // OPENVDB_TOOLS_POINT_PARTITIONER_HAS_BEEN_INCLUDED | ||
1052 |