GS: Add Direct3D 12 renderer

This commit is contained in:
Connor McLaughlin 2022-03-19 22:19:16 +10:00 committed by refractionpcsx2
parent 3c18cdcb1f
commit 1993203d26
20 changed files with 3709 additions and 8 deletions

View File

@ -18,9 +18,8 @@ struct VS_OUTPUT
float4 c : COLOR; float4 c : COLOR;
}; };
cbuffer cb0 cbuffer cb0 : register(b0)
{ {
float4 BGColor;
int EMODA; int EMODA;
int EMODC; int EMODC;
}; };
@ -405,4 +404,24 @@ PS_OUTPUT ps_yuv(PS_INPUT input)
return output; return output;
} }
float ps_stencil_image_init_0(PS_INPUT input) : SV_Target
{
float c;
if ((127.5f / 255.0f) < sample_c(input.t).a) // < 0x80 pass (== 0x80 should not pass)
c = float(-1);
else
c = float(0x7FFFFFFF);
return c;
}
float ps_stencil_image_init_1(PS_INPUT input) : SV_Target
{
float c;
if (sample_c(input.t).a < (127.5f / 255.0f)) // >= 0x80 pass
c = float(-1);
else
c = float(0x7FFFFFFF);
return c;
}
#endif #endif

View File

@ -61,6 +61,7 @@
#define PS_NO_COLOR1 0 #define PS_NO_COLOR1 0
#define PS_NO_ABLEND 0 #define PS_NO_ABLEND 0
#define PS_ONLY_ALPHA 0 #define PS_ONLY_ALPHA 0
#define PS_DATE 0
#endif #endif
#define SW_BLEND (PS_BLEND_A || PS_BLEND_B || PS_BLEND_D) #define SW_BLEND (PS_BLEND_A || PS_BLEND_B || PS_BLEND_D)
@ -99,16 +100,23 @@ struct PS_INPUT
#else #else
nointerpolation float4 c : COLOR0; nointerpolation float4 c : COLOR0;
#endif #endif
#if PS_DATE > 10 || PS_DATE == 3
uint primid : SV_PrimitiveID;
#endif
}; };
struct PS_OUTPUT struct PS_OUTPUT
{ {
#if !PS_NO_COLOR #if !PS_NO_COLOR
#if PS_DATE > 10
float c : SV_Target;
#else
float4 c0 : SV_Target0; float4 c0 : SV_Target0;
#if !PS_NO_COLOR1 #if !PS_NO_COLOR1
float4 c1 : SV_Target1; float4 c1 : SV_Target1;
#endif #endif
#endif #endif
#endif
#if PS_ZCLAMP #if PS_ZCLAMP
float depth : SV_Depth; float depth : SV_Depth;
#endif #endif
@ -117,10 +125,15 @@ struct PS_OUTPUT
Texture2D<float4> Texture : register(t0); Texture2D<float4> Texture : register(t0);
Texture2D<float4> Palette : register(t1); Texture2D<float4> Palette : register(t1);
Texture2D<float4> RtTexture : register(t2); Texture2D<float4> RtTexture : register(t2);
Texture2D<float> PrimMinTexture : register(t3);
SamplerState TextureSampler : register(s0); SamplerState TextureSampler : register(s0);
SamplerState PaletteSampler : register(s1); SamplerState PaletteSampler : register(s1);
#ifdef DX12
cbuffer cb0 : register(b0)
#else
cbuffer cb0 cbuffer cb0
#endif
{ {
float2 VertexScale; float2 VertexScale;
float2 VertexOffset; float2 VertexOffset;
@ -131,7 +144,11 @@ cbuffer cb0
uint pad_cb0; uint pad_cb0;
}; };
#ifdef DX12
cbuffer cb1 : register(b1)
#else
cbuffer cb1 cbuffer cb1
#endif
{ {
float3 FogColor; float3 FogColor;
float AREF; float AREF;
@ -853,6 +870,29 @@ PS_OUTPUT ps_main(PS_INPUT input)
if (C.a < A_one) C.a += A_one; if (C.a < A_one) C.a += A_one;
} }
#if PS_DATE == 3
// Note gl_PrimitiveID == stencil_ceil will be the primitive that will update
// the bad alpha value so we must keep it.
int stencil_ceil = int(PrimMinTexture.Load(int3(input.p.xy, 0)));
if (int(input.primid) > stencil_ceil)
discard;
#endif
// Get first primitive that will write a failling alpha value
#if PS_DATE == 11
// DATM == 0
// Pixel with alpha equal to 1 will failed (128-255)
output.c = (C.a > 127.5f) ? float(input.primid) : float(0x7FFFFFFF);
#elif PS_DATE == 12
// DATM == 1
// Pixel with alpha equal to 0 will failed (0-127)
output.c = (C.a < 127.5f) ? float(input.primid) : float(0x7FFFFFFF);
#else
// Not primid DATE setup
ps_blend(C, alpha_blend, input.p.xy); ps_blend(C, alpha_blend, input.p.xy);
ps_dither(C.rgb, input.p.xy); ps_dither(C.rgb, input.p.xy);
@ -878,6 +918,8 @@ PS_OUTPUT ps_main(PS_INPUT input)
#endif #endif
#endif #endif
#endif
#if PS_ZCLAMP #if PS_ZCLAMP
output.depth = min(input.p.z, MaxDepthPS); output.depth = min(input.p.z, MaxDepthPS);
#endif #endif

View File

@ -240,7 +240,7 @@ void MainWindow::connectVMThreadSignals(EmuThread* thread)
static constexpr GSRendererType renderers[] = { static constexpr GSRendererType renderers[] = {
#ifdef _WIN32 #ifdef _WIN32
GSRendererType::DX11, GSRendererType::DX11, GSRendererType::DX12,
#endif #endif
GSRendererType::OGL, GSRendererType::VK, GSRendererType::SW, GSRendererType::Null}; GSRendererType::OGL, GSRendererType::VK, GSRendererType::SW, GSRendererType::Null};
for (GSRendererType renderer : renderers) for (GSRendererType renderer : renderers)

View File

@ -30,6 +30,7 @@
#ifdef _WIN32 #ifdef _WIN32
#include "Frontend/D3D11HostDisplay.h" #include "Frontend/D3D11HostDisplay.h"
#include "Frontend/D3D12HostDisplay.h"
#endif #endif
struct RendererInfo struct RendererInfo
@ -44,6 +45,8 @@ static constexpr RendererInfo s_renderer_info[] = {
#ifdef _WIN32 #ifdef _WIN32
QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "Direct3D 11"), QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "Direct3D 11"),
GSRendererType::DX11, GSRendererType::DX11,
QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "Direct3D 12"),
GSRendererType::DX12,
#endif #endif
#ifdef ENABLE_OPENGL #ifdef ENABLE_OPENGL
QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "OpenGL"), QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "OpenGL"),
@ -355,7 +358,7 @@ void GraphicsSettingsWidget::updateRendererDependentOptions()
const bool is_dx11 = false; const bool is_dx11 = false;
#endif #endif
const bool is_hardware = (type == GSRendererType::DX11 || type == GSRendererType::OGL || type == GSRendererType::VK); const bool is_hardware = (type == GSRendererType::DX11 || type == GSRendererType::DX12 || type == GSRendererType::OGL || type == GSRendererType::VK);
const bool is_software = (type == GSRendererType::SW); const bool is_software = (type == GSRendererType::SW);
const int current_tab = m_hardware_renderer_visible ? m_ui.hardwareRendererGroup->currentIndex() : m_ui.softwareRendererGroup->currentIndex(); const int current_tab = m_hardware_renderer_visible ? m_ui.hardwareRendererGroup->currentIndex() : m_ui.softwareRendererGroup->currentIndex();
@ -420,6 +423,9 @@ void GraphicsSettingsWidget::updateRendererDependentOptions()
case GSRendererType::DX11: case GSRendererType::DX11:
modes = D3D11HostDisplay::StaticGetAdapterAndModeList(); modes = D3D11HostDisplay::StaticGetAdapterAndModeList();
break; break;
case GSRendererType::DX12:
modes = D3D12HostDisplay::StaticGetAdapterAndModeList();
break;
#endif #endif
#ifdef ENABLE_VULKAN #ifdef ENABLE_VULKAN

View File

