GPU (Windows): correctly report virtual GPUs

Fixes #2461
This commit is contained in:
李通洲
2026-07-16 19:03:43 +08:00
parent 445311978e
commit 8bf1aca7fc
+283 -254
View File
@@ -17,12 +17,16 @@
#include <windows.h>
#include <cfgmgr32.h>
#include <devguid.h>
#include <devpkey.h>
#include <pciprop.h>
#define GUID_DEVCLASS_DISPLAY_STRING L"{4d36e968-e325-11ce-bfc1-08002be10318}" // Found in <devguid.h>
static bool queryPciDeviceInfo(FFGPUResult* gpu, D3DKMT_DEVICE_IDS* outDeviceIds, bool queryPcieGen) {
FF_DEBUG("Query PCI device info: %08llX", gpu->deviceId);
// https://wine-devel.winehq.narkive.com/vKO2Bkgj/patch-1-2-dxgi-tests-add-test-for-enumerating-display-adapters-using-setupapi
DEFINE_DEVPROPKEY(DEVPROPKEY_DISPLAY_ADAPTER_LUID, 0x60b193cb, 0x5276, 0x4d0f, 0x96, 0xfc, 0xf1, 0x73, 0xab, 0xad, 0x3e, 0xc6, 2);
static bool queryDeviceInfoCM(FFGPUResult* gpu, uint64_t luid, D3DKMT_DEVICE_IDS* outDeviceIds, bool queryPcieGen) {
FF_DEBUG("Query PCI device info: %08llX with LUID: %08llX", gpu->deviceId, luid);
static FFlist deviceIdsCache;
static bool initialized;
@@ -32,7 +36,10 @@ static bool queryPciDeviceInfo(FFGPUResult* gpu, D3DKMT_DEVICE_IDS* outDeviceIds
uint32_t maxLinkSpeed;
uint32_t maxLinkWidth;
D3DKMT_DEVICE_IDS deviceIds;
uint64_t adapterAddress;
uint64_t pciAddr;
uint64_t luid;
FFstrbuf vendor;
FFstrbuf name;
} CacheEntry;
if (!initialized) {
@@ -71,13 +78,36 @@ static bool queryPciDeviceInfo(FFGPUResult* gpu, D3DKMT_DEVICE_IDS* outDeviceIds
}
}
if (wcsncmp(devId, L"PCI\\", 4) != 0) {
FF_DEBUG("Skipping non-PCI device ID: %ls", devId);
continue;
}
CacheEntry* entry = FF_LIST_ADD(CacheEntry, deviceIdsCache);
*entry = (CacheEntry) {};
*entry = (CacheEntry){};
ULONG bufLen = sizeof(entry->luid);
DEVPROPTYPE type;
CONFIGRET ret = CM_Get_DevNode_PropertyW(devInst, &DEVPROPKEY_DISPLAY_ADAPTER_LUID, &type, (PBYTE) &entry->luid, &bufLen, 0);
if (ret != CR_SUCCESS) { // Not available on Windows 8.1
FF_DEBUG("Failed to get device LUID: %s", ffDebugConfigRet(ret));
uint32_t pciBus = 0;
ULONG pciBufLen = sizeof(pciBus);
if (CM_Get_DevNode_Registry_PropertyW(devInst, CM_DRP_BUSNUMBER, nullptr, &pciBus, &pciBufLen, 0) == CR_SUCCESS) {
uint32_t pciAddr = 0;
pciBufLen = sizeof(pciAddr);
if (CM_Get_DevNode_Registry_PropertyW(devInst, CM_DRP_ADDRESS, nullptr, &pciAddr, &pciBufLen, 0) == CR_SUCCESS) {
entry->pciAddr = ffGPUPciAddr2Id(0, pciBus, (pciAddr >> 16) & 0xFFFF, pciAddr & 0xFFFF);
FF_DEBUG("Cached device IDs for PCI bus %u: vendor=0x%04x device=0x%04x", pciBus, entry->deviceIds.VendorID, entry->deviceIds.DeviceID);
} else {
FF_DEBUG("Failed to get PCI address");
}
} else {
FF_DEBUG("Failed to get PCI bus number");
}
if (entry->pciAddr == 0) {
FF_DEBUG("Skipping device ID: %ls due to missing LUID and PCI address", devId);
deviceIdsCache.length--;
continue;
}
}
// L"PCI\\VEN_10DE&DEV_2782&SUBSYS_513417AA&REV_A1\\4&3674a6b9&0&0008"
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) {
@@ -87,32 +117,44 @@ static bool queryPciDeviceInfo(FFGPUResult* gpu, D3DKMT_DEVICE_IDS* outDeviceIds
// And yeah, DXGKMDT_OPM_BUS_TYPE_PCIEXPRESS (3) exists
entry->deviceIds.BusType = 1;
} else {
FF_DEBUG("Failed to parse PCI IDs from device ID string");
deviceIdsCache.length--; // remove the cache entry since it's not valid
continue;
FF_DEBUG("Failed to parse PCI IDs from device ID: %ls", devId);
entry->deviceIds.VendorID = -1u;
}
uint32_t pciBus = 0;
ULONG pciBufLen = sizeof(pciBus);
if (CM_Get_DevNode_Registry_PropertyW(devInst, CM_DRP_BUSNUMBER, nullptr, &pciBus, &pciBufLen, 0) == CR_SUCCESS) {
uint32_t pciAddr = 0;
pciBufLen = sizeof(pciAddr);
if (CM_Get_DevNode_Registry_PropertyW(devInst, CM_DRP_ADDRESS, nullptr, &pciAddr, &pciBufLen, 0) == CR_SUCCESS) {
entry->adapterAddress = ffGPUPciAddr2Id(0, pciBus, (pciAddr >> 16) & 0xFFFF, pciAddr & 0xFFFF);
FF_DEBUG("Cached device IDs for PCI bus %u: vendor=0x%04x device=0x%04x", pciBus, entry->deviceIds.VendorID, entry->deviceIds.DeviceID);
} else {
FF_DEBUG("Failed to get PCI address");
}
wchar_t wstr[256];
bufLen = sizeof(wstr);
ret = CM_Get_DevNode_PropertyW(devInst, &DEVPKEY_Device_FriendlyName, &type, (PBYTE) wstr, &bufLen, 0);
if (ret == CR_SUCCESS) {
ffStrbufSetNWS(&entry->name, (bufLen - 1) / sizeof(wchar_t), wstr);
FF_DEBUG("Device friendly name: %ls", wstr);
} else {
FF_DEBUG("Failed to get PCI bus number");
FF_DEBUG("Failed to get device friendly name: %s", ffDebugConfigRet(ret));
bufLen = sizeof(wstr);
ret = CM_Get_DevNode_PropertyW(devInst, &DEVPKEY_Device_DeviceDesc, &type, (PBYTE) wstr, &bufLen, 0);
if (ret == CR_SUCCESS) {
ffStrbufSetNWS(&entry->name, (bufLen - 1) / sizeof(wchar_t), wstr);
FF_DEBUG("Device description: %ls", wstr);
} else {
FF_DEBUG("Failed to get device description: %s", ffDebugConfigRet(ret));
}
}
if (queryPcieGen) {
bufLen = sizeof(wstr);
ret = CM_Get_DevNode_PropertyW(devInst, &DEVPKEY_Device_Manufacturer, &type, (PBYTE) wstr, &bufLen, 0);
if (ret == CR_SUCCESS) {
ffStrbufSetNWS(&entry->vendor, (bufLen - 1) / sizeof(wchar_t), wstr);
FF_DEBUG("Device vendor name: %ls", wstr);
} else {
FF_DEBUG("Failed to get device vendor name: %s", ffDebugConfigRet(ret));
}
if (queryPcieGen && entry->deviceIds.VendorID != -1u) {
DEVPROPTYPE propType;
pciBufLen = sizeof(entry->maxLinkSpeed);
bufLen = sizeof(entry->maxLinkSpeed);
// Reports PCIe gen despite the PKEY name
CONFIGRET ret = CM_Get_DevNode_PropertyW(devInst, &DEVPKEY_PciDevice_MaxLinkSpeed, &propType, (PBYTE) &entry->maxLinkSpeed, &pciBufLen, 0);
CM_Get_DevNode_PropertyW(devInst, &DEVPKEY_PciDevice_MaxLinkSpeed, &propType, (PBYTE) &entry->maxLinkSpeed, &bufLen, 0);
if (ret == CR_SUCCESS) {
FF_DEBUG("PCIe max GEN: %u", entry->maxLinkSpeed);
} else {
@@ -120,8 +162,8 @@ static bool queryPciDeviceInfo(FFGPUResult* gpu, D3DKMT_DEVICE_IDS* outDeviceIds
}
if (entry->maxLinkSpeed != FF_GPU_PCIE_SPEED_UNSET) {
pciBufLen = sizeof(entry->maxLinkWidth);
ret = CM_Get_DevNode_PropertyW(devInst, &DEVPKEY_PciDevice_MaxLinkWidth, &propType, (PBYTE) &entry->maxLinkWidth, &pciBufLen, 0);
bufLen = sizeof(entry->maxLinkWidth);
ret = CM_Get_DevNode_PropertyW(devInst, &DEVPKEY_PciDevice_MaxLinkWidth, &propType, (PBYTE) &entry->maxLinkWidth, &bufLen, 0);
if (ret == CR_SUCCESS) {
FF_DEBUG("PCIe max link width: %u", entry->maxLinkWidth);
} else {
@@ -129,8 +171,8 @@ static bool queryPciDeviceInfo(FFGPUResult* gpu, D3DKMT_DEVICE_IDS* outDeviceIds
}
}
pciBufLen = sizeof(entry->currentLinkSpeed);
ret = CM_Get_DevNode_PropertyW(devInst, &DEVPKEY_PciDevice_CurrentLinkSpeed, &propType, (PBYTE) &entry->currentLinkSpeed, &pciBufLen, 0);
bufLen = sizeof(entry->currentLinkSpeed);
ret = CM_Get_DevNode_PropertyW(devInst, &DEVPKEY_PciDevice_CurrentLinkSpeed, &propType, (PBYTE) &entry->currentLinkSpeed, &bufLen, 0);
if (ret == CR_SUCCESS) {
FF_DEBUG("PCIe GEN: %u", entry->currentLinkSpeed);
} else {
@@ -138,8 +180,8 @@ static bool queryPciDeviceInfo(FFGPUResult* gpu, D3DKMT_DEVICE_IDS* outDeviceIds
}
if (entry->currentLinkSpeed != FF_GPU_PCIE_SPEED_UNSET) {
pciBufLen = sizeof(entry->currentLinkWidth);
ret = CM_Get_DevNode_PropertyW(devInst, &DEVPKEY_PciDevice_CurrentLinkWidth, &propType, (PBYTE) &entry->currentLinkWidth, &pciBufLen, 0);
bufLen = sizeof(entry->currentLinkWidth);
ret = CM_Get_DevNode_PropertyW(devInst, &DEVPKEY_PciDevice_CurrentLinkWidth, &propType, (PBYTE) &entry->currentLinkWidth, &bufLen, 0);
if (ret == CR_SUCCESS) {
FF_DEBUG("PCIe current link width: %u", entry->currentLinkWidth);
} else {
@@ -151,12 +193,31 @@ static bool queryPciDeviceInfo(FFGPUResult* gpu, D3DKMT_DEVICE_IDS* outDeviceIds
}
FF_LIST_FOR_EACH (CacheEntry, entry, deviceIdsCache) {
if (gpu->deviceId == entry->adapterAddress) {
FF_DEBUG("Cache hit for adapter address: %08llX", gpu->deviceId);
if (luid == entry->luid || entry->pciAddr == gpu->deviceId) {
FF_DEBUG("Cache hit for adapter LUID: %08llX", luid);
if (outDeviceIds->VendorID == -1u) {
*outDeviceIds = entry->deviceIds;
}
if (gpu->vendor.length == 0) {
if (entry->deviceIds.VendorID != -1u) {
ffStrbufSetStatic(&gpu->vendor, ffGPUGetVendorString(entry->deviceIds.VendorID));
}
if (gpu->vendor.length == 0 && entry->vendor.length > 0) {
ffStrbufDestroy(&gpu->vendor);
ffStrbufInitMove(&gpu->vendor, &entry->vendor);
}
} else if (gpu->name.length == 0 && entry->deviceIds.VendorID == -1u) {
// Some Indirect display adapters reports fake Device IDs
ffStrbufDestroy(&gpu->vendor);
ffStrbufInitMove(&gpu->vendor, &entry->vendor);
}
if (gpu->name.length == 0) {
ffStrbufDestroy(&gpu->name);
ffStrbufInitMove(&gpu->name, &entry->name);
}
if (queryPcieGen) {
gpu->psMax.gen = (uint16_t) entry->maxLinkSpeed;
gpu->psMax.lanes = (uint16_t) entry->maxLinkWidth;
@@ -167,37 +228,10 @@ static bool queryPciDeviceInfo(FFGPUResult* gpu, D3DKMT_DEVICE_IDS* outDeviceIds
}
}
FF_DEBUG("Cache miss for adapter address: %08llX", gpu->deviceId);
FF_DEBUG("Cache miss for adapter LUID: %08llX", luid);
return false;
}
static bool queryVendorNameViaRegistry(FFstrbuf* vendor, D3DKMT_HANDLE hAdapter) {
// `KMTQAITYPE_QUERY_ADAPTER_UNIQUE_GUID` reports the GUID value used by the adapter's registry key (DirectX and Video)
GUID guid;
NTSTATUS status = D3DKMTQueryAdapterInfo(&(D3DKMT_QUERYADAPTERINFO) {
.hAdapter = hAdapter,
.Type = KMTQAITYPE_QUERY_ADAPTER_UNIQUE_GUID,
.pPrivateDriverData = &guid,
.PrivateDriverDataSize = sizeof(guid),
});
if (!NT_SUCCESS(status)) {
FF_DEBUG("Failed to query adapter unique GUID: %s", ffDebugNtStatus(status));
return false;
}
wchar_t path[PATH_MAX];
swprintf(path, ARRAY_SIZE(path), L"SYSTEM\\CurrentControlSet\\Control\\Video\\{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}\\0000", guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]);
FF_DEBUG("Querying registry: HKEY_LOCAL_MACHINE\\%ls\\ProviderName", path);
FF_AUTO_CLOSE_FD HANDLE key = nullptr;
if (!ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, path, &key, nullptr)) {
return false;
}
return ffRegReadStrbuf(key, L"ProviderName", vendor, nullptr);
}
#else
#include <unistd.h>
#include <fcntl.h>
@@ -301,6 +335,8 @@ ffGPUDetectWsl2
goto close_adapter;
}
uint64_t luid = ((uint64_t) adapter->AdapterLuid.HighPart << 32) | (uint64_t) adapter->AdapterLuid.LowPart;
FFGPUResult* gpu = FF_LIST_ADD(FFGPUResult, *gpus);
ffStrbufInit(&gpu->vendor);
ffStrbufInit(&gpu->name);
@@ -321,7 +357,7 @@ ffGPUDetectWsl2
: FF_GPU_TYPE_UNKNOWN;
gpu->pcieSpeed = FF_GPU_PCIE_SPEED_UNSET;
D3DKMT_DRIVERVERSION wddmVersion = KMT_DRIVERVERSION_WDDM_2_0;
D3DKMT_DRIVERVERSION wddmVersion;
status = D3DKMTQueryAdapterInfo(&(D3DKMT_QUERYADAPTERINFO) {
.hAdapter = adapter->hAdapter,
.Type = KMTQAITYPE_DRIVERVERSION,
@@ -332,6 +368,7 @@ ffGPUDetectWsl2
ffStrbufSetF(&gpu->platformApi, "WDDM %u.%u", (uint32_t) wddmVersion / 1000, ((uint32_t) wddmVersion % 1000) / 100);
FF_DEBUG("Adapter #%u WDDM version: %u", i, (uint32_t) wddmVersion);
} else {
wddmVersion = KMT_DRIVERVERSION_WDDM_1_3; // Smallest supported WDDM version, used for fallback
ffStrbufSetStatic(&gpu->platformApi, "WDDM");
FF_DEBUG("KMTQAITYPE_DRIVERVERSION query failed for adapter #%u", i);
}
@@ -352,7 +389,7 @@ ffGPUDetectWsl2
adapterAddress.FunctionNumber);
} else {
adapterAddress.BusNumber = -1u;
gpu->deviceId = ffGPUGeneral2Id(((uint64_t) adapter->AdapterLuid.HighPart << 32) | (uint64_t) adapter->AdapterLuid.LowPart);
gpu->deviceId = ffGPUGeneral2Id(luid);
FF_DEBUG("KMTQAITYPE_ADAPTERADDRESS query failed for adapter #%u, fallback to LUID-based deviceId: %s",
i,
ffDebugNtStatus(status));
@@ -376,14 +413,6 @@ ffGPUDetectWsl2
FF_DEBUG("KMTQAITYPE_PHYSICALADAPTERDEVICEIDS query failed for adapter #%u: %s", i, ffDebugNtStatus(status));
}
#if _WIN32 && FF_WIN81_COMPAT
if (adapterAddress.BusNumber != -1u && (deviceIds.DeviceIds.VendorID == -1u || options->driverSpecific)) {
if (queryPciDeviceInfo(gpu, &deviceIds.DeviceIds, options->driverSpecific) && gpu->vendor.length == 0) {
ffStrbufSetStatic(&gpu->vendor, ffGPUGetVendorString(deviceIds.DeviceIds.VendorID));
}
}
#endif
D3DKMT_UMD_DRIVER_VERSION umdDriverVersion;
status = D3DKMTQueryAdapterInfo(&(D3DKMT_QUERYADAPTERINFO) {
.hAdapter = adapter->hAdapter,
@@ -403,63 +432,55 @@ ffGPUDetectWsl2
FF_DEBUG("KMTQAITYPE_UMD_DRIVER_VERSION query failed for adapter #%u: %s", i, ffDebugNtStatus(status));
}
typeof(&ffDetectNvidiaGpuInfo) detectFn;
const char* dllName;
if (options->driverSpecific && getDriverSpecificDetectionFn(gpu->vendor.chars, &detectFn, &dllName)) {
FF_DEBUG("Calling driver-specific detection function for vendor: %s, DLL: %s", gpu->vendor.chars, dllName);
[[maybe_unused]] const char* error = detectFn(
&(FFGpuDriverCondition) {
.type = FF_GPU_DRIVER_CONDITION_TYPE_LUID |
(deviceIds.DeviceIds.VendorID != -1u ? FF_GPU_DRIVER_CONDITION_TYPE_DEVICE_ID : 0) |
(adapterAddress.BusNumber != -1u ? FF_GPU_DRIVER_CONDITION_TYPE_BUS_ID : 0),
.pciDeviceId = {
.deviceId = deviceIds.DeviceIds.DeviceID,
.vendorId = deviceIds.DeviceIds.VendorID,
.subSystemId = deviceIds.DeviceIds.SubSystemID,
.revId = deviceIds.DeviceIds.RevisionID,
},
.pciBusId = {
.domain = 0,
.bus = adapterAddress.BusNumber,
.device = adapterAddress.DeviceNumber,
.func = adapterAddress.FunctionNumber,
},
.luid = ((uint64_t) adapter->AdapterLuid.HighPart << 32) | (uint64_t) adapter->AdapterLuid.LowPart,
},
(FFGpuDriverResult) {
.index = &gpu->index,
.temp = options->temp ? &gpu->temperature : nullptr,
.memory = options->driverSpecific ? &gpu->dedicated : nullptr,
.sharedMemory = options->driverSpecific ? &gpu->shared : nullptr,
.memoryType = options->driverSpecific ? &gpu->memoryType : nullptr,
.coreCount = options->driverSpecific ? (uint32_t*) &gpu->coreCount : nullptr,
.coreUsage = options->driverSpecific ? &gpu->coreUsage : nullptr,
.type = &gpu->type,
.frequency = options->driverSpecific ? &gpu->frequency : nullptr,
.name = &gpu->name,
.psCurr = options->driverSpecific ? &gpu->psCurr : nullptr,
.psMax = options->driverSpecific ? &gpu->psMax : nullptr,
},
dllName);
FF_DEBUG("Driver-specific detection completed: %s", error ?: "Success");
} else if (options->driverSpecific) {
FF_DEBUG("No driver-specific detection function found for vendor: %s", gpu->vendor.chars);
bool isIndirectDisplayDevice = gpu->type == FF_GPU_TYPE_UNKNOWN && adapterType.IndirectDisplayDevice;
if (isIndirectDisplayDevice) {
FF_DEBUG("Adapter #%u is an indirect display device", i);
}
#if _WIN32
// Put this after the driver-specific detection, as `getDriverSpecificDetectionFn` never succeeds
if (gpu->vendor.length == 0 && wddmVersion >= KMT_DRIVERVERSION_WDDM_2_4) {
// For non-PCI devices
FF_DEBUG("Attempting to query vendor name via registry for adapter #%u", i);
queryVendorNameViaRegistry(&gpu->vendor, adapter->hAdapter);
}
#if !FF_WIN81_COMPAT
if (adapterAddress.BusNumber != -1u && options->driverSpecific && gpu->pcieSpeed == FF_GPU_PCIE_SPEED_UNSET) {
queryPciDeviceInfo(gpu, &deviceIds.DeviceIds, options->driverSpecific);
}
#endif
#endif
if (!isIndirectDisplayDevice) {
typeof(&ffDetectNvidiaGpuInfo) detectFn;
const char* dllName;
if (options->driverSpecific && getDriverSpecificDetectionFn(gpu->vendor.chars, &detectFn, &dllName)) {
FF_DEBUG("Calling driver-specific detection function for vendor: %s, DLL: %s", gpu->vendor.chars, dllName);
[[maybe_unused]] const char* error = detectFn(
&(FFGpuDriverCondition) {
.type = FF_GPU_DRIVER_CONDITION_TYPE_LUID |
(deviceIds.DeviceIds.VendorID != -1u ? FF_GPU_DRIVER_CONDITION_TYPE_DEVICE_ID : 0) |
(adapterAddress.BusNumber != -1u ? FF_GPU_DRIVER_CONDITION_TYPE_BUS_ID : 0),
.pciDeviceId = {
.deviceId = deviceIds.DeviceIds.DeviceID,
.vendorId = deviceIds.DeviceIds.VendorID,
.subSystemId = deviceIds.DeviceIds.SubSystemID,
.revId = deviceIds.DeviceIds.RevisionID,
},
.pciBusId = {
.domain = 0,
.bus = adapterAddress.BusNumber,
.device = adapterAddress.DeviceNumber,
.func = adapterAddress.FunctionNumber,
},
.luid = luid,
},
(FFGpuDriverResult) {
.index = &gpu->index,
.temp = options->temp ? &gpu->temperature : nullptr,
.memory = options->driverSpecific ? &gpu->dedicated : nullptr,
.sharedMemory = options->driverSpecific ? &gpu->shared : nullptr,
.memoryType = options->driverSpecific ? &gpu->memoryType : nullptr,
.coreCount = options->driverSpecific ? (uint32_t*) &gpu->coreCount : nullptr,
.coreUsage = options->driverSpecific ? &gpu->coreUsage : nullptr,
.type = &gpu->type,
.frequency = options->driverSpecific ? &gpu->frequency : nullptr,
.name = &gpu->name,
.psCurr = options->driverSpecific ? &gpu->psCurr : nullptr,
.psMax = options->driverSpecific ? &gpu->psMax : nullptr,
},
dllName);
FF_DEBUG("Driver-specific detection completed: %s", error ?: "Success");
} else if (options->driverSpecific) {
FF_DEBUG("No driver-specific detection function found for vendor: %s", gpu->vendor.chars);
}
};
if (gpu->name.length == 0) {
D3DKMT_ADAPTERREGISTRYINFO registryInfo;
@@ -477,148 +498,156 @@ ffGPUDetectWsl2
}
}
if (gpu->dedicated.total == FF_GPU_VMEM_SIZE_UNSET && gpu->shared.total == FF_GPU_VMEM_SIZE_UNSET) {
if (wddmVersion >= KMT_DRIVERVERSION_WDDM_3_1 && options->driverSpecific) {
// Supports memory usage query; requires Windows 11 (22H2) or later
D3DKMT_QUERYSTATISTICS queryStatistics = {
.Type = D3DKMT_QUERYSTATISTICS_SEGMENT_GROUP_USAGE,
.AdapterLuid = adapter->AdapterLuid,
.QuerySegmentGroupUsage = {
.PhysicalAdapterIndex = 0,
.SegmentGroup = D3DKMT_MEMORY_SEGMENT_GROUP_LOCAL,
},
};
status = D3DKMTQueryStatistics(&queryStatistics);
if (NT_SUCCESS(status)) {
D3DKMT_QUERYSTATISTICS_MEMORY_USAGE* info = &queryStatistics.QueryResult.SegmentGroupUsageInformation;
uint64_t used = info->AllocatedBytes + info->ModifiedBytes + info->StandbyBytes;
uint64_t total = used + info->FreeBytes + info->ZeroBytes;
gpu->dedicated.used = used;
gpu->dedicated.total = total;
FF_DEBUG("Adapter #%u local memory usage: used=%" PRIu64 " total=%" PRIu64, i, used, total);
} else {
FF_DEBUG("D3DKMT_QUERYSTATISTICS_SEGMENT_GROUP_USAGE (LOCAL) failed for adapter #%u: %s",
i,
ffDebugNtStatus(status));
}
queryStatistics.QuerySegmentGroupUsage.SegmentGroup = D3DKMT_MEMORY_SEGMENT_GROUP_NON_LOCAL;
status = D3DKMTQueryStatistics(&queryStatistics);
if (NT_SUCCESS(status)) {
D3DKMT_QUERYSTATISTICS_MEMORY_USAGE* info = &queryStatistics.QueryResult.SegmentGroupUsageInformation;
uint64_t used = info->AllocatedBytes + info->ModifiedBytes + info->StandbyBytes;
uint64_t total = used + info->FreeBytes + info->ZeroBytes;
gpu->shared.used = used;
gpu->shared.total = total;
FF_DEBUG("Adapter #%u non-local memory usage: used=%" PRIu64 " total=%" PRIu64, i, used, total);
} else {
FF_DEBUG("D3DKMT_QUERYSTATISTICS_SEGMENT_GROUP_USAGE (NON_LOCAL) failed for adapter #%u: %s",
i,
ffDebugNtStatus(status));
}
} else {
// Supports basic segment (total) size query
D3DKMT_SEGMENTSIZEINFO segmentSizeInfo = {};
status = D3DKMTQueryAdapterInfo(&(D3DKMT_QUERYADAPTERINFO) {
.hAdapter = adapter->hAdapter,
.Type = KMTQAITYPE_GETSEGMENTSIZE,
.pPrivateDriverData = &segmentSizeInfo,
.PrivateDriverDataSize = sizeof(segmentSizeInfo),
});
if (NT_SUCCESS(status)) {
FF_DEBUG("Adapter #%u segment size - DedicatedVideoMemorySize: %" PRIu64
", DedicatedSystemMemorySize: %" PRIu64 ", SharedSystemMemorySize: %" PRIu64,
i,
(uint64_t) segmentSizeInfo.DedicatedVideoMemorySize,
(uint64_t) segmentSizeInfo.DedicatedSystemMemorySize,
(uint64_t) segmentSizeInfo.SharedSystemMemorySize);
gpu->dedicated.total = segmentSizeInfo.DedicatedVideoMemorySize;
gpu->shared.total = segmentSizeInfo.DedicatedSystemMemorySize + segmentSizeInfo.SharedSystemMemorySize;
} else {
FF_DEBUG("Failed to query segment size information for adapter #%u: %s", i, ffDebugNtStatus(status));
}
}
#if _WIN32
if (gpu->name.length == 0 || gpu->vendor.length == 0 || (options->driverSpecific && gpu->pcieSpeed == FF_GPU_PCIE_SPEED_UNSET)) {
queryDeviceInfoCM(gpu, luid, &deviceIds.DeviceIds, options->driverSpecific);
}
#endif
if (wddmVersion >= KMT_DRIVERVERSION_WDDM_2_4) {
if (gpu->frequency == FF_GPU_FREQUENCY_UNSET) {
for (uint32_t nodeIdx = 0;; nodeIdx++) {
D3DKMT_NODEMETADATA nodeMetadata = {
.NodeOrdinalAndAdapterIndex = (0 << 16) | nodeIdx,
if (!isIndirectDisplayDevice) {
if (gpu->dedicated.total == FF_GPU_VMEM_SIZE_UNSET && gpu->shared.total == FF_GPU_VMEM_SIZE_UNSET) {
if (wddmVersion >= KMT_DRIVERVERSION_WDDM_3_1 && options->driverSpecific) {
// Supports memory usage query; requires Windows 11 (22H2) or later
D3DKMT_QUERYSTATISTICS queryStatistics = {
.Type = D3DKMT_QUERYSTATISTICS_SEGMENT_GROUP_USAGE,
.AdapterLuid = adapter->AdapterLuid,
.QuerySegmentGroupUsage = {
.PhysicalAdapterIndex = 0,
.SegmentGroup = D3DKMT_MEMORY_SEGMENT_GROUP_LOCAL,
},
};
status = D3DKMTQueryAdapterInfo(&(D3DKMT_QUERYADAPTERINFO) {
.hAdapter = adapter->hAdapter,
.Type = KMTQAITYPE_NODEMETADATA,
.pPrivateDriverData = &nodeMetadata,
.PrivateDriverDataSize = sizeof(nodeMetadata),
});
if (!NT_SUCCESS(status)) { break; }
if (nodeMetadata.NodeData.EngineType != DXGK_ENGINE_TYPE_3D) { continue; }
D3DKMT_NODE_PERFDATA nodePerfData = {
.NodeOrdinal = nodeIdx,
.PhysicalAdapterIndex = 0,
};
status = D3DKMTQueryAdapterInfo(&(D3DKMT_QUERYADAPTERINFO) {
.hAdapter = adapter->hAdapter,
.Type = KMTQAITYPE_NODEPERFDATA,
.pPrivateDriverData = &nodePerfData,
.PrivateDriverDataSize = sizeof(nodePerfData),
});
status = D3DKMTQueryStatistics(&queryStatistics);
if (NT_SUCCESS(status)) {
if (nodePerfData.MaxFrequency != 0) {
gpu->frequency = (uint32_t) (nodePerfData.MaxFrequency / 1000 / 1000);
FF_DEBUG("Adapter #%u max graphics frequency: %u MHz", i, gpu->frequency);
} else {
FF_DEBUG("Adapter #%u does not report max graphics frequency", i);
}
break;
D3DKMT_QUERYSTATISTICS_MEMORY_USAGE* info = &queryStatistics.QueryResult.SegmentGroupUsageInformation;
uint64_t used = info->AllocatedBytes + info->ModifiedBytes + info->StandbyBytes;
uint64_t total = used + info->FreeBytes + info->ZeroBytes;
gpu->dedicated.used = used;
gpu->dedicated.total = total;
FF_DEBUG("Adapter #%u local memory usage: used=%" PRIu64 " total=%" PRIu64, i, used, total);
} else {
FF_DEBUG("Failed to query node performance data for adapter #%u node #%u: %s",
FF_DEBUG("D3DKMT_QUERYSTATISTICS_SEGMENT_GROUP_USAGE (LOCAL) failed for adapter #%u: %s",
i,
nodeIdx,
ffDebugNtStatus(status));
}
}
}
if (options->temp && gpu->temperature == FF_GPU_TEMP_UNSET) {
D3DKMT_ADAPTER_PERFDATA adapterPerfData = {
.PhysicalAdapterIndex = 0,
};
status = D3DKMTQueryAdapterInfo(&(D3DKMT_QUERYADAPTERINFO) {
.hAdapter = adapter->hAdapter,
.Type = KMTQAITYPE_ADAPTERPERFDATA,
.pPrivateDriverData = &adapterPerfData,
.PrivateDriverDataSize = sizeof(adapterPerfData),
});
if (NT_SUCCESS(status)) {
if (adapterPerfData.Temperature != 0) {
gpu->temperature = adapterPerfData.Temperature / 10.0;
FF_DEBUG("Adapter #%u temperature: %.1f°C", i, gpu->temperature);
queryStatistics.QuerySegmentGroupUsage.SegmentGroup = D3DKMT_MEMORY_SEGMENT_GROUP_NON_LOCAL;
status = D3DKMTQueryStatistics(&queryStatistics);
if (NT_SUCCESS(status)) {
D3DKMT_QUERYSTATISTICS_MEMORY_USAGE* info = &queryStatistics.QueryResult.SegmentGroupUsageInformation;
uint64_t used = info->AllocatedBytes + info->ModifiedBytes + info->StandbyBytes;
uint64_t total = used + info->FreeBytes + info->ZeroBytes;
gpu->shared.used = used;
gpu->shared.total = total;
FF_DEBUG("Adapter #%u non-local memory usage: used=%" PRIu64 " total=%" PRIu64, i, used, total);
} else {
FF_DEBUG("Adapter #%u does not report temperature data", i);
FF_DEBUG("D3DKMT_QUERYSTATISTICS_SEGMENT_GROUP_USAGE (NON_LOCAL) failed for adapter #%u: %s",
i,
ffDebugNtStatus(status));
}
} else {
FF_DEBUG("Failed to query temperature for adapter #%u: %s", i, ffDebugNtStatus(status));
// Supports basic segment (total) size query
D3DKMT_SEGMENTSIZEINFO segmentSizeInfo = {};
status = D3DKMTQueryAdapterInfo(&(D3DKMT_QUERYADAPTERINFO) {
.hAdapter = adapter->hAdapter,
.Type = KMTQAITYPE_GETSEGMENTSIZE,
.pPrivateDriverData = &segmentSizeInfo,
.PrivateDriverDataSize = sizeof(segmentSizeInfo),
});
if (NT_SUCCESS(status)) {
FF_DEBUG("Adapter #%u segment size - DedicatedVideoMemorySize: %" PRIu64
", DedicatedSystemMemorySize: %" PRIu64 ", SharedSystemMemorySize: %" PRIu64,
i,
(uint64_t) segmentSizeInfo.DedicatedVideoMemorySize,
(uint64_t) segmentSizeInfo.DedicatedSystemMemorySize,
(uint64_t) segmentSizeInfo.SharedSystemMemorySize);
gpu->dedicated.total = segmentSizeInfo.DedicatedVideoMemorySize;
gpu->shared.total = segmentSizeInfo.DedicatedSystemMemorySize + segmentSizeInfo.SharedSystemMemorySize;
} else {
FF_DEBUG("Failed to query segment size information for adapter #%u: %s", i, ffDebugNtStatus(status));
}
}
}
}
if (gpu->type == FF_GPU_TYPE_UNKNOWN) {
if (ffGPUDetectTypeByVendorAndName(gpu)) {
// OK
if (wddmVersion >= KMT_DRIVERVERSION_WDDM_2_4) {
if (gpu->frequency == FF_GPU_FREQUENCY_UNSET) {
for (uint32_t nodeIdx = 0;; nodeIdx++) {
D3DKMT_NODEMETADATA nodeMetadata = {
.NodeOrdinalAndAdapterIndex = (0 << 16) | nodeIdx,
};
status = D3DKMTQueryAdapterInfo(&(D3DKMT_QUERYADAPTERINFO) {
.hAdapter = adapter->hAdapter,
.Type = KMTQAITYPE_NODEMETADATA,
.pPrivateDriverData = &nodeMetadata,
.PrivateDriverDataSize = sizeof(nodeMetadata),
});
if (!NT_SUCCESS(status)) { break; }
if (nodeMetadata.NodeData.EngineType != DXGK_ENGINE_TYPE_3D) { continue; }
D3DKMT_NODE_PERFDATA nodePerfData = {
.NodeOrdinal = nodeIdx,
.PhysicalAdapterIndex = 0,
};
status = D3DKMTQueryAdapterInfo(&(D3DKMT_QUERYADAPTERINFO) {
.hAdapter = adapter->hAdapter,
.Type = KMTQAITYPE_NODEPERFDATA,
.pPrivateDriverData = &nodePerfData,
.PrivateDriverDataSize = sizeof(nodePerfData),
});
if (NT_SUCCESS(status)) {
if (nodePerfData.MaxFrequency != 0) {
gpu->frequency = (uint32_t) (nodePerfData.MaxFrequency / 1000 / 1000);
FF_DEBUG("Adapter #%u max graphics frequency: %u MHz", i, gpu->frequency);
} else {
FF_DEBUG("Adapter #%u does not report max graphics frequency", i);
}
break;
} else {
FF_DEBUG("Failed to query node performance data for adapter #%u node #%u: %s",
i,
nodeIdx,
ffDebugNtStatus(status));
}
}
}
if (options->temp && gpu->temperature == FF_GPU_TEMP_UNSET) {
D3DKMT_ADAPTER_PERFDATA adapterPerfData = {
.PhysicalAdapterIndex = 0,
};
status = D3DKMTQueryAdapterInfo(&(D3DKMT_QUERYADAPTERINFO) {
.hAdapter = adapter->hAdapter,
.Type = KMTQAITYPE_ADAPTERPERFDATA,
.pPrivateDriverData = &adapterPerfData,
.PrivateDriverDataSize = sizeof(adapterPerfData),
});
if (NT_SUCCESS(status)) {
if (adapterPerfData.Temperature != 0) {
gpu->temperature = adapterPerfData.Temperature / 10.0;
FF_DEBUG("Adapter #%u temperature: %.1f°C", i, gpu->temperature);
} else {
FF_DEBUG("Adapter #%u does not report temperature data", i);
}
} else {
FF_DEBUG("Failed to query temperature for adapter #%u: %s", i, ffDebugNtStatus(status));
}
}
}
#if _WIN32
else if (ffIsWindows10OrGreater()) {
const char* ffGPUDetectTypeWithDXCore(LUID adapterLuid, FFGPUResult * gpu);
[[maybe_unused]] const char* error = ffGPUDetectTypeWithDXCore(adapter->AdapterLuid, gpu);
FF_DEBUG("DXCore GPU type detection result: %s", error ?: "Success");
}
#endif
else {
FF_DEBUG("Unable to determine GPU type by any method for this adapter");
if (gpu->type == FF_GPU_TYPE_UNKNOWN) {
if (ffGPUDetectTypeByVendorAndName(gpu)) {
// OK
}
#if _WIN32
else if (ffIsWindows10OrGreater()) {
const char* ffGPUDetectTypeWithDXCore(LUID adapterLuid, FFGPUResult * gpu);
[[maybe_unused]] const char* error = ffGPUDetectTypeWithDXCore(adapter->AdapterLuid, gpu);
FF_DEBUG("DXCore GPU type detection result: %s", error ?: "Success");
}
#endif
else {
FF_DEBUG("Unable to determine GPU type by any method for this adapter");
}
}
}