diff --git a/Source/Core/DolphinWX/DolphinWX.vcxproj b/Source/Core/DolphinWX/DolphinWX.vcxproj
index cea95b666a..f7d158b1cd 100644
--- a/Source/Core/DolphinWX/DolphinWX.vcxproj
+++ b/Source/Core/DolphinWX/DolphinWX.vcxproj
@@ -233,6 +233,9 @@
{3de9ee35-3e91-4f27-a014-2866ad8c3fe3}
+
+ {570215b7-e32f-4438-95ae-c8d955f9fca3}
+
diff --git a/Source/Core/DolphinWX/Main.cpp b/Source/Core/DolphinWX/Main.cpp
index 30ae7ea5ac..a852f76777 100644
--- a/Source/Core/DolphinWX/Main.cpp
+++ b/Source/Core/DolphinWX/Main.cpp
@@ -532,7 +532,7 @@ void Host_ConnectWiimote(int wm_idx, bool connect)
void Host_ShowVideoConfig(void* parent, const std::string& backend_name,
const std::string& config_name)
{
- if (backend_name == "Direct3D" || backend_name == "OpenGL")
+ if (backend_name == "Direct3D" || backend_name == "Direct3D 12 (experimental)" || backend_name == "OpenGL")
{
VideoConfigDiag diag((wxWindow*)parent, backend_name, config_name);
diag.ShowModal();
diff --git a/Source/Core/VideoBackends/D3D12/BoundingBox.cpp b/Source/Core/VideoBackends/D3D12/BoundingBox.cpp
new file mode 100644
index 0000000000..d847924cf2
--- /dev/null
+++ b/Source/Core/VideoBackends/D3D12/BoundingBox.cpp
@@ -0,0 +1,44 @@
+// Copyright 2014 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include "Common/CommonTypes.h"
+#include "Common/MsgHandler.h"
+#include "VideoBackends/D3D12/BoundingBox.h"
+#include "VideoCommon/VideoConfig.h"
+
+// D3D12TODO: Support bounding box behavior.
+namespace DX12
+{
+
+ID3D11UnorderedAccessView* BBox::GetUAV()
+{
+ // D3D12TODO: Implement this;
+ return nullptr;
+}
+
+void BBox::Init()
+{
+ if (g_ActiveConfig.backend_info.bSupportsBBox)
+ {
+ // D3D12TODO: Implement this;
+ }
+}
+
+void BBox::Shutdown()
+{
+ // D3D12TODO: Implement this;
+}
+
+void BBox::Set(int index, int value)
+{
+ // D3D12TODO: Implement this;
+}
+
+int BBox::Get(int index)
+{
+ // D3D12TODO: Implement this;
+ return 0;
+}
+
+};
diff --git a/Source/Core/VideoBackends/D3D12/BoundingBox.h b/Source/Core/VideoBackends/D3D12/BoundingBox.h
new file mode 100644
index 0000000000..05126810db
--- /dev/null
+++ b/Source/Core/VideoBackends/D3D12/BoundingBox.h
@@ -0,0 +1,22 @@
+// Copyright 2014 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+#include "VideoBackends/D3D12/D3DBase.h"
+
+namespace DX12
+{
+
+class BBox
+{
+public:
+ static ID3D11UnorderedAccessView* GetUAV();
+ static void Init();
+ static void Shutdown();
+
+ static void Set(int index, int value);
+ static int Get(int index);
+};
+
+};
diff --git a/Source/Core/VideoBackends/D3D12/D3D12.vcxproj b/Source/Core/VideoBackends/D3D12/D3D12.vcxproj
new file mode 100644
index 0000000000..b44179a441
--- /dev/null
+++ b/Source/Core/VideoBackends/D3D12/D3D12.vcxproj
@@ -0,0 +1,109 @@
+
+
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ {570215B7-E32F-4438-95AE-C8D955F9FCA3}
+ 10.0.10240.0
+
+
+
+ StaticLibrary
+ v140
+ Unicode
+
+
+ true
+
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+ NotUsing
+
+
+
+
+
+ NotUsing
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {3de9ee35-3e91-4f27-a014-2866ad8c3fe3}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Source/Core/VideoBackends/D3D12/D3D12.vcxproj.filters b/Source/Core/VideoBackends/D3D12/D3D12.vcxproj.filters
new file mode 100644
index 0000000000..83038e5a7b
--- /dev/null
+++ b/Source/Core/VideoBackends/D3D12/D3D12.vcxproj.filters
@@ -0,0 +1,152 @@
+
+
+
+
+ {3683d29b-19f6-4e7a-803f-4ac70b1d49fd}
+
+
+ {ae700f7e-33c8-45b5-b7ee-a0ded3630549}
+
+
+
+
+ D3D12
+
+
+ D3D12
+
+
+ D3D12
+
+
+ D3D12
+
+
+ D3D12
+
+
+ Render
+
+
+ Render
+
+
+ Render
+
+
+ Render
+
+
+ Render
+
+
+ Render
+
+
+ Render
+
+
+ Render
+
+
+ Render
+
+
+
+ Render
+
+
+ D3D12
+
+
+ D3D12
+
+
+ D3D12
+
+
+ Render
+
+
+ Render
+
+
+ Render
+
+
+ D3D12
+
+
+
+
+ D3D12
+
+
+ D3D12
+
+
+ D3D12
+
+
+ D3D12
+
+
+ D3D12
+
+
+ Render
+
+
+ Render
+
+
+ Render
+
+
+ Render
+
+
+ Render
+
+
+ Render
+
+
+ Render
+
+
+ Render
+
+
+ Render
+
+
+
+ Render
+
+
+ D3D12
+
+
+ D3D12
+
+
+ Render
+
+
+ D3D12
+
+
+ Render
+
+
+ Render
+
+
+ Render
+
+
+ D3D12
+
+
+
\ No newline at end of file
diff --git a/Source/Core/VideoBackends/D3D12/D3DBase.cpp b/Source/Core/VideoBackends/D3D12/D3DBase.cpp
new file mode 100644
index 0000000000..54565ec273
--- /dev/null
+++ b/Source/Core/VideoBackends/D3D12/D3DBase.cpp
@@ -0,0 +1,970 @@
+// Copyright 2010 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include
+
+#include "Common/CommonTypes.h"
+#include "Common/MsgHandler.h"
+#include "Common/StringUtil.h"
+#include "Common/Logging/Log.h"
+#include "VideoBackends/D3D12/D3DBase.h"
+#include "VideoBackends/D3D12/D3DCommandListManager.h"
+#include "VideoBackends/D3D12/D3DDescriptorHeapManager.h"
+#include "VideoBackends/D3D12/D3DState.h"
+#include "VideoBackends/D3D12/D3DTexture.h"
+#include "VideoCommon/VideoConfig.h"
+
+static const unsigned int SWAP_CHAIN_BUFFER_COUNT = 4;
+
+namespace DX12
+{
+
+// d3dcompiler_*.dll exports
+static HINSTANCE s_d3d_compiler_dll = nullptr;
+static int s_d3d_compiler_dll_ref = 0;
+D3DREFLECT d3d_reflect = nullptr;
+D3DCREATEBLOB d3d_create_blob = nullptr;
+pD3DCompile d3d_compile = nullptr;
+
+// dxgi.dll exports
+static HINSTANCE s_dxgi_dll = nullptr;
+static int s_dxgi_dll_ref = 0;
+CREATEDXGIFACTORY create_dxgi_factory = nullptr;
+
+// d3d12.dll exports
+static HINSTANCE s_d3d12_dll = nullptr;
+static int s_d3d12_dll_ref = 0;
+D3D12CREATEDEVICE d3d12_create_device = nullptr;
+D3D12SERIALIZEROOTSIGNATURE d3d12_serialize_root_signature = nullptr;
+D3D12GETDEBUGINTERFACE d3d12_get_debug_interface = nullptr;
+
+namespace D3D
+{
+
+// Begin extern'd variables.
+ID3D12Device* device12 = nullptr;
+
+ID3D12CommandQueue* command_queue = nullptr;
+D3DCommandListManager* command_list_mgr = nullptr;
+ID3D12GraphicsCommandList* current_command_list = nullptr;
+ID3D12RootSignature* default_root_signature = nullptr;
+
+D3D12_CPU_DESCRIPTOR_HANDLE null_srv_cpu = {};
+D3D12_CPU_DESCRIPTOR_HANDLE null_srv_cpu_shadow = {};
+
+unsigned int resource_descriptor_size = 0;
+unsigned int sampler_descriptor_size = 0;
+D3DDescriptorHeapManager* gpu_descriptor_heap_mgr = nullptr;
+D3DDescriptorHeapManager* sampler_descriptor_heap_mgr = nullptr;
+D3DDescriptorHeapManager* dsv_descriptor_heap_mgr = nullptr;
+D3DDescriptorHeapManager* rtv_descriptor_heap_mgr = nullptr;
+std::array gpu_descriptor_heaps;
+
+HWND hWnd;
+// End extern'd variables.
+
+static IDXGISwapChain* s_swap_chain = nullptr;
+static unsigned int s_monitor_refresh_rate = 0;
+
+static LARGE_INTEGER s_qpc_frequency;
+
+static ID3D12DebugDevice* s_debug_device12 = nullptr;
+
+static D3D_FEATURE_LEVEL s_feat_level;
+static D3DTexture2D* s_backbuf[SWAP_CHAIN_BUFFER_COUNT];
+static unsigned int s_current_back_buf = 0;
+static unsigned int s_xres = 0;
+static unsigned int s_yres = 0;
+static bool s_frame_in_progress = false;
+
+static std::vector s_aa_modes; // supported AA modes of the current adapter
+static const D3D_FEATURE_LEVEL s_supported_feature_levels[] = {
+ D3D_FEATURE_LEVEL_11_0
+};
+
+HRESULT LoadDXGI()
+{
+ if (s_dxgi_dll_ref++ > 0)
+ return S_OK;
+
+ if (s_dxgi_dll)
+ return S_OK;
+
+ s_dxgi_dll = LoadLibraryA("dxgi.dll");
+ if (!s_dxgi_dll)
+ {
+ MessageBoxA(nullptr, "Failed to load dxgi.dll", "Critical error", MB_OK | MB_ICONERROR);
+ --s_dxgi_dll_ref;
+ return E_FAIL;
+ }
+ create_dxgi_factory = (CREATEDXGIFACTORY)GetProcAddress(s_dxgi_dll, "CreateDXGIFactory");
+
+ if (create_dxgi_factory == nullptr)
+ MessageBoxA(nullptr, "GetProcAddress failed for CreateDXGIFactory!", "Critical error", MB_OK | MB_ICONERROR);
+
+ return S_OK;
+}
+
+HRESULT LoadD3D()
+{
+ if (s_d3d12_dll_ref++ > 0)
+ return S_OK;
+
+ s_d3d12_dll = LoadLibraryA("d3d12.dll");
+ if (!s_d3d12_dll)
+ {
+ MessageBoxA(nullptr, "Failed to load d3d12.dll", "Critical error", MB_OK | MB_ICONERROR);
+ --s_d3d12_dll_ref;
+ return E_FAIL;
+ }
+
+ d3d12_create_device = (D3D12CREATEDEVICE)GetProcAddress(s_d3d12_dll, "D3D12CreateDevice");
+ if (d3d12_create_device == nullptr)
+ {
+ MessageBoxA(nullptr, "GetProcAddress failed for D3D12CreateDevice!", "Critical error", MB_OK | MB_ICONERROR);
+ return E_FAIL;
+ }
+
+ d3d12_serialize_root_signature = (D3D12SERIALIZEROOTSIGNATURE)GetProcAddress(s_d3d12_dll, "D3D12SerializeRootSignature");
+ if (d3d12_serialize_root_signature == nullptr)
+ {
+ MessageBoxA(nullptr, "GetProcAddress failed for D3D12SerializeRootSignature!", "Critical error", MB_OK | MB_ICONERROR);
+ return E_FAIL;
+ }
+
+ d3d12_get_debug_interface = (D3D12GETDEBUGINTERFACE)GetProcAddress(s_d3d12_dll, "D3D12GetDebugInterface");
+ if (d3d12_get_debug_interface == nullptr)
+ {
+ MessageBoxA(nullptr, "GetProcAddress failed for D3D12GetDebugInterface!", "Critical error", MB_OK | MB_ICONERROR);
+ return E_FAIL;
+ }
+
+ return S_OK;
+}
+
+HRESULT LoadD3DCompiler()
+{
+ if (s_d3d_compiler_dll_ref++ > 0)
+ return S_OK;
+
+ if (s_d3d_compiler_dll)
+ return S_OK;
+
+ // try to load D3DCompiler first to check whether we have proper runtime support
+ // try to use the dll the backend was compiled against first - don't bother about debug runtimes
+ s_d3d_compiler_dll = LoadLibraryA(D3DCOMPILER_DLL_A);
+ if (!s_d3d_compiler_dll)
+ {
+ // if that fails, use the dll which should be available in every SDK which officially supports DX12.
+ s_d3d_compiler_dll = LoadLibraryA("D3DCompiler_42.dll");
+ if (!s_d3d_compiler_dll)
+ {
+ MessageBoxA(nullptr, "Failed to load D3DCompiler_42.dll, update your DX12 runtime, please", "Critical error", MB_OK | MB_ICONERROR);
+ return E_FAIL;
+ }
+ else
+ {
+ NOTICE_LOG(VIDEO, "Successfully loaded D3DCompiler_42.dll. If you're having trouble, try updating your DX runtime first.");
+ }
+ }
+
+ d3d_reflect = (D3DREFLECT) GetProcAddress(s_d3d_compiler_dll, "D3DReflect");
+ if (d3d_reflect == nullptr)
+ MessageBoxA(nullptr, "GetProcAddress failed for D3DReflect!", "Critical error", MB_OK | MB_ICONERROR);
+
+ d3d_create_blob = (D3DCREATEBLOB)GetProcAddress(s_d3d_compiler_dll, "D3DCreateBlob");
+ if (d3d_create_blob == nullptr)
+ MessageBoxA(nullptr, "GetProcAddress failed for D3DCreateBlob!", "Critical error", MB_OK | MB_ICONERROR);
+
+ d3d_compile = (pD3DCompile) GetProcAddress(s_d3d_compiler_dll, "D3DCompile");
+ if (d3d_compile == nullptr)
+ MessageBoxA(nullptr, "GetProcAddress failed for D3DCompile!", "Critical error", MB_OK | MB_ICONERROR);
+
+ return S_OK;
+}
+
+void UnloadDXGI()
+{
+ if (!s_dxgi_dll_ref)
+ return;
+
+ if (--s_dxgi_dll_ref != 0)
+ return;
+
+ if (s_dxgi_dll)
+ FreeLibrary(s_dxgi_dll);
+
+ s_dxgi_dll = nullptr;
+ create_dxgi_factory = nullptr;
+}
+
+void UnloadD3D()
+{
+ if (!s_d3d12_dll_ref)
+ return;
+
+ if (--s_d3d12_dll_ref != 0)
+ return;
+
+ if (s_d3d12_dll)
+ FreeLibrary(s_d3d12_dll);
+
+ s_d3d12_dll = nullptr;
+ d3d12_create_device = nullptr;
+ d3d12_serialize_root_signature = nullptr;
+}
+
+void UnloadD3DCompiler()
+{
+ if (!s_d3d_compiler_dll_ref)
+ return;
+
+ if (--s_d3d_compiler_dll_ref != 0)
+ return;
+
+ if (s_d3d_compiler_dll)
+ FreeLibrary(s_d3d_compiler_dll);
+
+ s_d3d_compiler_dll = nullptr;
+ d3d_compile = nullptr;
+ d3d_create_blob = nullptr;
+ d3d_reflect = nullptr;
+}
+
+bool AlertUserIfSelectedAdapterDoesNotSupportD3D12()
+{
+ HRESULT hr = LoadDXGI();
+ if (SUCCEEDED(hr))
+ {
+ hr = LoadD3D();
+ }
+
+ if (FAILED(hr))
+ {
+ // LoadDXGI / LoadD3D display a specific error message,
+ // no need to do that here.
+ return false;
+ }
+
+ IDXGIFactory* factory = nullptr;
+ IDXGIAdapter* adapter = nullptr;
+ ID3D12Device* device = nullptr;
+
+ if (SUCCEEDED(hr))
+ {
+ hr = create_dxgi_factory(__uuidof(IDXGIFactory), (void**)&factory);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ hr = factory->EnumAdapters(g_ActiveConfig.iAdapter, &adapter);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ hr = d3d12_create_device(adapter, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&device));
+
+ SAFE_RELEASE(device);
+ SAFE_RELEASE(adapter);
+ SAFE_RELEASE(factory);
+
+ if (FAILED(hr))
+ {
+ UnloadD3D();
+ UnloadDXGI();
+ MessageBoxA(nullptr, "Failed to create a D3D12 device on the selected adapter.\n\nPlease make sure it supports Direct3D 12, and that your graphics drivers are up-to-date.", "Critical error", MB_OK | MB_ICONERROR);
+ return false;
+ }
+
+ // If succeeded, leave DXGI and D3D libraries loaded since we'll use them in Create().
+ return true;
+ }
+
+ // DXGI failed to create factory/enumerate adapter. This should be very uncommon.
+ MessageBoxA(nullptr, "Failed to create enumerate selected adapter. Please select a different graphics adapter.", "Critical error", MB_OK | MB_ICONERROR);
+ SAFE_RELEASE(adapter);
+ SAFE_RELEASE(factory);
+
+ UnloadD3D();
+ UnloadDXGI();
+ return false;
+}
+
+std::vector EnumAAModes(IDXGIAdapter* adapter)
+{
+ std::vector aa_modes;
+
+ bool d3d12_supported = AlertUserIfSelectedAdapterDoesNotSupportD3D12();
+
+ if (!d3d12_supported)
+ return aa_modes;
+
+ ID3D12Device* device12 = nullptr;
+ d3d12_create_device(adapter, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&device12));
+
+ if (device12)
+ {
+ for (int samples = 0; samples < D3D12_MAX_MULTISAMPLE_SAMPLE_COUNT; ++samples)
+ {
+ D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS multisample_quality_levels = {};
+ multisample_quality_levels.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+ multisample_quality_levels.SampleCount = samples;
+
+ device12->CheckFeatureSupport(D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS, &multisample_quality_levels, sizeof(multisample_quality_levels));
+
+ DXGI_SAMPLE_DESC desc;
+ desc.Count = samples;
+ desc.Quality = 0;
+
+ if (multisample_quality_levels.NumQualityLevels > 0)
+ {
+ aa_modes.push_back(desc);
+ }
+ }
+
+ device12->Release();
+ }
+
+ return aa_modes;
+}
+
+D3D_FEATURE_LEVEL GetFeatureLevel(IDXGIAdapter* adapter)
+{
+ return D3D_FEATURE_LEVEL_11_0;
+}
+
+HRESULT Create(HWND wnd)
+{
+ hWnd = wnd;
+ HRESULT hr;
+
+ RECT client;
+ GetClientRect(hWnd, &client);
+ s_xres = client.right - client.left;
+ s_yres = client.bottom - client.top;
+
+ hr = LoadDXGI();
+ if (SUCCEEDED(hr))
+ hr = LoadD3D();
+
+ if (SUCCEEDED(hr))
+ hr = LoadD3DCompiler();
+
+ if (FAILED(hr))
+ {
+ UnloadDXGI();
+ UnloadD3D();
+ UnloadD3DCompiler();
+ return hr;
+ }
+
+ IDXGIFactory* factory;
+ IDXGIAdapter* adapter;
+ IDXGIOutput* output;
+ hr = create_dxgi_factory(__uuidof(IDXGIFactory), (void**)&factory);
+ if (FAILED(hr))
+ MessageBox(wnd, _T("Failed to create IDXGIFactory object"), _T("Dolphin Direct3D 12 backend"), MB_OK | MB_ICONERROR);
+
+ hr = factory->EnumAdapters(g_ActiveConfig.iAdapter, &adapter);
+ if (FAILED(hr))
+ {
+ // try using the first one
+ hr = factory->EnumAdapters(0, &adapter);
+ if (FAILED(hr))
+ MessageBox(wnd, _T("Failed to enumerate adapters"), _T("Dolphin Direct3D 12 backend"), MB_OK | MB_ICONERROR);
+ }
+
+ // TODO: Make this configurable
+ hr = adapter->EnumOutputs(0, &output);
+ if (FAILED(hr))
+ {
+ // try using the first one
+ IDXGIAdapter* firstadapter;
+ hr = factory->EnumAdapters(0, &firstadapter);
+ if (!FAILED(hr))
+ hr = firstadapter->EnumOutputs(0, &output);
+ if (FAILED(hr))
+ MessageBox(wnd,
+ _T("Failed to enumerate outputs!\n")
+ _T("This usually happens when you've set your video adapter to the Nvidia GPU in an Optimus-equipped system.\n")
+ _T("Set Dolphin to use the high-performance graphics in Nvidia's drivers instead and leave Dolphin's video adapter set to the Intel GPU."),
+ _T("Dolphin Direct3D 12 backend"), MB_OK | MB_ICONERROR);
+
+ SAFE_RELEASE(firstadapter);
+ }
+
+ // get supported AA modes
+ s_aa_modes = EnumAAModes(adapter);
+
+ if (std::find_if(
+ s_aa_modes.begin(),
+ s_aa_modes.end(),
+ [](const DXGI_SAMPLE_DESC& desc) {return desc.Count == g_Config.iMultisamples; }
+ ) == s_aa_modes.end())
+ {
+ g_Config.iMultisamples = 1;
+ UpdateActiveConfig();
+ }
+
+ DXGI_SWAP_CHAIN_DESC swap_chain_desc = {};
+ swap_chain_desc.BufferCount = SWAP_CHAIN_BUFFER_COUNT;
+ swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+ swap_chain_desc.OutputWindow = wnd;
+ swap_chain_desc.SampleDesc.Count = 1;
+ swap_chain_desc.SampleDesc.Quality = 0;
+ swap_chain_desc.Windowed = true;
+ swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
+ swap_chain_desc.Flags = 0;
+
+ swap_chain_desc.BufferDesc.Width = s_xres;
+ swap_chain_desc.BufferDesc.Height = s_yres;
+ swap_chain_desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+ swap_chain_desc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
+
+#if defined(_DEBUG) || defined(DEBUGFAST)
+ // Creating debug devices can sometimes fail if the user doesn't have the correct
+ // version of the DirectX SDK. If it does, simply fallback to a non-debug device.
+ {
+ if (SUCCEEDED(hr))
+ {
+ ID3D12Debug* debug_controller;
+ hr = d3d12_get_debug_interface(IID_PPV_ARGS(&debug_controller));
+ if (SUCCEEDED(hr))
+ {
+ debug_controller->EnableDebugLayer();
+ debug_controller->Release();
+ }
+ else
+ {
+ MessageBox(wnd, _T("Failed to initialize Direct3D debug layer, please make sure it is installed."), _T("Dolphin Direct3D 12 backend"), MB_OK | MB_ICONERROR);
+ }
+
+ hr = d3d12_create_device(adapter, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&device12));
+
+ s_feat_level = D3D_FEATURE_LEVEL_11_0;
+ }
+ }
+
+ if (FAILED(hr))
+#endif
+ {
+ if (SUCCEEDED(hr))
+ {
+#ifdef USE_D3D12_DEBUG_LAYER
+ ID3D12Debug* debug_controller;
+ hr = d3d12_get_debug_interface(IID_PPV_ARGS(&debug_controller));
+ if (SUCCEEDED(hr))
+ {
+ debug_controller->EnableDebugLayer();
+ debug_controller->Release();
+ }
+ else
+ {
+ MessageBox(wnd, _T("Failed to initialize Direct3D debug layer."), _T("Dolphin Direct3D 12 backend"), MB_OK | MB_ICONERROR);
+ }
+#endif
+ hr = d3d12_create_device(adapter, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&device12));
+
+ s_feat_level = D3D_FEATURE_LEVEL_11_0;
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ D3D12_COMMAND_QUEUE_DESC command_queue_desc = {
+ D3D12_COMMAND_LIST_TYPE_DIRECT, // D3D12_COMMAND_LIST_TYPE Type;
+ 0, // INT Priority;
+ D3D12_COMMAND_QUEUE_FLAG_NONE, // D3D12_COMMAND_QUEUE_FLAG Flags;
+ 0 // UINT NodeMask;
+ };
+
+ CheckHR(device12->CreateCommandQueue(&command_queue_desc, IID_PPV_ARGS(&command_queue)));
+
+ IDXGIFactory* factory = nullptr;
+ adapter->GetParent(IID_PPV_ARGS(&factory));
+
+ CheckHR(factory->CreateSwapChain(command_queue, &swap_chain_desc, &s_swap_chain));
+
+ s_current_back_buf = 0;
+
+ factory->Release();
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ // Query the monitor refresh rate, to ensure proper Present throttling behavior.
+ DEVMODE dev_mode;
+ memset(&dev_mode, 0, sizeof(DEVMODE));
+ dev_mode.dmSize = sizeof(DEVMODE);
+ dev_mode.dmDriverExtra = 0;
+
+ if (EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dev_mode) == 0)
+ {
+ // If EnumDisplaySettings fails, assume monitor refresh rate of 60 Hz.
+ s_monitor_refresh_rate = 60;
+ }
+ else
+ {
+ s_monitor_refresh_rate = dev_mode.dmDisplayFrequency;
+ }
+ }
+
+ if (FAILED(hr))
+ {
+ MessageBox(wnd, _T("Failed to initialize Direct3D.\nMake sure your video card supports Direct3D 12 and your drivers are up-to-date."), _T("Dolphin Direct3D 12 backend"), MB_OK | MB_ICONERROR);
+ SAFE_RELEASE(s_swap_chain);
+ return E_FAIL;
+ }
+
+ ID3D12InfoQueue* info_queue = nullptr;
+ if (SUCCEEDED(device12->QueryInterface(&info_queue)))
+ {
+ CheckHR(info_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_ERROR, TRUE));
+ CheckHR(info_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_WARNING, TRUE));
+
+ D3D12_INFO_QUEUE_FILTER filter = {};
+ D3D12_MESSAGE_ID id_list[] = {
+ D3D12_MESSAGE_ID_CREATEGRAPHICSPIPELINESTATE_DEPTHSTENCILVIEW_NOT_SET, // Benign.
+ D3D12_MESSAGE_ID_CREATEGRAPHICSPIPELINESTATE_RENDERTARGETVIEW_NOT_SET, // Benign.
+ D3D12_MESSAGE_ID_CREATEINPUTLAYOUT_TYPE_MISMATCH, // Benign.
+ D3D12_MESSAGE_ID_DRAW_EMPTY_SCISSOR_RECTANGLE, // Benign. Probably.
+ D3D12_MESSAGE_ID_INVALID_SUBRESOURCE_STATE,
+ D3D12_MESSAGE_ID_MAP_INVALID_NULLRANGE, // Benign.
+ D3D12_MESSAGE_ID_EXECUTECOMMANDLISTS_GPU_WRITTEN_READBACK_RESOURCE_MAPPED, // Benign.
+ D3D12_MESSAGE_ID_RESOURCE_BARRIER_BEFORE_AFTER_MISMATCH // Benign. Probably.
+ };
+ filter.DenyList.NumIDs = ARRAYSIZE(id_list);
+ filter.DenyList.pIDList = id_list;
+ info_queue->PushStorageFilter(&filter);
+
+ info_queue->Release();
+
+ // Used at Close time to report live objects.
+ CheckHR(device12->QueryInterface(&s_debug_device12));
+ }
+
+ // prevent DXGI from responding to Alt+Enter, unfortunately DXGI_MWA_NO_ALT_ENTER
+ // does not work so we disable all monitoring of window messages. However this
+ // may make it more difficult for DXGI to handle display mode changes.
+ hr = factory->MakeWindowAssociation(wnd, DXGI_MWA_NO_WINDOW_CHANGES);
+ if (FAILED(hr))
+ MessageBox(wnd, _T("Failed to associate the window"), _T("Dolphin Direct3D 12 backend"), MB_OK | MB_ICONERROR);
+
+ SAFE_RELEASE(factory);
+ SAFE_RELEASE(output);
+ SAFE_RELEASE(adapter)
+
+ CreateDescriptorHeaps();
+ CreateRootSignatures();
+
+ command_list_mgr = new D3DCommandListManager(
+ D3D12_COMMAND_LIST_TYPE_DIRECT,
+ device12,
+ command_queue
+ );
+
+ command_list_mgr->GetCommandList(¤t_command_list);
+ command_list_mgr->SetInitialCommandListState();
+
+ for (UINT i = 0; i < SWAP_CHAIN_BUFFER_COUNT; i++)
+ {
+ ID3D12Resource* buf12 = nullptr;
+ hr = s_swap_chain->GetBuffer(i, IID_PPV_ARGS(&buf12));
+
+ CHECK(SUCCEEDED(hr), "Retrieve back buffer texture");
+
+ s_backbuf[i] = new D3DTexture2D(buf12,
+ D3D11_BIND_RENDER_TARGET,
+ DXGI_FORMAT_UNKNOWN,
+ DXGI_FORMAT_UNKNOWN,
+ DXGI_FORMAT_UNKNOWN,
+ false,
+ D3D12_RESOURCE_STATE_PRESENT // Swap Chain back buffers start out in D3D12_RESOURCE_STATE_PRESENT.
+ );
+
+ SAFE_RELEASE(buf12);
+ SetDebugObjectName12(s_backbuf[i]->GetTex12(), "backbuffer texture");
+ }
+
+ s_backbuf[s_current_back_buf]->TransitionToResourceState(current_command_list, D3D12_RESOURCE_STATE_RENDER_TARGET);
+ current_command_list->OMSetRenderTargets(1, &s_backbuf[s_current_back_buf]->GetRTV12(), FALSE, nullptr);
+
+ QueryPerformanceFrequency(&s_qpc_frequency);
+
+ return S_OK;
+}
+
+void CreateDescriptorHeaps()
+{
+ // Create D3D12 GPU and CPU descriptor heaps.
+
+ {
+ D3D12_DESCRIPTOR_HEAP_DESC gpu_descriptor_heap_desc = {};
+ gpu_descriptor_heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
+ gpu_descriptor_heap_desc.NumDescriptors = 500000;
+ gpu_descriptor_heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
+
+ gpu_descriptor_heap_mgr = new D3DDescriptorHeapManager(&gpu_descriptor_heap_desc, device12, 50000);
+
+ gpu_descriptor_heaps[0] = gpu_descriptor_heap_mgr->GetDescriptorHeap();
+
+ D3D12_CPU_DESCRIPTOR_HANDLE descriptor_heap_cpu_base = gpu_descriptor_heap_mgr->GetDescriptorHeap()->GetCPUDescriptorHandleForHeapStart();
+
+ resource_descriptor_size = device12->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
+ sampler_descriptor_size = device12->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
+
+ D3D12_GPU_DESCRIPTOR_HANDLE null_srv_gpu = {};
+ gpu_descriptor_heap_mgr->Allocate(&null_srv_cpu, &null_srv_gpu, &null_srv_cpu_shadow);
+
+ D3D12_SHADER_RESOURCE_VIEW_DESC null_srv_desc = {};
+ null_srv_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+ null_srv_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
+ null_srv_desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
+
+ device12->CreateShaderResourceView(NULL, &null_srv_desc, null_srv_cpu);
+
+ for (UINT i = 0; i < 500000; i++)
+ {
+ // D3D12TODO: Make paving of descriptor heap optional.
+
+ D3D12_CPU_DESCRIPTOR_HANDLE destination_descriptor = {};
+ destination_descriptor.ptr = descriptor_heap_cpu_base.ptr + i * resource_descriptor_size;
+
+ device12->CreateShaderResourceView(NULL, &null_srv_desc, destination_descriptor);
+ }
+ }
+
+ {
+ D3D12_DESCRIPTOR_HEAP_DESC sampler_descriptor_heap_desc = {};
+ sampler_descriptor_heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
+ sampler_descriptor_heap_desc.NumDescriptors = 2000;
+ sampler_descriptor_heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER;
+
+ sampler_descriptor_heap_mgr = new D3DDescriptorHeapManager(&sampler_descriptor_heap_desc, device12);
+
+ gpu_descriptor_heaps[1] = sampler_descriptor_heap_mgr->GetDescriptorHeap();
+ }
+
+ {
+ D3D12_DESCRIPTOR_HEAP_DESC dsv_descriptor_heap_desc = {};
+ dsv_descriptor_heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
+ dsv_descriptor_heap_desc.NumDescriptors = 2000;
+ dsv_descriptor_heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_DSV;
+
+ dsv_descriptor_heap_mgr = new D3DDescriptorHeapManager(&dsv_descriptor_heap_desc, device12);
+ }
+
+ {
+ // D3D12TODO: Temporary workaround.. really need to properly suballocate out of render target heap.
+ D3D12_DESCRIPTOR_HEAP_DESC rtv_descriptor_heap_desc = {};
+ rtv_descriptor_heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
+ rtv_descriptor_heap_desc.NumDescriptors = 1000000;
+ rtv_descriptor_heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
+
+ rtv_descriptor_heap_mgr = new D3DDescriptorHeapManager(&rtv_descriptor_heap_desc, device12);
+ }
+}
+
+void CreateRootSignatures()
+{
+ D3D12_DESCRIPTOR_RANGE desc_range_srv = {
+ D3D12_DESCRIPTOR_RANGE_TYPE_SRV, // D3D12_DESCRIPTOR_RANGE_TYPE RangeType;
+ 8, // UINT NumDescriptors;
+ 0, // UINT BaseShaderRegister;
+ 0, // UINT RegisterSpace;
+ D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND // UINT OffsetInDescriptorsFromTableStart;
+ };
+
+ D3D12_DESCRIPTOR_RANGE desc_range_sampler = {
+ D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, // D3D12_DESCRIPTOR_RANGE_TYPE RangeType;
+ 8, // UINT NumDescriptors;
+ 0, // UINT BaseShaderRegister;
+ 0, // UINT RegisterSpace;
+ D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND // UINT OffsetInDescriptorsFromTableStart;
+ };
+
+ D3D12_ROOT_PARAMETER root_parameters[6];
+
+ root_parameters[DESCRIPTOR_TABLE_PS_SRV].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
+ root_parameters[DESCRIPTOR_TABLE_PS_SRV].DescriptorTable.NumDescriptorRanges = 1;
+ root_parameters[DESCRIPTOR_TABLE_PS_SRV].DescriptorTable.pDescriptorRanges = &desc_range_srv;
+ root_parameters[DESCRIPTOR_TABLE_PS_SRV].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
+
+ root_parameters[DESCRIPTOR_TABLE_PS_SAMPLER].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
+ root_parameters[DESCRIPTOR_TABLE_PS_SAMPLER].DescriptorTable.NumDescriptorRanges = 1;
+ root_parameters[DESCRIPTOR_TABLE_PS_SAMPLER].DescriptorTable.pDescriptorRanges = &desc_range_sampler;
+ root_parameters[DESCRIPTOR_TABLE_PS_SAMPLER].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
+
+ root_parameters[DESCRIPTOR_TABLE_GS_CBV].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
+ root_parameters[DESCRIPTOR_TABLE_GS_CBV].Descriptor.RegisterSpace = 0;
+ root_parameters[DESCRIPTOR_TABLE_GS_CBV].Descriptor.ShaderRegister = 0;
+ root_parameters[DESCRIPTOR_TABLE_GS_CBV].ShaderVisibility = D3D12_SHADER_VISIBILITY_GEOMETRY;
+
+ root_parameters[DESCRIPTOR_TABLE_VS_CBV].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
+ root_parameters[DESCRIPTOR_TABLE_VS_CBV].Descriptor.RegisterSpace = 0;
+ root_parameters[DESCRIPTOR_TABLE_VS_CBV].Descriptor.ShaderRegister = 0;
+ root_parameters[DESCRIPTOR_TABLE_VS_CBV].ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX;
+
+ root_parameters[DESCRIPTOR_TABLE_PS_CBVONE].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
+ root_parameters[DESCRIPTOR_TABLE_PS_CBVONE].Descriptor.RegisterSpace = 0;
+ root_parameters[DESCRIPTOR_TABLE_PS_CBVONE].Descriptor.ShaderRegister = 0;
+ root_parameters[DESCRIPTOR_TABLE_PS_CBVONE].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
+
+ root_parameters[DESCRIPTOR_TABLE_PS_CBVTWO].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
+ root_parameters[DESCRIPTOR_TABLE_PS_CBVTWO].Descriptor.RegisterSpace = 0;
+ root_parameters[DESCRIPTOR_TABLE_PS_CBVTWO].Descriptor.ShaderRegister = 1;
+ root_parameters[DESCRIPTOR_TABLE_PS_CBVTWO].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
+
+ // D3D12TODO: Add bounding box UAV to root signature.
+
+ D3D12_ROOT_SIGNATURE_DESC root_signature_desc = {};
+ root_signature_desc.pParameters = root_parameters;
+ root_signature_desc.Flags =
+ D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |
+ D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS |
+ D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS;
+
+ root_signature_desc.NumParameters = ARRAYSIZE(root_parameters);
+
+ ID3DBlob* text_root_signature_blob;
+ ID3DBlob* text_root_signature_error_blob;
+
+ CheckHR(d3d12_serialize_root_signature(&root_signature_desc, D3D_ROOT_SIGNATURE_VERSION_1, &text_root_signature_blob, &text_root_signature_error_blob));
+
+ CheckHR(D3D::device12->CreateRootSignature(0, text_root_signature_blob->GetBufferPointer(), text_root_signature_blob->GetBufferSize(), IID_PPV_ARGS(&default_root_signature)));
+}
+
+void WaitForOutstandingRenderingToComplete()
+{
+ command_list_mgr->ClearQueueAndWaitForCompletionOfInflightWork();
+}
+
+void Close()
+{
+ // we can't release the swapchain while in fullscreen.
+ s_swap_chain->SetFullscreenState(false, nullptr);
+
+ // Release all back buffer references
+ for (UINT i = 0; i < ARRAYSIZE(s_backbuf); i++)
+ {
+ SAFE_RELEASE(s_backbuf[i]);
+ }
+
+ D3D::CleanupPersistentD3DTextureResources();
+
+ command_list_mgr->ImmediatelyDestroyAllResourcesScheduledForDestruction();
+
+ SAFE_RELEASE(s_swap_chain);
+
+ SAFE_DELETE(command_list_mgr);
+ command_queue->Release();
+
+ default_root_signature->Release();
+
+ SAFE_DELETE(gpu_descriptor_heap_mgr);
+ SAFE_DELETE(sampler_descriptor_heap_mgr);
+ SAFE_DELETE(rtv_descriptor_heap_mgr);
+ SAFE_DELETE(dsv_descriptor_heap_mgr);
+
+ ULONG remaining_references = device12->Release();
+ if ((!s_debug_device12 && remaining_references) || (s_debug_device12 && remaining_references > 1))
+ {
+ ERROR_LOG(VIDEO, "Unreleased D3D12 references: %i.", remaining_references);
+ }
+ else
+ {
+ NOTICE_LOG(VIDEO, "Successfully released all D3D12 device references!");
+ }
+
+#if defined(_DEBUG) || defined(DEBUGFAST)
+ if (s_debug_device12)
+ {
+ --remaining_references; // the debug interface increases the refcount of the device, subtract that.
+ if (remaining_references)
+ {
+ // print out alive objects, but only if we actually have pending references
+ // note this will also print out internal live objects to the debug console
+ s_debug_device12->ReportLiveDeviceObjects(D3D12_RLDO_DETAIL);
+ }
+ SAFE_RELEASE(s_debug_device12);
+ }
+#endif
+
+ device12 = nullptr;
+ current_command_list = nullptr;
+
+ // unload DLLs
+ UnloadDXGI();
+ UnloadD3DCompiler();
+ UnloadD3D();
+}
+
+const std::string VertexShaderVersionString()
+{
+ return "vs_5_0";
+}
+
+const std::string GeometryShaderVersionString()
+{
+ return "gs_5_0";
+}
+
+const std::string PixelShaderVersionString()
+{
+ return "ps_5_0";
+}
+
+D3DTexture2D* &GetBackBuffer()
+{
+ return s_backbuf[s_current_back_buf];
+}
+
+unsigned int GetBackBufferWidth()
+{
+ return s_xres;
+}
+
+unsigned int GetBackBufferHeight()
+{
+ return s_yres;
+}
+
+// Returns the maximum width/height of a texture.
+unsigned int GetMaxTextureSize()
+{
+ return D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION;
+}
+
+void Reset()
+{
+ command_list_mgr->ExecuteQueuedWork(true);
+
+ // release all back buffer references
+ for (UINT i = 0; i < ARRAYSIZE(s_backbuf); i++)
+ {
+ SAFE_RELEASE(s_backbuf[i]);
+ }
+
+ D3D::command_list_mgr->ImmediatelyDestroyAllResourcesScheduledForDestruction();
+
+ // resize swapchain buffers
+ RECT client;
+ GetClientRect(hWnd, &client);
+ s_xres = client.right - client.left;
+ s_yres = client.bottom - client.top;
+
+ CheckHR(s_swap_chain->ResizeBuffers(SWAP_CHAIN_BUFFER_COUNT, s_xres, s_yres, DXGI_FORMAT_R8G8B8A8_UNORM, 0));
+
+ // recreate back buffer textures
+
+ HRESULT hr = S_OK;
+
+ for (UINT i = 0; i < SWAP_CHAIN_BUFFER_COUNT; i++)
+ {
+ ID3D12Resource* buf12 = nullptr;
+ hr = s_swap_chain->GetBuffer(i, IID_PPV_ARGS(&buf12));
+
+ CHECK(SUCCEEDED(hr), "Retrieve back buffer texture");
+
+ s_backbuf[i] = new D3DTexture2D(buf12,
+ D3D11_BIND_RENDER_TARGET,
+ DXGI_FORMAT_UNKNOWN,
+ DXGI_FORMAT_UNKNOWN,
+ DXGI_FORMAT_UNKNOWN,
+ false,
+ D3D12_RESOURCE_STATE_PRESENT
+ );
+
+ SAFE_RELEASE(buf12);
+ SetDebugObjectName12(s_backbuf[i]->GetTex12(), "backbuffer texture");
+ }
+
+ // The 'about-to-be-presented' back buffer index is always set back to '0' upon ResizeBuffers, just like
+ // creating a new swap chain.
+ s_current_back_buf = 0;
+
+ s_backbuf[s_current_back_buf]->TransitionToResourceState(current_command_list, D3D12_RESOURCE_STATE_RENDER_TARGET);
+}
+
+bool BeginFrame()
+{
+ if (s_frame_in_progress)
+ {
+ PanicAlert("BeginFrame called although a frame is already in progress");
+ return false;
+ }
+ s_frame_in_progress = true;
+ return (device12 != nullptr);
+}
+
+void EndFrame()
+{
+ if (!s_frame_in_progress)
+ {
+ PanicAlert("EndFrame called although no frame is in progress");
+ return;
+ }
+ s_frame_in_progress = false;
+}
+
+void Present()
+{
+ // The Present function contains logic to ensure we never Present faster than Windows can
+ // send to the monitor. If we Present too fast, the Present call will start to block, and we'll be
+ // throttled - obviously not desired if vsync is disabled and the emulated CPU speed is > 100%.
+
+ // The throttling logic ensures that we don't Present more than twice in a given monitor vsync.
+ // This is accomplished through timing data - there is a programmatic way to determine if a
+ // Present call will block, however after investigation that is not feasible here (without invasive
+ // workarounds), due to the fact this method does not actually call Present - we just queue a Present
+ // command for the background thread to dispatch.
+
+ // The monitor refresh rate is determined in Create().
+
+ static LARGE_INTEGER s_last_present_qpc;
+
+ LARGE_INTEGER current_qpc;
+ QueryPerformanceCounter(¤t_qpc);
+
+ const double time_elapsed_since_last_present = static_cast(current_qpc.QuadPart - s_last_present_qpc.QuadPart) / s_qpc_frequency.QuadPart;
+
+ unsigned int present_flags = 0;
+
+ if (g_ActiveConfig.IsVSync() == false &&
+ time_elapsed_since_last_present < (1.0 / static_cast(s_monitor_refresh_rate)) / 2.0
+ )
+ {
+ present_flags = DXGI_PRESENT_TEST; // Causes Present to be a no-op.
+ }
+ else
+ {
+ s_last_present_qpc = current_qpc;
+
+ s_backbuf[s_current_back_buf]->TransitionToResourceState(current_command_list, D3D12_RESOURCE_STATE_PRESENT);
+ s_current_back_buf = (s_current_back_buf + 1) % SWAP_CHAIN_BUFFER_COUNT;
+ }
+
+ command_list_mgr->ExecuteQueuedWorkAndPresent(s_swap_chain, g_ActiveConfig.IsVSync() ? 1 : 0, present_flags);
+
+ command_list_mgr->m_cpu_access_last_frame = command_list_mgr->m_cpu_access_this_frame;
+ command_list_mgr->m_cpu_access_this_frame = false;
+ command_list_mgr->m_draws_since_last_execution = 0;
+}
+
+HRESULT SetFullscreenState(bool enable_fullscreen)
+{
+ return S_OK;
+}
+
+HRESULT GetFullscreenState(bool* fullscreen_state)
+{
+ // Fullscreen exclusive intentionally not supported in DX12 backend. No performance
+ // difference between it and windowed full-screen due to usage of a FLIP swap chain.
+ *fullscreen_state = false;
+ return S_OK;
+}
+
+} // namespace D3D
+
+} // namespace DX12
diff --git a/Source/Core/VideoBackends/D3D12/D3DBase.h b/Source/Core/VideoBackends/D3D12/D3DBase.h
new file mode 100644
index 0000000000..b3ae9555a1
--- /dev/null
+++ b/Source/Core/VideoBackends/D3D12/D3DBase.h
@@ -0,0 +1,158 @@
+// Copyright 2010 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#define USE_D3D12_QUEUED_COMMAND_LISTS
+
+// D3D12TODO: Support this from Graphics Settings, not require a recompile to enable.
+//#define USE_D3D12_DEBUG_LAYER
+
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+
+#include "../../Externals/d3dx12/d3dx12.h"
+
+#include "Common/Common.h"
+#include "Common/CommonTypes.h"
+#include "Common/MsgHandler.h"
+
+namespace DX12
+{
+
+#define SAFE_RELEASE(x) { if (x) (x)->Release(); (x) = nullptr; }
+#define SAFE_DELETE(x) { delete (x); (x) = nullptr; }
+#define SAFE_DELETE_ARRAY(x) { delete[] (x); (x) = nullptr; }
+#define CHECK(cond, Message, ...) if (!(cond)) { __debugbreak(); PanicAlert(__FUNCTION__ " failed in %s at line %d: " Message, __FILE__, __LINE__, __VA_ARGS__); }
+
+// DEBUGCHECK is for high-frequency functions that we only want to check on debug builds.
+#if defined(_DEBUG) || defined(DEBUGFAST)
+#define DEBUGCHECK(cond, Message, ...) if (!(cond)) { PanicAlert(__FUNCTION__ " failed in %s at line %d: " Message, __FILE__, __LINE__, __VA_ARGS__); }
+#else
+#define DEBUGCHECK(cond, Message, ...)
+#endif
+
+inline void CheckHR(HRESULT hr)
+{
+ CHECK(SUCCEEDED(hr), "Failed HRESULT.");
+}
+
+class D3DCommandListManager;
+class D3DDescriptorHeapManager;
+class D3DTexture2D;
+
+namespace D3D
+{
+
+#define DESCRIPTOR_TABLE_PS_SRV 0
+#define DESCRIPTOR_TABLE_PS_SAMPLER 1
+#define DESCRIPTOR_TABLE_GS_CBV 2
+#define DESCRIPTOR_TABLE_VS_CBV 3
+// #define DESCRIPTOR_TABLE_PS_UAV 4
+#define DESCRIPTOR_TABLE_PS_CBVONE 4
+#define DESCRIPTOR_TABLE_PS_CBVTWO 5
+
+HRESULT LoadDXGI();
+HRESULT LoadD3D();
+HRESULT LoadD3DCompiler();
+void UnloadDXGI();
+void UnloadD3D();
+void UnloadD3DCompiler();
+
+D3D_FEATURE_LEVEL GetFeatureLevel(IDXGIAdapter* adapter);
+std::vector EnumAAModes(IDXGIAdapter* adapter);
+
+bool AlertUserIfSelectedAdapterDoesNotSupportD3D12();
+
+HRESULT Create(HWND wnd);
+
+void CreateDescriptorHeaps();
+void CreateRootSignatures();
+
+void WaitForOutstandingRenderingToComplete();
+void Close();
+
+extern ID3D12Device* device12;
+
+extern unsigned int resource_descriptor_size;
+extern unsigned int sampler_descriptor_size;
+extern D3DDescriptorHeapManager* gpu_descriptor_heap_mgr;
+extern D3DDescriptorHeapManager* sampler_descriptor_heap_mgr;
+extern D3DDescriptorHeapManager* dsv_descriptor_heap_mgr;
+extern D3DDescriptorHeapManager* rtv_descriptor_heap_mgr;
+extern std::array gpu_descriptor_heaps;
+
+
+extern D3D12_CPU_DESCRIPTOR_HANDLE null_srv_cpu;
+extern D3D12_CPU_DESCRIPTOR_HANDLE null_srv_cpu_shadow;
+
+extern D3DCommandListManager* command_list_mgr;
+extern ID3D12GraphicsCommandList* current_command_list;
+
+extern ID3D12RootSignature* default_root_signature;
+
+extern HWND hWnd;
+
+void Reset();
+bool BeginFrame();
+void EndFrame();
+void Present();
+
+unsigned int GetBackBufferWidth();
+unsigned int GetBackBufferHeight();
+D3DTexture2D*& GetBackBuffer();
+const std::string PixelShaderVersionString();
+const std::string GeometryShaderVersionString();
+const std::string VertexShaderVersionString();
+
+unsigned int GetMaxTextureSize();
+
+HRESULT SetFullscreenState(bool enable_fullscreen);
+HRESULT GetFullscreenState(bool* fullscreen_state);
+
+// This function will assign a name to the given resource.
+// The DirectX debug layer will make it easier to identify resources that way,
+// e.g. when listing up all resources who have unreleased references.
+static void SetDebugObjectName12(ID3D12Resource* resource, LPCSTR name)
+{
+ HRESULT hr = resource->SetPrivateData(WKPDID_D3DDebugObjectName, (UINT)(name ? strlen(name) : 0), name);
+ if (FAILED(hr))
+ {
+ throw std::exception("Failure setting name for D3D12 object");
+ }
+}
+
+static std::string GetDebugObjectName12(ID3D12Resource* resource)
+{
+ std::string name;
+ if (resource)
+ {
+ UINT size = 0;
+ resource->GetPrivateData(WKPDID_D3DDebugObjectName, &size, nullptr); //get required size
+ name.resize(size);
+ resource->GetPrivateData(WKPDID_D3DDebugObjectName, &size, const_cast(name.data()));
+ }
+}
+
+} // namespace D3D
+
+using CREATEDXGIFACTORY = HRESULT(WINAPI*)(REFIID, void**);
+extern CREATEDXGIFACTORY create_dxgi_factory;
+
+using D3D12CREATEDEVICE = HRESULT(WINAPI*)(IUnknown*, D3D_FEATURE_LEVEL, REFIID, void**);
+using D3D12SERIALIZEROOTSIGNATURE = HRESULT(WINAPI*)(const D3D12_ROOT_SIGNATURE_DESC* pRootSignature, D3D_ROOT_SIGNATURE_VERSION Version, ID3DBlob** ppBlob, ID3DBlob** ppErrorBlob);
+using D3D12GETDEBUGINTERFACE = HRESULT(WINAPI*)(REFIID riid, void** ppvDebug);
+
+using D3DREFLECT = HRESULT(WINAPI*)(LPCVOID, SIZE_T, REFIID, void**);
+extern D3DREFLECT d3d_reflect;
+
+using D3DCREATEBLOB = HRESULT(WINAPI*)(SIZE_T, ID3DBlob**);
+extern D3DCREATEBLOB d3d_create_blob;
+
+extern pD3DCompile d3d_compile;
+
+} // namespace DX12
diff --git a/Source/Core/VideoBackends/D3D12/D3DCommandListManager.cpp b/Source/Core/VideoBackends/D3D12/D3DCommandListManager.cpp
new file mode 100644
index 0000000000..fc4a98ae84
--- /dev/null
+++ b/Source/Core/VideoBackends/D3D12/D3DCommandListManager.cpp
@@ -0,0 +1,355 @@
+// Copyright 2015 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include
+#include
+
+#include "VideoBackends/D3D12/D3DBase.h"
+#include "VideoBackends/D3D12/D3DCommandListManager.h"
+#include "VideoBackends/D3D12/D3DDescriptorHeapManager.h"
+#include "VideoBackends/D3D12/D3DQueuedCommandList.h"
+#include "VideoBackends/D3D12/D3DState.h"
+#include "VideoBackends/D3D12/D3DTexture.h"
+
+#include "VideoBackends/D3D12/Render.h"
+#include "VideoBackends/D3D12/ShaderConstantsManager.h"
+#include "VideoBackends/D3D12/VertexManager.h"
+
+static constexpr unsigned int COMMAND_ALLOCATORS_PER_LIST = 2;
+
+namespace DX12
+{
+extern StateCache gx_state_cache;
+
+D3DCommandListManager::D3DCommandListManager(
+ D3D12_COMMAND_LIST_TYPE command_list_type,
+ ID3D12Device* device,
+ ID3D12CommandQueue* command_queue
+ ) :
+ m_device(device),
+ m_command_queue(command_queue)
+{
+ // Create two lists, with two command allocators each. This corresponds to up to two frames in flight at once.
+ m_current_command_allocator = 0;
+ m_current_command_allocator_list = 0;
+ for (UINT i = 0; i < COMMAND_ALLOCATORS_PER_LIST; i++)
+ {
+ for (UINT j = 0; j < m_command_allocator_lists.size(); j++)
+ {
+ ID3D12CommandAllocator* command_allocator = nullptr;
+
+ CheckHR(m_device->CreateCommandAllocator(command_list_type, IID_PPV_ARGS(&command_allocator)));
+ m_command_allocator_lists[j].push_back(command_allocator);
+ }
+ }
+
+ // Create backing command list.
+ CheckHR(m_device->CreateCommandList(0, command_list_type, m_command_allocator_lists[m_current_command_allocator_list][0], nullptr, IID_PPV_ARGS(&m_backing_command_list)));
+
+#ifdef USE_D3D12_QUEUED_COMMAND_LISTS
+ m_queued_command_list = new ID3D12QueuedCommandList(m_backing_command_list, m_command_queue);
+#endif
+
+ // Create fence that will be used to measure GPU progress of app rendering requests (e.g. CPU readback of GPU data).
+ m_queue_fence_value = 0;
+ CheckHR(m_device->CreateFence(m_queue_fence_value, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&m_queue_fence)));
+
+ // Create fence that will be used internally by D3DCommandListManager for frame-level resource tracking.
+ m_queue_frame_fence_value = 0;
+ CheckHR(m_device->CreateFence(m_queue_frame_fence_value, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&m_queue_frame_fence)));
+
+ // Create event that will be used for waiting on CPU until a fence is signaled by GPU.
+ m_wait_on_cpu_fence_event = CreateEvent(nullptr, FALSE, FALSE, nullptr);
+
+ // Pre-size the deferred destruction lists.
+ for (UINT i = 0; i < m_deferred_destruction_lists.size(); i++)
+ {
+ m_deferred_destruction_lists[i].reserve(200);
+ }
+
+ m_current_deferred_destruction_list = 0;
+}
+
+void D3DCommandListManager::SetInitialCommandListState()
+{
+ ID3D12GraphicsCommandList* command_list = nullptr;
+ GetCommandList(&command_list);
+
+ command_list->SetDescriptorHeaps(static_cast(D3D::gpu_descriptor_heaps.size()), D3D::gpu_descriptor_heaps.data());
+ command_list->SetGraphicsRootSignature(D3D::default_root_signature);
+
+ if (g_renderer)
+ {
+ // It is possible that we change command lists in the middle of the frame. In that case, restore
+ // the viewport/scissor to the current console GPU state.
+ g_renderer->RestoreAPIState();
+ }
+
+ m_command_list_dirty_state = UINT_MAX;
+
+ command_list->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
+ m_command_list_current_topology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP;
+
+ if (g_vertex_manager)
+ reinterpret_cast(g_vertex_manager.get())->SetIndexBuffer();
+}
+
+void D3DCommandListManager::GetCommandList(ID3D12GraphicsCommandList** command_list) const
+{
+#ifdef USE_D3D12_QUEUED_COMMAND_LISTS
+ *command_list = this->m_queued_command_list;
+#else
+ *command_list = this->m_backing_command_list;
+#endif
+}
+
+void D3DCommandListManager::ExecuteQueuedWork(bool wait_for_gpu_completion)
+{
+ m_queue_fence_value++;
+
+#ifdef USE_D3D12_QUEUED_COMMAND_LISTS
+ CheckHR(m_queued_command_list->Close());
+ m_queued_command_list->QueueExecute();
+
+ m_queued_command_list->QueueFenceGpuSignal(m_queue_fence, m_queue_fence_value);
+
+ ResetCommandListWithIdleCommandAllocator();
+
+ m_queued_command_list->ProcessQueuedItems();
+#else
+ CheckHR(m_backing_command_list->Close());
+
+ ID3D12CommandList* const commandListsToExecute[1] = { m_backing_command_list };
+ m_command_queue->ExecuteCommandLists(1, commandListsToExecute);
+
+ if (wait_for_gpu_completion)
+ {
+ CheckHR(m_command_queue->Signal(m_queue_fence, m_queue_fence_value));
+ }
+
+ if (m_current_command_allocator == 0)
+ {
+ PerformGpuRolloverChecks();
+ }
+
+ ResetCommandListWithIdleCommandAllocator();
+#endif
+
+ for (auto it : m_queue_fence_callbacks)
+ it.second(it.first, m_queue_fence_value);
+
+ SetInitialCommandListState();
+
+ if (wait_for_gpu_completion)
+ {
+ WaitOnCPUForFence(m_queue_fence, m_queue_fence_value);
+ }
+}
+
+void D3DCommandListManager::ExecuteQueuedWorkAndPresent(IDXGISwapChain* swap_chain, UINT sync_interval, UINT flags)
+{
+ m_queue_fence_value++;
+
+#ifdef USE_D3D12_QUEUED_COMMAND_LISTS
+ CheckHR(m_queued_command_list->Close());
+ m_queued_command_list->QueueExecute();
+ m_queued_command_list->QueueFenceGpuSignal(m_queue_fence, m_queue_fence_value);
+ m_queued_command_list->QueuePresent(swap_chain, sync_interval, flags);
+ m_queued_command_list->ProcessQueuedItems(true);
+
+ if (m_current_command_allocator == 0)
+ {
+ PerformGpuRolloverChecks();
+ }
+
+ m_current_command_allocator = (m_current_command_allocator + 1) % m_command_allocator_lists[m_current_command_allocator_list].size();
+
+ ResetCommandListWithIdleCommandAllocator();
+
+ SetInitialCommandListState();
+#else
+ ExecuteQueuedWork();
+ m_command_queue->Signal(m_queue_fence, m_queue_fence_value);
+ CheckHR(swap_chain->Present(sync_interval, flags));
+#endif
+
+ for (auto it : m_queue_fence_callbacks)
+ it.second(it.first, m_queue_fence_value);
+}
+
+void D3DCommandListManager::WaitForQueuedWorkToBeExecutedOnGPU()
+{
+ // Wait for GPU to finish all outstanding work.
+ m_queue_fence_value++;
+
+#ifdef USE_D3D12_QUEUED_COMMAND_LISTS
+ m_queued_command_list->QueueExecute();
+ m_queued_command_list->QueueFenceGpuSignal(m_queue_fence, m_queue_fence_value);
+
+ m_queued_command_list->ProcessQueuedItems(true);
+#else
+ CheckHR(m_command_queue->Signal(m_queue_fence, m_queue_fence_value));
+#endif
+
+ WaitOnCPUForFence(m_queue_fence, m_queue_fence_value);
+}
+
+void D3DCommandListManager::PerformGpuRolloverChecks()
+{
+ // Insert fence to measure GPU progress, ensure we aren't using in-use command allocators.
+ if (m_queue_frame_fence->GetCompletedValue() < m_queue_frame_fence_value)
+ {
+ WaitOnCPUForFence(m_queue_frame_fence, m_queue_frame_fence_value);
+ }
+
+ // We now know that the previous 'set' of command lists has completed on GPU, and it is safe to
+ // release resources / start back at beginning of command allocator list.
+
+ // Begin Deferred Resource Destruction
+ UINT safe_to_delete_deferred_destruction_list = (m_current_deferred_destruction_list - 1) % m_deferred_destruction_lists.size();
+
+ for (UINT i = 0; i < m_deferred_destruction_lists[safe_to_delete_deferred_destruction_list].size(); i++)
+ {
+ CHECK(m_deferred_destruction_lists[safe_to_delete_deferred_destruction_list][i]->Release() == 0, "Resource leak.");
+ }
+
+ m_deferred_destruction_lists[safe_to_delete_deferred_destruction_list].clear();
+
+ m_current_deferred_destruction_list = (m_current_deferred_destruction_list + 1) % m_deferred_destruction_lists.size();
+ // End Deferred Resource Destruction
+
+
+ // Begin Command Allocator Resets
+ UINT safe_to_reset_command_allocator_list = (m_current_command_allocator_list - 1) % m_command_allocator_lists.size();
+
+ for (UINT i = 0; i < m_command_allocator_lists[safe_to_reset_command_allocator_list].size(); i++)
+ {
+ CheckHR(m_command_allocator_lists[safe_to_reset_command_allocator_list][i]->Reset());
+ }
+
+ m_current_command_allocator_list = (m_current_command_allocator_list + 1) % m_command_allocator_lists.size();
+ // End Command Allocator Resets
+
+ m_queue_frame_fence_value++;
+#ifdef USE_D3D12_QUEUED_COMMAND_LISTS
+ m_queued_command_list->QueueFenceGpuSignal(m_queue_frame_fence, m_queue_frame_fence_value);
+#else
+ CheckHR(m_command_queue->Signal(m_queue_frame_fence, m_queue_frame_fence_value));
+#endif
+}
+
+void D3DCommandListManager::ResetCommandListWithIdleCommandAllocator()
+{
+#ifdef USE_D3D12_QUEUED_COMMAND_LISTS
+ ID3D12QueuedCommandList* command_list = m_queued_command_list;
+#else
+ ID3D12GraphicsCommandList* command_list = m_backing_command_list;
+#endif
+
+ CheckHR(command_list->Reset(m_command_allocator_lists[m_current_command_allocator_list][m_current_command_allocator], nullptr));
+}
+
+void D3DCommandListManager::DestroyResourceAfterCurrentCommandListExecuted(ID3D12Resource* resource)
+{
+ CHECK(resource, "Null resource being inserted!");
+
+ m_deferred_destruction_lists[m_current_deferred_destruction_list].push_back(resource);
+}
+
+void D3DCommandListManager::ImmediatelyDestroyAllResourcesScheduledForDestruction()
+{
+ for (auto& destruction_list : m_deferred_destruction_lists)
+ {
+ for (auto& resource : destruction_list)
+ resource->Release();
+
+ destruction_list.clear();
+ }
+}
+
+void D3DCommandListManager::ClearQueueAndWaitForCompletionOfInflightWork()
+{
+ // Wait for GPU to finish all outstanding work.
+ m_queue_fence_value++;
+#ifdef USE_D3D12_QUEUED_COMMAND_LISTS
+ m_queued_command_list->ClearQueue(); // Waits for currently-processing work to finish, then clears queue.
+ m_queued_command_list->QueueFenceGpuSignal(m_queue_fence, m_queue_fence_value);
+ m_queued_command_list->ProcessQueuedItems(true);
+#else
+ CheckHR(m_command_queue->Signal(m_queue_fence, m_queue_fence_value));
+#endif
+ WaitOnCPUForFence(m_queue_fence, m_queue_fence_value);
+}
+
+D3DCommandListManager::~D3DCommandListManager()
+{
+ ImmediatelyDestroyAllResourcesScheduledForDestruction();
+
+#ifdef USE_D3D12_QUEUED_COMMAND_LISTS
+ m_queued_command_list->Release();
+#endif
+ m_backing_command_list->Release();
+
+ for (auto& allocator_list : m_command_allocator_lists)
+ {
+ for (auto& resource : allocator_list)
+ resource->Release();
+ }
+
+ m_queue_fence->Release();
+ m_queue_frame_fence->Release();
+
+ CloseHandle(m_wait_on_cpu_fence_event);
+}
+
+void D3DCommandListManager::WaitOnCPUForFence(ID3D12Fence* fence, UINT64 fence_value)
+{
+ CheckHR(fence->SetEventOnCompletion(fence_value, m_wait_on_cpu_fence_event));
+
+ WaitForSingleObject(m_wait_on_cpu_fence_event, INFINITE);
+}
+
+void D3DCommandListManager::SetCommandListDirtyState(unsigned int command_list_state, bool dirty)
+{
+ if (dirty)
+ m_command_list_dirty_state |= command_list_state;
+ else
+ m_command_list_dirty_state &= ~command_list_state;
+}
+
+bool D3DCommandListManager::GetCommandListDirtyState(COMMAND_LIST_STATE command_list_state) const
+{
+ return ((m_command_list_dirty_state & command_list_state) != 0);
+}
+
+void D3DCommandListManager::SetCommandListPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY primitive_topology)
+{
+ m_command_list_current_topology = primitive_topology;
+}
+
+D3D_PRIMITIVE_TOPOLOGY D3DCommandListManager::GetCommandListPrimitiveTopology() const
+{
+ return m_command_list_current_topology;
+}
+
+void D3DCommandListManager::CPUAccessNotify()
+{
+ m_cpu_access_last_frame = true;
+ m_cpu_access_this_frame = true;
+ m_draws_since_last_execution = 0;
+};
+
+ID3D12Fence* D3DCommandListManager::RegisterQueueFenceCallback(void* owning_object, PFN_QUEUE_FENCE_CALLBACK* callback_function)
+{
+ m_queue_fence_callbacks[owning_object] = callback_function;
+
+ return m_queue_fence;
+}
+
+void D3DCommandListManager::RemoveQueueFenceCallback(void* owning_object)
+{
+ m_queue_fence_callbacks.erase(owning_object);
+}
+
+} // namespace DX12
\ No newline at end of file
diff --git a/Source/Core/VideoBackends/D3D12/D3DCommandListManager.h b/Source/Core/VideoBackends/D3D12/D3DCommandListManager.h
new file mode 100644
index 0000000000..b9622df5fc
--- /dev/null
+++ b/Source/Core/VideoBackends/D3D12/D3DCommandListManager.h
@@ -0,0 +1,98 @@
+// Copyright 2015 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include
+#include