D3D12: Dynamically load D3D12, DXGI and D3DCompiler

Linking against their .lib makes loading Dawn fail on systems that don't
have the DLLs. This happens for example on Windows7 that doesn't have
d3d12.dll. Instead we dynamically load functions pointers from these
DLLs at d3d12::Device startup.

Change-Id: I4d01a12d0f91bec45bf125450d2c08aaa9ff9fac
diff --git a/BUILD.gn b/BUILD.gn
index 737d905..fad9992 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -389,12 +389,7 @@
   ]
 
   if (dawn_enable_d3d12) {
-    libs += [
-      "d3d12.lib",
-      "dxgi.lib",
-      "dxguid.lib",
-      "d3dcompiler.lib",
-    ]
+    libs += [ "dxguid.lib" ]
     sources += [
       "src/dawn_native/d3d12/BindGroupD3D12.cpp",
       "src/dawn_native/d3d12/BindGroupD3D12.h",
@@ -423,6 +418,8 @@
       "src/dawn_native/d3d12/NativeSwapChainImplD3D12.h",
       "src/dawn_native/d3d12/PipelineLayoutD3D12.cpp",
       "src/dawn_native/d3d12/PipelineLayoutD3D12.h",
+      "src/dawn_native/d3d12/PlatformFunctions.cpp",
+      "src/dawn_native/d3d12/PlatformFunctions.h",
       "src/dawn_native/d3d12/QueueD3D12.cpp",
       "src/dawn_native/d3d12/QueueD3D12.h",
       "src/dawn_native/d3d12/RenderPassDescriptorD3D12.cpp",
diff --git a/src/dawn_native/CMakeLists.txt b/src/dawn_native/CMakeLists.txt
index 92db945..a24999d 100644
--- a/src/dawn_native/CMakeLists.txt
+++ b/src/dawn_native/CMakeLists.txt
@@ -225,6 +225,8 @@
         ${D3D12_DIR}/NativeSwapChainImplD3D12.h
         ${D3D12_DIR}/PipelineLayoutD3D12.cpp
         ${D3D12_DIR}/PipelineLayoutD3D12.h
+        ${D3D12_DIR}/PlatformFunctions.cpp
+        ${D3D12_DIR}/PlatformFunctions.h
         ${D3D12_DIR}/QueueD3D12.cpp
         ${D3D12_DIR}/QueueD3D12.h
         ${D3D12_DIR}/RenderPassDescriptorD3D12.cpp
diff --git a/src/dawn_native/d3d12/ComputePipelineD3D12.cpp b/src/dawn_native/d3d12/ComputePipelineD3D12.cpp
index c885ea4..8599d04 100644
--- a/src/dawn_native/d3d12/ComputePipelineD3D12.cpp
+++ b/src/dawn_native/d3d12/ComputePipelineD3D12.cpp
@@ -17,10 +17,9 @@
 #include "common/Assert.h"
 #include "dawn_native/d3d12/DeviceD3D12.h"
 #include "dawn_native/d3d12/PipelineLayoutD3D12.h"
+#include "dawn_native/d3d12/PlatformFunctions.h"
 #include "dawn_native/d3d12/ShaderModuleD3D12.h"
 
-#include <d3dcompiler.h>
-
 namespace dawn_native { namespace d3d12 {
 
     ComputePipeline::ComputePipeline(ComputePipelineBuilder* builder)
@@ -40,9 +39,10 @@
         ComPtr<ID3DBlob> compiledShader;
         ComPtr<ID3DBlob> errors;
 
-        if (FAILED(D3DCompile(hlslSource.c_str(), hlslSource.length(), nullptr, nullptr, nullptr,
-                              entryPoint.c_str(), "cs_5_1", compileFlags, 0, &compiledShader,
-                              &errors))) {
+        const PlatformFunctions* functions = ToBackend(builder->GetDevice())->GetFunctions();
+        if (FAILED(functions->d3dCompile(hlslSource.c_str(), hlslSource.length(), nullptr, nullptr,
+                                         nullptr, entryPoint.c_str(), "cs_5_1", compileFlags, 0,
+                                         &compiledShader, &errors))) {
             printf("%s\n", reinterpret_cast<char*>(errors->GetBufferPointer()));
             ASSERT(false);
         }
diff --git a/src/dawn_native/d3d12/DeviceD3D12.cpp b/src/dawn_native/d3d12/DeviceD3D12.cpp
index f43936c..9fe7304 100644
--- a/src/dawn_native/d3d12/DeviceD3D12.cpp
+++ b/src/dawn_native/d3d12/DeviceD3D12.cpp
@@ -29,6 +29,7 @@
 #include "dawn_native/d3d12/InputStateD3D12.h"
 #include "dawn_native/d3d12/NativeSwapChainImplD3D12.h"
 #include "dawn_native/d3d12/PipelineLayoutD3D12.h"
+#include "dawn_native/d3d12/PlatformFunctions.h"
 #include "dawn_native/d3d12/QueueD3D12.h"
 #include "dawn_native/d3d12/RenderPassDescriptorD3D12.h"
 #include "dawn_native/d3d12/RenderPipelineD3D12.h"
@@ -66,7 +67,7 @@
     }
 
     namespace {
-        ComPtr<IDXGIFactory4> CreateFactory() {
+        ComPtr<IDXGIFactory4> CreateFactory(const PlatformFunctions* functions) {
             ComPtr<IDXGIFactory4> factory;
 
             uint32_t dxgiFactoryFlags = 0;
@@ -74,7 +75,7 @@
             // Enable the debug layer (requires the Graphics Tools "optional feature").
             {
                 ComPtr<ID3D12Debug> debugController;
-                if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debugController)))) {
+                if (SUCCEEDED(functions->d3d12GetDebugInterface(IID_PPV_ARGS(&debugController)))) {
                     debugController->EnableDebugLayer();
 
                     // Enable additional debug layers.
@@ -82,18 +83,19 @@
                 }
 
                 ComPtr<IDXGIDebug1> dxgiDebug;
-                if (SUCCEEDED(DXGIGetDebugInterface1(0, IID_PPV_ARGS(&dxgiDebug)))) {
+                if (SUCCEEDED(functions->dxgiGetDebugInterface1(0, IID_PPV_ARGS(&dxgiDebug)))) {
                     dxgiDebug->ReportLiveObjects(DXGI_DEBUG_ALL,
                                                  DXGI_DEBUG_RLO_FLAGS(DXGI_DEBUG_RLO_ALL));
                 }
             }
 #endif  // defined(DAWN_ENABLE_ASSERTS)
 
-            ASSERT_SUCCESS(CreateDXGIFactory2(dxgiFactoryFlags, IID_PPV_ARGS(&factory)));
+            ASSERT_SUCCESS(functions->createDxgiFactory2(dxgiFactoryFlags, IID_PPV_ARGS(&factory)));
             return factory;
         }
 
-        ComPtr<IDXGIAdapter1> GetHardwareAdapter(ComPtr<IDXGIFactory4> factory) {
+        ComPtr<IDXGIAdapter1> GetHardwareAdapter(ComPtr<IDXGIFactory4> factory,
+                                                 const PlatformFunctions* functions) {
             for (uint32_t adapterIndex = 0;; ++adapterIndex) {
                 IDXGIAdapter1* adapter = nullptr;
                 if (factory->EnumAdapters1(adapterIndex, &adapter) == DXGI_ERROR_NOT_FOUND) {
@@ -102,8 +104,8 @@
 
                 // Check to see if the adapter supports Direct3D 12, but don't create the actual
                 // device yet.
-                if (SUCCEEDED(D3D12CreateDevice(adapter, D3D_FEATURE_LEVEL_11_0,
-                                                _uuidof(ID3D12Device), nullptr))) {
+                if (SUCCEEDED(functions->d3d12CreateDevice(adapter, D3D_FEATURE_LEVEL_11_0,
+                                                           _uuidof(ID3D12Device), nullptr))) {
                     return adapter;
                 }
                 adapter->Release();
@@ -114,15 +116,22 @@
     }  // anonymous namespace
 
     Device::Device() {
+        mFunctions = new PlatformFunctions();
+
+        {
+            MaybeError status = mFunctions->LoadFunctions();
+            ASSERT(status.IsSuccess());
+        }
+
         // Create the connection to DXGI and the D3D12 device
-        mFactory = CreateFactory();
+        mFactory = CreateFactory(mFunctions);
         ASSERT(mFactory.Get() != nullptr);
 
-        mHardwareAdapter = GetHardwareAdapter(mFactory);
+        mHardwareAdapter = GetHardwareAdapter(mFactory, mFunctions);
         ASSERT(mHardwareAdapter.Get() != nullptr);
 
-        ASSERT_SUCCESS(D3D12CreateDevice(mHardwareAdapter.Get(), D3D_FEATURE_LEVEL_11_0,
-                                         IID_PPV_ARGS(&mD3d12Device)));
+        ASSERT_SUCCESS(mFunctions->d3d12CreateDevice(mHardwareAdapter.Get(), D3D_FEATURE_LEVEL_11_0,
+                                                     IID_PPV_ARGS(&mD3d12Device)));
 
         // Create device-global objects
         D3D12_COMMAND_QUEUE_DESC queueDesc = {};
@@ -157,6 +166,7 @@
         delete mMapRequestTracker;
         delete mResourceAllocator;
         delete mResourceUploader;
+        delete mFunctions;
     }
 
     ComPtr<IDXGIFactory4> Device::GetFactory() {
@@ -175,6 +185,10 @@
         return mDescriptorHeapAllocator;
     }
 
+    const PlatformFunctions* Device::GetFunctions() {
+        return mFunctions;
+    }
+
     MapRequestTracker* Device::GetMapRequestTracker() const {
         return mMapRequestTracker;
     }
diff --git a/src/dawn_native/d3d12/DeviceD3D12.h b/src/dawn_native/d3d12/DeviceD3D12.h
index da9af0a..cbb3523 100644
--- a/src/dawn_native/d3d12/DeviceD3D12.h
+++ b/src/dawn_native/d3d12/DeviceD3D12.h
@@ -27,6 +27,7 @@
     class CommandAllocatorManager;
     class DescriptorHeapAllocator;
     class MapRequestTracker;
+    class PlatformFunctions;
     class ResourceAllocator;
     class ResourceUploader;
 
@@ -59,6 +60,7 @@
 
         DescriptorHeapAllocator* GetDescriptorHeapAllocator();
         MapRequestTracker* GetMapRequestTracker() const;
+        const PlatformFunctions* GetFunctions();
         ResourceAllocator* GetResourceAllocator();
         ResourceUploader* GetResourceUploader();
 
@@ -97,6 +99,7 @@
         CommandAllocatorManager* mCommandAllocatorManager = nullptr;
         DescriptorHeapAllocator* mDescriptorHeapAllocator = nullptr;
         MapRequestTracker* mMapRequestTracker = nullptr;
+        PlatformFunctions* mFunctions = nullptr;
         ResourceAllocator* mResourceAllocator = nullptr;
         ResourceUploader* mResourceUploader = nullptr;
 
diff --git a/src/dawn_native/d3d12/PipelineLayoutD3D12.cpp b/src/dawn_native/d3d12/PipelineLayoutD3D12.cpp
index a0f2da4..ee6e992 100644
--- a/src/dawn_native/d3d12/PipelineLayoutD3D12.cpp
+++ b/src/dawn_native/d3d12/PipelineLayoutD3D12.cpp
@@ -18,6 +18,7 @@
 #include "common/BitSetIterator.h"
 #include "dawn_native/d3d12/BindGroupLayoutD3D12.h"
 #include "dawn_native/d3d12/DeviceD3D12.h"
+#include "dawn_native/d3d12/PlatformFunctions.h"
 
 using Microsoft::WRL::ComPtr;
 
@@ -92,7 +93,7 @@
 
         ComPtr<ID3DBlob> signature;
         ComPtr<ID3DBlob> error;
-        ASSERT_SUCCESS(D3D12SerializeRootSignature(
+        ASSERT_SUCCESS(device->GetFunctions()->d3d12SerializeRootSignature(
             &rootSignatureDescriptor, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error));
         ASSERT_SUCCESS(device->GetD3D12Device()->CreateRootSignature(
             0, signature->GetBufferPointer(), signature->GetBufferSize(),
diff --git a/src/dawn_native/d3d12/PlatformFunctions.cpp b/src/dawn_native/d3d12/PlatformFunctions.cpp
new file mode 100644
index 0000000..6486d42
--- /dev/null
+++ b/src/dawn_native/d3d12/PlatformFunctions.cpp
@@ -0,0 +1,74 @@
+// Copyright 2018 The Dawn Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "dawn_native/d3d12/PlatformFunctions.h"
+
+#include "common/DynamicLib.h"
+
+namespace dawn_native { namespace d3d12 {
+
+    PlatformFunctions::PlatformFunctions() {
+    }
+    PlatformFunctions::~PlatformFunctions() {
+    }
+
+    MaybeError PlatformFunctions::LoadFunctions() {
+        DAWN_TRY(LoadD3D12());
+        DAWN_TRY(LoadDXGI());
+        DAWN_TRY(LoadD3DCompiler());
+
+        return {};
+    }
+
+    MaybeError PlatformFunctions::LoadD3D12() {
+        std::string error;
+        if (!mD3D12Lib.Open("d3d12.dll", &error) ||
+            !mD3D12Lib.GetProc(&d3d12CreateDevice, "D3D12CreateDevice", &error) ||
+            !mD3D12Lib.GetProc(&d3d12GetDebugInterface, "D3D12GetDebugInterface", &error) ||
+            !mD3D12Lib.GetProc(&d3d12SerializeRootSignature, "D3D12SerializeRootSignature",
+                               &error) ||
+            !mD3D12Lib.GetProc(&d3d12CreateRootSignatureDeserializer,
+                               "D3D12CreateRootSignatureDeserializer", &error) ||
+            !mD3D12Lib.GetProc(&d3d12SerializeVersionedRootSignature,
+                               "D3D12SerializeVersionedRootSignature", &error) ||
+            !mD3D12Lib.GetProc(&d3d12CreateVersionedRootSignatureDeserializer,
+                               "D3D12CreateVersionedRootSignatureDeserializer", &error)) {
+            DAWN_RETURN_ERROR(error.c_str());
+        }
+
+        return {};
+    }
+
+    MaybeError PlatformFunctions::LoadDXGI() {
+        std::string error;
+        if (!mDXGILib.Open("dxgi.dll", &error) ||
+            !mDXGILib.GetProc(&dxgiGetDebugInterface1, "DXGIGetDebugInterface1", &error) ||
+            !mDXGILib.GetProc(&createDxgiFactory2, "CreateDXGIFactory2", &error)) {
+            DAWN_RETURN_ERROR(error.c_str());
+        }
+
+        return {};
+    }
+
+    MaybeError PlatformFunctions::LoadD3DCompiler() {
+        std::string error;
+        if (!mD3DCompilerLib.Open("d3dcompiler_47.dll", &error) ||
+            !mD3DCompilerLib.GetProc(&d3dCompile, "D3DCompile", &error)) {
+            DAWN_RETURN_ERROR(error.c_str());
+        }
+
+        return {};
+    }
+
+}}  // namespace dawn_native::d3d12
diff --git a/src/dawn_native/d3d12/PlatformFunctions.h b/src/dawn_native/d3d12/PlatformFunctions.h
new file mode 100644
index 0000000..710fb84
--- /dev/null
+++ b/src/dawn_native/d3d12/PlatformFunctions.h
@@ -0,0 +1,75 @@
+// Copyright 2018 The Dawn Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef DAWNNATIVE_D3D12_PLATFORMFUNCTIONS_H_
+#define DAWNNATIVE_D3D12_PLATFORMFUNCTIONS_H_
+
+#include "dawn_native/d3d12/d3d12_platform.h"
+
+#include "common/DynamicLib.h"
+#include "dawn_native/Error.h"
+
+#include <d3dcompiler.h>
+
+class DynamicLib;
+
+namespace dawn_native { namespace d3d12 {
+
+    // Loads the functions required from the platform dynamically so that we don't need to rely on
+    // them being present in the system. For example linking against d3d12.lib would prevent
+    // dawn_native from loading on Windows 7 system where d3d12.dll doesn't exist.
+    class PlatformFunctions {
+      public:
+        PlatformFunctions();
+        ~PlatformFunctions();
+
+        MaybeError LoadFunctions();
+
+        // Functions from d3d12.dll
+        PFN_D3D12_CREATE_DEVICE d3d12CreateDevice = nullptr;
+        PFN_D3D12_GET_DEBUG_INTERFACE d3d12GetDebugInterface = nullptr;
+
+        PFN_D3D12_SERIALIZE_ROOT_SIGNATURE d3d12SerializeRootSignature = nullptr;
+        PFN_D3D12_CREATE_ROOT_SIGNATURE_DESERIALIZER d3d12CreateRootSignatureDeserializer = nullptr;
+        PFN_D3D12_SERIALIZE_VERSIONED_ROOT_SIGNATURE d3d12SerializeVersionedRootSignature = nullptr;
+        PFN_D3D12_CREATE_VERSIONED_ROOT_SIGNATURE_DESERIALIZER
+        d3d12CreateVersionedRootSignatureDeserializer = nullptr;
+
+        // Functions from dxgi.dll
+        using PFN_DXGI_GET_DEBUG_INTERFACE1 = HRESULT(WINAPI*)(UINT Flags,
+                                                               REFIID riid,
+                                                               _COM_Outptr_ void** pDebug);
+        PFN_DXGI_GET_DEBUG_INTERFACE1 dxgiGetDebugInterface1 = nullptr;
+
+        using PFN_CREATE_DXGI_FACTORY2 = HRESULT(WINAPI*)(UINT Flags,
+                                                          REFIID riid,
+                                                          _COM_Outptr_ void** ppFactory);
+        PFN_CREATE_DXGI_FACTORY2 createDxgiFactory2 = nullptr;
+
+        // Functions from d3d3compiler.dll
+        pD3DCompile d3dCompile = nullptr;
+
+      private:
+        MaybeError LoadD3D12();
+        MaybeError LoadDXGI();
+        MaybeError LoadD3DCompiler();
+
+        DynamicLib mD3D12Lib;
+        DynamicLib mDXGILib;
+        DynamicLib mD3DCompilerLib;
+    };
+
+}}  // namespace dawn_native::d3d12
+
+#endif  // DAWNNATIVE_VULKAN_VULKANFUNCTIONS_H_
diff --git a/src/dawn_native/d3d12/RenderPipelineD3D12.cpp b/src/dawn_native/d3d12/RenderPipelineD3D12.cpp
index 20904da..b375a30 100644
--- a/src/dawn_native/d3d12/RenderPipelineD3D12.cpp
+++ b/src/dawn_native/d3d12/RenderPipelineD3D12.cpp
@@ -20,6 +20,7 @@
 #include "dawn_native/d3d12/DeviceD3D12.h"
 #include "dawn_native/d3d12/InputStateD3D12.h"
 #include "dawn_native/d3d12/PipelineLayoutD3D12.h"
+#include "dawn_native/d3d12/PlatformFunctions.h"
 #include "dawn_native/d3d12/ShaderModuleD3D12.h"
 #include "dawn_native/d3d12/TextureD3D12.h"
 
@@ -101,9 +102,10 @@
                     break;
             }
 
-            if (FAILED(D3DCompile(hlslSource.c_str(), hlslSource.length(), nullptr, nullptr,
-                                  nullptr, entryPoint.c_str(), compileTarget, compileFlags, 0,
-                                  &compiledShader[stage], &errors))) {
+            const PlatformFunctions* functions = ToBackend(builder->GetDevice())->GetFunctions();
+            if (FAILED(functions->d3dCompile(hlslSource.c_str(), hlslSource.length(), nullptr,
+                                             nullptr, nullptr, entryPoint.c_str(), compileTarget,
+                                             compileFlags, 0, &compiledShader[stage], &errors))) {
                 printf("%s\n", reinterpret_cast<char*>(errors->GetBufferPointer()));
                 ASSERT(false);
             }