@ -895,6 +895,8 @@ elseif(WIN32)
GS/Renderers/DX11/GSDevice11.cpp GS/Renderers/DX11/GSDevice11.cpp
GS/Renderers/DX11/GSTexture11.cpp GS/Renderers/DX11/GSTexture11.cpp
GS/Renderers/DX11/GSTextureFX11.cpp GS/Renderers/DX11/GSTextureFX11.cpp
GS/Renderers/DX12/GSDevice12.cpp
GS/Renderers/DX12/GSTexture12.cpp
GS/Window/GSCaptureDlg.cpp GS/Window/GSCaptureDlg.cpp
GS/Window/GSDialog.cpp GS/Window/GSDialog.cpp
) )
@ -902,6 +904,8 @@ elseif(WIN32)
GS/Renderers/DX11/D3D.h GS/Renderers/DX11/D3D.h
GS/Renderers/DX11/GSDevice11.h GS/Renderers/DX11/GSDevice11.h
GS/Renderers/DX11/GSTexture11.h GS/Renderers/DX11/GSTexture11.h
GS/Renderers/DX12/GSDevice12.h
GS/Renderers/DX12/GSTexture12.h
) )
else() else()
list(APPEND pcsx2SPU2Sources list(APPEND pcsx2SPU2Sources

View File

@ -124,6 +124,7 @@ enum class GSRendererType : s8
SW = 13, SW = 13,
VK = 14, VK = 14,
Metal = 17, Metal = 17,
DX12 = 15,
}; };
enum class GSInterlaceMode : u8 enum class GSInterlaceMode : u8

View File

@ -56,6 +56,7 @@
#ifdef _WIN32 #ifdef _WIN32
#include "Renderers/DX11/GSDevice11.h" #include "Renderers/DX11/GSDevice11.h"
#include "Renderers/DX12/GSDevice12.h"
#include "GS/Renderers/DX11/D3D.h" #include "GS/Renderers/DX11/D3D.h"
@ -190,6 +191,9 @@ static HostDisplay::RenderAPI GetAPIForRenderer(GSRendererType renderer)
#ifdef _WIN32 #ifdef _WIN32
case GSRendererType::DX11: case GSRendererType::DX11:
return HostDisplay::RenderAPI::D3D11; return HostDisplay::RenderAPI::D3D11;
case GSRendererType::DX12:
return HostDisplay::RenderAPI::D3D12;
#endif #endif
#ifdef __APPLE__ #ifdef __APPLE__
@ -215,6 +219,9 @@ static bool DoGSOpen(GSRendererType renderer, u8* basemem)
case HostDisplay::RenderAPI::D3D11: case HostDisplay::RenderAPI::D3D11:
g_gs_device = std::make_unique<GSDevice11>(); g_gs_device = std::make_unique<GSDevice11>();
break; break;
case HostDisplay::RenderAPI::D3D12:
g_gs_device = std::make_unique<GSDevice12>();
break;
#endif #endif
#ifdef __APPLE__ #ifdef __APPLE__
case HostDisplay::RenderAPI::Metal: case HostDisplay::RenderAPI::Metal:
@ -1226,6 +1233,7 @@ void GSApp::Init()
m_gs_renderers.push_back(GSSetting(static_cast<u32>(GSRendererType::Auto), "Automatic", "")); m_gs_renderers.push_back(GSSetting(static_cast<u32>(GSRendererType::Auto), "Automatic", ""));
#ifdef _WIN32 #ifdef _WIN32
m_gs_renderers.push_back(GSSetting(static_cast<u32>(GSRendererType::DX11), "Direct3D 11", "")); m_gs_renderers.push_back(GSSetting(static_cast<u32>(GSRendererType::DX11), "Direct3D 11", ""));
m_gs_renderers.push_back(GSSetting(static_cast<u32>(GSRendererType::DX12), "Direct3D 12", ""));
#endif #endif
#ifdef __APPLE__ #ifdef __APPLE__
m_gs_renderers.push_back(GSSetting(static_cast<u32>(GSRendererType::Metal), "Metal", "")); m_gs_renderers.push_back(GSSetting(static_cast<u32>(GSRendererType::Metal), "Metal", ""));

View File

