D3D12: Initial commit for D3D12 backend implementation.

This commit is contained in:
hdcmeta 2016-01-07 19:40:35 -08:00
parent a2e2e36745
commit 8cc686b360
54 changed files with 12296 additions and 7 deletions

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">

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();

View File

@ -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;
}
};

View File

@ -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);
};
};

View File

@ -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>

View File

@ -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>

View File

@ -0,0 +1,970 @@
// Copyright 2010 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include <algorithm>
#include "Common/CommonTypes.h"
#include "Common/MsgHandler.h"
#include "Common/StringUtil.h"
#include "Common/Logging/Log.h"
#include "VideoBackends/D3D12/D3DBase.h"
#include "VideoBackends/D3D12/D3DCommandListManager.h"
#include "VideoBackends/D3D12/D3DDescriptorHeapManager.h"
#include "VideoBackends/D3D12/D3DState.h"
#include "VideoBackends/D3D12/D3DTexture.h"
#include "VideoCommon/VideoConfig.h"
static const unsigned int SWAP_CHAIN_BUFFER_COUNT = 4;
namespace DX12
{
// d3dcompiler_*.dll exports
static HINSTANCE s_d3d_compiler_dll = nullptr;
static int s_d3d_compiler_dll_ref = 0;
D3DREFLECT d3d_reflect = nullptr;
D3DCREATEBLOB d3d_create_blob = nullptr;
pD3DCompile d3d_compile = nullptr;
// dxgi.dll exports
static HINSTANCE s_dxgi_dll = nullptr;
static int s_dxgi_dll_ref = 0;
CREATEDXGIFACTORY create_dxgi_factory = nullptr;
// d3d12.dll exports
static HINSTANCE s_d3d12_dll = nullptr;
static int s_d3d12_dll_ref = 0;
D3D12CREATEDEVICE d3d12_create_device = nullptr;
D3D12SERIALIZEROOTSIGNATURE d3d12_serialize_root_signature = nullptr;
D3D12GETDEBUGINTERFACE d3d12_get_debug_interface = nullptr;
namespace D3D
{
// Begin extern'd variables.
ID3D12Device* device12 = nullptr;
ID3D12CommandQueue* command_queue = nullptr;
D3DCommandListManager* command_list_mgr = nullptr;
ID3D12GraphicsCommandList* current_command_list = nullptr;
ID3D12RootSignature* default_root_signature = nullptr;
D3D12_CPU_DESCRIPTOR_HANDLE null_srv_cpu = {};
D3D12_CPU_DESCRIPTOR_HANDLE null_srv_cpu_shadow = {};
unsigned int resource_descriptor_size = 0;
unsigned int sampler_descriptor_size = 0;
D3DDescriptorHeapManager* gpu_descriptor_heap_mgr = nullptr;
D3DDescriptorHeapManager* sampler_descriptor_heap_mgr = nullptr;
D3DDescriptorHeapManager* dsv_descriptor_heap_mgr = nullptr;
D3DDescriptorHeapManager* rtv_descriptor_heap_mgr = nullptr;
std::array<ID3D12DescriptorHeap*, 2> gpu_descriptor_heaps;
HWND hWnd;
// End extern'd variables.
static IDXGISwapChain* s_swap_chain = nullptr;
static unsigned int s_monitor_refresh_rate = 0;
static LARGE_INTEGER s_qpc_frequency;
static ID3D12DebugDevice* s_debug_device12 = nullptr;
static D3D_FEATURE_LEVEL s_feat_level;
static D3DTexture2D* s_backbuf[SWAP_CHAIN_BUFFER_COUNT];
static unsigned int s_current_back_buf = 0;
static unsigned int s_xres = 0;
static unsigned int s_yres = 0;
static bool s_frame_in_progress = false;
static std::vector<DXGI_SAMPLE_DESC> s_aa_modes; // supported AA modes of the current adapter
static const D3D_FEATURE_LEVEL s_supported_feature_levels[] = {
D3D_FEATURE_LEVEL_11_0
};
HRESULT LoadDXGI()
{
if (s_dxgi_dll_ref++ > 0)
return S_OK;
if (s_dxgi_dll)
return S_OK;
s_dxgi_dll = LoadLibraryA("dxgi.dll");
if (!s_dxgi_dll)
{
MessageBoxA(nullptr, "Failed to load dxgi.dll", "Critical error", MB_OK | MB_ICONERROR);
--s_dxgi_dll_ref;
return E_FAIL;
}
create_dxgi_factory = (CREATEDXGIFACTORY)GetProcAddress(s_dxgi_dll, "CreateDXGIFactory");
if (create_dxgi_factory == nullptr)
MessageBoxA(nullptr, "GetProcAddress failed for CreateDXGIFactory!", "Critical error", MB_OK | MB_ICONERROR);
return S_OK;
}
HRESULT LoadD3D()
{
if (s_d3d12_dll_ref++ > 0)
return S_OK;
s_d3d12_dll = LoadLibraryA("d3d12.dll");
if (!s_d3d12_dll)
{
MessageBoxA(nullptr, "Failed to load d3d12.dll", "Critical error", MB_OK | MB_ICONERROR);
--s_d3d12_dll_ref;
return E_FAIL;
}
d3d12_create_device = (D3D12CREATEDEVICE)GetProcAddress(s_d3d12_dll, "D3D12CreateDevice");
if (d3d12_create_device == nullptr)
{
MessageBoxA(nullptr, "GetProcAddress failed for D3D12CreateDevice!", "Critical error", MB_OK | MB_ICONERROR);
return E_FAIL;
}
d3d12_serialize_root_signature = (D3D12SERIALIZEROOTSIGNATURE)GetProcAddress(s_d3d12_dll, "D3D12SerializeRootSignature");
if (d3d12_serialize_root_signature == nullptr)
{
MessageBoxA(nullptr, "GetProcAddress failed for D3D12SerializeRootSignature!", "Critical error", MB_OK | MB_ICONERROR);
return E_FAIL;
}
d3d12_get_debug_interface = (D3D12GETDEBUGINTERFACE)GetProcAddress(s_d3d12_dll, "D3D12GetDebugInterface");
if (d3d12_get_debug_interface == nullptr)
{
MessageBoxA(nullptr, "GetProcAddress failed for D3D12GetDebugInterface!", "Critical error", MB_OK | MB_ICONERROR);
return E_FAIL;
}
return S_OK;
}
HRESULT LoadD3DCompiler()
{
if (s_d3d_compiler_dll_ref++ > 0)
return S_OK;
if (s_d3d_compiler_dll)
return S_OK;
// try to load D3DCompiler first to check whether we have proper runtime support
// try to use the dll the backend was compiled against first - don't bother about debug runtimes
s_d3d_compiler_dll = LoadLibraryA(D3DCOMPILER_DLL_A);
if (!s_d3d_compiler_dll)
{
// if that fails, use the dll which should be available in every SDK which officially supports DX12.
s_d3d_compiler_dll = LoadLibraryA("D3DCompiler_42.dll");
if (!s_d3d_compiler_dll)
{
MessageBoxA(nullptr, "Failed to load D3DCompiler_42.dll, update your DX12 runtime, please", "Critical error", MB_OK | MB_ICONERROR);
return E_FAIL;
}
else
{
NOTICE_LOG(VIDEO, "Successfully loaded D3DCompiler_42.dll. If you're having trouble, try updating your DX runtime first.");
}
}
d3d_reflect = (D3DREFLECT) GetProcAddress(s_d3d_compiler_dll, "D3DReflect");
if (d3d_reflect == nullptr)
MessageBoxA(nullptr, "GetProcAddress failed for D3DReflect!", "Critical error", MB_OK | MB_ICONERROR);
d3d_create_blob = (D3DCREATEBLOB)GetProcAddress(s_d3d_compiler_dll, "D3DCreateBlob");
if (d3d_create_blob == nullptr)
MessageBoxA(nullptr, "GetProcAddress failed for D3DCreateBlob!", "Critical error", MB_OK | MB_ICONERROR);
d3d_compile = (pD3DCompile) GetProcAddress(s_d3d_compiler_dll, "D3DCompile");
if (d3d_compile == nullptr)
MessageBoxA(nullptr, "GetProcAddress failed for D3DCompile!", "Critical error", MB_OK | MB_ICONERROR);
return S_OK;
}
void UnloadDXGI()
{
if (!s_dxgi_dll_ref)
return;
if (--s_dxgi_dll_ref != 0)
return;
if (s_dxgi_dll)
FreeLibrary(s_dxgi_dll);
s_dxgi_dll = nullptr;
create_dxgi_factory = nullptr;
}
void UnloadD3D()
{
if (!s_d3d12_dll_ref)
return;
if (--s_d3d12_dll_ref != 0)
return;
if (s_d3d12_dll)
FreeLibrary(s_d3d12_dll);
s_d3d12_dll = nullptr;
d3d12_create_device = nullptr;
d3d12_serialize_root_signature = nullptr;
}
void UnloadD3DCompiler()
{
if (!s_d3d_compiler_dll_ref)
return;
if (--s_d3d_compiler_dll_ref != 0)
return;
if (s_d3d_compiler_dll)
FreeLibrary(s_d3d_compiler_dll);
s_d3d_compiler_dll = nullptr;
d3d_compile = nullptr;
d3d_create_blob = nullptr;
d3d_reflect = nullptr;
}
bool AlertUserIfSelectedAdapterDoesNotSupportD3D12()
{
HRESULT hr = LoadDXGI();
if (SUCCEEDED(hr))
{
hr = LoadD3D();
}
if (FAILED(hr))
{
// LoadDXGI / LoadD3D display a specific error message,
// no need to do that here.
return false;
}
IDXGIFactory* factory = nullptr;
IDXGIAdapter* adapter = nullptr;
ID3D12Device* device = nullptr;
if (SUCCEEDED(hr))
{
hr = create_dxgi_factory(__uuidof(IDXGIFactory), (void**)&factory);
}
if (SUCCEEDED(hr))
{
hr = factory->EnumAdapters(g_ActiveConfig.iAdapter, &adapter);
}
if (SUCCEEDED(hr))
{
hr = d3d12_create_device(adapter, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&device));
SAFE_RELEASE(device);
SAFE_RELEASE(adapter);
SAFE_RELEASE(factory);
if (FAILED(hr))
{
UnloadD3D();
UnloadDXGI();
MessageBoxA(nullptr, "Failed to create a D3D12 device on the selected adapter.\n\nPlease make sure it supports Direct3D 12, and that your graphics drivers are up-to-date.", "Critical error", MB_OK | MB_ICONERROR);
return false;
}
// If succeeded, leave DXGI and D3D libraries loaded since we'll use them in Create().
return true;
}
// DXGI failed to create factory/enumerate adapter. This should be very uncommon.
MessageBoxA(nullptr, "Failed to create enumerate selected adapter. Please select a different graphics adapter.", "Critical error", MB_OK | MB_ICONERROR);
SAFE_RELEASE(adapter);
SAFE_RELEASE(factory);
UnloadD3D();
UnloadDXGI();
return false;
}
std::vector<DXGI_SAMPLE_DESC> EnumAAModes(IDXGIAdapter* adapter)
{
std::vector<DXGI_SAMPLE_DESC> aa_modes;
bool d3d12_supported = AlertUserIfSelectedAdapterDoesNotSupportD3D12();
if (!d3d12_supported)
return aa_modes;
ID3D12Device* device12 = nullptr;
d3d12_create_device(adapter, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&device12));
if (device12)
{
for (int samples = 0; samples < D3D12_MAX_MULTISAMPLE_SAMPLE_COUNT; ++samples)
{
D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS multisample_quality_levels = {};
multisample_quality_levels.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
multisample_quality_levels.SampleCount = samples;
device12->CheckFeatureSupport(D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS, &multisample_quality_levels, sizeof(multisample_quality_levels));
DXGI_SAMPLE_DESC desc;
desc.Count = samples;
desc.Quality = 0;
if (multisample_quality_levels.NumQualityLevels > 0)
{
aa_modes.push_back(desc);
}
}
device12->Release();
}
return aa_modes;
}
D3D_FEATURE_LEVEL GetFeatureLevel(IDXGIAdapter* adapter)
{
return D3D_FEATURE_LEVEL_11_0;
}
HRESULT Create(HWND wnd)
{
hWnd = wnd;
HRESULT hr;
RECT client;
GetClientRect(hWnd, &client);
s_xres = client.right - client.left;
s_yres = client.bottom - client.top;
hr = LoadDXGI();
if (SUCCEEDED(hr))
hr = LoadD3D();
if (SUCCEEDED(hr))
hr = LoadD3DCompiler();
if (FAILED(hr))
{
UnloadDXGI();
UnloadD3D();
UnloadD3DCompiler();
return hr;
}
IDXGIFactory* factory;
IDXGIAdapter* adapter;
IDXGIOutput* output;
hr = create_dxgi_factory(__uuidof(IDXGIFactory), (void**)&factory);
if (FAILED(hr))
MessageBox(wnd, _T("Failed to create IDXGIFactory object"), _T("Dolphin Direct3D 12 backend"), MB_OK | MB_ICONERROR);
hr = factory->EnumAdapters(g_ActiveConfig.iAdapter, &adapter);
if (FAILED(hr))
{
// try using the first one
hr = factory->EnumAdapters(0, &adapter);
if (FAILED(hr))
MessageBox(wnd, _T("Failed to enumerate adapters"), _T("Dolphin Direct3D 12 backend"), MB_OK | MB_ICONERROR);
}
// TODO: Make this configurable
hr = adapter->EnumOutputs(0, &output);
if (FAILED(hr))
{
// try using the first one
IDXGIAdapter* firstadapter;
hr = factory->EnumAdapters(0, &firstadapter);
if (!FAILED(hr))
hr = firstadapter->EnumOutputs(0, &output);
if (FAILED(hr))
MessageBox(wnd,
_T("Failed to enumerate outputs!\n")
_T("This usually happens when you've set your video adapter to the Nvidia GPU in an Optimus-equipped system.\n")
_T("Set Dolphin to use the high-performance graphics in Nvidia's drivers instead and leave Dolphin's video adapter set to the Intel GPU."),
_T("Dolphin Direct3D 12 backend"), MB_OK | MB_ICONERROR);
SAFE_RELEASE(firstadapter);
}
// get supported AA modes
s_aa_modes = EnumAAModes(adapter);
if (std::find_if(
s_aa_modes.begin(),
s_aa_modes.end(),
[](const DXGI_SAMPLE_DESC& desc) {return desc.Count == g_Config.iMultisamples; }
) == s_aa_modes.end())
{
g_Config.iMultisamples = 1;
UpdateActiveConfig();
}
DXGI_SWAP_CHAIN_DESC swap_chain_desc = {};
swap_chain_desc.BufferCount = SWAP_CHAIN_BUFFER_COUNT;
swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swap_chain_desc.OutputWindow = wnd;
swap_chain_desc.SampleDesc.Count = 1;
swap_chain_desc.SampleDesc.Quality = 0;
swap_chain_desc.Windowed = true;
swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
swap_chain_desc.Flags = 0;
swap_chain_desc.BufferDesc.Width = s_xres;
swap_chain_desc.BufferDesc.Height = s_yres;
swap_chain_desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
swap_chain_desc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
#if defined(_DEBUG) || defined(DEBUGFAST)
// Creating debug devices can sometimes fail if the user doesn't have the correct
// version of the DirectX SDK. If it does, simply fallback to a non-debug device.
{
if (SUCCEEDED(hr))
{
ID3D12Debug* debug_controller;
hr = d3d12_get_debug_interface(IID_PPV_ARGS(&debug_controller));
if (SUCCEEDED(hr))
{
debug_controller->EnableDebugLayer();
debug_controller->Release();
}
else
{
MessageBox(wnd, _T("Failed to initialize Direct3D debug layer, please make sure it is installed."), _T("Dolphin Direct3D 12 backend"), MB_OK | MB_ICONERROR);
}
hr = d3d12_create_device(adapter, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&device12));
s_feat_level = D3D_FEATURE_LEVEL_11_0;
}
}
if (FAILED(hr))
#endif
{
if (SUCCEEDED(hr))
{
#ifdef USE_D3D12_DEBUG_LAYER
ID3D12Debug* debug_controller;
hr = d3d12_get_debug_interface(IID_PPV_ARGS(&debug_controller));
if (SUCCEEDED(hr))
{
debug_controller->EnableDebugLayer();
debug_controller->Release();
}
else
{
MessageBox(wnd, _T("Failed to initialize Direct3D debug layer."), _T("Dolphin Direct3D 12 backend"), MB_OK | MB_ICONERROR);
}
#endif
hr = d3d12_create_device(adapter, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&device12));
s_feat_level = D3D_FEATURE_LEVEL_11_0;
}
}
if (SUCCEEDED(hr))
{
D3D12_COMMAND_QUEUE_DESC command_queue_desc = {
D3D12_COMMAND_LIST_TYPE_DIRECT, // D3D12_COMMAND_LIST_TYPE Type;
0, // INT Priority;
D3D12_COMMAND_QUEUE_FLAG_NONE, // D3D12_COMMAND_QUEUE_FLAG Flags;
0 // UINT NodeMask;
};
CheckHR(device12->CreateCommandQueue(&command_queue_desc, IID_PPV_ARGS(&command_queue)));
IDXGIFactory* factory = nullptr;
adapter->GetParent(IID_PPV_ARGS(&factory));
CheckHR(factory->CreateSwapChain(command_queue, &swap_chain_desc, &s_swap_chain));
s_current_back_buf = 0;
factory->Release();
}
if (SUCCEEDED(hr))
{
// Query the monitor refresh rate, to ensure proper Present throttling behavior.
DEVMODE dev_mode;
memset(&dev_mode, 0, sizeof(DEVMODE));
dev_mode.dmSize = sizeof(DEVMODE);
dev_mode.dmDriverExtra = 0;
if (EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dev_mode) == 0)
{
// If EnumDisplaySettings fails, assume monitor refresh rate of 60 Hz.
s_monitor_refresh_rate = 60;
}
else
{
s_monitor_refresh_rate = dev_mode.dmDisplayFrequency;
}
}
if (FAILED(hr))
{
MessageBox(wnd, _T("Failed to initialize Direct3D.\nMake sure your video card supports Direct3D 12 and your drivers are up-to-date."), _T("Dolphin Direct3D 12 backend"), MB_OK | MB_ICONERROR);
SAFE_RELEASE(s_swap_chain);
return E_FAIL;
}
ID3D12InfoQueue* info_queue = nullptr;
if (SUCCEEDED(device12->QueryInterface(&info_queue)))
{
CheckHR(info_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_ERROR, TRUE));
CheckHR(info_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_WARNING, TRUE));
D3D12_INFO_QUEUE_FILTER filter = {};
D3D12_MESSAGE_ID id_list[] = {
D3D12_MESSAGE_ID_CREATEGRAPHICSPIPELINESTATE_DEPTHSTENCILVIEW_NOT_SET, // Benign.
D3D12_MESSAGE_ID_CREATEGRAPHICSPIPELINESTATE_RENDERTARGETVIEW_NOT_SET, // Benign.
D3D12_MESSAGE_ID_CREATEINPUTLAYOUT_TYPE_MISMATCH, // Benign.
D3D12_MESSAGE_ID_DRAW_EMPTY_SCISSOR_RECTANGLE, // Benign. Probably.
D3D12_MESSAGE_ID_INVALID_SUBRESOURCE_STATE,
D3D12_MESSAGE_ID_MAP_INVALID_NULLRANGE, // Benign.
D3D12_MESSAGE_ID_EXECUTECOMMANDLISTS_GPU_WRITTEN_READBACK_RESOURCE_MAPPED, // Benign.
D3D12_MESSAGE_ID_RESOURCE_BARRIER_BEFORE_AFTER_MISMATCH // Benign. Probably.
};
filter.DenyList.NumIDs = ARRAYSIZE(id_list);
filter.DenyList.pIDList = id_list;
info_queue->PushStorageFilter(&filter);
info_queue->Release();
// Used at Close time to report live objects.
CheckHR(device12->QueryInterface(&s_debug_device12));
}
// prevent DXGI from responding to Alt+Enter, unfortunately DXGI_MWA_NO_ALT_ENTER
// does not work so we disable all monitoring of window messages. However this
// may make it more difficult for DXGI to handle display mode changes.
hr = factory->MakeWindowAssociation(wnd, DXGI_MWA_NO_WINDOW_CHANGES);
if (FAILED(hr))
MessageBox(wnd, _T("Failed to associate the window"), _T("Dolphin Direct3D 12 backend"), MB_OK | MB_ICONERROR);
SAFE_RELEASE(factory);
SAFE_RELEASE(output);
SAFE_RELEASE(adapter)
CreateDescriptorHeaps();
CreateRootSignatures();
command_list_mgr = new D3DCommandListManager(
D3D12_COMMAND_LIST_TYPE_DIRECT,
device12,
command_queue
);
command_list_mgr->GetCommandList(&current_command_list);
command_list_mgr->SetInitialCommandListState();
for (UINT i = 0; i < SWAP_CHAIN_BUFFER_COUNT; i++)
{
ID3D12Resource* buf12 = nullptr;
hr = s_swap_chain->GetBuffer(i, IID_PPV_ARGS(&buf12));
CHECK(SUCCEEDED(hr), "Retrieve back buffer texture");
s_backbuf[i] = new D3DTexture2D(buf12,
D3D11_BIND_RENDER_TARGET,
DXGI_FORMAT_UNKNOWN,
DXGI_FORMAT_UNKNOWN,
DXGI_FORMAT_UNKNOWN,
false,
D3D12_RESOURCE_STATE_PRESENT // Swap Chain back buffers start out in D3D12_RESOURCE_STATE_PRESENT.
);
SAFE_RELEASE(buf12);
SetDebugObjectName12(s_backbuf[i]->GetTex12(), "backbuffer texture");
}
s_backbuf[s_current_back_buf]->TransitionToResourceState(current_command_list, D3D12_RESOURCE_STATE_RENDER_TARGET);
current_command_list->OMSetRenderTargets(1, &s_backbuf[s_current_back_buf]->GetRTV12(), FALSE, nullptr);
QueryPerformanceFrequency(&s_qpc_frequency);
return S_OK;
}
void CreateDescriptorHeaps()
{
// Create D3D12 GPU and CPU descriptor heaps.
{
D3D12_DESCRIPTOR_HEAP_DESC gpu_descriptor_heap_desc = {};
gpu_descriptor_heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
gpu_descriptor_heap_desc.NumDescriptors = 500000;
gpu_descriptor_heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
gpu_descriptor_heap_mgr = new D3DDescriptorHeapManager(&gpu_descriptor_heap_desc, device12, 50000);
gpu_descriptor_heaps[0] = gpu_descriptor_heap_mgr->GetDescriptorHeap();
D3D12_CPU_DESCRIPTOR_HANDLE descriptor_heap_cpu_base = gpu_descriptor_heap_mgr->GetDescriptorHeap()->GetCPUDescriptorHandleForHeapStart();
resource_descriptor_size = device12->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
sampler_descriptor_size = device12->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
D3D12_GPU_DESCRIPTOR_HANDLE null_srv_gpu = {};
gpu_descriptor_heap_mgr->Allocate(&null_srv_cpu, &null_srv_gpu, &null_srv_cpu_shadow);
D3D12_SHADER_RESOURCE_VIEW_DESC null_srv_desc = {};
null_srv_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
null_srv_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
null_srv_desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
device12->CreateShaderResourceView(NULL, &null_srv_desc, null_srv_cpu);
for (UINT i = 0; i < 500000; i++)
{
// D3D12TODO: Make paving of descriptor heap optional.
D3D12_CPU_DESCRIPTOR_HANDLE destination_descriptor = {};
destination_descriptor.ptr = descriptor_heap_cpu_base.ptr + i * resource_descriptor_size;
device12->CreateShaderResourceView(NULL, &null_srv_desc, destination_descriptor);
}
}
{
D3D12_DESCRIPTOR_HEAP_DESC sampler_descriptor_heap_desc = {};
sampler_descriptor_heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
sampler_descriptor_heap_desc.NumDescriptors = 2000;
sampler_descriptor_heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER;
sampler_descriptor_heap_mgr = new D3DDescriptorHeapManager(&sampler_descriptor_heap_desc, device12);
gpu_descriptor_heaps[1] = sampler_descriptor_heap_mgr->GetDescriptorHeap();
}
{
D3D12_DESCRIPTOR_HEAP_DESC dsv_descriptor_heap_desc = {};
dsv_descriptor_heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
dsv_descriptor_heap_desc.NumDescriptors = 2000;
dsv_descriptor_heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_DSV;
dsv_descriptor_heap_mgr = new D3DDescriptorHeapManager(&dsv_descriptor_heap_desc, device12);
}
{
// D3D12TODO: Temporary workaround.. really need to properly suballocate out of render target heap.
D3D12_DESCRIPTOR_HEAP_DESC rtv_descriptor_heap_desc = {};
rtv_descriptor_heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
rtv_descriptor_heap_desc.NumDescriptors = 1000000;
rtv_descriptor_heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
rtv_descriptor_heap_mgr = new D3DDescriptorHeapManager(&rtv_descriptor_heap_desc, device12);
}
}
void CreateRootSignatures()
{
D3D12_DESCRIPTOR_RANGE desc_range_srv = {
D3D12_DESCRIPTOR_RANGE_TYPE_SRV, // D3D12_DESCRIPTOR_RANGE_TYPE RangeType;
8, // UINT NumDescriptors;
0, // UINT BaseShaderRegister;
0, // UINT RegisterSpace;
D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND // UINT OffsetInDescriptorsFromTableStart;
};
D3D12_DESCRIPTOR_RANGE desc_range_sampler = {
D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, // D3D12_DESCRIPTOR_RANGE_TYPE RangeType;
8, // UINT NumDescriptors;
0, // UINT BaseShaderRegister;
0, // UINT RegisterSpace;
D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND // UINT OffsetInDescriptorsFromTableStart;
};
D3D12_ROOT_PARAMETER root_parameters[6];
root_parameters[DESCRIPTOR_TABLE_PS_SRV].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
root_parameters[DESCRIPTOR_TABLE_PS_SRV].DescriptorTable.NumDescriptorRanges = 1;
root_parameters[DESCRIPTOR_TABLE_PS_SRV].DescriptorTable.pDescriptorRanges = &desc_range_srv;
root_parameters[DESCRIPTOR_TABLE_PS_SRV].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
root_parameters[DESCRIPTOR_TABLE_PS_SAMPLER].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
root_parameters[DESCRIPTOR_TABLE_PS_SAMPLER].DescriptorTable.NumDescriptorRanges = 1;
root_parameters[DESCRIPTOR_TABLE_PS_SAMPLER].DescriptorTable.pDescriptorRanges = &desc_range_sampler;
root_parameters[DESCRIPTOR_TABLE_PS_SAMPLER].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
root_parameters[DESCRIPTOR_TABLE_GS_CBV].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
root_parameters[DESCRIPTOR_TABLE_GS_CBV].Descriptor.RegisterSpace = 0;
root_parameters[DESCRIPTOR_TABLE_GS_CBV].Descriptor.ShaderRegister = 0;
root_parameters[DESCRIPTOR_TABLE_GS_CBV].ShaderVisibility = D3D12_SHADER_VISIBILITY_GEOMETRY;
root_parameters[DESCRIPTOR_TABLE_VS_CBV].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
root_parameters[DESCRIPTOR_TABLE_VS_CBV].Descriptor.RegisterSpace = 0;
root_parameters[DESCRIPTOR_TABLE_VS_CBV].Descriptor.ShaderRegister = 0;
root_parameters[DESCRIPTOR_TABLE_VS_CBV].ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX;
root_parameters[DESCRIPTOR_TABLE_PS_CBVONE].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
root_parameters[DESCRIPTOR_TABLE_PS_CBVONE].Descriptor.RegisterSpace = 0;
root_parameters[DESCRIPTOR_TABLE_PS_CBVONE].Descriptor.ShaderRegister = 0;
root_parameters[DESCRIPTOR_TABLE_PS_CBVONE].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
root_parameters[DESCRIPTOR_TABLE_PS_CBVTWO].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
root_parameters[DESCRIPTOR_TABLE_PS_CBVTWO].Descriptor.RegisterSpace = 0;
root_parameters[DESCRIPTOR_TABLE_PS_CBVTWO].Descriptor.ShaderRegister = 1;
root_parameters[DESCRIPTOR_TABLE_PS_CBVTWO].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
// D3D12TODO: Add bounding box UAV to root signature.
D3D12_ROOT_SIGNATURE_DESC root_signature_desc = {};
root_signature_desc.pParameters = root_parameters;
root_signature_desc.Flags =
D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |
D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS |
D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS;
root_signature_desc.NumParameters = ARRAYSIZE(root_parameters);
ID3DBlob* text_root_signature_blob;
ID3DBlob* text_root_signature_error_blob;
CheckHR(d3d12_serialize_root_signature(&root_signature_desc, D3D_ROOT_SIGNATURE_VERSION_1, &text_root_signature_blob, &text_root_signature_error_blob));
CheckHR(D3D::device12->CreateRootSignature(0, text_root_signature_blob->GetBufferPointer(), text_root_signature_blob->GetBufferSize(), IID_PPV_ARGS(&default_root_signature)));
}
void WaitForOutstandingRenderingToComplete()
{
command_list_mgr->ClearQueueAndWaitForCompletionOfInflightWork();
}
void Close()
{
// we can't release the swapchain while in fullscreen.
s_swap_chain->SetFullscreenState(false, nullptr);
// Release all back buffer references
for (UINT i = 0; i < ARRAYSIZE(s_backbuf); i++)
{
SAFE_RELEASE(s_backbuf[i]);
}
D3D::CleanupPersistentD3DTextureResources();
command_list_mgr->ImmediatelyDestroyAllResourcesScheduledForDestruction();
SAFE_RELEASE(s_swap_chain);
SAFE_DELETE(command_list_mgr);
command_queue->Release();
default_root_signature->Release();
SAFE_DELETE(gpu_descriptor_heap_mgr);
SAFE_DELETE(sampler_descriptor_heap_mgr);
SAFE_DELETE(rtv_descriptor_heap_mgr);
SAFE_DELETE(dsv_descriptor_heap_mgr);
ULONG remaining_references = device12->Release();
if ((!s_debug_device12 && remaining_references) || (s_debug_device12 && remaining_references > 1))
{
ERROR_LOG(VIDEO, "Unreleased D3D12 references: %i.", remaining_references);
}
else
{
NOTICE_LOG(VIDEO, "Successfully released all D3D12 device references!");
}
#if defined(_DEBUG) || defined(DEBUGFAST)
if (s_debug_device12)
{
--remaining_references; // the debug interface increases the refcount of the device, subtract that.
if (remaining_references)
{
// print out alive objects, but only if we actually have pending references
// note this will also print out internal live objects to the debug console
s_debug_device12->ReportLiveDeviceObjects(D3D12_RLDO_DETAIL);
}
SAFE_RELEASE(s_debug_device12);
}
#endif
device12 = nullptr;
current_command_list = nullptr;
// unload DLLs
UnloadDXGI();
UnloadD3DCompiler();
UnloadD3D();
}
const std::string VertexShaderVersionString()
{
return "vs_5_0";
}
const std::string GeometryShaderVersionString()
{
return "gs_5_0";
}
const std::string PixelShaderVersionString()
{
return "ps_5_0";
}
D3DTexture2D* &GetBackBuffer()
{
return s_backbuf[s_current_back_buf];
}
unsigned int GetBackBufferWidth()
{
return s_xres;
}
unsigned int GetBackBufferHeight()
{
return s_yres;
}
// Returns the maximum width/height of a texture.
unsigned int GetMaxTextureSize()
{
return D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION;
}
void Reset()
{
command_list_mgr->ExecuteQueuedWork(true);
// release all back buffer references
for (UINT i = 0; i < ARRAYSIZE(s_backbuf); i++)
{
SAFE_RELEASE(s_backbuf[i]);
}
D3D::command_list_mgr->ImmediatelyDestroyAllResourcesScheduledForDestruction();
// resize swapchain buffers
RECT client;
GetClientRect(hWnd, &client);
s_xres = client.right - client.left;
s_yres = client.bottom - client.top;
CheckHR(s_swap_chain->ResizeBuffers(SWAP_CHAIN_BUFFER_COUNT, s_xres, s_yres, DXGI_FORMAT_R8G8B8A8_UNORM, 0));
// recreate back buffer textures
HRESULT hr = S_OK;
for (UINT i = 0; i < SWAP_CHAIN_BUFFER_COUNT; i++)
{
ID3D12Resource* buf12 = nullptr;
hr = s_swap_chain->GetBuffer(i, IID_PPV_ARGS(&buf12));
CHECK(SUCCEEDED(hr), "Retrieve back buffer texture");
s_backbuf[i] = new D3DTexture2D(buf12,
D3D11_BIND_RENDER_TARGET,
DXGI_FORMAT_UNKNOWN,
DXGI_FORMAT_UNKNOWN,
DXGI_FORMAT_UNKNOWN,
false,
D3D12_RESOURCE_STATE_PRESENT
);
SAFE_RELEASE(buf12);
SetDebugObjectName12(s_backbuf[i]->GetTex12(), "backbuffer texture");
}
// The 'about-to-be-presented' back buffer index is always set back to '0' upon ResizeBuffers, just like
// creating a new swap chain.
s_current_back_buf = 0;
s_backbuf[s_current_back_buf]->TransitionToResourceState(current_command_list, D3D12_RESOURCE_STATE_RENDER_TARGET);
}
bool BeginFrame()
{
if (s_frame_in_progress)
{
PanicAlert("BeginFrame called although a frame is already in progress");
return false;
}
s_frame_in_progress = true;
return (device12 != nullptr);
}
void EndFrame()
{
if (!s_frame_in_progress)
{
PanicAlert("EndFrame called although no frame is in progress");
return;
}
s_frame_in_progress = false;
}
void Present()
{
// The Present function contains logic to ensure we never Present faster than Windows can
// send to the monitor. If we Present too fast, the Present call will start to block, and we'll be
// throttled - obviously not desired if vsync is disabled and the emulated CPU speed is > 100%.
// The throttling logic ensures that we don't Present more than twice in a given monitor vsync.
// This is accomplished through timing data - there is a programmatic way to determine if a
// Present call will block, however after investigation that is not feasible here (without invasive
// workarounds), due to the fact this method does not actually call Present - we just queue a Present
// command for the background thread to dispatch.
// The monitor refresh rate is determined in Create().
static LARGE_INTEGER s_last_present_qpc;
LARGE_INTEGER current_qpc;
QueryPerformanceCounter(&current_qpc);
const double time_elapsed_since_last_present = static_cast<double>(current_qpc.QuadPart - s_last_present_qpc.QuadPart) / s_qpc_frequency.QuadPart;
unsigned int present_flags = 0;
if (g_ActiveConfig.IsVSync() == false &&
time_elapsed_since_last_present < (1.0 / static_cast<double>(s_monitor_refresh_rate)) / 2.0
)
{
present_flags = DXGI_PRESENT_TEST; // Causes Present to be a no-op.
}
else
{
s_last_present_qpc = current_qpc;
s_backbuf[s_current_back_buf]->TransitionToResourceState(current_command_list, D3D12_RESOURCE_STATE_PRESENT);
s_current_back_buf = (s_current_back_buf + 1) % SWAP_CHAIN_BUFFER_COUNT;
}
command_list_mgr->ExecuteQueuedWorkAndPresent(s_swap_chain, g_ActiveConfig.IsVSync() ? 1 : 0, present_flags);
command_list_mgr->m_cpu_access_last_frame = command_list_mgr->m_cpu_access_this_frame;
command_list_mgr->m_cpu_access_this_frame = false;
command_list_mgr->m_draws_since_last_execution = 0;
}
HRESULT SetFullscreenState(bool enable_fullscreen)
{
return S_OK;
}
HRESULT GetFullscreenState(bool* fullscreen_state)
{
// Fullscreen exclusive intentionally not supported in DX12 backend. No performance
// difference between it and windowed full-screen due to usage of a FLIP swap chain.
*fullscreen_state = false;
return S_OK;
}
} // namespace D3D
} // namespace DX12

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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,634 @@
// Copyright hdcmeta
// Dual-Licensed under MIT and GPLv2+
// Refer to the license.txt/license_mit.txt files included.
#pragma once
#include <atomic>
#include <d3d12.h>
namespace DX12
{
static const unsigned int QUEUE_ARRAY_SIZE = 24 * 1024 * 1024;
enum D3DQueueItemType
{
AbortProcessing = 0,
SetPipelineState,
SetRenderTargets,
SetVertexBuffers,
SetIndexBuffer,
RSSetViewports,
RSSetScissorRects,
SetGraphicsRootDescriptorTable,
SetGraphicsRootConstantBufferView,
SetGraphicsRootSignature,
ClearRenderTargetView,
ClearDepthStencilView,
DrawInstanced,
DrawIndexedInstanced,
IASetPrimitiveTopology,
CopyBufferRegion,
CopyTextureRegion,
SetDescriptorHeaps,
ResourceBarrier,
ResolveSubresource,
ExecuteCommandList,
CloseCommandList,
Present,
ResetCommandList,
ResetCommandAllocator,
FenceGpuSignal,
FenceCpuSignal,
Stop
};
struct SetPipelineStateArguments
{
ID3D12PipelineState* pPipelineStateObject;
};
struct SetRenderTargetsArguments
{
D3D12_CPU_DESCRIPTOR_HANDLE RenderTargetDescriptor;
D3D12_CPU_DESCRIPTOR_HANDLE DepthStencilDescriptor;
};
struct SetVertexBuffersArguments
{
// UINT startSlot; - Dolphin only uses the 0th slot.
D3D12_VERTEX_BUFFER_VIEW desc;
// UINT numBuffers; - Only supporting single vertex buffer set since that's all Dolphin uses.
};
struct SetIndexBufferArguments
{
D3D12_INDEX_BUFFER_VIEW desc;
};
struct RSSetViewportsArguments
{
FLOAT TopLeftX;
FLOAT TopLeftY;
FLOAT Width;
FLOAT Height;
FLOAT MinDepth;
FLOAT MaxDepth;
};
struct RSSetScissorRectsArguments
{
LONG left;
LONG top;
LONG right;
LONG bottom;
};
struct SetGraphicsRootDescriptorTableArguments
{
UINT RootParameterIndex;
D3D12_GPU_DESCRIPTOR_HANDLE BaseDescriptor;
};
struct SetGraphicsRootConstantBufferViewArguments
{
UINT RootParameterIndex;
D3D12_GPU_VIRTUAL_ADDRESS BufferLocation;
};
struct SetGraphicsRootSignatureArguments
{
ID3D12RootSignature* pRootSignature;
};
struct ClearRenderTargetViewArguments
{
D3D12_CPU_DESCRIPTOR_HANDLE RenderTargetView;
};
struct ClearDepthStencilViewArguments
{
D3D12_CPU_DESCRIPTOR_HANDLE DepthStencilView;
};
struct DrawInstancedArguments
{
UINT VertexCount;
UINT StartVertexLocation;
};
struct DrawIndexedInstancedArguments
{
UINT IndexCount;
UINT StartIndexLocation;
INT BaseVertexLocation;
};
struct IASetPrimitiveTopologyArguments
{
D3D12_PRIMITIVE_TOPOLOGY PrimitiveTopology;
};
struct CopyBufferRegionArguments
{
ID3D12Resource* pDstBuffer;
UINT DstOffset;
ID3D12Resource* pSrcBuffer;
UINT SrcOffset;
UINT NumBytes;
};
struct CopyTextureRegionArguments
{
D3D12_TEXTURE_COPY_LOCATION dst;
UINT DstX;
UINT DstY;
UINT DstZ;
D3D12_TEXTURE_COPY_LOCATION src;
D3D12_BOX srcBox;
};
struct SetDescriptorHeapsArguments
{
ID3D12DescriptorHeap** ppDescriptorHeap;
UINT NumDescriptorHeaps;
};
struct ResourceBarrierArguments
{
D3D12_RESOURCE_BARRIER barrier;
};
struct ResolveSubresourceArguments
{
ID3D12Resource* pDstResource;
UINT DstSubresource;
ID3D12Resource* pSrcResource;
UINT SrcSubresource;
DXGI_FORMAT Format;
};
struct CloseCommandListArguments
{
};
struct ExecuteCommandListArguments
{
};
struct PresentArguments
{
IDXGISwapChain* swapChain;
UINT syncInterval;
UINT flags;
};
struct ResetCommandListArguments
{
ID3D12CommandAllocator* allocator;
};
struct ResetCommandAllocatorArguments
{
ID3D12CommandAllocator* allocator;
};
struct FenceGpuSignalArguments
{
ID3D12Fence* fence;
UINT64 fence_value;
};
struct FenceCpuSignalArguments
{
ID3D12Fence* fence;
UINT64 fence_value;
};
struct StopArguments
{
bool eligible_to_move_to_front_of_queue;
bool signal_stop_event;
};
struct D3DQueueItem
{
D3DQueueItemType Type;
union
{
SetPipelineStateArguments SetPipelineState;
SetRenderTargetsArguments SetRenderTargets;
SetVertexBuffersArguments SetVertexBuffers;
SetIndexBufferArguments SetIndexBuffer;
RSSetViewportsArguments RSSetViewports;
RSSetScissorRectsArguments RSSetScissorRects;
SetGraphicsRootDescriptorTableArguments SetGraphicsRootDescriptorTable;
SetGraphicsRootConstantBufferViewArguments SetGraphicsRootConstantBufferView;
SetGraphicsRootSignatureArguments SetGraphicsRootSignature;
ClearRenderTargetViewArguments ClearRenderTargetView;
ClearDepthStencilViewArguments ClearDepthStencilView;
DrawInstancedArguments DrawInstanced;
DrawIndexedInstancedArguments DrawIndexedInstanced;
IASetPrimitiveTopologyArguments IASetPrimitiveTopology;
CopyBufferRegionArguments CopyBufferRegion;
CopyTextureRegionArguments CopyTextureRegion;
SetDescriptorHeapsArguments SetDescriptorHeaps;
ResourceBarrierArguments ResourceBarrier;
ResolveSubresourceArguments ResolveSubresource;
CloseCommandListArguments CloseCommandList;
ExecuteCommandListArguments ExecuteCommandList;
PresentArguments Present;
ResetCommandListArguments ResetCommandList;
ResetCommandAllocatorArguments ResetCommandAllocator;
FenceGpuSignalArguments FenceGpuSignal;
FenceCpuSignalArguments FenceCpuSignal;
StopArguments Stop;
};
};
class ID3D12QueuedCommandList : public ID3D12GraphicsCommandList
{
public:
ID3D12QueuedCommandList(ID3D12GraphicsCommandList* backing_command_list, ID3D12CommandQueue* backing_command_queue);
void ProcessQueuedItems(bool eligible_to_move_to_front_of_queue = false, bool wait_for_stop = false);
void QueueExecute();
void QueueFenceGpuSignal(ID3D12Fence* fence_to_signal, UINT64 fence_value);
void QueueFenceCpuSignal(ID3D12Fence* fence_to_signal, UINT64 fence_value);
void QueuePresent(IDXGISwapChain* swap_chain, UINT sync_interval, UINT flags);
void ClearQueue();
// IUnknown methods
ULONG STDMETHODCALLTYPE AddRef();
ULONG STDMETHODCALLTYPE Release();
HRESULT STDMETHODCALLTYPE QueryInterface(
_In_ REFIID riid,
_COM_Outptr_ void __RPC_FAR* __RPC_FAR* ppvObject
);
// ID3D12Object methods
HRESULT STDMETHODCALLTYPE GetPrivateData(
_In_ REFGUID guid,
_Inout_ UINT* pDataSize,
_Out_writes_bytes_opt_(*pDataSize) void* pData
);
HRESULT STDMETHODCALLTYPE SetPrivateData(
_In_ REFGUID guid,
_In_ UINT DataSize,
_In_reads_bytes_opt_(DataSize) const void* pData
);
HRESULT STDMETHODCALLTYPE SetPrivateDataInterface(
_In_ REFGUID guid,
_In_opt_ const IUnknown* pData
);
HRESULT STDMETHODCALLTYPE SetName(
_In_z_ LPCWSTR pName
);
// ID3D12DeviceChild methods
D3D12_COMMAND_LIST_TYPE STDMETHODCALLTYPE GetType(
);
// ID3D12CommandList methods
HRESULT STDMETHODCALLTYPE GetDevice(
REFIID riid,
void** ppvDevice
);
HRESULT STDMETHODCALLTYPE Close(void);
HRESULT STDMETHODCALLTYPE Reset(
_In_ ID3D12CommandAllocator* pAllocator,
_In_opt_ ID3D12PipelineState* pInitialState
);
void STDMETHODCALLTYPE ClearState(
_In_ ID3D12PipelineState* pPipelineState
);
void STDMETHODCALLTYPE DrawInstanced(
_In_ UINT VertexCountPerInstance,
_In_ UINT InstanceCount,
_In_ UINT StartVertexLocation,
_In_ UINT StartInstanceLocation
);
void STDMETHODCALLTYPE DrawIndexedInstanced(
_In_ UINT IndexCountPerInstance,
_In_ UINT InstanceCount,
_In_ UINT StartIndexLocation,
_In_ INT BaseVertexLocation,
_In_ UINT StartInstanceLocation
);
void STDMETHODCALLTYPE Dispatch(
_In_ UINT ThreadGroupCountX,
_In_ UINT ThreadGroupCountY,
_In_ UINT ThreadGroupCountZ
);
void STDMETHODCALLTYPE DispatchIndirect(
_In_ ID3D12Resource* pBufferForArgs,
_In_ UINT AlignedByteOffsetForArgs
);
void STDMETHODCALLTYPE CopyBufferRegion(
_In_ ID3D12Resource* pDstBuffer,
UINT64 DstOffset,
_In_ ID3D12Resource* pSrcBuffer,
UINT64 SrcOffset,
UINT64 NumBytes
);
void STDMETHODCALLTYPE CopyTextureRegion(
_In_ const D3D12_TEXTURE_COPY_LOCATION* pDst,
UINT DstX,
UINT DstY,
UINT DstZ,
_In_ const D3D12_TEXTURE_COPY_LOCATION* pSrc,
_In_opt_ const D3D12_BOX* pSrcBox
);
void STDMETHODCALLTYPE CopyResource(
_In_ ID3D12Resource* pDstResource,
_In_ ID3D12Resource* pSrcResource
);
void STDMETHODCALLTYPE CopyTiles(
_In_ ID3D12Resource* pTiledResource,
_In_ const D3D12_TILED_RESOURCE_COORDINATE* pTileRegionStartCoordinate,
_In_ const D3D12_TILE_REGION_SIZE* pTileRegionSize,
_In_ ID3D12Resource* pBuffer,
UINT64 BufferStartOffsetInBytes,
D3D12_TILE_COPY_FLAGS Flags
);
void STDMETHODCALLTYPE ResolveSubresource(
_In_ ID3D12Resource* pDstResource,
_In_ UINT DstSubresource,
_In_ ID3D12Resource* pSrcResource,
_In_ UINT SrcSubresource,
_In_ DXGI_FORMAT Format
);
void STDMETHODCALLTYPE IASetPrimitiveTopology(
_In_ D3D12_PRIMITIVE_TOPOLOGY PrimitiveTopology
);
void STDMETHODCALLTYPE RSSetViewports(
_In_range_(0, D3D12_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE) UINT Count,
_In_reads_(Count) const D3D12_VIEWPORT* pViewports
);
void STDMETHODCALLTYPE RSSetScissorRects(
_In_range_(0, D3D12_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE) UINT Count,
_In_reads_(Count) const D3D12_RECT* pRects
);
void STDMETHODCALLTYPE OMSetBlendFactor(
_In_opt_ const FLOAT BlendFactor[4]
);
void STDMETHODCALLTYPE OMSetStencilRef(
_In_ UINT StencilRef
);
void STDMETHODCALLTYPE SetPipelineState(
_In_ ID3D12PipelineState* pPipelineState
);
void STDMETHODCALLTYPE ResourceBarrier(
_In_ UINT NumBarriers,
_In_reads_(NumBarriers) const D3D12_RESOURCE_BARRIER* pBarriers
);
void STDMETHODCALLTYPE ExecuteBundle(
_In_ ID3D12GraphicsCommandList* command_list
);
void STDMETHODCALLTYPE BeginQuery(
_In_ ID3D12QueryHeap* pQueryHeap,
_In_ D3D12_QUERY_TYPE Type,
_In_ UINT Index
);
void STDMETHODCALLTYPE EndQuery(
_In_ ID3D12QueryHeap* pQueryHeap,
_In_ D3D12_QUERY_TYPE Type,
_In_ UINT Index
);
void STDMETHODCALLTYPE ResolveQueryData(
_In_ ID3D12QueryHeap* pQueryHeap,
_In_ D3D12_QUERY_TYPE Type,
_In_ UINT StartElement,
_In_ UINT ElementCount,
_In_ ID3D12Resource* pDestinationBuffer,
_In_ UINT64 AlignedDestinationBufferOffset
);
void STDMETHODCALLTYPE SetPredication(
_In_opt_ ID3D12Resource* pBuffer,
_In_ UINT64 AlignedBufferOffset,
_In_ D3D12_PREDICATION_OP Operation
);
void STDMETHODCALLTYPE SetDescriptorHeaps(
_In_ UINT NumDescriptorHeaps,
_In_reads_(NumDescriptorHeaps) ID3D12DescriptorHeap** pDescriptorHeaps
);
void STDMETHODCALLTYPE SetComputeRootSignature(
_In_ ID3D12RootSignature* pRootSignature
);
void STDMETHODCALLTYPE SetGraphicsRootSignature(
_In_ ID3D12RootSignature* pRootSignature
);
void STDMETHODCALLTYPE SetComputeRootDescriptorTable(
_In_ UINT RootParameterIndex,
_In_ D3D12_GPU_DESCRIPTOR_HANDLE BaseDescriptor
);
void STDMETHODCALLTYPE SetGraphicsRootDescriptorTable(
_In_ UINT RootParameterIndex,
_In_ D3D12_GPU_DESCRIPTOR_HANDLE BaseDescriptor
);
void STDMETHODCALLTYPE SetComputeRoot32BitConstant(
_In_ UINT RootParameterIndex,
_In_ UINT SrcData,
_In_ UINT DestOffsetIn32BitValues
);
void STDMETHODCALLTYPE SetGraphicsRoot32BitConstant(
_In_ UINT RootParameterIndex,
_In_ UINT SrcData,
_In_ UINT DestOffsetIn32BitValues
);
void STDMETHODCALLTYPE SetComputeRoot32BitConstants(
_In_ UINT RootParameterIndex,
_In_ UINT Num32BitValuesToSet,
_In_reads_(Num32BitValuesToSet*sizeof(UINT)) const void* pSrcData,
_In_ UINT DestOffsetIn32BitValues
);
void STDMETHODCALLTYPE SetGraphicsRoot32BitConstants(
_In_ UINT RootParameterIndex,
_In_ UINT Num32BitValuesToSet,
_In_reads_(Num32BitValuesToSet*sizeof(UINT)) const void* pSrcData,
_In_ UINT DestOffsetIn32BitValues
);
void STDMETHODCALLTYPE SetGraphicsRootConstantBufferView(
_In_ UINT RootParameterIndex,
_In_ D3D12_GPU_VIRTUAL_ADDRESS BufferLocation
);
void STDMETHODCALLTYPE SetComputeRootConstantBufferView(
_In_ UINT RootParameterIndex,
_In_ D3D12_GPU_VIRTUAL_ADDRESS BufferLocation
);
void STDMETHODCALLTYPE SetComputeRootShaderResourceView(
_In_ UINT RootParameterIndex,
_In_ D3D12_GPU_VIRTUAL_ADDRESS DescriptorHandle
);
void STDMETHODCALLTYPE SetGraphicsRootShaderResourceView(
_In_ UINT RootParameterIndex,
_In_ D3D12_GPU_VIRTUAL_ADDRESS DescriptorHandle
);
void STDMETHODCALLTYPE SetComputeRootUnorderedAccessView(
_In_ UINT RootParameterIndex,
_In_ D3D12_GPU_VIRTUAL_ADDRESS DescriptorHandle
);
void STDMETHODCALLTYPE SetGraphicsRootUnorderedAccessView(
_In_ UINT RootParameterIndex,
_In_ D3D12_GPU_VIRTUAL_ADDRESS DescriptorHandle
);
void STDMETHODCALLTYPE IASetIndexBuffer(
_In_opt_ const D3D12_INDEX_BUFFER_VIEW* pDesc
);
void STDMETHODCALLTYPE IASetVertexBuffers(
_In_ UINT StartSlot,
_In_ UINT NumBuffers,
_In_ const D3D12_VERTEX_BUFFER_VIEW* pDesc
);
void STDMETHODCALLTYPE SOSetTargets(
_In_ UINT StartSlot,
_In_ UINT NumViews,
_In_ const D3D12_STREAM_OUTPUT_BUFFER_VIEW* pViews
);
void STDMETHODCALLTYPE OMSetRenderTargets(
_In_ UINT NumRenderTargetDescriptors,
_In_ const D3D12_CPU_DESCRIPTOR_HANDLE* pRenderTargetDescriptors,
_In_ BOOL RTsSingleHandleToDescriptorRange,
_In_opt_ const D3D12_CPU_DESCRIPTOR_HANDLE* pDepthStencilDescriptor
);
void STDMETHODCALLTYPE ClearDepthStencilView(
_In_ D3D12_CPU_DESCRIPTOR_HANDLE DepthStencilView,
_In_ D3D12_CLEAR_FLAGS ClearFlags,
_In_ FLOAT Depth,
_In_ UINT8 Stencil,
_In_ UINT NumRects,
_In_reads_opt_(NumRects) const D3D12_RECT* pRect
);
void STDMETHODCALLTYPE ClearRenderTargetView(
_In_ D3D12_CPU_DESCRIPTOR_HANDLE RenderTargetView,
_In_ const FLOAT ColorRGBA[4],
_In_ UINT NumRects,
_In_reads_opt_(NumRects) const D3D12_RECT* pRects
);
void STDMETHODCALLTYPE ClearUnorderedAccessViewUint(
_In_ D3D12_GPU_DESCRIPTOR_HANDLE ViewGPUHandleInCurrentHeap,
_In_ D3D12_CPU_DESCRIPTOR_HANDLE ViewCPUHandle,
_In_ ID3D12Resource* pResource,
_In_ const UINT Values[4],
_In_ UINT NumRects,
_In_reads_opt_(NumRects) const D3D12_RECT* pRects
);
void STDMETHODCALLTYPE ClearUnorderedAccessViewFloat(
_In_ D3D12_GPU_DESCRIPTOR_HANDLE ViewGPUHandleInCurrentHeap,
_In_ D3D12_CPU_DESCRIPTOR_HANDLE ViewCPUHandle,
_In_ ID3D12Resource* pResource,
_In_ const FLOAT Values[4],
_In_ UINT NumRects,
_In_reads_opt_(NumRects) const D3D12_RECT* pRects
);
void STDMETHODCALLTYPE DiscardResource(
_In_ ID3D12Resource* pResource,
_In_opt_ const D3D12_DISCARD_REGION* pRegion
);
void STDMETHODCALLTYPE SetMarker(
UINT Metadata,
_In_reads_bytes_opt_(Size) const void* pData,
UINT Size);
void STDMETHODCALLTYPE BeginEvent(
UINT Metadata,
_In_reads_bytes_opt_(Size) const void* pData,
UINT Size);
void STDMETHODCALLTYPE EndEvent(void);
void STDMETHODCALLTYPE ExecuteIndirect(
_In_ ID3D12CommandSignature* pCommandSignature,
_In_ UINT MaxCommandCount,
_In_ ID3D12Resource* pArgumentBuffer,
_In_ UINT64 ArgumentBufferOffset,
_In_opt_ ID3D12Resource* pCountBuffer,
_In_ UINT64 CountBufferOffset
);
private:
~ID3D12QueuedCommandList();
void ResetQueueOverflowTracking();
void CheckForOverflow();
static DWORD WINAPI BackgroundThreadFunction(LPVOID param);
byte m_queue_array[QUEUE_ARRAY_SIZE];
byte* m_queue_array_back = m_queue_array;
byte* m_queue_array_back_at_start_of_frame = m_queue_array_back;
DWORD m_background_thread_id;
HANDLE m_background_thread;
HANDLE m_begin_execution_event;
HANDLE m_stop_execution_event;
ID3D12GraphicsCommandList* m_command_list;
ID3D12CommandQueue* m_command_queue;
std::atomic<unsigned long> m_ref = 1;
};
} // namespace

