EBGeometry
Compact, header-only C++ library for fast evaluation of signed distance functions
Loading...
Searching...
No Matches
EBGeometry_GPUAlgorithms.hpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2026 Robert Marskar <robert.marskar@sintef.no>
2//
3// SPDX-License-Identifier: GPL-3.0-or-later
4
17#ifndef EBGEOMETRY_GPUALGORITHMS_HPP
18#define EBGEOMETRY_GPUALGORITHMS_HPP
19
20// Std includes
21#include <cmath>
22#include <cstddef>
23#include <cstdint>
24#include <type_traits>
25
26// Our includes
28#include "EBGeometry_GPU.hpp"
31#include "EBGeometry_Macros.hpp"
32#include "EBGeometry_Math.hpp"
33#include "EBGeometry_Vec.hpp"
34
35namespace EBGeometry {
36
37namespace GPU {
38
42inline constexpr uint32_t ScanChunk = 256;
43
49[[nodiscard]] EBGEOMETRY_HOST
50inline uint32_t
51exclusiveScanScratchSize(const uint32_t a_count) noexcept
52{
53 return (a_count + ScanChunk - 1) / ScanChunk + 1U;
54}
55
59inline constexpr uint32_t BoundingBoxChunk = 256;
60
66[[nodiscard]] EBGEOMETRY_HOST
67inline uint32_t
68boundingBoxScratchSize(const uint32_t a_count) noexcept
69{
70 return 2U * Math::max(1U, (a_count + BoundingBoxChunk - 1) / BoundingBoxChunk);
71}
72
77namespace AlgorithmsDetail {
78
83template <class T>
85{
88
94 [[nodiscard]] EBGEOMETRY_HOST_DEVICE
96 operator()(const uint32_t a_i) const noexcept
97 {
98 return m_points[a_i];
99 }
100};
101
107template <class T, class Source>
109{
111 Source m_points;
112
115
117 uint32_t m_count;
118
124 void
125 operator()(const std::size_t a_c) const noexcept
126 {
127 const uint32_t begin = static_cast<uint32_t>(a_c) * BoundingBoxChunk;
128 const uint32_t end = Math::min(m_count, begin + BoundingBoxChunk);
129
130 Vec3T<T> lo = +Vec3T<T>::max();
131 Vec3T<T> hi = -Vec3T<T>::max();
132
133 bool finite = true;
134
135 for (uint32_t i = begin; i < end; i++) {
136 const Vec3T<T> p = m_points(i);
137
138 finite = finite && std::isfinite(p[0]) && std::isfinite(p[1]) && std::isfinite(p[2]);
139
140 lo = min(lo, p);
141 hi = max(hi, p);
142 }
143
144 // A chunk with a non-finite point marks its box with a lower corner of -infinity, which the merge keeps.
145 if (!finite) {
146 lo[0] = -Math::Limits<T>::infinity();
147 }
148
149 m_corners[2 * a_c] = lo;
150 m_corners[2 * a_c + 1] = hi;
151 }
152};
153
159template <class T>
161{
164
166 uint32_t m_chunks;
167
172 void
173 operator()(std::size_t /*a_thread*/) const noexcept
174 {
175 Vec3T<T> lo = m_corners[0];
176 Vec3T<T> hi = m_corners[1];
177
178 for (uint32_t c = 1; c < m_chunks; c++) {
179 lo = min(lo, m_corners[2 * c]);
180 hi = max(hi, m_corners[2 * c + 1]);
181 }
182
183 m_corners[0] = lo;
184 m_corners[1] = hi;
185 }
186};
187
193template <class Key, class Less>
195{
197 Key* m_keys;
198
200 Less m_less;
201
203 uint32_t m_k;
204
206 uint32_t m_j;
207
213 void
214 operator()(const std::size_t a_t) const noexcept
215 {
216 const auto t = static_cast<uint32_t>(a_t);
217 const uint32_t i = (t / m_j) * 2 * m_j + t % m_j;
218 const uint32_t l = i + m_j;
219
220 Key& a = m_keys[i];
221 Key& b = m_keys[l];
222
223 const bool ascending = (i & m_k) == 0;
224
225 if (ascending ? m_less(b, a) : m_less(a, b)) {
226 const Key swap = a;
227
228 a = b;
229 b = swap;
230 }
231 }
232};
233
238{
240 const uint32_t* m_values;
241
243 uint32_t* m_sums;
244
246 uint32_t m_count;
247
253 void
254 operator()(const std::size_t a_c) const noexcept
255 {
256 const uint32_t begin = static_cast<uint32_t>(a_c) * ScanChunk;
257 const uint32_t end = Math::min(m_count, begin + ScanChunk);
258
259 uint32_t sum = 0;
260
261 for (uint32_t i = begin; i < end; i++) {
262 sum += m_values[i];
263 }
264
265 m_sums[a_c] = sum;
266 }
267};
268
273{
275 uint32_t* m_sums;
276
278 uint32_t m_chunks;
279
284 void
285 operator()(std::size_t /*a_thread*/) const noexcept
286 {
287 uint32_t running = 0;
288
289 for (uint32_t c = 0; c < m_chunks; c++) {
290 const uint32_t sum = m_sums[c];
291
292 m_sums[c] = running;
293 running += sum;
294 }
295
296 m_sums[m_chunks] = running;
297 }
298};
299
304{
306 uint32_t* m_values;
307
309 const uint32_t* m_offsets;
310
312 uint32_t m_count;
313
319 void
320 operator()(const std::size_t a_c) const noexcept
321 {
322 const uint32_t begin = static_cast<uint32_t>(a_c) * ScanChunk;
323 const uint32_t end = Math::min(m_count, begin + ScanChunk);
324
325 uint32_t running = m_offsets[a_c];
326
327 for (uint32_t i = begin; i < end; i++) {
328 const uint32_t value = m_values[i];
329
330 m_values[i] = running;
331 running += value;
332 }
333 }
334};
335
336} // namespace AlgorithmsDetail
337
342{
350 template <class Key>
351 [[nodiscard]] EBGEOMETRY_HOST_DEVICE
352 bool
353 operator()(const Key& a_lhs, const Key& a_rhs) const noexcept
354 {
355 return a_lhs < a_rhs;
356 }
357};
358
373template <class T, class Source>
374[[nodiscard]] EBGEOMETRY_HOST
376boundingBoxOf(const Source& a_points, const uint32_t a_count, Vec3T<T>* a_scratch)
377{
378 static_assert(std::is_trivially_copyable_v<Source>, "GPU::boundingBoxOf: the source must be trivially copyable");
379
380 if (a_count == 0) {
382 }
383
384 const uint32_t chunks = boundingBoxScratchSize(a_count) / 2;
385
386 parallelFor(chunks, AlgorithmsDetail::ChunkBox<T, Source>{a_points, a_scratch, a_count});
387 parallelFor(1, AlgorithmsDetail::MergeChunks<T>{a_scratch, chunks});
388
389 Vec3T<T> corners[2];
390
391#if defined(EBGEOMETRY_CUDA) || defined(EBGEOMETRY_HIP)
392 EBGEOMETRY_GPU_CHECK(GPU::memcpy(corners, a_scratch, sizeof(corners), GPU::MemcpyDeviceToHost));
393#else
394 corners[0] = a_scratch[0];
395 corners[1] = a_scratch[1];
396#endif
397
398 return BoundingVolumes::AABBT<T>(corners[0], corners[1]);
399}
400
409template <class T>
410[[nodiscard]] EBGEOMETRY_HOST
412boundingBox(const Vec3T<T>* a_points, const uint32_t a_count, Vec3T<T>* a_scratch)
413{
414 return boundingBoxOf(AlgorithmsDetail::FromPointer<T>{a_points}, a_count, a_scratch);
415}
416
429template <class Key, class Less = Ascending>
431inline void
432bitonicSort(Key* a_keys, const uint32_t a_count, const Less& a_less = Less())
433{
434 static_assert(std::is_trivially_copyable_v<Key>, "GPU::bitonicSort: the key type must be trivially copyable");
435
437 (a_count & (a_count - 1)) == 0, "GPU::bitonicSort: the key count must be a power of two (%u)", unsigned(a_count));
438
439 for (uint32_t k = 2; k <= a_count && k != 0; k *= 2) {
440 for (uint32_t j = k / 2; j > 0; j /= 2) {
441 parallelFor(a_count / 2, AlgorithmsDetail::BitonicPass<Key, Less>{a_keys, a_less, k, j});
442 }
443 }
444}
445
457[[nodiscard]] EBGEOMETRY_HOST
458inline uint32_t
459exclusiveScan(uint32_t* a_values, const uint32_t a_count, uint32_t* a_scratch)
460{
461 if (a_count == 0) {
462 return 0;
463 }
464
465 const uint32_t chunks = exclusiveScanScratchSize(a_count) - 1U;
466
467 parallelFor(chunks, AlgorithmsDetail::ChunkSums{a_values, a_scratch, a_count});
468 parallelFor(1, AlgorithmsDetail::ScanSums{a_scratch, chunks});
469 parallelFor(chunks, AlgorithmsDetail::ScanChunks{a_values, a_scratch, a_count});
470
471 uint32_t total = 0;
472
473#if defined(EBGEOMETRY_CUDA) || defined(EBGEOMETRY_HIP)
474 EBGEOMETRY_GPU_CHECK(GPU::memcpy(&total, a_scratch + chunks, sizeof(total), GPU::MemcpyDeviceToHost));
475#else
476 total = a_scratch[chunks];
477#endif
478
479 return total;
480}
481
482} // namespace GPU
483
484} // namespace EBGeometry
485
486#endif
Declarations of bounding volume types used in bounding volume hierarchies.
Backend-neutral GPU runtime API layer (CUDA or HIP) plus the EBGEOMETRY_GPU_CHECK macro.
#define EBGEOMETRY_GPU_CHECK(x)
Always-on GPU runtime API error check: prints the failing call and aborts.
Definition EBGeometry_GPURuntime.hpp:527
GPU::parallelFor() and GPU::transform(), which run a functor over many indices or inputs: in a kernel...
GPU portability layer: host/device decoration macros and backend detection.
#define EBGEOMETRY_HOST_DEVICE
Mark a function as callable from both host and device code (no-op on host compilers).
Definition EBGeometry_GPU.hpp:80
#define EBGEOMETRY_HOST
Mark a function as callable from host code only (no-op on host compilers).
Definition EBGeometry_GPU.hpp:83
Utility macros for EBGeometry.
#define EBGEOMETRY_REQUIRE(cond,...)
Always-on check of user input and one-time host setup.
Definition EBGeometry_Macros.hpp:84
Scalar helpers and numeric limits that compile for both host and device.
Declaration of 2D and 3D point/vector classes with templated precision, and free functions on them.
Axis-aligned bounding box (AABB) enclosing a set of 3D points.
Definition EBGeometry_BoundingVolumes.hpp:37
Three-dimensional vector class with arithmetic operators.
Definition EBGeometry_Vec.hpp:258
static constexpr Vec3T< T > max() noexcept
Return the most-positive representable vector.
void parallelFor(std::size_t a_n, const F &a_f)
Run a functor on every index: a_f(i) for i = 0, ..., a_n - 1, in no particular order.
BoundingVolumes::AABBT< T > boundingBox(const Vec3T< T > *a_points, const uint32_t a_count, Vec3T< T > *a_scratch)
The smallest box holding an array of points; see boundingBoxOf().
Definition EBGeometry_GPUAlgorithms.hpp:412
BoundingVolumes::AABBT< T > boundingBoxOf(const Source &a_points, const uint32_t a_count, Vec3T< T > *a_scratch)
The smallest box holding every point, from a source of points: one box per chunk of BoundingBoxChunk ...
Definition EBGeometry_GPUAlgorithms.hpp:376
Error memcpy(void *a_dst, const void *a_src, size_t a_numBytes, MemcpyKind a_kind) noexcept
Copy memory between host and device (cudaMemcpy / hipMemcpy). Synchronous.
uint32_t exclusiveScan(uint32_t *a_values, const uint32_t a_count, uint32_t *a_scratch)
Replace a_count values by their exclusive prefix sums, in place, and return their total: one sum per ...
Definition EBGeometry_GPUAlgorithms.hpp:459
constexpr uint32_t ScanChunk
Values per chunk in exclusiveScan()'s passes.
Definition EBGeometry_GPUAlgorithms.hpp:42
void bitonicSort(Key *a_keys, const uint32_t a_count, const Less &a_less=Less())
Sort a_count keys in place with a bitonic network: log2(n) (log2(n) + 1) / 2 passes of n / 2 compare-...
Definition EBGeometry_GPUAlgorithms.hpp:432
constexpr uint32_t BoundingBoxChunk
Points per chunk in boundingBox()'s first pass.
Definition EBGeometry_GPUAlgorithms.hpp:59
uint32_t boundingBoxScratchSize(const uint32_t a_count) noexcept
Scratch elements boundingBox() needs for a_count points: two corners per chunk.
Definition EBGeometry_GPUAlgorithms.hpp:68
constexpr MemcpyKind MemcpyDeviceToHost
Device-to-host copy direction (cudaMemcpyDeviceToHost / hipMemcpyDeviceToHost).
Definition EBGeometry_GPURuntime.hpp:81
uint32_t exclusiveScanScratchSize(const uint32_t a_count) noexcept
Scratch elements exclusiveScan() needs for a_count values: one per chunk, and one for the total.
Definition EBGeometry_GPUAlgorithms.hpp:51
constexpr T max(const T &a_a, const T &a_b) noexcept
The larger of two values.
Definition EBGeometry_Math.hpp:61
constexpr T min(const T &a_a, const T &a_b) noexcept
The smaller of two values.
Definition EBGeometry_Math.hpp:44
Namespace containing all of EBGeometry's functionality.
Definition EBGeometry_Affine.hpp:26
Vec2T< T > min(const Vec2T< T > &u, const Vec2T< T > &v) noexcept
Component-wise minimum of two vectors.
Vec2T< T > max(const Vec2T< T > &u, const Vec2T< T > &v) noexcept
Component-wise maximum of two vectors.
bitonicSort(): one compare-exchange pass, one thread per pair.
Definition EBGeometry_GPUAlgorithms.hpp:195
Key * m_keys
The keys.
Definition EBGeometry_GPUAlgorithms.hpp:197
void operator()(const std::size_t a_t) const noexcept
Pair a_t.
Definition EBGeometry_GPUAlgorithms.hpp:214
uint32_t m_k
Size of the bitonic sequences being merged.
Definition EBGeometry_GPUAlgorithms.hpp:203
Less m_less
The order.
Definition EBGeometry_GPUAlgorithms.hpp:200
uint32_t m_j
Distance between the two keys of a pair.
Definition EBGeometry_GPUAlgorithms.hpp:206
boundingBox(), pass 1: the box of each chunk of points.
Definition EBGeometry_GPUAlgorithms.hpp:109
void operator()(const std::size_t a_c) const noexcept
Box of chunk a_c.
Definition EBGeometry_GPUAlgorithms.hpp:125
uint32_t m_count
Number of points.
Definition EBGeometry_GPUAlgorithms.hpp:117
Vec3T< T > * m_corners
Chunk corners: lower corner of chunk c at 2c, upper at 2c + 1.
Definition EBGeometry_GPUAlgorithms.hpp:114
Source m_points
The points.
Definition EBGeometry_GPUAlgorithms.hpp:111
exclusiveScan(), pass 1: the sum of each chunk of values.
Definition EBGeometry_GPUAlgorithms.hpp:238
uint32_t * m_sums
Chunk sums, one per chunk.
Definition EBGeometry_GPUAlgorithms.hpp:243
void operator()(const std::size_t a_c) const noexcept
Sum of chunk a_c.
Definition EBGeometry_GPUAlgorithms.hpp:254
uint32_t m_count
Number of values.
Definition EBGeometry_GPUAlgorithms.hpp:246
const uint32_t * m_values
The values.
Definition EBGeometry_GPUAlgorithms.hpp:240
boundingBox()'s source for an array of points.
Definition EBGeometry_GPUAlgorithms.hpp:85
const Vec3T< T > * m_points
The points.
Definition EBGeometry_GPUAlgorithms.hpp:87
Vec3T< T > operator()(const uint32_t a_i) const noexcept
Point a_i.
Definition EBGeometry_GPUAlgorithms.hpp:96
boundingBox(), pass 2: one thread merges the chunk boxes into chunk 0's. One thread is enough at the ...
Definition EBGeometry_GPUAlgorithms.hpp:161
void operator()(std::size_t) const noexcept
Merge every chunk's box.
Definition EBGeometry_GPUAlgorithms.hpp:173
Vec3T< T > * m_corners
Chunk corners, as ChunkBox wrote them.
Definition EBGeometry_GPUAlgorithms.hpp:163
uint32_t m_chunks
Number of chunks.
Definition EBGeometry_GPUAlgorithms.hpp:166
exclusiveScan(), pass 3: each chunk's values replaced by their exclusive prefix sums.
Definition EBGeometry_GPUAlgorithms.hpp:304
uint32_t * m_values
The values, in place.
Definition EBGeometry_GPUAlgorithms.hpp:306
const uint32_t * m_offsets
Each chunk's offset.
Definition EBGeometry_GPUAlgorithms.hpp:309
void operator()(const std::size_t a_c) const noexcept
Scan chunk a_c.
Definition EBGeometry_GPUAlgorithms.hpp:320
uint32_t m_count
Number of values.
Definition EBGeometry_GPUAlgorithms.hpp:312
exclusiveScan(), pass 2: one thread turns the chunk sums into each chunk's offset,...
Definition EBGeometry_GPUAlgorithms.hpp:273
uint32_t * m_sums
Chunk sums in, chunk offsets out; one more entry receives the total.
Definition EBGeometry_GPUAlgorithms.hpp:275
uint32_t m_chunks
Number of chunks.
Definition EBGeometry_GPUAlgorithms.hpp:278
void operator()(std::size_t) const noexcept
Scan the chunk sums.
Definition EBGeometry_GPUAlgorithms.hpp:285
The order operator< gives, as a trivially copyable functor callable on a device.
Definition EBGeometry_GPUAlgorithms.hpp:342
bool operator()(const Key &a_lhs, const Key &a_rhs) const noexcept
Whether a_lhs sorts before a_rhs.
Definition EBGeometry_GPUAlgorithms.hpp:353
Numeric limits, with the meanings of the std::numeric_limits members of the same names.
Definition EBGeometry_Math.hpp:91