master ohmu/fastfetch-openbsd / src / detection / gpu / gpu_nvidia.c
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}