View File

@ -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

View File

@ -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

View File

@ -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

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

View File

@ -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;
}
}

View File

@ -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;
};
}

View File

@ -0,0 +1,275 @@
// Copyright 2010 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "Common/CommonTypes.h"
#include "Common/MsgHandler.h"
#include "VideoBackends/D3D12/D3DBase.h"
#include "VideoBackends/D3D12/D3DCommandListManager.h"
#include "VideoBackends/D3D12/D3DDescriptorHeapManager.h"
#include "VideoBackends/D3D12/D3DStreamBuffer.h"
#include "VideoBackends/D3D12/D3DTexture.h"
#include "VideoBackends/D3D12/D3DUtil.h"
#include "VideoBackends/D3D12/FramebufferManager.h"
#include "VideoBackends/D3D12/Render.h"
namespace DX12
{
namespace D3D
{
static D3DStreamBuffer* s_texture_upload_stream_buffer = nullptr;
void CleanupPersistentD3DTextureResources()
{
SAFE_DELETE(s_texture_upload_stream_buffer);
}
void ReplaceRGBATexture2D(ID3D12Resource* texture12, const u8* buffer, unsigned int width, unsigned int height, unsigned int src_pitch, unsigned int level, D3D12_RESOURCE_STATES current_resource_state)
{
const unsigned int upload_size = AlignValue(src_pitch, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT) * height;
if (!s_texture_upload_stream_buffer)
{
s_texture_upload_stream_buffer = new D3DStreamBuffer(4 * 1024 * 1024, 64 * 1024 * 1024, nullptr);
}
bool current_command_list_executed = s_texture_upload_stream_buffer->AllocateSpaceInBuffer(upload_size, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT);
if (current_command_list_executed)
{
g_renderer->SetViewport();
D3D::current_command_list->OMSetRenderTargets(1, &FramebufferManager::GetEFBColorTexture()->GetRTV12(), FALSE, &FramebufferManager::GetEFBDepthTexture()->GetDSV12());
}
ResourceBarrier(current_command_list, texture12, current_resource_state, D3D12_RESOURCE_STATE_COPY_DEST, level);
D3D12_PLACED_SUBRESOURCE_FOOTPRINT upload_footprint = {};
u32 upload_rows = 0;
u64 upload_row_size_in_bytes = 0;
u64 upload_total_bytes = 0;
D3D::device12->GetCopyableFootprints(&texture12->GetDesc(), level, 1, s_texture_upload_stream_buffer->GetOffsetOfCurrentAllocation(), &upload_footprint, &upload_rows, &upload_row_size_in_bytes, &upload_total_bytes);
u8* dest_data = reinterpret_cast<u8*>(s_texture_upload_stream_buffer->GetCPUAddressOfCurrentAllocation());
const u8* src_data = reinterpret_cast<const u8*>(buffer);
for (u32 y = 0; y < upload_rows; ++y)
{
memcpy(
dest_data + upload_footprint.Footprint.RowPitch * y,
src_data + src_pitch * y,
upload_row_size_in_bytes
);
}
D3D::current_command_list->CopyTextureRegion(&CD3DX12_TEXTURE_COPY_LOCATION(texture12, level), 0, 0, 0, &CD3DX12_TEXTURE_COPY_LOCATION(s_texture_upload_stream_buffer->GetBuffer(), upload_footprint), nullptr);
ResourceBarrier(D3D::current_command_list, texture12, D3D12_RESOURCE_STATE_COPY_DEST, current_resource_state, level);
}
} // namespace
D3DTexture2D* D3DTexture2D::Create(unsigned int width, unsigned int height, D3D11_BIND_FLAG bind, D3D11_USAGE usage, DXGI_FORMAT fmt, unsigned int levels, unsigned int slices, D3D12_SUBRESOURCE_DATA* data)
{
ID3D12Resource* texture12 = nullptr;
D3D12_RESOURCE_DESC texdesc12 = CD3DX12_RESOURCE_DESC::Tex2D(
fmt,
width,
height,
slices,
levels
);
D3D12_CLEAR_VALUE optimized_clear_value = {};
optimized_clear_value.Format = fmt;
if (bind & D3D11_BIND_RENDER_TARGET)
{
texdesc12.Flags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
optimized_clear_value.Color[0] = 0.0f;
optimized_clear_value.Color[1] = 0.0f;
optimized_clear_value.Color[2] = 0.0f;
optimized_clear_value.Color[3] = 1.0f;
}
if (bind & D3D11_BIND_DEPTH_STENCIL)
{
texdesc12.Flags |= D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
optimized_clear_value.DepthStencil.Depth = 0.0f;
optimized_clear_value.DepthStencil.Stencil = 0;
}
CheckHR(
D3D::device12->CreateCommittedResource(
&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
D3D12_HEAP_FLAG_NONE,
&CD3DX12_RESOURCE_DESC(texdesc12),
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
&optimized_clear_value,
IID_PPV_ARGS(&texture12)
)
);
D3D::SetDebugObjectName12(texture12, "Texture created via D3DTexture2D::Create");
D3DTexture2D* ret = new D3DTexture2D(texture12, bind, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, false, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
if (data)
{
DX12::D3D::ReplaceRGBATexture2D(texture12, reinterpret_cast<const u8*>(data->pData), width, height, static_cast<unsigned int>(data->RowPitch), 0, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
}
SAFE_RELEASE(texture12);
return ret;
}
void D3DTexture2D::AddRef()
{
m_ref.fetch_add(1);
}
UINT D3DTexture2D::Release()
{
// fetch_sub returns the value held before the subtraction.
if (m_ref.fetch_sub(1) == 1)
{
delete this;
return 0;
}
return m_ref.load();
}
D3D12_RESOURCE_STATES D3DTexture2D::GetResourceUsageState() const
{
return m_resource_state;
}
bool D3DTexture2D::GetMultisampled() const
{
return m_multisampled;
}
ID3D12Resource* D3DTexture2D::GetTex12() const
{
return m_tex12;
}
D3D12_CPU_DESCRIPTOR_HANDLE D3DTexture2D::GetSRV12CPU() const
{
return m_srv12_cpu;
}
D3D12_GPU_DESCRIPTOR_HANDLE D3DTexture2D::GetSRV12GPU() const
{
return m_srv12_gpu;
}
D3D12_CPU_DESCRIPTOR_HANDLE D3DTexture2D::GetSRV12GPUCPUShadow() const
{
return m_srv12_gpu_cpu_shadow;
}
D3D12_CPU_DESCRIPTOR_HANDLE D3DTexture2D::GetDSV12() const
{
return m_dsv12;
}
D3D12_CPU_DESCRIPTOR_HANDLE D3DTexture2D::GetRTV12() const
{
return m_rtv12;
}
D3DTexture2D::D3DTexture2D(ID3D12Resource* texptr, D3D11_BIND_FLAG bind,
DXGI_FORMAT srv_format, DXGI_FORMAT dsv_format, DXGI_FORMAT rtv_format, bool multisampled, D3D12_RESOURCE_STATES resource_state)
: m_tex12(texptr), m_resource_state(resource_state), m_multisampled(multisampled)
{
D3D12_SRV_DIMENSION srv_dim12 = multisampled ? D3D12_SRV_DIMENSION_TEXTURE2DMSARRAY : D3D12_SRV_DIMENSION_TEXTURE2DARRAY;
D3D12_DSV_DIMENSION dsv_dim12 = multisampled ? D3D12_DSV_DIMENSION_TEXTURE2DMSARRAY : D3D12_DSV_DIMENSION_TEXTURE2DARRAY;
D3D12_RTV_DIMENSION rtv_dim12 = multisampled ? D3D12_RTV_DIMENSION_TEXTURE2DMSARRAY : D3D12_RTV_DIMENSION_TEXTURE2DARRAY;
if (bind & D3D11_BIND_SHADER_RESOURCE)
{
D3D12_SHADER_RESOURCE_VIEW_DESC srv_desc = {
srv_format, // DXGI_FORMAT Format
srv_dim12 // D3D12_SRV_DIMENSION ViewDimension
};
if (srv_dim12 == D3D12_SRV_DIMENSION_TEXTURE2DARRAY)
{
srv_desc.Texture2DArray.MipLevels = -1;
srv_desc.Texture2DArray.MostDetailedMip = 0;
srv_desc.Texture2DArray.ResourceMinLODClamp = 0;
srv_desc.Texture2DArray.ArraySize = -1;
}
else
{
srv_desc.Texture2DMSArray.ArraySize = -1;
}
srv_desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
CHECK(D3D::gpu_descriptor_heap_mgr->Allocate(&m_srv12_cpu, &m_srv12_gpu, &m_srv12_gpu_cpu_shadow), "Error: Ran out of permenant slots in GPU descriptor heap, but don't support rolling over heap.");
D3D::device12->CreateShaderResourceView(m_tex12, &srv_desc, m_srv12_cpu);
D3D::device12->CreateShaderResourceView(m_tex12, &srv_desc, m_srv12_gpu_cpu_shadow);
}
if (bind & D3D11_BIND_DEPTH_STENCIL)
{
D3D12_DEPTH_STENCIL_VIEW_DESC dsv_desc = {
dsv_format, // DXGI_FORMAT Format
dsv_dim12, // D3D12_DSV_DIMENSION
D3D12_DSV_FLAG_NONE // D3D12_DSV_FLAG Flags
};
if (dsv_dim12 == D3D12_DSV_DIMENSION_TEXTURE2DARRAY)
dsv_desc.Texture2DArray.ArraySize = -1;
else
dsv_desc.Texture2DMSArray.ArraySize = -1;
D3D::dsv_descriptor_heap_mgr->Allocate(&m_dsv12);
D3D::device12->CreateDepthStencilView(m_tex12, &dsv_desc, m_dsv12);
}
if (bind & D3D11_BIND_RENDER_TARGET)
{
D3D12_RENDER_TARGET_VIEW_DESC rtv_desc = {
rtv_format, // DXGI_FORMAT Format
rtv_dim12 // D3D12_RTV_DIMENSION ViewDimension
};
if (rtv_dim12 == D3D12_RTV_DIMENSION_TEXTURE2DARRAY)
rtv_desc.Texture2DArray.ArraySize = -1;
else
rtv_desc.Texture2DMSArray.ArraySize = -1;
D3D::rtv_descriptor_heap_mgr->Allocate(&m_rtv12);
D3D::device12->CreateRenderTargetView(m_tex12, &rtv_desc, m_rtv12);
}
m_tex12->AddRef();
}
void D3DTexture2D::TransitionToResourceState(ID3D12GraphicsCommandList* command_list, D3D12_RESOURCE_STATES state_after)
{
DX12::D3D::ResourceBarrier(command_list, m_tex12, m_resource_state, state_after, D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES);
m_resource_state = state_after;
}
D3DTexture2D::~D3DTexture2D()
{
DX12::D3D::command_list_mgr->DestroyResourceAfterCurrentCommandListExecuted(m_tex12);
if (m_srv12_cpu.ptr)
{
D3D12_SHADER_RESOURCE_VIEW_DESC null_srv_desc = {};
null_srv_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
null_srv_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
null_srv_desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
DX12::D3D::device12->CreateShaderResourceView(NULL, &null_srv_desc, m_srv12_cpu);
}
}
} // namespace DX12

View File

@ -0,0 +1,66 @@
// Copyright 2008 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <atomic>
#include <d3d11.h>
namespace DX12
{
namespace D3D
{
void ReplaceRGBATexture2D(ID3D12Resource* pTexture, const u8* buffer, unsigned int width, unsigned int height, unsigned int src_pitch, unsigned int level, D3D12_RESOURCE_STATES current_resource_state = D3D12_RESOURCE_STATE_COMMON);
void CleanupPersistentD3DTextureResources();
}
class D3DTexture2D
{
public:
// there are two ways to create a D3DTexture2D object:
// either create an ID3D12Resource object, pass it to the constructor and specify what views to create
// or let the texture automatically be created by D3DTexture2D::Create
D3DTexture2D(ID3D12Resource* texptr, D3D11_BIND_FLAG bind, DXGI_FORMAT srv_format = DXGI_FORMAT_UNKNOWN, DXGI_FORMAT dsv_format = DXGI_FORMAT_UNKNOWN, DXGI_FORMAT rtv_format = DXGI_FORMAT_UNKNOWN, bool multisampled = false, D3D12_RESOURCE_STATES resource_state = D3D12_RESOURCE_STATE_COMMON);
static D3DTexture2D* Create(unsigned int width, unsigned int height, D3D11_BIND_FLAG bind, D3D11_USAGE usage, DXGI_FORMAT, unsigned int levels = 1, unsigned int slices = 1, D3D12_SUBRESOURCE_DATA* data = nullptr);
void TransitionToResourceState(ID3D12GraphicsCommandList* command_list, D3D12_RESOURCE_STATES state_after);
// reference counting, use AddRef() when creating a new reference and Release() it when you don't need it anymore
void AddRef();
UINT Release();
ID3D12Resource* GetTex12() const;
D3D12_CPU_DESCRIPTOR_HANDLE GetSRV12CPU() const;
D3D12_GPU_DESCRIPTOR_HANDLE GetSRV12GPU() const;
D3D12_CPU_DESCRIPTOR_HANDLE GetSRV12GPUCPUShadow() const;
D3D12_CPU_DESCRIPTOR_HANDLE GetDSV12() const;
D3D12_CPU_DESCRIPTOR_HANDLE GetRTV12() const;
D3D12_RESOURCE_STATES GetResourceUsageState() const;
bool GetMultisampled() const;
private:
~D3DTexture2D();
ID3D12Resource* m_tex12 = nullptr;
D3D12_CPU_DESCRIPTOR_HANDLE m_srv12_cpu = {};
D3D12_GPU_DESCRIPTOR_HANDLE m_srv12_gpu = {};
D3D12_CPU_DESCRIPTOR_HANDLE m_srv12_gpu_cpu_shadow = {};
D3D12_CPU_DESCRIPTOR_HANDLE m_dsv12 = {};
D3D12_CPU_DESCRIPTOR_HANDLE m_rtv12 = {};
D3D12_RESOURCE_STATES m_resource_state = D3D12_RESOURCE_STATE_COMMON;
bool m_multisampled = false;
std::atomic<unsigned long> m_ref = 1;
};
} // namespace DX12

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,108 @@
// Copyright 2010 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <d3d11.h>
#include <string>
#include "Common/MathUtil.h"
#include "VideoBackends/D3D12/D3DState.h"
#include "VideoCommon/RenderBase.h"
namespace DX12
{
extern StateCache gx_state_cache;
namespace D3D
{
unsigned int AlignValue(unsigned int value, unsigned int alignment);
void ResourceBarrier(ID3D12GraphicsCommandList* command_list, ID3D12Resource* resource, D3D12_RESOURCE_STATES state_before, D3D12_RESOURCE_STATES state_after, UINT subresource);
// Font creation flags
static const unsigned int D3DFONT_BOLD = 0x0001;
static const unsigned int D3DFONT_ITALIC = 0x0002;
// Font rendering flags
static const unsigned int D3DFONT_CENTERED = 0x0001;
class CD3DFont
{
public:
CD3DFont();
// 2D text drawing function
// Initializing and destroying device-dependent objects
int Init();
int Shutdown();
int DrawTextScaled(float x, float y,
float size,
float spacing, u32 dwColor,
const std::string& text);
private:
ID3D12Resource* m_texture12 = nullptr;
D3D12_CPU_DESCRIPTOR_HANDLE m_texture12_cpu = {};
D3D12_GPU_DESCRIPTOR_HANDLE m_texture12_gpu = {};
ID3D12Resource* m_vb12 = nullptr;
D3D12_VERTEX_BUFFER_VIEW m_vb12_view = {};
void* m_vb12_data = nullptr;
unsigned int m_vb12_offset = 0;
D3D12_INPUT_LAYOUT_DESC m_input_layout12 = {};
D3D12_SHADER_BYTECODE m_pshader12 = {};
D3D12_SHADER_BYTECODE m_vshader12 = {};
D3D12_BLEND_DESC m_blendstate12 = {};
D3D12_RASTERIZER_DESC m_raststate12 = {};
ID3D12PipelineState* m_pso = nullptr;
unsigned int m_line_height = 0;
float m_tex_coords[128 - 32][4] = {};
const int m_tex_width = 512;
const int m_tex_height = 512;
};
extern CD3DFont font;
void InitUtils();
void ShutdownUtils();
void SetPointCopySampler();
void SetLinearCopySampler();
void DrawShadedTexQuad(D3DTexture2D* texture,
const D3D12_RECT* source,
int source_width,
int source_height,
D3D12_SHADER_BYTECODE pshader12 = {},
D3D12_SHADER_BYTECODE vshader12 = {},
D3D12_INPUT_LAYOUT_DESC layout12 = {},
D3D12_SHADER_BYTECODE gshader12 = {},
float gamma = 1.0f,
u32 slice = 0,
DXGI_FORMAT rt_format = DXGI_FORMAT_R8G8B8A8_UNORM,
bool inherit_srv_binding = false,
bool rt_multisampled = false,
D3D12_DEPTH_STENCIL_DESC* depth_stencil_desc_override = nullptr
);
void DrawClearQuad(u32 Color, float z, D3D12_BLEND_DESC* blend_desc, D3D12_DEPTH_STENCIL_DESC* depth_stencil_desc, bool rt_multisampled);
void DrawColorQuad(u32 Color, float z, float x1, float y1, float x2, float y2, D3D12_BLEND_DESC* blend_desc, D3D12_DEPTH_STENCIL_DESC* depth_stencil_desc, bool rt_multisampled);
void DrawEFBPokeQuads(EFBAccessType type,
const EfbPokeData* points,
size_t num_points,
D3D12_BLEND_DESC* blend_desc,
D3D12_DEPTH_STENCIL_DESC* depth_stencil_desc,
D3D12_VIEWPORT* viewport,
D3D12_CPU_DESCRIPTOR_HANDLE* render_target,
D3D12_CPU_DESCRIPTOR_HANDLE* depth_buffer,
bool rt_multisampled);
}
}

View File

@ -0,0 +1,292 @@
// Copyright 2010 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "Core/HW/Memmap.h"
#include "VideoBackends/D3D12/D3DBase.h"
#include "VideoBackends/D3D12/D3DCommandListManager.h"
#include "VideoBackends/D3D12/D3DUtil.h"
#include "VideoBackends/D3D12/FramebufferManager.h"
#include "VideoBackends/D3D12/Render.h"
#include "VideoBackends/D3D12/StaticShaderCache.h"
#include "VideoBackends/D3D12/XFBEncoder.h"
#include "VideoCommon/VideoConfig.h"
namespace DX12
{
static XFBEncoder s_xfbEncoder;
FramebufferManager::Efb FramebufferManager::m_efb;
unsigned int FramebufferManager::m_target_width;
unsigned int FramebufferManager::m_target_height;
D3D12_DEPTH_STENCIL_DESC FramebufferManager::m_depth_resolve_depth_stencil_desc;
D3DTexture2D*& FramebufferManager::GetEFBColorTexture() { return m_efb.color_tex; }
ID3D12Resource*& FramebufferManager::GetEFBColorStagingBuffer() { return m_efb.color_staging_buf; }
D3DTexture2D*& FramebufferManager::GetEFBDepthTexture() { return m_efb.depth_tex; }
D3DTexture2D*& FramebufferManager::GetEFBDepthReadTexture() { return m_efb.depth_read_texture; }
ID3D12Resource*& FramebufferManager::GetEFBDepthStagingBuffer() { return m_efb.depth_staging_buf; }
D3DTexture2D*& FramebufferManager::GetEFBColorTempTexture() { return m_efb.color_temp_tex; }
void FramebufferManager::SwapReinterpretTexture()
{
D3DTexture2D* swaptex = GetEFBColorTempTexture();
m_efb.color_temp_tex = GetEFBColorTexture();
m_efb.color_tex = swaptex;
}
D3DTexture2D*& FramebufferManager::GetResolvedEFBColorTexture()
{
if (g_ActiveConfig.iMultisamples > 1)
{
m_efb.resolved_color_tex->TransitionToResourceState(D3D::current_command_list, D3D12_RESOURCE_STATE_RESOLVE_DEST);
m_efb.color_tex->TransitionToResourceState(D3D::current_command_list, D3D12_RESOURCE_STATE_RESOLVE_SOURCE);
for (int i = 0; i < m_efb.slices; i++)
{
D3D::current_command_list->ResolveSubresource(m_efb.resolved_color_tex->GetTex12(), D3D11CalcSubresource(0, i, 1), m_efb.color_tex->GetTex12(), D3D11CalcSubresource(0, i, 1), DXGI_FORMAT_R8G8B8A8_UNORM);
}
m_efb.color_tex->TransitionToResourceState(D3D::current_command_list, D3D12_RESOURCE_STATE_RENDER_TARGET);
return m_efb.resolved_color_tex;
}
else
{
return m_efb.color_tex;
}
}
D3DTexture2D*& FramebufferManager::GetResolvedEFBDepthTexture()
{
if (g_ActiveConfig.iMultisamples > 1)
{
ResolveDepthTexture();
return m_efb.resolved_depth_tex;
}
else
{
return m_efb.depth_tex;
}
}
FramebufferManager::FramebufferManager()
{
m_target_width = std::max(Renderer::GetTargetWidth(), 1);
m_target_height = std::max(Renderer::GetTargetHeight(), 1);
DXGI_SAMPLE_DESC sample_desc;
sample_desc.Count = g_ActiveConfig.iMultisamples;
sample_desc.Quality = 0;
ID3D12Resource* buf12;
D3D12_RESOURCE_DESC texdesc12;
D3D12_CLEAR_VALUE optimized_clear_valueRTV = { DXGI_FORMAT_R8G8B8A8_UNORM, { 0.0f, 0.0f, 0.0f, 1.0f } };
D3D12_CLEAR_VALUE optimized_clear_valueDSV = CD3DX12_CLEAR_VALUE(DXGI_FORMAT_D32_FLOAT, 0.0f, 0);
HRESULT hr;
m_EFBLayers = m_efb.slices = (g_ActiveConfig.iStereoMode > 0) ? 2 : 1;
// EFB color texture - primary render target
texdesc12 = CD3DX12_RESOURCE_DESC::Tex2D(DXGI_FORMAT_R8G8B8A8_UNORM, m_target_width, m_target_height, m_efb.slices, 1, sample_desc.Count, sample_desc.Quality, D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET);
hr = D3D::device12->CreateCommittedResource(&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT), D3D12_HEAP_FLAG_NONE, &texdesc12, D3D12_RESOURCE_STATE_COMMON, &optimized_clear_valueRTV, IID_PPV_ARGS(&buf12));
m_efb.color_tex = new D3DTexture2D(buf12, (D3D11_BIND_FLAG)(D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET), DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R8G8B8A8_UNORM, (sample_desc.Count > 1), D3D12_RESOURCE_STATE_COMMON);
SAFE_RELEASE(buf12);
// Temporary EFB color texture - used in ReinterpretPixelData
texdesc12 = CD3DX12_RESOURCE_DESC::Tex2D(DXGI_FORMAT_R8G8B8A8_UNORM, m_target_width, m_target_height, m_efb.slices, 1, sample_desc.Count, sample_desc.Quality, D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET);
CheckHR(D3D::device12->CreateCommittedResource(&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT), D3D12_HEAP_FLAG_NONE, &texdesc12, D3D12_RESOURCE_STATE_COMMON, &optimized_clear_valueRTV, IID_PPV_ARGS(&buf12)));
m_efb.color_temp_tex = new D3DTexture2D(buf12, (D3D11_BIND_FLAG)(D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET), DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R8G8B8A8_UNORM, (sample_desc.Count > 1), D3D12_RESOURCE_STATE_COMMON);
SAFE_RELEASE(buf12);
D3D::SetDebugObjectName12(m_efb.color_temp_tex->GetTex12(), "EFB color temp texture");
// AccessEFB - Sysmem buffer used to retrieve the pixel data from color_tex
texdesc12 = CD3DX12_RESOURCE_DESC::Buffer(64 * 1024);
CheckHR(D3D::device12->CreateCommittedResource(&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_READBACK), D3D12_HEAP_FLAG_NONE, &texdesc12, D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS(&m_efb.color_staging_buf)));
CHECK(hr == S_OK, "create EFB color staging buffer (hr=%#x)", hr);
// EFB depth buffer - primary depth buffer
texdesc12 = CD3DX12_RESOURCE_DESC::Tex2D(DXGI_FORMAT_R32_TYPELESS, m_target_width, m_target_height, m_efb.slices, 1, sample_desc.Count, sample_desc.Quality, D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL);
CheckHR(D3D::device12->CreateCommittedResource(&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT), D3D12_HEAP_FLAG_NONE, &texdesc12, D3D12_RESOURCE_STATE_COMMON, &optimized_clear_valueDSV, IID_PPV_ARGS(&buf12)));
m_efb.depth_tex = new D3DTexture2D(buf12, (D3D11_BIND_FLAG)(D3D11_BIND_DEPTH_STENCIL | D3D11_BIND_SHADER_RESOURCE), DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_D32_FLOAT, DXGI_FORMAT_UNKNOWN, (sample_desc.Count > 1), D3D12_RESOURCE_STATE_COMMON);
SAFE_RELEASE(buf12);
D3D::SetDebugObjectName12(m_efb.depth_tex->GetTex12(), "EFB depth texture");
// Render buffer for AccessEFB (depth data)
texdesc12 = CD3DX12_RESOURCE_DESC::Tex2D(DXGI_FORMAT_R32_FLOAT, 1, 1, m_efb.slices, 1, 1, 0, D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET);
optimized_clear_valueRTV.Format = DXGI_FORMAT_R32_FLOAT;
hr = D3D::device12->CreateCommittedResource(&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT), D3D12_HEAP_FLAG_NONE, &texdesc12, D3D12_RESOURCE_STATE_COMMON, &optimized_clear_valueRTV, IID_PPV_ARGS(&buf12));
CHECK(hr == S_OK, "create EFB depth read texture (hr=%#x)", hr);
m_efb.depth_read_texture = new D3DTexture2D(buf12, D3D11_BIND_RENDER_TARGET, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, false, D3D12_RESOURCE_STATE_COMMON);
SAFE_RELEASE(buf12);
D3D::SetDebugObjectName12(m_efb.depth_read_texture->GetTex12(), "EFB depth read texture (used in Renderer::AccessEFB)");
// AccessEFB - Sysmem buffer used to retrieve the pixel data from depth_read_texture
texdesc12 = CD3DX12_RESOURCE_DESC::Buffer(64 * 1024);
hr = D3D::device12->CreateCommittedResource(&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_READBACK), D3D12_HEAP_FLAG_NONE, &texdesc12, D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS(&m_efb.depth_staging_buf));
CHECK(hr == S_OK, "create EFB depth staging buffer (hr=%#x)", hr);
D3D::SetDebugObjectName12(m_efb.depth_staging_buf, "EFB depth staging texture (used for Renderer::AccessEFB)");
if (g_ActiveConfig.iMultisamples > 1)
{
// Framebuffer resolve textures (color+depth)
texdesc12 = CD3DX12_RESOURCE_DESC::Tex2D(DXGI_FORMAT_R8G8B8A8_UNORM, m_target_width, m_target_height, m_efb.slices, 1);
hr = D3D::device12->CreateCommittedResource(&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT), D3D12_HEAP_FLAG_NONE, &texdesc12, D3D12_RESOURCE_STATE_COMMON, nullptr, IID_PPV_ARGS(&buf12));
CHECK(hr == S_OK, "create EFB color resolve texture (size: %dx%d)", m_target_width, m_target_height);
m_efb.resolved_color_tex = new D3DTexture2D(buf12, D3D11_BIND_SHADER_RESOURCE, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, false, D3D12_RESOURCE_STATE_COMMON);
SAFE_RELEASE(buf12);
D3D::SetDebugObjectName12(m_efb.resolved_color_tex->GetTex12(), "EFB color resolve texture shader resource view");
texdesc12 = CD3DX12_RESOURCE_DESC::Tex2D(DXGI_FORMAT_R32_TYPELESS, m_target_width, m_target_height, m_efb.slices, 1, 1, 0, D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL);
hr = D3D::device12->CreateCommittedResource(&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT), D3D12_HEAP_FLAG_NONE, &texdesc12, D3D12_RESOURCE_STATE_COMMON, nullptr, IID_PPV_ARGS(&buf12));
CHECK(hr == S_OK, "create EFB depth resolve texture (size: %dx%d; hr=%#x)", m_target_width, m_target_height, hr);
m_efb.resolved_depth_tex = new D3DTexture2D(buf12, (D3D11_BIND_FLAG)(D3D11_BIND_DEPTH_STENCIL | D3D11_BIND_SHADER_RESOURCE), DXGI_FORMAT_R32_TYPELESS, DXGI_FORMAT_D32_FLOAT, DXGI_FORMAT_UNKNOWN, false, D3D12_RESOURCE_STATE_COMMON);
SAFE_RELEASE(buf12);
D3D::SetDebugObjectName12(m_efb.resolved_depth_tex->GetTex12(), "EFB depth resolve texture shader resource view");
m_depth_resolve_depth_stencil_desc = {};
m_depth_resolve_depth_stencil_desc.StencilEnable = FALSE;
m_depth_resolve_depth_stencil_desc.DepthEnable = TRUE;
m_depth_resolve_depth_stencil_desc.DepthFunc = D3D12_COMPARISON_FUNC_ALWAYS;
m_depth_resolve_depth_stencil_desc.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL;
}
else
{
m_efb.resolved_color_tex = nullptr;
m_efb.resolved_depth_tex = nullptr;
}
s_xfbEncoder.Init();
}
FramebufferManager::~FramebufferManager()
{
s_xfbEncoder.Shutdown();
SAFE_RELEASE(m_efb.color_tex);
SAFE_RELEASE(m_efb.color_temp_tex);
D3D::command_list_mgr->DestroyResourceAfterCurrentCommandListExecuted(m_efb.color_staging_buf);
SAFE_RELEASE(m_efb.resolved_color_tex);
SAFE_RELEASE(m_efb.depth_tex);
D3D::command_list_mgr->DestroyResourceAfterCurrentCommandListExecuted(m_efb.depth_staging_buf);
SAFE_RELEASE(m_efb.depth_read_texture);
SAFE_RELEASE(m_efb.resolved_depth_tex);
}
void FramebufferManager::CopyToRealXFB(u32 xfbAddr, u32 fbStride, u32 fbHeight, const EFBRectangle& sourceRc, float gamma)
{
u8* dst = Memory::GetPointer(xfbAddr);
s_xfbEncoder.Encode(dst, fbStride/2, fbHeight, sourceRc, gamma);
}
std::unique_ptr<XFBSourceBase> FramebufferManager::CreateXFBSource(unsigned int target_width, unsigned int target_height, unsigned int layers)
{
return std::make_unique<XFBSource>(D3DTexture2D::Create(target_width, target_height,
(D3D11_BIND_FLAG)(D3D11_BIND_RENDER_TARGET|D3D11_BIND_SHADER_RESOURCE),
D3D11_USAGE_DEFAULT, DXGI_FORMAT_R8G8B8A8_UNORM, 1, layers), layers);
}
void FramebufferManager::GetTargetSize(unsigned int* width, unsigned int* height)
{
*width = m_target_width;
*height = m_target_height;
}
void FramebufferManager::ResolveDepthTexture()
{
// ResolveSubresource does not work with depth textures.
// Instead, we use a shader that selects the minimum depth from all samples.
const D3D12_VIEWPORT vp12 = { 0.f, 0.f, static_cast<float>(m_target_width), static_cast<float>(m_target_height), D3D12_MIN_DEPTH, D3D12_MAX_DEPTH };
D3D::current_command_list->RSSetViewports(1, &vp12);
m_efb.resolved_depth_tex->TransitionToResourceState(D3D::current_command_list, D3D12_RESOURCE_STATE_DEPTH_WRITE);
D3D::current_command_list->OMSetRenderTargets(0, nullptr, FALSE, &m_efb.resolved_depth_tex->GetDSV12());
FramebufferManager::GetEFBDepthTexture()->TransitionToResourceState(D3D::current_command_list, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
D3D::SetLinearCopySampler();
// Render a quad covering the entire target, writing SV_Depth.
const D3D12_RECT source_rect = CD3DX12_RECT(0, 0, m_target_width, m_target_height);
D3D::DrawShadedTexQuad(
FramebufferManager::GetEFBDepthTexture(),
&source_rect,
m_target_width,
m_target_height,
StaticShaderCache::GetDepthCopyPixelShader(true),
StaticShaderCache::GetSimpleVertexShader(),
StaticShaderCache::GetSimpleVertexShaderInputLayout(),
StaticShaderCache::GetCopyGeometryShader(),
1.0,
0,
DXGI_FORMAT_D32_FLOAT
);
FramebufferManager::GetEFBColorTexture()->TransitionToResourceState(D3D::current_command_list, D3D12_RESOURCE_STATE_RENDER_TARGET);
FramebufferManager::GetEFBDepthTexture()->TransitionToResourceState(D3D::current_command_list, D3D12_RESOURCE_STATE_DEPTH_WRITE);
D3D::current_command_list->OMSetRenderTargets(1, &FramebufferManager::GetEFBColorTexture()->GetRTV12(), FALSE, &FramebufferManager::GetEFBDepthTexture()->GetDSV12());
// Restores proper viewport/scissor settings.
g_renderer->RestoreAPIState();
}
void XFBSource::DecodeToTexture(u32 xfbAddr, u32 fbWidth, u32 fbHeight)
{
// DX12's XFB decoder does not use this function.
// YUYV data is decoded in Render::Swap.
}
void XFBSource::CopyEFB(float gamma)
{
// Copy EFB data to XFB and restore render target again
const D3D12_VIEWPORT vp12 = { 0.f, 0.f, static_cast<float>(texWidth), static_cast<float>(texHeight), D3D12_MIN_DEPTH, D3D12_MAX_DEPTH };
D3D::current_command_list->RSSetViewports(1, &vp12);
const D3D12_RECT rect = CD3DX12_RECT(0, 0, texWidth, texHeight);
m_tex->TransitionToResourceState(D3D::current_command_list, D3D12_RESOURCE_STATE_RENDER_TARGET);
D3D::current_command_list->OMSetRenderTargets(1, &m_tex->GetRTV12(), FALSE, nullptr);
D3D::SetPointCopySampler();
D3D::DrawShadedTexQuad(
FramebufferManager::GetEFBColorTexture(),
&rect,
Renderer::GetTargetWidth(),
Renderer::GetTargetHeight(),
StaticShaderCache::GetColorCopyPixelShader(true),
StaticShaderCache::GetSimpleVertexShader(),
StaticShaderCache::GetSimpleVertexShaderInputLayout(),
StaticShaderCache::GetCopyGeometryShader(),
gamma,
0,
DXGI_FORMAT_R8G8B8A8_UNORM,
false,
m_tex->GetMultisampled()
);
FramebufferManager::GetEFBColorTexture()->TransitionToResourceState(D3D::current_command_list, D3D12_RESOURCE_STATE_RENDER_TARGET);
FramebufferManager::GetEFBDepthTexture()->TransitionToResourceState(D3D::current_command_list, D3D12_RESOURCE_STATE_DEPTH_WRITE );
D3D::current_command_list->OMSetRenderTargets(1, &FramebufferManager::GetEFBColorTexture()->GetRTV12(), FALSE, &FramebufferManager::GetEFBDepthTexture()->GetDSV12());
// Restores proper viewport/scissor settings.
g_renderer->RestoreAPIState();
}
} // namespace DX12

