D3D12: Add a toggle to use DXC for HLSL compilation

This patch adds the following:
 - UseDXC toggle
 - Loads DXC (and DXIL to sign the DXBC) in d3d12/PlatformFunctions
 - Adds GetModuleDirectory to SystemUtils

GetModuleDirectory was added to prevent loading issues regarding dynamic libraries when the executable is not in the same path as the dawn module.

This patch doesn't add DXC to RenderPipelineD3D12 nor ComputePipelineD3D12.

Bug: dawn:402
Change-Id: I2b8e4a2b7df31b9c766c748f92e11050c0aec3a0
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/21420
Commit-Queue: Felix Maier <xilefmai@gmail.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/dawn_native/Toggles.cpp b/src/dawn_native/Toggles.cpp
index eb6ecd7..b4ec0db 100644
--- a/src/dawn_native/Toggles.cpp
+++ b/src/dawn_native/Toggles.cpp
@@ -128,6 +128,9 @@
               "Enable use of a small D3D12 shader visible heap, instead of using a large one by "
               "default. This setting is used to test bindgroup encoding.",
               "https://crbug.com/dawn/155"}},
+            {Toggle::UseDXC,
+             {"use_dxc", "Use DXC instead of FXC for compiling HLSL",
+              "https://crbug.com/dawn/402"}},
         }};
 
     }  // anonymous namespace
diff --git a/src/dawn_native/Toggles.h b/src/dawn_native/Toggles.h
index 93a31d2..18a26a9 100644
--- a/src/dawn_native/Toggles.h
+++ b/src/dawn_native/Toggles.h
@@ -41,6 +41,7 @@
         DisableBaseVertex,
         DisableBaseInstance,
         UseD3D12SmallShaderVisibleHeapForTesting,
+        UseDXC,
 
         EnumCount,
         InvalidEnum = EnumCount,
diff --git a/src/dawn_native/d3d12/DeviceD3D12.cpp b/src/dawn_native/d3d12/DeviceD3D12.cpp
index ce935a9..cfd109f 100644
--- a/src/dawn_native/d3d12/DeviceD3D12.cpp
+++ b/src/dawn_native/d3d12/DeviceD3D12.cpp
@@ -432,6 +432,7 @@
         SetToggle(Toggle::UseD3D12ResourceHeapTier2, useResourceHeapTier2);
         SetToggle(Toggle::UseD3D12RenderPass, GetDeviceInfo().supportsRenderPass);
         SetToggle(Toggle::UseD3D12ResidencyManagement, true);
+        SetToggle(Toggle::UseDXC, false);
 
         // By default use the maximum shader-visible heap size allowed.
         SetToggle(Toggle::UseD3D12SmallShaderVisibleHeapForTesting, false);
diff --git a/src/dawn_native/d3d12/PlatformFunctions.cpp b/src/dawn_native/d3d12/PlatformFunctions.cpp
index 1e79f88..467e8d7 100644
--- a/src/dawn_native/d3d12/PlatformFunctions.cpp
+++ b/src/dawn_native/d3d12/PlatformFunctions.cpp
@@ -26,7 +26,9 @@
     MaybeError PlatformFunctions::LoadFunctions() {
         DAWN_TRY(LoadD3D12());
         DAWN_TRY(LoadDXGI());
-        DAWN_TRY(LoadD3DCompiler());
+        LoadDXIL();
+        LoadDXCompiler();
+        DAWN_TRY(LoadFXCompiler());
         DAWN_TRY(LoadD3D11());
         LoadPIXRuntime();
         return {};
@@ -72,10 +74,24 @@
         return {};
     }
 
-    MaybeError PlatformFunctions::LoadD3DCompiler() {
+    void PlatformFunctions::LoadDXIL() {
+        if (!mDXILLib.Open("dxil.dll", nullptr)) {
+            mDXILLib.Close();
+        }
+    }
+
+    void PlatformFunctions::LoadDXCompiler() {
+        // DXIL must be loaded before DXC, otherwise shader signing is unavailable
+        if (!mDXCompilerLib.Open("dxcompiler.dll", nullptr) ||
+            !mDXCompilerLib.GetProc(&dxcCreateInstance, "DxcCreateInstance", nullptr)) {
+            mDXCompilerLib.Close();
+        }
+    }
+
+    MaybeError PlatformFunctions::LoadFXCompiler() {
         std::string error;
-        if (!mD3DCompilerLib.Open("d3dcompiler_47.dll", &error) ||
-            !mD3DCompilerLib.GetProc(&d3dCompile, "D3DCompile", &error)) {
+        if (!mFXCompilerLib.Open("d3dcompiler_47.dll", &error) ||
+            !mFXCompilerLib.GetProc(&d3dCompile, "D3DCompile", &error)) {
             return DAWN_INTERNAL_ERROR(error.c_str());
         }
 
@@ -86,6 +102,10 @@
         return mPIXEventRuntimeLib.Valid();
     }
 
+    bool PlatformFunctions::IsDXCAvailable() const {
+        return mDXILLib.Valid() && mDXCompilerLib.Valid();
+    }
+
     void PlatformFunctions::LoadPIXRuntime() {
         if (!mPIXEventRuntimeLib.Open("WinPixEventRuntime.dll") ||
             !mPIXEventRuntimeLib.GetProc(&pixBeginEventOnCommandList,
diff --git a/src/dawn_native/d3d12/PlatformFunctions.h b/src/dawn_native/d3d12/PlatformFunctions.h
index a9e85d2..7b2851b 100644
--- a/src/dawn_native/d3d12/PlatformFunctions.h
+++ b/src/dawn_native/d3d12/PlatformFunctions.h
@@ -36,6 +36,7 @@
 
         MaybeError LoadFunctions();
         bool IsPIXEventRuntimeLoaded() const;
+        bool IsDXCAvailable() const;
 
         // Functions from d3d12.dll
         PFN_D3D12_CREATE_DEVICE d3d12CreateDevice = nullptr;
@@ -58,6 +59,12 @@
                                                           _COM_Outptr_ void** ppFactory);
         PFN_CREATE_DXGI_FACTORY2 createDxgiFactory2 = nullptr;
 
+        // Functions from dxcompiler.dll
+        using PFN_DXC_CREATE_INSTANCE = HRESULT(WINAPI*)(REFCLSID rclsid,
+                                                         REFIID riid,
+                                                         _COM_Outptr_ void** ppCompiler);
+        PFN_DXC_CREATE_INSTANCE dxcCreateInstance = nullptr;
+
         // Functions from d3d3compiler.dll
         pD3DCompile d3dCompile = nullptr;
 
@@ -84,13 +91,17 @@
         MaybeError LoadD3D12();
         MaybeError LoadD3D11();
         MaybeError LoadDXGI();
-        MaybeError LoadD3DCompiler();
+        void LoadDXIL();
+        void LoadDXCompiler();
+        MaybeError LoadFXCompiler();
         void LoadPIXRuntime();
 
         DynamicLib mD3D12Lib;
         DynamicLib mD3D11Lib;
         DynamicLib mDXGILib;
-        DynamicLib mD3DCompilerLib;
+        DynamicLib mDXILLib;
+        DynamicLib mDXCompilerLib;
+        DynamicLib mFXCompilerLib;
         DynamicLib mPIXEventRuntimeLib;
     };