master ohmu/fastfetch-openbsd / src / detection / gpu / gpu_windows.c
ohmu  ·  2026-10-15
  1#include "detection/gpu/gpu.h"
  2#if __linux__
  3    #define FF_GPU_DRIVER_DLLNAME_PATH_PREFIX "/usr/lib/wsl/lib/"
  4#endif
  5#include "detection/gpu/gpu_driver_specific.h"
  6#include "common/debug.h"
  7
  8#include <inttypes.h>
  9#include "d3dkmthk.h"
 10
 11#if _WIN32
 12    #include "common/windows/unicode.h"
 13    #include "common/windows/registry.h"
 14    #include "common/mallocHelper.h"
 15
 16    #define INITGUID
 17    #include <windows.h>
 18    #include <cfgmgr32.h>
 19    #include <devguid.h>
 20    #include <devpkey.h>
 21    #include <pciprop.h>
 22
 23    #define GUID_DEVCLASS_DISPLAY_STRING L"{4d36e968-e325-11ce-bfc1-08002be10318}" // Found in <devguid.h>
 24
 25// https://wine-devel.winehq.narkive.com/vKO2Bkgj/patch-1-2-dxgi-tests-add-test-for-enumerating-display-adapters-using-setupapi
 26DEFINE_DEVPROPKEY(DEVPROPKEY_DISPLAY_ADAPTER_LUID, 0x60b193cb, 0x5276, 0x4d0f, 0x96, 0xfc, 0xf1, 0x73, 0xab, 0xad, 0x3e, 0xc6, 2);
 27
 28static bool queryDeviceInfoCM(FFGPUResult* gpu, uint64_t luid, D3DKMT_DEVICE_IDS* outDeviceIds, bool queryPcieGen) {
 29    FF_DEBUG("Query PCI device info: %08llX with LUID: %08llX", gpu->deviceId, luid);
 30
 31    static FFlist deviceIdsCache;
 32    static bool initialized;
 33    typedef struct {
 34        uint32_t currentLinkSpeed;
 35        uint32_t currentLinkWidth;
 36        uint32_t maxLinkSpeed;
 37        uint32_t maxLinkWidth;
 38        D3DKMT_DEVICE_IDS deviceIds;
 39        uint64_t pciAddr;
 40        uint64_t luid;
 41        FFstrbuf vendor;
 42        FFstrbuf name;
 43    } CacheEntry;
 44
 45    if (!initialized) {
 46        initialized = true;
 47        ffListInit(&deviceIdsCache);
 48
 49        ULONG devIdListSize = 0;
 50        if (CM_Get_Device_ID_List_SizeW(&devIdListSize, GUID_DEVCLASS_DISPLAY_STRING, CM_GETIDLIST_FILTER_CLASS | CM_GETIDLIST_FILTER_PRESENT) != CR_SUCCESS || devIdListSize <= 1) {
 51            FF_DEBUG("No display devices found, list size: %lu", devIdListSize);
 52            return false;
 53        }
 54
 55        FF_DEBUG("Found device ID list size: %lu", devIdListSize);
 56
 57        FF_AUTO_FREE DEVINSTID_W devIdList = malloc(devIdListSize * sizeof(*devIdList));
 58
 59        if (CM_Get_Device_ID_ListW(GUID_DEVCLASS_DISPLAY_STRING, devIdList, devIdListSize, CM_GETIDLIST_FILTER_CLASS | CM_GETIDLIST_FILTER_PRESENT) != CR_SUCCESS) {
 60            FF_DEBUG("CM_Get_Device_ID_ListW failed");
 61            return false;
 62        }
 63
 64        for (wchar_t* devId = devIdList; *devId; devId += wcslen(devId) + 1) {
 65            FF_DEBUG("Processing device ID: %ls", devId);
 66
 67            DEVINST devInst = 0;
 68
 69            if (CM_Locate_DevNodeW(&devInst, devId, CM_LOCATE_DEVNODE_NORMAL) != CR_SUCCESS) {
 70                FF_DEBUG("Failed to get device instance ID or locate device node");
 71                continue;
 72            }
 73            FF_DEBUG("Device instance ID: %lu", devInst);
 74
 75            for (wchar_t* p = devId; *p; p++) {
 76                if (*p >= L'a' && *p <= L'z') {
 77                    *p -= L'a' - L'A';
 78                }
 79            }
 80
 81            CacheEntry* entry = FF_LIST_ADD(CacheEntry, deviceIdsCache);
 82            *entry = (CacheEntry){};
 83
 84            ULONG bufLen = sizeof(entry->luid);
 85            DEVPROPTYPE type;
 86            CONFIGRET ret = CM_Get_DevNode_PropertyW(devInst, &DEVPROPKEY_DISPLAY_ADAPTER_LUID, &type, (PBYTE) &entry->luid, &bufLen, 0);
 87            if (ret != CR_SUCCESS) { // Not available on Windows 8.1
 88                FF_DEBUG("Failed to get device LUID: %s", ffDebugConfigRet(ret));
 89
 90                uint32_t pciBus = 0;
 91                ULONG pciBufLen = sizeof(pciBus);
 92                if (CM_Get_DevNode_Registry_PropertyW(devInst, CM_DRP_BUSNUMBER, nullptr, &pciBus, &pciBufLen, 0) == CR_SUCCESS) {
 93                    uint32_t pciAddr = 0;
 94                    pciBufLen = sizeof(pciAddr);
 95                    if (CM_Get_DevNode_Registry_PropertyW(devInst, CM_DRP_ADDRESS, nullptr, &pciAddr, &pciBufLen, 0) == CR_SUCCESS) {
 96                        entry->pciAddr = ffGPUPciAddr2Id(0, pciBus, (pciAddr >> 16) & 0xFFFF, pciAddr & 0xFFFF);
 97                        FF_DEBUG("Cached device IDs for PCI bus %u: vendor=0x%04x device=0x%04x", pciBus, entry->deviceIds.VendorID, entry->deviceIds.DeviceID);
 98                    } else {
 99                        FF_DEBUG("Failed to get PCI address");
100                    }
101                } else {
102                    FF_DEBUG("Failed to get PCI bus number");
103                }
104
105                if (entry->pciAddr == 0) {
106                    FF_DEBUG("Skipping device ID: %ls due to missing LUID and PCI address", devId);
107                    deviceIdsCache.length--;
108                    continue;
109                }
110            }
111
112            // L"PCI\\VEN_10DE&DEV_2782&SUBSYS_513417AA&REV_A1\\4&3674a6b9&0&0008"
113            if (swscanf(devId + 4, L"VEN_%x&DEV_%x&SUBSYS_%4x%4x&REV_%x", &entry->deviceIds.VendorID, &entry->deviceIds.DeviceID, &entry->deviceIds.SubSystemID, &entry->deviceIds.SubVendorID, &entry->deviceIds.RevisionID) >= 2) {
114                FF_DEBUG("Parsed PCI IDs - Vendor: 0x%04x, Device: 0x%04x, SubVendor: 0x%04x, SubSystem: 0x%04x, Rev: 0x%04x", entry->deviceIds.VendorID, entry->deviceIds.DeviceID, entry->deviceIds.SubVendorID, entry->deviceIds.SubSystemID, entry->deviceIds.RevisionID);
115                // I thought it was DXGKMDT_OPM_BUS_TYPE_PCI, but it turns out to be false
116                // Who TF knows what 1 actually means. It's just reported by most graphic cards
117                // And yeah, DXGKMDT_OPM_BUS_TYPE_PCIEXPRESS (3) exists
118                entry->deviceIds.BusType = 1;
119            } else {
120                FF_DEBUG("Failed to parse PCI IDs from device ID: %ls", devId);
121                entry->deviceIds.VendorID = -1u;
122            }
123
124            wchar_t wstr[256];
125            bufLen = sizeof(wstr);
126            ret = CM_Get_DevNode_PropertyW(devInst, &DEVPKEY_Device_FriendlyName, &type, (PBYTE) wstr, &bufLen, 0);
127            if (ret == CR_SUCCESS) {
128                ffStrbufSetNWS(&entry->name, (bufLen - 1) / sizeof(wchar_t), wstr);
129                FF_DEBUG("Device friendly name: %ls", wstr);
130            } else {
131                FF_DEBUG("Failed to get device friendly name: %s", ffDebugConfigRet(ret));
132
133                bufLen = sizeof(wstr);
134                ret = CM_Get_DevNode_PropertyW(devInst, &DEVPKEY_Device_DeviceDesc, &type, (PBYTE) wstr, &bufLen, 0);
135                if (ret == CR_SUCCESS) {
136                    ffStrbufSetNWS(&entry->name, (bufLen - 1) / sizeof(wchar_t), wstr);
137                    FF_DEBUG("Device description: %ls", wstr);
138                } else {
139                    FF_DEBUG("Failed to get device description: %s", ffDebugConfigRet(ret));
140                }
141            }
142
143            bufLen = sizeof(wstr);
144            ret = CM_Get_DevNode_PropertyW(devInst, &DEVPKEY_Device_Manufacturer, &type, (PBYTE) wstr, &bufLen, 0);
145            if (ret == CR_SUCCESS) {
146                ffStrbufSetNWS(&entry->vendor, (bufLen - 1) / sizeof(wchar_t), wstr);
147                FF_DEBUG("Device vendor name: %ls", wstr);
148            } else {
149                FF_DEBUG("Failed to get device vendor name: %s", ffDebugConfigRet(ret));
150            }
151
152            if (queryPcieGen && entry->deviceIds.VendorID != -1u) {
153                DEVPROPTYPE propType;
154
155                bufLen = sizeof(entry->maxLinkSpeed);
156                // Reports PCIe gen despite the PKEY name
157                CM_Get_DevNode_PropertyW(devInst, &DEVPKEY_PciDevice_MaxLinkSpeed, &propType, (PBYTE) &entry->maxLinkSpeed, &bufLen, 0);
158                if (ret == CR_SUCCESS) {
159                    FF_DEBUG("PCIe max GEN: %u", entry->maxLinkSpeed);
160                } else {
161                    FF_DEBUG("Failed to get PCIe max GEN: %s", ffDebugConfigRet(ret));
162                }
163
164                if (entry->maxLinkSpeed != FF_GPU_PCIE_SPEED_UNSET) {
165                    bufLen = sizeof(entry->maxLinkWidth);
166                    ret = CM_Get_DevNode_PropertyW(devInst, &DEVPKEY_PciDevice_MaxLinkWidth, &propType, (PBYTE) &entry->maxLinkWidth, &bufLen, 0);
167                    if (ret == CR_SUCCESS) {
168                        FF_DEBUG("PCIe max link width: %u", entry->maxLinkWidth);
169                    } else {
170                        FF_DEBUG("Failed to get PCIe max link width: %s", ffDebugConfigRet(ret));
171                    }
172                }
173
174                bufLen = sizeof(entry->currentLinkSpeed);
175                ret = CM_Get_DevNode_PropertyW(devInst, &DEVPKEY_PciDevice_CurrentLinkSpeed, &propType, (PBYTE) &entry->currentLinkSpeed, &bufLen, 0);
176                if (ret == CR_SUCCESS) {
177                    FF_DEBUG("PCIe GEN: %u", entry->currentLinkSpeed);
178                } else {
179                    FF_DEBUG("Failed to get PCIe GEN: %s", ffDebugConfigRet(ret));
180                }
181
182                if (entry->currentLinkSpeed != FF_GPU_PCIE_SPEED_UNSET) {
183                    bufLen = sizeof(entry->currentLinkWidth);
184                    ret = CM_Get_DevNode_PropertyW(devInst, &DEVPKEY_PciDevice_CurrentLinkWidth, &propType, (PBYTE) &entry->currentLinkWidth, &bufLen, 0);
185                    if (ret == CR_SUCCESS) {
186                        FF_DEBUG("PCIe current link width: %u", entry->currentLinkWidth);
187                    } else {
188                        FF_DEBUG("Failed to get PCIe current link width: %s", ffDebugConfigRet(ret));
189                    }
190                }
191            }
192        }
193    }
194
195    FF_LIST_FOR_EACH (CacheEntry, entry, deviceIdsCache) {
196        if (luid == entry->luid || entry->pciAddr == gpu->deviceId) {
197            FF_DEBUG("Cache hit for adapter LUID: %08llX", luid);
198            if (outDeviceIds->VendorID == -1u) {
199                *outDeviceIds = entry->deviceIds;
200            }
201
202            if (gpu->vendor.length == 0) {
203                if (entry->deviceIds.VendorID != -1u) {
204                    ffStrbufSetStatic(&gpu->vendor, ffGPUGetVendorString(entry->deviceIds.VendorID));
205                }
206                if (gpu->vendor.length == 0 && entry->vendor.length > 0) {
207                    ffStrbufDestroy(&gpu->vendor);
208                    ffStrbufInitMove(&gpu->vendor, &entry->vendor);
209                }
210            } else if (gpu->name.length == 0 && entry->deviceIds.VendorID == -1u) {
211                // Some Indirect display adapters reports fake Device IDs
212                ffStrbufDestroy(&gpu->vendor);
213                ffStrbufInitMove(&gpu->vendor, &entry->vendor);
214            }
215
216            if (gpu->name.length == 0) {
217                ffStrbufDestroy(&gpu->name);
218                ffStrbufInitMove(&gpu->name, &entry->name);
219            }
220
221            if (queryPcieGen) {
222                gpu->psMax.gen = (uint16_t) entry->maxLinkSpeed;
223                gpu->psMax.lanes = (uint16_t) entry->maxLinkWidth;
224                gpu->psCurr.gen = (uint16_t) entry->currentLinkSpeed;
225                gpu->psCurr.lanes = (uint16_t) entry->currentLinkWidth;
226            }
227            return true;
228        }
229    }
230
231    FF_DEBUG("Cache miss for adapter LUID: %08llX", luid);
232    return false;
233}
234
235#else
236    #include <unistd.h>
237    #include <fcntl.h>
238    #include <sys/ioctl.h>
239    #include <uchar.h>
240
241int dxgfd = -2;
242
243static void ffStrbufSetWS(FFstrbuf* strbuf, const char16_t* str) {
244    ffStrbufClear(strbuf);
245
246    mbstate_t state = {};
247    while (*str) {
248        char buf[5];
249        size_t len = c16rtomb(buf, *str, &state);
250        if (len == (size_t) -1) {
251            ffStrbufAppendS(strbuf, "�"); // U+FFFD REPLACEMENT CHARACTER
252        } else if (len > 0) {
253            ffStrbufAppendNS(strbuf, (uint32_t) len, buf);
254        }
255        str++;
256    }
257}
258
259static void closeDxgfd(void) {
260    if (dxgfd >= 0) {
261        close(dxgfd);
262        dxgfd = -2;
263        FF_DEBUG("Closed /dev/dxg file descriptor");
264    }
265}
266
267[[maybe_unused]] static inline const char* ffDebugNtStatus(NTSTATUS status) {
268    return status < 0 ? strerror(-status) : "Success";
269}
270#endif
271
272const char*
273#if _WIN32
274ffDetectGPUImpl
275#else
276ffGPUDetectWsl2
277#endif
278    (const FFGPUOptions* options, FFlist* gpus) {
279#if __linux__
280    if (dxgfd == -2) {
281        dxgfd = open("/dev/dxg", O_RDWR); // Windows DXCore/D3DKMT adapter driver for WSL
282        if (dxgfd < 0) {
283            if (errno == ENOENT) {
284                FF_DEBUG("/dev/dxg is not available, WSL DXCore GPU driver not detected");
285                return "No DXCore GPU driver detected (no /dev/dxg)";
286            } else {
287                FF_DEBUG("Failed to open /dev/dxg: %s", strerror(errno));
288                return "Failed to open /dev/dxg";
289            }
290        }
291        FF_DEBUG("Opened /dev/dxg successfully");
292        atexit(closeDxgfd);
293    }
294    if (dxgfd < 0) {
295        return "Failed to open /dev/dxg";
296    }
297#endif
298
299#if FF_WIN81_COMPAT
300    D3DKMT_ENUMADAPTERS enumAdapters = {};
301    D3DKMT_ADAPTERINFO* const adapters = enumAdapters.Adapters;
302    NTSTATUS status = D3DKMTEnumAdapters(&enumAdapters);
303#else
304    D3DKMT_ADAPTERINFO adapters[64];
305    D3DKMT_ENUMADAPTERS2 enumAdapters = {
306        .NumAdapters = ARRAY_SIZE(adapters),
307        .pAdapters = adapters,
308    };
309    NTSTATUS status = D3DKMTEnumAdapters2(&enumAdapters);
310#endif
311    if (!NT_SUCCESS(status)) {
312        FF_DEBUG("D3DKMTEnumAdapters(2) failed: %s", ffDebugNtStatus(status));
313        return "Failed to enumerate adapters with D3DKMTEnumAdapters2";
314    }
315
316    FF_DEBUG("D3DKMTEnumAdapters(2) succeeded, adapter count: %" PRIu32, (uint32_t) enumAdapters.NumAdapters);
317
318    for (uint32_t i = 0; i < enumAdapters.NumAdapters; i++) {
319        const D3DKMT_ADAPTERINFO* adapter = &adapters[i];
320        FF_DEBUG("Processing adapter #%u", i);
321
322        D3DKMT_ADAPTERTYPE adapterType;
323        status = D3DKMTQueryAdapterInfo(&(D3DKMT_QUERYADAPTERINFO) {
324            .hAdapter = adapter->hAdapter,
325            .Type = KMTQAITYPE_ADAPTERTYPE,
326            .pPrivateDriverData = &adapterType,
327            .PrivateDriverDataSize = sizeof(adapterType),
328        });
329        if (!NT_SUCCESS(status)) {
330            FF_DEBUG("KMTQAITYPE_ADAPTERTYPE query failed for adapter #%u: %s", i, ffDebugNtStatus(status));
331            goto close_adapter;
332        }
333        if (adapterType.SoftwareDevice) {
334            FF_DEBUG("Skipping software adapter #%u", i);
335            goto close_adapter;
336        }
337
338        uint64_t luid = ((uint64_t) adapter->AdapterLuid.HighPart << 32) | (uint64_t) adapter->AdapterLuid.LowPart;
339
340        FFGPUResult* gpu = FF_LIST_ADD(FFGPUResult, *gpus);
341        ffStrbufInit(&gpu->vendor);
342        ffStrbufInit(&gpu->name);
343        ffStrbufInit(&gpu->driver);
344        ffStrbufInit(&gpu->platformApi);
345        ffStrbufInit(&gpu->memoryType);
346        gpu->index = FF_GPU_INDEX_UNSET;
347        gpu->temperature = FF_GPU_TEMP_UNSET;
348        gpu->coreCount = FF_GPU_CORE_COUNT_UNSET;
349        gpu->coreUsage = FF_GPU_CORE_USAGE_UNSET;
350        gpu->dedicated.total = gpu->dedicated.used = gpu->shared.total = gpu->shared.used = FF_GPU_VMEM_SIZE_UNSET;
351        gpu->deviceId = 0;
352        gpu->frequency = FF_GPU_FREQUENCY_UNSET;
353        gpu->type = adapterType.HybridIntegrated
354            ? FF_GPU_TYPE_INTEGRATED
355            : adapterType.HybridDiscrete
356            ? FF_GPU_TYPE_DISCRETE
357            : FF_GPU_TYPE_UNKNOWN;
358        gpu->pcieSpeed = FF_GPU_PCIE_SPEED_UNSET;
359
360        D3DKMT_DRIVERVERSION wddmVersion;
361        status = D3DKMTQueryAdapterInfo(&(D3DKMT_QUERYADAPTERINFO) {
362            .hAdapter = adapter->hAdapter,
363            .Type = KMTQAITYPE_DRIVERVERSION,
364            .pPrivateDriverData = &wddmVersion,
365            .PrivateDriverDataSize = sizeof(wddmVersion),
366        });
367        if (NT_SUCCESS(status)) {
368            ffStrbufSetF(&gpu->platformApi, "WDDM %u.%u", (uint32_t) wddmVersion / 1000, ((uint32_t) wddmVersion % 1000) / 100);
369            FF_DEBUG("Adapter #%u WDDM version: %u", i, (uint32_t) wddmVersion);
370        } else {
371            wddmVersion = KMT_DRIVERVERSION_WDDM_1_3; // Smallest supported WDDM version, used for fallback
372            ffStrbufSetStatic(&gpu->platformApi, "WDDM");
373            FF_DEBUG("KMTQAITYPE_DRIVERVERSION query failed for adapter #%u", i);
374        }
375
376        D3DKMT_ADAPTERADDRESS adapterAddress = {};
377        status = D3DKMTQueryAdapterInfo(&(D3DKMT_QUERYADAPTERINFO) {
378            .hAdapter = adapter->hAdapter,
379            .Type = KMTQAITYPE_ADAPTERADDRESS,
380            .pPrivateDriverData = &adapterAddress,
381            .PrivateDriverDataSize = sizeof(adapterAddress),
382        });
383        if (NT_SUCCESS(status) && adapterAddress.FunctionNumber != 0xFFFF /* non-PCI device */) {
384            gpu->deviceId = ffGPUPciAddr2Id(0, adapterAddress.BusNumber, adapterAddress.DeviceNumber, adapterAddress.FunctionNumber);
385            FF_DEBUG("Adapter #%u PCI address: bus=%u device=%u function=%u",
386                i,
387                adapterAddress.BusNumber,
388                adapterAddress.DeviceNumber,
389                adapterAddress.FunctionNumber);
390        } else {
391            adapterAddress.BusNumber = -1u;
392            gpu->deviceId = ffGPUGeneral2Id(luid);
393            FF_DEBUG("KMTQAITYPE_ADAPTERADDRESS query failed for adapter #%u, fallback to LUID-based deviceId: %s",
394                i,
395                ffDebugNtStatus(status));
396        }
397
398        D3DKMT_QUERY_DEVICE_IDS deviceIds = { .PhysicalAdapterIndex = 0 };
399        status = D3DKMTQueryAdapterInfo(&(D3DKMT_QUERYADAPTERINFO) {
400            .hAdapter = adapter->hAdapter,
401            .Type = KMTQAITYPE_PHYSICALADAPTERDEVICEIDS,
402            .pPrivateDriverData = &deviceIds,
403            .PrivateDriverDataSize = sizeof(deviceIds),
404        });
405        if (NT_SUCCESS(status)) {
406            ffStrbufSetStatic(&gpu->vendor, ffGPUGetVendorString(deviceIds.DeviceIds.VendorID));
407            FF_DEBUG("Adapter #%u vendor/device IDs: vendor=0x%04x device=0x%04x",
408                i,
409                deviceIds.DeviceIds.VendorID,
410                deviceIds.DeviceIds.DeviceID);
411        } else {
412            deviceIds.DeviceIds.VendorID = -1u;
413            FF_DEBUG("KMTQAITYPE_PHYSICALADAPTERDEVICEIDS query failed for adapter #%u: %s", i, ffDebugNtStatus(status));
414        }
415
416        D3DKMT_UMD_DRIVER_VERSION umdDriverVersion;
417        status = D3DKMTQueryAdapterInfo(&(D3DKMT_QUERYADAPTERINFO) {
418            .hAdapter = adapter->hAdapter,
419            .Type = KMTQAITYPE_UMD_DRIVER_VERSION,
420            .pPrivateDriverData = &umdDriverVersion,
421            .PrivateDriverDataSize = sizeof(umdDriverVersion),
422        });
423        if (NT_SUCCESS(status)) {
424            ffStrbufSetF(&gpu->driver,
425                "%u.%u.%u.%u",
426                (uint32_t) (umdDriverVersion.DriverVersion.QuadPart >> 48ul & 0xFFFF),
427                (uint32_t) (umdDriverVersion.DriverVersion.QuadPart >> 32ul & 0xFFFF),
428                (uint32_t) (umdDriverVersion.DriverVersion.QuadPart >> 16ul & 0xFFFF),
429                (uint32_t) (umdDriverVersion.DriverVersion.QuadPart >> 0ul & 0xFFFF));
430            FF_DEBUG("Adapter #%u UMD driver version: %08" PRIX64, i, (uint64_t) umdDriverVersion.DriverVersion.QuadPart);
431        } else {
432            FF_DEBUG("KMTQAITYPE_UMD_DRIVER_VERSION query failed for adapter #%u: %s", i, ffDebugNtStatus(status));
433        }
434
435        bool isIndirectDisplayDevice = gpu->type == FF_GPU_TYPE_UNKNOWN && adapterType.IndirectDisplayDevice;
436        if (isIndirectDisplayDevice) {
437            FF_DEBUG("Adapter #%u is an indirect display device", i);
438        }
439
440        if (!isIndirectDisplayDevice) {
441            typeof(&ffDetectNvidiaGpuInfo) detectFn;
442            const char* dllName;
443            if (options->driverSpecific && getDriverSpecificDetectionFn(gpu->vendor.chars, &detectFn, &dllName)) {
444                FF_DEBUG("Calling driver-specific detection function for vendor: %s, DLL: %s", gpu->vendor.chars, dllName);
445                [[maybe_unused]] const char* error = detectFn(
446                    &(FFGpuDriverCondition) {
447                        .type = FF_GPU_DRIVER_CONDITION_TYPE_LUID |
448                            (deviceIds.DeviceIds.VendorID != -1u ? FF_GPU_DRIVER_CONDITION_TYPE_DEVICE_ID : 0) |
449                            (adapterAddress.BusNumber != -1u ? FF_GPU_DRIVER_CONDITION_TYPE_BUS_ID : 0),
450                        .pciDeviceId = {
451                            .deviceId = deviceIds.DeviceIds.DeviceID,
452                            .vendorId = deviceIds.DeviceIds.VendorID,
453                            .subSystemId = deviceIds.DeviceIds.SubSystemID,
454                            .revId = deviceIds.DeviceIds.RevisionID,
455                        },
456                        .pciBusId = {
457                            .domain = 0,
458                            .bus = adapterAddress.BusNumber,
459                            .device = adapterAddress.DeviceNumber,
460                            .func = adapterAddress.FunctionNumber,
461                        },
462                        .luid = luid,
463                    },
464                    (FFGpuDriverResult) {
465                        .index = &gpu->index,
466                        .temp = options->temp ? &gpu->temperature : nullptr,
467                        .memory = options->driverSpecific ? &gpu->dedicated : nullptr,
468                        .sharedMemory = options->driverSpecific ? &gpu->shared : nullptr,
469                        .memoryType = options->driverSpecific ? &gpu->memoryType : nullptr,
470                        .coreCount = options->driverSpecific ? (uint32_t*) &gpu->coreCount : nullptr,
471                        .coreUsage = options->driverSpecific ? &gpu->coreUsage : nullptr,
472                        .type = &gpu->type,
473                        .frequency = options->driverSpecific ? &gpu->frequency : nullptr,
474                        .name = &gpu->name,
475                        .psCurr = options->driverSpecific ? &gpu->psCurr : nullptr,
476                        .psMax = options->driverSpecific ? &gpu->psMax : nullptr,
477                    },
478                    dllName);
479                FF_DEBUG("Driver-specific detection completed: %s", error ?: "Success");
480            } else if (options->driverSpecific) {
481                FF_DEBUG("No driver-specific detection function found for vendor: %s", gpu->vendor.chars);
482            }
483        };
484
485        if (gpu->name.length == 0) {
486            D3DKMT_ADAPTERREGISTRYINFO registryInfo;
487            status = D3DKMTQueryAdapterInfo(&(D3DKMT_QUERYADAPTERINFO) {
488                .hAdapter = adapter->hAdapter,
489                .Type = KMTQAITYPE_ADAPTERREGISTRYINFO,
490                .pPrivateDriverData = &registryInfo,
491                .PrivateDriverDataSize = sizeof(registryInfo),
492            });
493            if (NT_SUCCESS(status)) {
494                ffStrbufSetWS(&gpu->name, registryInfo.AdapterString);
495                FF_DEBUG("Adapter #%u adapter string: %s", i, gpu->name.chars);
496            } else {
497                FF_DEBUG("KMTQAITYPE_ADAPTERREGISTRYINFO query failed for adapter #%u: %s", i, ffDebugNtStatus(status));
498            }
499        }
500
501        #if _WIN32
502        if (gpu->name.length == 0 || gpu->vendor.length == 0 || (options->driverSpecific && gpu->pcieSpeed == FF_GPU_PCIE_SPEED_UNSET)) {
503            queryDeviceInfoCM(gpu, luid, &deviceIds.DeviceIds, options->driverSpecific);
504        }
505        #endif
506
507        if (!isIndirectDisplayDevice) {
508            if (gpu->dedicated.total == FF_GPU_VMEM_SIZE_UNSET && gpu->shared.total == FF_GPU_VMEM_SIZE_UNSET) {
509                if (wddmVersion >= KMT_DRIVERVERSION_WDDM_3_1 && options->driverSpecific) {
510                    // Supports memory usage query; requires Windows 11 (22H2) or later
511                    D3DKMT_QUERYSTATISTICS queryStatistics = {
512                        .Type = D3DKMT_QUERYSTATISTICS_SEGMENT_GROUP_USAGE,
513                        .AdapterLuid = adapter->AdapterLuid,
514                        .QuerySegmentGroupUsage = {
515                            .PhysicalAdapterIndex = 0,
516                            .SegmentGroup = D3DKMT_MEMORY_SEGMENT_GROUP_LOCAL,
517                        },
518                    };
519                    status = D3DKMTQueryStatistics(&queryStatistics);
520                    if (NT_SUCCESS(status)) {
521                        D3DKMT_QUERYSTATISTICS_MEMORY_USAGE* info = &queryStatistics.QueryResult.SegmentGroupUsageInformation;
522                        uint64_t used = info->AllocatedBytes + info->ModifiedBytes + info->StandbyBytes;
523                        uint64_t total = used + info->FreeBytes + info->ZeroBytes;
524                        gpu->dedicated.used = used;
525                        gpu->dedicated.total = total;
526                        FF_DEBUG("Adapter #%u local memory usage: used=%" PRIu64 " total=%" PRIu64, i, used, total);
527                    } else {
528                        FF_DEBUG("D3DKMT_QUERYSTATISTICS_SEGMENT_GROUP_USAGE (LOCAL) failed for adapter #%u: %s",
529                            i,
530                            ffDebugNtStatus(status));
531                    }
532
533                    queryStatistics.QuerySegmentGroupUsage.SegmentGroup = D3DKMT_MEMORY_SEGMENT_GROUP_NON_LOCAL;
534                    status = D3DKMTQueryStatistics(&queryStatistics);
535                    if (NT_SUCCESS(status)) {
536                        D3DKMT_QUERYSTATISTICS_MEMORY_USAGE* info = &queryStatistics.QueryResult.SegmentGroupUsageInformation;
537                        uint64_t used = info->AllocatedBytes + info->ModifiedBytes + info->StandbyBytes;
538                        uint64_t total = used + info->FreeBytes + info->ZeroBytes;
539                        gpu->shared.used = used;
540                        gpu->shared.total = total;
541                        FF_DEBUG("Adapter #%u non-local memory usage: used=%" PRIu64 " total=%" PRIu64, i, used, total);
542                    } else {
543                        FF_DEBUG("D3DKMT_QUERYSTATISTICS_SEGMENT_GROUP_USAGE (NON_LOCAL) failed for adapter #%u: %s",
544                            i,
545                            ffDebugNtStatus(status));
546                    }
547                } else {
548                    // Supports basic segment (total) size query
549                    D3DKMT_SEGMENTSIZEINFO segmentSizeInfo = {};
550                    status = D3DKMTQueryAdapterInfo(&(D3DKMT_QUERYADAPTERINFO) {
551                        .hAdapter = adapter->hAdapter,
552                        .Type = KMTQAITYPE_GETSEGMENTSIZE,
553                        .pPrivateDriverData = &segmentSizeInfo,
554                        .PrivateDriverDataSize = sizeof(segmentSizeInfo),
555                    });
556                    if (NT_SUCCESS(status)) {
557                        FF_DEBUG("Adapter #%u segment size - DedicatedVideoMemorySize: %" PRIu64
558                                ", DedicatedSystemMemorySize: %" PRIu64 ", SharedSystemMemorySize: %" PRIu64,
559                            i,
560                            (uint64_t) segmentSizeInfo.DedicatedVideoMemorySize,
561                            (uint64_t) segmentSizeInfo.DedicatedSystemMemorySize,
562                            (uint64_t) segmentSizeInfo.SharedSystemMemorySize);
563                        gpu->dedicated.total = segmentSizeInfo.DedicatedVideoMemorySize;
564                        gpu->shared.total = segmentSizeInfo.DedicatedSystemMemorySize + segmentSizeInfo.SharedSystemMemorySize;
565                    } else {
566                        FF_DEBUG("Failed to query segment size information for adapter #%u: %s", i, ffDebugNtStatus(status));
567                    }
568                }
569            }
570
571            if (wddmVersion >= KMT_DRIVERVERSION_WDDM_2_4) {
572                if (gpu->frequency == FF_GPU_FREQUENCY_UNSET) {
573                    for (uint32_t nodeIdx = 0;; nodeIdx++) {
574                        D3DKMT_NODEMETADATA nodeMetadata = {
575                            .NodeOrdinalAndAdapterIndex = (0 << 16) | nodeIdx,
576                        };
577                        status = D3DKMTQueryAdapterInfo(&(D3DKMT_QUERYADAPTERINFO) {
578                            .hAdapter = adapter->hAdapter,
579                            .Type = KMTQAITYPE_NODEMETADATA,
580                            .pPrivateDriverData = &nodeMetadata,
581                            .PrivateDriverDataSize = sizeof(nodeMetadata),
582                        });
583                        if (!NT_SUCCESS(status)) { break; }
584
585                        if (nodeMetadata.NodeData.EngineType != DXGK_ENGINE_TYPE_3D) { continue; }
586
587                        D3DKMT_NODE_PERFDATA nodePerfData = {
588                            .NodeOrdinal = nodeIdx,
589                            .PhysicalAdapterIndex = 0,
590                        };
591                        status = D3DKMTQueryAdapterInfo(&(D3DKMT_QUERYADAPTERINFO) {
592                            .hAdapter = adapter->hAdapter,
593                            .Type = KMTQAITYPE_NODEPERFDATA,
594                            .pPrivateDriverData = &nodePerfData,
595                            .PrivateDriverDataSize = sizeof(nodePerfData),
596                        });
597                        if (NT_SUCCESS(status)) {
598                            if (nodePerfData.MaxFrequency != 0) {
599                                gpu->frequency = (uint32_t) (nodePerfData.MaxFrequency / 1000 / 1000);
600                                FF_DEBUG("Adapter #%u max graphics frequency: %u MHz", i, gpu->frequency);
601                            } else {
602                                FF_DEBUG("Adapter #%u does not report max graphics frequency", i);
603                            }
604                            break;
605                        } else {
606                            FF_DEBUG("Failed to query node performance data for adapter #%u node #%u: %s",
607                                i,
608                                nodeIdx,
609                                ffDebugNtStatus(status));
610                        }
611                    }
612                }
613
614                if (options->temp && gpu->temperature == FF_GPU_TEMP_UNSET) {
615                    D3DKMT_ADAPTER_PERFDATA adapterPerfData = {
616                        .PhysicalAdapterIndex = 0,
617                    };
618                    status = D3DKMTQueryAdapterInfo(&(D3DKMT_QUERYADAPTERINFO) {
619                        .hAdapter = adapter->hAdapter,
620                        .Type = KMTQAITYPE_ADAPTERPERFDATA,
621                        .pPrivateDriverData = &adapterPerfData,
622                        .PrivateDriverDataSize = sizeof(adapterPerfData),
623                    });
624                    if (NT_SUCCESS(status)) {
625                        if (adapterPerfData.Temperature != 0) {
626                            gpu->temperature = adapterPerfData.Temperature / 10.0;
627                            FF_DEBUG("Adapter #%u temperature: %.1f°C", i, gpu->temperature);
628                        } else {
629                            FF_DEBUG("Adapter #%u does not report temperature data", i);
630                        }
631                    } else {
632                        FF_DEBUG("Failed to query temperature for adapter #%u: %s", i, ffDebugNtStatus(status));
633                    }
634                }
635            }
636
637            if (gpu->type == FF_GPU_TYPE_UNKNOWN) {
638                if (ffGPUDetectTypeByVendorAndName(gpu)) {
639                    // OK
640                }
641    #if _WIN32
642                else if (ffIsWindows10OrGreater()) {
643                    const char* ffGPUDetectTypeWithDXCore(LUID adapterLuid, FFGPUResult * gpu);
644                    [[maybe_unused]] const char* error = ffGPUDetectTypeWithDXCore(adapter->AdapterLuid, gpu);
645                    FF_DEBUG("DXCore GPU type detection result: %s", error ?: "Success");
646                }
647    #endif
648                else {
649                    FF_DEBUG("Unable to determine GPU type by any method for this adapter");
650                }
651            }
652        }
653
654        FF_DEBUG("Adapter #%u summary: name='%s', vendor='%s', type=%u, deviceId=%" PRIu64,
655            i,
656            gpu->name.length ? gpu->name.chars : "unknown",
657            gpu->vendor.length ? gpu->vendor.chars : "unknown",
658            (uint32_t) gpu->type,
659            (uint64_t) gpu->deviceId);
660
661    close_adapter:
662        status = D3DKMTCloseAdapter(&(D3DKMT_CLOSEADAPTER) { .hAdapter = adapter->hAdapter });
663        if (NT_SUCCESS(status)) {
664            FF_DEBUG("Closed adapter #%u successfully", i);
665        } else {
666            FF_DEBUG("Failed to close adapter #%u: %s", i, ffDebugNtStatus(status));
667        }
668    }
669
670    return nullptr;
671}