View File

@ -0,0 +1,107 @@
// Copyright 2009 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include "VideoBackends/D3D12/D3DTexture.h"
#include "VideoCommon/FramebufferManagerBase.h"
namespace DX12
{
// On the GameCube, the game sends a request for the graphics processor to
// transfer its internal EFB (Embedded Framebuffer) to an area in GameCube RAM
// called the XFB (External Framebuffer). The size and location of the XFB is
// decided at the time of the copy, and the format is always YUYV. The video
// interface is given a pointer to the XFB, which will be decoded and
// displayed on the TV.
//
// There are two ways for Dolphin to emulate this:
//
// Real XFB mode:
//
// Dolphin will behave like the GameCube and encode the EFB to
// a portion of GameCube RAM. The emulated video interface will decode the data
// for output to the screen.
//
// Advantages: Behaves exactly like the GameCube.
// Disadvantages: Resolution will be limited.
//
// Virtual XFB mode:
//
// When a request is made to copy the EFB to an XFB, Dolphin
// will remember the RAM location and size of the XFB in a Virtual XFB list.
// The video interface will look up the XFB in the list and use the enhanced
// data stored there, if available.
//
// Advantages: Enables high resolution graphics, better than real hardware.
// Disadvantages: If the GameCube CPU writes directly to the XFB (which is
// possible but uncommon), the Virtual XFB will not capture this information.
// There may be multiple XFBs in GameCube RAM. This is the maximum number to
// virtualize.
struct XFBSource final : public XFBSourceBase
{
XFBSource(D3DTexture2D* tex, int slices) : m_tex(tex), m_slices(slices) {}
~XFBSource() { m_tex->Release(); }
void DecodeToTexture(u32 xfbAddr, u32 fbWidth, u32 fbHeight) override;
void CopyEFB(float gamma) override;
D3DTexture2D* m_tex;
const int m_slices;
};
class FramebufferManager final : public FramebufferManagerBase
{
public:
FramebufferManager();
~FramebufferManager();
static D3DTexture2D*& GetEFBColorTexture();
static ID3D12Resource*& GetEFBColorStagingBuffer();
static D3DTexture2D*& GetEFBDepthTexture();
static D3DTexture2D*& GetEFBDepthReadTexture();
static ID3D12Resource*& GetEFBDepthStagingBuffer();
static D3DTexture2D*& GetResolvedEFBColorTexture();
static D3DTexture2D*& GetResolvedEFBDepthTexture();
static D3DTexture2D*& GetEFBColorTempTexture();
static void SwapReinterpretTexture();
static void ResolveDepthTexture();
private:
std::unique_ptr<XFBSourceBase> CreateXFBSource(unsigned int target_width, unsigned int target_height, unsigned int layers) override;
void GetTargetSize(unsigned int* width, unsigned int* height) override;
void CopyToRealXFB(u32 xfbAddr, u32 fbStride, u32 fbHeight, const EFBRectangle& sourceRc, float gamma) override;
static struct Efb
{
D3DTexture2D* color_tex;
ID3D12Resource* color_staging_buf;
D3DTexture2D* depth_tex;
ID3D12Resource* depth_staging_buf;
D3DTexture2D* depth_read_texture;
D3DTexture2D* color_temp_tex;
D3DTexture2D* resolved_color_tex;
D3DTexture2D* resolved_depth_tex;
int slices;
} m_efb;
static unsigned int m_target_width;
static unsigned int m_target_height;
static D3D12_DEPTH_STENCIL_DESC m_depth_resolve_depth_stencil_desc;
};
} // namespace DX12

