Quellcode durchsuchen

GPU: D3D12: Use WinPixEventRuntime.dll for debug events when available.

This prevents validation errors on D3D12 caused by the previous solution
that utilized internal D3D12 functions. This is still the fallback
solution if WinPixEventRuntime.dll is not present in PATH, but a warning
is printed to let you know how to fix these errors.
Jakub Wasilewski vor 1 Woche
Ursprung
Commit
0c5ad99ec1
1 geänderte Dateien mit 95 neuen und 2 gelöschten Zeilen
  1. 95 2
      src/gpu/d3d12/SDL_gpu_d3d12.c

+ 95 - 2
src/gpu/d3d12/SDL_gpu_d3d12.c

@@ -90,6 +90,8 @@
 #define D3D12_DLL "d3d12_x.dll"
 #else
 #define D3D12_DLL "d3d12.dll"
+#define USE_PIX_RUNTIME
+#define WINPIXEVENTRUNTIME_DLL "WinPixEventRuntime.dll"
 #endif
 #define DXGI_DLL      "dxgi.dll"
 #define DXGIDEBUG_DLL "dxgidebug.dll"
@@ -131,6 +133,15 @@
 typedef HRESULT (WINAPI *pfnCreateDXGIFactory1)(const GUID *riid, void **ppFactory);
 typedef HRESULT (WINAPI *pfnDXGIGetDebugInterface)(const GUID *riid, void **ppDebug);
 
+#ifdef USE_PIX_RUNTIME
+#define PIX_BEGIN_EVENT_ON_COMMAND_LIST_FUNC "PIXBeginEventOnCommandList"
+#define PIX_END_EVENT_ON_COMMAND_LIST_FUNC "PIXEndEventOnCommandList"
+#define PIX_SET_MARKER_ON_COMMAND_LIST_FUNC "PIXSetMarkerOnCommandList"
+typedef void(WINAPI* pfnBeginEventOnCommandList)(ID3D12GraphicsCommandList* commandList, UINT64 color, _In_ PCSTR formatString);
+typedef void(WINAPI* pfnEndEventOnCommandList)(ID3D12GraphicsCommandList* commandList);
+typedef void(WINAPI* pfnSetMarkerOnCommandList)(ID3D12GraphicsCommandList* commandList, UINT64 color, _In_ PCSTR formatString);
+#endif
+
 // IIDs (from https://www.magnumdb.com/)
 static const IID D3D_IID_IDXGIFactory1 = { 0x770aae78, 0xf26f, 0x4dba, { 0xa8, 0x29, 0x25, 0x3c, 0x83, 0xd1, 0xb3, 0x87 } };
 static const IID D3D_IID_IDXGIFactory4 = { 0x1bc6ea02, 0xef36, 0x464f, { 0xbf, 0x0c, 0x21, 0xca, 0x39, 0xe5, 0x16, 0x8a } };
@@ -844,6 +855,14 @@ typedef struct D3D12PresentData
     Uint32 swapchainImageIndex;
 } D3D12PresentData;
 
+#ifdef USE_PIX_RUNTIME
+typedef struct WinPixEventRuntimeFns {
+    pfnBeginEventOnCommandList pBeginEventOnCommandList;
+    pfnEndEventOnCommandList pEndEventOnCommandList;
+    pfnSetMarkerOnCommandList pSetMarkerOnCommandList;
+} WinPixEventRuntimeFns;
+#endif
+
 struct D3D12Renderer
 {
     // Reference to the parent device
@@ -858,6 +877,10 @@ struct D3D12Renderer
     IDXGIAdapter1 *adapter;
     SDL_SharedObject *dxgi_dll;
     SDL_SharedObject *dxgidebug_dll;
+#endif
+#ifdef USE_PIX_RUNTIME
+    SDL_SharedObject *winpixeventruntime_dll;
+    WinPixEventRuntimeFns winpixeventruntimeFns;
 #endif
     ID3D12Debug *d3d12Debug;
     BOOL supportsTearing;
@@ -1700,6 +1723,12 @@ static void D3D12_INTERNAL_DestroyRenderer(D3D12Renderer *renderer)
         SDL_UnloadObject(renderer->dxgidebug_dll);
         renderer->dxgidebug_dll = NULL;
     }
+#endif
+#ifdef USE_PIX_RUNTIME
+    if (renderer->winpixeventruntime_dll) {
+        SDL_UnloadObject(renderer->winpixeventruntime_dll);
+        renderer->winpixeventruntime_dll = NULL;
+    }
 #endif
     renderer->pD3D12SerializeRootSignature = NULL;
 
@@ -2104,8 +2133,9 @@ static void D3D12_SetTextureName(
     }
 }
 
