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 = ®istryInfo,
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}