View File

@ -0,0 +1,110 @@
// Copyright 2010 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include "VideoBackends/D3D12/D3DBase.h"
#include "VideoBackends/D3D12/D3DState.h"
#include "VideoBackends/D3D12/D3DUtil.h"
#include "VideoBackends/D3D12/NativeVertexFormat.h"
#include "VideoBackends/D3D12/VertexManager.h"
namespace DX12
{
NativeVertexFormat* VertexManager::CreateNativeVertexFormat(const PortableVertexDeclaration& vtx_decl)
{
return new D3DVertexFormat(vtx_decl);
}
static const constexpr DXGI_FORMAT d3d_format_lookup[5*4*2] =
{
// float formats
DXGI_FORMAT_R8_UNORM, DXGI_FORMAT_R8_SNORM, DXGI_FORMAT_R16_UNORM, DXGI_FORMAT_R16_SNORM, DXGI_FORMAT_R32_FLOAT,
DXGI_FORMAT_R8G8_UNORM, DXGI_FORMAT_R8G8_SNORM, DXGI_FORMAT_R16G16_UNORM, DXGI_FORMAT_R16G16_SNORM, DXGI_FORMAT_R32G32_FLOAT,
DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R32G32B32_FLOAT,
DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_SNORM, DXGI_FORMAT_R16G16B16A16_UNORM, DXGI_FORMAT_R16G16B16A16_SNORM, DXGI_FORMAT_R32G32B32A32_FLOAT,
// integer formats
DXGI_FORMAT_R8_UINT, DXGI_FORMAT_R8_SINT, DXGI_FORMAT_R16_UINT, DXGI_FORMAT_R16_SINT, DXGI_FORMAT_UNKNOWN,
DXGI_FORMAT_R8G8_UINT, DXGI_FORMAT_R8G8_SINT, DXGI_FORMAT_R16G16_UINT, DXGI_FORMAT_R16G16_SINT, DXGI_FORMAT_UNKNOWN,
DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN,
DXGI_FORMAT_R8G8B8A8_UINT, DXGI_FORMAT_R8G8B8A8_SINT, DXGI_FORMAT_R16G16B16A16_UINT, DXGI_FORMAT_R16G16B16A16_SINT, DXGI_FORMAT_UNKNOWN,
};
DXGI_FORMAT VarToD3D(VarType t, int size, bool integer)
{
DXGI_FORMAT retval = d3d_format_lookup[static_cast<int>(t) + 5 * (size-1) + 5 * 4 * static_cast<int>(integer)];
if (retval == DXGI_FORMAT_UNKNOWN)
{
PanicAlert("VarToD3D: Invalid type/size combo %i , %i, %i", static_cast<int>(t), size, static_cast<int>(integer));
}
return retval;
}
D3DVertexFormat::D3DVertexFormat(const PortableVertexDeclaration &vtx_decl)
: m_num_elems(0), m_layout12({}), m_elems()
{
this->vtx_decl = vtx_decl;
AddInputElementDescFromAttributeFormatIfValid(&vtx_decl.position, "POSITION", 0);
for (int i = 0; i < 3; i++)
{
AddInputElementDescFromAttributeFormatIfValid(&vtx_decl.normals[i], "NORMAL", i);
}
for (int i = 0; i < 2; i++)
{
AddInputElementDescFromAttributeFormatIfValid(&vtx_decl.colors[i], "COLOR", i);
}
for (int i = 0; i < 8; i++)
{
AddInputElementDescFromAttributeFormatIfValid(&vtx_decl.texcoords[i], "TEXCOORD", i);
}
AddInputElementDescFromAttributeFormatIfValid(&vtx_decl.posmtx, "BLENDINDICES", 0);
m_layout12.NumElements = m_num_elems;
m_layout12.pInputElementDescs = m_elems.data();
}
D3DVertexFormat::~D3DVertexFormat()
{
}
void D3DVertexFormat::AddInputElementDescFromAttributeFormatIfValid(const AttributeFormat* format, const char* semantic_name, unsigned int semantic_index)
{
if (!format->enable)
{
return;
}
D3D12_INPUT_ELEMENT_DESC desc = {};
desc.AlignedByteOffset = format->offset;
desc.Format = VarToD3D(format->type, format->components, format->integer);
desc.InputSlot = 0;
desc.InputSlotClass = D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA;
desc.SemanticName = semantic_name;
desc.SemanticIndex = semantic_index;
m_elems[m_num_elems] = desc;
++m_num_elems;
}
void D3DVertexFormat::SetupVertexPointers()
{
// No-op on DX12.
}
D3D12_INPUT_LAYOUT_DESC D3DVertexFormat::GetActiveInputLayout12() const
{
return m_layout12;
}
} // namespace DX12

View File

@ -0,0 +1,31 @@
// Copyright 2015 Dolphin Emulator Project
// Licensed under GPLv2+
#pragma once
#include <array>
#include <d3d12.h>
#include "VideoCommon/NativeVertexFormat.h"
namespace DX12
{
class D3DVertexFormat final : public NativeVertexFormat
{
std::array<D3D12_INPUT_ELEMENT_DESC, 15> m_elems;
UINT m_num_elems;
D3D12_INPUT_LAYOUT_DESC m_layout12;
public:
D3DVertexFormat(const PortableVertexDeclaration& vtx_decl);
~D3DVertexFormat();
void SetupVertexPointers();
D3D12_INPUT_LAYOUT_DESC GetActiveInputLayout12() const;
private:
void AddInputElementDescFromAttributeFormatIfValid(const AttributeFormat* format, const char* semantic_name, unsigned int semantic_index);
};
}

View File

@ -0,0 +1,299 @@
// Copyright 2011 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "Core/HW/Memmap.h"
#include "VideoBackends/D3D12/D3DBase.h"
#include "VideoBackends/D3D12/D3DCommandListManager.h"
#include "VideoBackends/D3D12/D3DDescriptorHeapManager.h"
#include "VideoBackends/D3D12/D3DShader.h"
#include "VideoBackends/D3D12/D3DState.h"
#include "VideoBackends/D3D12/D3DUtil.h"
#include "VideoBackends/D3D12/FramebufferManager.h"
#include "VideoBackends/D3D12/PSTextureEncoder.h"
#include "VideoBackends/D3D12/Render.h"
#include "VideoBackends/D3D12/StaticShaderCache.h"
#include "VideoBackends/D3D12/TextureCache.h"
#include "VideoCommon/TextureConversionShader.h"
namespace DX12
{
struct EFBEncodeParams
{
DWORD SrcLeft;
DWORD SrcTop;
DWORD DestWidth;
DWORD ScaleFactor;
};
PSTextureEncoder::PSTextureEncoder()
{
}
void PSTextureEncoder::Init()
{
// Create output texture RGBA format
D3D12_RESOURCE_DESC out_tex_desc = CD3DX12_RESOURCE_DESC::Tex2D(
DXGI_FORMAT_B8G8R8A8_UNORM,
EFB_WIDTH * 4,
EFB_HEIGHT / 4,
1,
0,
1,
0,
D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET
);
D3D12_CLEAR_VALUE optimized_clear_value = { DXGI_FORMAT_B8G8R8A8_UNORM, { 0.0f, 0.0f, 0.0f, 1.0f } };
CheckHR(
D3D::device12->CreateCommittedResource(
&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
D3D12_HEAP_FLAG_NONE,
&out_tex_desc,
D3D12_RESOURCE_STATE_COPY_SOURCE,
&optimized_clear_value,
IID_PPV_ARGS(&m_out)
)
);
D3D::SetDebugObjectName12(m_out, "efb encoder output texture");
// Create output render target view
D3D12_RENDER_TARGET_VIEW_DESC tex_rtv_desc = {
DXGI_FORMAT_B8G8R8A8_UNORM, // DXGI_FORMAT Format;
D3D12_RTV_DIMENSION_TEXTURE2D // D3D12_RTV_DIMENSION ViewDimension;
};
tex_rtv_desc.Texture2D.MipSlice = 0;
D3D::rtv_descriptor_heap_mgr->Allocate(&m_out_rtv_cpu);
D3D::device12->CreateRenderTargetView(m_out, &tex_rtv_desc, m_out_rtv_cpu);
// Create output staging buffer
CheckHR(
D3D::device12->CreateCommittedResource(
&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_READBACK),
D3D12_HEAP_FLAG_NONE,
&CD3DX12_RESOURCE_DESC::Buffer(
D3D::AlignValue(static_cast<unsigned int>(out_tex_desc.Width) * 4, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT) *
out_tex_desc.Height
),
D3D12_RESOURCE_STATE_COPY_DEST,
nullptr,
IID_PPV_ARGS(&m_out_readback_buffer)
)
);
D3D::SetDebugObjectName12(m_out_readback_buffer, "efb encoder output staging buffer");
CheckHR(m_out_readback_buffer->Map(0, nullptr, &m_out_readback_buffer_data));
// Create constant buffer for uploading data to shaders. Need to align to 256 bytes.
unsigned int encode_params_buffer_size = (sizeof(EFBEncodeParams) + 0xff) & ~0xff;
CheckHR(
D3D::device12->CreateCommittedResource(
&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD),
D3D12_HEAP_FLAG_NONE,
&CD3DX12_RESOURCE_DESC::Buffer(encode_params_buffer_size),
D3D12_RESOURCE_STATE_GENERIC_READ,
nullptr,
IID_PPV_ARGS(&m_encode_params_buffer)
)
);
D3D::SetDebugObjectName12(m_encode_params_buffer, "efb encoder params buffer");
CheckHR(m_encode_params_buffer->Map(0, nullptr, &m_encode_params_buffer_data));
m_ready = true;
}
void PSTextureEncoder::Shutdown()
{
m_ready = false;
D3D::command_list_mgr->DestroyResourceAfterCurrentCommandListExecuted(m_out);
D3D::command_list_mgr->DestroyResourceAfterCurrentCommandListExecuted(m_out_readback_buffer);
D3D::command_list_mgr->DestroyResourceAfterCurrentCommandListExecuted(m_encode_params_buffer);
for (auto& it : m_static_shaders_blobs)
{
SAFE_RELEASE(it);
}
m_static_shaders_blobs.clear();
m_static_shaders_map.clear();
}
void PSTextureEncoder::Encode(u8* dst, u32 format, u32 native_width, u32 bytes_per_row, u32 num_blocks_y, u32 memory_stride,
PEControl::PixelFormat src_format, const EFBRectangle& src_rect,
bool is_intensity, bool scale_by_half)
{
if (!m_ready) // Make sure we initialized OK
return;
D3D::command_list_mgr->CPUAccessNotify();
// Resolve MSAA targets before copying.
D3DTexture2D* efb_source = (src_format == PEControl::Z24) ?
FramebufferManager::GetResolvedEFBDepthTexture() :
// EXISTINGD3D11TODO: Instead of resolving EFB, it would be better to pick out a
// single sample from each pixel. The game may break if it isn't
// expecting the blurred edges around multisampled shapes.
FramebufferManager::GetResolvedEFBColorTexture();
// GetResolvedEFBDepthTexture will set the render targets, when MSAA is enabled
// (since it needs to do a manual depth resolve). So make sure to set the RTs
// afterwards.
const u32 words_per_row = bytes_per_row / sizeof(u32);
D3D12_VIEWPORT vp = { 0.f, 0.f, FLOAT(words_per_row), FLOAT(num_blocks_y), D3D12_MIN_DEPTH, D3D12_MAX_DEPTH };
D3D::current_command_list->RSSetViewports(1, &vp);
constexpr EFBRectangle full_src_rect(0, 0, EFB_WIDTH, EFB_HEIGHT);
TargetRectangle target_rect = g_renderer->ConvertEFBRectangle(full_src_rect);
D3D::ResourceBarrier(D3D::current_command_list, m_out, D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET, 0);
D3D::current_command_list->OMSetRenderTargets(1, &m_out_rtv_cpu, FALSE, nullptr);
EFBEncodeParams params;
params.SrcLeft = src_rect.left;
params.SrcTop = src_rect.top;
params.DestWidth = native_width;
params.ScaleFactor = scale_by_half ? 2 : 1;
memcpy(m_encode_params_buffer_data, &params, sizeof(params));
D3D::current_command_list->SetGraphicsRootConstantBufferView(
DESCRIPTOR_TABLE_PS_CBVONE,
m_encode_params_buffer->GetGPUVirtualAddress()
);
D3D::command_list_mgr->SetCommandListDirtyState(COMMAND_LIST_STATE_PS_CBV, true);
// Use linear filtering if (bScaleByHalf), use point filtering otherwise
if (scale_by_half)
D3D::SetLinearCopySampler();
else
D3D::SetPointCopySampler();
D3D::DrawShadedTexQuad(efb_source,
target_rect.AsRECT(),
Renderer::GetTargetWidth(),
Renderer::GetTargetHeight(),
SetStaticShader(format, src_format, is_intensity, scale_by_half),
StaticShaderCache::GetSimpleVertexShader(),
StaticShaderCache::GetSimpleVertexShaderInputLayout(),
D3D12_SHADER_BYTECODE(),
1.0f,
0,
DXGI_FORMAT_B8G8R8A8_UNORM,
false,
false /* Render target is not multisampled */
);
// Copy to staging buffer
D3D12_BOX src_box = CD3DX12_BOX(0, 0, 0, words_per_row, num_blocks_y, 1);
D3D12_TEXTURE_COPY_LOCATION dst_location = {};
dst_location.pResource = m_out_readback_buffer;
dst_location.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
dst_location.PlacedFootprint.Offset = 0;
dst_location.PlacedFootprint.Footprint.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
dst_location.PlacedFootprint.Footprint.Width = EFB_WIDTH * 4;
dst_location.PlacedFootprint.Footprint.Height = EFB_HEIGHT / 4;
dst_location.PlacedFootprint.Footprint.Depth = 1;
dst_location.PlacedFootprint.Footprint.RowPitch = D3D::AlignValue(dst_location.PlacedFootprint.Footprint.Width * 4, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
D3D12_TEXTURE_COPY_LOCATION src_location = {};
src_location.pResource = m_out;
src_location.SubresourceIndex = 0;
src_location.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
D3D::ResourceBarrier(D3D::current_command_list, m_out, D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE, 0);
D3D::current_command_list->CopyTextureRegion(&dst_location, 0, 0, 0, &src_location, &src_box);
D3D::command_list_mgr->ExecuteQueuedWork(true);
// Transfer staging buffer to GameCube/Wii RAM
u8* src = static_cast<u8*>(m_out_readback_buffer_data);
u32 read_stride = std::min(bytes_per_row, dst_location.PlacedFootprint.Footprint.RowPitch);
for (unsigned int y = 0; y < num_blocks_y; ++y)
{
memcpy(dst, src, read_stride);
dst += memory_stride;
src += dst_location.PlacedFootprint.Footprint.RowPitch;
}
// Restores proper viewport/scissor settings.
g_renderer->RestoreAPIState();
FramebufferManager::GetEFBColorTexture()->TransitionToResourceState(D3D::current_command_list, D3D12_RESOURCE_STATE_RENDER_TARGET);
FramebufferManager::GetEFBDepthTexture()->TransitionToResourceState(D3D::current_command_list, D3D12_RESOURCE_STATE_DEPTH_WRITE );
D3D::current_command_list->OMSetRenderTargets(1, &FramebufferManager::GetEFBColorTexture()->GetRTV12(), FALSE, &FramebufferManager::GetEFBDepthTexture()->GetDSV12());
}
D3D12_SHADER_BYTECODE PSTextureEncoder::SetStaticShader(unsigned int dst_format, PEControl::PixelFormat src_format,
bool is_intensity, bool scale_by_half)
{
size_t fetch_num = static_cast<size_t>(src_format);
size_t scaled_fetch_num = scale_by_half ? 1 : 0;
size_t intensity_num = is_intensity ? 1 : 0;
size_t generator_num = dst_format;
ComboKey key = MakeComboKey(dst_format, src_format, is_intensity, scale_by_half);
ComboMap::iterator it = m_static_shaders_map.find(key);
if (it == m_static_shaders_map.end())
{
INFO_LOG(VIDEO, "Compiling efb encoding shader for dst_format 0x%X, src_format %d, is_intensity %d, scale_by_half %d",
dst_format, static_cast<int>(src_format), is_intensity ? 1 : 0, scale_by_half ? 1 : 0);
u32 format = dst_format;
if (src_format == PEControl::Z24)
{
format |= _GX_TF_ZTF;
if (dst_format == 11)
format = GX_TF_Z16;
else if (format < GX_TF_Z8 || format > GX_TF_Z24X8)
format |= _GX_TF_CTF;
}
else
{
if (dst_format > GX_TF_RGBA8 || (dst_format < GX_TF_RGB565 && !is_intensity))
format |= _GX_TF_CTF;
}
ID3DBlob* bytecode = nullptr;
const char* shader = TextureConversionShader::GenerateEncodingShader(format, API_D3D);
if (!D3D::CompilePixelShader(shader, &bytecode))
{
WARN_LOG(VIDEO, "EFB encoder shader for dst_format 0x%X, src_format %d, is_intensity %d, scale_by_half %d failed to compile",
dst_format, static_cast<int>(src_format), is_intensity ? 1 : 0, scale_by_half ? 1 : 0);
m_static_shaders_blobs[key] = {};
return {};
}
D3D12_SHADER_BYTECODE new_shader = {
bytecode->GetBufferPointer(),
bytecode->GetBufferSize()
};
it = m_static_shaders_map.emplace(key, new_shader).first;
// Keep track of the ID3DBlobs, so we can free them upon shutdown.
m_static_shaders_blobs.push_back(bytecode);
}
return it->second;
}
}

View File

@ -0,0 +1,53 @@
// Copyright 2011 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include "VideoBackends/D3D12/TextureEncoder.h"
#include "VideoCommon/TextureCacheBase.h"
namespace DX12
{
class PSTextureEncoder final : public TextureEncoder
{
public:
PSTextureEncoder();
void Init();
void Shutdown();
void Encode(u8* dst, u32 format, u32 native_width, u32 bytes_per_row, u32 num_blocks_y, u32 memory_stride,
PEControl::PixelFormat src_format, const EFBRectangle& src_rect,
bool is_intensity, bool scale_by_half);
private:
bool m_ready = false;
ID3D12Resource* m_out = nullptr;
D3D12_CPU_DESCRIPTOR_HANDLE m_out_rtv_cpu = {};
ID3D12Resource* m_out_readback_buffer = nullptr;
void* m_out_readback_buffer_data = nullptr;
ID3D12Resource* m_encode_params_buffer = nullptr;
void* m_encode_params_buffer_data = nullptr;
D3D12_SHADER_BYTECODE SetStaticShader(unsigned int dst_format,
PEControl::PixelFormat src_format, bool is_intensity, bool scale_by_half);
using ComboKey = unsigned int; // Key for a shader combination
static ComboKey MakeComboKey(unsigned int dst_format,
PEControl::PixelFormat src_format, bool is_intensity, bool scale_by_half)
{
return (dst_format << 4) | (static_cast<int>(src_format) << 2) | (is_intensity ? (1 << 1) : 0)
| (scale_by_half ? (1 << 0) : 0);
}
using ComboMap = std::map<ComboKey, D3D12_SHADER_BYTECODE>;
ComboMap m_static_shaders_map;
std::vector<ID3DBlob*> m_static_shaders_blobs;
};
}

View File

@ -0,0 +1,69 @@
// Copyright 2012 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "Common/CommonFuncs.h"
#include "Common/CommonTypes.h"
#include "Common/Logging/Log.h"
#include "VideoBackends/D3D12/D3DBase.h"
#include "VideoBackends/D3D12/PerfQuery.h"
#include "VideoCommon/RenderBase.h"
//D3D12TODO: Implement PerfQuery class.
namespace DX12
{
PerfQuery::PerfQuery()
{
//D3D12TODO: Add implementation
}
PerfQuery::~PerfQuery()
{
//D3D12TODO: Add implementation
}
void PerfQuery::EnableQuery(PerfQueryGroup type)
{
//D3D12TODO: Add implementation
}
void PerfQuery::DisableQuery(PerfQueryGroup type)
{
//D3D12TODO: Add implementation
}
void PerfQuery::ResetQuery()
{
//D3D12TODO: Add implementation
}
u32 PerfQuery::GetQueryResult(PerfQueryType type)
{
//D3D12TODO: Add implementation
return 0;
}
void PerfQuery::FlushOne()
{
//D3D12TODO: Add implementation
}
void PerfQuery::FlushResults()
{
//D3D12TODO: Add implementation
}
void PerfQuery::WeakFlush()
{
//D3D12TODO: Add implementation
}
bool PerfQuery::IsFlushed() const
{
//D3D12TODO: Add implementation
return true;
}
} // namespace

View File

@ -0,0 +1,46 @@
// Copyright 2012 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <array>
#include "VideoCommon/PerfQueryBase.h"
namespace DX12
{
class PerfQuery final : public PerfQueryBase
{
public:
PerfQuery();
~PerfQuery();
void EnableQuery(PerfQueryGroup type) override;
void DisableQuery(PerfQueryGroup type) override;
void ResetQuery() override;
u32 GetQueryResult(PerfQueryType type) override;
void FlushResults() override;
bool IsFlushed() const override;
private:
struct ActiveQuery
{
ID3D11Query* query;
PerfQueryGroup query_type;
};
void WeakFlush();
// Only use when non-empty
void FlushOne();
// when testing in SMS: 64 was too small, 128 was ok
static const int s_perf_query_buffer_size = 512;
std::array<ActiveQuery, s_perf_query_buffer_size> m_query_buffer;
int m_query_read_pos = 0;
};
} // namespace

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,70 @@
// Copyright 2010 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <string>
#include "VideoCommon/RenderBase.h"
namespace DX12
{
class Renderer final : public ::Renderer
{
public:
Renderer(void*& window_handle);
~Renderer();
void SetColorMask() override;
void SetBlendMode(bool force_update) override;
void SetScissorRect(const EFBRectangle& rc) override;
void SetGenerationMode() override;
void SetDepthMode() override;
void SetLogicOpMode() override;
void SetDitherMode() override;
void SetSamplerState(int stage, int tex_index, bool custom_tex) override;
void SetInterlacingMode() override;
void SetViewport() override;
// TODO: Fix confusing names (see ResetAPIState and RestoreAPIState)
void ApplyState(bool use_dst_alpha) override;
void RestoreState() override;
void ApplyCullDisable();
void RestoreCull();
void RenderText(const std::string& text, int left, int top, u32 color) override;
u32 AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data) override;
void PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num_points) override;
u16 BBoxRead(int index) override;
void BBoxWrite(int index, u16 value) override;
void ResetAPIState() override;
void RestoreAPIState() override;
TargetRectangle ConvertEFBRectangle(const EFBRectangle& rc) override;
void SwapImpl(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, const EFBRectangle& rc, float gamma) override;
void ClearScreen(const EFBRectangle& rc, bool color_enable, bool alpha_enable, bool z_enable, u32 color, u32 z) override;
void ReinterpretPixelData(unsigned int conv_type) override;
bool SaveScreenshot(const std::string& filename, const TargetRectangle& rc) override;
static bool CheckForResize();
int GetMaxTextureSize() override;
static D3D12_BLEND_DESC GetResetBlendDesc();
static D3D12_DEPTH_STENCIL_DESC GetResetDepthStencilDesc();
static D3D12_RASTERIZER_DESC GetResetRasterizerDesc();
private:
void BlitScreen(TargetRectangle src, TargetRectangle dst, D3DTexture2D* src_texture, u32 src_width, u32 src_height, float gamma);
};
}

