chrono_batch_render/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
11 / 136
8.1%
Branches:
7 / 60
11.7%
Functions:
3 / 11
27.3%

device/cuda/api.cpp
Line Branch Exec Source
1 // SPDX-License-Identifier: BSD-3-Clause
2 #include "chrono_batch_render/device/cuda/api.h"
3
4 #include <utility>
5 #include <variant>
6
7 #include <fmt/format.h>
8
9 #include "chrono_batch_render/util/error.h"
10 #include "chrono_batch_render/util/library.h"
11 #include "chrono_batch_render/util/log.h"
12
13 namespace chrono::batch_render::device::cuda {
14 namespace {
15
16 using CUdriverProcAddressQueryResult = int;
17 using GetProcAddress = CUresult (*)(const char* symbol, void** function, int cudaVersion,
18 uint64_t flags, CUdriverProcAddressQueryResult* status);
19
20 ✗ void ResolveFunctions(SymbolResolver& resolve, Api& api) {
21 ✗ resolve("cuInit", api.cuInit);
22 ✗ resolve("cuDriverGetVersion", api.cuDriverGetVersion);
23 ✗ resolve("cuDeviceGetCount", api.cuDeviceGetCount);
24 ✗ resolve("cuGetErrorName", api.cuGetErrorName);
25 ✗ resolve("cuGetErrorString", api.cuGetErrorString);
26 ✗ resolve("cuDeviceGet", api.cuDeviceGet);
27 ✗ resolve("cuDeviceGetName", api.cuDeviceGetName);
28 ✗ resolve("cuDeviceGetUuid", api.cuDeviceGetUuid);
29 ✗ resolve("cuDeviceTotalMem", api.cuDeviceTotalMem);
30 ✗ resolve("cuDeviceGetAttribute", api.cuDeviceGetAttribute);
31 ✗ resolve("cuDevicePrimaryCtxRetain", api.cuDevicePrimaryCtxRetain);
32 ✗ resolve("cuDevicePrimaryCtxRelease", api.cuDevicePrimaryCtxRelease);
33 ✗ resolve("cuCtxPushCurrent", api.cuCtxPushCurrent);
34 ✗ resolve("cuCtxPopCurrent", api.cuCtxPopCurrent);
35 ✗ resolve("cuMemAlloc", api.cuMemAlloc);
36 ✗ resolve("cuMemFree", api.cuMemFree);
37 ✗ resolve("cuMemGetInfo", api.cuMemGetInfo);
38 ✗ resolve("cuMemPoolCreate", api.cuMemPoolCreate);
39 ✗ resolve("cuMemPoolDestroy", api.cuMemPoolDestroy);
40 ✗ resolve("cuMemPoolSetAttribute", api.cuMemPoolSetAttribute);
41 ✗ resolve("cuMemAllocFromPoolAsync", api.cuMemAllocFromPoolAsync);
42 ✗ resolve("cuMemFreeAsync", api.cuMemFreeAsync);
43 ✗ resolve("cuMemHostAlloc", api.cuMemHostAlloc);
44 ✗ resolve("cuMemFreeHost", api.cuMemFreeHost);
45 ✗ resolve("cuMemHostGetDevicePointer", api.cuMemHostGetDevicePointer);
46 ✗ resolve("cuStreamCreate", api.cuStreamCreate);
47 ✗ resolve("cuStreamDestroy", api.cuStreamDestroy);
48 ✗ resolve("cuStreamSynchronize", api.cuStreamSynchronize);
49 ✗ resolve("cuEventCreate", api.cuEventCreate);
50 ✗ resolve("cuEventDestroy", api.cuEventDestroy);
51 ✗ resolve("cuEventQuery", api.cuEventQuery);
52 ✗ resolve("cuEventSynchronize", api.cuEventSynchronize);
53 ✗ resolve("cuEventRecordWithFlags", api.cuEventRecordWithFlags);
54 ✗ resolve("cuStreamWaitEvent", api.cuStreamWaitEvent);
55 ✗ resolve("cuLaunchHostFunc", api.cuLaunchHostFunc);
56 ✗ resolve("cuImportExternalSemaphore", api.cuImportExternalSemaphore);
57 ✗ resolve("cuDestroyExternalSemaphore", api.cuDestroyExternalSemaphore);
58 ✗ resolve("cuImportExternalMemory", api.cuImportExternalMemory);
59 ✗ resolve("cuExternalMemoryGetMappedBuffer", api.cuExternalMemoryGetMappedBuffer);
60 ✗ resolve("cuDestroyExternalMemory", api.cuDestroyExternalMemory);
61 ✗ resolve("cuMemcpyDtoH", api.cuMemcpyDtoH);
62 ✗ resolve("cuSignalExternalSemaphoresAsync", api.cuSignalExternalSemaphoresAsync);
63 ✗ resolve("cuWaitExternalSemaphoresAsync", api.cuWaitExternalSemaphoresAsync);
64 ✗ resolve("cuEventElapsedTime", api.cuEventElapsedTime);
65 ✗ resolve("cuMemcpyAsync", api.cuMemcpyAsync);
66 ✗ resolve("cuMemsetD32Async", api.cuMemsetD32Async);
67 ✗ resolve("cuMemcpyHtoDAsync", api.cuMemcpyHtoDAsync);
68 ✗ resolve("cuModuleLoadData", api.cuModuleLoadData);
69 ✗ resolve("cuModuleUnload", api.cuModuleUnload);
70 ✗ resolve("cuModuleGetGlobal", api.cuModuleGetGlobal);
71 ✗ resolve("cuMemcpyHtoD", api.cuMemcpyHtoD);
72 ✗ resolve("cuModuleGetFunction", api.cuModuleGetFunction);
73 ✗ resolve("cuFuncGetAttribute", api.cuFuncGetAttribute);
74 ✗ resolve("cuLaunchKernel", api.cuLaunchKernel);
75 ✗ resolve("cuStreamBeginCapture", api.cuStreamBeginCapture);
76 ✗ resolve("cuStreamEndCapture", api.cuStreamEndCapture);
77 ✗ resolve("cuGraphInstantiateWithFlags", api.cuGraphInstantiateWithFlags);
78 ✗ resolve("cuGraphLaunch", api.cuGraphLaunch);
79 ✗ resolve("cuGraphExecDestroy", api.cuGraphExecDestroy);
80 ✗ resolve("cuGraphDestroy", api.cuGraphDestroy);
81 ✗ resolve("cuMipmappedArrayCreate", api.cuMipmappedArrayCreate);
82 ✗ resolve("cuMipmappedArrayGetLevel", api.cuMipmappedArrayGetLevel);
83 ✗ resolve("cuMipmappedArrayDestroy", api.cuMipmappedArrayDestroy);
84 ✗ resolve("cuMemcpy2DAsync", api.cuMemcpy2DAsync);
85 ✗ resolve("cuTexObjectCreate", api.cuTexObjectCreate);
86 ✗ resolve("cuTexObjectDestroy", api.cuTexObjectDestroy);
87 ✗ }
88
89 ✗ Loaded<Api> Initialize(Api api) {
90 ✗ int version = 0;
91 ✗ api.cuDriverGetVersion(&version);
92
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ api.driverVersion = DecodeVersion(version);
93
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (api.driverVersion < kMinimumDriverVersion) {
94 ✗ return TooOld("the CUDA driver", api.driverVersion, kMinimumDriverVersion);
95 }
96
97 ✗ const CUresult initialized = api.cuInit(0);
98
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (initialized == CUDA_ERROR_NO_DEVICE) {
99 ✗ return "the CUDA driver found no device";
100 }
101
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (initialized != CUDA_SUCCESS) {
102 ✗ return fmt::format("cuInit failed: {}", api.ErrorName(initialized));
103 }
104
105
0/1
✗ Branch 2 not taken.
✗ Log(LogLevel::Debug, "the CUDA driver supports CUDA {}", api.driverVersion.ToString());
106 ✗ return api;
107 }
108
109 11 Loaded<Api> Load() {
110 #if defined(_WIN32)
111 const auto names = {"nvcuda.dll"};
112 #else
113 11 const auto names = {"libcuda.so.1", "libcuda.so"};
114 #endif
115
1/1
✓ Branch 2 taken 11 times.
11 const Library library = Library::Load(names);
116
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
11 if (!library) {
117
1/1
✓ Branch 1 taken 11 times.
11 return "no CUDA driver was found";
118 }
119 ✗ GetProcAddress getProcAddress = nullptr;
120
0/3
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ if (!library.Symbol("cuGetProcAddress_v2", getProcAddress)) {
121
0/1
✗ Branch 1 not taken.
✗ return fmt::format("{} is older than CUDA 12.0", library.Name());
122 }
123
124 ✗ constexpr int kAbi = 12000;
125 ✗ SymbolResolver resolve([getProcAddress](const char* symbol) -> void* {
126 ✗ void* address = nullptr;
127 ✗ CUdriverProcAddressQueryResult status = 0;
128
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ return getProcAddress(symbol, &address, kAbi, 0, &status) == CUDA_SUCCESS ? address
129 ✗ : nullptr;
130 ✗ });
131
132 ✗ Api api;
133
0/1
✗ Branch 1 not taken.
✗ api.library = library.Name();
134
0/1
✗ Branch 1 not taken.
✗ ResolveFunctions(resolve, api);
135
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (resolve.Missing() != nullptr) {
136
0/1
✗ Branch 1 not taken.
✗ return fmt::format("{} lacks {}", api.library, resolve.Missing());
137 }
138
0/1
✗ Branch 2 not taken.
✗ return Initialize(std::move(api));
139 11 }
140
141 28 const Loaded<Api>& Cached() {
142
4/5
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 17 times.
✓ Branch 3 taken 11 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 11 times.
39 static const Loaded<Api> loaded = LogUnavailable("CUDA", Load());
143 28 return loaded;
144 }
145
146 }
147
148 ✗ bool Available() {
149 ✗ return std::holds_alternative<Api>(Cached());
150 }
151
152 28 const Api& GetApi() {
153 28 return RequireLoaded(Cached());
154 }
155
156 ✗ const compute::RuntimeCompiler& GetNvrtc() {
157 #if defined(_WIN32)
158 const auto names = {"nvrtc64_130_0.dll", "nvrtc64_120_0.dll"};
159 #else
160 ✗ const auto names = {"libnvrtc.so.13", "libnvrtc.so.12"};
161 #endif
162 ✗ static const Loaded<compute::RuntimeCompiler> loaded =
163
0/1
✗ Branch 1 not taken.
✗ LogUnavailable("NVRTC", compute::LoadRuntimeCompiler("NVRTC", "nvrtc", "CUBIN", names, {},
164
0/5
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 7 not taken.
✗ kMinimumDriverVersion));
165 ✗ return RequireLoaded(loaded);
166 }
167
168 ✗ const char* Api::ErrorName(CUresult result) const {
169 ✗ const char* name = nullptr;
170
0/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ if (cuGetErrorName == nullptr || cuGetErrorName(result, &name) != CUDA_SUCCESS ||
171
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ name == nullptr) {
172 ✗ return "unknown";
173 }
174 return name;
175 }
176
177 ✗ void Api::Warn(CUresult result, const char* what) const {
178
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (result != CUDA_SUCCESS) {
179 ✗ Log(LogLevel::Warning, "{} failed: {}", what, ErrorName(result));
180 }
181 ✗ }
182
183 ✗ void Api::Check(CUresult result, const char* what) const {
184
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (result == CUDA_SUCCESS) {
185 ✗ return;
186 }
187 ✗ const char* text = nullptr;
188
0/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ if (cuGetErrorString == nullptr || cuGetErrorString(result, &text) != CUDA_SUCCESS ||
189
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ text == nullptr) {
190 ✗ text = "";
191 }
192
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (LostDevice(result)) {
193 ✗ throw DeviceLostError("{} failed: the device was lost: {} ({}): {}", what,
194
0/2
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✗ ErrorName(result), result, text);
195 }
196
0/4
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✗ Branch 8 not taken.
✗ Branch 11 not taken.
✗ throw ApiError(what, ErrorName(result), result, text);
197 }
198
199 }
200