Add Debug Marker Functionality for D3D12

Implemention of InsertDebugMarker, PushDebugGroup and PopDebugGroup
for D3D12 using PIX event runtime.

Bug: dawn:44
Change-Id: I488f4638777afad3420ba96b350d9f19f2cd80dc
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/7400
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Commit-Queue: Brandon Jones <brandon1.jones@intel.com>
diff --git a/docs/DebugMarkers.md b/docs/DebugMarkers.md
index b84024c..da11dfa 100644
--- a/docs/DebugMarkers.md
+++ b/docs/DebugMarkers.md
@@ -17,7 +17,17 @@
 

 ## D3D12

 

-Debug markers are currently unimplemented on D3D12 pending resolution of a licensing issue.

+Debug markers on D3D12 are implemented with the [PIX Event Runtime](https://blogs.msdn.microsoft.com/pix/winpixeventruntime/).

+

+To enable marker functionality, you must:

+1. Click the download link on https://www.nuget.org/packages/WinPixEventRuntime

+2. Rename the .nupkg file to a .zip extension, then extract its contents.

+3. Copy `bin\WinPixEventRuntime.dll` into the same directory as `libdawn_native.dll`.

+4. Launch your application.

+

+You may now call the debug marker APIs mentioned above and see them from your GPU debugging tool. When using your tool, it is supported to both launch your application with the debugger attached, or attach the debugger while your application is running.

+

+D3D12 debug markers have been tested with [Microsoft PIX](https://blogs.msdn.microsoft.com/pix/download/) and [Intel Graphics Frame Analyzer](https://software.intel.com/en-us/gpa/graphics-frame-analyzer).

 

 ## Vulkan

 

diff --git a/src/dawn_native/d3d12/CommandBufferD3D12.cpp b/src/dawn_native/d3d12/CommandBufferD3D12.cpp
index f6fd304..18e6d1c 100644
--- a/src/dawn_native/d3d12/CommandBufferD3D12.cpp
+++ b/src/dawn_native/d3d12/CommandBufferD3D12.cpp
@@ -24,6 +24,7 @@
 #include "dawn_native/d3d12/DescriptorHeapAllocator.h"
 #include "dawn_native/d3d12/DeviceD3D12.h"
 #include "dawn_native/d3d12/PipelineLayoutD3D12.h"
+#include "dawn_native/d3d12/PlatformFunctions.h"
 #include "dawn_native/d3d12/RenderPipelineD3D12.h"
 #include "dawn_native/d3d12/ResourceAllocator.h"
 #include "dawn_native/d3d12/SamplerD3D12.h"
@@ -802,12 +803,40 @@
                                                       draw->firstInstance);
                 } break;
 
-                case Command::InsertDebugMarker:
-                case Command::PopDebugGroup:
+                case Command::InsertDebugMarker: {
+                    InsertDebugMarkerCmd* cmd = mCommands.NextCommand<InsertDebugMarkerCmd>();
+                    const char* label = mCommands.NextData<char>(cmd->length + 1);
+
+                    if (ToBackend(GetDevice())->GetFunctions()->isPIXEventRuntimeLoaded()) {
+                        // PIX color is 1 byte per channel in ARGB format
+                        constexpr uint64_t kPIXBlackColor = 0xff000000;
+                        ToBackend(GetDevice())
+                            ->GetFunctions()
+                            ->pixSetMarkerOnCommandList(commandList.Get(), kPIXBlackColor, label);
+                    }
+                } break;
+
+                case Command::PopDebugGroup: {
+                    mCommands.NextCommand<PopDebugGroupCmd>();
+
+                    if (ToBackend(GetDevice())->GetFunctions()->isPIXEventRuntimeLoaded()) {
+                        ToBackend(GetDevice())
+                            ->GetFunctions()
+                            ->pixEndEventOnCommandList(commandList.Get());
+                    }
+                } break;
+
                 case Command::PushDebugGroup: {
-                    // TODO(brandon1.jones@intel.com): Implement debug markers after PIX licensing
-                    // issue is resolved.
-                    SkipCommand(&mCommands, type);
+                    PushDebugGroupCmd* cmd = mCommands.NextCommand<PushDebugGroupCmd>();
+                    const char* label = mCommands.NextData<char>(cmd->length + 1);
+
+                    if (ToBackend(GetDevice())->GetFunctions()->isPIXEventRuntimeLoaded()) {
+                        // PIX color is 1 byte per channel in ARGB format
+                        constexpr uint64_t kPIXBlackColor = 0xff000000;
+                        ToBackend(GetDevice())
+                            ->GetFunctions()
+                            ->pixBeginEventOnCommandList(commandList.Get(), kPIXBlackColor, label);
+                    }
                 } break;
 
                 case Command::SetRenderPipeline: {
diff --git a/src/dawn_native/d3d12/PlatformFunctions.cpp b/src/dawn_native/d3d12/PlatformFunctions.cpp
index 5834a2a..d913736 100644
--- a/src/dawn_native/d3d12/PlatformFunctions.cpp
+++ b/src/dawn_native/d3d12/PlatformFunctions.cpp
@@ -27,7 +27,7 @@
         DAWN_TRY(LoadD3D12());
         DAWN_TRY(LoadDXGI());
         DAWN_TRY(LoadD3DCompiler());
-
+        LoadPIXRuntime();
         return {};
     }
 