View File

@ -0,0 +1,388 @@
// Copyright 2015 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "Common/LinearDiskCache.h"
#include "Core/ConfigManager.h"
#include "VideoBackends/D3D12/D3DCommandListManager.h"
#include "VideoBackends/D3D12/D3DShader.h"
#include "VideoBackends/D3D12/ShaderCache.h"
#include "VideoCommon/Debugger.h"
#include "VideoCommon/Statistics.h"
namespace DX12
{
// Primitive topology type is always triangle, unless the GS stage is used. This is consumed
// by the PSO created in Renderer::ApplyState.
static D3D12_PRIMITIVE_TOPOLOGY_TYPE s_current_primitive_topology = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
using GsBytecodeCache = std::map<GeometryShaderUid, D3D12_SHADER_BYTECODE>;
using PsBytecodeCache = std::map<PixelShaderUid, D3D12_SHADER_BYTECODE>;
using VsBytecodeCache = std::map<VertexShaderUid, D3D12_SHADER_BYTECODE>;
GsBytecodeCache s_gs_bytecode_cache;
PsBytecodeCache s_ps_bytecode_cache;
VsBytecodeCache s_vs_bytecode_cache;
// Used to keep track of blobs to release at Shutdown time.
static std::vector<ID3DBlob*> s_shader_blob_list;
// Only used for shader debugging..
using GsHlslCache = std::map<GeometryShaderUid, std::string>;
using PsHlslCache = std::map<PixelShaderUid, std::string>;
using VsHlslCache = std::map<VertexShaderUid, std::string>;
static GsHlslCache s_gs_hlsl_cache;
static PsHlslCache s_ps_hlsl_cache;
static VsHlslCache s_vs_hlsl_cache;
static LinearDiskCache<GeometryShaderUid, u8> s_gs_disk_cache;
static LinearDiskCache<PixelShaderUid, u8> s_ps_disk_cache;
static LinearDiskCache<VertexShaderUid, u8> s_vs_disk_cache;
static UidChecker<GeometryShaderUid, ShaderCode> s_geometry_uid_checker;
static UidChecker<PixelShaderUid, ShaderCode> s_pixel_uid_checker;
static UidChecker<VertexShaderUid, ShaderCode> s_vertex_uid_checker;
static D3D12_SHADER_BYTECODE s_last_geometry_shader_bytecode;
static D3D12_SHADER_BYTECODE s_last_pixel_shader_bytecode;
static D3D12_SHADER_BYTECODE s_last_vertex_shader_bytecode;
static GeometryShaderUid s_last_geometry_shader_uid;
static PixelShaderUid s_last_pixel_shader_uid;
static VertexShaderUid s_last_vertex_shader_uid;
template<class UidType, class ShaderCacheType, ShaderCacheType* cache>
class ShaderCacheInserter final : public LinearDiskCacheReader<UidType, u8>
{
public:
void Read(const UidType &key, const u8* value, u32 value_size)
{
ID3DBlob* blob = nullptr;
CheckHR(d3d_create_blob(value_size, &blob));
memcpy(blob->GetBufferPointer(), value, value_size);
ShaderCache::InsertByteCode<UidType, ShaderCacheType>(key, cache, blob);
}
};
void ShaderCache::Init()
{
// This class intentionally shares its shader cache files with DX11, as the shaders are (right now) identical.
// Reduces unnecessary compilation when switching between APIs.
s_last_geometry_shader_bytecode = {};
s_last_pixel_shader_bytecode = {};
s_last_vertex_shader_bytecode = {};
s_last_geometry_shader_uid = {};
s_last_pixel_shader_uid = {};
s_last_vertex_shader_uid = {};
// Ensure shader cache directory exists..
std::string shader_cache_path = File::GetUserPath(D_SHADERCACHE_IDX);
if (!File::Exists(shader_cache_path))
File::CreateDir(File::GetUserPath(D_SHADERCACHE_IDX));
std::string title_unique_id = SConfig::GetInstance().m_strUniqueID.c_str();
std::string gs_cache_filename = StringFromFormat("%sdx11-%s-gs.cache", shader_cache_path.c_str(), title_unique_id.c_str());
std::string ps_cache_filename = StringFromFormat("%sdx11-%s-ps.cache", shader_cache_path.c_str(), title_unique_id.c_str());
std::string vs_cache_filename = StringFromFormat("%sdx11-%s-vs.cache", shader_cache_path.c_str(), title_unique_id.c_str());
ShaderCacheInserter<GeometryShaderUid, GsBytecodeCache, &s_gs_bytecode_cache> gs_inserter;
s_gs_disk_cache.OpenAndRead(gs_cache_filename, gs_inserter);
ShaderCacheInserter<PixelShaderUid, PsBytecodeCache, &s_ps_bytecode_cache> ps_inserter;
s_ps_disk_cache.OpenAndRead(ps_cache_filename, ps_inserter);
ShaderCacheInserter<VertexShaderUid, VsBytecodeCache, &s_vs_bytecode_cache> vs_inserter;
s_vs_disk_cache.OpenAndRead(vs_cache_filename, vs_inserter);
// Clear out cache when debugging shaders to ensure stale ones don't stick around..
if (g_Config.bEnableShaderDebugging)
Clear();
SETSTAT(stats.numPixelShadersAlive, static_cast<int>(s_ps_bytecode_cache.size()));
SETSTAT(stats.numPixelShadersCreated, static_cast<int>(s_ps_bytecode_cache.size()));
SETSTAT(stats.numVertexShadersAlive, static_cast<int>(s_vs_bytecode_cache.size()));
SETSTAT(stats.numVertexShadersCreated, static_cast<int>(s_vs_bytecode_cache.size()));
}
void ShaderCache::Clear()
{
for (auto& iter : s_shader_blob_list)
SAFE_RELEASE(iter);
s_shader_blob_list.clear();
s_gs_bytecode_cache.clear();
s_ps_bytecode_cache.clear();
s_vs_bytecode_cache.clear();
s_last_geometry_shader_bytecode = {};
s_last_geometry_shader_uid = {};
s_last_pixel_shader_bytecode = {};
s_last_pixel_shader_uid = {};
s_last_vertex_shader_bytecode = {};
s_last_vertex_shader_uid = {};
}
void ShaderCache::Shutdown()
{
Clear();
s_gs_disk_cache.Sync();
s_gs_disk_cache.Close();
s_ps_disk_cache.Sync();
s_ps_disk_cache.Close();
s_vs_disk_cache.Sync();
s_vs_disk_cache.Close();
if (g_Config.bEnableShaderDebugging)
{
s_gs_hlsl_cache.clear();
s_ps_hlsl_cache.clear();
s_vs_hlsl_cache.clear();
}
s_geometry_uid_checker.Invalidate();
s_pixel_uid_checker.Invalidate();
s_vertex_uid_checker.Invalidate();
}
void ShaderCache::LoadAndSetActiveShaders(DSTALPHA_MODE ps_dst_alpha_mode, u32 gs_primitive_type)
{
SetCurrentPrimitiveTopology(gs_primitive_type);
GeometryShaderUid gs_uid = GetGeometryShaderUid(gs_primitive_type, API_D3D);
PixelShaderUid ps_uid = GetPixelShaderUid(ps_dst_alpha_mode, API_D3D);
VertexShaderUid vs_uid = GetVertexShaderUid(API_D3D);
bool gs_changed = gs_uid != s_last_geometry_shader_uid;
bool ps_changed = ps_uid != s_last_pixel_shader_uid;
bool vs_changed = vs_uid != s_last_vertex_shader_uid;
if (!gs_changed && !ps_changed && !vs_changed)
{
return;
}
if (gs_changed)
{
HandleGSUIDChange(gs_uid, gs_primitive_type);
}
if (ps_changed)
{
HandlePSUIDChange(ps_uid, ps_dst_alpha_mode);
}
if (vs_changed)
{
HandleVSUIDChange(vs_uid);
}
// A Uid has changed, so the PSO will need to be reset at next ApplyState.
D3D::command_list_mgr->SetCommandListDirtyState(COMMAND_LIST_STATE_PSO, true);
}
void ShaderCache::SetCurrentPrimitiveTopology(u32 gs_primitive_type)
{
switch (gs_primitive_type)
{
case PRIMITIVE_TRIANGLES:
s_current_primitive_topology = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
break;
case PRIMITIVE_LINES:
s_current_primitive_topology = D3D12_PRIMITIVE_TOPOLOGY_TYPE_LINE;
break;
case PRIMITIVE_POINTS:
s_current_primitive_topology = D3D12_PRIMITIVE_TOPOLOGY_TYPE_POINT;
break;
default:
CHECK(0, "Invalid primitive type.");
break;
}
}
void ShaderCache::HandleGSUIDChange(GeometryShaderUid gs_uid, u32 gs_primitive_type)
{
s_last_geometry_shader_uid = gs_uid;
if (g_ActiveConfig.bEnableShaderDebugging)
{
ShaderCode code = GenerateGeometryShaderCode(gs_primitive_type, API_D3D);
s_geometry_uid_checker.AddToIndexAndCheck(code, gs_uid, "Geometry", "g");
}
if (gs_uid.GetUidData()->IsPassthrough())
{
s_last_geometry_shader_bytecode = {};
return;
}
auto gs_iterator = s_gs_bytecode_cache.find(gs_uid);
if (gs_iterator != s_gs_bytecode_cache.end())
{
s_last_geometry_shader_bytecode = gs_iterator->second;
}
else
{
ShaderCode gs_code = GenerateGeometryShaderCode(gs_primitive_type, API_D3D);
ID3DBlob* gs_bytecode = nullptr;
if (!D3D::CompileGeometryShader(gs_code.GetBuffer(), &gs_bytecode))
{
GFX_DEBUGGER_PAUSE_AT(NEXT_ERROR, true);
return;
}
s_last_geometry_shader_bytecode = InsertByteCode<GeometryShaderUid, GsBytecodeCache>(gs_uid, &s_gs_bytecode_cache, gs_bytecode);
s_gs_disk_cache.Append(gs_uid, reinterpret_cast<u8*>(gs_bytecode->GetBufferPointer()), static_cast<u32>(gs_bytecode->GetBufferSize()));
if (g_ActiveConfig.bEnableShaderDebugging && gs_bytecode)
{
s_gs_hlsl_cache[gs_uid] = gs_code.GetBuffer();
}
}
}
void ShaderCache::HandlePSUIDChange(PixelShaderUid ps_uid, DSTALPHA_MODE ps_dst_alpha_mode)
{
s_last_pixel_shader_uid = ps_uid;
if (g_ActiveConfig.bEnableShaderDebugging)
{
ShaderCode code = GeneratePixelShaderCode(ps_dst_alpha_mode, API_D3D);
s_pixel_uid_checker.AddToIndexAndCheck(code, ps_uid, "Pixel", "p");
}
auto ps_iterator = s_ps_bytecode_cache.find(ps_uid);
if (ps_iterator != s_ps_bytecode_cache.end())
{
s_last_pixel_shader_bytecode = ps_iterator->second;
GFX_DEBUGGER_PAUSE_AT(NEXT_PIXEL_SHADER_CHANGE, true);
}
else
{
ShaderCode ps_code = GeneratePixelShaderCode(ps_dst_alpha_mode, API_D3D);
ID3DBlob* ps_bytecode = nullptr;
if (!D3D::CompilePixelShader(ps_code.GetBuffer(), &ps_bytecode))
{
GFX_DEBUGGER_PAUSE_AT(NEXT_ERROR, true);
return;
}
s_last_pixel_shader_bytecode = InsertByteCode<PixelShaderUid, PsBytecodeCache>(ps_uid, &s_ps_bytecode_cache, ps_bytecode);
s_ps_disk_cache.Append(ps_uid, reinterpret_cast<u8*>(ps_bytecode->GetBufferPointer()), static_cast<u32>(ps_bytecode->GetBufferSize()));
SETSTAT(stats.numPixelShadersAlive, static_cast<int>(s_ps_bytecode_cache.size()));
INCSTAT(stats.numPixelShadersCreated);
if (g_ActiveConfig.bEnableShaderDebugging && ps_bytecode)
{
s_ps_hlsl_cache[ps_uid] = ps_code.GetBuffer();
}
}
}
void ShaderCache::HandleVSUIDChange(VertexShaderUid vs_uid)
{
s_last_vertex_shader_uid = vs_uid;
if (g_ActiveConfig.bEnableShaderDebugging)
{
ShaderCode code = GenerateVertexShaderCode(API_D3D);
s_vertex_uid_checker.AddToIndexAndCheck(code, vs_uid, "Vertex", "v");
}
auto vs_iterator = s_vs_bytecode_cache.find(vs_uid);
if (vs_iterator != s_vs_bytecode_cache.end())
{
s_last_vertex_shader_bytecode = vs_iterator->second;
GFX_DEBUGGER_PAUSE_AT(NEXT_VERTEX_SHADER_CHANGE, true);
}
else
{
ShaderCode vs_code = GenerateVertexShaderCode(API_D3D);
ID3DBlob* vs_bytecode = nullptr;
if (!D3D::CompileVertexShader(vs_code.GetBuffer(), &vs_bytecode))
{
GFX_DEBUGGER_PAUSE_AT(NEXT_ERROR, true);
return;
}
s_last_vertex_shader_bytecode = InsertByteCode<VertexShaderUid, VsBytecodeCache>(vs_uid, &s_vs_bytecode_cache, vs_bytecode);
s_vs_disk_cache.Append(vs_uid, reinterpret_cast<u8*>(vs_bytecode->GetBufferPointer()), static_cast<u32>(vs_bytecode->GetBufferSize()));
SETSTAT(stats.numVertexShadersAlive, static_cast<int>(s_vs_bytecode_cache.size()));
INCSTAT(stats.numVertexShadersCreated);
if (g_ActiveConfig.bEnableShaderDebugging && vs_bytecode)
{
s_vs_hlsl_cache[vs_uid] = vs_code.GetBuffer();
}
}
}
template<class UidType, class ShaderCacheType>
D3D12_SHADER_BYTECODE ShaderCache::InsertByteCode(const UidType& uid, ShaderCacheType* shader_cache, ID3DBlob* bytecode_blob)
{
// Note: Don't release the incoming bytecode, we need it to stick around, since in D3D12
// the raw bytecode itself is bound. It is released at Shutdown() time.
s_shader_blob_list.push_back(bytecode_blob);
D3D12_SHADER_BYTECODE shader_bytecode;
shader_bytecode.pShaderBytecode = bytecode_blob->GetBufferPointer();
shader_bytecode.BytecodeLength = bytecode_blob->GetBufferSize();
(*shader_cache)[uid] = shader_bytecode;
return shader_bytecode;
}
D3D12_PRIMITIVE_TOPOLOGY_TYPE ShaderCache::GetCurrentPrimitiveTopology() { return s_current_primitive_topology; }
D3D12_SHADER_BYTECODE ShaderCache::GetActiveGeometryShaderBytecode() { return s_last_geometry_shader_bytecode; }
D3D12_SHADER_BYTECODE ShaderCache::GetActivePixelShaderBytecode() { return s_last_pixel_shader_bytecode; }
D3D12_SHADER_BYTECODE ShaderCache::GetActiveVertexShaderBytecode() { return s_last_vertex_shader_bytecode; }
const GeometryShaderUid* ShaderCache::GetActiveGeometryShaderUid() { return &s_last_geometry_shader_uid; }
const PixelShaderUid* ShaderCache::GetActivePixelShaderUid() { return &s_last_pixel_shader_uid; }
const VertexShaderUid* ShaderCache::GetActiveVertexShaderUid() { return &s_last_vertex_shader_uid; }
D3D12_SHADER_BYTECODE ShaderCache::GetGeometryShaderFromUid(const GeometryShaderUid* uid)
{
auto bytecode = s_gs_bytecode_cache.find(*uid);
if (bytecode != s_gs_bytecode_cache.end())
return bytecode->second;
return D3D12_SHADER_BYTECODE();
}
D3D12_SHADER_BYTECODE ShaderCache::GetPixelShaderFromUid(const PixelShaderUid* uid)
{
auto bytecode = s_ps_bytecode_cache.find(*uid);
if (bytecode != s_ps_bytecode_cache.end())
return bytecode->second;
return D3D12_SHADER_BYTECODE();
}
D3D12_SHADER_BYTECODE ShaderCache::GetVertexShaderFromUid(const VertexShaderUid* uid)
{
auto bytecode = s_vs_bytecode_cache.find(*uid);
if (bytecode != s_vs_bytecode_cache.end())
return bytecode->second;
return D3D12_SHADER_BYTECODE();
}
}

View File

@ -0,0 +1,50 @@
// Copyright 2015 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include "VideoCommon/GeometryShaderGen.h"
#include "VideoCommon/PixelShaderGen.h"
#include "VideoCommon/VertexShaderGen.h"
namespace DX12
{
class D3DBlob;
class ShaderCache final
{
public:
static void Init();
static void Clear();
static void Shutdown();
static void LoadAndSetActiveShaders(DSTALPHA_MODE ps_dst_alpha_mode, u32 gs_primitive_type);
template<class UidType, class ShaderCacheType>
static D3D12_SHADER_BYTECODE InsertByteCode(const UidType& uid, ShaderCacheType* shader_cache, ID3DBlob* bytecode_blob);
static D3D12_SHADER_BYTECODE GetActiveGeometryShaderBytecode();
static D3D12_SHADER_BYTECODE GetActivePixelShaderBytecode();
static D3D12_SHADER_BYTECODE GetActiveVertexShaderBytecode();
static const GeometryShaderUid* GetActiveGeometryShaderUid();
static const PixelShaderUid* GetActivePixelShaderUid();
static const VertexShaderUid* GetActiveVertexShaderUid();
static D3D12_SHADER_BYTECODE GetGeometryShaderFromUid(const GeometryShaderUid* uid);
static D3D12_SHADER_BYTECODE GetPixelShaderFromUid(const PixelShaderUid* uid);
static D3D12_SHADER_BYTECODE GetVertexShaderFromUid(const VertexShaderUid* uid);
static D3D12_PRIMITIVE_TOPOLOGY_TYPE GetCurrentPrimitiveTopology();
private:
static void SetCurrentPrimitiveTopology(u32 gs_primitive_type);
static void HandleGSUIDChange(GeometryShaderUid gs_uid, u32 gs_primitive_type);
static void HandlePSUIDChange(PixelShaderUid ps_uid, DSTALPHA_MODE ps_dst_alpha_mode);
static void HandleVSUIDChange(VertexShaderUid vs_uid);
};
}

View File

@ -0,0 +1,167 @@
// Copyright 2015 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "D3DBase.h"
#include "D3DCommandListManager.h"
#include "D3DStreamBuffer.h"
#include "ShaderConstantsManager.h"
#include "VideoCommon/GeometryShaderManager.h"
#include "VideoCommon/PixelShaderManager.h"
#include "VideoCommon/Statistics.h"
#include "VideoCommon/VertexShaderManager.h"
#include "VideoCommon/VideoConfig.h"
namespace DX12
{
enum SHADER_STAGE
{
SHADER_STAGE_GEOMETRY_SHADER = 0,
SHADER_STAGE_PIXEL_SHADER = 1,
SHADER_STAGE_VERTEX_SHADER = 2,
SHADER_STAGE_COUNT = 3
};
static std::array<D3DStreamBuffer*, SHADER_STAGE_COUNT> s_shader_constant_stream_buffers = {};
static const unsigned int s_shader_constant_buffer_padded_sizes[SHADER_STAGE_COUNT] = {
(sizeof(GeometryShaderConstants) + 0xff) & ~0xff,
(sizeof(PixelShaderConstants) + 0xff) & ~0xff,
(sizeof(VertexShaderConstants) + 0xff) & ~0xff
};
void ShaderConstantsManager::Init()
{
// Allow a large maximum size, as we want to minimize stalls here
std::generate(std::begin(s_shader_constant_stream_buffers), std::end(s_shader_constant_stream_buffers), []() {
return new D3DStreamBuffer(2 * 1024 * 1024, 64 * 1024 * 1024, nullptr);
});
}
void ShaderConstantsManager::Shutdown()
{
for (auto& it : s_shader_constant_stream_buffers)
SAFE_DELETE(it);
}
bool ShaderConstantsManager::LoadAndSetGeometryShaderConstants()
{
bool command_list_executed = false;
if (GeometryShaderManager::dirty)
{
command_list_executed = s_shader_constant_stream_buffers[SHADER_STAGE_GEOMETRY_SHADER]->AllocateSpaceInBuffer(
s_shader_constant_buffer_padded_sizes[SHADER_STAGE_GEOMETRY_SHADER],
0 // The padded sizes are already aligned to 256 bytes, so don't need to worry about manually aligning offset.
);
memcpy(
s_shader_constant_stream_buffers[SHADER_STAGE_GEOMETRY_SHADER]->GetCPUAddressOfCurrentAllocation(),
&GeometryShaderManager::constants,
sizeof(GeometryShaderConstants));
GeometryShaderManager::dirty = false;
ADDSTAT(stats.thisFrame.bytesUniformStreamed, sizeof(GeometryShaderConstants));
D3D::command_list_mgr->SetCommandListDirtyState(COMMAND_LIST_STATE_GS_CBV, true);
}
if (D3D::command_list_mgr->GetCommandListDirtyState(COMMAND_LIST_STATE_GS_CBV))
{
D3D::current_command_list->SetGraphicsRootConstantBufferView(
DESCRIPTOR_TABLE_GS_CBV,
s_shader_constant_stream_buffers[SHADER_STAGE_GEOMETRY_SHADER]->GetGPUAddressOfCurrentAllocation()
);
D3D::command_list_mgr->SetCommandListDirtyState(COMMAND_LIST_STATE_GS_CBV, false);
}
return command_list_executed;
}
bool ShaderConstantsManager::LoadAndSetPixelShaderConstants()
{
bool command_list_executed = false;
if (PixelShaderManager::dirty)
{
command_list_executed = s_shader_constant_stream_buffers[SHADER_STAGE_PIXEL_SHADER]->AllocateSpaceInBuffer(
s_shader_constant_buffer_padded_sizes[SHADER_STAGE_PIXEL_SHADER],
0 // The padded sizes are already aligned to 256 bytes, so don't need to worry about manually aligning offset.
);
memcpy(
s_shader_constant_stream_buffers[SHADER_STAGE_PIXEL_SHADER]->GetCPUAddressOfCurrentAllocation(),
&PixelShaderManager::constants,
sizeof(PixelShaderConstants));
PixelShaderManager::dirty = false;
ADDSTAT(stats.thisFrame.bytesUniformStreamed, sizeof(PixelShaderConstants));
D3D::command_list_mgr->SetCommandListDirtyState(COMMAND_LIST_STATE_PS_CBV, true);
}
if (D3D::command_list_mgr->GetCommandListDirtyState(COMMAND_LIST_STATE_PS_CBV))
{
D3D::current_command_list->SetGraphicsRootConstantBufferView(
DESCRIPTOR_TABLE_PS_CBVONE,
s_shader_constant_stream_buffers[SHADER_STAGE_PIXEL_SHADER]->GetGPUAddressOfCurrentAllocation()
);
D3D::command_list_mgr->SetCommandListDirtyState(COMMAND_LIST_STATE_PS_CBV, false);
}
return command_list_executed;
}
bool ShaderConstantsManager::LoadAndSetVertexShaderConstants()
{
bool command_list_executed = false;
if (VertexShaderManager::dirty)
{
command_list_executed = s_shader_constant_stream_buffers[SHADER_STAGE_VERTEX_SHADER]->AllocateSpaceInBuffer(
s_shader_constant_buffer_padded_sizes[SHADER_STAGE_VERTEX_SHADER],
0 // The padded sizes are already aligned to 256 bytes, so don't need to worry about manually aligning offset.
);
memcpy(
s_shader_constant_stream_buffers[SHADER_STAGE_VERTEX_SHADER]->GetCPUAddressOfCurrentAllocation(),
&VertexShaderManager::constants,
sizeof(VertexShaderConstants));
VertexShaderManager::dirty = false;
ADDSTAT(stats.thisFrame.bytesUniformStreamed, sizeof(VertexShaderConstants));
D3D::command_list_mgr->SetCommandListDirtyState(COMMAND_LIST_STATE_VS_CBV, true);
}
if (D3D::command_list_mgr->GetCommandListDirtyState(COMMAND_LIST_STATE_VS_CBV))
{
const D3D12_GPU_VIRTUAL_ADDRESS calculated_gpu_va =
s_shader_constant_stream_buffers[SHADER_STAGE_VERTEX_SHADER]->GetGPUAddressOfCurrentAllocation();
D3D::current_command_list->SetGraphicsRootConstantBufferView(
DESCRIPTOR_TABLE_VS_CBV,
calculated_gpu_va
);
if (g_ActiveConfig.bEnablePixelLighting)
D3D::current_command_list->SetGraphicsRootConstantBufferView(
DESCRIPTOR_TABLE_PS_CBVTWO,
calculated_gpu_va
);
D3D::command_list_mgr->SetCommandListDirtyState(COMMAND_LIST_STATE_VS_CBV, false);
}
return command_list_executed;
}
}

View File

@ -0,0 +1,23 @@
// Copyright 2015 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include "ShaderCache.h"
namespace DX12
{
class ShaderConstantsManager final
{
public:
static void Init();
static void Shutdown();
static bool LoadAndSetGeometryShaderConstants();
static bool LoadAndSetPixelShaderConstants();
static bool LoadAndSetVertexShaderConstants();
};
}

View File

