EBGeometry
Compact, header-only C++ library for fast evaluation of signed distance functions
Loading...
Searching...
No Matches
Source
EBGeometry_GPU.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
37
#ifndef EBGEOMETRY_GPU_HPP
38
#define EBGEOMETRY_GPU_HPP
39
40
// ── Backend detection ─────────────────────────────────────────────────────────
41
// A single #if/#elif chain, so at most one backend flag is defined per translation unit. HIP is
42
// tested BEFORE CUDA: hipcc on the NVIDIA platform defines both __HIPCC__ and __CUDACC__, and there
43
// the hip* entry points exist and forward to cuda*, so hip-on-NVIDIA must resolve to HIP. The flags
44
// are value-less: every consumer tests them with #if defined(...), never by arithmetic value.
45
#if defined(__HIPCC__)
46
#define EBGEOMETRY_HIP
47
#elif defined(__CUDACC__)
48
#define EBGEOMETRY_CUDA
49
#endif
50
51
// ── Host-tuned defaults are for host-only builds ──────────────────────────────
52
// EBGEOMETRY_HOST_TUNED_DEFAULTS makes TriangleSoA::DefaultWidth follow the SIMD flags. The host
53
// and device passes of one CUDA or HIP compile see different ISA macros, so the same type would get
54
// two layouts within one translation unit. The test keys on EBGEOMETRY_CUDA/EBGEOMETRY_HIP (from
55
// __CUDACC__/__HIPCC__), which both passes define, and deliberately not on EBGEOMETRY_DEVICE_COMPILE,
56
// which only the device pass does: that would let the host pass through and reintroduce the split
57
// this guard exists to prevent.
58
#if defined(EBGEOMETRY_HOST_TUNED_DEFAULTS) && (defined(EBGEOMETRY_CUDA) || defined(EBGEOMETRY_HIP))
59
#error \
60
"EBGEOMETRY_HOST_TUNED_DEFAULTS is for host-only builds; it must not be defined in a CUDA or HIP translation unit"
61
#endif
62
63
// ── Function decoration ───────────────────────────────────────────────────────
64
// CUDA and HIP require an explicit prefix on any function that may be called from device code. On
65
// every other compiler the decoration macros expand to nothing.
66
#if defined(EBGEOMETRY_CUDA) || defined(EBGEOMETRY_HIP)
67
69
#define EBGEOMETRY_HOST_DEVICE __host__ __device__
70
72
#define EBGEOMETRY_HOST __host__
73
75
#define EBGEOMETRY_GLOBAL __global__
76
77
#else
78
80
#define EBGEOMETRY_HOST_DEVICE
81
83
#define EBGEOMETRY_HOST
84
86
#define EBGEOMETRY_GLOBAL
87
88
#endif
89
90
// ── Device-compilation-pass detection ─────────────────────────────────────────
91
// True only while the offload compiler is generating device code for the current __host__ __device__
92
// function (CUDA/HIP compile each such function twice). Used to select device-safe code paths, e.g.
93
// the assertion in EBGeometry_Macros.hpp.
94
#if defined(__CUDA_ARCH__) || defined(__HIP_DEVICE_COMPILE__)
95
#define EBGEOMETRY_DEVICE_COMPILE
96
#endif
97
98
// ── Out-of-line device functions ──────────────────────────────────────────────
99
// HIP inlines every device function by default (-amdgpu-function-calls=false), so a function called
100
// from many places is copied into each of them. A function that calls itself one level down from
101
// many places (the tape's evaluate<D>) then grows as the number of call sites to the power of the
102
// depth. Marking it out of line in the device pass keeps it a real call there; the host pass is left
103
// to its own inliner. Fully inlined, an optimized HIP compile of TestTape was stopped after ten minutes and
104
// 2.5 GB; with evaluate<D> out of line it takes half a minute.
105
#if defined(EBGEOMETRY_DEVICE_COMPILE)
107
#define EBGEOMETRY_DEVICE_NOINLINE __noinline__
108
#else
110
#define EBGEOMETRY_DEVICE_NOINLINE
111
#endif
112
113
// ── Inlined host kernels ──────────────────────────────────────────────────────
114
// GCC weighs a large kernel against the code around each call and may keep it out of line, which for
115
// a SIMD kernel costs a round trip of every register through memory. The host pass of a kernel that
116
// must be inlined is marked so; the device pass is left to its own inliner. Use it together with
117
// `inline` (or on a function defined in its class), as GCC warns about always_inline otherwise.
118
#if !defined(EBGEOMETRY_DEVICE_COMPILE) && (defined(__GNUC__) || defined(__clang__))
120
#define EBGEOMETRY_HOST_ALWAYS_INLINE __attribute__((always_inline))
121
#else
123
#define EBGEOMETRY_HOST_ALWAYS_INLINE
124
#endif
125
126
// ── Out-of-line host functions ────────────────────────────────────────────────
127
// GCC's choice of what to inline into a large function can change with unrelated code elsewhere in the
128
// translation unit. Where a hot function must be split at a fixed point (the tape's primitive dispatch), the
129
// host pass of the part beyond it is marked so; the device pass is left to its own inliner.
130
#if !defined(EBGEOMETRY_DEVICE_COMPILE) && (defined(__GNUC__) || defined(__clang__))
132
#define EBGEOMETRY_HOST_NOINLINE __attribute__((noinline))
133
#else
135
#define EBGEOMETRY_HOST_NOINLINE
136
#endif
137
138
// ── Kernel parameters read in place ───────────────────────────────────────────
139
// A kernel receives its arguments in a read-only parameter space. A function that needs the address of
140
// one (a member function of a query that is not inlined, such as a Tape's evaluator) makes the compiler
141
// copy the whole argument to each thread's local memory first: for a Tape, a few hundred bytes per
142
// thread, which made it about 30 times slower on a device than the same object written by hand. CUDA
143
// 11.7 and later can give the address of a __grid_constant__ parameter without the copy, on
144
// compute capability 7.0 and later; nvcc rejects the qualifier in a device pass for an older
145
// architecture, so there (__CUDA_ARCH__ below 700) the macro is empty and the parameter is copied.
146
// The host pass and the newer device passes of the same build keep it. HIP has no such
147
// qualifier, so there the macro is empty.
148
#if defined(EBGEOMETRY_CUDA) && defined(__CUDACC_VER_MAJOR__) && \
149
(__CUDACC_VER_MAJOR__ > 11 || (__CUDACC_VER_MAJOR__ == 11 && __CUDACC_VER_MINOR__ >= 7)) && \
150
!(defined(__CUDA_ARCH__) && __CUDA_ARCH__ < 700)
152
#define EBGEOMETRY_GRID_CONSTANT __grid_constant__
153
#else
155
#define EBGEOMETRY_GRID_CONSTANT
156
#endif
157
158
#endif
// EBGEOMETRY_GPU_HPP
Generated by
1.9.8