@@ -71,4 +71,18 @@
         return {};
     }
 
+    bool PlatformFunctions::isPIXEventRuntimeLoaded() const {
+        return mPIXEventRuntimeLib.Valid();
+    }
+
+    void PlatformFunctions::LoadPIXRuntime() {
+        if (!mPIXEventRuntimeLib.Open("WinPixEventRuntime.dll") ||
+            !mPIXEventRuntimeLib.GetProc(&pixBeginEventOnCommandList,
+                                         "PIXBeginEventOnCommandList") ||
+            !mPIXEventRuntimeLib.GetProc(&pixEndEventOnCommandList, "PIXEndEventOnCommandList") ||
+            !mPIXEventRuntimeLib.GetProc(&pixSetMarkerOnCommandList, "PIXSetMarkerOnCommandList")) {
+            mPIXEventRuntimeLib.Close();
+        }
+    }
+
 }}  // namespace dawn_native::d3d12
diff --git a/src/dawn_native/d3d12/PlatformFunctions.h b/src/dawn_native/d3d12/PlatformFunctions.h
index 710fb84..ec51ba1 100644
--- a/src/dawn_native/d3d12/PlatformFunctions.h
+++ b/src/dawn_native/d3d12/PlatformFunctions.h
@@ -35,6 +35,7 @@
         ~PlatformFunctions();
 
         MaybeError LoadFunctions();
+        bool isPIXEventRuntimeLoaded() const;
 
         // Functions from d3d12.dll
         PFN_D3D12_CREATE_DEVICE d3d12CreateDevice = nullptr;
@@ -60,14 +61,32 @@
         // Functions from d3d3compiler.dll
         pD3DCompile d3dCompile = nullptr;
 
+        // Functions from WinPixEventRuntime.dll
+        using PFN_PIX_END_EVENT_ON_COMMAND_LIST =
+            HRESULT(WINAPI*)(ID3D12GraphicsCommandList* commandList);
+
+        PFN_PIX_END_EVENT_ON_COMMAND_LIST pixEndEventOnCommandList = nullptr;
+
+        using PFN_PIX_BEGIN_EVENT_ON_COMMAND_LIST = HRESULT(
+            WINAPI*)(ID3D12GraphicsCommandList* commandList, UINT64 color, _In_ PCSTR formatString);
+
+        PFN_PIX_BEGIN_EVENT_ON_COMMAND_LIST pixBeginEventOnCommandList = nullptr;
+
+        using PFN_SET_MARKER_ON_COMMAND_LIST = HRESULT(
+            WINAPI*)(ID3D12GraphicsCommandList* commandList, UINT64 color, _In_ PCSTR formatString);
+
+        PFN_SET_MARKER_ON_COMMAND_LIST pixSetMarkerOnCommandList = nullptr;
+
       private:
         MaybeError LoadD3D12();
         MaybeError LoadDXGI();
         MaybeError LoadD3DCompiler();
+        void LoadPIXRuntime();
 
         DynamicLib mD3D12Lib;
         DynamicLib mDXGILib;
         DynamicLib mD3DCompilerLib;
+        DynamicLib mPIXEventRuntimeLib;
     };
 
 }}  // namespace dawn_native::d3d12