D3D12: Initial commit for D3D12 backend implementation.

This commit is contained in:
hdcmeta
2016-02-15 09:48:25 -08:00
parent a2e2e36745
commit 8cc686b360
54 changed files with 12296 additions and 7 deletions
+3
View File
@@ -233,6 +233,9 @@
<ProjectReference Include="$(CoreDir)VideoCommon\VideoCommon.vcxproj">
<Project>{3de9ee35-3e91-4f27-a014-2866ad8c3fe3}</Project>
</ProjectReference>
<ProjectReference Include="$(CoreDir)VideoBackends\D3D12\D3D12.vcxproj">
<Project>{570215b7-e32f-4438-95ae-c8d955f9fca3}</Project>
</ProjectReference>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
+1 -1
View File
@@ -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();
@@ -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;
}
};
@@ -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);
};
};
@@ -0,0 +1,109 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{570215B7-E32F-4438-95AE-C8D955F9FCA3}</ProjectGuid>
<WindowsTargetPlatformVersion>10.0.10240.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<PlatformToolset>v140</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
<UseDebugLibraries>true</UseDebugLibraries>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
<UseDebugLibraries>false</UseDebugLibraries>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="..\..\..\VSProps\Base.props" />
<Import Project="..\..\..\VSProps\PCHUse.props" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<ForcedIncludeFiles />
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<ForcedIncludeFiles />
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="BoundingBox.cpp" />
<ClCompile Include="D3DBase.cpp" />
<ClCompile Include="D3DCommandListManager.cpp" />
<ClCompile Include="D3DDescriptorHeapManager.cpp" />
<ClCompile Include="D3DQueuedCommandList.cpp" />
<ClCompile Include="D3DShader.cpp" />
<ClCompile Include="D3DState.cpp" />
<ClCompile Include="D3DStreamBuffer.cpp" />
<ClCompile Include="D3DTexture.cpp" />
<ClCompile Include="D3DUtil.cpp" />
<ClCompile Include="FramebufferManager.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="NativeVertexFormat.cpp" />
<ClCompile Include="PerfQuery.cpp" />
<ClCompile Include="PSTextureEncoder.cpp" />
<ClCompile Include="Render.cpp" />
<ClCompile Include="ShaderCache.cpp" />
<ClCompile Include="ShaderConstantsManager.cpp" />
<ClCompile Include="StaticShaderCache.cpp" />
<ClCompile Include="Television.cpp" />
<ClCompile Include="TextureCache.cpp" />
<ClCompile Include="VertexManager.cpp" />
<ClCompile Include="XFBEncoder.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="BoundingBox.h" />
<ClInclude Include="D3DBase.h" />
<ClInclude Include="D3DCommandListManager.h" />
<ClInclude Include="D3DDescriptorHeapManager.h" />
<ClInclude Include="D3DQueuedCommandList.h" />
<ClInclude Include="D3DShader.h" />
<ClInclude Include="D3DState.h" />
<ClInclude Include="D3DStreamBuffer.h" />
<ClInclude Include="D3DTexture.h" />
<ClInclude Include="D3DUtil.h" />
<ClInclude Include="FramebufferManager.h" />
<ClInclude Include="NativeVertexFormat.h" />
<ClInclude Include="PerfQuery.h" />
<ClInclude Include="PSTextureEncoder.h" />
<ClInclude Include="Render.h" />
<ClInclude Include="ShaderCache.h" />
<ClInclude Include="ShaderConstantsManager.h" />
<ClInclude Include="StaticShaderCache.h" />
<ClInclude Include="Television.h" />
<ClInclude Include="TextureCache.h" />
<ClInclude Include="TextureEncoder.h" />
<ClInclude Include="VertexManager.h" />
<ClInclude Include="VideoBackend.h" />
<ClInclude Include="XFBEncoder.h" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="$(CoreDir)VideoCommon\VideoCommon.vcxproj">
<Project>{3de9ee35-3e91-4f27-a014-2866ad8c3fe3}</Project>
</ProjectReference>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
@@ -0,0 +1,152 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Render">
<UniqueIdentifier>{3683d29b-19f6-4e7a-803f-4ac70b1d49fd}</UniqueIdentifier>
</Filter>
<Filter Include="D3D12">
<UniqueIdentifier>{ae700f7e-33c8-45b5-b7ee-a0ded3630549}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="D3DBase.cpp">
<Filter>D3D12</Filter>
</ClCompile>
<ClCompile Include="D3DShader.cpp">
<Filter>D3D12</Filter>
</ClCompile>
<ClCompile Include="D3DTexture.cpp">
<Filter>D3D12</Filter>
</ClCompile>
<ClCompile Include="D3DUtil.cpp">
<Filter>D3D12</Filter>
</ClCompile>
<ClCompile Include="D3DState.cpp">
<Filter>D3D12</Filter>
</ClCompile>
<ClCompile Include="FramebufferManager.cpp">
<Filter>Render</Filter>
</ClCompile>
<ClCompile Include="NativeVertexFormat.cpp">
<Filter>Render</Filter>
</ClCompile>
<ClCompile Include="PerfQuery.cpp">
<Filter>Render</Filter>
</ClCompile>
<ClCompile Include="PSTextureEncoder.cpp">
<Filter>Render</Filter>
</ClCompile>
<ClCompile Include="Render.cpp">
<Filter>Render</Filter>
</ClCompile>
<ClCompile Include="Television.cpp">
<Filter>Render</Filter>
</ClCompile>
<ClCompile Include="TextureCache.cpp">
<Filter>Render</Filter>
</ClCompile>
<ClCompile Include="VertexManager.cpp">
<Filter>Render</Filter>
</ClCompile>
<ClCompile Include="XFBEncoder.cpp">
<Filter>Render</Filter>
</ClCompile>
<ClCompile Include="main.cpp" />
<ClCompile Include="BoundingBox.cpp">
<Filter>Render</Filter>
</ClCompile>
<ClCompile Include="D3DCommandListManager.cpp">
<Filter>D3D12</Filter>
</ClCompile>
<ClCompile Include="D3DDescriptorHeapManager.cpp">
<Filter>D3D12</Filter>
</ClCompile>
<ClCompile Include="D3DQueuedCommandList.cpp">
<Filter>D3D12</Filter>
</ClCompile>
<ClCompile Include="StaticShaderCache.cpp">
<Filter>Render</Filter>
</ClCompile>
<ClCompile Include="ShaderCache.cpp">
<Filter>Render</Filter>
</ClCompile>
<ClCompile Include="ShaderConstantsManager.cpp">
<Filter>Render</Filter>
</ClCompile>
<ClCompile Include="D3DStreamBuffer.cpp">
<Filter>D3D12</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="D3DBase.h">
<Filter>D3D12</Filter>
</ClInclude>
<ClInclude Include="D3DShader.h">
<Filter>D3D12</Filter>
</ClInclude>
<ClInclude Include="D3DTexture.h">
<Filter>D3D12</Filter>
</ClInclude>
<ClInclude Include="D3DUtil.h">
<Filter>D3D12</Filter>
</ClInclude>
<ClInclude Include="D3DState.h">
<Filter>D3D12</Filter>
</ClInclude>
<ClInclude Include="FramebufferManager.h">
<Filter>Render</Filter>
</ClInclude>
<ClInclude Include="PerfQuery.h">
<Filter>Render</Filter>
</ClInclude>
<ClInclude Include="PSTextureEncoder.h">
<Filter>Render</Filter>
</ClInclude>
<ClInclude Include="Render.h">
<Filter>Render</Filter>
</ClInclude>
<ClInclude Include="Television.h">
<Filter>Render</Filter>
</ClInclude>
<ClInclude Include="TextureCache.h">
<Filter>Render</Filter>
</ClInclude>
<ClInclude Include="TextureEncoder.h">
<Filter>Render</Filter>
</ClInclude>
<ClInclude Include="VertexManager.h">
<Filter>Render</Filter>
</ClInclude>
<ClInclude Include="XFBEncoder.h">
<Filter>Render</Filter>
</ClInclude>
<ClInclude Include="VideoBackend.h" />
<ClInclude Include="BoundingBox.h">
<Filter>Render</Filter>
</ClInclude>
<ClInclude Include="D3DCommandListManager.h">
<Filter>D3D12</Filter>
</ClInclude>
<ClInclude Include="D3DDescriptorHeapManager.h">
<Filter>D3D12</Filter>
</ClInclude>
<ClInclude Include="NativeVertexFormat.h">
<Filter>Render</Filter>
</ClInclude>
<ClInclude Include="D3DQueuedCommandList.h">
<Filter>D3D12</Filter>
</ClInclude>
<ClInclude Include="StaticShaderCache.h">
<Filter>Render</Filter>
</ClInclude>
<ClInclude Include="ShaderCache.h">
<Filter>Render</Filter>
</ClInclude>
<ClInclude Include="ShaderConstantsManager.h">
<Filter>Render</Filter>
</ClInclude>
<ClInclude Include="D3DStreamBuffer.h">
<Filter>D3D12</Filter>
</ClInclude>
</ItemGroup>
</Project>
File diff suppressed because it is too large Load Diff
+158
View File
@@ -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 <d3d11.h>
#include <d3d12.h>
#include <d3dcompiler.h>
#include <dxgi1_4.h>
#include <vector>
#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<DXGI_SAMPLE_DESC> 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<ID3D12DescriptorHeap*, 2> 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<char*>(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
@@ -0,0 +1,355 @@
// Copyright 2015 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include <queue>
#include <vector>
#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<unsigned int>(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<VertexManager*>(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
@@ -0,0 +1,98 @@
// Copyright 2015 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <map>
#include <vector>
#include "D3DQueuedCommandList.h"
namespace DX12
{
enum COMMAND_LIST_STATE
{
COMMAND_LIST_STATE_GS_CBV = 1,
COMMAND_LIST_STATE_PS_CBV = 2,
COMMAND_LIST_STATE_VS_CBV = 4,
COMMAND_LIST_STATE_PSO = 8,
COMMAND_LIST_STATE_SAMPLERS = 16,
COMMAND_LIST_STATE_VERTEX_BUFFER = 32
};
// This class provides an abstraction for D3D12 descriptor heaps.
class D3DCommandListManager
{
public:
D3DCommandListManager(D3D12_COMMAND_LIST_TYPE command_list_type, ID3D12Device* device, ID3D12CommandQueue* command_queue);
~D3DCommandListManager();
void SetInitialCommandListState();
void GetCommandList(ID3D12GraphicsCommandList** command_list) const;
void ExecuteQueuedWork(bool wait_for_gpu_completion = false);
void ExecuteQueuedWorkAndPresent(IDXGISwapChain* swap_chain, UINT sync_interval, UINT flags);
void WaitForQueuedWorkToBeExecutedOnGPU();
void ClearQueueAndWaitForCompletionOfInflightWork();
void DestroyResourceAfterCurrentCommandListExecuted(ID3D12Resource* resource);
void ImmediatelyDestroyAllResourcesScheduledForDestruction();
void SetCommandListDirtyState(unsigned int command_list_state, bool dirty);
bool GetCommandListDirtyState(COMMAND_LIST_STATE command_list_state) const;
void SetCommandListPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY primitive_topology);
D3D_PRIMITIVE_TOPOLOGY GetCommandListPrimitiveTopology() const;
unsigned int m_draws_since_last_execution = 0;
bool m_cpu_access_last_frame = false;
bool m_cpu_access_this_frame = false;
void CPUAccessNotify();
// Allow other components to register for a callback each time a fence is queued.
using PFN_QUEUE_FENCE_CALLBACK = void(void* owning_object, UINT64 fence_value);
ID3D12Fence* RegisterQueueFenceCallback(void* owning_object, PFN_QUEUE_FENCE_CALLBACK* callback_function);
void RemoveQueueFenceCallback(void* owning_object);
void WaitOnCPUForFence(ID3D12Fence* fence, UINT64 fence_value);
private:
void PerformGpuRolloverChecks();
void ResetCommandListWithIdleCommandAllocator();
unsigned int m_command_list_dirty_state = UINT_MAX;
D3D_PRIMITIVE_TOPOLOGY m_command_list_current_topology = D3D_PRIMITIVE_TOPOLOGY_UNDEFINED;
HANDLE m_wait_on_cpu_fence_event;
ID3D12Device* m_device;
ID3D12CommandQueue* m_command_queue;
UINT64 m_queue_fence_value;
ID3D12Fence* m_queue_fence;
UINT64 m_queue_frame_fence_value;
ID3D12Fence* m_queue_frame_fence;
std::map<void*, PFN_QUEUE_FENCE_CALLBACK*> m_queue_fence_callbacks;
UINT m_current_command_allocator;
UINT m_current_command_allocator_list;
std::array<std::vector<ID3D12CommandAllocator*>, 2> m_command_allocator_lists;
ID3D12GraphicsCommandList* m_backing_command_list;
ID3D12QueuedCommandList* m_queued_command_list;
ID3D12RootSignature* m_default_root_signature;
UINT m_current_deferred_destruction_list;
std::array<std::vector<ID3D12Resource*>, 2> m_deferred_destruction_lists;
};
} // namespace
@@ -0,0 +1,169 @@
// Copyright 2015 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "VideoBackends/D3D12/D3DBase.h"
#include "VideoBackends/D3D12/D3DDescriptorHeapManager.h"
#include "VideoBackends/D3D12/D3DState.h"
namespace DX12
{
bool operator==(const D3DDescriptorHeapManager::SamplerStateSet& lhs, const D3DDescriptorHeapManager::SamplerStateSet& rhs)
{
// D3D12TODO: Do something more efficient than this.
return (!memcmp(&lhs, &rhs, sizeof(D3DDescriptorHeapManager::SamplerStateSet)));
}
D3DDescriptorHeapManager::D3DDescriptorHeapManager(D3D12_DESCRIPTOR_HEAP_DESC* desc, ID3D12Device* device, unsigned int temporarySlots) :
m_device(device)
{
CheckHR(device->CreateDescriptorHeap(desc, IID_PPV_ARGS(&m_descriptor_heap)));
m_descriptor_heap_size = desc->NumDescriptors;
m_descriptor_increment_size = device->GetDescriptorHandleIncrementSize(desc->Type);
m_gpu_visible = (desc->Flags == D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE);
if (m_gpu_visible)
{
D3D12_DESCRIPTOR_HEAP_DESC cpu_shadow_heap_desc = *desc;
cpu_shadow_heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
CheckHR(device->CreateDescriptorHeap(&cpu_shadow_heap_desc, IID_PPV_ARGS(&m_descriptor_heap_cpu_shadow)));
m_heap_base_gpu = m_descriptor_heap->GetGPUDescriptorHandleForHeapStart();
m_heap_base_gpu_cpu_shadow = m_descriptor_heap_cpu_shadow->GetCPUDescriptorHandleForHeapStart();
}
m_heap_base_cpu = m_descriptor_heap->GetCPUDescriptorHandleForHeapStart();
m_first_temporary_slot_in_heap = m_descriptor_heap_size - temporarySlots;
m_current_temporary_offset_in_heap = m_first_temporary_slot_in_heap;
}
bool D3DDescriptorHeapManager::Allocate(D3D12_CPU_DESCRIPTOR_HANDLE* cpu_handle, D3D12_GPU_DESCRIPTOR_HANDLE* gpu_handle, D3D12_CPU_DESCRIPTOR_HANDLE* gpu_handle_cpu_shadow, bool temporary)
{
bool allocated_from_current_heap = true;
if (m_current_permanent_offset_in_heap + 1 >= m_first_temporary_slot_in_heap)
{
// If out of room in the heap, start back at beginning.
allocated_from_current_heap = false;
m_current_permanent_offset_in_heap = 0;
}
CHECK(!gpu_handle || (gpu_handle && m_gpu_visible), "D3D12_GPU_DESCRIPTOR_HANDLE used on non-GPU-visible heap.");
if (temporary && m_current_temporary_offset_in_heap + 1 >= m_descriptor_heap_size)
{
m_current_temporary_offset_in_heap = m_first_temporary_slot_in_heap;
}
unsigned int heapOffsetToUse = temporary ? m_current_temporary_offset_in_heap : m_current_permanent_offset_in_heap;
if (m_gpu_visible)
{
gpu_handle->ptr = m_heap_base_gpu.ptr + heapOffsetToUse * m_descriptor_increment_size;
if (gpu_handle_cpu_shadow)
gpu_handle_cpu_shadow->ptr = m_heap_base_gpu_cpu_shadow.ptr + heapOffsetToUse * m_descriptor_increment_size;
}
cpu_handle->ptr = m_heap_base_cpu.ptr + heapOffsetToUse * m_descriptor_increment_size;
if (!temporary)
{
m_current_permanent_offset_in_heap++;
}
return allocated_from_current_heap;
}
bool D3DDescriptorHeapManager::AllocateGroup(D3D12_CPU_DESCRIPTOR_HANDLE* base_cpu_handle, unsigned int num_handles, D3D12_GPU_DESCRIPTOR_HANDLE* base_gpu_handle, D3D12_CPU_DESCRIPTOR_HANDLE* base_gpu_handle_cpu_shadow, bool temporary)
{
bool allocated_from_current_heap = true;
if (m_current_permanent_offset_in_heap + num_handles >= m_first_temporary_slot_in_heap)
{
// If out of room in the heap, start back at beginning.
allocated_from_current_heap = false;
m_current_permanent_offset_in_heap = 0;
}
CHECK(!base_gpu_handle || (base_gpu_handle && m_gpu_visible), "D3D12_GPU_DESCRIPTOR_HANDLE used on non-GPU-visible heap.");
if (temporary && m_current_temporary_offset_in_heap + num_handles >= m_descriptor_heap_size)
{
m_current_temporary_offset_in_heap = m_first_temporary_slot_in_heap;
}
unsigned int heapOffsetToUse = temporary ? m_current_temporary_offset_in_heap : m_current_permanent_offset_in_heap;
if (m_gpu_visible)
{
base_gpu_handle->ptr = m_heap_base_gpu.ptr + heapOffsetToUse * m_descriptor_increment_size;
if (base_gpu_handle_cpu_shadow)
base_gpu_handle_cpu_shadow->ptr = m_heap_base_gpu_cpu_shadow.ptr + heapOffsetToUse * m_descriptor_increment_size;
}
base_cpu_handle->ptr = m_heap_base_cpu.ptr + heapOffsetToUse * m_descriptor_increment_size;
if (temporary)
{
m_current_temporary_offset_in_heap += num_handles;
}
else
{
m_current_permanent_offset_in_heap += num_handles;
}
return allocated_from_current_heap;
}
D3D12_GPU_DESCRIPTOR_HANDLE D3DDescriptorHeapManager::GetHandleForSamplerGroup(SamplerState* sampler_state, unsigned int num_sampler_samples)
{
auto it = m_sampler_map.find(*reinterpret_cast<SamplerStateSet*>(sampler_state));
if (it == m_sampler_map.end())
{
D3D12_CPU_DESCRIPTOR_HANDLE base_sampler_cpu_handle;
D3D12_GPU_DESCRIPTOR_HANDLE base_sampler_gpu_handle;
bool allocatedFromExistingHeap = AllocateGroup(&base_sampler_cpu_handle, num_sampler_samples, &base_sampler_gpu_handle);
if (!allocatedFromExistingHeap)
{
m_sampler_map.clear();
}
for (unsigned int i = 0; i < num_sampler_samples; i++)
{
D3D12_CPU_DESCRIPTOR_HANDLE destinationDescriptor;
destinationDescriptor.ptr = base_sampler_cpu_handle.ptr + i * D3D::sampler_descriptor_size;
D3D::device12->CreateSampler(&StateCache::GetDesc12(sampler_state[i]), destinationDescriptor);
}
m_sampler_map[*reinterpret_cast<SamplerStateSet*>(sampler_state)] = base_sampler_gpu_handle;
return base_sampler_gpu_handle;
}
else
{
return it->second;
}
}
ID3D12DescriptorHeap* D3DDescriptorHeapManager::GetDescriptorHeap() const
{
return m_descriptor_heap;
}
D3DDescriptorHeapManager::~D3DDescriptorHeapManager()
{
SAFE_RELEASE(m_descriptor_heap);
SAFE_RELEASE(m_descriptor_heap_cpu_shadow);
}
} // namespace DX12
@@ -0,0 +1,72 @@
// Copyright 2015 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <d3d12.h>
#include <unordered_map>
#include "VideoBackends/D3D12/D3DState.h"
namespace DX12
{
// This class provides an abstraction for D3D12 descriptor heaps.
class D3DDescriptorHeapManager
{
public:
D3DDescriptorHeapManager(D3D12_DESCRIPTOR_HEAP_DESC* desc, ID3D12Device* device, unsigned int temporarySlots = 0);
~D3DDescriptorHeapManager();
bool Allocate(D3D12_CPU_DESCRIPTOR_HANDLE* cpu_handle, D3D12_GPU_DESCRIPTOR_HANDLE* gpu_handle = nullptr, D3D12_CPU_DESCRIPTOR_HANDLE* gpu_handle_cpu_shadow = nullptr, bool temporary = false);
bool AllocateGroup(D3D12_CPU_DESCRIPTOR_HANDLE* cpu_handles, unsigned int num_handles, D3D12_GPU_DESCRIPTOR_HANDLE* gpu_handles = nullptr, D3D12_CPU_DESCRIPTOR_HANDLE* gpu_handle_cpu_shadows = nullptr, bool temporary = false);
D3D12_GPU_DESCRIPTOR_HANDLE GetHandleForSamplerGroup(SamplerState* sampler_state, unsigned int num_sampler_samples);
ID3D12DescriptorHeap* GetDescriptorHeap() const;
struct SamplerStateSet
{
SamplerState desc0;
SamplerState desc1;
SamplerState desc2;
SamplerState desc3;
SamplerState desc4;
SamplerState desc5;
SamplerState desc6;
SamplerState desc7;
};
private:
ID3D12Device* m_device = nullptr;
ID3D12DescriptorHeap* m_descriptor_heap = nullptr;
ID3D12DescriptorHeap* m_descriptor_heap_cpu_shadow = nullptr;
D3D12_CPU_DESCRIPTOR_HANDLE m_heap_base_cpu;
D3D12_GPU_DESCRIPTOR_HANDLE m_heap_base_gpu;
D3D12_CPU_DESCRIPTOR_HANDLE m_heap_base_gpu_cpu_shadow;
struct hash_sampler_desc
{
size_t operator()(const SamplerStateSet sampler_state_set) const
{
return sampler_state_set.desc0.hex;
}
};
std::unordered_map<SamplerStateSet, D3D12_GPU_DESCRIPTOR_HANDLE, hash_sampler_desc> m_sampler_map;
unsigned int m_current_temporary_offset_in_heap = 0;
unsigned int m_current_permanent_offset_in_heap = 0;
unsigned int m_descriptor_increment_size;
unsigned int m_descriptor_heap_size;
bool m_gpu_visible;
unsigned int m_first_temporary_slot_in_heap;
};
} // namespace
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,84 @@
// Copyright 2010 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include <fstream>
#include <string>
#include "Common/FileUtil.h"
#include "Common/MsgHandler.h"
#include "Common/StringUtil.h"
#include "Common/Logging/Log.h"
#include "VideoBackends/D3D12/D3DBase.h"
#include "VideoBackends/D3D12/D3DShader.h"
#include "VideoCommon/VideoConfig.h"
namespace DX12
{
namespace D3D
{
bool CompileShader(const std::string& code, ID3DBlob** blob, const D3D_SHADER_MACRO* defines, std::string shader_version_string)
{
ID3D10Blob* shader_buffer = nullptr;
ID3D10Blob* error_buffer = nullptr;
#if defined(_DEBUG) || defined(DEBUGFAST)
UINT flags = D3DCOMPILE_ENABLE_BACKWARDS_COMPATIBILITY | D3DCOMPILE_DEBUG;
#else
UINT flags = D3DCOMPILE_ENABLE_BACKWARDS_COMPATIBILITY | D3DCOMPILE_OPTIMIZATION_LEVEL3 | D3DCOMPILE_SKIP_VALIDATION;
#endif
HRESULT hr = d3d_compile(code.c_str(), code.length(), nullptr, defines, nullptr, "main", shader_version_string.data(),
flags, 0, &shader_buffer, &error_buffer);
if (error_buffer)
{
INFO_LOG(VIDEO, "Shader compiler messages:\n%s\n",
static_cast<const char*>(error_buffer->GetBufferPointer()));
}
if (FAILED(hr))
{
static int num_failures = 0;
std::string filename = StringFromFormat("%sbad_%s_%04i.txt", File::GetUserPath(D_DUMP_IDX).c_str(), shader_version_string, num_failures++);
std::ofstream file;
OpenFStream(file, filename, std::ios_base::out);
file << code;
file.close();
PanicAlert("Failed to compile shader: %s\nDebug info (%s):\n%s",
filename.c_str(), shader_version_string, static_cast<const char*>(error_buffer->GetBufferPointer()));
*blob = nullptr;
error_buffer->Release();
}
else
{
*blob = shader_buffer;
}
return SUCCEEDED(hr);
}
// code->bytecode
bool CompileVertexShader(const std::string& code, ID3DBlob** blob)
{
return CompileShader(code, blob, nullptr, D3D::VertexShaderVersionString());
}
// code->bytecode
bool CompileGeometryShader(const std::string& code, ID3DBlob** blob, const D3D_SHADER_MACRO* defines)
{
return CompileShader(code, blob, defines, D3D::GeometryShaderVersionString());
}
// code->bytecode
bool CompilePixelShader(const std::string& code, ID3DBlob** blob, const D3D_SHADER_MACRO* defines)
{
return CompileShader(code, blob, defines, D3D::PixelShaderVersionString());
}
} // namespace
} // namespace DX12
@@ -0,0 +1,26 @@
// Copyright 2010 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <string>
#include "VideoBackends/D3D12/D3DBase.h"
class D3DBlob;
namespace DX12
{
namespace D3D
{
// The returned bytecode buffers should be Release()d.
bool CompileVertexShader(const std::string& code, ID3DBlob** blob);
bool CompileGeometryShader(const std::string& code, ID3DBlob** blob, const D3D_SHADER_MACRO* defines = nullptr);
bool CompilePixelShader(const std::string& code, ID3DBlob** blob, const D3D_SHADER_MACRO* defines = nullptr);
}
} // namespace DX12
@@ -0,0 +1,482 @@
// Copyright 2014 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "Common/BitSet.h"
#include "Common/CommonTypes.h"
#include "Common/FileUtil.h"
#include "Common/LinearDiskCache.h"
#include "Common/MsgHandler.h"
#include "Common/StringUtil.h"
#include "Common/Logging/Log.h"
#include "Core/ConfigManager.h"
#include "VideoBackends/D3D12/D3DBase.h"
#include "VideoBackends/D3D12/D3DState.h"
#include "VideoBackends/D3D12/D3DUtil.h"
#include "VideoBackends/D3D12/NativeVertexFormat.h"
#include "VideoBackends/D3D12/ShaderCache.h"
#include "VideoBackends/D3D12/StaticShaderCache.h"
#include "VideoCommon/VertexLoaderManager.h"
#include "VideoCommon/VideoConfig.h"
namespace DX12
{
static bool s_cache_is_corrupted = false;
static LinearDiskCache<SmallPsoDiskDesc, u8> s_pso_disk_cache;
class PipelineStateCacheInserter : public LinearDiskCacheReader<SmallPsoDiskDesc, u8>
{
public:
void Read(const SmallPsoDiskDesc& key, const u8* value, u32 value_size)
{
if (s_cache_is_corrupted)
return;
D3D12_GRAPHICS_PIPELINE_STATE_DESC desc = {};
desc.pRootSignature = D3D::default_root_signature;
desc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM; // This state changes in PSTextureEncoder::Encode.
desc.DSVFormat = DXGI_FORMAT_D32_FLOAT; // This state changes in PSTextureEncoder::Encode.
desc.IBStripCutValue = D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_0xFFFF;
desc.NumRenderTargets = 1;
desc.SampleMask = UINT_MAX;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.GS = ShaderCache::GetGeometryShaderFromUid(&key.gs_uid);
desc.PS = ShaderCache::GetPixelShaderFromUid(&key.ps_uid);
desc.VS = ShaderCache::GetVertexShaderFromUid(&key.vs_uid);
if (!desc.PS.pShaderBytecode || !desc.VS.pShaderBytecode)
{
s_cache_is_corrupted = true;
return;
}
BlendState blend_state = {};
blend_state.hex = key.blend_state_hex;
desc.BlendState = StateCache::GetDesc12(blend_state);
ZMode depth_stencil_state = {};
depth_stencil_state.hex = key.depth_stencil_state_hex;
desc.DepthStencilState = StateCache::GetDesc12(depth_stencil_state);
RasterizerState rasterizer_state = {};
rasterizer_state.hex = key.rasterizer_state_hex;
desc.RasterizerState = StateCache::GetDesc12(rasterizer_state);
desc.PrimitiveTopologyType = key.topology;
// search for a cached native vertex format
const PortableVertexDeclaration& native_vtx_decl = key.vertex_declaration;
std::unique_ptr<NativeVertexFormat>& native = (*VertexLoaderManager::GetNativeVertexFormatMap())[native_vtx_decl];
if (!native)
{
native.reset(g_vertex_manager->CreateNativeVertexFormat(native_vtx_decl));
}
desc.InputLayout = reinterpret_cast<D3DVertexFormat*>(native.get())->GetActiveInputLayout12();
desc.CachedPSO.CachedBlobSizeInBytes = value_size;
desc.CachedPSO.pCachedBlob = value;
ID3D12PipelineState* pso = nullptr;
HRESULT hr = D3D::device12->CreateGraphicsPipelineState(&desc, IID_PPV_ARGS(&pso));
if (FAILED(hr))
{
// Failure can occur if disk cache is corrupted, or a driver upgrade invalidates the existing blobs.
// In this case, we need to clear the disk cache.
s_cache_is_corrupted = true;
return;
}
SmallPsoDesc small_desc = {};
small_desc.blend_state.hex = key.blend_state_hex;
small_desc.depth_stencil_state.hex = key.depth_stencil_state_hex;
small_desc.rasterizer_state.hex = key.rasterizer_state_hex;
small_desc.gs_bytecode = desc.GS;
small_desc.ps_bytecode = desc.PS;
small_desc.vs_bytecode = desc.VS;
small_desc.input_layout = reinterpret_cast<D3DVertexFormat*>(native.get());
gx_state_cache.m_small_pso_map[small_desc] = pso;
}
};
StateCache::StateCache()
{
m_current_pso_desc = {};
m_current_pso_desc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM; // This state changes in PSTextureEncoder::Encode.
m_current_pso_desc.DSVFormat = DXGI_FORMAT_D32_FLOAT; // This state changes in PSTextureEncoder::Encode.
m_current_pso_desc.IBStripCutValue = D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_0xFFFF;
m_current_pso_desc.NumRenderTargets = 1;
m_current_pso_desc.SampleMask = UINT_MAX;
}
void StateCache::Init()
{
// Root signature isn't available at time of StateCache construction, so fill it in now.
gx_state_cache.m_current_pso_desc.pRootSignature = D3D::default_root_signature;
// Multi-sample configuration isn't available at time of StateCache construction, so fille it in now.
gx_state_cache.m_current_pso_desc.SampleDesc.Count = g_ActiveConfig.iMultisamples;
gx_state_cache.m_current_pso_desc.SampleDesc.Quality = 0;
if (!File::Exists(File::GetUserPath(D_SHADERCACHE_IDX)))
File::CreateDir(File::GetUserPath(D_SHADERCACHE_IDX));
std::string cache_filename = StringFromFormat("%sdx12-%s-pso.cache", File::GetUserPath(D_SHADERCACHE_IDX).c_str(),
SConfig::GetInstance().m_strUniqueID.c_str());
PipelineStateCacheInserter inserter;
s_pso_disk_cache.OpenAndRead(cache_filename, inserter);
if (s_cache_is_corrupted)
{
// If a PSO fails to create, that means either:
// - The file itself is corrupt.
// - A driver/HW change has occured, causing the existing cache blobs to be invalid.
//
// In either case, we want to re-create the disk cache. This should not be a frequent occurence.
s_pso_disk_cache.Close();
for (auto it : gx_state_cache.m_small_pso_map)
{
SAFE_RELEASE(it.second);
}
gx_state_cache.m_small_pso_map.clear();
File::Delete(cache_filename);
s_pso_disk_cache.OpenAndRead(cache_filename, inserter);
s_cache_is_corrupted = false;
}
}
D3D12_SAMPLER_DESC StateCache::GetDesc12(SamplerState state)
{
const unsigned int d3d_mip_filters[4] =
{
TexMode0::TEXF_NONE,
TexMode0::TEXF_POINT,
TexMode0::TEXF_LINEAR,
TexMode0::TEXF_NONE, //reserved
};
const D3D12_TEXTURE_ADDRESS_MODE d3d_clamps[4] =
{
D3D12_TEXTURE_ADDRESS_MODE_CLAMP,
D3D12_TEXTURE_ADDRESS_MODE_WRAP,
D3D12_TEXTURE_ADDRESS_MODE_MIRROR,
D3D12_TEXTURE_ADDRESS_MODE_WRAP //reserved
};
D3D12_SAMPLER_DESC sampdc;
unsigned int mip = d3d_mip_filters[state.min_filter & 3];
sampdc.MaxAnisotropy = 1;
if (g_ActiveConfig.iMaxAnisotropy > 1)
{
sampdc.Filter = D3D12_FILTER_ANISOTROPIC;
sampdc.MaxAnisotropy = 1 << g_ActiveConfig.iMaxAnisotropy;
}
else if (state.min_filter & 4) // linear min filter
{
if (state.mag_filter) // linear mag filter
{
if (mip == TexMode0::TEXF_NONE)
sampdc.Filter = D3D12_FILTER_MIN_MAG_LINEAR_MIP_POINT;
else if (mip == TexMode0::TEXF_POINT)
sampdc.Filter = D3D12_FILTER_MIN_MAG_LINEAR_MIP_POINT;
else if (mip == TexMode0::TEXF_LINEAR)
sampdc.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR;
}
else // point mag filter
{
if (mip == TexMode0::TEXF_NONE)
sampdc.Filter = D3D12_FILTER_MIN_LINEAR_MAG_MIP_POINT;
else if (mip == TexMode0::TEXF_POINT)
sampdc.Filter = D3D12_FILTER_MIN_LINEAR_MAG_MIP_POINT;
else if (mip == TexMode0::TEXF_LINEAR)
sampdc.Filter = D3D12_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR;
}
}
else // point min filter
{
if (state.mag_filter) // linear mag filter
{
if (mip == TexMode0::TEXF_NONE)
sampdc.Filter = D3D12_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT;
else if (mip == TexMode0::TEXF_POINT)
sampdc.Filter = D3D12_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT;
else if (mip == TexMode0::TEXF_LINEAR)
sampdc.Filter = D3D12_FILTER_MIN_POINT_MAG_MIP_LINEAR;
}
else // point mag filter
{
if (mip == TexMode0::TEXF_NONE)
sampdc.Filter = D3D12_FILTER_MIN_MAG_MIP_POINT;
else if (mip == TexMode0::TEXF_POINT)
sampdc.Filter = D3D12_FILTER_MIN_MAG_MIP_POINT;
else if (mip == TexMode0::TEXF_LINEAR)
sampdc.Filter = D3D12_FILTER_MIN_MAG_POINT_MIP_LINEAR;
}
}
sampdc.AddressU = d3d_clamps[state.wrap_s];
sampdc.AddressV = d3d_clamps[state.wrap_t];
sampdc.AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
sampdc.ComparisonFunc = D3D12_COMPARISON_FUNC_NEVER;
sampdc.BorderColor[0] = sampdc.BorderColor[1] = sampdc.BorderColor[2] = sampdc.BorderColor[3] = 1.0f;
sampdc.MaxLOD = (mip == TexMode0::TEXF_NONE) ? 0.0f : static_cast<float>(state.max_lod) / 16.f;
sampdc.MinLOD = static_cast<float>(state.min_lod) / 16.f;
sampdc.MipLODBias = static_cast<s32>(state.lod_bias) / 32.0f;
return sampdc;
}
D3D12_BLEND GetBlendingAlpha(D3D12_BLEND blend)
{
switch (blend)
{
case D3D12_BLEND_SRC_COLOR:
return D3D12_BLEND_SRC_ALPHA;
case D3D12_BLEND_INV_SRC_COLOR:
return D3D12_BLEND_INV_SRC_ALPHA;
case D3D12_BLEND_DEST_COLOR:
return D3D12_BLEND_DEST_ALPHA;
case D3D12_BLEND_INV_DEST_COLOR:
return D3D12_BLEND_INV_DEST_ALPHA;
default:
return blend;
}
}
D3D12_BLEND_DESC StateCache::GetDesc12(BlendState state)
{
if (!state.blend_enable)
{
state.src_blend = D3D12_BLEND_ONE;
state.dst_blend = D3D12_BLEND_ZERO;
state.blend_op = D3D12_BLEND_OP_ADD;
state.use_dst_alpha = false;
}
D3D12_BLEND_DESC blenddc = {
FALSE, // BOOL AlphaToCoverageEnable;
FALSE, // BOOL IndependentBlendEnable;
{
state.blend_enable, // BOOL BlendEnable;
FALSE, // BOOL LogicOpEnable;
state.src_blend, // D3D12_BLEND SrcBlend;
state.dst_blend, // D3D12_BLEND DestBlend;
state.blend_op, // D3D12_BLEND_OP BlendOp;
state.src_blend, // D3D12_BLEND SrcBlendAlpha;
state.dst_blend, // D3D12_BLEND DestBlendAlpha;
state.blend_op, // D3D12_BLEND_OP BlendOpAlpha;
D3D12_LOGIC_OP_NOOP, // D3D12_LOGIC_OP LogicOp
state.write_mask // UINT8 RenderTargetWriteMask;
}
};
blenddc.RenderTarget[0].SrcBlendAlpha = GetBlendingAlpha(blenddc.RenderTarget[0].SrcBlend);
blenddc.RenderTarget[0].DestBlendAlpha = GetBlendingAlpha(blenddc.RenderTarget[0].DestBlend);
if (state.use_dst_alpha)
{
// Colors should blend against SRC1_ALPHA
if (blenddc.RenderTarget[0].SrcBlend == D3D12_BLEND_SRC_ALPHA)
blenddc.RenderTarget[0].SrcBlend = D3D12_BLEND_SRC1_ALPHA;
else if (blenddc.RenderTarget[0].SrcBlend == D3D12_BLEND_INV_SRC_ALPHA)
blenddc.RenderTarget[0].SrcBlend = D3D12_BLEND_INV_SRC1_ALPHA;
// Colors should blend against SRC1_ALPHA
if (blenddc.RenderTarget[0].DestBlend == D3D12_BLEND_SRC_ALPHA)
blenddc.RenderTarget[0].DestBlend = D3D12_BLEND_SRC1_ALPHA;
else if (blenddc.RenderTarget[0].DestBlend == D3D12_BLEND_INV_SRC_ALPHA)
blenddc.RenderTarget[0].DestBlend = D3D12_BLEND_INV_SRC1_ALPHA;
blenddc.RenderTarget[0].SrcBlendAlpha = D3D12_BLEND_ONE;
blenddc.RenderTarget[0].DestBlendAlpha = D3D12_BLEND_ZERO;
blenddc.RenderTarget[0].BlendOpAlpha = D3D12_BLEND_OP_ADD;
}
return blenddc;
}
D3D12_RASTERIZER_DESC StateCache::GetDesc12(RasterizerState state)
{
return {
D3D12_FILL_MODE_SOLID,
state.cull_mode,
false,
0,
0.f,
0,
true,
true,
false,
0,
D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF
};
}
inline D3D12_DEPTH_STENCIL_DESC StateCache::GetDesc12(ZMode state)
{
D3D12_DEPTH_STENCIL_DESC depthdc;
depthdc.StencilEnable = FALSE;
depthdc.StencilReadMask = D3D11_DEFAULT_STENCIL_READ_MASK;
depthdc.StencilWriteMask = D3D11_DEFAULT_STENCIL_WRITE_MASK;
D3D12_DEPTH_STENCILOP_DESC defaultStencilOp = { D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_KEEP, D3D12_COMPARISON_FUNC_ALWAYS };
depthdc.FrontFace = defaultStencilOp;
depthdc.BackFace = defaultStencilOp;
const D3D12_COMPARISON_FUNC d3dCmpFuncs[8] =
{
D3D12_COMPARISON_FUNC_NEVER,
D3D12_COMPARISON_FUNC_GREATER,
D3D12_COMPARISON_FUNC_EQUAL,
D3D12_COMPARISON_FUNC_GREATER_EQUAL,
D3D12_COMPARISON_FUNC_LESS,
D3D12_COMPARISON_FUNC_NOT_EQUAL,
D3D12_COMPARISON_FUNC_LESS_EQUAL,
D3D12_COMPARISON_FUNC_ALWAYS
};
if (state.testenable)
{
depthdc.DepthEnable = TRUE;
depthdc.DepthWriteMask = state.updateenable ? D3D12_DEPTH_WRITE_MASK_ALL : D3D12_DEPTH_WRITE_MASK_ZERO;
depthdc.DepthFunc = d3dCmpFuncs[state.func];
}
else
{
// if the test is disabled write is disabled too
depthdc.DepthEnable = FALSE;
depthdc.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ZERO;
}
return depthdc;
}
HRESULT StateCache::GetPipelineStateObjectFromCache(D3D12_GRAPHICS_PIPELINE_STATE_DESC* pso_desc, ID3D12PipelineState** pso)
{
auto it = m_pso_map.find(*pso_desc);
if (it == m_pso_map.end())
{
// Not found, create new PSO.
ID3D12PipelineState* new_pso = nullptr;
HRESULT hr = D3D::device12->CreateGraphicsPipelineState(pso_desc, IID_PPV_ARGS(&new_pso));
if (FAILED(hr))
{
return hr;
}
m_pso_map[*pso_desc] = new_pso;
*pso = new_pso;
}
else
{
*pso = it->second;
}
return S_OK;
}
HRESULT StateCache::GetPipelineStateObjectFromCache(SmallPsoDesc* pso_desc, ID3D12PipelineState** pso, D3D12_PRIMITIVE_TOPOLOGY_TYPE topology, const GeometryShaderUid* gs_uid, const PixelShaderUid* ps_uid, const VertexShaderUid* vs_uid)
{
auto it = m_small_pso_map.find(*pso_desc);
if (it == m_small_pso_map.end())
{
// Not found, create new PSO.
// RootSignature, SampleMask, SampleDesc, NumRenderTargets, RTVFormats, DSVFormat
// never change so they are set in constructor and forgotten.
m_current_pso_desc.GS = pso_desc->gs_bytecode;
m_current_pso_desc.PS = pso_desc->ps_bytecode;
m_current_pso_desc.VS = pso_desc->vs_bytecode;
m_current_pso_desc.BlendState = GetDesc12(pso_desc->blend_state);
m_current_pso_desc.DepthStencilState = GetDesc12(pso_desc->depth_stencil_state);
m_current_pso_desc.RasterizerState = GetDesc12(pso_desc->rasterizer_state);
m_current_pso_desc.PrimitiveTopologyType = topology;
m_current_pso_desc.InputLayout = pso_desc->input_layout->GetActiveInputLayout12();
ID3D12PipelineState* new_pso = nullptr;
HRESULT hr = D3D::device12->CreateGraphicsPipelineState(&m_current_pso_desc, IID_PPV_ARGS(&new_pso));
if (FAILED(hr))
{
return hr;
}
m_small_pso_map[*pso_desc] = new_pso;
*pso = new_pso;
// This contains all of the information needed to reconstruct a PSO at startup.
SmallPsoDiskDesc disk_desc = {};
disk_desc.blend_state_hex = pso_desc->blend_state.hex;
disk_desc.depth_stencil_state_hex = pso_desc->depth_stencil_state.hex;
disk_desc.rasterizer_state_hex = pso_desc->rasterizer_state.hex;
disk_desc.ps_uid = *ps_uid;
disk_desc.vs_uid = *vs_uid;
disk_desc.gs_uid = *gs_uid;
disk_desc.vertex_declaration = pso_desc->input_layout->GetVertexDeclaration();
disk_desc.topology = topology;
// This shouldn't fail.. but if it does, don't cache to disk.
ID3DBlob* psoBlob = nullptr;
hr = new_pso->GetCachedBlob(&psoBlob);
if (SUCCEEDED(hr))
{
s_pso_disk_cache.Append(disk_desc, reinterpret_cast<const u8*>(psoBlob->GetBufferPointer()), static_cast<u32>(psoBlob->GetBufferSize()));
psoBlob->Release();
}
}
else
{
*pso = it->second;
}
return S_OK;
}
void StateCache::Clear()
{
for (auto& it : m_pso_map)
{
SAFE_RELEASE(it.second);
}
m_pso_map.clear();
for (auto& it : m_small_pso_map)
{
SAFE_RELEASE(it.second);
}
m_small_pso_map.clear();
s_pso_disk_cache.Sync();
s_pso_disk_cache.Close();
}
} // namespace DX12
+187
View File
@@ -0,0 +1,187 @@
// Copyright 2014 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <stack>
#include <unordered_map>
#include "Common/BitField.h"
#include "Common/CommonTypes.h"
#include "VideoBackends/D3D12/D3DBase.h"
#include "VideoBackends/D3D12/NativeVertexFormat.h"
#include "VideoBackends/D3D12/ShaderCache.h"
#include "VideoCommon/BPMemory.h"
namespace DX12
{
class PipelineStateCacheInserter;
union RasterizerState
{
BitField<0, 2, D3D12_CULL_MODE> cull_mode;
u32 hex;
};
union BlendState
{
BitField<0, 1, u32> blend_enable;
BitField<1, 3, D3D12_BLEND_OP> blend_op;
BitField<4, 4, u8> write_mask;
BitField<8, 5, D3D12_BLEND> src_blend;
BitField<13, 5, D3D12_BLEND> dst_blend;
BitField<18, 1, u32> use_dst_alpha;
u32 hex;
};
union SamplerState
{
BitField<0, 3, u32> min_filter;
BitField<3, 1, u32> mag_filter;
BitField<4, 8, u32> min_lod;
BitField<12, 8, u32> max_lod;
BitField<20, 8, s32> lod_bias;
BitField<28, 2, u32> wrap_s;
BitField<30, 2, u32> wrap_t;
u32 hex;
};
struct SmallPsoDesc
{
D3D12_SHADER_BYTECODE gs_bytecode;
D3D12_SHADER_BYTECODE ps_bytecode;
D3D12_SHADER_BYTECODE vs_bytecode;
D3DVertexFormat* input_layout;
BlendState blend_state;
RasterizerState rasterizer_state;
ZMode depth_stencil_state;
};
// The Bitfield members in BlendState, RasterizerState, and ZMode cause the..
// static_assert(std::is_trivially_copyable<K>::value, "K must be a trivially copyable type");
// .. check in LinearDiskCache to fail. So, just storing the packed u32 values.
struct SmallPsoDiskDesc
{
u32 blend_state_hex;
u32 rasterizer_state_hex;
u32 depth_stencil_state_hex;
PixelShaderUid ps_uid;
VertexShaderUid vs_uid;
GeometryShaderUid gs_uid;
D3D12_PRIMITIVE_TOPOLOGY_TYPE topology;
PortableVertexDeclaration vertex_declaration; // Used to construct the input layout.
};
class StateCache
{
public:
StateCache();
static void Init();
// Get D3D12 descs for the internal state bitfields.
static D3D12_SAMPLER_DESC GetDesc12(SamplerState state);
static D3D12_BLEND_DESC GetDesc12(BlendState state);
static D3D12_RASTERIZER_DESC GetDesc12(RasterizerState state);
static D3D12_DEPTH_STENCIL_DESC GetDesc12(ZMode state);
HRESULT GetPipelineStateObjectFromCache(D3D12_GRAPHICS_PIPELINE_STATE_DESC* pso_desc, ID3D12PipelineState** pso);
HRESULT GetPipelineStateObjectFromCache(SmallPsoDesc* pso_desc, ID3D12PipelineState** pso, D3D12_PRIMITIVE_TOPOLOGY_TYPE topology, const GeometryShaderUid* gs_uid, const PixelShaderUid* ps_uid, const VertexShaderUid* vs_uid);
// Release all cached states and clear hash tables.
void Clear();
private:
friend DX12::PipelineStateCacheInserter;
D3D12_GRAPHICS_PIPELINE_STATE_DESC m_current_pso_desc;
struct hash_pso_desc
{
size_t operator()(const D3D12_GRAPHICS_PIPELINE_STATE_DESC& pso_desc) const
{
return ((uintptr_t)pso_desc.PS.pShaderBytecode * 1000000) ^ ((uintptr_t)pso_desc.VS.pShaderBytecode * 1000) ^ ((uintptr_t)pso_desc.InputLayout.pInputElementDescs);
}
};
struct equality_pipeline_state_desc
{
bool operator()(const D3D12_GRAPHICS_PIPELINE_STATE_DESC& lhs, const D3D12_GRAPHICS_PIPELINE_STATE_DESC& rhs) const
{
return std::tie(lhs.PS.pShaderBytecode, lhs.VS.pShaderBytecode, lhs.GS.pShaderBytecode,
lhs.RasterizerState.CullMode,
lhs.DepthStencilState.DepthEnable,
lhs.DepthStencilState.DepthFunc,
lhs.DepthStencilState.DepthWriteMask,
lhs.BlendState.RenderTarget[0].BlendEnable,
lhs.BlendState.RenderTarget[0].BlendOp,
lhs.BlendState.RenderTarget[0].DestBlend,
lhs.BlendState.RenderTarget[0].SrcBlend,
lhs.BlendState.RenderTarget[0].RenderTargetWriteMask,
lhs.RTVFormats[0]) ==
std::tie(rhs.PS.pShaderBytecode, rhs.VS.pShaderBytecode, rhs.GS.pShaderBytecode,
rhs.RasterizerState.CullMode,
rhs.DepthStencilState.DepthEnable,
rhs.DepthStencilState.DepthFunc,
rhs.DepthStencilState.DepthWriteMask,
rhs.BlendState.RenderTarget[0].BlendEnable,
rhs.BlendState.RenderTarget[0].BlendOp,
rhs.BlendState.RenderTarget[0].DestBlend,
rhs.BlendState.RenderTarget[0].SrcBlend,
rhs.BlendState.RenderTarget[0].RenderTargetWriteMask,
rhs.RTVFormats[0]);
}
};
std::unordered_map<D3D12_GRAPHICS_PIPELINE_STATE_DESC, ID3D12PipelineState*, hash_pso_desc, equality_pipeline_state_desc> m_pso_map;
struct hash_small_pso_desc
{
size_t operator()(const SmallPsoDesc& pso_desc) const
{
return ((uintptr_t)pso_desc.vs_bytecode.pShaderBytecode << 10) ^
((uintptr_t)pso_desc.ps_bytecode.pShaderBytecode) +
pso_desc.blend_state.hex +
pso_desc.depth_stencil_state.hex;
}
};
struct equality_small_pipeline_state_desc
{
bool operator()(const SmallPsoDesc& lhs, const SmallPsoDesc& rhs) const
{
return std::tie(lhs.ps_bytecode.pShaderBytecode, lhs.vs_bytecode.pShaderBytecode, lhs.gs_bytecode.pShaderBytecode,
lhs.input_layout, lhs.blend_state.hex, lhs.depth_stencil_state.hex, lhs.rasterizer_state.hex) ==
std::tie(rhs.ps_bytecode.pShaderBytecode, rhs.vs_bytecode.pShaderBytecode, rhs.gs_bytecode.pShaderBytecode,
rhs.input_layout, rhs.blend_state.hex, rhs.depth_stencil_state.hex, rhs.rasterizer_state.hex);
}
};
struct hash_shader_bytecode
{
size_t operator()(const D3D12_SHADER_BYTECODE& shader) const
{
return (uintptr_t)shader.pShaderBytecode;
}
};
struct equality_shader_bytecode
{
bool operator()(const D3D12_SHADER_BYTECODE& lhs, const D3D12_SHADER_BYTECODE& rhs) const
{
return lhs.pShaderBytecode == rhs.pShaderBytecode;
}
};
std::unordered_map<SmallPsoDesc, ID3D12PipelineState*, hash_small_pso_desc, equality_small_pipeline_state_desc> m_small_pso_map;
};
} // namespace DX12
@@ -0,0 +1,358 @@
// Copyright 2016 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include <algorithm>
#include "VideoBackends/D3D12/D3DBase.h"
#include "VideoBackends/D3D12/D3DCommandListManager.h"
#include "VideoBackends/D3D12/D3DStreamBuffer.h"
#include "VideoBackends/D3D12/D3DUtil.h"
namespace DX12
{
D3DStreamBuffer::D3DStreamBuffer(unsigned int initial_size, unsigned int max_size, bool* buffer_reallocation_notification) :
m_buffer_size(initial_size),
m_buffer_max_size(max_size),
m_buffer_reallocation_notification(buffer_reallocation_notification)
{
CHECK(initial_size <= max_size, "Error: Initial size for D3DStreamBuffer is greater than max_size.");
AllocateBuffer(initial_size);
// Register for callback from D3DCommandListManager each time a fence is queued to be signaled.
m_buffer_tracking_fence = D3D::command_list_mgr->RegisterQueueFenceCallback(this, &D3DStreamBuffer::QueueFenceCallback);
}
D3DStreamBuffer::~D3DStreamBuffer()
{
D3D::command_list_mgr->RemoveQueueFenceCallback(this);
m_buffer->Unmap(0, nullptr);
D3D::command_list_mgr->DestroyResourceAfterCurrentCommandListExecuted(m_buffer);
}
// Function returns true if (worst case), needed to flush existing command list in order to
// ensure the GPU finished with current use of buffer. The calling function will need to take
// care to reset GPU state to what it was previously.
// Obviously this is non-performant, so the buffer max_size should be large enough to
// ensure this never happens.
bool D3DStreamBuffer::AllocateSpaceInBuffer(unsigned int allocation_size, unsigned int alignment)
{
CHECK(allocation_size <= m_buffer_max_size, "Error: Requested allocation size in D3DStreamBuffer is greater than max allowed size of backing buffer.");
if (alignment)
{
unsigned int padding = m_buffer_offset % alignment;
// Check for case when adding alignment causes CPU offset to equal GPU offset,
// which would imply entire buffer is available (if not corrected).
if (m_buffer_offset < m_buffer_gpu_completion_offset &&
m_buffer_offset + alignment - padding >= m_buffer_gpu_completion_offset)
{
m_buffer_gpu_completion_offset++;
}
m_buffer_offset += alignment - padding;
if (m_buffer_offset > m_buffer_size)
{
m_buffer_offset = 0;
// Correct for case where CPU was about to run into GPU.
if (m_buffer_gpu_completion_offset == 0)
m_buffer_gpu_completion_offset = 1;
}
}
// First, check if there is available (not-in-use-by-GPU) space in existing buffer.
if (AttemptToAllocateOutOfExistingUnusedSpaceInBuffer(allocation_size))
{
return false;
}
// Slow path. No room at front, or back, due to the GPU still (possibly) accessing parts of the buffer.
// Resize if possible, else stall.
bool command_list_executed = AttemptBufferResizeOrElseStall(allocation_size);
return command_list_executed;
}
// In VertexManager, we don't know the 'real' size of the allocation at the time
// we call AllocateSpaceInBuffer. We have to conservatively allocate 16MB (!).
// After the vertex data is written, we can choose to specify the 'real' allocation
// size to avoid wasting space.
void D3DStreamBuffer::OverrideSizeOfPreviousAllocation(unsigned int override_allocation_size)
{
m_buffer_offset = m_buffer_current_allocation_offset + override_allocation_size;
}
void D3DStreamBuffer::AllocateBuffer(unsigned int size)
{
// First, put existing buffer (if it exists) in deferred destruction list.
if (m_buffer)
{
m_buffer->Unmap(0, nullptr);
D3D::command_list_mgr->DestroyResourceAfterCurrentCommandListExecuted(m_buffer);
m_buffer = nullptr;
}
CheckHR(
D3D::device12->CreateCommittedResource(
&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD),
D3D12_HEAP_FLAG_NONE,
&CD3DX12_RESOURCE_DESC::Buffer(size),
D3D12_RESOURCE_STATE_GENERIC_READ,
nullptr,
IID_PPV_ARGS(&m_buffer)
)
);
CheckHR(m_buffer->Map(0, nullptr, &m_buffer_cpu_address));
m_buffer_gpu_address = m_buffer->GetGPUVirtualAddress();
m_buffer_size = size;
}
// Function returns true if current command list executed as a result of current command list
// referencing all of buffer's contents, AND we are already at max_size. No alternative but to
// flush. See comments above AllocateSpaceInBuffer for more details.
bool D3DStreamBuffer::AttemptBufferResizeOrElseStall(unsigned int allocation_size)
{
// This function will attempt to increase the size of the buffer, in response
// to running out of room. If the buffer is already at its maximum size specified
// at creation time, then stall waiting for the GPU to finish with the currently
// requested memory.
// Four possibilities, in order of desirability.
// 1) Best - Update GPU tracking progress - maybe the GPU has made enough
// progress such that there is now room.
// 2) Enlarge GPU buffer, up to our max allowed size.
// 3) Stall until GPU finishes existing queued work/advances offset
// in buffer enough to free room.
// 4) Worst - flush current GPU commands and wait, which will free all room
// in buffer.
// 1) First, let's check if GPU has already continued farther along buffer. If it has freed up
// enough of the buffer, we won't have to stall/allocate new memory.
UpdateGPUProgress();
// Now that GPU progress is updated, do we have room in the queue?
if (AttemptToAllocateOutOfExistingUnusedSpaceInBuffer(allocation_size))
{
return false;
}
// 2) Next, prefer increasing buffer size instead of stalling.
unsigned int new_size = std::min(static_cast<unsigned int>(m_buffer_size * 1.5f), m_buffer_max_size);
new_size = std::max(new_size, allocation_size);
// Can we grow buffer further?
if (new_size > m_buffer_size)
{
AllocateBuffer(new_size);
m_buffer_current_allocation_offset = 0;
m_buffer_offset = allocation_size;
if (m_buffer_reallocation_notification != nullptr)
{
*m_buffer_reallocation_notification = true;
}
return false;
}
// 3) Bad case - we need to stall.
// This might be ok if we have > 2 frames queued up or something, but
// we don't want to be stalling as we generate the front-of-queue frame.
const bool found_fence_to_wait_on = AttemptToFindExistingFenceToStallOn(allocation_size);
if (found_fence_to_wait_on)
{
return false;
}
// 4) If we get to this point, that means there is no outstanding queued GPU work, and we're still out of room.
// This is bad - and performance will suffer due to the CPU/GPU serialization, but the show must go on.
// This is guaranteed to succeed, since we've already CHECK'd that the allocation_size <= max_buffer_size, and flushing now and waiting will
// free all space in buffer.
D3D::command_list_mgr->ExecuteQueuedWork(true);
m_buffer_offset = allocation_size;
m_buffer_current_allocation_offset = 0;
m_buffer_gpu_completion_offset = 0;
return true;
}
// Return true if space is found.
bool D3DStreamBuffer::AttemptToAllocateOutOfExistingUnusedSpaceInBuffer(unsigned int allocation_size)
{
// First, check if there is room at end of buffer. Fast path.
if (m_buffer_offset >= m_buffer_gpu_completion_offset)
{
if (m_buffer_offset + allocation_size <= m_buffer_size)
{
m_buffer_current_allocation_offset = m_buffer_offset;
m_buffer_offset += allocation_size;
return true;
}
if (0 + allocation_size < m_buffer_gpu_completion_offset)
{
m_buffer_current_allocation_offset = 0;
m_buffer_offset = allocation_size;
return true;
}
}
// Next, check if there is room at front of buffer. Fast path.
if (m_buffer_offset < m_buffer_gpu_completion_offset && m_buffer_offset + allocation_size < m_buffer_gpu_completion_offset)
{
m_buffer_current_allocation_offset = m_buffer_offset;
m_buffer_offset += allocation_size;
return true;
}
return false;
}
// Returns true if fence was found and waited on.
bool D3DStreamBuffer::AttemptToFindExistingFenceToStallOn(unsigned int allocation_size)
{
// Let's find the first fence that will free up enough space in our buffer.
UINT64 fence_value_required = 0;
unsigned int new_buffer_offset = 0;
while (m_queued_fences.size() > 0)
{
FenceTrackingInformation tracking_information = m_queued_fences.front();
m_queued_fences.pop();
if (m_buffer_offset >= m_buffer_gpu_completion_offset)
{
// At this point, we need to wrap around, so req'd gpu offset is allocation_size.
if (tracking_information.buffer_offset >= allocation_size)
{
fence_value_required = tracking_information.fence_value;
m_buffer_current_allocation_offset = 0;
m_buffer_offset = allocation_size;
break;
}
}
else
{
if (m_buffer_offset + allocation_size <= m_buffer_size)
{
if (tracking_information.buffer_offset >= m_buffer_offset + allocation_size)
{
fence_value_required = tracking_information.fence_value;
m_buffer_current_allocation_offset = m_buffer_offset;
m_buffer_offset = m_buffer_offset + allocation_size;
break;
}
}
else
{
if (tracking_information.buffer_offset >= allocation_size)
{
fence_value_required = tracking_information.fence_value;
m_buffer_current_allocation_offset = 0;
m_buffer_offset = allocation_size;
break;
}
}
}
}
// Check if we found a fence we can wait on, for GPU to make sufficient progress.
// If so, wait on it.
if (fence_value_required > 0)
{
D3D::command_list_mgr->WaitOnCPUForFence(m_buffer_tracking_fence, fence_value_required);
return true;
}
return false;
}
void D3DStreamBuffer::UpdateGPUProgress()
{
const UINT64 fence_value = m_buffer_tracking_fence->GetCompletedValue();
while (m_queued_fences.size() > 0)
{
FenceTrackingInformation tracking_information = m_queued_fences.front();
m_queued_fences.pop();
// Has fence gone past this point?
if (fence_value > tracking_information.fence_value)
{
m_buffer_gpu_completion_offset = tracking_information.buffer_offset;
}
else
{
// Fences are stored in assending order, so once we hit a fence we haven't yet crossed on GPU, abort search.
break;
}
}
}
void D3DStreamBuffer::QueueFenceCallback(void* owning_object, UINT64 fence_value)
{
reinterpret_cast<D3DStreamBuffer*>(owning_object)->QueueFence(fence_value);
}
void D3DStreamBuffer::QueueFence(UINT64 fence_value)
{
FenceTrackingInformation tracking_information = {};
tracking_information.fence_value = fence_value;
tracking_information.buffer_offset = m_buffer_offset;
m_queued_fences.push(tracking_information);
}
ID3D12Resource* D3DStreamBuffer::GetBuffer() const
{
return m_buffer;
}
D3D12_GPU_VIRTUAL_ADDRESS D3DStreamBuffer::GetGPUAddressOfCurrentAllocation() const
{
return m_buffer_gpu_address + m_buffer_current_allocation_offset;
}
void* D3DStreamBuffer::GetCPUAddressOfCurrentAllocation() const
{
return static_cast<u8*>(m_buffer_cpu_address) + m_buffer_current_allocation_offset;
}
unsigned int D3DStreamBuffer::GetOffsetOfCurrentAllocation() const
{
return m_buffer_current_allocation_offset;
}
unsigned int D3DStreamBuffer::GetSize() const
{
return m_buffer_size;
}
void* D3DStreamBuffer::GetBaseCPUAddress() const
{
return m_buffer_cpu_address;
}
D3D12_GPU_VIRTUAL_ADDRESS D3DStreamBuffer::GetBaseGPUAddress() const
{
return m_buffer_gpu_address;
}
}
@@ -0,0 +1,70 @@
// Copyright 2016 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <queue>
struct ID3D12Resource;
namespace DX12
{
class D3DStreamBuffer
{
public:
D3DStreamBuffer(unsigned int initial_size, unsigned int max_size, bool* buffer_reallocation_notification);
~D3DStreamBuffer();
bool AllocateSpaceInBuffer(unsigned int allocation_size, unsigned int alignment);
void OverrideSizeOfPreviousAllocation(unsigned int override_allocation_size);
void* GetBaseCPUAddress() const;
D3D12_GPU_VIRTUAL_ADDRESS GetBaseGPUAddress() const;
ID3D12Resource* GetBuffer() const;
void* GetCPUAddressOfCurrentAllocation() const;
D3D12_GPU_VIRTUAL_ADDRESS GetGPUAddressOfCurrentAllocation() const;
unsigned int GetOffsetOfCurrentAllocation() const;
unsigned int GetSize() const;
static void QueueFenceCallback(void* owning_object, UINT64 fence_value);
private:
void AllocateBuffer(unsigned int size);
bool AttemptBufferResizeOrElseStall(unsigned int new_size);
bool AttemptToAllocateOutOfExistingUnusedSpaceInBuffer(unsigned int allocation_size);
bool AttemptToFindExistingFenceToStallOn(unsigned int allocation_size);
void UpdateGPUProgress();
void QueueFence(UINT64 fence_value);
struct FenceTrackingInformation
{
UINT64 fence_value;
unsigned int buffer_offset;
};
std::queue<FenceTrackingInformation> m_queued_fences;
ID3D12Fence* m_buffer_tracking_fence = nullptr;
ID3D12Resource* m_buffer = nullptr;
void* m_buffer_cpu_address = nullptr;
D3D12_GPU_VIRTUAL_ADDRESS m_buffer_gpu_address = {};
unsigned int m_buffer_current_allocation_offset = 0;
unsigned int m_buffer_offset = 0;
unsigned int m_buffer_size = 0;
const unsigned int m_buffer_max_size = 0;
unsigned int m_buffer_gpu_completion_offset = 0;
bool* m_buffer_reallocation_notification = nullptr;
};
}

Some files were not shown because too many files have changed in this diff Show More