@ -0,0 +1,699 @@
// Copyright 2015 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "Common/StringUtil.h"
#include "VideoBackends/D3D12/D3DBase.h"
#include "VideoBackends/D3D12/D3DShader.h"
#include "VideoBackends/D3D12/StaticShaderCache.h"
#include "VideoCommon/VideoConfig.h"
namespace DX12
{
// Pixel Shader blobs
static ID3DBlob* s_color_matrix_program_blob[2] = {};
static ID3DBlob* s_color_copy_program_blob[2] = {};
static ID3DBlob* s_depth_matrix_program_blob[2] = {};
static ID3DBlob* s_depth_copy_program_blob[2] = {};
static ID3DBlob* s_clear_program_blob = {};
static ID3DBlob* s_anaglyph_program_blob = {};
static ID3DBlob* s_rgba6_to_rgb8_program_blob[2] = {};
static ID3DBlob* s_rgb8_to_rgba6_program_blob[2] = {};
// Vertex Shader blobs/input layouts
static ID3DBlob* s_simple_vertex_shader_blob = {};
static ID3DBlob* s_simple_clear_vertex_shader_blob = {};
static const D3D12_INPUT_ELEMENT_DESC s_simple_vertex_shader_input_elements[] = {
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
};
static const D3D12_INPUT_LAYOUT_DESC s_simple_vertex_shader_input_layout = {
s_simple_vertex_shader_input_elements,
ARRAYSIZE(s_simple_vertex_shader_input_elements)
};
static const D3D12_INPUT_ELEMENT_DESC s_clear_vertex_shader_input_elements[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
{ "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
};
static const D3D12_INPUT_LAYOUT_DESC s_clear_vertex_shader_input_layout =
{
s_clear_vertex_shader_input_elements,
ARRAYSIZE(s_clear_vertex_shader_input_elements)
};
// Geometry Shader blobs
static ID3DBlob* s_clear_geometry_shader_blob = nullptr;
static ID3DBlob* s_copy_geometry_shader_blob = nullptr;
// Pixel Shader HLSL
static constexpr const char s_clear_program_hlsl[] = {
"void main(\n"
"out float4 ocol0 : SV_Target,\n"
"in float4 pos : SV_Position,\n"
"in float4 incol0 : COLOR0){\n"
"ocol0 = incol0;\n"
"}\n"
};
// EXISTINGD3D11TODO: Find some way to avoid having separate shaders for non-MSAA and MSAA...
static constexpr const char s_color_copy_program_hlsl[] = {
"sampler samp0 : register(s0);\n"
"Texture2DArray Tex0 : register(t0);\n"
"void main(\n"
"out float4 ocol0 : SV_Target,\n"
"in float4 pos : SV_Position,\n"
"in float3 uv0 : TEXCOORD0){\n"
"ocol0 = Tex0.Sample(samp0,uv0);\n"
"}\n"
};
static constexpr const char s_depth_copy_program_hlsl[] = {
"sampler samp0 : register(s0);\n"
"Texture2DArray Tex0 : register(t0);\n"
"void main(\n"
"out float odepth : SV_Depth,\n"
"in float4 pos : SV_Position,\n"
"in float3 uv0 : TEXCOORD0){\n"
"odepth = Tex0.Sample(samp0,uv0);\n"
"}\n"
};
// Anaglyph Red-Cyan shader based on Dubois algorithm
// Constants taken from the paper:
// "Conversion of a Stereo Pair to Anaglyph with
// the Least-Squares Projection Method"
// Eric Dubois, March 2009
static constexpr const char s_anaglyph_program_hlsl[] = {
"sampler samp0 : register(s0);\n"
"Texture2DArray Tex0 : register(t0);\n"
"void main(\n"
"out float4 ocol0 : SV_Target,\n"
"in float4 pos : SV_Position,\n"
"in float3 uv0 : TEXCOORD0){\n"
"float4 c0 = Tex0.Sample(samp0, float3(uv0.xy, 0.0));\n"
"float4 c1 = Tex0.Sample(samp0, float3(uv0.xy, 1.0));\n"
"float3x3 l = float3x3( 0.437, 0.449, 0.164,\n"
" -0.062,-0.062,-0.024,\n"
" -0.048,-0.050,-0.017);\n"
"float3x3 r = float3x3(-0.011,-0.032,-0.007,\n"
" 0.377, 0.761, 0.009,\n"
" -0.026,-0.093, 1.234);\n"
"ocol0 = float4(mul(l, c0.rgb) + mul(r, c1.rgb), c0.a);\n"
"}\n"
};
// TODO: Improve sampling algorithm!
static constexpr const char s_color_copy_program_msaa_hlsl[] = {
"#define SAMPLES %d\n"
"sampler samp0 : register(s0);\n"
"Texture2DMSArray<float4, SAMPLES> Tex0 : register(t0);\n"
"void main(\n"
"out float4 ocol0 : SV_Target,\n"
"in float4 pos : SV_Position,\n"
"in float3 uv0 : TEXCOORD0){\n"
"int width, height, slices, samples;\n"
"Tex0.GetDimensions(width, height, slices, samples);\n"
"ocol0 = 0;\n"
"for(int i = 0; i < SAMPLES; ++i)\n"
" ocol0 += Tex0.Load(int3(uv0.x*(width), uv0.y*(height), uv0.z), i);\n"
"ocol0 /= SAMPLES;\n"
"}\n"
};
static constexpr const char s_depth_copy_program_msaa_hlsl[] = {
"#define SAMPLES %d\n"
"Texture2DMSArray<float4, SAMPLES> Tex0 : register(t0);\n"
"void main(\n"
" out float depth : SV_Depth,\n"
" in float4 pos : SV_Position,\n"
" in float3 uv0 : TEXCOORD0)\n"
"{\n"
" int width, height, slices, samples;\n"
" Tex0.GetDimensions(width, height, slices, samples);\n"
" depth = Tex0.Load(int3(uv0.x*(width), uv0.y*(height), uv0.z), 0).x;\n"
" for(int i = 1; i < SAMPLES; ++i)\n"
" depth = min(depth, Tex0.Load(int3(uv0.x*(width), uv0.y*(height), uv0.z), i).x);\n"
"}\n"
};
static constexpr const char s_color_matrix_program_hlsl[] = {
"sampler samp0 : register(s0);\n"
"Texture2DArray Tex0 : register(t0);\n"
"uniform float4 cColMatrix[7] : register(c0);\n"
"void main(\n"
"out float4 ocol0 : SV_Target,\n"
"in float4 pos : SV_Position,\n"
"in float3 uv0 : TEXCOORD0){\n"
"float4 texcol = Tex0.Sample(samp0,uv0);\n"
"texcol = round(texcol * cColMatrix[5])*cColMatrix[6];\n"
"ocol0 = float4(dot(texcol,cColMatrix[0]),dot(texcol,cColMatrix[1]),dot(texcol,cColMatrix[2]),dot(texcol,cColMatrix[3])) + cColMatrix[4];\n"
"}\n"
};
static constexpr const char s_color_matrix_program_msaa_hlsl[] = {
"#define SAMPLES %d\n"
"sampler samp0 : register(s0);\n"
"Texture2DMSArray<float4, SAMPLES> Tex0 : register(t0);\n"
"uniform float4 cColMatrix[7] : register(c0);\n"
"void main(\n"
"out float4 ocol0 : SV_Target,\n"
"in float4 pos : SV_Position,\n"
"in float3 uv0 : TEXCOORD0){\n"
"int width, height, slices, samples;\n"
"Tex0.GetDimensions(width, height, slices, samples);\n"
"float4 texcol = 0;\n"
"for(int i = 0; i < SAMPLES; ++i)\n"
" texcol += Tex0.Load(int3(uv0.x*(width), uv0.y*(height), uv0.z), i);\n"
"texcol /= SAMPLES;\n"
"texcol = round(texcol * cColMatrix[5])*cColMatrix[6];\n"
"ocol0 = float4(dot(texcol,cColMatrix[0]),dot(texcol,cColMatrix[1]),dot(texcol,cColMatrix[2]),dot(texcol,cColMatrix[3])) + cColMatrix[4];\n"
"}\n"
};
static constexpr const char s_depth_matrix_program_hlsl[] = {
"sampler samp0 : register(s0);\n"
"Texture2DArray Tex0 : register(t0);\n"
"uniform float4 cColMatrix[7] : register(c0);\n"
"void main(\n"
"out float4 ocol0 : SV_Target,\n"
" in float4 pos : SV_Position,\n"
" in float3 uv0 : TEXCOORD0){\n"
" float4 texcol = Tex0.Sample(samp0,uv0);\n"
" int depth = int((1.0 - texcol.x) * 16777216.0);\n"
// Convert to Z24 format
" int4 workspace;\n"
" workspace.r = (depth >> 16) & 255;\n"
" workspace.g = (depth >> 8) & 255;\n"
" workspace.b = depth & 255;\n"
// Convert to Z4 format
" workspace.a = (depth >> 16) & 0xF0;\n"
// Normalize components to [0.0..1.0]
" texcol = float4(workspace) / 255.0;\n"
// Apply color matrix
" ocol0 = float4(dot(texcol,cColMatrix[0]),dot(texcol,cColMatrix[1]),dot(texcol,cColMatrix[2]),dot(texcol,cColMatrix[3])) + cColMatrix[4];\n"
"}\n"
};
static constexpr const char s_depth_matrix_program_msaa_hlsl[] = {
"#define SAMPLES %d\n"
"sampler samp0 : register(s0);\n"
"Texture2DMSArray<float4, SAMPLES> Tex0 : register(t0);\n"
"uniform float4 cColMatrix[7] : register(c0);\n"
"void main(\n"
"out float4 ocol0 : SV_Target,\n"
" in float4 pos : SV_Position,\n"
" in float3 uv0 : TEXCOORD0){\n"
" int width, height, slices, samples;\n"
" Tex0.GetDimensions(width, height, slices, samples);\n"
" float4 texcol = 0;\n"
" for(int i = 0; i < SAMPLES; ++i)\n"
" texcol += Tex0.Load(int3(uv0.x*(width), uv0.y*(height), uv0.z), i);\n"
" texcol /= SAMPLES;\n"
" int depth = int((1.0 - texcol.x) * 16777216.0);\n"
// Convert to Z24 format
" int4 workspace;\n"
" workspace.r = (depth >> 16) & 255;\n"
" workspace.g = (depth >> 8) & 255;\n"
" workspace.b = depth & 255;\n"
// Convert to Z4 format
" workspace.a = (depth >> 16) & 0xF0;\n"
// Normalize components to [0.0..1.0]
" texcol = float4(workspace) / 255.0;\n"
// Apply color matrix
" ocol0 = float4(dot(texcol,cColMatrix[0]),dot(texcol,cColMatrix[1]),dot(texcol,cColMatrix[2]),dot(texcol,cColMatrix[3])) + cColMatrix[4];\n"
"}\n"
};
static constexpr const char s_reint_rgba6_to_rgb8_program_hlsl[] = {
"sampler samp0 : register(s0);\n"
"Texture2DArray Tex0 : register(t0);\n"
"void main(\n"
" out float4 ocol0 : SV_Target,\n"
" in float4 pos : SV_Position,\n"
" in float3 uv0 : TEXCOORD0)\n"
"{\n"
" int4 src6 = round(Tex0.Sample(samp0,uv0) * 63.f);\n"
" int4 dst8;\n"
" dst8.r = (src6.r << 2) | (src6.g >> 4);\n"
" dst8.g = ((src6.g & 0xF) << 4) | (src6.b >> 2);\n"
" dst8.b = ((src6.b & 0x3) << 6) | src6.a;\n"
" dst8.a = 255;\n"
" ocol0 = (float4)dst8 / 255.f;\n"
"}"
};
static constexpr const char s_reint_rgba6_to_rgb8_program_msaa_hlsl[] = {
"#define SAMPLES %d\n"
"sampler samp0 : register(s0);\n"
"Texture2DMSArray<float4, SAMPLES> Tex0 : register(t0);\n"
"void main(\n"
" out float4 ocol0 : SV_Target,\n"
" in float4 pos : SV_Position,\n"
" in float3 uv0 : TEXCOORD0)\n"
"{\n"
" int width, height, slices, samples;\n"
" Tex0.GetDimensions(width, height, slices, samples);\n"
" float4 texcol = 0;\n"
" for (int i = 0; i < SAMPLES; ++i)\n"
" texcol += Tex0.Load(int3(uv0.x*(width), uv0.y*(height), uv0.z), i);\n"
" texcol /= SAMPLES;\n"
" int4 src6 = round(texcol * 63.f);\n"
" int4 dst8;\n"
" dst8.r = (src6.r << 2) | (src6.g >> 4);\n"
" dst8.g = ((src6.g & 0xF) << 4) | (src6.b >> 2);\n"
" dst8.b = ((src6.b & 0x3) << 6) | src6.a;\n"
" dst8.a = 255;\n"
" ocol0 = (float4)dst8 / 255.f;\n"
"}"
};
static constexpr const char s_reint_rgb8_to_rgba6_program_hlsl[] = {
"sampler samp0 : register(s0);\n"
"Texture2DArray Tex0 : register(t0);\n"
"void main(\n"
" out float4 ocol0 : SV_Target,\n"
" in float4 pos : SV_Position,\n"
" in float3 uv0 : TEXCOORD0)\n"
"{\n"
" int4 src8 = round(Tex0.Sample(samp0,uv0) * 255.f);\n"
" int4 dst6;\n"
" dst6.r = src8.r >> 2;\n"
" dst6.g = ((src8.r & 0x3) << 4) | (src8.g >> 4);\n"
" dst6.b = ((src8.g & 0xF) << 2) | (src8.b >> 6);\n"
" dst6.a = src8.b & 0x3F;\n"
" ocol0 = (float4)dst6 / 63.f;\n"
"}\n"
};
static constexpr const char s_reint_rgb8_to_rgba6_program_msaa_hlsl[] = {
"#define SAMPLES %d\n"
"sampler samp0 : register(s0);\n"
"Texture2DMSArray<float4, SAMPLES> Tex0 : register(t0);\n"
"void main(\n"
" out float4 ocol0 : SV_Target,\n"
" in float4 pos : SV_Position,\n"
" in float3 uv0 : TEXCOORD0)\n"
"{\n"
" int width, height, slices, samples;\n"
" Tex0.GetDimensions(width, height, slices, samples);\n"
" float4 texcol = 0;\n"
" for (int i = 0; i < SAMPLES; ++i)\n"
" texcol += Tex0.Load(int3(uv0.x*(width), uv0.y*(height), uv0.z), i);\n"
" texcol /= SAMPLES;\n"
" int4 src8 = round(texcol * 255.f);\n"
" int4 dst6;\n"
" dst6.r = src8.r >> 2;\n"
" dst6.g = ((src8.r & 0x3) << 4) | (src8.g >> 4);\n"
" dst6.b = ((src8.g & 0xF) << 2) | (src8.b >> 6);\n"
" dst6.a = src8.b & 0x3F;\n"
" ocol0 = (float4)dst6 / 63.f;\n"
"}\n"
};
// Vertex Shader HLSL
static constexpr const char s_simple_vertex_shader_hlsl[] = {
"struct VSOUTPUT\n"
"{\n"
"float4 vPosition : POSITION;\n"
"float3 vTexCoord : TEXCOORD0;\n"
"float vTexCoord1 : TEXCOORD1;\n"
"};\n"
"VSOUTPUT main(float4 inPosition : POSITION,float4 inTEX0 : TEXCOORD0)\n"
"{\n"
"VSOUTPUT OUT;\n"
"OUT.vPosition = inPosition;\n"
"OUT.vTexCoord = inTEX0.xyz;\n"
"OUT.vTexCoord1 = inTEX0.w;\n"
"return OUT;\n"
"}\n"
};
static constexpr const char s_clear_vertex_shader_hlsl[] = {
"struct VSOUTPUT\n"
"{\n"
"float4 vPosition : POSITION;\n"
"float4 vColor0 : COLOR0;\n"
"};\n"
"VSOUTPUT main(float4 inPosition : POSITION,float4 inColor0: COLOR0)\n"
"{\n"
"VSOUTPUT OUT;\n"
"OUT.vPosition = inPosition;\n"
"OUT.vColor0 = inColor0;\n"
"return OUT;\n"
"}\n"
};
// Geometry Shader HLSL
static constexpr const char s_clear_geometry_shader_hlsl[] = {
"struct VSOUTPUT\n"
"{\n"
" float4 vPosition : POSITION;\n"
" float4 vColor0 : COLOR0;\n"
"};\n"
"struct GSOUTPUT\n"
"{\n"
" float4 vPosition : POSITION;\n"
" float4 vColor0 : COLOR0;\n"
" uint slice : SV_RenderTargetArrayIndex;\n"
"};\n"
"[maxvertexcount(6)]\n"
"void main(triangle VSOUTPUT o[3], inout TriangleStream<GSOUTPUT> Output)\n"
"{\n"
"for(int slice = 0; slice < 2; slice++)\n"
"{\n"
" for(int i = 0; i < 3; i++)\n"
" {\n"
" GSOUTPUT OUT;\n"
" OUT.vPosition = o[i].vPosition;\n"
" OUT.vColor0 = o[i].vColor0;\n"
" OUT.slice = slice;\n"
" Output.Append(OUT);\n"
" }\n"
" Output.RestartStrip();\n"
"}\n"
"}\n"
};
static constexpr const char s_copy_geometry_shader_hlsl[] = {
"struct VSOUTPUT\n"
"{\n"
" float4 vPosition : POSITION;\n"
" float3 vTexCoord : TEXCOORD0;\n"
" float vTexCoord1 : TEXCOORD1;\n"
"};\n"
"struct GSOUTPUT\n"
"{\n"
" float4 vPosition : POSITION;\n"
" float3 vTexCoord : TEXCOORD0;\n"
" float vTexCoord1 : TEXCOORD1;\n"
" uint slice : SV_RenderTargetArrayIndex;\n"
"};\n"
"[maxvertexcount(6)]\n"
"void main(triangle VSOUTPUT o[3], inout TriangleStream<GSOUTPUT> Output)\n"
"{\n"
"for(int slice = 0; slice < 2; slice++)\n"
"{\n"
" for(int i = 0; i < 3; i++)\n"
" {\n"
" GSOUTPUT OUT;\n"
" OUT.vPosition = o[i].vPosition;\n"
" OUT.vTexCoord = o[i].vTexCoord;\n"
" OUT.vTexCoord.z = slice;\n"
" OUT.vTexCoord1 = o[i].vTexCoord1;\n"
" OUT.slice = slice;\n"
" Output.Append(OUT);\n"
" }\n"
" Output.RestartStrip();\n"
"}\n"
"}\n"
};
D3D12_SHADER_BYTECODE StaticShaderCache::GetReinterpRGBA6ToRGB8PixelShader(bool multisampled)
{
D3D12_SHADER_BYTECODE bytecode = {};
if (!multisampled || g_ActiveConfig.iMultisamples == 1)
{
if (!s_rgba6_to_rgb8_program_blob[0])
{
D3D::CompilePixelShader(s_reint_rgba6_to_rgb8_program_hlsl, &s_rgba6_to_rgb8_program_blob[0]);
}
bytecode = { s_rgba6_to_rgb8_program_blob[0]->GetBufferPointer(), s_rgba6_to_rgb8_program_blob[0]->GetBufferSize() };
return bytecode;
}
else if (!s_rgba6_to_rgb8_program_blob[1])
{
// create MSAA shader for current AA mode
std::string buf = StringFromFormat(s_reint_rgba6_to_rgb8_program_msaa_hlsl, g_ActiveConfig.iMultisamples);
D3D::CompilePixelShader(buf, &s_rgba6_to_rgb8_program_blob[1]);
bytecode = { s_rgba6_to_rgb8_program_blob[1]->GetBufferPointer(), s_rgba6_to_rgb8_program_blob[1]->GetBufferSize() };
}
return bytecode;
}
D3D12_SHADER_BYTECODE StaticShaderCache::GetReinterpRGB8ToRGBA6PixelShader(bool multisampled)
{
D3D12_SHADER_BYTECODE bytecode = {};
if (!multisampled || g_ActiveConfig.iMultisamples == 1)
{
if (!s_rgb8_to_rgba6_program_blob[0])
{
D3D::CompilePixelShader(s_reint_rgb8_to_rgba6_program_hlsl, &s_rgb8_to_rgba6_program_blob[0]);
}
bytecode = { s_rgb8_to_rgba6_program_blob[0]->GetBufferPointer(), s_rgb8_to_rgba6_program_blob[0]->GetBufferSize() };
return bytecode;
}
else if (!s_rgb8_to_rgba6_program_blob[1])
{
// create MSAA shader for current AA mode
std::string buf = StringFromFormat(s_reint_rgb8_to_rgba6_program_msaa_hlsl, g_ActiveConfig.iMultisamples);
D3D::CompilePixelShader(buf, &s_rgb8_to_rgba6_program_blob[1]);
bytecode = { s_rgb8_to_rgba6_program_blob[1]->GetBufferPointer(), s_rgb8_to_rgba6_program_blob[1]->GetBufferSize() };
}
return bytecode;
}
D3D12_SHADER_BYTECODE StaticShaderCache::GetColorCopyPixelShader(bool multisampled)
{
D3D12_SHADER_BYTECODE bytecode = {};
if (!multisampled || g_ActiveConfig.iMultisamples == 1)
{
bytecode = { s_color_copy_program_blob[0]->GetBufferPointer(), s_color_copy_program_blob[0]->GetBufferSize() };
}
else if (s_color_copy_program_blob[1])
{
bytecode = { s_color_copy_program_blob[1]->GetBufferPointer(), s_color_copy_program_blob[1]->GetBufferSize() };
}
else
{
// create MSAA shader for current AA mode
std::string buf = StringFromFormat(s_color_copy_program_msaa_hlsl, g_ActiveConfig.iMultisamples);
D3D::CompilePixelShader(buf, &s_color_copy_program_blob[1]);
bytecode = { s_color_copy_program_blob[1]->GetBufferPointer(), s_color_copy_program_blob[1]->GetBufferSize() };
}
return bytecode;
}
D3D12_SHADER_BYTECODE StaticShaderCache::GetDepthCopyPixelShader(bool multisampled)
{
D3D12_SHADER_BYTECODE bytecode = {};
if (!multisampled || g_ActiveConfig.iMultisamples == 1)
{
bytecode = { s_depth_copy_program_blob[0]->GetBufferPointer(), s_depth_copy_program_blob[0]->GetBufferSize() };
}
else if (s_depth_copy_program_blob[1])
{
bytecode = { s_depth_copy_program_blob[1]->GetBufferPointer(), s_depth_copy_program_blob[1]->GetBufferSize() };
}
else
{
// create MSAA shader for current AA mode
std::string buf = StringFromFormat(s_depth_copy_program_msaa_hlsl, g_ActiveConfig.iMultisamples);
D3D::CompilePixelShader(buf, &s_depth_copy_program_blob[1]);
bytecode = { s_depth_copy_program_blob[1]->GetBufferPointer(), s_depth_copy_program_blob[1]->GetBufferSize() };
}
return bytecode;
}
D3D12_SHADER_BYTECODE StaticShaderCache::GetColorMatrixPixelShader(bool multisampled)
{
D3D12_SHADER_BYTECODE bytecode = {};
if (!multisampled || g_ActiveConfig.iMultisamples == 1)
{
bytecode = { s_color_matrix_program_blob[0]->GetBufferPointer(), s_color_matrix_program_blob[0]->GetBufferSize() };
}
else if (s_color_matrix_program_blob[1])
{
bytecode = { s_color_matrix_program_blob[1]->GetBufferPointer(), s_color_matrix_program_blob[1]->GetBufferSize() };
}
else
{
// create MSAA shader for current AA mode
std::string buf = StringFromFormat(s_color_matrix_program_msaa_hlsl, g_ActiveConfig.iMultisamples);
D3D::CompilePixelShader(buf, &s_color_matrix_program_blob[1]);
bytecode = { s_color_matrix_program_blob[1]->GetBufferPointer(), s_color_matrix_program_blob[1]->GetBufferSize() };
}
return bytecode;
}
D3D12_SHADER_BYTECODE StaticShaderCache::GetDepthMatrixPixelShader(bool multisampled)
{
D3D12_SHADER_BYTECODE bytecode = {};
if (!multisampled || g_ActiveConfig.iMultisamples == 1)
{
bytecode = { s_depth_matrix_program_blob[0]->GetBufferPointer(), s_depth_matrix_program_blob[0]->GetBufferSize() };
}
else if (s_depth_matrix_program_blob[1])
{
bytecode = { s_depth_matrix_program_blob[1]->GetBufferPointer(), s_depth_matrix_program_blob[1]->GetBufferSize() };
}
else
{
// create MSAA shader for current AA mode
std::string buf = StringFromFormat(s_depth_matrix_program_msaa_hlsl, g_ActiveConfig.iMultisamples);
D3D::CompilePixelShader(buf, &s_depth_matrix_program_blob[1]);
bytecode = { s_depth_matrix_program_blob[1]->GetBufferPointer(), s_depth_matrix_program_blob[1]->GetBufferSize() };
}
return bytecode;
}
D3D12_SHADER_BYTECODE StaticShaderCache::GetClearPixelShader()
{
D3D12_SHADER_BYTECODE shader = {};
shader.BytecodeLength = s_clear_program_blob->GetBufferSize();
shader.pShaderBytecode = s_clear_program_blob->GetBufferPointer();
return shader;
}
D3D12_SHADER_BYTECODE StaticShaderCache::GetAnaglyphPixelShader()
{
D3D12_SHADER_BYTECODE shader = {};
shader.BytecodeLength = s_anaglyph_program_blob->GetBufferSize();
shader.pShaderBytecode = s_anaglyph_program_blob->GetBufferPointer();
return shader;
}
D3D12_SHADER_BYTECODE StaticShaderCache::GetSimpleVertexShader()
{
D3D12_SHADER_BYTECODE shader = {};
shader.BytecodeLength = s_simple_vertex_shader_blob->GetBufferSize();
shader.pShaderBytecode = s_simple_vertex_shader_blob->GetBufferPointer();
return shader;
}
D3D12_SHADER_BYTECODE StaticShaderCache::GetClearVertexShader()
{
D3D12_SHADER_BYTECODE shader = {};
shader.BytecodeLength = s_simple_clear_vertex_shader_blob->GetBufferSize();
shader.pShaderBytecode = s_simple_clear_vertex_shader_blob->GetBufferPointer();
return shader;
}
D3D12_INPUT_LAYOUT_DESC StaticShaderCache::GetSimpleVertexShaderInputLayout()
{
return s_simple_vertex_shader_input_layout;
}
D3D12_INPUT_LAYOUT_DESC StaticShaderCache::GetClearVertexShaderInputLayout()
{
return s_clear_vertex_shader_input_layout;
}
D3D12_SHADER_BYTECODE StaticShaderCache::GetClearGeometryShader()
{
D3D12_SHADER_BYTECODE bytecode = {};
if (g_ActiveConfig.iStereoMode > 0)
{
bytecode.BytecodeLength = s_clear_geometry_shader_blob->GetBufferSize();
bytecode.pShaderBytecode = s_clear_geometry_shader_blob->GetBufferPointer();
}
return bytecode;
}
D3D12_SHADER_BYTECODE StaticShaderCache::GetCopyGeometryShader()
{
D3D12_SHADER_BYTECODE bytecode = {};
if (g_ActiveConfig.iStereoMode > 0)
{
bytecode.BytecodeLength = s_copy_geometry_shader_blob->GetBufferSize();
bytecode.pShaderBytecode = s_copy_geometry_shader_blob->GetBufferPointer();
}
return bytecode;
}
void StaticShaderCache::Init()
{
// Compile static pixel shaders
D3D::CompilePixelShader(s_clear_program_hlsl, &s_clear_program_blob);
D3D::CompilePixelShader(s_anaglyph_program_hlsl, &s_anaglyph_program_blob);
D3D::CompilePixelShader(s_color_copy_program_hlsl, &s_color_copy_program_blob[0]);
D3D::CompilePixelShader(s_depth_copy_program_hlsl, &s_depth_copy_program_blob[0]);
D3D::CompilePixelShader(s_color_matrix_program_hlsl, &s_color_matrix_program_blob[0]);
D3D::CompilePixelShader(s_depth_matrix_program_hlsl, &s_depth_matrix_program_blob[0]);
// Compile static vertex shaders
D3D::CompileVertexShader(s_simple_vertex_shader_hlsl, &s_simple_vertex_shader_blob);
D3D::CompileVertexShader(s_clear_vertex_shader_hlsl, &s_simple_clear_vertex_shader_blob);
// Compile static geometry shaders
D3D::CompileGeometryShader(s_clear_geometry_shader_hlsl, &s_clear_geometry_shader_blob);
D3D::CompileGeometryShader(s_copy_geometry_shader_hlsl, &s_copy_geometry_shader_blob);
}
// Call this when multisampling mode changes, and shaders need to be regenerated.
void StaticShaderCache::InvalidateMSAAShaders()
{
SAFE_RELEASE(s_color_copy_program_blob[1]);
SAFE_RELEASE(s_color_matrix_program_blob[1]);
SAFE_RELEASE(s_depth_matrix_program_blob[1]);
SAFE_RELEASE(s_rgb8_to_rgba6_program_blob[1]);
SAFE_RELEASE(s_rgba6_to_rgb8_program_blob[1]);
}
void StaticShaderCache::Shutdown()
{
// Free pixel shader blobs
SAFE_RELEASE(s_clear_program_blob);
SAFE_RELEASE(s_anaglyph_program_blob);
for (unsigned int i = 0; i < 2; ++i)
{
SAFE_RELEASE(s_color_copy_program_blob[i]);
SAFE_RELEASE(s_color_matrix_program_blob[i]);
SAFE_RELEASE(s_depth_matrix_program_blob[i]);
SAFE_RELEASE(s_rgba6_to_rgb8_program_blob[i]);
SAFE_RELEASE(s_rgb8_to_rgba6_program_blob[i]);
}
// Free vertex shader blobs
SAFE_RELEASE(s_simple_vertex_shader_blob);
SAFE_RELEASE(s_simple_clear_vertex_shader_blob);
// Free geometry shader blobs
SAFE_RELEASE(s_clear_geometry_shader_blob);
SAFE_RELEASE(s_copy_geometry_shader_blob);
}
}

View File

@ -0,0 +1,38 @@
// Copyright 2015 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
namespace DX12
{
class StaticShaderCache final
{
public:
static void Init();
static void InvalidateMSAAShaders();
static void Shutdown();
// Pixel shaders
static D3D12_SHADER_BYTECODE GetColorMatrixPixelShader(bool multisampled);
static D3D12_SHADER_BYTECODE GetColorCopyPixelShader(bool multisampled);
static D3D12_SHADER_BYTECODE GetDepthMatrixPixelShader(bool multisampled);
static D3D12_SHADER_BYTECODE GetDepthCopyPixelShader(bool multisampled);
static D3D12_SHADER_BYTECODE GetClearPixelShader();
static D3D12_SHADER_BYTECODE GetAnaglyphPixelShader();
static D3D12_SHADER_BYTECODE GetReinterpRGBA6ToRGB8PixelShader(bool multisampled);
static D3D12_SHADER_BYTECODE GetReinterpRGB8ToRGBA6PixelShader(bool multisampled);
// Vertex shaders
static D3D12_SHADER_BYTECODE GetSimpleVertexShader();
static D3D12_SHADER_BYTECODE GetClearVertexShader();
static D3D12_INPUT_LAYOUT_DESC GetSimpleVertexShaderInputLayout();
static D3D12_INPUT_LAYOUT_DESC GetClearVertexShaderInputLayout();
// Geometry shaders
static D3D12_SHADER_BYTECODE GetClearGeometryShader();
static D3D12_SHADER_BYTECODE GetCopyGeometryShader();
};
}

View File

@ -0,0 +1,45 @@
// Copyright 2011 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include <vector>
#include "Core/HW/Memmap.h"
#include "VideoBackends/D3D12/D3DBase.h"
#include "VideoBackends/D3D12/D3DShader.h"
#include "VideoBackends/D3D12/D3DState.h"
#include "VideoBackends/D3D12/D3DUtil.h"
#include "VideoBackends/D3D12/Television.h"
#include "VideoCommon/VideoConfig.h"
// D3D12TODO: Add DX12 path for this file.
namespace DX12
{
Television::Television()
{
// D3D12TODO: Add DX12 path for this file.
}
void Television::Init()
{
// D3D12TODO: Add DX12 path for this file.
}
void Television::Shutdown()
{
// D3D12TODO: Add DX12 path for this file.
}
void Television::Submit(u32 xfb_address, u32 stride, u32 width, u32 height)
{
// D3D12TODO: Add DX12 path for this file.
}
void Television::Render()
{
// D3D12TODO: Add DX12 path for this file.
}
}

View File

@ -0,0 +1,37 @@
// Copyright 2011 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include "VideoCommon/VideoCommon.h"
// D3D12TODO: Add DX12 path for this file.
namespace DX12
{
class Television final
{
public:
Television();
void Init();
void Shutdown();
// Submit video data to be drawn. This will change the current state of the
// TV. xfbAddr points to YUYV data stored in GameCube/Wii RAM, but the XFB
// may be virtualized when rendering so the RAM may not actually be read.
void Submit(u32 xfb_address, u32 stride, u32 width, u32 height);
// Render the current state of the TV.
void Render();
private:
};
}

View File

@ -0,0 +1,672 @@
// Copyright 2010 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "VideoBackends/D3D12/D3DBase.h"
#include "VideoBackends/D3D12/D3DCommandListManager.h"
#include "VideoBackends/D3D12/D3DDescriptorHeapManager.h"
#include "VideoBackends/D3D12/D3DShader.h"
#include "VideoBackends/D3D12/D3DState.h"
#include "VideoBackends/D3D12/D3DStreamBuffer.h"
#include "VideoBackends/D3D12/D3DUtil.h"
#include "VideoBackends/D3D12/FramebufferManager.h"
#include "VideoBackends/D3D12/PSTextureEncoder.h"
#include "VideoBackends/D3D12/StaticShaderCache.h"
#include "VideoBackends/D3D12/TextureCache.h"
#include "VideoBackends/D3D12/TextureEncoder.h"
#include "VideoCommon/ImageWrite.h"
#include "VideoCommon/LookUpTables.h"
#include "VideoCommon/RenderBase.h"
#include "VideoCommon/VideoConfig.h"
namespace DX12
{
static std::unique_ptr<TextureEncoder> s_encoder = nullptr;
static std::unique_ptr<D3DStreamBuffer> s_efb_copy_stream_buffer = nullptr;
static ID3D12Resource* s_texture_cache_entry_readback_buffer = nullptr;
static void* s_texture_cache_entry_readback_buffer_data = nullptr;
static UINT s_texture_cache_entry_readback_buffer_size = 0;
TextureCache::TCacheEntry::~TCacheEntry()
{
m_texture->Release();
}
void TextureCache::TCacheEntry::Bind(unsigned int stage)
{
// Textures bound as group in TextureCache::BindTextures method.
}
bool TextureCache::TCacheEntry::Save(const std::string& filename, unsigned int level)
{
// EXISTINGD3D11TODO: Somehow implement this (D3DX11 doesn't support dumping individual LODs)
static bool warn_once = true;
if (level && warn_once)
{
WARN_LOG(VIDEO, "Dumping individual LOD not supported by D3D12 backend!");
warn_once = false;
return false;
}
D3D12_RESOURCE_DESC texture_desc = m_texture->GetTex12()->GetDesc();
const unsigned int required_readback_buffer_size = D3D::AlignValue(static_cast<unsigned int>(texture_desc.Width) * 4, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
if (s_texture_cache_entry_readback_buffer_size < required_readback_buffer_size)
{
s_texture_cache_entry_readback_buffer_size = required_readback_buffer_size;
// We know the readback buffer won't be in use right now, since we wait on this thread
// for the GPU to finish execution right after copying to it.
SAFE_RELEASE(s_texture_cache_entry_readback_buffer);
}
if (!s_texture_cache_entry_readback_buffer_size)
{
CheckHR(
D3D::device12->CreateCommittedResource(
&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_READBACK),
D3D12_HEAP_FLAG_NONE,
&CD3DX12_RESOURCE_DESC::Buffer(s_texture_cache_entry_readback_buffer_size),
D3D12_RESOURCE_STATE_COPY_DEST,
nullptr,
IID_PPV_ARGS(&s_texture_cache_entry_readback_buffer)
)
);
CheckHR(s_texture_cache_entry_readback_buffer->Map(0, nullptr, &s_texture_cache_entry_readback_buffer_data));
}
bool saved_png = false;
m_texture->TransitionToResourceState(D3D::current_command_list, D3D12_RESOURCE_STATE_COPY_SOURCE);
D3D12_TEXTURE_COPY_LOCATION dst_location = {};
dst_location.pResource = s_texture_cache_entry_readback_buffer;
dst_location.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
dst_location.PlacedFootprint.Offset = 0;
dst_location.PlacedFootprint.Footprint.Depth = 1;
dst_location.PlacedFootprint.Footprint.Format = texture_desc.Format;
dst_location.PlacedFootprint.Footprint.Width = static_cast<UINT>(texture_desc.Width);
dst_location.PlacedFootprint.Footprint.Height = texture_desc.Height;
dst_location.PlacedFootprint.Footprint.RowPitch = D3D::AlignValue(dst_location.PlacedFootprint.Footprint.Width * 4, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
D3D12_TEXTURE_COPY_LOCATION src_location = CD3DX12_TEXTURE_COPY_LOCATION(m_texture->GetTex12(), 0);
D3D::current_command_list->CopyTextureRegion(&dst_location, 0, 0, 0, &src_location, nullptr);
D3D::command_list_mgr->ExecuteQueuedWork(true);
saved_png = TextureToPng(
static_cast<u8*>(s_texture_cache_entry_readback_buffer_data),
dst_location.PlacedFootprint.Footprint.RowPitch,
filename,
dst_location.PlacedFootprint.Footprint.Width,
dst_location.PlacedFootprint.Footprint.Height
);
return saved_png;
}
void TextureCache::TCacheEntry::CopyRectangleFromTexture(
const TCacheEntryBase* source,
const MathUtil::Rectangle<int>& src_rect,
const MathUtil::Rectangle<int>& dst_rect)
{
const TCacheEntry* srcentry = reinterpret_cast<const TCacheEntry*>(source);
if (src_rect.GetWidth() == dst_rect.GetWidth()
&& src_rect.GetHeight() == dst_rect.GetHeight())
{
const D3D12_BOX* src_box_pointer = nullptr;
D3D12_BOX src_box;
if (src_rect.left != 0 || src_rect.top != 0)
{
src_box.front = 0;
src_box.back = 1;
src_box.left = src_rect.left;
src_box.top = src_rect.top;
src_box.right = src_rect.right;
src_box.bottom = src_rect.bottom;
src_box_pointer = &src_box;
}
if (static_cast<u32>(src_rect.GetHeight()) > config.height ||
static_cast<u32>(src_rect.GetWidth()) > config.width)
{
// To mimic D3D11 behavior, we're just going to drop the clear since it is invalid.
// This invalid copy needs to be fixed above the Backend level.
// On D3D12, instead of silently dropping this invalid clear, the runtime throws an exception
// so we need to filter it out ourselves.
return;
}
D3D12_TEXTURE_COPY_LOCATION dst_location = CD3DX12_TEXTURE_COPY_LOCATION(m_texture->GetTex12(), 0);
D3D12_TEXTURE_COPY_LOCATION src_location = CD3DX12_TEXTURE_COPY_LOCATION(srcentry->m_texture->GetTex12(), 0);
m_texture->TransitionToResourceState(D3D::current_command_list, D3D12_RESOURCE_STATE_COPY_DEST);
srcentry->m_texture->TransitionToResourceState(D3D::current_command_list, D3D12_RESOURCE_STATE_COPY_SOURCE);
D3D::current_command_list->CopyTextureRegion(&dst_location, dst_rect.left, dst_rect.top, 0, &src_location, src_box_pointer);
m_texture->TransitionToResourceState(D3D::current_command_list, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
srcentry->m_texture->TransitionToResourceState(D3D::current_command_list, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
return;
}
else if (!config.rendertarget)
{
return;
}
const D3D12_VIEWPORT vp = {
float(dst_rect.left),
float(dst_rect.top),
float(dst_rect.GetWidth()),
float(dst_rect.GetHeight()),
D3D12_MIN_DEPTH,
D3D12_MAX_DEPTH
};
D3D::current_command_list->RSSetViewports(1, &vp);
m_texture->TransitionToResourceState(D3D::current_command_list, D3D12_RESOURCE_STATE_RENDER_TARGET);
D3D::current_command_list->OMSetRenderTargets(1, &m_texture->GetRTV12(), FALSE, nullptr);
D3D::SetLinearCopySampler();
D3D12_RECT src_rc;
src_rc.left = src_rect.left;
src_rc.right = src_rect.right;
src_rc.top = src_rect.top;
src_rc.bottom = src_rect.bottom;
D3D::DrawShadedTexQuad(srcentry->m_texture, &src_rc,
srcentry->config.width, srcentry->config.height,
StaticShaderCache::GetColorCopyPixelShader(false),
StaticShaderCache::GetSimpleVertexShader(),
StaticShaderCache::GetSimpleVertexShaderInputLayout(), D3D12_SHADER_BYTECODE(), 1.0, 0,
DXGI_FORMAT_R8G8B8A8_UNORM, false, m_texture->GetMultisampled());
m_texture->TransitionToResourceState(D3D::current_command_list, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
FramebufferManager::GetEFBColorTexture()->TransitionToResourceState(D3D::current_command_list, D3D12_RESOURCE_STATE_RENDER_TARGET);
FramebufferManager::GetEFBDepthTexture()->TransitionToResourceState(D3D::current_command_list, D3D12_RESOURCE_STATE_DEPTH_WRITE);
D3D::current_command_list->OMSetRenderTargets(1, &FramebufferManager::GetEFBColorTexture()->GetRTV12(), FALSE, &FramebufferManager::GetEFBDepthTexture()->GetDSV12());
g_renderer->RestoreAPIState();
}
void TextureCache::TCacheEntry::Load(unsigned int width, unsigned int height,
unsigned int expanded_width, unsigned int level)
{
unsigned int src_pitch = 4 * expanded_width;
D3D::ReplaceRGBATexture2D(m_texture->GetTex12(), TextureCache::temp, width, height, src_pitch, level, m_texture->GetResourceUsageState());
}
TextureCacheBase::TCacheEntryBase* TextureCache::CreateTexture(const TCacheEntryConfig& config)
{
if (config.rendertarget)
{
D3DTexture2D* texture = D3DTexture2D::Create(config.width, config.height,
static_cast<D3D11_BIND_FLAG>((static_cast<int>(D3D11_BIND_RENDER_TARGET) | static_cast<int>(D3D11_BIND_SHADER_RESOURCE))),
D3D11_USAGE_DEFAULT, DXGI_FORMAT_R8G8B8A8_UNORM, 1, config.layers);
TCacheEntry* entry = new TCacheEntry(config, texture);
entry->m_texture_srv_cpu_handle = texture->GetSRV12CPU();
entry->m_texture_srv_gpu_handle = texture->GetSRV12GPU();
entry->m_texture_srv_gpu_handle_cpu_shadow = texture->GetSRV12GPUCPUShadow();
return entry;
}
else
{
ID3D12Resource* texture_resource = nullptr;
D3D12_RESOURCE_DESC texture_resource_desc = CD3DX12_RESOURCE_DESC::Tex2D(DXGI_FORMAT_R8G8B8A8_UNORM,
config.width, config.height, 1, config.levels);
CheckHR(
D3D::device12->CreateCommittedResource(
&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
D3D12_HEAP_FLAG_NONE,
&CD3DX12_RESOURCE_DESC(texture_resource_desc),
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
nullptr,
IID_PPV_ARGS(&texture_resource)
)
);
D3DTexture2D* texture = new D3DTexture2D(
texture_resource,
D3D11_BIND_SHADER_RESOURCE,
DXGI_FORMAT_UNKNOWN,
DXGI_FORMAT_UNKNOWN,
DXGI_FORMAT_UNKNOWN,
false,
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE
);
TCacheEntry* const entry = new TCacheEntry(
config, texture
);
entry->m_texture_srv_cpu_handle = texture->GetSRV12CPU();
entry->m_texture_srv_gpu_handle = texture->GetSRV12GPU();
entry->m_texture_srv_gpu_handle_cpu_shadow = texture->GetSRV12GPUCPUShadow();
// EXISTINGD3D11TODO: better debug names
D3D::SetDebugObjectName12(entry->m_texture->GetTex12(), "a texture of the TextureCache");
SAFE_RELEASE(texture_resource);
return entry;
}
}
void TextureCache::TCacheEntry::FromRenderTarget(u8* dst, PEControl::PixelFormat src_format, const EFBRectangle& srcRect,
bool scale_by_half, unsigned int cbuf_id, const float* colmat)
{
static unsigned int old_cbuf_id = UINT_MAX;
// When copying at half size, in multisampled mode, resolve the color/depth buffer first.
// This is because multisampled texture reads go through Load, not Sample, and the linear
// filter is ignored.
bool multisampled = (g_ActiveConfig.iMultisamples > 1);
D3DTexture2D* efb_tex = (src_format == PEControl::Z24) ?
FramebufferManager::GetEFBDepthTexture() :
FramebufferManager::GetEFBColorTexture();
if (multisampled && scale_by_half)
{
multisampled = false;
efb_tex = (src_format == PEControl::Z24) ?
FramebufferManager::GetResolvedEFBDepthTexture() :
FramebufferManager::GetResolvedEFBColorTexture();
}
// stretch picture with increased internal resolution
const D3D12_VIEWPORT vp = {
0.f,
0.f,
static_cast<float>(config.width),
static_cast<float>(config.height),
D3D12_MIN_DEPTH,
D3D12_MAX_DEPTH
};
D3D::current_command_list->RSSetViewports(1, &vp);
// set transformation
if (cbuf_id != old_cbuf_id)
{
s_efb_copy_stream_buffer->AllocateSpaceInBuffer(28 * sizeof(float), 256);
memcpy(s_efb_copy_stream_buffer->GetCPUAddressOfCurrentAllocation(), colmat, 28 * sizeof(float));
old_cbuf_id = cbuf_id;
}
D3D::current_command_list->SetGraphicsRootConstantBufferView(DESCRIPTOR_TABLE_PS_CBVONE, s_efb_copy_stream_buffer->GetGPUAddressOfCurrentAllocation());
D3D::command_list_mgr->SetCommandListDirtyState(COMMAND_LIST_STATE_PS_CBV, true);
const TargetRectangle targetSource = g_renderer->ConvertEFBRectangle(srcRect);
// EXISTINGD3D11TODO: try targetSource.asRECT();
const D3D12_RECT sourcerect = CD3DX12_RECT(targetSource.left, targetSource.top, targetSource.right, targetSource.bottom);
// Use linear filtering if (bScaleByHalf), use point filtering otherwise
if (scale_by_half)
D3D::SetLinearCopySampler();
else
D3D::SetPointCopySampler();
// Make sure we don't draw with the texture set as both a source and target.
// (This can happen because we don't unbind textures when we free them.)
m_texture->TransitionToResourceState(D3D::current_command_list, D3D12_RESOURCE_STATE_RENDER_TARGET);
D3D::current_command_list->OMSetRenderTargets(1, &m_texture->GetRTV12(), FALSE, nullptr);
// Create texture copy
D3D::DrawShadedTexQuad(
efb_tex,
&sourcerect,
Renderer::GetTargetWidth(),
Renderer::GetTargetHeight(),
(src_format == PEControl::Z24) ? StaticShaderCache::GetDepthMatrixPixelShader(multisampled) : StaticShaderCache::GetColorMatrixPixelShader(multisampled),
StaticShaderCache::GetSimpleVertexShader(),
StaticShaderCache::GetSimpleVertexShaderInputLayout(),
StaticShaderCache::GetCopyGeometryShader(),
1.0f, 0, DXGI_FORMAT_R8G8B8A8_UNORM, false, m_texture->GetMultisampled()
);
m_texture->TransitionToResourceState(D3D::current_command_list, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
FramebufferManager::GetEFBColorTexture()->TransitionToResourceState(D3D::current_command_list, D3D12_RESOURCE_STATE_RENDER_TARGET);
FramebufferManager::GetEFBDepthTexture()->TransitionToResourceState(D3D::current_command_list, D3D12_RESOURCE_STATE_DEPTH_WRITE);
D3D::current_command_list->OMSetRenderTargets(1, &FramebufferManager::GetEFBColorTexture()->GetRTV12(), FALSE, &FramebufferManager::GetEFBDepthTexture()->GetDSV12());
g_renderer->RestoreAPIState();
}
void TextureCache::CopyEFB(u8* dst, u32 format, u32 native_width, u32 bytes_per_row, u32 num_blocks_y, u32 memory_stride,
PEControl::PixelFormat srcFormat, const EFBRectangle& srcRect,
bool isIntensity, bool scaleByHalf)
{
s_encoder->Encode(dst, format, native_width, bytes_per_row, num_blocks_y, memory_stride, srcFormat, srcRect, isIntensity, scaleByHalf);
}
static const constexpr char s_palette_shader_hlsl[] =
R"HLSL(
sampler samp0 : register(s0);
Texture2DArray Tex0 : register(t0);
Buffer<uint> Tex1 : register(t1);
uniform float Multiply;
uint Convert3To8(uint v)
{
// Swizzle bits: 00000123 -> 12312312
return (v << 5) | (v << 2) | (v >> 1);
}
uint Convert4To8(uint v)
{
// Swizzle bits: 00001234 -> 12341234
return (v << 4) | v;
}
uint Convert5To8(uint v)
{
// Swizzle bits: 00012345 -> 12345123
return (v << 3) | (v >> 2);
}
uint Convert6To8(uint v)
{
// Swizzle bits: 00123456 -> 12345612
return (v << 2) | (v >> 4);
}
float4 DecodePixel_RGB5A3(uint val)
{
int r,g,b,a;
if ((val&0x8000))
{
r=Convert5To8((val>>10) & 0x1f);
g=Convert5To8((val>>5 ) & 0x1f);
b=Convert5To8((val ) & 0x1f);
a=0xFF;
}
else
{
a=Convert3To8((val>>12) & 0x7);
r=Convert4To8((val>>8 ) & 0xf);
g=Convert4To8((val>>4 ) & 0xf);
b=Convert4To8((val ) & 0xf);
}
return float4(r, g, b, a) / 255;
}
float4 DecodePixel_RGB565(uint val)
{
int r, g, b, a;
r = Convert5To8((val >> 11) & 0x1f);
g = Convert6To8((val >> 5) & 0x3f);
b = Convert5To8((val) & 0x1f);
a = 0xFF;
return float4(r, g, b, a) / 255;
}
float4 DecodePixel_IA8(uint val)
{
int i = val & 0xFF;
int a = val >> 8;
return float4(i, i, i, a) / 255;
}
void main(
out float4 ocol0 : SV_Target,
in float4 pos : SV_Position,
in float3 uv0 : TEXCOORD0)
{
uint src = round(Tex0.Sample(samp0,uv0) * Multiply).r;
src = Tex1.Load(src);
src = ((src << 8) & 0xFF00) | (src >> 8);
ocol0 = DECODE(src);
}
)HLSL";
void TextureCache::ConvertTexture(TCacheEntryBase* entry, TCacheEntryBase* unconverted, void* palette, TlutFormat format)
{
// stretch picture with increased internal resolution
const D3D12_VIEWPORT vp = { 0.f, 0.f, static_cast<float>(unconverted->config.width), static_cast<float>(unconverted->config.height), D3D12_MIN_DEPTH, D3D12_MAX_DEPTH };
D3D::current_command_list->RSSetViewports(1, &vp);
const unsigned int palette_buffer_allocation_size = 512;
m_palette_stream_buffer->AllocateSpaceInBuffer(palette_buffer_allocation_size, 256);
memcpy(m_palette_stream_buffer->GetCPUAddressOfCurrentAllocation(), palette, palette_buffer_allocation_size);
// D3D12: Because the second SRV slot is occupied by this buffer, and an arbitrary texture occupies the first SRV slot,
// we need to allocate temporary space out of our descriptor heap, place the palette SRV in the second slot, then copy the
// existing texture's descriptor into the first slot.
// First, allocate the (temporary) space in the descriptor heap.
D3D12_CPU_DESCRIPTOR_HANDLE srv_group_cpu_handle[2] = {};
D3D12_GPU_DESCRIPTOR_HANDLE srv_group_gpu_handle[2] = {};
D3D::gpu_descriptor_heap_mgr->AllocateGroup(srv_group_cpu_handle, 2, srv_group_gpu_handle, nullptr, true);
srv_group_cpu_handle[1].ptr = srv_group_cpu_handle[0].ptr + D3D::resource_descriptor_size;
// Now, create the palette SRV at the appropriate offset.
D3D12_SHADER_RESOURCE_VIEW_DESC palette_buffer_srv_desc = {
DXGI_FORMAT_R16_UINT, // DXGI_FORMAT Format;
D3D12_SRV_DIMENSION_BUFFER, // D3D12_SRV_DIMENSION ViewDimension;
D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING // UINT Shader4ComponentMapping;
};
// Each 'element' is two bytes since format is R16.
palette_buffer_srv_desc.Buffer.FirstElement = m_palette_stream_buffer->GetOffsetOfCurrentAllocation() / sizeof(u16);
palette_buffer_srv_desc.Buffer.NumElements = 256;
D3D::device12->CreateShaderResourceView(m_palette_stream_buffer->GetBuffer(), &palette_buffer_srv_desc, srv_group_cpu_handle[1]);
// Now, copy the existing texture's descriptor into the new temporary location.
static_cast<TCacheEntry*>(unconverted)->m_texture->TransitionToResourceState(D3D::current_command_list, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
D3D::device12->CopyDescriptorsSimple(
1,
srv_group_cpu_handle[0],
static_cast<TCacheEntry*>(unconverted)->m_texture->GetSRV12GPUCPUShadow(),
D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV
);
// Finally, bind our temporary location.
D3D::current_command_list->SetGraphicsRootDescriptorTable(DESCRIPTOR_TABLE_PS_SRV, srv_group_gpu_handle[0]);
// D3D11EXISTINGTODO: Add support for C14X2 format. (Different multiplier, more palette entries.)
// D3D12: See TextureCache::TextureCache() - because there are only two possible buffer contents here,
// just pre-populate the data in two parts of the same upload heap.
if ((unconverted->format & 0xf) == GX_TF_I4)
{
D3D::current_command_list->SetGraphicsRootConstantBufferView(DESCRIPTOR_TABLE_PS_CBVONE, m_palette_uniform_buffer->GetGPUVirtualAddress());
}
else
{
D3D::current_command_list->SetGraphicsRootConstantBufferView(DESCRIPTOR_TABLE_PS_CBVONE, m_palette_uniform_buffer->GetGPUVirtualAddress() + 256);
}
D3D::command_list_mgr->SetCommandListDirtyState(COMMAND_LIST_STATE_PS_CBV, true);
const D3D12_RECT source_rect = CD3DX12_RECT(0, 0, unconverted->config.width, unconverted->config.height);
D3D::SetPointCopySampler();
// Make sure we don't draw with the texture set as both a source and target.
// (This can happen because we don't unbind textures when we free them.)
static_cast<TCacheEntry*>(entry)->m_texture->TransitionToResourceState(D3D::current_command_list, D3D12_RESOURCE_STATE_RENDER_TARGET);
D3D::current_command_list->OMSetRenderTargets(1, &static_cast<TCacheEntry*>(entry)->m_texture->GetRTV12(), FALSE, nullptr);
// Create texture copy
D3D::DrawShadedTexQuad(
static_cast<TCacheEntry*>(unconverted)->m_texture,
&source_rect, unconverted->config.width,
unconverted->config.height,
m_palette_pixel_shaders[format],
StaticShaderCache::GetSimpleVertexShader(),
StaticShaderCache::GetSimpleVertexShaderInputLayout(),
StaticShaderCache::GetCopyGeometryShader(),
1.0f,
0,
DXGI_FORMAT_R8G8B8A8_UNORM,
true,
static_cast<TCacheEntry*>(entry)->m_texture->GetMultisampled()
);
static_cast<TCacheEntry*>(entry)->m_texture->TransitionToResourceState(D3D::current_command_list, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
FramebufferManager::GetEFBColorTexture()->TransitionToResourceState(D3D::current_command_list, D3D12_RESOURCE_STATE_RENDER_TARGET);
FramebufferManager::GetEFBDepthTexture()->TransitionToResourceState(D3D::current_command_list, D3D12_RESOURCE_STATE_DEPTH_WRITE );
D3D::current_command_list->OMSetRenderTargets(1, &FramebufferManager::GetEFBColorTexture()->GetRTV12(), FALSE, &FramebufferManager::GetEFBDepthTexture()->GetDSV12());
g_renderer->RestoreAPIState();
}
D3D12_SHADER_BYTECODE GetConvertShader12(std::string& Type)
{
std::string shader = "#define DECODE DecodePixel_";
shader.append(Type);
shader.append("\n");
shader.append(s_palette_shader_hlsl);
ID3DBlob* blob = nullptr;
D3D::CompilePixelShader(shader, &blob);
return { blob->GetBufferPointer(), blob->GetBufferSize() };
}
TextureCache::TextureCache()
{
s_encoder = std::make_unique<PSTextureEncoder>();
s_encoder->Init();
s_efb_copy_stream_buffer = std::make_unique<D3DStreamBuffer>(1024 * 1024, 1024 * 1024, nullptr);
s_texture_cache_entry_readback_buffer = nullptr;
s_texture_cache_entry_readback_buffer_data = nullptr;
s_texture_cache_entry_readback_buffer_size = 0;
m_palette_pixel_shaders[GX_TL_IA8] = GetConvertShader12(std::string("IA8"));
m_palette_pixel_shaders[GX_TL_RGB565] = GetConvertShader12(std::string("RGB565"));
m_palette_pixel_shaders[GX_TL_RGB5A3] = GetConvertShader12(std::string("RGB5A3"));
m_palette_stream_buffer = new D3DStreamBuffer(sizeof(u16) * 256 * 1024, sizeof(u16) * 256 * 1024 * 16, nullptr);
// Right now, there are only two variants of palette_uniform data. So, we'll just create an upload heap to permanently store both of these.
CheckHR(
D3D::device12->CreateCommittedResource(
&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
D3D12_HEAP_FLAG_NONE,
&CD3DX12_RESOURCE_DESC::Buffer(((16 + 255) & ~255) * 2), // Constant Buffers have to be 256b aligned. "* 2" to create for two sets of data.
D3D12_RESOURCE_STATE_COPY_DEST,
nullptr,
IID_PPV_ARGS(&m_palette_uniform_buffer)
)
);
D3D::SetDebugObjectName12(m_palette_uniform_buffer, "a constant buffer used in TextureCache::ConvertTexture");
// Temporarily repurpose m_palette_stream_buffer as a copy source to populate initial data here.
m_palette_stream_buffer->AllocateSpaceInBuffer(256 * 2, 256);
u8* upload_heap_data_location = reinterpret_cast<u8*>(m_palette_stream_buffer->GetCPUAddressOfCurrentAllocation());
memset(upload_heap_data_location, 0, 256 * 2);
float paramsFormatZero[4] = { 15.f };
float paramsFormatNonzero[4] = { 255.f };
memcpy(upload_heap_data_location, paramsFormatZero, sizeof(paramsFormatZero));
memcpy(upload_heap_data_location + 256, paramsFormatNonzero, sizeof(paramsFormatNonzero));
D3D::current_command_list->CopyBufferRegion(m_palette_uniform_buffer, 0, m_palette_stream_buffer->GetBuffer(), m_palette_stream_buffer->GetOffsetOfCurrentAllocation(), 256 * 2);
DX12::D3D::ResourceBarrier(D3D::current_command_list, m_palette_uniform_buffer, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER, 0);
}
TextureCache::~TextureCache()
{
s_encoder->Shutdown();
s_encoder.reset();
s_efb_copy_stream_buffer.reset();
SAFE_DELETE(m_palette_stream_buffer);
if (s_texture_cache_entry_readback_buffer)
{
D3D::command_list_mgr->DestroyResourceAfterCurrentCommandListExecuted(s_texture_cache_entry_readback_buffer);
s_texture_cache_entry_readback_buffer = nullptr;
}
D3D::command_list_mgr->DestroyResourceAfterCurrentCommandListExecuted(m_palette_uniform_buffer);
}
void TextureCache::BindTextures()
{
unsigned int last_texture = 0;
for (unsigned int i = 0; i < 8; ++i)
{
if (bound_textures[i] != nullptr)
{
last_texture = i;
}
}
if (last_texture == 0 && bound_textures[0] != nullptr)
{
DX12::D3D::current_command_list->SetGraphicsRootDescriptorTable(DESCRIPTOR_TABLE_PS_SRV, reinterpret_cast<TCacheEntry*>(bound_textures[0])->m_texture_srv_gpu_handle);
return;
}
// If more than one texture, allocate space for group.
D3D12_CPU_DESCRIPTOR_HANDLE s_group_base_texture_cpu_handle;
D3D12_GPU_DESCRIPTOR_HANDLE s_group_base_texture_gpu_handle;
DX12::D3D::gpu_descriptor_heap_mgr->AllocateGroup(&s_group_base_texture_cpu_handle, 8, &s_group_base_texture_gpu_handle, nullptr, true);
for (unsigned int stage = 0; stage <= last_texture; stage++)
{
if (bound_textures[stage] != nullptr)
{
D3D12_CPU_DESCRIPTOR_HANDLE textureDestDescriptor;
textureDestDescriptor.ptr = s_group_base_texture_cpu_handle.ptr + stage * D3D::resource_descriptor_size;
DX12::D3D::device12->CopyDescriptorsSimple(
1,
textureDestDescriptor,
reinterpret_cast<TCacheEntry*>(bound_textures[stage])->m_texture_srv_gpu_handle_cpu_shadow,
D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV
);
}
else
{
D3D12_CPU_DESCRIPTOR_HANDLE nullDestDescriptor;
nullDestDescriptor.ptr = s_group_base_texture_cpu_handle.ptr + stage * D3D::resource_descriptor_size;
DX12::D3D::device12->CopyDescriptorsSimple(
1,
nullDestDescriptor,
DX12::D3D::null_srv_cpu_shadow,
D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV
);
}
}
// Actually bind the textures.
DX12::D3D::current_command_list->SetGraphicsRootDescriptorTable(DESCRIPTOR_TABLE_PS_SRV, s_group_base_texture_gpu_handle);
}
}

View File

@ -0,0 +1,68 @@
// Copyright 2008 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include "VideoBackends/D3D12/D3DTexture.h"
#include "VideoCommon/TextureCacheBase.h"
namespace DX12
{
class D3DStreamBuffer;
class TextureCache final : public TextureCacheBase
{
public:
TextureCache();
~TextureCache();
virtual void BindTextures();
private:
struct TCacheEntry : TCacheEntryBase
{
D3DTexture2D* const m_texture = nullptr;
D3D12_CPU_DESCRIPTOR_HANDLE m_texture_srv_cpu_handle = {};
D3D12_GPU_DESCRIPTOR_HANDLE m_texture_srv_gpu_handle = {};
D3D12_CPU_DESCRIPTOR_HANDLE m_texture_srv_gpu_handle_cpu_shadow = {};
TCacheEntry(const TCacheEntryConfig& config, D3DTexture2D* tex) : TCacheEntryBase(config), m_texture(tex) {}
~TCacheEntry();
void CopyRectangleFromTexture(
const TCacheEntryBase* source,
const MathUtil::Rectangle<int> &src_rect,
const MathUtil::Rectangle<int> &dst_rect) override;
void Load(unsigned int width, unsigned int height,
unsigned int expanded_width, unsigned int levels) override;
void FromRenderTarget(u8* dst, PEControl::PixelFormat src_format, const EFBRectangle& src_rect,
bool scale_by_half, unsigned int cbuf_id, const float* colmat) override;
void Bind(unsigned int stage) override;
bool Save(const std::string& filename, unsigned int level) override;
};
TCacheEntryBase* CreateTexture(const TCacheEntryConfig& config) override;
u64 EncodeToRamFromTexture(u32 address, void* source_texture, u32 source_width, u32 source_height, bool is_from_z_buffer, bool is_intensity_format, u32 copy_format, int scale_by_half, const EFBRectangle& source) {return 0;};
void ConvertTexture(TCacheEntryBase* entry, TCacheEntryBase* unconverted, void* palette, TlutFormat format) override;
void CopyEFB(u8* dst, u32 format, u32 native_width, u32 bytes_per_row, u32 num_blocks_y, u32 memory_stride,
PEControl::PixelFormat src_format, const EFBRectangle& src_rect,
bool is_intensity, bool scale_by_half) override;
void CompileShaders() override { }
void DeleteShaders() override { }
D3DStreamBuffer* m_palette_stream_buffer = nullptr;
ID3D12Resource* m_palette_uniform_buffer = nullptr;
D3D12_SHADER_BYTECODE m_palette_pixel_shaders[3] = {};
};
}

View File

@ -0,0 +1,32 @@
// Copyright 2011 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include "VideoCommon/BPMemory.h"
#include "VideoCommon/TextureCacheBase.h"
#include "VideoCommon/VideoCommon.h"
namespace DX12
{
// Maximum number of bytes that can occur in a texture block-row generated by
// the encoder
static constexpr unsigned int MAX_BYTES_PER_BLOCK_ROW = (EFB_WIDTH / 4) * 64;
// The maximum amount of data that the texture encoder can generate in one call
static constexpr unsigned int MAX_BYTES_PER_ENCODE = MAX_BYTES_PER_BLOCK_ROW * (EFB_HEIGHT / 4);
class TextureEncoder
{
public:
virtual ~TextureEncoder() { }
virtual void Init() = 0;
virtual void Shutdown() = 0;
// Returns size in bytes of encoded block of memory
virtual void Encode(u8* dst, u32 format, u32 native_width, u32 bytes_per_row, u32 num_blocks_y, u32 memory_stride,
PEControl::PixelFormat src_format, const EFBRectangle& src_rect,
bool is_intensity, bool scale_by_half) = 0;
};
}

View File

@ -0,0 +1,218 @@
// Copyright 2010 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "Common/CommonTypes.h"
#include "VideoBackends/D3D12/BoundingBox.h"
#include "VideoBackends/D3D12/D3DBase.h"
#include "VideoBackends/D3D12/D3DCommandListManager.h"
#include "VideoBackends/D3D12/D3DState.h"
#include "VideoBackends/D3D12/D3DStreamBuffer.h"
#include "VideoBackends/D3D12/FramebufferManager.h"
#include "VideoBackends/D3D12/Render.h"
#include "VideoBackends/D3D12/ShaderCache.h"
#include "VideoBackends/D3D12/VertexManager.h"
#include "VideoCommon/BoundingBox.h"
#include "VideoCommon/Debugger.h"
#include "VideoCommon/IndexGenerator.h"
#include "VideoCommon/RenderBase.h"
#include "VideoCommon/Statistics.h"
#include "VideoCommon/VertexLoaderManager.h"
#include "VideoCommon/VideoConfig.h"
namespace DX12
{
static constexpr unsigned int MAX_IBUFFER_SIZE = VertexManager::MAXIBUFFERSIZE * sizeof(u16) * 16;
static constexpr unsigned int MAX_VBUFFER_SIZE = VertexManager::MAXVBUFFERSIZE * 4;
void VertexManager::SetIndexBuffer()
{
D3D12_INDEX_BUFFER_VIEW ib_view = {
m_index_stream_buffer->GetBaseGPUAddress(), // D3D12_GPU_VIRTUAL_ADDRESS BufferLocation;
m_index_stream_buffer->GetSize(), // UINT SizeInBytes;
DXGI_FORMAT_R16_UINT // DXGI_FORMAT Format;
};
D3D::current_command_list->IASetIndexBuffer(&ib_view);
}
void VertexManager::CreateDeviceObjects()
{
m_vertex_draw_offset = 0;
m_index_draw_offset = 0;
m_vertex_stream_buffer = new D3DStreamBuffer(VertexManager::MAXVBUFFERSIZE * 2, MAX_VBUFFER_SIZE, &m_vertex_stream_buffer_reallocated);
m_index_stream_buffer = new D3DStreamBuffer(VertexManager::MAXIBUFFERSIZE * sizeof(u16) * 2, VertexManager::MAXIBUFFERSIZE * sizeof(u16) * 16, &m_index_stream_buffer_reallocated);
SetIndexBuffer();
// Use CPU-only memory if the GPU won't be reading from the buffers,
// since reading upload heaps on the CPU is slow..
m_vertex_cpu_buffer.resize(MAXVBUFFERSIZE);
m_index_cpu_buffer.resize(MAXIBUFFERSIZE);
}
void VertexManager::DestroyDeviceObjects()
{
SAFE_DELETE(m_vertex_stream_buffer);
SAFE_DELETE(m_index_stream_buffer);
m_vertex_cpu_buffer.clear();
m_index_cpu_buffer.clear();
}
VertexManager::VertexManager()
{
CreateDeviceObjects();
}
VertexManager::~VertexManager()
{
DestroyDeviceObjects();
}
void VertexManager::PrepareDrawBuffers(u32 stride)
{
u32 vertex_data_size = IndexGenerator::GetNumVerts() * stride;
u32 index_data_size = IndexGenerator::GetIndexLen() * sizeof(u16);
m_vertex_stream_buffer->OverrideSizeOfPreviousAllocation(vertex_data_size);
m_index_stream_buffer->OverrideSizeOfPreviousAllocation(index_data_size);
ADDSTAT(stats.thisFrame.bytesVertexStreamed, vertex_data_size);
ADDSTAT(stats.thisFrame.bytesIndexStreamed, index_data_size);
}
void VertexManager::Draw(u32 stride)
{
static u32 s_previous_stride = UINT_MAX;
u32 indices = IndexGenerator::GetIndexLen();
if (D3D::command_list_mgr->GetCommandListDirtyState(COMMAND_LIST_STATE_VERTEX_BUFFER) || s_previous_stride != stride)
{
D3D12_VERTEX_BUFFER_VIEW vb_view = {
m_vertex_stream_buffer->GetBaseGPUAddress(), // D3D12_GPU_VIRTUAL_ADDRESS BufferLocation;
m_vertex_stream_buffer->GetSize(), // UINT SizeInBytes;
stride // UINT StrideInBytes;
};
D3D::current_command_list->IASetVertexBuffers(0, 1, &vb_view);
D3D::command_list_mgr->SetCommandListDirtyState(COMMAND_LIST_STATE_VERTEX_BUFFER, false);
s_previous_stride = stride;
}
D3D_PRIMITIVE_TOPOLOGY d3d_primitive_topology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP;
switch (current_primitive_type)
{
case PRIMITIVE_POINTS:
d3d_primitive_topology = D3D_PRIMITIVE_TOPOLOGY_POINTLIST;
break;
case PRIMITIVE_LINES:
d3d_primitive_topology = D3D_PRIMITIVE_TOPOLOGY_LINELIST;
break;
}
if (D3D::command_list_mgr->GetCommandListPrimitiveTopology() != d3d_primitive_topology)
{
D3D::current_command_list->IASetPrimitiveTopology(d3d_primitive_topology);
D3D::command_list_mgr->SetCommandListPrimitiveTopology(d3d_primitive_topology);
}
u32 base_vertex = m_vertex_draw_offset / stride;
u32 start_index = m_index_draw_offset / sizeof(u16);
D3D::current_command_list->DrawIndexedInstanced(indices, 1, start_index, base_vertex, 0);
INCSTAT(stats.thisFrame.numDrawCalls);
}
void VertexManager::vFlush(bool use_dst_alpha)
{
ShaderCache::LoadAndSetActiveShaders(use_dst_alpha ? DSTALPHA_DUAL_SOURCE_BLEND : DSTALPHA_NONE, current_primitive_type);
if (g_ActiveConfig.backend_info.bSupportsBBox && BoundingBox::active)
{
// D3D12TODO: Support GPU-side bounding box.
// D3D::context->OMSetRenderTargetsAndUnorderedAccessViews(D3D11_KEEP_RENDER_TARGETS_AND_DEPTH_STENCIL, nullptr, nullptr, 2, 1, &BBox::GetUAV(), nullptr);
}
u32 stride = VertexLoaderManager::GetCurrentVertexFormat()->GetVertexStride();
PrepareDrawBuffers(stride);
g_renderer->ApplyState(use_dst_alpha);
Draw(stride);
D3D::command_list_mgr->m_draws_since_last_execution++;
// Many Gamecube/Wii titles read from the EFB each frame to determine what new rendering work to submit, e.g. where sun rays are
// occluded and where they aren't. When the CPU wants to read this data (done in Renderer::AccessEFB), it requires that the GPU
// finish all oustanding work. As an optimization, when we detect that the CPU is likely to read back data this frame, we break
// up the rendering work and submit it more frequently to the GPU (via ExecuteCommandList). Thus, when the CPU finally needs the
// the GPU to finish all of its work, there is (hopefully) less work outstanding to wait on at that moment.
// D3D12TODO: Decide right threshold for drawCountSinceAsyncFlush at runtime depending on
// amount of stall measured in AccessEFB.
if (D3D::command_list_mgr->m_draws_since_last_execution > 100 && D3D::command_list_mgr->m_cpu_access_last_frame)
{
D3D::command_list_mgr->m_draws_since_last_execution = 0;
D3D::command_list_mgr->ExecuteQueuedWork();
g_renderer->SetViewport();
D3D::current_command_list->OMSetRenderTargets(1, &FramebufferManager::GetEFBColorTexture()->GetRTV12(), FALSE, &FramebufferManager::GetEFBDepthTexture()->GetDSV12());
}
}
void VertexManager::ResetBuffer(u32 stride)
{
if (s_cull_all)
{
s_pCurBufferPointer = m_vertex_cpu_buffer.data();
s_pBaseBufferPointer = m_vertex_cpu_buffer.data();
s_pEndBufferPointer = m_vertex_cpu_buffer.data() + MAXVBUFFERSIZE;
IndexGenerator::Start(reinterpret_cast<u16*>(m_index_cpu_buffer.data()));
return;
}
bool command_list_executed = m_vertex_stream_buffer->AllocateSpaceInBuffer(MAXVBUFFERSIZE, stride);
if (m_vertex_stream_buffer_reallocated)
{
D3D::command_list_mgr->SetCommandListDirtyState(COMMAND_LIST_STATE_VERTEX_BUFFER, true);
m_vertex_stream_buffer_reallocated = false;
}
s_pBaseBufferPointer = static_cast<u8*>(m_vertex_stream_buffer->GetBaseCPUAddress());
s_pEndBufferPointer = s_pBaseBufferPointer + m_vertex_stream_buffer->GetSize();
s_pCurBufferPointer = static_cast<u8*>(m_vertex_stream_buffer->GetCPUAddressOfCurrentAllocation());
m_vertex_draw_offset = m_vertex_stream_buffer->GetOffsetOfCurrentAllocation();
command_list_executed |= m_index_stream_buffer->AllocateSpaceInBuffer(MAXIBUFFERSIZE * sizeof(u16), sizeof(u16));
if (command_list_executed)
{
g_renderer->SetViewport();
D3D::current_command_list->OMSetRenderTargets(1, &FramebufferManager::GetEFBColorTexture()->GetRTV12(), FALSE, &FramebufferManager::GetEFBDepthTexture()->GetDSV12());
}
if (m_index_stream_buffer_reallocated)
{
SetIndexBuffer();
m_index_stream_buffer_reallocated = false;
}
m_index_draw_offset = m_index_stream_buffer->GetOffsetOfCurrentAllocation();
IndexGenerator::Start(reinterpret_cast<u16*>(m_index_stream_buffer->GetCPUAddressOfCurrentAllocation()));
}
} // namespace

View File

@ -0,0 +1,47 @@
// Copyright 2008 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include "VideoCommon/VertexManagerBase.h"
namespace DX12
{
class D3DStreamBuffer;
class VertexManager final : public VertexManagerBase
{
public:
VertexManager();
~VertexManager();
NativeVertexFormat* CreateNativeVertexFormat(const PortableVertexDeclaration& vtx_decl) override;
void CreateDeviceObjects() override;
void DestroyDeviceObjects() override;
void SetIndexBuffer();
protected:
void ResetBuffer(u32 stride) override;
private:
void PrepareDrawBuffers(u32 stride);
void Draw(u32 stride);
void vFlush(bool use_dst_alpha) override;
u32 m_vertex_draw_offset;
u32 m_index_draw_offset;
D3DStreamBuffer* m_vertex_stream_buffer = nullptr;
D3DStreamBuffer* m_index_stream_buffer = nullptr;
bool m_vertex_stream_buffer_reallocated = false;
bool m_index_stream_buffer_reallocated = false;
std::vector<u8> m_index_cpu_buffer;
std::vector<u8> m_vertex_cpu_buffer;
};
} // namespace

View File

@ -0,0 +1,33 @@
// Copyright 2011 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <string>
#include "VideoCommon/VideoBackendBase.h"
namespace DX12
{
class VideoBackend : public VideoBackendBase
{
bool Initialize(void*) override;
void Shutdown() override;
std::string GetName() const override;
std::string GetDisplayName() const override;
void Video_Prepare() override;
void Video_Cleanup() override;
void ShowConfig(void* parent) override;
unsigned int PeekMessages() override;
private:
void* m_window_handle;
};
}

View File

@ -0,0 +1,38 @@
// Copyright 2011 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "Common/CommonTypes.h"
#include "Common/MsgHandler.h"
#include "Common/Logging/Log.h"
#include "VideoBackends/D3D12/D3DBase.h"
#include "VideoBackends/D3D12/D3DShader.h"
#include "VideoBackends/D3D12/D3DState.h"
#include "VideoBackends/D3D12/FramebufferManager.h"
#include "VideoBackends/D3D12/Render.h"
#include "VideoBackends/D3D12/XFBEncoder.h"
// D3D12TODO: Convert this file..
namespace DX12
{
XFBEncoder::XFBEncoder()
{ }
void XFBEncoder::Init()
{
// D3D12TODO: Convert this file..
}
void XFBEncoder::Shutdown()
{
// D3D12TODO: Convert this file..
}
void XFBEncoder::Encode(u8* dst, u32 width, u32 height, const EFBRectangle& srcRect, float gamma)
{
// D3D12TODO: Convert this file..
}
}

View File

@ -0,0 +1,28 @@
// Copyright 2011 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include "VideoCommon/VideoCommon.h"
namespace DX12
{
class XFBEncoder
{
public:
XFBEncoder();
void Init();
void Shutdown();
void Encode(u8* dst, u32 width, u32 height, const EFBRectangle& src_rect, float gamma);
private:
// D3D12TODO: Implement this class
};
}

View File

@ -0,0 +1,244 @@
// Copyright 2008 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include <string>
#include "Common/CommonTypes.h"
#include "Common/FileUtil.h"
#include "Common/StringUtil.h"
#include "Core/ConfigManager.h"
#include "Core/Host.h"
#include "VideoBackends/D3D12/BoundingBox.h"
#include "VideoBackends/D3D12/D3DBase.h"
#include "VideoBackends/D3D12/D3DCommandListManager.h"
#include "VideoBackends/D3D12/D3DUtil.h"
#include "VideoBackends/D3D12/PerfQuery.h"
#include "VideoBackends/D3D12/Render.h"
#include "VideoBackends/D3D12/ShaderCache.h"
#include "VideoBackends/D3D12/ShaderConstantsManager.h"
#include "VideoBackends/D3D12/StaticShaderCache.h"
#include "VideoBackends/D3D12/TextureCache.h"
#include "VideoBackends/D3D12/VertexManager.h"
#include "VideoBackends/D3D12/VideoBackend.h"
#include "VideoCommon/BPStructs.h"
#include "VideoCommon/CommandProcessor.h"
#include "VideoCommon/Fifo.h"
#include "VideoCommon/GeometryShaderManager.h"
#include "VideoCommon/IndexGenerator.h"
#include "VideoCommon/OpcodeDecoding.h"
#include "VideoCommon/PixelEngine.h"
#include "VideoCommon/PixelShaderManager.h"
#include "VideoCommon/VertexLoaderManager.h"
#include "VideoCommon/VertexShaderManager.h"
#include "VideoCommon/VideoConfig.h"
namespace DX12
{
unsigned int VideoBackend::PeekMessages()
{
MSG msg;
while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
return FALSE;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return TRUE;
}
std::string VideoBackend::GetName() const
{
return "D3D12";
}
std::string VideoBackend::GetDisplayName() const
{
return "Direct3D 12 (experimental)";
}
void InitBackendInfo()
{
HRESULT hr = DX12::D3D::LoadDXGI();
if (SUCCEEDED(hr)) hr = DX12::D3D::LoadD3D();
if (FAILED(hr))
{
DX12::D3D::UnloadDXGI();
return;
}
g_Config.backend_info.APIType = API_D3D;
g_Config.backend_info.bSupportsExclusiveFullscreen = false;
g_Config.backend_info.bSupportsDualSourceBlend = true;
g_Config.backend_info.bSupportsPrimitiveRestart = true;
g_Config.backend_info.bSupportsOversizedViewports = false;
g_Config.backend_info.bSupportsGeometryShaders = true;
g_Config.backend_info.bSupports3DVision = true;
g_Config.backend_info.bSupportsPostProcessing = false;
g_Config.backend_info.bSupportsPaletteConversion = true;
g_Config.backend_info.bSupportsClipControl = true;
IDXGIFactory* factory;
IDXGIAdapter* ad;
hr = DX12::create_dxgi_factory(__uuidof(IDXGIFactory), (void**)&factory);
if (FAILED(hr))
PanicAlert("Failed to create IDXGIFactory object");
// adapters
g_Config.backend_info.Adapters.clear();
g_Config.backend_info.AAModes.clear();
while (factory->EnumAdapters((UINT)g_Config.backend_info.Adapters.size(), &ad) != DXGI_ERROR_NOT_FOUND)
{
const size_t adapter_index = g_Config.backend_info.Adapters.size();
DXGI_ADAPTER_DESC desc;
ad->GetDesc(&desc);
// TODO: These don't get updated on adapter change, yet
if (adapter_index == g_Config.iAdapter)
{
std::string samples;
std::vector<DXGI_SAMPLE_DESC> modes = DX12::D3D::EnumAAModes(ad);
// First iteration will be 1. This equals no AA.
for (unsigned int i = 0; i < modes.size(); ++i)
{
g_Config.backend_info.AAModes.push_back(modes[i].Count);
}
bool shader_model_5_supported = (DX12::D3D::GetFeatureLevel(ad) >= D3D_FEATURE_LEVEL_11_0);
// Requires the earlydepthstencil attribute (only available in shader model 5)
g_Config.backend_info.bSupportsEarlyZ = shader_model_5_supported;
// Requires full UAV functionality (only available in shader model 5)
g_Config.backend_info.bSupportsBBox = false;
// Requires the instance attribute (only available in shader model 5)
g_Config.backend_info.bSupportsGSInstancing = shader_model_5_supported;
// Sample shading requires shader model 5
g_Config.backend_info.bSupportsSSAA = shader_model_5_supported;
}
g_Config.backend_info.Adapters.push_back(UTF16ToUTF8(desc.Description));
ad->Release();
}
factory->Release();
// Clear ppshaders string vector
g_Config.backend_info.PPShaders.clear();
g_Config.backend_info.AnaglyphShaders.clear();
DX12::D3D::UnloadDXGI();
DX12::D3D::UnloadD3D();
}
void VideoBackend::ShowConfig(void *hParent)
{
InitBackendInfo();
Host_ShowVideoConfig(hParent, GetDisplayName(), "gfx_dx12");
}
bool VideoBackend::Initialize(void *window_handle)
{
bool d3d12_supported = D3D::AlertUserIfSelectedAdapterDoesNotSupportD3D12();
if (!d3d12_supported)
return false;
if (window_handle == nullptr)
return false;
InitializeShared();
InitBackendInfo();
frameCount = 0;
if (File::Exists(File::GetUserPath(D_CONFIG_IDX) + "GFX.ini"))
g_Config.Load(File::GetUserPath(D_CONFIG_IDX) + "GFX.ini");
else
g_Config.Load(File::GetUserPath(D_CONFIG_IDX) + "gfx_dx12.ini");
g_Config.GameIniLoad();
g_Config.UpdateProjectionHack();
g_Config.VerifyValidity();
UpdateActiveConfig();
m_window_handle = window_handle;
m_initialized = true;
return true;
}
void VideoBackend::Video_Prepare()
{
// internal interfaces
g_renderer = std::make_unique<Renderer>(m_window_handle);
g_texture_cache = std::make_unique<TextureCache>();
g_vertex_manager = std::make_unique<VertexManager>();
g_perf_query = std::make_unique<PerfQuery>();
ShaderCache::Init();
ShaderConstantsManager::Init();
StaticShaderCache::Init();
StateCache::Init(); // PSO cache is populated here, after constituent shaders are loaded.
D3D::InitUtils();
// VideoCommon
BPInit();
Fifo::Init();
IndexGenerator::Init();
VertexLoaderManager::Init();
OpcodeDecoder::Init();
VertexShaderManager::Init();
PixelShaderManager::Init();
GeometryShaderManager::Init();
CommandProcessor::Init();
PixelEngine::Init();
BBox::Init();
// Tell the host that the window is ready
Host_Message(WM_USER_CREATE);
}
void VideoBackend::Shutdown()
{
m_initialized = true;
// TODO: should be in Video_Cleanup
if (g_renderer)
{
// Immediately stop app from submitting work to GPU, and wait for all submitted work to complete. D3D12TODO: Check this.
D3D::command_list_mgr->ExecuteQueuedWork(true);
// VideoCommon
Fifo::Shutdown();
CommandProcessor::Shutdown();
GeometryShaderManager::Shutdown();
PixelShaderManager::Shutdown();
VertexShaderManager::Shutdown();
OpcodeDecoder::Shutdown();
VertexLoaderManager::Shutdown();
// internal interfaces
D3D::ShutdownUtils();
ShaderCache::Shutdown();
ShaderConstantsManager::Shutdown();
StaticShaderCache::Shutdown();
BBox::Shutdown();
g_perf_query.reset();
g_vertex_manager.reset();
g_texture_cache.reset();
g_renderer.reset();
}
}
void VideoBackend::Video_Cleanup()
{
}
}

View File

@ -8,6 +8,7 @@
// TODO: ugly
#ifdef _WIN32
#include "VideoBackends/D3D/VideoBackend.h"
#include "VideoBackends/D3D12/VideoBackend.h"
#endif
#include "VideoBackends/OGL/VideoBackend.h"
#include "VideoBackends/Software/VideoBackend.h"
@ -33,10 +34,18 @@ void VideoBackendBase::PopulateList()
{
VideoBackendBase* backends[4] = { nullptr };
// OGL > D3D11 > SW
// OGL > D3D11 > D3D12 > SW
g_available_video_backends.push_back(backends[0] = new OGL::VideoBackend);
#ifdef _WIN32
g_available_video_backends.push_back(backends[1] = new DX11::VideoBackend);
// More robust way to check for D3D12 support than (unreliable) OS version checks.
HMODULE d3d12_module = LoadLibraryA("d3d12.dll");
if (d3d12_module != NULL)
{
FreeLibrary(d3d12_module);
g_available_video_backends.push_back(backends[2] = new DX12::VideoBackend);
}
#endif
g_available_video_backends.push_back(backends[3] = new SW::VideoSoftware);

View File

@ -78,6 +78,9 @@
<ProjectReference Include="$(CoreDir)VideoBackends\Software\Software.vcxproj">
<Project>{a4c423aa-f57c-46c7-a172-d1a777017d29}</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">

View File

@ -1,4 +1,3 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.23107.0
@ -69,6 +68,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UnitTests", "UnitTests\Unit
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "enet", "..\Externals\enet\enet.vcxproj", "{CBC76802-C128-4B17-BF6C-23B08C313E5E}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "D3D12", "Core\VideoBackends\D3D12\D3D12.vcxproj", "{570215B7-E32F-4438-95AE-C8D955F9FCA3}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@ -79,10 +80,6 @@ Global
{47411FDB-1BF2-48D0-AB4E-C7C41160F898}.Debug|x64.Build.0 = Debug|x64
{47411FDB-1BF2-48D0-AB4E-C7C41160F898}.Release|x64.ActiveCfg = Release|x64
{47411FDB-1BF2-48D0-AB4E-C7C41160F898}.Release|x64.Build.0 = Release|x64
{69D2B16E-122A-4E5D-8C37-8EC7B0F7CEB0}.Debug|x64.ActiveCfg = Debug|x64
{69D2B16E-122A-4E5D-8C37-8EC7B0F7CEB0}.Debug|x64.Build.0 = Debug|x64
{69D2B16E-122A-4E5D-8C37-8EC7B0F7CEB0}.Release|x64.ActiveCfg = Release|x64
{69D2B16E-122A-4E5D-8C37-8EC7B0F7CEB0}.Release|x64.Build.0 = Release|x64
{E54CF649-140E-4255-81A5-30A673C1FB36}.Debug|x64.ActiveCfg = Debug|x64
{E54CF649-140E-4255-81A5-30A673C1FB36}.Debug|x64.Build.0 = Debug|x64
{E54CF649-140E-4255-81A5-30A673C1FB36}.Release|x64.ActiveCfg = Release|x64
@ -199,6 +196,10 @@ Global
{CBC76802-C128-4B17-BF6C-23B08C313E5E}.Debug|x64.Build.0 = Debug|x64
{CBC76802-C128-4B17-BF6C-23B08C313E5E}.Release|x64.ActiveCfg = Release|x64
{CBC76802-C128-4B17-BF6C-23B08C313E5E}.Release|x64.Build.0 = Release|x64
{570215B7-E32F-4438-95AE-C8D955F9FCA3}.Debug|x64.ActiveCfg = Debug|x64
{570215B7-E32F-4438-95AE-C8D955F9FCA3}.Debug|x64.Build.0 = Debug|x64
{570215B7-E32F-4438-95AE-C8D955F9FCA3}.Release|x64.ActiveCfg = Release|x64
{570215B7-E32F-4438-95AE-C8D955F9FCA3}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -230,5 +231,6 @@ Global
{AAD1BCD6-9804-44A5-A5FC-4782EA00E9D4} = {15670B2E-CED6-4ED5-94CE-A00B1B2B5BA6}
{76563A7F-1011-4EAD-B667-7BB18D09568E} = {15670B2E-CED6-4ED5-94CE-A00B1B2B5BA6}
{CBC76802-C128-4B17-BF6C-23B08C313E5E} = {87ADDFF9-5768-4DA2-A33B-2477593D6677}
{570215B7-E32F-4438-95AE-C8D955F9FCA3} = {AAD1BCD6-9804-44A5-A5FC-4782EA00E9D4}
EndGlobalSection
EndGlobal