@ -75,7 +75,6 @@ enum ChannelFetch
class MergeConstantBuffer class MergeConstantBuffer
{ {
public: public:
GSVector4 BGColor;
u32 EMODA; u32 EMODA;
u32 EMODC; u32 EMODC;
u32 pad[2]; u32 pad[2];

View File

@ -705,7 +705,6 @@ void GSDevice11::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex,
if (feedback_write_2 || feedback_write_1 || sTex[0]) if (feedback_write_2 || feedback_write_1 || sTex[0])
{ {
MergeConstantBuffer cb; MergeConstantBuffer cb;
cb.BGColor = c;
cb.EMODA = EXTBUF.EMODA; cb.EMODA = EXTBUF.EMODA;
cb.EMODC = EXTBUF.EMODC; cb.EMODC = EXTBUF.EMODC;
m_ctx->UpdateSubresource(m_merge.cb.get(), 0, nullptr, &cb, 0, 0); m_ctx->UpdateSubresource(m_merge.cb.get(), 0, nullptr, &cb, 0, 0);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,416 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2021 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "GSTexture12.h"
#include "GS/GSVector.h"
#include "GS/Renderers/Common/GSDevice.h"
#include "common/D3D12/ShaderCache.h"
#include "common/D3D12/StreamBuffer.h"
#include "common/HashCombine.h"
#include <array>
#include <unordered_map>
namespace D3D12MA
{
class Allocation;
}
class GSDevice12 final : public GSDevice
{
public:
template <typename T>
using ComPtr = wil::com_ptr_nothrow<T>;
struct alignas(8) PipelineSelector
{
GSHWDrawConfig::PSSelector ps;
union
{
struct
{
u32 topology : 2;
u32 rt : 1;
u32 ds : 1;
};
u32 key;
};
GSHWDrawConfig::VSSelector vs;
GSHWDrawConfig::GSSelector gs;
GSHWDrawConfig::DepthStencilSelector dss;
GSHWDrawConfig::ColorMaskSelector cms;
GSHWDrawConfig::BlendState bs;
__fi bool operator==(const PipelineSelector& p) const { return (memcmp(this, &p, sizeof(p)) == 0); }
__fi bool operator!=(const PipelineSelector& p) const { return (memcmp(this, &p, sizeof(p)) != 0); }
__fi PipelineSelector() { memset(this, 0, sizeof(*this)); }
};
static_assert(sizeof(PipelineSelector) == 24, "Pipeline selector is 24 bytes");
struct PipelineSelectorHash
{
std::size_t operator()(const PipelineSelector& e) const noexcept
{
std::size_t hash = 0;
HashCombine(hash, e.vs.key, e.gs.key, e.ps.key_hi, e.ps.key_lo, e.dss.key, e.cms.key, e.bs.key, e.key);
return hash;
}
};
class ShaderMacro
{
struct mcstr
{
const char *name, *def;
mcstr(const char* n, const char* d)
: name(n)
, def(d)
{
}
};
struct mstring
{
std::string name, def;
mstring(const char* n, std::string d)
: name(n)
, def(d)
{
}
};
std::vector<mstring> mlist;
std::vector<mcstr> mout;
public:
ShaderMacro(D3D_FEATURE_LEVEL fl);
void AddMacro(const char* n, int d);
D3D_SHADER_MACRO* GetPtr(void);
};
enum : u32
{
NUM_TFX_CONSTANT_BUFFERS = 2,
NUM_TFX_TEXTURES = 2,
NUM_TFX_RT_TEXTURES = 2,
NUM_TOTAL_TFX_TEXTURES = NUM_TFX_TEXTURES + NUM_TFX_RT_TEXTURES,
NUM_TFX_SAMPLERS = 2,
NUM_UTILITY_TEXTURES = 1,
NUM_UTILITY_SAMPLERS = 1,
CONVERT_PUSH_CONSTANTS_SIZE = 32,
VERTEX_BUFFER_SIZE = 32 * 1024 * 1024,
INDEX_BUFFER_SIZE = 16 * 1024 * 1024,
VERTEX_UNIFORM_BUFFER_SIZE = 8 * 1024 * 1024,
FRAGMENT_UNIFORM_BUFFER_SIZE = 8 * 1024 * 1024,
TFX_ROOT_SIGNATURE_PARAM_VS_CBV = 0,
TFX_ROOT_SIGNATURE_PARAM_PS_CBV = 1,
TFX_ROOT_SIGNATURE_PARAM_PS_TEXTURES = 2,
TFX_ROOT_SIGNATURE_PARAM_PS_SAMPLERS = 3,
TFX_ROOT_SIGNATURE_PARAM_PS_RT_TEXTURES = 4,
UTILITY_ROOT_SIGNATURE_PARAM_PUSH_CONSTANTS = 0,
UTILITY_ROOT_SIGNATURE_PARAM_PS_TEXTURES = 1,
UTILITY_ROOT_SIGNATURE_PARAM_PS_SAMPLERS = 2,
};
private:
static constexpr u32 SHADER_VERSION = 1;
ComPtr<ID3D12RootSignature> m_tfx_root_signature;
ComPtr<ID3D12RootSignature> m_utility_root_signature;
D3D12::StreamBuffer m_vertex_stream_buffer;
D3D12::StreamBuffer m_index_stream_buffer;
D3D12::StreamBuffer m_vertex_constant_buffer;
D3D12::StreamBuffer m_pixel_constant_buffer;
ComPtr<D3D12MA::Allocation> m_readback_staging_allocation;
ComPtr<ID3D12Resource> m_readback_staging_buffer;
void* m_readback_staging_buffer_map = nullptr;
u32 m_readback_staging_buffer_size = 0;
D3D12::DescriptorHandle m_point_sampler_cpu;
D3D12::DescriptorHandle m_linear_sampler_cpu;
std::unordered_map<u32, D3D12::DescriptorHandle> m_samplers;
std::array<ComPtr<ID3D12PipelineState>, static_cast<int>(ShaderConvert::Count)> m_convert{};
std::array<ComPtr<ID3D12PipelineState>, static_cast<int>(ShaderConvert::Count)> m_present{};
std::array<ComPtr<ID3D12PipelineState>, 16> m_color_copy{};
std::array<ComPtr<ID3D12PipelineState>, 2> m_merge{};
std::array<ComPtr<ID3D12PipelineState>, 4> m_interlace{};
std::array<ComPtr<ID3D12PipelineState>, 2> m_hdr_setup_pipelines{}; // [depth]
std::array<ComPtr<ID3D12PipelineState>, 2> m_hdr_finish_pipelines{}; // [depth]
std::array<std::array<ComPtr<ID3D12PipelineState>, 2>, 2> m_date_image_setup_pipelines{}; // [depth][datm]
ComPtr<ID3D12PipelineState> m_fxaa_pipeline;
ComPtr<ID3D12PipelineState> m_shadeboost_pipeline;
std::unordered_map<u32, ComPtr<ID3DBlob>> m_tfx_vertex_shaders;
std::unordered_map<u32, ComPtr<ID3DBlob>> m_tfx_geometry_shaders;
std::unordered_map<GSHWDrawConfig::PSSelector, ComPtr<ID3DBlob>, GSHWDrawConfig::PSSelectorHash> m_tfx_pixel_shaders;
std::unordered_map<PipelineSelector, ComPtr<ID3D12PipelineState>, PipelineSelectorHash> m_tfx_pipelines;
GSHWDrawConfig::VSConstantBuffer m_vs_cb_cache;
GSHWDrawConfig::PSConstantBuffer m_ps_cb_cache;
D3D12::ShaderCache m_shader_cache;
ComPtr<ID3DBlob> m_convert_vs;
std::string m_tfx_source;
void LookupNativeFormat(GSTexture::Format format, DXGI_FORMAT* d3d_format, DXGI_FORMAT* srv_format, DXGI_FORMAT* rtv_format, DXGI_FORMAT* dsv_format) const;
GSTexture* CreateSurface(GSTexture::Type type, int width, int height, int levels, GSTexture::Format format) override;
void DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, GSVector4* dRect, const GSRegPMODE& PMODE,
const GSRegEXTBUF& EXTBUF, const GSVector4& c) final;
void DoInterlace(GSTexture* sTex, GSTexture* dTex, int shader, bool linear, float yoffset = 0) final;
void DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float params[4]) final;
void DoFXAA(GSTexture* sTex, GSTexture* dTex) final;
bool GetSampler(D3D12::DescriptorHandle* cpu_handle, GSHWDrawConfig::SamplerSelector ss);
void ClearSamplerCache() final;
bool GetTextureGroupDescriptors(D3D12::DescriptorHandle* gpu_handle, const D3D12::DescriptorHandle* cpu_handles, u32 count);
const ID3DBlob* GetTFXVertexShader(GSHWDrawConfig::VSSelector sel);
const ID3DBlob* GetTFXGeometryShader(GSHWDrawConfig::GSSelector sel);
const ID3DBlob* GetTFXPixelShader(const GSHWDrawConfig::PSSelector& sel);
ComPtr<ID3D12PipelineState> CreateTFXPipeline(const PipelineSelector& p);
const ID3D12PipelineState* GetTFXPipeline(const PipelineSelector& p);
ComPtr<ID3DBlob> GetUtilityVertexShader(const std::string& source, const char* entry_point);
ComPtr<ID3DBlob> GetUtilityPixelShader(const std::string& source, const char* entry_point);
bool CheckFeatures();
bool CreateNullTexture();
bool CreateBuffers();
bool CreateRootSignatures();
bool CompileConvertPipelines();
bool CompileInterlacePipelines();
bool CompileMergePipelines();
bool CompilePostProcessingPipelines();
bool CheckStagingBufferSize(u32 required_size);
bool MapStagingBuffer(u32 size_to_read);
void UnmapStagingBuffer();
void DestroyStagingBuffer();
void DestroyResources();
public:
GSDevice12();
~GSDevice12() override;
__fi static GSDevice12* GetInstance() { return static_cast<GSDevice12*>(g_gs_device.get()); }
bool Create(HostDisplay* display) override;
void Destroy() override;
void ResetAPIState() override;
void RestoreAPIState() override;
void PushDebugGroup(const char* fmt, ...) override;
void PopDebugGroup() override;
void InsertDebugMessage(DebugMessageCategory category, const char* fmt, ...) override;
void DrawPrimitive();
void DrawIndexedPrimitive();
void DrawIndexedPrimitive(int offset, int count);
void ClearRenderTarget(GSTexture* t, const GSVector4& c) override;
void ClearRenderTarget(GSTexture* t, u32 c) override;
void InvalidateRenderTarget(GSTexture* t) override;
void ClearDepth(GSTexture* t) override;
void ClearStencil(GSTexture* t, u8 c) override;
bool DownloadTexture(GSTexture* src, const GSVector4i& rect, GSTexture::GSMap& out_map) override;
void DownloadTextureComplete() override;
GSTexture* DrawForReadback(GSTexture* src, const GSVector4& sRect, int w, int h, int format = 0, int ps_shader = 0);
bool ReadbackTexture(GSTexture* src, const GSVector4i& rect, u32 level, GSTexture::GSMap* dst);
void CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r) override;
void DoCopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r, const GSVector4i& dst_rc);
void StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect,
ShaderConvert shader = ShaderConvert::COPY, bool linear = true) override;
void StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, bool red,
bool green, bool blue, bool alpha) override;
void BeginRenderPassForStretchRect(GSTexture12* dTex, const GSVector4i& dtex_rc, const GSVector4i& dst_rc);
void DoStretchRect(GSTexture12* sTex, const GSVector4& sRect, GSTexture12* dTex, const GSVector4& dRect,
const ID3D12PipelineState* pipeline, bool linear);
void DrawStretchRect(const GSVector4& sRect, const GSVector4& dRect, const GSVector2i& ds);
void SetupDATE(GSTexture* rt, GSTexture* ds, bool datm, const GSVector4i& bbox);
GSTexture12* SetupPrimitiveTrackingDATE(GSHWDrawConfig& config, PipelineSelector& pipe);
void IASetVertexBuffer(const void* vertex, size_t stride, size_t count);
bool IAMapVertexBuffer(void** vertex, size_t stride, size_t count);
void IAUnmapVertexBuffer();
void IASetIndexBuffer(const void* index, size_t count);
void PSSetShaderResource(int i, GSTexture* sr, bool check_state);
void PSSetSampler(u32 index, GSHWDrawConfig::SamplerSelector sel);
void OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector4i& scissor);
void SetVSConstantBuffer(const GSHWDrawConfig::VSConstantBuffer& cb);
void SetPSConstantBuffer(const GSHWDrawConfig::PSConstantBuffer& cb);
bool BindDrawPipeline(const PipelineSelector& p);
void RenderHW(GSHWDrawConfig& config) override;
void UpdateHWPipelineSelector(GSHWDrawConfig& config);
public:
/// Ends any render pass, executes the command buffer, and invalidates cached state.
void ExecuteCommandList(bool wait_for_completion);
void ExecuteCommandList(bool wait_for_completion, const char* reason, ...);
void ExecuteCommandListAndRestartRenderPass(const char* reason);
/// Set dirty flags on everything to force re-bind at next draw time.
void InvalidateCachedState();
/// Binds all dirty state to the command buffer.
bool ApplyUtilityState(bool already_execed = false);
bool ApplyTFXState(bool already_execed = false);
void SetVertexBuffer(D3D12_GPU_VIRTUAL_ADDRESS buffer, size_t size, size_t stride);
void SetIndexBuffer(D3D12_GPU_VIRTUAL_ADDRESS buffer, size_t size, DXGI_FORMAT type);
void SetPrimitiveTopology(D3D12_PRIMITIVE_TOPOLOGY topology);
void SetBlendConstants(u8 color);
void SetStencilRef(u8 ref);
void SetUtilityRootSignature();
void SetUtilityTexture(GSTexture* tex, const D3D12::DescriptorHandle& sampler);
void SetUtilityPushConstants(const void* data, u32 size);
void UnbindTexture(GSTexture12* tex);
// Assumes that the previous level has been transitioned to PS resource,
// and the current level has been transitioned to RT.
void RenderTextureMipmap(const D3D12::Texture& texture,
u32 dst_level, u32 dst_width, u32 dst_height, u32 src_level, u32 src_width, u32 src_height);
// Ends a render pass if we're currently in one.
// When Bind() is next called, the pass will be restarted.
// Calling this function is allowed even if a pass has not begun.
bool InRenderPass();
void BeginRenderPass(
D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE color_begin = D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_NO_ACCESS,
D3D12_RENDER_PASS_ENDING_ACCESS_TYPE color_end = D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS,
D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE depth_begin = D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_NO_ACCESS,
D3D12_RENDER_PASS_ENDING_ACCESS_TYPE depth_end = D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS,
D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE stencil_begin = D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_NO_ACCESS,
D3D12_RENDER_PASS_ENDING_ACCESS_TYPE stencil_end = D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS,
const GSVector4& clear_color = GSVector4::zero(), float clear_depth = 0.0f, u8 clear_stencil = 0);
void EndRenderPass();
void SetViewport(const D3D12_VIEWPORT& viewport);
void SetScissor(const GSVector4i& scissor);
void SetPipeline(const ID3D12PipelineState* pipeline);
private:
enum DIRTY_FLAG : u32
{
DIRTY_FLAG_VS_CONSTANT_BUFFER = (1 << 0),
DIRTY_FLAG_PS_CONSTANT_BUFFER = (1 << 1),
DIRTY_FLAG_TFX_TEXTURES = (1 << 2),
DIRTY_FLAG_TFX_SAMPLERS = (1 << 3),
DIRTY_FLAG_TFX_RT_TEXTURES = (1 << 4),
DIRTY_FLAG_VS_CONSTANT_BUFFER_BINDING = (1 << 5),
DIRTY_FLAG_PS_CONSTANT_BUFFER_BINDING = (1 << 6),
DIRTY_FLAG_TEXTURES_DESCRIPTOR_TABLE = (1 << 7),
DIRTY_FLAG_SAMPLERS_DESCRIPTOR_TABLE = (1 << 8),
DIRTY_FLAG_TEXTURES_DESCRIPTOR_TABLE_2 = (1 << 9),
DIRTY_FLAG_VERTEX_BUFFER = (1 << 10),
DIRTY_FLAG_INDEX_BUFFER = (1 << 11),
DIRTY_FLAG_PRIMITIVE_TOPOLOGY = (1 << 12),
DIRTY_FLAG_VIEWPORT = (1 << 13),
DIRTY_FLAG_SCISSOR = (1 << 14),
DIRTY_FLAG_RENDER_TARGET = (1 << 15),
DIRTY_FLAG_PIPELINE = (1 << 16),
DIRTY_FLAG_BLEND_CONSTANTS = (1 << 17),
DIRTY_FLAG_STENCIL_REF = (1 << 18),
DIRTY_BASE_STATE = DIRTY_FLAG_VS_CONSTANT_BUFFER_BINDING | DIRTY_FLAG_PS_CONSTANT_BUFFER_BINDING |
DIRTY_FLAG_TEXTURES_DESCRIPTOR_TABLE | DIRTY_FLAG_SAMPLERS_DESCRIPTOR_TABLE | DIRTY_FLAG_TEXTURES_DESCRIPTOR_TABLE_2 |
DIRTY_FLAG_VERTEX_BUFFER | DIRTY_FLAG_INDEX_BUFFER | DIRTY_FLAG_PRIMITIVE_TOPOLOGY |
DIRTY_FLAG_VIEWPORT | DIRTY_FLAG_SCISSOR | DIRTY_FLAG_RENDER_TARGET |
DIRTY_FLAG_PIPELINE | DIRTY_FLAG_BLEND_CONSTANTS | DIRTY_FLAG_STENCIL_REF,
DIRTY_TFX_STATE = DIRTY_BASE_STATE | DIRTY_FLAG_TFX_TEXTURES | DIRTY_FLAG_TFX_SAMPLERS | DIRTY_FLAG_TFX_RT_TEXTURES,
DIRTY_UTILITY_STATE = DIRTY_BASE_STATE,
DIRTY_CONSTANT_BUFFER_STATE = DIRTY_FLAG_VS_CONSTANT_BUFFER | DIRTY_FLAG_PS_CONSTANT_BUFFER,
};
enum class RootSignature
{
Undefined,
TFX,
Utility
};
void InitializeState();
void InitializeSamplers();
void ApplyBaseState(u32 flags, ID3D12GraphicsCommandList* cmdlist);
// Which bindings/state has to be updated before the next draw.
u32 m_dirty_flags = 0;
// input assembly
D3D12_VERTEX_BUFFER_VIEW m_vertex_buffer = {};
D3D12_INDEX_BUFFER_VIEW m_index_buffer = {};
D3D12_PRIMITIVE_TOPOLOGY m_primitive_topology = {};
GSTexture12* m_current_render_target = nullptr;
GSTexture12* m_current_depth_target = nullptr;
D3D12_VIEWPORT m_viewport = {0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f};
GSVector4i m_scissor = GSVector4i::zero();
u8 m_blend_constant_color = 0;
u8 m_stencil_ref = 0;
bool m_in_render_pass = false;
std::array<D3D12_GPU_VIRTUAL_ADDRESS, NUM_TFX_CONSTANT_BUFFERS> m_tfx_constant_buffers{};
std::array<D3D12::DescriptorHandle, NUM_TOTAL_TFX_TEXTURES> m_tfx_textures{};
std::array<D3D12::DescriptorHandle, NUM_TFX_SAMPLERS> m_tfx_samplers{};
std::array<u32, NUM_TFX_SAMPLERS> m_tfx_sampler_sel{};
D3D12::DescriptorHandle m_tfx_textures_handle_gpu;
D3D12::DescriptorHandle m_tfx_samplers_handle_gpu;
D3D12::DescriptorHandle m_tfx_rt_textures_handle_gpu;
D3D12::DescriptorHandle m_utility_texture_cpu;
D3D12::DescriptorHandle m_utility_texture_gpu;
D3D12::DescriptorHandle m_utility_sampler_cpu;
D3D12::DescriptorHandle m_utility_sampler_gpu;
RootSignature m_current_root_signature = RootSignature::Undefined;
const ID3D12PipelineState* m_current_pipeline = nullptr;
D3D12::Texture m_null_texture;
// current pipeline selector - we save this in the struct to avoid re-zeroing it every draw
PipelineSelector m_pipeline_selector = {};
};

