File sc_gpumem.patch of Package wine
From 07b2f01b9b5721c502bee6fb4d6397756a51b376 Mon Sep 17 00:00:00 2001
From: Vingian <89702391+Vingian@users.noreply.github.com>
Date: Sat, 10 Jan 2026 06:08:52 -0300
Subject: [PATCH] D3DKMT: Minimal query implementation
A minimal implementation... just to satisfy the way SC monitors GPU memory.
---
dlls/win32u/d3dkmt.c | 126 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 126 insertions(+)
diff --git a/dlls/win32u/d3dkmt.c b/dlls/win32u/d3dkmt.c
index baf99f69291..573803c1de1 100644
--- a/dlls/win32u/d3dkmt.c
+++ b/dlls/win32u/d3dkmt.c
@@ -711,6 +711,22 @@ NTSTATUS WINAPI NtGdiDdDDIQueryAdapterInfo( D3DKMT_QUERYADAPTERINFO *desc )
*value = KMT_DRIVERVERSION_WDDM_1_3;
return STATUS_SUCCESS;
}
+ case KMTQAITYPE_NODEMETADATA:
+ {
+ struct d3dkmt_adapter *adapter;
+ UINT *node = desc->pPrivateDriverData;
+
+ if (desc->PrivateDriverDataSize < sizeof(*node) * 2)
+ return STATUS_INVALID_PARAMETER;
+
+ if (!(adapter = get_d3dkmt_object( desc->hAdapter, D3DKMT_ADAPTER )) || !adapter->physical_device)
+ return STATUS_NOT_IMPLEMENTED;
+
+ memset( node, 0, desc->PrivateDriverDataSize );
+ //NodeData.EngineType = DXGK_ENGINE_TYPE_3D
+ node[1] = 1;
+ return STATUS_SUCCESS;
+ }
default:
{
FIXME( "type %d not handled.\n", desc->Type );
@@ -719,12 +735,122 @@ NTSTATUS WINAPI NtGdiDdDDIQueryAdapterInfo( D3DKMT_QUERYADAPTERINFO *desc )
}
}
+static __thread struct {
+ LUID AdapterLuid;
+ DWORD time;
+ ULONGLONG total[2];
+ ULONGLONG budget[2];
+ ULONGLONG usage[2];
+} g_GPUMemCache = {0};
+
+static inline ULONGLONG GPUMem( LUID AdapterLuid, ULONGLONG *budget, ULONGLONG *usage, BOOL shared )
+{
+ DWORD now = NtGetTickCount();
+ BOOL use_cache = FALSE;
+ int idx_target = shared ? 1 : 0;
+
+ if (g_GPUMemCache.AdapterLuid.LowPart == AdapterLuid.LowPart && g_GPUMemCache.AdapterLuid.HighPart == AdapterLuid.HighPart)
+ if (now - g_GPUMemCache.time < 123)
+ use_cache = TRUE;
+
+ if (!use_cache)
+ {
+ D3DKMT_OPENADAPTERFROMLUID adp = { .AdapterLuid = AdapterLuid };
+ struct d3dkmt_adapter *adapter;
+ struct vulkan_physical_device *physical_device;
+
+ g_GPUMemCache.AdapterLuid = AdapterLuid;
+ g_GPUMemCache.time = now;
+ g_GPUMemCache.total[0] = g_GPUMemCache.total[1] = 0;
+ g_GPUMemCache.budget[0] = g_GPUMemCache.budget[1] = 0;
+ g_GPUMemCache.usage[0] = g_GPUMemCache.usage[1] = 0;
+
+ if (NtGdiDdDDIOpenAdapterFromLuid( &adp ) == STATUS_SUCCESS)
+ {
+ if ((adapter = get_d3dkmt_object( adp.hAdapter, D3DKMT_ADAPTER )) && (physical_device = adapter->physical_device))
+ {
+ struct vulkan_instance *instance = physical_device->instance;
+ VkPhysicalDeviceMemoryBudgetPropertiesEXT mem_budget = {0};
+ VkPhysicalDeviceMemoryProperties2 mem_prop2 = {0};
+
+ mem_budget.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT;
+ mem_prop2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2;
+ mem_prop2.pNext = &mem_budget;
+
+ instance->p_vkGetPhysicalDeviceMemoryProperties2KHR( physical_device->host.physical_device, &mem_prop2 );
+ for (UINT i = 0; i < mem_prop2.memoryProperties.memoryHeapCount; ++i)
+ {
+ int idx = (mem_prop2.memoryProperties.memoryHeaps[i].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) ? 0 : 1;
+ ULONGLONG s = mem_prop2.memoryProperties.memoryHeaps[i].size;
+ ULONGLONG b = min( s, mem_budget.heapBudget[i] );
+ ULONGLONG u = min( b, mem_budget.heapUsage[i] );
+ g_GPUMemCache.total[idx] += s;
+ g_GPUMemCache.budget[idx] += b;
+ g_GPUMemCache.usage[idx] += u;
+ }
+ }
+ NtGdiDdDDICloseAdapter( &(D3DKMT_CLOSEADAPTER){ .hAdapter = adp.hAdapter } );
+ }
+ }
+
+ if (budget) *budget = g_GPUMemCache.budget[idx_target];
+ if (usage) *usage = g_GPUMemCache.usage[idx_target];
+ return g_GPUMemCache.total[idx_target];
+}
+
/******************************************************************************
* NtGdiDdDDIQueryStatistics (win32u.@)
*/
NTSTATUS WINAPI NtGdiDdDDIQueryStatistics( D3DKMT_QUERYSTATISTICS *stats )
{
FIXME( "(%p): stub\n", stats );
+ switch ((int)stats->Type)
+ {
+ case D3DKMT_QUERYSTATISTICS_ADAPTER:
+ {
+ D3DKMT_OPENADAPTERFROMLUID adp = { .AdapterLuid = stats->AdapterLuid };
+ memset( &stats->QueryResult, 0, sizeof(stats->QueryResult) );
+ if (NtGdiDdDDIOpenAdapterFromLuid( &adp ) == STATUS_SUCCESS)
+ {
+ stats->QueryResult.AdapterInformation.NbSegments = 2;
+ stats->QueryResult.AdapterInformation.NodeCount = 1;
+ g_GPUMemCache.AdapterLuid.LowPart = g_GPUMemCache.AdapterLuid.HighPart = 0;
+ NtGdiDdDDICloseAdapter( &(D3DKMT_CLOSEADAPTER){ .hAdapter = adp.hAdapter } );
+ }
+ break;
+ }
+ case D3DKMT_QUERYSTATISTICS_SEGMENT:
+ case D3DKMT_QUERYSTATISTICS_PROCESS_SEGMENT:
+ case /*D3DKMT_QUERYSTATISTICS_PROCESS_SEGMENT_GROUP*/0x09:
+ {
+ ULONGLONG total, budget, usage;
+ ULONG aperture = !!stats->QueryProcessSegment.SegmentId;
+
+ memset( &stats->QueryResult, 0, sizeof(stats->QueryResult) );
+ if (stats->QuerySegment.SegmentId > 1) break;
+ if (stats->Type >= D3DKMT_QUERYSTATISTICS_PROCESS_SEGMENT)
+ if (stats->hProcess && stats->hProcess != GetCurrentProcess())
+ break;
+
+ if ((total = GPUMem( stats->AdapterLuid, &budget, &usage, aperture )) > 0)
+ {
+ if (stats->Type == D3DKMT_QUERYSTATISTICS_SEGMENT) {
+ stats->QueryResult.SegmentInformation.CommitLimit = total;
+ stats->QueryResult.SegmentInformation.BytesCommitted = total - budget + usage;
+ stats->QueryResult.SegmentInformation.BytesResident = stats->QueryResult.SegmentInformation.BytesCommitted;
+ stats->QueryResult.SegmentInformation.Aperture = aperture;
+ } else if (stats->Type == D3DKMT_QUERYSTATISTICS_PROCESS_SEGMENT) {
+ stats->QueryResult.ProcessSegmentInformation.BytesCommitted = usage;
+ } else {
+ //ProcessSegmentGroupInformation.Budget
+ stats->QueryResult.ProcessSegmentInformation.BytesCommitted = budget;
+ //ProcessSegmentGroupInformation.Usage
+ stats->QueryResult.ProcessSegmentInformation.MinimumWorkingSet = usage;
+ }
+ }
+ break;
+ }
+ }
return STATUS_SUCCESS;
}