ohmu
·
2026-10-15
1#include "gpu_driver_specific.h"
2
3#include "common/library.h"
4#include "nvml.h"
5
6struct FFNvmlData {
7 FF_LIBRARY_SYMBOL(nvmlDeviceGetCount_v2)
8 FF_LIBRARY_SYMBOL(nvmlDeviceGetHandleByIndex_v2)
9 FF_LIBRARY_SYMBOL(nvmlDeviceGetHandleByPciBusId_v2)
10 FF_LIBRARY_SYMBOL(nvmlDeviceGetPciInfo_v3)
11 FF_LIBRARY_SYMBOL(nvmlDeviceGetTemperatureV)
12 FF_LIBRARY_SYMBOL(nvmlDeviceGetMemoryInfo_v2)
13 FF_LIBRARY_SYMBOL(nvmlDeviceGetMemoryInfo)
14 FF_LIBRARY_SYMBOL(nvmlDeviceGetNumGpuCores)
15 FF_LIBRARY_SYMBOL(nvmlDeviceGetMaxClockInfo)
16 FF_LIBRARY_SYMBOL(nvmlDeviceGetUtilizationRates)
17 FF_LIBRARY_SYMBOL(nvmlDeviceGetMaxPcieLinkGeneration)
18 FF_LIBRARY_SYMBOL(nvmlDeviceGetMaxPcieLinkWidth)
19 FF_LIBRARY_SYMBOL(nvmlDeviceGetCurrPcieLinkGeneration)
20 FF_LIBRARY_SYMBOL(nvmlDeviceGetCurrPcieLinkWidth)
21 FF_LIBRARY_SYMBOL(nvmlDeviceGetBrand)
22 FF_LIBRARY_SYMBOL(nvmlDeviceGetIndex)
23 FF_LIBRARY_SYMBOL(nvmlDeviceGetName)
24
25 bool inited;
26} nvmlData;
27
28#if defined(_WIN32) && !defined(FF_DISABLE_DLOPEN)
29
30 #include "nvapi.h"
31
32struct FFNvapiData {
33 FF_LIBRARY_SYMBOL(nvapi_Unload)
34 FF_LIBRARY_SYMBOL(nvapi_EnumPhysicalGPUs)
35 FF_LIBRARY_SYMBOL(nvapi_GPU_GetRamType)
36 FF_LIBRARY_SYMBOL(nvapi_GPU_GetGPUType)
37
38 bool inited;
39} nvapiData;
40
41static const char* detectMoreByNvapi(FFGpuDriverResult* result) {
42 if (!nvapiData.inited) {
43 nvapiData.inited = true;
44
45 FF_LIBRARY_LOAD_MESSAGE(libnvapi,
46 #ifdef _WIN64
47 "nvapi64.dll"
48 #else
49 "nvapi.dll"
50 #endif
51 ,
52 1);
53 FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libnvapi, nvapi_QueryInterface)
54 #define FF_NVAPI_INTERFACE(iName, iOffset) \
55 typeof(&iName) ff##iName = ffnvapi_QueryInterface(iOffset); \
56 if (ff##iName == nullptr) return "nvapi_QueryInterface " #iName " failed";
57
58 FF_NVAPI_INTERFACE(nvapi_Initialize, NVAPI_INTERFACE_OFFSET_INITIALIZE)
59 FF_NVAPI_INTERFACE(nvapi_Unload, NVAPI_INTERFACE_OFFSET_UNLOAD)
60 FF_NVAPI_INTERFACE(nvapi_EnumPhysicalGPUs, NVAPI_INTERFACE_OFFSET_ENUM_PHYSICAL_GPUS)
61 FF_NVAPI_INTERFACE(nvapi_GPU_GetRamType, NVAPI_INTERFACE_OFFSET_GPU_GET_RAM_TYPE)
62 FF_NVAPI_INTERFACE(nvapi_GPU_GetGPUType, NVAPI_INTERFACE_OFFSET_GPU_GET_GPU_TYPE)
63 #undef FF_NVAPI_INTERFACE
64
65 if (ffnvapi_Initialize() < 0) {
66 return "NvAPI_Initialize() failed";
67 }
68
69 nvapiData.ffnvapi_EnumPhysicalGPUs = ffnvapi_EnumPhysicalGPUs;
70 nvapiData.ffnvapi_GPU_GetRamType = ffnvapi_GPU_GetRamType;
71 nvapiData.ffnvapi_GPU_GetGPUType = ffnvapi_GPU_GetGPUType;
72 nvapiData.ffnvapi_Unload = ffnvapi_Unload;
73
74 atexit((void*) ffnvapi_Unload);
75 libnvapi = nullptr; // don't close nvapi
76 }
77
78 if (nvapiData.ffnvapi_EnumPhysicalGPUs == nullptr) {
79 return "loading nvapi library failed";
80 }
81
82 NvPhysicalGpuHandle handles[32];
83 int gpuCount = 0;
84
85 if (nvapiData.ffnvapi_EnumPhysicalGPUs(handles, &gpuCount) < 0) {
86 return "NvAPI_EnumPhysicalGPUs() failed";
87 }
88
89 uint32_t gpuIndex = *result->index;
90
91 if (gpuIndex >= (uint32_t) gpuCount) {
92 return "GPU index out of range";
93 }
94
95 // Not very sure. Need to check in multi-GPU system
96 NvPhysicalGpuHandle gpuHandle = handles[gpuIndex];
97
98 NvApiGPUMemoryType memType;
99 if (result->memoryType && nvapiData.ffnvapi_GPU_GetRamType(gpuHandle, &memType) == 0) {
100 switch (memType) {
101 #define FF_NVAPI_MEMORY_TYPE(type) \
102 case NVAPI_GPU_MEMORY_TYPE_##type: \
103 ffStrbufSetStatic(result->memoryType, #type); \
104 break;
105 FF_NVAPI_MEMORY_TYPE(UNKNOWN)
106 FF_NVAPI_MEMORY_TYPE(SDRAM)
107 FF_NVAPI_MEMORY_TYPE(DDR1)
108 FF_NVAPI_MEMORY_TYPE(DDR2)
109 FF_NVAPI_MEMORY_TYPE(GDDR2)
110 FF_NVAPI_MEMORY_TYPE(GDDR3)
111 FF_NVAPI_MEMORY_TYPE(GDDR4)
112 FF_NVAPI_MEMORY_TYPE(DDR3)
113 FF_NVAPI_MEMORY_TYPE(GDDR5)
114 FF_NVAPI_MEMORY_TYPE(LPDDR2)
115 FF_NVAPI_MEMORY_TYPE(GDDR5X)
116 FF_NVAPI_MEMORY_TYPE(LPDDR3)
117 FF_NVAPI_MEMORY_TYPE(LPDDR4)
118 FF_NVAPI_MEMORY_TYPE(LPDDR5)
119 FF_NVAPI_MEMORY_TYPE(GDDR6)
120 FF_NVAPI_MEMORY_TYPE(GDDR6X)
121 FF_NVAPI_MEMORY_TYPE(GDDR7)
122 #undef FF_NVAPI_MEMORY_TYPE
123 default:
124 ffStrbufSetF(result->memoryType, "Unknown (%d)", memType);
125 break;
126 }
127 }
128
129 NvApiGPUType gpuType;
130 if (result->type && nvapiData.ffnvapi_GPU_GetGPUType(gpuHandle, &gpuType) == 0) {
131 switch (gpuType) {
132 case NV_SYSTEM_TYPE_IGPU:
133 *result->type = FF_GPU_TYPE_INTEGRATED;
134 break;
135 case NV_SYSTEM_TYPE_DGPU:
136 *result->type = FF_GPU_TYPE_DISCRETE;
137 break;
138 default:
139 *result->type = FF_GPU_TYPE_UNKNOWN;
140 break;
141 }
142 }
143
144 return nullptr;
145}
146
147#endif
148
149const char* ffDetectNvidiaGpuInfo(const FFGpuDriverCondition* cond, FFGpuDriverResult result, const char* soName) {
150#ifndef FF_DISABLE_DLOPEN
151
152 if (!nvmlData.inited) {
153 nvmlData.inited = true;
154 FF_LIBRARY_LOAD(libnvml, "dlopen nvml failed", soName, 1);
155 FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libnvml, nvmlInit_v2)
156 FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libnvml, nvmlShutdown)
157 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libnvml, nvmlData, nvmlDeviceGetCount_v2)
158 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libnvml, nvmlData, nvmlDeviceGetHandleByIndex_v2)
159 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libnvml, nvmlData, nvmlDeviceGetHandleByPciBusId_v2)
160 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libnvml, nvmlData, nvmlDeviceGetPciInfo_v3)
161 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libnvml, nvmlData, nvmlDeviceGetTemperatureV)
162 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libnvml, nvmlData, nvmlDeviceGetMemoryInfo_v2)
163 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libnvml, nvmlData, nvmlDeviceGetMemoryInfo)
164 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libnvml, nvmlData, nvmlDeviceGetNumGpuCores)
165 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libnvml, nvmlData, nvmlDeviceGetMaxClockInfo)
166 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libnvml, nvmlData, nvmlDeviceGetMaxPcieLinkGeneration)
167 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libnvml, nvmlData, nvmlDeviceGetMaxPcieLinkWidth)
168 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libnvml, nvmlData, nvmlDeviceGetCurrPcieLinkGeneration)
169 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libnvml, nvmlData, nvmlDeviceGetCurrPcieLinkWidth)
170 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libnvml, nvmlData, nvmlDeviceGetUtilizationRates)
171 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libnvml, nvmlData, nvmlDeviceGetBrand)
172 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libnvml, nvmlData, nvmlDeviceGetIndex)
173 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libnvml, nvmlData, nvmlDeviceGetName)
174
175 if (ffnvmlInit_v2() != NVML_SUCCESS) {
176 nvmlData.ffnvmlDeviceGetNumGpuCores = nullptr;
177 return "nvmlInit_v2() failed";
178 }
179 atexit((void*) ffnvmlShutdown);
180 libnvml = nullptr; // don't close nvml
181 }
182
183 if (nvmlData.ffnvmlDeviceGetNumGpuCores == nullptr) {
184 return "loading nvml library failed";
185 }
186
187 nvmlDevice_t device = nullptr;
188 if (cond->type & FF_GPU_DRIVER_CONDITION_TYPE_BUS_ID) {
189 char pciBusIdStr[32];
190 snprintf(pciBusIdStr, ARRAY_SIZE(pciBusIdStr), "%04x:%02x:%02x.%d", cond->pciBusId.domain, cond->pciBusId.bus, cond->pciBusId.device, cond->pciBusId.func);
191
192 nvmlReturn_t ret = nvmlData.ffnvmlDeviceGetHandleByPciBusId_v2(pciBusIdStr, &device);
193 if (ret != NVML_SUCCESS) {
194 return "nvmlDeviceGetHandleByPciBusId_v2() failed";
195 }
196 } else if (cond->type & FF_GPU_DRIVER_CONDITION_TYPE_DEVICE_ID) {
197 uint32_t count;
198 if (nvmlData.ffnvmlDeviceGetCount_v2(&count) != NVML_SUCCESS) {
199 return "nvmlDeviceGetCount_v2() failed";
200 }
201
202 for (uint32_t i = 0; i < count; i++, device = nullptr) {
203 if (nvmlData.ffnvmlDeviceGetHandleByIndex_v2(i, &device) != NVML_SUCCESS) {
204 continue;
205 }
206
207 nvmlPciInfo_t pciInfo;
208 if (nvmlData.ffnvmlDeviceGetPciInfo_v3(device, &pciInfo) != NVML_SUCCESS) {
209 continue;
210 }
211
212 if (pciInfo.pciDeviceId != ((cond->pciDeviceId.deviceId << 16u) | cond->pciDeviceId.vendorId) ||
213 pciInfo.pciSubSystemId != cond->pciDeviceId.subSystemId) {
214 continue;
215 }
216
217 break;
218 }
219 }
220
221 if (!device) {
222 return "Device not found";
223 }
224
225 if (result.type) {
226 nvmlBrandType_t brand;
227 if (nvmlData.ffnvmlDeviceGetBrand(device, &brand) == NVML_SUCCESS) {
228 switch (brand) {
229 case NVML_BRAND_NVIDIA_RTX:
230 case NVML_BRAND_QUADRO_RTX:
231 case NVML_BRAND_GEFORCE:
232 case NVML_BRAND_TITAN:
233 case NVML_BRAND_TESLA:
234 case NVML_BRAND_QUADRO:
235 *result.type = FF_GPU_TYPE_DISCRETE;
236 break;
237 default:
238 break;
239 }
240 }
241 }
242
243 if (result.index) {
244 unsigned int value;
245 if (nvmlData.ffnvmlDeviceGetIndex(device, &value) == NVML_SUCCESS) {
246 *result.index = value;
247 #ifdef _WIN32
248 // Don't bother loading nvapi for GPU type detection only
249 if (result.memoryType) {
250 detectMoreByNvapi(&result);
251 }
252 #endif
253 }
254 }
255
256 if (result.temp) {
257 nvmlTemperature_v1_t temp = {
258 .version = nvmlTemperature_v1,
259 .sensorType = NVML_TEMPERATURE_GPU,
260 };
261 if (nvmlData.ffnvmlDeviceGetTemperatureV(device, &temp) == NVML_SUCCESS) {
262 *result.temp = temp.temperature;
263 }
264 }
265
266 if (result.memory) {
267 nvmlMemory_v2_t memory = { .version = nvmlMemory_v2 };
268 if (nvmlData.ffnvmlDeviceGetMemoryInfo_v2(device, &memory) == NVML_SUCCESS) {
269 result.memory->total = memory.used + memory.free;
270 result.memory->used = memory.used;
271 } else {
272 nvmlMemory_t memory_v1;
273 if (nvmlData.ffnvmlDeviceGetMemoryInfo(device, &memory_v1) == NVML_SUCCESS) {
274 result.memory->total = memory_v1.total;
275 result.memory->used = memory_v1.used;
276 }
277 }
278 }
279
280 if (result.coreCount) {
281 nvmlData.ffnvmlDeviceGetNumGpuCores(device, result.coreCount);
282 }
283
284 if (result.frequency) {
285 nvmlData.ffnvmlDeviceGetMaxClockInfo(device, NVML_CLOCK_GRAPHICS, result.frequency);
286 }
287
288 if (result.coreUsage) {
289 nvmlUtilization_t utilization;
290 if (nvmlData.ffnvmlDeviceGetUtilizationRates(device, &utilization) == NVML_SUCCESS) {
291 *result.coreUsage = utilization.gpu;
292 }
293 }
294
295 if (result.name) {
296 char name[NVML_DEVICE_NAME_V2_BUFFER_SIZE];
297 if (nvmlData.ffnvmlDeviceGetName(device, name, ARRAY_SIZE(name)) == NVML_SUCCESS) {
298 ffStrbufSetS(result.name, name);
299 }
300 }
301
302 if (result.psMax) {
303 unsigned int value;
304 if (nvmlData.ffnvmlDeviceGetMaxPcieLinkGeneration(device, &value) == NVML_SUCCESS) {
305 result.psMax->gen = (uint16_t) value;
306 }
307 if (nvmlData.ffnvmlDeviceGetMaxPcieLinkWidth(device, &value) == NVML_SUCCESS) {
308 result.psMax->lanes = (uint16_t) value;
309 }
310 }
311
312 if (result.psCurr) {
313 unsigned int value;
314 if (nvmlData.ffnvmlDeviceGetCurrPcieLinkGeneration(device, &value) == NVML_SUCCESS) {
315 result.psCurr->gen = (uint16_t) value;
316 }
317 if (nvmlData.ffnvmlDeviceGetCurrPcieLinkWidth(device, &value) == NVML_SUCCESS) {
318 result.psCurr->lanes = (uint16_t) value;
319 }
320 }
321
322 return nullptr;
323
324#else
325
326 FF_UNUSED(cond, result, soName);
327 return "dlopen is disabled";
328
329#endif
330}