View File

@ -0,0 +1,393 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2021 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "PrecompiledHeader.h"
#include "GSTexture12.h"
#include "GSDevice12.h"
#include "common/Assertions.h"
#include "common/Align.h"
#include "common/D3D12/Builders.h"
#include "common/D3D12/Context.h"
#include "common/D3D12/Util.h"
#include "common/StringUtil.h"
#include "D3D12MemAlloc.h"
#include "GS/GSPerfMon.h"
#include "GS/GSGL.h"
GSTexture12::GSTexture12(Type type, Format format, D3D12::Texture texture)
: m_texture(std::move(texture))
{
m_type = type;
m_format = format;
m_size.x = m_texture.GetWidth();
m_size.y = m_texture.GetHeight();
m_mipmap_levels = m_texture.GetLevels();
}
GSTexture12::~GSTexture12()
{
GSDevice12::GetInstance()->UnbindTexture(this);
}
std::unique_ptr<GSTexture12> GSTexture12::Create(Type type, u32 width, u32 height, u32 levels, Format format,
DXGI_FORMAT d3d_format, DXGI_FORMAT srv_format, DXGI_FORMAT rtv_format, DXGI_FORMAT dsv_format)
{
switch (type)
{
case Type::Texture:
{
// this is a little annoying. basically, to do mipmap generation, we need to be a render target.
const D3D12_RESOURCE_FLAGS flags = (levels > 1) ? D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET : D3D12_RESOURCE_FLAG_NONE;
if (levels > 1 && d3d_format != DXGI_FORMAT_R8G8B8A8_UNORM)
{
Console.Warning("DX12: Refusing to create a %ux%u format %u mipmapped texture", width, height, format);
return nullptr;
}
D3D12::Texture texture;
if (!texture.Create(width, height, levels, d3d_format, srv_format,
DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, flags))
{
return {};
}
D3D12::SetObjectNameFormatted(texture.GetResource(), "%ux%u texture", width, height);
return std::make_unique<GSTexture12>(type, format, std::move(texture));
}
case Type::RenderTarget:
{
pxAssert(levels == 1);
// RT's tend to be larger, so we'll keep them committed for speed.
D3D12::Texture texture;
if (!texture.Create(width, height, levels, d3d_format, srv_format, rtv_format,
DXGI_FORMAT_UNKNOWN, D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET, D3D12MA::ALLOCATION_FLAG_COMMITTED))
{
return {};
}
D3D12::SetObjectNameFormatted(texture.GetResource(), "%ux%u render target", width, height);
return std::make_unique<GSTexture12>(type, format, std::move(texture));
}
case Type::DepthStencil:
{
pxAssert(levels == 1);
D3D12::Texture texture;
if (!texture.Create(width, height, levels, d3d_format, srv_format,
DXGI_FORMAT_UNKNOWN, dsv_format, D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL,
D3D12MA::ALLOCATION_FLAG_COMMITTED))
{
return {};
}
D3D12::SetObjectNameFormatted(texture.GetResource(), "%ux%u depth stencil", width, height);
return std::make_unique<GSTexture12>(type, format, std::move(texture));
}
default:
return {};
}
}
void* GSTexture12::GetNativeHandle() const { return const_cast<D3D12::Texture*>(&m_texture); }
ID3D12GraphicsCommandList* GSTexture12::GetCommandBufferForUpdate()
{
if (m_type != Type::Texture || m_use_fence_counter == g_d3d12_context->GetCurrentFenceValue())
{
// Console.WriteLn("Texture update within frame, can't use do beforehand");
GSDevice12::GetInstance()->EndRenderPass();
return g_d3d12_context->GetCommandList();
}
return g_d3d12_context->GetInitCommandList();
}
ID3D12Resource* GSTexture12::AllocateUploadStagingBuffer(const void* data, u32 pitch, u32 upload_pitch, u32 height) const
{
const u32 buffer_size = CalcUploadSize(height, upload_pitch);
wil::com_ptr_nothrow<ID3D12Resource> resource;
wil::com_ptr_nothrow<D3D12MA::Allocation> allocation;
const D3D12MA::ALLOCATION_DESC allocation_desc = {D3D12MA::ALLOCATION_FLAG_NONE, D3D12_HEAP_TYPE_UPLOAD};
const D3D12_RESOURCE_DESC resource_desc = {
D3D12_RESOURCE_DIMENSION_BUFFER, 0, buffer_size, 1, 1, 1, DXGI_FORMAT_UNKNOWN, {1, 0}, D3D12_TEXTURE_LAYOUT_ROW_MAJOR,
D3D12_RESOURCE_FLAG_NONE};
HRESULT hr = g_d3d12_context->GetAllocator()->CreateResource(&allocation_desc, &resource_desc, D3D12_RESOURCE_STATE_COPY_SOURCE,
nullptr, allocation.put(), IID_PPV_ARGS(resource.put()));
if (FAILED(hr))
{
Console.WriteLn("(AllocateUploadStagingBuffer) CreateCommittedResource() failed with %08X", hr);
return nullptr;
}
void* map_ptr;
hr = resource->Map(0, nullptr, &map_ptr);
if (FAILED(hr))
{
Console.WriteLn("(AllocateUploadStagingBuffer) Map() failed with %08X", hr);
return nullptr;
}
CopyTextureDataForUpload(map_ptr, data, pitch, upload_pitch, height);
const D3D12_RANGE write_range = {0, buffer_size};
resource->Unmap(0, &write_range);
// Immediately queue it for freeing after the command buffer finishes, since it's only needed for the copy.
// This adds the reference needed to keep the buffer alive.
g_d3d12_context->DeferResourceDestruction(allocation.get(), resource.get());
return resource.get();
}
void GSTexture12::CopyTextureDataForUpload(void* dst, const void* src, u32 pitch, u32 upload_pitch, u32 height) const
{
const u32 block_size = GetCompressedBlockSize();
const u32 count = (height + (block_size - 1)) / block_size;
StringUtil::StrideMemCpy(dst, upload_pitch, src, pitch, std::min(upload_pitch, pitch), count);
}
bool GSTexture12::Update(const GSVector4i& r, const void* data, int pitch, int layer)
{
if (layer >= m_mipmap_levels)
return false;
g_perfmon.Put(GSPerfMon::TextureUploads, 1);
const u32 width = r.width();
const u32 height = r.height();
const u32 upload_pitch = Common::AlignUpPow2<u32>(pitch, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
const u32 required_size = CalcUploadSize(height, upload_pitch);
D3D12_TEXTURE_COPY_LOCATION srcloc;
srcloc.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
srcloc.PlacedFootprint.Footprint.Width = width;
srcloc.PlacedFootprint.Footprint.Height = height;
srcloc.PlacedFootprint.Footprint.Depth = 1;
srcloc.PlacedFootprint.Footprint.Format = m_texture.GetFormat();
srcloc.PlacedFootprint.Footprint.RowPitch = upload_pitch;
// If the texture is larger than half our streaming buffer size, use a separate buffer.
// Otherwise allocation will either fail, or require lots of cmdbuffer submissions.
if (required_size > (g_d3d12_context->GetTextureStreamBuffer().GetSize() / 2))
{
srcloc.pResource = AllocateUploadStagingBuffer(data, pitch, upload_pitch, height);
if (!srcloc.pResource)
return false;
srcloc.PlacedFootprint.Offset = 0;
}
else
{
D3D12::StreamBuffer& sbuffer = g_d3d12_context->GetTextureStreamBuffer();
if (!sbuffer.ReserveMemory(required_size, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT))
{
GSDevice12::GetInstance()->ExecuteCommandList(
false, "While waiting for %u bytes in texture upload buffer", required_size);
if (!sbuffer.ReserveMemory(required_size, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT))
{
Console.Error("Failed to reserve texture upload memory (%u bytes).", required_size);
return false;
}
}
srcloc.pResource = sbuffer.GetBuffer();
srcloc.PlacedFootprint.Offset = sbuffer.GetCurrentOffset();
CopyTextureDataForUpload(sbuffer.GetCurrentHostPointer(), data, pitch, upload_pitch, height);
sbuffer.CommitMemory(required_size);
}
ID3D12GraphicsCommandList* cmdlist = GetCommandBufferForUpdate();
GL_PUSH("GSTexture12::Update({%d,%d} %dx%d Lvl:%u", r.x, r.y, r.width(), r.height(), layer);
// first time the texture is used? don't leave it undefined
if (m_texture.GetState() == D3D12_RESOURCE_STATE_COMMON)
m_texture.TransitionToState(cmdlist, D3D12_RESOURCE_STATE_COPY_DEST);
else if (m_texture.GetState() != D3D12_RESOURCE_STATE_COPY_DEST)
m_texture.TransitionSubresourceToState(cmdlist, layer, m_texture.GetState(), D3D12_RESOURCE_STATE_COPY_DEST);
// if we're an rt and have been cleared, and the full rect isn't being uploaded, do the clear
if (m_type == Type::RenderTarget)
{
if (!r.eq(GSVector4i(0, 0, m_size.x, m_size.y)))
CommitClear(cmdlist);
else
m_state = State::Dirty;
}
D3D12_TEXTURE_COPY_LOCATION dstloc;
dstloc.pResource = m_texture.GetResource();
dstloc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
dstloc.SubresourceIndex = layer;
const D3D12_BOX srcbox{0u, 0u, 0u, static_cast<UINT>(r.width()), static_cast<UINT>(r.height()), 1u};
cmdlist->CopyTextureRegion(&dstloc, r.x, r.y, 0, &srcloc, &srcbox);
if (m_texture.GetState() != D3D12_RESOURCE_STATE_COPY_DEST)
m_texture.TransitionSubresourceToState(cmdlist, layer, D3D12_RESOURCE_STATE_COPY_DEST, m_texture.GetState());
if (m_type == Type::Texture)
m_needs_mipmaps_generated |= (layer == 0);
return true;
}
bool GSTexture12::Map(GSMap& m, const GSVector4i* r, int layer)
{
if (layer >= m_mipmap_levels || IsCompressedFormat())
return false;
// map for writing
m_map_area = r ? *r : GSVector4i(0, 0, m_texture.GetWidth(), m_texture.GetHeight());
m_map_level = layer;
m.pitch = Common::AlignUpPow2(m_map_area.width() * D3D12::GetTexelSize(m_texture.GetFormat()), D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
// see note in Update() for the reason why.
const u32 required_size = CalcUploadSize(m_map_area.height(), m.pitch);
D3D12::StreamBuffer& buffer = g_d3d12_context->GetTextureStreamBuffer();
if (required_size >= (buffer.GetSize() / 2))
return false;
if (!buffer.ReserveMemory(required_size, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT))
{
GSDevice12::GetInstance()->ExecuteCommandList(
false, "While waiting for %u bytes in texture upload buffer", required_size);
if (!buffer.ReserveMemory(required_size, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT))
pxFailRel("Failed to reserve texture upload memory");
}
m.bits = static_cast<u8*>(buffer.GetCurrentHostPointer());
return true;
}
void GSTexture12::Unmap()
{
pxAssert(m_map_level < m_texture.GetLevels());
g_perfmon.Put(GSPerfMon::TextureUploads, 1);
// TODO: non-tightly-packed formats
const u32 width = static_cast<u32>(m_map_area.width());
const u32 height = static_cast<u32>(m_map_area.height());
const u32 pitch = Common::AlignUpPow2(m_map_area.width() * D3D12::GetTexelSize(m_texture.GetFormat()), D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
const u32 required_size = CalcUploadSize(height, pitch);
D3D12::StreamBuffer& buffer = g_d3d12_context->GetTextureStreamBuffer();
const u32 buffer_offset = buffer.GetCurrentOffset();
buffer.CommitMemory(required_size);
ID3D12GraphicsCommandList* cmdlist = GetCommandBufferForUpdate();
GL_PUSH("GSTexture12::Update({%d,%d} %dx%d Lvl:%u", m_map_area.x, m_map_area.y, m_map_area.width(),
m_map_area.height(), m_map_level);
// first time the texture is used? don't leave it undefined
if (m_texture.GetState() == D3D12_RESOURCE_STATE_COMMON)
m_texture.TransitionToState(cmdlist, D3D12_RESOURCE_STATE_COPY_DEST);
else if (m_texture.GetState() != D3D12_RESOURCE_STATE_COPY_DEST)
m_texture.TransitionSubresourceToState(cmdlist, m_map_level, m_texture.GetState(), D3D12_RESOURCE_STATE_COPY_DEST);
// if we're an rt and have been cleared, and the full rect isn't being uploaded, do the clear
if (m_type == Type::RenderTarget)
{
if (!m_map_area.eq(GSVector4i(0, 0, m_size.x, m_size.y)))
CommitClear(cmdlist);
else
m_state = State::Dirty;
}
D3D12_TEXTURE_COPY_LOCATION srcloc;
srcloc.pResource = buffer.GetBuffer();
srcloc.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
srcloc.PlacedFootprint.Offset = buffer_offset;
srcloc.PlacedFootprint.Footprint.Width = width;
srcloc.PlacedFootprint.Footprint.Height = height;
srcloc.PlacedFootprint.Footprint.Depth = 1;
srcloc.PlacedFootprint.Footprint.Format = m_texture.GetFormat();
srcloc.PlacedFootprint.Footprint.RowPitch = pitch;
D3D12_TEXTURE_COPY_LOCATION dstloc;
dstloc.pResource = m_texture.GetResource();
dstloc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
dstloc.SubresourceIndex = m_map_level;
const D3D12_BOX srcbox{0u, 0u, 0u, width, height, 1};
cmdlist->CopyTextureRegion(&dstloc, m_map_area.x, m_map_area.y, 0, &srcloc, &srcbox);
if (m_texture.GetState() != D3D12_RESOURCE_STATE_COPY_DEST)
m_texture.TransitionSubresourceToState(cmdlist, m_map_level, D3D12_RESOURCE_STATE_COPY_DEST, m_texture.GetState());
if (m_type == Type::Texture)
m_needs_mipmaps_generated |= (m_map_level == 0);
}
void GSTexture12::GenerateMipmap()
{
for (int dst_level = 1; dst_level < m_mipmap_levels; dst_level++)
{
const int src_level = dst_level - 1;
const int src_width = std::max<int>(m_size.x >> src_level, 1);
const int src_height = std::max<int>(m_size.y >> src_level, 1);
const int dst_width = std::max<int>(m_size.x >> dst_level, 1);
const int dst_height = std::max<int>(m_size.y >> dst_level, 1);
GSDevice12::GetInstance()->RenderTextureMipmap(m_texture,
dst_level, dst_width, dst_height, src_level, src_width, src_height);
}
SetUsedThisCommandBuffer();
}
void GSTexture12::Swap(GSTexture* tex)
{
GSTexture::Swap(tex);
std::swap(m_texture, static_cast<GSTexture12*>(tex)->m_texture);
std::swap(m_use_fence_counter, static_cast<GSTexture12*>(tex)->m_use_fence_counter);
std::swap(m_clear_value, static_cast<GSTexture12*>(tex)->m_clear_value);
std::swap(m_map_area, static_cast<GSTexture12*>(tex)->m_map_area);
std::swap(m_map_level, static_cast<GSTexture12*>(tex)->m_map_level);
}
void GSTexture12::TransitionToState(D3D12_RESOURCE_STATES state)
{
m_texture.TransitionToState(g_d3d12_context->GetCommandList(), state);
}
void GSTexture12::CommitClear()
{
if (m_state != GSTexture::State::Cleared)
return;
GSDevice12::GetInstance()->EndRenderPass();
CommitClear(g_d3d12_context->GetCommandList());
}
void GSTexture12::CommitClear(ID3D12GraphicsCommandList* cmdlist)
{
if (IsDepthStencil())
{
m_texture.TransitionToState(cmdlist, D3D12_RESOURCE_STATE_DEPTH_WRITE);
cmdlist->ClearDepthStencilView(m_texture.GetRTVOrDSVDescriptor(), D3D12_CLEAR_FLAG_DEPTH, m_clear_value.depth, 0, 0, nullptr);
}
else
{
m_texture.TransitionToState(cmdlist, D3D12_RESOURCE_STATE_RENDER_TARGET);
cmdlist->ClearRenderTargetView(m_texture.GetRTVOrDSVDescriptor(), m_clear_value.color, 0, nullptr);
}
SetState(GSTexture::State::Dirty);
}

View File

@ -0,0 +1,92 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2021 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "GS.h"
#include "GS/Renderers/Common/GSTexture.h"
#include "common/D3D12/Context.h"
#include "common/D3D12/Texture.h"
class GSTexture12 final : public GSTexture
{
public:
union alignas(16) ClearValue
{
float color[4];
float depth;
};
public:
GSTexture12(Type type, Format format, D3D12::Texture texture);
~GSTexture12() override;
static std::unique_ptr<GSTexture12> Create(Type type, u32 width, u32 height, u32 levels, Format format,
DXGI_FORMAT d3d_format, DXGI_FORMAT srv_format, DXGI_FORMAT rtv_format, DXGI_FORMAT dsv_format);
__fi D3D12::Texture& GetTexture() { return m_texture; }
__fi const D3D12::DescriptorHandle& GetSRVDescriptor() const { return m_texture.GetSRVDescriptor(); }
__fi const D3D12::DescriptorHandle& GetRTVOrDSVHandle() const { return m_texture.GetRTVOrDSVDescriptor(); }
__fi D3D12_RESOURCE_STATES GetResourceState() const { return m_texture.GetState(); }
__fi DXGI_FORMAT GetNativeFormat() const { return m_texture.GetFormat(); }
__fi ID3D12Resource* GetResource() const { return m_texture.GetResource(); }
__fi GSVector4 GetClearColor() const { return GSVector4::load<true>(m_clear_value.color); }
__fi float GetClearDepth() const { return m_clear_value.depth; }
void* GetNativeHandle() const override;
bool Update(const GSVector4i& r, const void* data, int pitch, int layer = 0) override;
bool Map(GSMap& m, const GSVector4i* r = NULL, int layer = 0) override;
void Unmap() override;
void GenerateMipmap() override;
void Swap(GSTexture* tex) override;
void TransitionToState(D3D12_RESOURCE_STATES state);
void CommitClear();
void CommitClear(ID3D12GraphicsCommandList* cmdlist);
__fi void SetClearColor(const GSVector4& color)
{
m_state = State::Cleared;
GSVector4::store<true>(m_clear_value.color, color);
}
__fi void SetClearDepth(float depth)
{
m_state = State::Cleared;
m_clear_value.depth = depth;
}
// Call when the texture is bound to the pipeline, or read from in a copy.
__fi void SetUsedThisCommandBuffer()
{
m_use_fence_counter = g_d3d12_context->GetCurrentFenceValue();
}
private:
ID3D12GraphicsCommandList* GetCommandBufferForUpdate();
ID3D12Resource* AllocateUploadStagingBuffer(const void* data, u32 pitch, u32 upload_pitch, u32 height) const;
void CopyTextureDataForUpload(void* dst, const void* src, u32 pitch, u32 upload_pitch, u32 height) const;
D3D12::Texture m_texture;
// Contains the fence counter when the texture was last used.
// When this matches the current fence counter, the texture was used this command buffer.
u64 m_use_fence_counter = 0;
ClearValue m_clear_value = {};
GSVector4i m_map_area = GSVector4i::zero();
u32 m_map_level = UINT32_MAX;
};

View File

@ -21,6 +21,7 @@
#ifdef _WIN32 #ifdef _WIN32
#include "Frontend/D3D11HostDisplay.h" #include "Frontend/D3D11HostDisplay.h"
#include "Frontend/D3D12HostDisplay.h"
#endif #endif
#include "GS/Renderers/Metal/GSMetalCPPAccessible.h" #include "GS/Renderers/Metal/GSMetalCPPAccessible.h"
@ -699,6 +700,9 @@ void Dialog::RendererChange()
case GSRendererType::DX11: case GSRendererType::DX11:
list = D3D11HostDisplay::StaticGetAdapterAndModeList(); list = D3D11HostDisplay::StaticGetAdapterAndModeList();
break; break;
case GSRendererType::DX12:
list = D3D12HostDisplay::StaticGetAdapterAndModeList();
break;
#endif #endif
#ifdef ENABLE_VULKAN #ifdef ENABLE_VULKAN
case GSRendererType::VK: case GSRendererType::VK:
@ -789,7 +793,7 @@ void Dialog::Update()
else else
{ {
// cross-tab dependencies yay // cross-tab dependencies yay
const bool is_hw = renderer == GSRendererType::OGL || renderer == GSRendererType::DX11 || renderer == GSRendererType::VK || renderer == GSRendererType::Metal; const bool is_hw = renderer == GSRendererType::OGL || renderer == GSRendererType::DX11 || renderer == GSRendererType::VK || renderer == GSRendererType::Metal || renderer == GSRendererType::DX12;
const bool is_upscale = m_renderer_panel->m_internal_resolution->GetSelection() != 0; const bool is_upscale = m_renderer_panel->m_internal_resolution->GetSelection() != 0;
m_hacks_panel->m_is_native_res = !is_hw || !is_upscale; m_hacks_panel->m_is_native_res = !is_hw || !is_upscale;
m_hacks_panel->m_is_hardware = is_hw; m_hacks_panel->m_is_hardware = is_hw;

View File

@ -281,6 +281,7 @@ const char* Pcsx2Config::GSOptions::GetRendererName(GSRendererType type)
{ {
case GSRendererType::Auto: return "Auto"; case GSRendererType::Auto: return "Auto";
case GSRendererType::DX11: return "Direct3D 11"; case GSRendererType::DX11: return "Direct3D 11";
case GSRendererType::DX12: return "Direct3D 12";
case GSRendererType::Metal: return "Metal"; case GSRendererType::Metal: return "Metal";
case GSRendererType::OGL: return "OpenGL"; case GSRendererType::OGL: return "OpenGL";
case GSRendererType::VK: return "Vulkan"; case GSRendererType::VK: return "Vulkan";
@ -652,7 +653,9 @@ void Pcsx2Config::GSOptions::MaskUpscalingHacks()
bool Pcsx2Config::GSOptions::UseHardwareRenderer() const bool Pcsx2Config::GSOptions::UseHardwareRenderer() const
{ {
return (Renderer == GSRendererType::DX11 || Renderer == GSRendererType::OGL || Renderer == GSRendererType::VK || Renderer == GSRendererType::Metal); return (Renderer == GSRendererType::DX11 || Renderer == GSRendererType::DX12 ||
Renderer == GSRendererType::OGL || Renderer == GSRendererType::VK ||
Renderer == GSRendererType::Metal);
} }
VsyncMode Pcsx2Config::GetEffectiveVsyncMode() const VsyncMode Pcsx2Config::GetEffectiveVsyncMode() const

View File

@ -129,6 +129,7 @@ Dialogs::GSDumpDialog::GSDumpDialog(wxWindow* parent)
#endif #endif
#if defined(_WIN32) #if defined(_WIN32)
rdoverrides.Add(Pcsx2Config::GSOptions::GetRendererName(GSRendererType::DX11)); rdoverrides.Add(Pcsx2Config::GSOptions::GetRendererName(GSRendererType::DX11));
rdoverrides.Add(Pcsx2Config::GSOptions::GetRendererName(GSRendererType::DX12));
#elif defined(__APPLE__) #elif defined(__APPLE__)
rdoverrides.Add(Pcsx2Config::GSOptions::GetRendererName(GSRendererType::Metal)); rdoverrides.Add(Pcsx2Config::GSOptions::GetRendererName(GSRendererType::Metal));
#endif #endif
@ -846,6 +847,11 @@ void Dialogs::GSDumpDialog::GSThread::ExecuteTaskInThread()
renderer = GSRendererType::Metal; renderer = GSRendererType::Metal;
#endif #endif
break; break;
#ifdef _WIN32
case 5:
renderer = GSRendererType::DX12;
break;
#endif
default: default:
break; break;
} }

View File

@ -331,6 +331,8 @@
<ClCompile Include="Gif_Logger.cpp" /> <ClCompile Include="Gif_Logger.cpp" />
<ClCompile Include="Gif_Unit.cpp" /> <ClCompile Include="Gif_Unit.cpp" />
<ClCompile Include="GS\Renderers\DX11\D3D.cpp" /> <ClCompile Include="GS\Renderers\DX11\D3D.cpp" />
<ClCompile Include="GS\Renderers\DX12\GSDevice12.cpp" />
<ClCompile Include="GS\Renderers\DX12\GSTexture12.cpp" />
<ClCompile Include="GS\Renderers\HW\GSTextureReplacementLoaders.cpp" /> <ClCompile Include="GS\Renderers\HW\GSTextureReplacementLoaders.cpp" />
<ClCompile Include="GS\Renderers\HW\GSTextureReplacements.cpp" /> <ClCompile Include="GS\Renderers\HW\GSTextureReplacements.cpp" />
<ClCompile Include="GS\Window\GSwxDialog.cpp" /> <ClCompile Include="GS\Window\GSwxDialog.cpp" />
@ -792,6 +794,8 @@
<ClInclude Include="GameDatabase.h" /> <ClInclude Include="GameDatabase.h" />
<ClInclude Include="Gif_Unit.h" /> <ClInclude Include="Gif_Unit.h" />
<ClInclude Include="GS\Renderers\DX11\D3D.h" /> <ClInclude Include="GS\Renderers\DX11\D3D.h" />
<ClInclude Include="GS\Renderers\DX12\GSDevice12.h" />
<ClInclude Include="GS\Renderers\DX12\GSTexture12.h" />
<ClInclude Include="GS\Renderers\HW\GSTextureReplacements.h" /> <ClInclude Include="GS\Renderers\HW\GSTextureReplacements.h" />
<ClInclude Include="GS\Window\GSwxDialog.h" /> <ClInclude Include="GS\Window\GSwxDialog.h" />
<ClInclude Include="GS\Renderers\Vulkan\GSDeviceVK.h" /> <ClInclude Include="GS\Renderers\Vulkan\GSDeviceVK.h" />

View File

@ -313,6 +313,9 @@
<Filter Include="System\Ps2\GS\Shaders\Direct3D11"> <Filter Include="System\Ps2\GS\Shaders\Direct3D11">
<UniqueIdentifier>{eb697f5b-85f5-424a-a7e4-8d8b73d3426e}</UniqueIdentifier> <UniqueIdentifier>{eb697f5b-85f5-424a-a7e4-8d8b73d3426e}</UniqueIdentifier>
</Filter> </Filter>
<Filter Include="System\Ps2\GS\Renderers\Direct3D12">
<UniqueIdentifier>{9cb1aa40-043d-47f7-86fd-837e10012e75}</UniqueIdentifier>
</Filter>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="Utilities\folderdesc.txt"> <None Include="Utilities\folderdesc.txt">
@ -1769,6 +1772,12 @@
<ClCompile Include="Frontend\D3D12HostDisplay.cpp"> <ClCompile Include="Frontend\D3D12HostDisplay.cpp">
<Filter>Host</Filter> <Filter>Host</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="GS\Renderers\DX12\GSTexture12.cpp">
<Filter>System\Ps2\GS\Renderers\Direct3D12</Filter>
</ClCompile>
<ClCompile Include="GS\Renderers\DX12\GSDevice12.cpp">
<Filter>System\Ps2\GS\Renderers\Direct3D12</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="Patch.h"> <ClInclude Include="Patch.h">
@ -2942,6 +2951,12 @@
<ClInclude Include="Frontend\D3D12HostDisplay.h"> <ClInclude Include="Frontend\D3D12HostDisplay.h">
<Filter>Host</Filter> <Filter>Host</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="GS\Renderers\DX12\GSDevice12.h">
<Filter>System\Ps2\GS\Renderers\Direct3D12</Filter>
</ClInclude>
<ClInclude Include="GS\Renderers\DX12\GSTexture12.h">
<Filter>System\Ps2\GS\Renderers\Direct3D12</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ResourceCompile Include="windows\wxResources.rc"> <ResourceCompile Include="windows\wxResources.rc">

View File

@ -202,6 +202,8 @@
<ClCompile Include="Gif_Unit.cpp" /> <ClCompile Include="Gif_Unit.cpp" />
<ClCompile Include="GSDumpReplayer.cpp" /> <ClCompile Include="GSDumpReplayer.cpp" />
<ClCompile Include="GS\Renderers\DX11\D3D.cpp" /> <ClCompile Include="GS\Renderers\DX11\D3D.cpp" />
<ClCompile Include="GS\Renderers\DX12\GSDevice12.cpp" />
<ClCompile Include="GS\Renderers\DX12\GSTexture12.cpp" />
<ClCompile Include="GS\Renderers\HW\GSTextureReplacementLoaders.cpp" /> <ClCompile Include="GS\Renderers\HW\GSTextureReplacementLoaders.cpp" />
<ClCompile Include="GS\Renderers\HW\GSTextureReplacements.cpp" /> <ClCompile Include="GS\Renderers\HW\GSTextureReplacements.cpp" />
<ClCompile Include="GS\Window\GSwxDialog.cpp" /> <ClCompile Include="GS\Window\GSwxDialog.cpp" />
@ -516,6 +518,8 @@
<ClInclude Include="Gif_Unit.h" /> <ClInclude Include="Gif_Unit.h" />
<ClInclude Include="GSDumpReplayer.h" /> <ClInclude Include="GSDumpReplayer.h" />
<ClInclude Include="GS\Renderers\DX11\D3D.h" /> <ClInclude Include="GS\Renderers\DX11\D3D.h" />
<ClInclude Include="GS\Renderers\DX12\GSDevice12.h" />
<ClInclude Include="GS\Renderers\DX12\GSTexture12.h" />
<ClInclude Include="GS\Renderers\HW\GSTextureReplacements.h" /> <ClInclude Include="GS\Renderers\HW\GSTextureReplacements.h" />
<ClInclude Include="GS\Window\GSwxDialog.h" /> <ClInclude Include="GS\Window\GSwxDialog.h" />
<ClInclude Include="GS\Renderers\Vulkan\GSDeviceVK.h" /> <ClInclude Include="GS\Renderers\Vulkan\GSDeviceVK.h" />

View File

@ -229,6 +229,9 @@
<Filter Include="System\Ps2\GS\Shaders\Direct3D11"> <Filter Include="System\Ps2\GS\Shaders\Direct3D11">
<UniqueIdentifier>{eb697f5b-85f5-424a-a7e4-8d8b73d3426e}</UniqueIdentifier> <UniqueIdentifier>{eb697f5b-85f5-424a-a7e4-8d8b73d3426e}</UniqueIdentifier>
</Filter> </Filter>
<Filter Include="System\Ps2\GS\Renderers\Direct3D12">
<UniqueIdentifier>{3951f622-8975-4d9a-9bc0-65e6646b9416}</UniqueIdentifier>
</Filter>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="Utilities\folderdesc.txt"> <None Include="Utilities\folderdesc.txt">
@ -1251,6 +1254,12 @@
<Filter>System\Ps2\EmotionEngine\EE\Dynarec</Filter> <Filter>System\Ps2\EmotionEngine\EE\Dynarec</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="GSDumpReplayer.cpp" /> <ClCompile Include="GSDumpReplayer.cpp" />
<ClCompile Include="GS\Renderers\DX12\GSTexture12.cpp">
<Filter>System\Ps2\GS\Renderers\Direct3D12</Filter>
</ClCompile>
<ClCompile Include="GS\Renderers\DX12\GSDevice12.cpp">
<Filter>System\Ps2\GS\Renderers\Direct3D12</Filter>
</ClCompile>
<ClCompile Include="Frontend\D3D12HostDisplay.cpp"> <ClCompile Include="Frontend\D3D12HostDisplay.cpp">
<Filter>Host</Filter> <Filter>Host</Filter>
</ClCompile> </ClCompile>
@ -2068,6 +2077,12 @@
<Filter>System\Ps2\EmotionEngine\EE\Dynarec</Filter> <Filter>System\Ps2\EmotionEngine\EE\Dynarec</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="GSDumpReplayer.h" /> <ClInclude Include="GSDumpReplayer.h" />
<ClInclude Include="GS\Renderers\DX12\GSTexture12.h">
<Filter>System\Ps2\GS\Renderers\Direct3D12</Filter>
</ClInclude>
<ClInclude Include="GS\Renderers\DX12\GSDevice12.h">
<Filter>System\Ps2\GS\Renderers\Direct3D12</Filter>
</ClInclude>
<ClInclude Include="Frontend\D3D12HostDisplay.h"> <ClInclude Include="Frontend\D3D12HostDisplay.h">
<Filter>Host</Filter> <Filter>Host</Filter>
</ClInclude> </ClInclude>