diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 3bd9b9755..b558f9532 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -121,6 +121,20 @@ target_link_libraries(common PRIVATE glad stb Threads::Threads libchdr glslang v
if(WIN32)
target_sources(common PRIVATE
+ d3d12/context.cpp
+ d3d12/context.h
+ d3d12/descriptor_heap_manager.cpp
+ d3d12/descriptor_heap_manager.h
+ d3d12/shader_cache.cpp
+ d3d12/shader_cache.h
+ d3d12/staging_texture.cpp
+ d3d12/staging_texture.h
+ d3d12/stream_buffer.cpp
+ d3d12/stream_buffer.h
+ d3d12/texture.cpp
+ d3d12/texture.h
+ d3d12/util.cpp
+ d3d12/util.h
d3d11/shader_cache.cpp
d3d11/shader_cache.h
d3d11/shader_compiler.cpp
diff --git a/src/common/common.vcxproj b/src/common/common.vcxproj
index c49885fb0..797d14ed2 100644
--- a/src/common/common.vcxproj
+++ b/src/common/common.vcxproj
@@ -18,6 +18,13 @@
+
+
+
+
+
+
+
@@ -98,6 +105,13 @@
+
+
+
+
+
+
+
diff --git a/src/common/common.vcxproj.filters b/src/common/common.vcxproj.filters
index 169ad7bf8..1b68fbbbb 100644
--- a/src/common/common.vcxproj.filters
+++ b/src/common/common.vcxproj.filters
@@ -113,6 +113,27 @@
+
+ d3d12
+
+
+ d3d12
+
+
+ d3d12
+
+
+ d3d12
+
+
+ d3d12
+
+
+ d3d12
+
+
+ d3d12
+
@@ -221,6 +242,27 @@
+
+ d3d12
+
+
+ d3d12
+
+
+ d3d12
+
+
+ d3d12
+
+
+ d3d12
+
+
+ d3d12
+
+
+ d3d12
+
@@ -238,5 +280,8 @@
{fd4150b0-6f82-4251-ab23-34c25fbc5b5e}
+
+ {358e11c4-34af-4169-9a66-ec66342a6a2f}
+
diff --git a/src/common/d3d12/context.cpp b/src/common/d3d12/context.cpp
new file mode 100644
index 000000000..43e49370a
--- /dev/null
+++ b/src/common/d3d12/context.cpp
@@ -0,0 +1,453 @@
+// Copyright 2019 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include "context.h"
+#include "../assert.h"
+#include "../log.h"
+#include "../scope_guard.h"
+#include
+#include
+#include
+#include
+#include
+Log_SetChannel(D3D12::Context);
+
+std::unique_ptr g_d3d12_context;
+
+namespace D3D12 {
+
+#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
+
+// Private D3D12 state
+static HMODULE s_d3d12_library;
+static PFN_D3D12_CREATE_DEVICE s_d3d12_create_device;
+static PFN_D3D12_GET_DEBUG_INTERFACE s_d3d12_get_debug_interface;
+static PFN_D3D12_SERIALIZE_ROOT_SIGNATURE s_d3d12_serialize_root_signature;
+
+static bool LoadD3D12Library()
+{
+ if (!(s_d3d12_library = LoadLibrary("d3d12.dll")) ||
+ !(s_d3d12_create_device =
+ reinterpret_cast(GetProcAddress(s_d3d12_library, "D3D12CreateDevice"))) ||
+ !(s_d3d12_get_debug_interface =
+ reinterpret_cast(GetProcAddress(s_d3d12_library, "D3D12GetDebugInterface"))) ||
+ !(s_d3d12_serialize_root_signature = reinterpret_cast(
+ GetProcAddress(s_d3d12_library, "D3D12SerializeRootSignature"))))
+ {
+ Log_ErrorPrintf("d3d12.dll could not be loaded.");
+ s_d3d12_create_device = nullptr;
+ s_d3d12_get_debug_interface = nullptr;
+ s_d3d12_serialize_root_signature = nullptr;
+ if (s_d3d12_library)
+ FreeLibrary(s_d3d12_library);
+ s_d3d12_library = nullptr;
+ return false;
+ }
+
+ return true;
+}
+
+static void UnloadD3D12Library()
+{
+ s_d3d12_serialize_root_signature = nullptr;
+ s_d3d12_get_debug_interface = nullptr;
+ s_d3d12_create_device = nullptr;
+ if (s_d3d12_library)
+ {
+ FreeLibrary(s_d3d12_library);
+ s_d3d12_library = nullptr;
+ }
+}
+
+#else
+
+static const PFN_D3D12_CREATE_DEVICE s_d3d12_create_device = D3D12CreateDevice;
+static const PFN_D3D12_GET_DEBUG_INTERFACE s_d3d12_get_debug_interface = D3D12GetDebugInterface;
+static const PFN_D3D12_SERIALIZE_ROOT_SIGNATURE s_d3d12_serialize_root_signature = D3D12SerializeRootSignature;
+
+static bool LoadD3D12Library()
+{
+ return true;
+}
+
+static void UnloadD3D12Library() {}
+
+#endif
+
+Context::Context() = default;
+
+Context::~Context()
+{
+ DestroyResources();
+}
+
+Context::ComPtr Context::SerializeRootSignature(const D3D12_ROOT_SIGNATURE_DESC* desc)
+{
+ ComPtr blob;
+ ComPtr error_blob;
+ const HRESULT hr = s_d3d12_serialize_root_signature(desc, D3D_ROOT_SIGNATURE_VERSION_1, blob.GetAddressOf(),
+ error_blob.GetAddressOf());
+ if (FAILED(hr))
+ {
+ Log_ErrorPrintf("D3D12SerializeRootSignature() failed: %08X", hr);
+ if (error_blob)
+ Log_ErrorPrintf("%s", error_blob->GetBufferPointer());
+
+ return {};
+ }
+
+ return blob;
+}
+
+D3D12::Context::ComPtr Context::CreateRootSignature(const D3D12_ROOT_SIGNATURE_DESC* desc)
+{
+ ComPtr blob = SerializeRootSignature(desc);
+ if (!blob)
+ return {};
+
+ ComPtr rs;
+ const HRESULT hr =
+ m_device->CreateRootSignature(0, blob->GetBufferPointer(), blob->GetBufferSize(), IID_PPV_ARGS(rs.GetAddressOf()));
+ if (FAILED(hr))
+ {
+ Log_ErrorPrintf("CreateRootSignature() failed: %08X", hr);
+ return {};
+ }
+
+ return rs;
+}
+
+bool Context::SupportsTextureFormat(DXGI_FORMAT format)
+{
+ constexpr u32 required = D3D12_FORMAT_SUPPORT1_TEXTURE2D | D3D12_FORMAT_SUPPORT1_SHADER_SAMPLE;
+
+ D3D12_FEATURE_DATA_FORMAT_SUPPORT support = {format};
+ return SUCCEEDED(m_device->CheckFeatureSupport(D3D12_FEATURE_FORMAT_SUPPORT, &support, sizeof(support))) &&
+ (support.Support1 & required) == required;
+}
+
+bool Context::Create(IDXGIFactory* dxgi_factory, u32 adapter_index, bool enable_debug_layer)
+{
+ Assert(!g_d3d12_context);
+
+ if (!LoadD3D12Library())
+ return false;
+
+ g_d3d12_context.reset(new Context());
+ if (!g_d3d12_context->CreateDevice(dxgi_factory, adapter_index, enable_debug_layer) ||
+ !g_d3d12_context->CreateCommandQueue() || !g_d3d12_context->CreateFence() ||
+ !g_d3d12_context->CreateDescriptorHeaps() || !g_d3d12_context->CreateCommandLists() ||
+ !g_d3d12_context->CreateTextureStreamBuffer())
+ {
+ Destroy();
+ return false;
+ }
+
+ return true;
+}
+
+void Context::Destroy()
+{
+ if (g_d3d12_context)
+ g_d3d12_context.reset();
+
+ UnloadD3D12Library();
+}
+
+bool Context::CreateDevice(IDXGIFactory* dxgi_factory, u32 adapter_index, bool enable_debug_layer)
+{
+ ComPtr adapter;
+ HRESULT hr = dxgi_factory->EnumAdapters(adapter_index, &adapter);
+ if (FAILED(hr))
+ {
+ Log_ErrorPrintf("Adapter %u not found, using default", adapter_index);
+ adapter = nullptr;
+ }
+ else
+ {
+ DXGI_ADAPTER_DESC adapter_desc;
+ if (SUCCEEDED(adapter->GetDesc(&adapter_desc)))
+ {
+ char adapter_name_buffer[128];
+ const int name_length = WideCharToMultiByte(CP_UTF8, 0, adapter_desc.Description,
+ static_cast(std::wcslen(adapter_desc.Description)),
+ adapter_name_buffer, countof(adapter_name_buffer), 0, nullptr);
+ if (name_length >= 0)
+ {
+ adapter_name_buffer[name_length] = 0;
+ Log_InfoPrintf("D3D Adapter: %s", adapter_name_buffer);
+ }
+ }
+ }
+
+ // Enabling the debug layer will fail if the Graphics Tools feature is not installed.
+ if (enable_debug_layer)
+ {
+ hr = s_d3d12_get_debug_interface(IID_PPV_ARGS(&m_debug_interface));
+ if (SUCCEEDED(hr))
+ {
+ m_debug_interface->EnableDebugLayer();
+ }
+ else
+ {
+ Log_ErrorPrintf("Debug layer requested but not available.");
+ enable_debug_layer = false;
+ }
+ }
+
+ // Create the actual device.
+ hr = s_d3d12_create_device(adapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&m_device));
+ AssertMsg(SUCCEEDED(hr), "Create D3D12 device");
+ if (FAILED(hr))
+ return false;
+
+ if (enable_debug_layer)
+ {
+ ComPtr info_queue;
+ if (SUCCEEDED(m_device.As(&info_queue)))
+ {
+ info_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_ERROR, TRUE);
+ info_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_WARNING, TRUE);
+
+ D3D12_INFO_QUEUE_FILTER filter = {};
+ std::array id_list{
+ D3D12_MESSAGE_ID_CLEARRENDERTARGETVIEW_MISMATCHINGCLEARVALUE,
+ D3D12_MESSAGE_ID_CLEARDEPTHSTENCILVIEW_MISMATCHINGCLEARVALUE,
+ D3D12_MESSAGE_ID_CREATEGRAPHICSPIPELINESTATE_RENDERTARGETVIEW_NOT_SET,
+ D3D12_MESSAGE_ID_CREATEINPUTLAYOUT_TYPE_MISMATCH,
+ D3D12_MESSAGE_ID_DRAW_EMPTY_SCISSOR_RECTANGLE,
+ };
+ filter.DenyList.NumIDs = static_cast(id_list.size());
+ filter.DenyList.pIDList = id_list.data();
+ info_queue->PushStorageFilter(&filter);
+ }
+ }
+
+ return true;
+}
+
+bool Context::CreateCommandQueue()
+{
+ const D3D12_COMMAND_QUEUE_DESC queue_desc = {D3D12_COMMAND_LIST_TYPE_DIRECT, D3D12_COMMAND_QUEUE_PRIORITY_NORMAL,
+ D3D12_COMMAND_QUEUE_FLAG_NONE};
+ HRESULT hr = m_device->CreateCommandQueue(&queue_desc, IID_PPV_ARGS(&m_command_queue));
+ AssertMsg(SUCCEEDED(hr), "Create command queue");
+ return SUCCEEDED(hr);
+}
+
+bool Context::CreateFence()
+{
+ HRESULT hr = m_device->CreateFence(m_completed_fence_value, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&m_fence));
+ AssertMsg(SUCCEEDED(hr), "Create fence");
+ if (FAILED(hr))
+ return false;
+
+ m_fence_event = CreateEvent(nullptr, FALSE, FALSE, nullptr);
+ AssertMsg(m_fence_event != NULL, "Create fence event");
+ if (!m_fence_event)
+ return false;
+
+ return true;
+}
+
+bool Context::CreateDescriptorHeaps()
+{
+ static constexpr size_t MAX_SRVS = 16384;
+ static constexpr size_t MAX_RTVS = 8192;
+ static constexpr size_t MAX_DSVS = 128;
+ static constexpr size_t MAX_SAMPLERS = 128;
+
+ if (!m_descriptor_heap_manager.Create(m_device.Get(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, MAX_SRVS, true) ||
+ !m_rtv_heap_manager.Create(m_device.Get(), D3D12_DESCRIPTOR_HEAP_TYPE_RTV, MAX_RTVS, false) ||
+ !m_dsv_heap_manager.Create(m_device.Get(), D3D12_DESCRIPTOR_HEAP_TYPE_DSV, MAX_DSVS, false) ||
+ !m_sampler_heap_manager.Create(m_device.Get(), D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, MAX_SAMPLERS, true))
+ {
+ return false;
+ }
+
+ m_gpu_descriptor_heaps[0] = m_descriptor_heap_manager.GetDescriptorHeap();
+ m_gpu_descriptor_heaps[1] = m_sampler_heap_manager.GetDescriptorHeap();
+
+ // Allocate null SRV descriptor for unbound textures.
+ constexpr D3D12_SHADER_RESOURCE_VIEW_DESC null_srv_desc = {DXGI_FORMAT_R8G8B8A8_UNORM, D3D12_SRV_DIMENSION_TEXTURE2D,
+ D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING};
+
+ if (!m_descriptor_heap_manager.Allocate(&m_null_srv_descriptor))
+ {
+ Panic("Failed to allocate null descriptor");
+ return false;
+ }
+
+ m_device->CreateShaderResourceView(nullptr, &null_srv_desc, m_null_srv_descriptor.cpu_handle);
+ return true;
+}
+
+bool Context::CreateCommandLists()
+{
+ for (u32 i = 0; i < NUM_COMMAND_LISTS; i++)
+ {
+ CommandListResources& res = m_command_lists[i];
+ HRESULT hr = m_device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT,
+ IID_PPV_ARGS(res.command_allocator.GetAddressOf()));
+ AssertMsg(SUCCEEDED(hr), "Create command allocator");
+ if (FAILED(hr))
+ return false;
+
+ hr = m_device->CreateCommandList(1, D3D12_COMMAND_LIST_TYPE_DIRECT, res.command_allocator.Get(), nullptr,
+ IID_PPV_ARGS(res.command_list.GetAddressOf()));
+ if (FAILED(hr))
+ {
+ Log_ErrorPrintf("Failed to create command list: %08X", hr);
+ return false;
+ }
+
+ // Close the command list, since the first thing we do is reset them.
+ hr = res.command_list->Close();
+ AssertMsg(SUCCEEDED(hr), "Closing new command list failed");
+ if (FAILED(hr))
+ return false;
+ }
+
+ MoveToNextCommandList();
+ return true;
+}
+
+bool Context::CreateTextureStreamBuffer()
+{
+ return m_texture_stream_buffer.Create(TEXTURE_UPLOAD_BUFFER_SIZE);
+}
+
+void Context::MoveToNextCommandList()
+{
+ m_current_command_list = (m_current_command_list + 1) % NUM_COMMAND_LISTS;
+ m_current_fence_value++;
+
+ // We may have to wait if this command list hasn't finished on the GPU.
+ CommandListResources& res = m_command_lists[m_current_command_list];
+ WaitForFence(res.ready_fence_value);
+
+ // Begin command list.
+ res.command_allocator->Reset();
+ res.command_list->Reset(res.command_allocator.Get(), nullptr);
+ res.command_list->SetDescriptorHeaps(static_cast(m_gpu_descriptor_heaps.size()), m_gpu_descriptor_heaps.data());
+ res.ready_fence_value = m_current_fence_value;
+}
+
+void Context::ExecuteCommandList(bool wait_for_completion)
+{
+ CommandListResources& res = m_command_lists[m_current_command_list];
+
+ // Close and queue command list.
+ HRESULT hr = res.command_list->Close();
+ AssertMsg(SUCCEEDED(hr), "Close command list");
+ const std::array execute_lists{res.command_list.Get()};
+ m_command_queue->ExecuteCommandLists(static_cast(execute_lists.size()), execute_lists.data());
+
+ // Update fence when GPU has completed.
+ hr = m_command_queue->Signal(m_fence.Get(), m_current_fence_value);
+ AssertMsg(SUCCEEDED(hr), "Signal fence");
+
+ MoveToNextCommandList();
+ if (wait_for_completion)
+ WaitForFence(res.ready_fence_value);
+}
+
+void Context::DeferResourceDestruction(ID3D12Resource* resource)
+{
+ if (!resource)
+ return;
+
+ resource->AddRef();
+ m_command_lists[m_current_command_list].pending_resources.push_back(resource);
+}
+
+void Context::DeferDescriptorDestruction(DescriptorHeapManager& manager, u32 index)
+{
+ m_command_lists[m_current_command_list].pending_descriptors.emplace_back(manager, index);
+}
+
+void Context::DeferDescriptorDestruction(DescriptorHeapManager& manager, DescriptorHandle* handle)
+{
+ if (handle->index == DescriptorHandle::INVALID_INDEX)
+ return;
+
+ m_command_lists[m_current_command_list].pending_descriptors.emplace_back(manager, handle->index);
+ handle->Clear();
+}
+
+void Context::DestroyPendingResources(CommandListResources& cmdlist)
+{
+ for (const auto& dd : cmdlist.pending_descriptors)
+ dd.first.Free(dd.second);
+ cmdlist.pending_descriptors.clear();
+
+ for (ID3D12Resource* res : cmdlist.pending_resources)
+ res->Release();
+ cmdlist.pending_resources.clear();
+}
+
+void Context::DestroyResources()
+{
+ ExecuteCommandList(true);
+
+ m_texture_stream_buffer.Destroy(false);
+ m_descriptor_heap_manager.Free(&m_null_srv_descriptor);
+ m_sampler_heap_manager.Destroy();
+ m_dsv_heap_manager.Destroy();
+ m_rtv_heap_manager.Destroy();
+ m_descriptor_heap_manager.Destroy();
+ m_command_lists = {};
+ m_current_command_list = 0;
+ m_completed_fence_value = 0;
+ m_current_fence_value = 0;
+ if (m_fence_event)
+ {
+ CloseHandle(m_fence_event);
+ m_fence_event = {};
+ }
+
+ m_command_queue.Reset();
+ m_debug_interface.Reset();
+ m_device.Reset();
+}
+
+void Context::WaitForFence(u64 fence)
+{
+ if (m_completed_fence_value >= fence)
+ return;
+
+ // Try non-blocking check.
+ m_completed_fence_value = m_fence->GetCompletedValue();
+ if (m_completed_fence_value < fence)
+ {
+ // Fall back to event.
+ HRESULT hr = m_fence->SetEventOnCompletion(fence, m_fence_event);
+ AssertMsg(SUCCEEDED(hr), "Set fence event on completion");
+ WaitForSingleObject(m_fence_event, INFINITE);
+ m_completed_fence_value = m_fence->GetCompletedValue();
+ }
+
+ // Release resources for as many command lists which have completed.
+ u32 index = (m_current_command_list + 1) % NUM_COMMAND_LISTS;
+ for (u32 i = 0; i < NUM_COMMAND_LISTS; i++)
+ {
+ CommandListResources& res = m_command_lists[index];
+ if (m_completed_fence_value < res.ready_fence_value)
+ break;
+
+ DestroyPendingResources(res);
+ index = (index + 1) % NUM_COMMAND_LISTS;
+ }
+}
+
+void Context::WaitForGPUIdle()
+{
+ u32 index = (m_current_command_list + 1) % NUM_COMMAND_LISTS;
+ for (u32 i = 0; i < (NUM_COMMAND_LISTS - 1); i++)
+ {
+ WaitForFence(m_command_lists[index].ready_fence_value);
+ index = (index + 1) % NUM_COMMAND_LISTS;
+ }
+}
+} // namespace D3D12
diff --git a/src/common/d3d12/context.h b/src/common/d3d12/context.h
new file mode 100644
index 000000000..474eaeb3d
--- /dev/null
+++ b/src/common/d3d12/context.h
@@ -0,0 +1,142 @@
+// Copyright 2019 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "../types.h"
+#include "../windows_headers.h"
+#include "descriptor_heap_manager.h"
+#include "stream_buffer.h"
+#include
+#include
+#include
+#include
+#include
+
+struct IDXGIFactory;
+
+namespace D3D12 {
+
+class Context
+{
+public:
+ template
+ using ComPtr = Microsoft::WRL::ComPtr;
+
+ enum : u32
+ {
+ // Number of command lists. One is being built while the other(s) are executed.
+ NUM_COMMAND_LISTS = 3,
+
+ // Textures that don't fit into this buffer will be uploaded with a staging buffer.
+ TEXTURE_UPLOAD_BUFFER_SIZE = 16 * 1024 * 1024,
+ };
+
+ ~Context();
+
+ // Creates new device and context.
+ static bool Create(IDXGIFactory* dxgi_factory, u32 adapter_index, bool enable_debug_layer);
+
+ // Destroys active context.
+ static void Destroy();
+
+ ID3D12Device* GetDevice() const { return m_device.Get(); }
+ ID3D12CommandQueue* GetCommandQueue() const { return m_command_queue.Get(); }
+
+ // Returns the current command list, commands can be recorded directly.
+ ID3D12GraphicsCommandList* GetCommandList() const
+ {
+ return m_command_lists[m_current_command_list].command_list.Get();
+ }
+
+ // Descriptor manager access.
+ DescriptorHeapManager& GetDescriptorHeapManager() { return m_descriptor_heap_manager; }
+ DescriptorHeapManager& GetRTVHeapManager() { return m_rtv_heap_manager; }
+ DescriptorHeapManager& GetDSVHeapManager() { return m_dsv_heap_manager; }
+ DescriptorHeapManager& GetSamplerHeapManager() { return m_sampler_heap_manager; }
+ ID3D12DescriptorHeap* const* GetGPUDescriptorHeaps() const { return m_gpu_descriptor_heaps.data(); }
+ u32 GetGPUDescriptorHeapCount() const { return static_cast(m_gpu_descriptor_heaps.size()); }
+ const DescriptorHandle& GetNullSRVDescriptor() const { return m_null_srv_descriptor; }
+ StreamBuffer& GetTextureStreamBuffer() { return m_texture_stream_buffer; }
+
+ // Root signature access.
+ ComPtr SerializeRootSignature(const D3D12_ROOT_SIGNATURE_DESC* desc);
+ ComPtr CreateRootSignature(const D3D12_ROOT_SIGNATURE_DESC* desc);
+
+ // Fence value for current command list.
+ u64 GetCurrentFenceValue() const { return m_current_fence_value; }
+
+ // Last "completed" fence.
+ u64 GetCompletedFenceValue() const { return m_completed_fence_value; }
+
+ // Feature level to use when compiling shaders.
+ D3D_FEATURE_LEVEL GetFeatureLevel() const { return m_feature_level; }
+
+ // Test for support for the specified texture format.
+ bool SupportsTextureFormat(DXGI_FORMAT format);
+
+ // Executes the current command list.
+ void ExecuteCommandList(bool wait_for_completion);
+
+ // Waits for a specific fence.
+ void WaitForFence(u64 fence);
+
+ // Waits for any in-flight command buffers to complete.
+ void WaitForGPUIdle();
+
+ // Defers destruction of a D3D resource (associates it with the current list).
+ void DeferResourceDestruction(ID3D12Resource* resource);
+
+ // Defers destruction of a descriptor handle (associates it with the current list).
+ void DeferDescriptorDestruction(DescriptorHeapManager& manager, u32 index);
+ void DeferDescriptorDestruction(DescriptorHeapManager& manager, DescriptorHandle* handle);
+
+private:
+ struct CommandListResources
+ {
+ ComPtr command_allocator;
+ ComPtr command_list;
+ std::vector pending_resources;
+ std::vector> pending_descriptors;
+ u64 ready_fence_value = 0;
+ };
+
+ Context();
+
+ bool CreateDevice(IDXGIFactory* dxgi_factory, u32 adapter_index, bool enable_debug_layer);
+ bool CreateCommandQueue();
+ bool CreateFence();
+ bool CreateDescriptorHeaps();
+ bool CreateCommandLists();
+ bool CreateTextureStreamBuffer();
+ void MoveToNextCommandList();
+ void DestroyPendingResources(CommandListResources& cmdlist);
+ void DestroyResources();
+
+ ComPtr m_debug_interface;
+ ComPtr m_device;
+ ComPtr m_command_queue;
+
+ ComPtr m_fence = nullptr;
+ HANDLE m_fence_event = {};
+ u32 m_current_fence_value = 0;
+ u64 m_completed_fence_value = 0;
+
+ std::array m_command_lists;
+ u32 m_current_command_list = NUM_COMMAND_LISTS - 1;
+
+ DescriptorHeapManager m_descriptor_heap_manager;
+ DescriptorHeapManager m_rtv_heap_manager;
+ DescriptorHeapManager m_dsv_heap_manager;
+ DescriptorHeapManager m_sampler_heap_manager;
+ std::array m_gpu_descriptor_heaps = {};
+ DescriptorHandle m_null_srv_descriptor;
+ StreamBuffer m_texture_stream_buffer;
+
+ D3D_FEATURE_LEVEL m_feature_level = D3D_FEATURE_LEVEL_11_0;
+};
+
+} // namespace D3D12
+
+extern std::unique_ptr g_d3d12_context;
diff --git a/src/common/d3d12/descriptor_heap_manager.cpp b/src/common/d3d12/descriptor_heap_manager.cpp
new file mode 100644
index 000000000..573e47514
--- /dev/null
+++ b/src/common/d3d12/descriptor_heap_manager.cpp
@@ -0,0 +1,101 @@
+// Copyright 2019 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include "descriptor_heap_manager.h"
+#include "../assert.h"
+#include "../log.h"
+#include "context.h"
+Log_SetChannel(DescriptorHeapManager);
+
+namespace D3D12 {
+DescriptorHeapManager::DescriptorHeapManager() = default;
+DescriptorHeapManager::~DescriptorHeapManager() = default;
+
+bool DescriptorHeapManager::Create(ID3D12Device* device, D3D12_DESCRIPTOR_HEAP_TYPE type, u32 num_descriptors,
+ bool shader_visible)
+{
+ D3D12_DESCRIPTOR_HEAP_DESC desc = {type, static_cast(num_descriptors),
+ shader_visible ? D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE :
+ D3D12_DESCRIPTOR_HEAP_FLAG_NONE};
+
+ HRESULT hr = device->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&m_descriptor_heap));
+ AssertMsg(SUCCEEDED(hr), "Create descriptor heap");
+ if (FAILED(hr))
+ return false;
+
+ m_heap_base_cpu = m_descriptor_heap->GetCPUDescriptorHandleForHeapStart();
+ m_heap_base_gpu = m_descriptor_heap->GetGPUDescriptorHandleForHeapStart();
+ m_num_descriptors = num_descriptors;
+ m_descriptor_increment_size = device->GetDescriptorHandleIncrementSize(type);
+
+ // Set all slots to unallocated (1)
+ const u32 bitset_count = num_descriptors / BITSET_SIZE + (((num_descriptors % BITSET_SIZE) != 0) ? 1 : 0);
+ m_free_slots.resize(bitset_count);
+ for (BitSetType& bs : m_free_slots)
+ bs.flip();
+
+ return true;
+}
+
+void DescriptorHeapManager::Destroy()
+{
+ for (BitSetType& bs : m_free_slots)
+ Assert(bs.all());
+
+ m_num_descriptors = 0;
+ m_descriptor_increment_size = 0;
+ m_heap_base_cpu = {};
+ m_heap_base_gpu = {};
+ m_descriptor_heap.Reset();
+ m_free_slots.clear();
+}
+
+bool DescriptorHeapManager::Allocate(DescriptorHandle* handle)
+{
+ // Start past the temporary slots, no point in searching those.
+ for (u32 group = 0; group < m_free_slots.size(); group++)
+ {
+ BitSetType& bs = m_free_slots[group];
+ if (bs.none())
+ continue;
+
+ u32 bit = 0;
+ for (; bit < BITSET_SIZE; bit++)
+ {
+ if (bs[bit])
+ break;
+ }
+
+ u32 index = group * BITSET_SIZE + bit;
+ bs[bit] = false;
+
+ handle->index = index;
+ handle->cpu_handle.ptr = m_heap_base_cpu.ptr + index * m_descriptor_increment_size;
+ handle->gpu_handle.ptr = m_heap_base_gpu.ptr + index * m_descriptor_increment_size;
+ return true;
+ }
+
+ Panic("Out of fixed descriptors");
+ return false;
+}
+
+void DescriptorHeapManager::Free(u32 index)
+{
+ Assert(index < m_num_descriptors);
+
+ u32 group = index / BITSET_SIZE;
+ u32 bit = index % BITSET_SIZE;
+ m_free_slots[group][bit] = true;
+}
+
+void DescriptorHeapManager::Free(DescriptorHandle* handle)
+{
+ if (handle->index == DescriptorHandle::INVALID_INDEX)
+ return;
+
+ Free(handle->index);
+ handle->Clear();
+}
+
+} // namespace D3D12
diff --git a/src/common/d3d12/descriptor_heap_manager.h b/src/common/d3d12/descriptor_heap_manager.h
new file mode 100644
index 000000000..4be5144ed
--- /dev/null
+++ b/src/common/d3d12/descriptor_heap_manager.h
@@ -0,0 +1,70 @@
+// Copyright 2019 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "../types.h"
+#include "../windows_headers.h"
+#include
+#include
+#include