master ohmu/fastfetch-openbsd / src / detection / gpu / gpu.c
ohmu  ·  2026-10-15
  1#include "gpu.h"
  2#include "common/debug.h"
  3#include "detection/vulkan/vulkan.h"
  4#include "detection/opencl/opencl.h"
  5#include "detection/opengl/opengl.h"
  6#include "modules/opengl/opengl.h"
  7
  8const char* FF_GPU_VENDOR_NAME_APPLE = "Apple";
  9const char* FF_GPU_VENDOR_NAME_AMD = "AMD";
 10const char* FF_GPU_VENDOR_NAME_INTEL = "Intel";
 11const char* FF_GPU_VENDOR_NAME_NVIDIA = "NVIDIA";
 12const char* FF_GPU_VENDOR_NAME_MTHREADS = "Moore Threads";
 13const char* FF_GPU_VENDOR_NAME_QUALCOMM = "Qualcomm";
 14const char* FF_GPU_VENDOR_NAME_MTK = "MTK";
 15const char* FF_GPU_VENDOR_NAME_VMWARE = "VMware";
 16const char* FF_GPU_VENDOR_NAME_PARALLELS = "Parallels";
 17const char* FF_GPU_VENDOR_NAME_MICROSOFT = "Microsoft";
 18const char* FF_GPU_VENDOR_NAME_REDHAT = "RedHat";
 19const char* FF_GPU_VENDOR_NAME_ORACLE = "Oracle";
 20const char* FF_GPU_VENDOR_NAME_BROADCOM = "Broadcom";
 21const char* FF_GPU_VENDOR_NAME_LOONGSON = "Loongson";
 22const char* FF_GPU_VENDOR_NAME_JINGJIA_MICRO = "Jingjia Micro";
 23const char* FF_GPU_VENDOR_NAME_HUAWEI = "Huawei";
 24const char* FF_GPU_VENDOR_NAME_ZHAOXIN = "Zhaoxin";
 25const char* FF_GPU_VENDOR_NAME_QEMU = "QEMU";
 26
 27const char* ffGPUGetVendorString(unsigned vendorId) {
 28    // https://devicehunt.com/all-pci-vendors
 29    switch (vendorId) {
 30        case 0x106b:
 31            return FF_GPU_VENDOR_NAME_APPLE;
 32        case 0x1002:
 33        case 0x1022:
 34        case 0x1dd8:
 35            return FF_GPU_VENDOR_NAME_AMD;
 36        case 0x8086:
 37        case 0x8087:
 38        case 0x03e7:
 39            return FF_GPU_VENDOR_NAME_INTEL;
 40        case 0x0955:
 41        case 0x10de:
 42        case 0x12d2:
 43            return FF_GPU_VENDOR_NAME_NVIDIA;
 44        case 0x1ed5:
 45            return FF_GPU_VENDOR_NAME_MTHREADS;
 46        case 0x17cb:
 47        case 0x5143:
 48            return FF_GPU_VENDOR_NAME_QUALCOMM;
 49        case 0x14c3:
 50            return FF_GPU_VENDOR_NAME_MTK;
 51        case 0x15ad:
 52            return FF_GPU_VENDOR_NAME_VMWARE;
 53        case 0x1af4:
 54            return FF_GPU_VENDOR_NAME_REDHAT;
 55        case 0x1ab8:
 56        case 0x05404c42: // PD
 57            return FF_GPU_VENDOR_NAME_PARALLELS;
 58        case 0x1414:
 59            return FF_GPU_VENDOR_NAME_MICROSOFT;
 60        case 0x108e:
 61            return FF_GPU_VENDOR_NAME_ORACLE;
 62        case 0x182f:
 63        case 0x14e4:
 64            return FF_GPU_VENDOR_NAME_BROADCOM;
 65        case 0x0014:
 66            return FF_GPU_VENDOR_NAME_LOONGSON;
 67        case 0x0731:
 68            return FF_GPU_VENDOR_NAME_JINGJIA_MICRO;
 69        case 0x19e5:
 70            return FF_GPU_VENDOR_NAME_HUAWEI;
 71        case 0x1d17:
 72            return FF_GPU_VENDOR_NAME_ZHAOXIN;
 73        case 0x1234: // https://admin.pci-ids.ucw.cz/read/PC/1234
 74            return FF_GPU_VENDOR_NAME_QEMU;
 75        default:
 76            return nullptr;
 77    }
 78}
 79
 80static bool normalizeVendorName(FFGPUResult* gpu) {
 81    if (ffStrbufContainS(&gpu->name, "Apple")) {
 82        ffStrbufSetStatic(&gpu->vendor, FF_GPU_VENDOR_NAME_APPLE);
 83        gpu->type = FF_GPU_TYPE_INTEGRATED;
 84    } else if (ffStrbufContainS(&gpu->name, "Intel")) {
 85        ffStrbufSetStatic(&gpu->vendor, FF_GPU_VENDOR_NAME_INTEL);
 86    } else if (ffStrbufContainS(&gpu->name, "AMD") || ffStrbufContainS(&gpu->name, "ATI")) {
 87        ffStrbufSetStatic(&gpu->vendor, FF_GPU_VENDOR_NAME_AMD);
 88    } else if (ffStrbufContainS(&gpu->name, "NVIDIA")) {
 89        ffStrbufSetStatic(&gpu->vendor, FF_GPU_VENDOR_NAME_NVIDIA);
 90    } else if (ffStrbufContainS(&gpu->name, "MTT")) {
 91        ffStrbufSetStatic(&gpu->vendor, FF_GPU_VENDOR_NAME_MTHREADS);
 92    } else {
 93        return false;
 94    }
 95
 96    return true;
 97}
 98
 99const char* detectByOpenGL(FFlist* gpus) {
100    FF_DEBUG("Starting OpenGL GPU detection fallback");
101
102    FFOpenGLResult result;
103    ffStrbufInit(&result.version);
104    ffStrbufInit(&result.renderer);
105    ffStrbufInit(&result.vendor);
106    ffStrbufInit(&result.slv);
107    ffStrbufInit(&result.library);
108
109    [[gnu::cleanup(ffDestroyOpenGLOptions)]] FFOpenGLOptions options;
110    ffInitOpenGLOptions(&options);
111    const char* error = ffDetectOpenGL(&options, &result);
112    FF_DEBUG("OpenGL detection returns: %s", error ?: "success");
113
114    if (!error) {
115        FFGPUResult* gpu = FF_LIST_ADD(FFGPUResult, *gpus);
116        gpu->type = FF_GPU_TYPE_UNKNOWN;
117        ffStrbufInitMove(&gpu->vendor, &result.vendor);
118        ffStrbufInitMove(&gpu->name, &result.renderer);
119        ffStrbufInit(&gpu->driver);
120        ffStrbufInitF(&gpu->platformApi, "OpenGL %s", result.version.chars);
121        ffStrbufInit(&gpu->memoryType);
122        gpu->index = FF_GPU_INDEX_UNSET;
123        gpu->temperature = FF_GPU_TEMP_UNSET;
124        gpu->coreCount = FF_GPU_CORE_COUNT_UNSET;
125        gpu->frequency = FF_GPU_FREQUENCY_UNSET;
126        gpu->coreUsage = FF_GPU_CORE_USAGE_UNSET;
127        gpu->dedicated.total = gpu->dedicated.used = gpu->shared.total = gpu->shared.used = FF_GPU_VMEM_SIZE_UNSET;
128        gpu->deviceId = 0;
129        gpu->pcieSpeed = FF_GPU_PCIE_SPEED_UNSET;
130
131        FF_DEBUG("OpenGL reported renderer='%s', vendor='%s', version='%s'",
132            gpu->name.chars,
133            gpu->vendor.chars,
134            result.version.chars);
135
136        normalizeVendorName(gpu);
137
138        FF_DEBUG("OpenGL fallback produced GPU: name='%s', vendor='%s', type=%u",
139            gpu->name.chars,
140            gpu->vendor.chars,
141            gpu->type);
142    }
143
144    ffStrbufDestroy(&result.version);
145    ffStrbufDestroy(&result.renderer);
146    ffStrbufDestroy(&result.vendor);
147    ffStrbufDestroy(&result.slv);
148    ffStrbufDestroy(&result.library);
149    return error;
150}
151
152#if defined(FF_HAVE_EGL) || __has_include(<EGL/egl.h>)
153    #include <EGL/egl.h>
154    #include <EGL/eglext.h>
155
156    #if EGL_EXT_device_base && EGL_EXT_device_persistent_id && EGL_EXT_device_query_name
157
158        #define FF_HAVE_EGL_EXT 1
159        #include <inttypes.h>
160
161        #include "common/library.h"
162        #include "common/strutil.h"
163        #include "common/io.h"
164
165const char* detectByEglext(FFlist* result) {
166    FF_LIBRARY_LOAD_MESSAGE(egl, "libEGL" FF_LIBRARY_EXTENSION, 1);
167    FF_LIBRARY_LOAD_SYMBOL_MESSAGE(egl, eglGetProcAddress);
168
169    PFNEGLQUERYDEVICESEXTPROC ffeglQueryDevicesEXT = (PFNEGLQUERYDEVICESEXTPROC) ffeglGetProcAddress("eglQueryDevicesEXT");
170    if (!ffeglQueryDevicesEXT) {
171        return "eglQueryDevicesEXT is unavailable";
172    }
173    PFNEGLQUERYDEVICESTRINGEXTPROC ffeglQueryDeviceStringEXT = (PFNEGLQUERYDEVICESTRINGEXTPROC) ffeglGetProcAddress("eglQueryDeviceStringEXT");
174    if (!ffeglQueryDeviceStringEXT) {
175        return "eglQueryDeviceStringEXT is unavailable";
176    }
177    PFNEGLQUERYDEVICEBINARYEXTPROC ffeglQueryDeviceBinaryEXT = (PFNEGLQUERYDEVICEBINARYEXTPROC) ffeglGetProcAddress("eglQueryDeviceBinaryEXT");
178    if (!ffeglQueryDeviceBinaryEXT) {
179        return "eglQueryDeviceBinaryEXT is unavailable";
180    }
181
182    FF_DEBUG("Loaded EGL library and required symbols");
183
184    FF_SUPPRESS_IO();
185
186    EGLDeviceEXT devices[16];
187    EGLint numDevices;
188    if (ffeglQueryDevicesEXT(ARRAY_SIZE(devices), devices, &numDevices) == EGL_TRUE) {
189        FF_DEBUG("eglQueryDevicesEXT() returned %d device(s)", numDevices);
190        for (EGLint i = 0; i < numDevices; i++) {
191            const EGLDeviceEXT device = devices[i];
192            if (device == (EGLDeviceEXT) EGL_NO_DEVICE_EXT || device == (EGLDeviceEXT) EGL_BAD_DEVICE_EXT) {
193                FF_DEBUG("eglQueryDevicesEXT() returned invalid device at index %d", i);
194                continue;
195            }
196            const char* exts = (const char*) ffeglQueryDeviceStringEXT(device, EGL_EXTENSIONS);
197            FF_DEBUG("eglQueryDeviceStringEXT() returned extensions for device %d: %s", i, exts);
198            if (exts && ffStrContains(exts, "EGL_MESA_device_software")) {
199                FF_DEBUG("eglQueryDeviceStringEXT() returned software device at index %d, skipping", i);
200                continue;
201            }
202
203            const char* name = ffeglQueryDeviceStringEXT(device, EGL_RENDERER_EXT);
204            if (!name) {
205                FF_DEBUG("eglQueryDeviceStringEXT() returned nullptr name for device %d, skipping", i);
206                continue;
207            }
208
209            FFGPUResult* gpu = FF_LIST_ADD(FFGPUResult, *result);
210            ffStrbufInitS(&gpu->name, name);
211            ffStrbufInit(&gpu->vendor);
212            ffStrbufInitS(&gpu->driver, ffeglQueryDeviceStringEXT(device, EGL_DRIVER_NAME_EXT));
213            ffStrbufInitF(&gpu->platformApi, "egl-ext");
214            ffStrbufInit(&gpu->memoryType);
215            gpu->index = FF_GPU_INDEX_UNSET;
216            gpu->temperature = FF_GPU_TEMP_UNSET;
217            gpu->coreCount = FF_GPU_CORE_COUNT_UNSET;
218            gpu->coreUsage = FF_GPU_CORE_USAGE_UNSET;
219            gpu->type = FF_GPU_TYPE_UNKNOWN;
220            gpu->dedicated.total = gpu->dedicated.used = gpu->shared.total = gpu->shared.used = FF_GPU_VMEM_SIZE_UNSET;
221            gpu->deviceId = 0;
222            gpu->frequency = FF_GPU_FREQUENCY_UNSET;
223            gpu->pcieSpeed = FF_GPU_PCIE_SPEED_UNSET;
224
225            ffeglQueryDeviceBinaryEXT(device, EGL_DEVICE_UUID_EXT, sizeof(gpu->deviceId), &gpu->deviceId, nullptr);
226
227            if (!normalizeVendorName(gpu)) {
228                ffStrbufSetS(&gpu->vendor, ffeglQueryDeviceStringEXT(device, EGL_VENDOR));
229            }
230
231            FF_DEBUG("Detected GPU %d: vendor='%s', name='%s', driver='%s', id='%16" PRIX64 "'", i, gpu->vendor.chars, gpu->name.chars, gpu->driver.chars, gpu->deviceId);
232        }
233    } else {
234        FF_DEBUG("eglQueryDevicesEXT() returned EGL_FALSE");
235    }
236
237    return nullptr;
238}
239    #endif
240
241#endif
242
243
244const char* ffDetectGPU(const FFGPUOptions* options, FFlist* result) {
245    FF_DEBUG("Starting GPU detection with method=%d", (int) options->detectionMethod);
246
247    if (options->detectionMethod <= FF_GPU_DETECTION_METHOD_PCI) {
248        FF_DEBUG("Trying PCI/native GPU detection");
249        const char* error = ffDetectGPUImpl(options, result);
250        if (!error && result->length > 0) {
251            FF_DEBUG("PCI/native GPU detection succeeded with %u GPU(s)", result->length);
252            return nullptr;
253        }
254
255        FF_DEBUG("PCI/native GPU detection did not produce results (error=%s, gpuCount=%u)",
256            error ?: "none",
257            result->length);
258    }
259    if (options->detectionMethod <= FF_GPU_DETECTION_METHOD_VULKAN) {
260        FF_DEBUG("Trying Vulkan GPU detection fallback");
261        FFVulkanResult* vulkan = ffDetectVulkan();
262        if (!vulkan->error && vulkan->gpus.length > 0) {
263            FF_DEBUG("Vulkan detection succeeded with %u GPU(s)", vulkan->gpus.length);
264            ffListDestroy(result);
265            ffListInitMove(result, &vulkan->gpus);
266
267#ifdef __ANDROID__
268            double ffGPUDetectTempFromTZ(void);
269            if (options->temp && result->length == 1) {
270                FF_DEBUG("Applying Android thermal-zone temperature to single Vulkan GPU");
271                FF_LIST_GET(FFGPUResult, *result, 0)->temperature = ffGPUDetectTempFromTZ();
272            }
273#endif
274
275            return nullptr;
276        }
277
278        FF_DEBUG("Vulkan detection did not produce results (error=%s, gpuCount=%u)",
279            vulkan->error ?: "none",
280            vulkan->gpus.length);
281    }
282    if (options->detectionMethod <= FF_GPU_DETECTION_METHOD_OPENCL) {
283        FF_DEBUG("Trying OpenCL GPU detection fallback");
284        FFOpenCLResult* opencl = ffDetectOpenCL();
285        if (!opencl->error && opencl->gpus.length > 0) {
286            FF_DEBUG("OpenCL detection succeeded with %u GPU(s)", opencl->gpus.length);
287            ffListDestroy(result);
288            ffListInitMove(result, &opencl->gpus);
289            return nullptr;
290        }
291
292        FF_DEBUG("OpenCL detection did not produce results (error=%s, gpuCount=%u)",
293            opencl->error ?: "none",
294            opencl->gpus.length);
295    }
296    if (options->detectionMethod <= FF_GPU_DETECTION_METHOD_EGL_EXT) {
297        #if FF_HAVE_EGL_EXT
298        FF_DEBUG("Trying EGL_EXT GPU detection fallback");
299        const char* error = detectByEglext(result);
300        if (!error && result->length > 0) {
301            FF_DEBUG("EGL_EXT GPU detection succeeded with %u GPU(s)", result->length);
302            return nullptr;
303        }
304
305        FF_DEBUG("EGL_EXT GPU detection did not produce results (error=%s, gpuCount=%u)",
306            error ?: "none",
307            result->length);
308        #else
309        FF_DEBUG("EGL_EXT GPU detection is unavailable");
310        #endif
311    }
312    if (options->detectionMethod <= FF_GPU_DETECTION_METHOD_OPENGL) {
313        FF_DEBUG("Trying OpenGL GPU detection fallback");
314        const char* error = detectByOpenGL(result);
315        if (error == nullptr) {
316            FF_DEBUG("OpenGL fallback succeeded with %u GPU(s)", result->length);
317            return nullptr;
318        }
319
320        FF_DEBUG("OpenGL fallback failed: %s", error);
321    }
322
323    FF_DEBUG("GPU detection failed in all enabled backends");
324    return "GPU detection failed";
325}
326
327bool ffGPUDetectTypeByVendorAndName(FFGPUResult* gpu) {
328    if (gpu->type != FF_GPU_TYPE_UNKNOWN) {
329        return true;
330    }
331
332    FF_DEBUG("Using fallback GPU type detection");
333    if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_NVIDIA) {
334        if (ffStrbufStartsWithIgnCaseS(&gpu->name, "GeForce") ||
335            ffStrbufStartsWithIgnCaseS(&gpu->name, "Quadro") ||
336            ffStrbufStartsWithIgnCaseS(&gpu->name, "Tesla")) {
337            gpu->type = FF_GPU_TYPE_DISCRETE;
338        }
339    } else if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_MTHREADS) {
340        if (ffStrbufStartsWithIgnCaseS(&gpu->name, "MTT ")) { gpu->type = FF_GPU_TYPE_DISCRETE; }
341    } else if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_INTEL) {
342        // 0000:00:02.0 is reserved for Intel integrated graphics
343        gpu->type = gpu->deviceId == ffGPUPciAddr2Id(0, 0, 2, 0) ? FF_GPU_TYPE_INTEGRATED : FF_GPU_TYPE_DISCRETE;
344    } else if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_VMWARE || gpu->vendor.chars == FF_GPU_VENDOR_NAME_PARALLELS) {
345        // Virtualized GPUs
346        gpu->type = FF_GPU_TYPE_INTEGRATED;
347    }
348
349    if (gpu->type != FF_GPU_TYPE_UNKNOWN) {
350        FF_DEBUG("Determined GPU type based on vendor (%s) and name: %u", gpu->vendor.chars, gpu->type);
351        return true;
352    }
353
354    return false;
355}