-/* These debug functions are all marked as "for internal usage only"
- * on D3D12... works on renderdoc!
+/* These debug functions now use the PIX runtime where available to avoid validation
+ * layer errors. They still fall back to calling internal functions if WinPixEventRuntime.dll
+ * is not present, but with a warning explaining the problem.
  */
 
 static void D3D12_InsertDebugLabel(
@@ -2113,6 +2143,19 @@ static void D3D12_InsertDebugLabel(
     const char *text)
 {
     D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
+#ifdef USE_PIX_RUNTIME
+    // Prefer using PIX runtime, but fallthrough with a warning if not available.
+    WinPixEventRuntimeFns *fns = &d3d12CommandBuffer->renderer->winpixeventruntimeFns;
+    if (fns->pSetMarkerOnCommandList) {
+        fns->pSetMarkerOnCommandList(d3d12CommandBuffer->graphicsCommandList, 0 /*default color*/, text);
+        return;
+    } else {
+        SDL_LogWarn(SDL_LOG_CATEGORY_GPU,
+            "WinPixEventRuntime.dll needs to be in your PATH for debug functions like SDL_Push/PopGPUDebugGroup() and SDL_InsertGPUDebugLabel() to function correctly. "
+            "Otherwise, these functions will cause D3D12 validation errors. "
+            "See https://devblogs.microsoft.com/pix/winpixeventruntime/ for information on obtaining the DLL.");
+    }
+#endif
     WCHAR *wchar_text = WIN_UTF8ToStringW(text);
 
     ID3D12GraphicsCommandList_SetMarker(
@@ -2129,6 +2172,19 @@ static void D3D12_PushDebugGroup(
     const char *name)
 {
     D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
+#ifdef USE_PIX_RUNTIME
+    // Prefer using PIX runtime, but fallthrough with a warning if not available.
+    WinPixEventRuntimeFns *fns = &d3d12CommandBuffer->renderer->winpixeventruntimeFns;
+    if (fns->pBeginEventOnCommandList) {
+        fns->pBeginEventOnCommandList(d3d12CommandBuffer->graphicsCommandList, 0 /*default color*/, name);
+        return;
+    } else {
+        SDL_LogWarn(SDL_LOG_CATEGORY_GPU,
+            "WinPixEventRuntime.dll needs to be in your PATH for debug functions like SDL_Push/PopGPUDebugGroup() and SDL_InsertGPUDebugLabel() to function correctly. "
+            "Otherwise, these functions will cause D3D12 validation errors. "
+            "See https://devblogs.microsoft.com/pix/winpixeventruntime/ for information on obtaining the DLL.");
+    }
+#endif
     WCHAR *wchar_text = WIN_UTF8ToStringW(name);
 
     ID3D12GraphicsCommandList_BeginEvent(
@@ -2144,6 +2200,19 @@ static void D3D12_PopDebugGroup(
     SDL_GPUCommandBuffer *commandBuffer)
 {
     D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
+#ifdef USE_PIX_RUNTIME
+    // Prefer using PIX runtime, but fallthrough with a warning if not available.
+    WinPixEventRuntimeFns *fns = &d3d12CommandBuffer->renderer->winpixeventruntimeFns;
+    if (fns->pEndEventOnCommandList) {
+        fns->pEndEventOnCommandList(d3d12CommandBuffer->graphicsCommandList);
+        return;
+    } else {
+        SDL_LogWarn(SDL_LOG_CATEGORY_GPU,
+            "WinPixEventRuntime.dll needs to be in your PATH for debug functions like SDL_Push/PopGPUDebugGroup() and SDL_InsertGPUDebugLabel() to function correctly. "
+            "Otherwise, these functions will cause D3D12 validation errors. "
+            "See https://devblogs.microsoft.com/pix/winpixeventruntime/ for information on obtaining the DLL.");
+    }
+#endif
     ID3D12GraphicsCommandList_EndEvent(d3d12CommandBuffer->graphicsCommandList);
 }
 
@@ -8796,6 +8865,30 @@ static SDL_GPUDevice *D3D12_CreateDevice(bool debugMode, bool preferLowPower, SD
     hasDxgiDebug = true;
 #endif
 
+#ifdef USE_PIX_RUNTIME
+    // Load the PIX runtime DLL so that we can set D3D12 debug events on command lists
+    renderer->winpixeventruntime_dll = SDL_LoadObject(WINPIXEVENTRUNTIME_DLL);
+    WinPixEventRuntimeFns *fns = &renderer->winpixeventruntimeFns;
+    if (renderer->winpixeventruntime_dll) {
+        // Load the specific functions we need from the PIX runtime
+        fns->pBeginEventOnCommandList = (pfnBeginEventOnCommandList)SDL_LoadFunction(
+            renderer->winpixeventruntime_dll,
+            PIX_BEGIN_EVENT_ON_COMMAND_LIST_FUNC);
+        fns->pEndEventOnCommandList = (pfnEndEventOnCommandList)SDL_LoadFunction(
+            renderer->winpixeventruntime_dll,
+            PIX_END_EVENT_ON_COMMAND_LIST_FUNC);
+        fns->pSetMarkerOnCommandList = (pfnSetMarkerOnCommandList)SDL_LoadFunction(
+            renderer->winpixeventruntime_dll,
+            PIX_SET_MARKER_ON_COMMAND_LIST_FUNC);
+    } else {
+        // Not having the PIX runtime is not a critical error itself, but will cause warnings
+        // when using SDL_Push/PopGPUDebugGroup and SDL_InsertGPUDebugLabel.
+        fns->pBeginEventOnCommandList = NULL;
+        fns->pEndEventOnCommandList = NULL;
+        fns->pSetMarkerOnCommandList = NULL;
+    }
+#endif
+
     // Load the CreateDXGIFactory1 function
     pCreateDXGIFactory1 = (pfnCreateDXGIFactory1)SDL_LoadFunction(
         renderer->dxgi_dll,