diff --git a/bin/resources/shaders/dx11/convert.fx b/bin/resources/shaders/dx11/convert.fx index 7bd8f2f3f6..adb6247506 100644 --- a/bin/resources/shaders/dx11/convert.fx +++ b/bin/resources/shaders/dx11/convert.fx @@ -30,7 +30,9 @@ static const float3x3 rgb2yuv = {-0.419, -0.081, 0.500} }; -Texture2D Texture; +Texture2D Texture : register(t0); +Texture2D RTTexture : register(t2); +Texture2D DSDepth : register(t4); SamplerState TextureSampler; float4 sample_c(float2 uv) @@ -50,6 +52,12 @@ struct PS_OUTPUT float4 c : SV_Target0; }; +struct PS_OUTPUT_BLIT_COMBINED +{ + float4 color : SV_Target0; + float depth : SV_Depth; +}; + VS_OUTPUT vs_main(VS_INPUT input) { VS_OUTPUT output; @@ -61,6 +69,22 @@ VS_OUTPUT vs_main(VS_INPUT input) return output; } +VS_OUTPUT vs_blit_1to1(uint vID : SV_VertexID) +{ + VS_OUTPUT output; + + // Hard-coded positions for the Oversized Triangle. + static const float2 positions[3] = { + float2(-1.0f, 1.0f), + float2( 3.0f, 1.0f), + float2(-1.0f, -3.0f) + }; + + output.p = float4(positions[vID], 0.0f, 1.0f); + + return output; +} + PS_OUTPUT ps_copy(PS_INPUT input) { PS_OUTPUT output; @@ -70,6 +94,29 @@ PS_OUTPUT ps_copy(PS_INPUT input) return output; } +float4 ps_blit_1to1_color(float4 pos : SV_Position) : SV_Target +{ + int3 coord = int3(pos.xy, 0); + return RTTexture.Load(coord); +} + +float ps_blit_1to1_depth(float4 pos : SV_Position) : SV_Depth +{ + int3 coord = int3(pos.xy, 0); + return DSDepth.Load(coord).r; +} + +PS_OUTPUT_BLIT_COMBINED ps_blit_1to1_combined(float4 pos : SV_Position) +{ + PS_OUTPUT_BLIT_COMBINED blit_out; + int3 coord = int3(pos.xy, 0); + + blit_out.color = RTTexture.Load(coord); + blit_out.depth = DSDepth.Load(coord).r; + + return blit_out; +} + float ps_depth_copy(PS_INPUT input) : SV_Depth { return sample_c(input.t).r; diff --git a/pcsx2/GS/Renderers/DX11/GSDevice11.cpp b/pcsx2/GS/Renderers/DX11/GSDevice11.cpp index 3e2be6411a..a5be1b8fd2 100644 --- a/pcsx2/GS/Renderers/DX11/GSDevice11.cpp +++ b/pcsx2/GS/Renderers/DX11/GSDevice11.cpp @@ -225,6 +225,12 @@ bool GSDevice11::Create(GSVSyncMode vsync_mode, bool allow_present_throttle) return false; } + if (!m_shader_cache.GetVertexShaderAndInputLayout(m_dev.get(), m_convert.vs_blit_1to1.put(), nullptr, + il_convert, std::size(il_convert), *convert_hlsl, nullptr, "vs_blit_1to1")) + { + return false; + } + for (size_t i = 0; i < std::size(m_convert.ps); i++) { m_convert.ps[i] = m_shader_cache.GetPixelShader(m_dev.get(), *convert_hlsl, nullptr, shaderName(static_cast(i))); @@ -232,6 +238,18 @@ bool GSDevice11::Create(GSVSyncMode vsync_mode, bool allow_present_throttle) return false; } + m_convert.ps_blit_1to1_color = m_shader_cache.GetPixelShader(m_dev.get(), *convert_hlsl, nullptr, "ps_blit_1to1_color"); + if (!m_convert.ps_blit_1to1_color) + return false; + + m_convert.ps_blit_1to1_depth = m_shader_cache.GetPixelShader(m_dev.get(), *convert_hlsl, nullptr, "ps_blit_1to1_depth"); + if (!m_convert.ps_blit_1to1_depth) + return false; + + m_convert.ps_blit_1to1_combined = m_shader_cache.GetPixelShader(m_dev.get(), *convert_hlsl, nullptr, "ps_blit_1to1_combined"); + if (!m_convert.ps_blit_1to1_combined) + return false; + shader = ReadShaderSource("shaders/dx11/present.fx"); if (!shader.has_value()) return false; @@ -1444,7 +1462,6 @@ void GSDevice11::DoStretchRect(GSTexture* sTex, const GSVector4& sRect, GSTextur {GSVector4(right, bottom, 0.5f, 1.0f), GSVector2(sRect.z, sRect.w)}, }; - IASetVertexBuffer(vertices, sizeof(vertices[0]), std::size(vertices)); IASetInputLayout(m_convert.il.get()); IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); @@ -1464,6 +1481,60 @@ void GSDevice11::DoStretchRect(GSTexture* sTex, const GSVector4& sRect, GSTextur DrawPrimitive(); } +void GSDevice11::BlitOnetoOne(GSTexture* sTexRT, GSTexture* dTexRT, GSTexture* sTexDS, GSTexture* dTexDS, const GSVector4i& scissor) +{ + g_perfmon.Put(GSPerfMon::TextureCopies, 1); + + if (sTexRT) + CommitClear(sTexRT); + if (sTexDS) + CommitClear(sTexDS); + + // om + + OMSetRenderTargets(dTexRT, dTexDS, &scissor); + + if (dTexDS) + OMSetDepthStencilState(m_convert.dss_write.get(), 0); + else + OMSetDepthStencilState(m_convert.dss.get(), 0); + + if (dTexRT) + OMSetBlendState(m_convert.bs[D3D11_COLOR_WRITE_ENABLE_ALL].get(), 0); + else + OMSetBlendState(nullptr, 0); + + // ia + + IASetInputLayout(nullptr); + IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + + // vs + + VSSetShader(m_convert.vs_blit_1to1.get(), nullptr); + + // ps + + if (sTexRT) + PSSetShaderResource(2, sTexRT); + if (sTexDS) + PSSetShaderResource(4, sTexDS); + + if (dTexRT && dTexDS) + PSSetShader(m_convert.ps_blit_1to1_combined.get(), nullptr); + else if (dTexDS) + PSSetShader(m_convert.ps_blit_1to1_depth.get(), nullptr); + else + PSSetShader(m_convert.ps_blit_1to1_color.get(), nullptr); + + PSUpdateShaderState(true, false); + + // draw + + g_perfmon.Put(GSPerfMon::DrawCalls, 1); + m_ctx->Draw(3, 0); +} + void GSDevice11::PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, PresentShader shader, float shaderTime, bool linear) { g_perfmon.Put(GSPerfMon::TextureCopies, 1); @@ -2476,6 +2547,21 @@ void GSDevice11::IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY topology) } } +D3D11_PRIMITIVE_TOPOLOGY GSDevice11::IAGetPrimitiveTopology(GSHWDrawConfig::Topology topology) +{ + switch (topology) + { + case GSHWDrawConfig::Topology::Point: + return D3D11_PRIMITIVE_TOPOLOGY_POINTLIST; + case GSHWDrawConfig::Topology::Line: + return D3D11_PRIMITIVE_TOPOLOGY_LINELIST; + case GSHWDrawConfig::Topology::Triangle: + return D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST; + default: + return D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED; + } +} + void GSDevice11::VSSetShader(ID3D11VertexShader* vs, ID3D11Buffer* vs_cb) { if (m_state.vs != vs) @@ -2893,15 +2979,6 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config) } } - D3D11_PRIMITIVE_TOPOLOGY topology = D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED; - switch (config.topology) - { - case GSHWDrawConfig::Topology::Point: topology = D3D11_PRIMITIVE_TOPOLOGY_POINTLIST; break; - case GSHWDrawConfig::Topology::Line: topology = D3D11_PRIMITIVE_TOPOLOGY_LINELIST; break; - case GSHWDrawConfig::Topology::Triangle: topology = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST; break; - } - IASetPrimitiveTopology(topology); - // Depth testing and sampling, bind resource as dsv read only and srv at the same time without the need of a copy. // Disable this for depth feedback loops, since in those cases we simultaneously read/write depth. ID3D11DepthStencilView* read_only_dsv = nullptr; @@ -2913,23 +2990,22 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config) config.tex = nullptr; } - SetupVS(config.vs, &config.cb_vs); - SetupPS(config.ps, &config.cb_ps, config.sampler); - if (primid_texture) { + IASetPrimitiveTopology(IAGetPrimitiveTopology(config.topology)); OMDepthStencilSelector dss = config.depth; dss.zwe = 0; const OMBlendSelector blend(GSHWDrawConfig::ColorMaskSelector(1), GSHWDrawConfig::BlendState(true, CONST_ONE, CONST_ONE, 3 /* MIN */, CONST_ONE, CONST_ZERO, false, 0)); SetupOM(dss, blend, 0); + SetupVS(config.vs, &config.cb_vs); + SetupPS(config.ps, &config.cb_ps, config.sampler); OMSetRenderTargets(primid_texture, config.ds, &config.scissor, read_only_dsv); SetRenderHWShaderResources(config, nullptr); Draw(config); config.ps.date = 3; config.alpha_second_pass.ps.date = 3; - SetupPS(config.ps, nullptr, config.sampler); } // Avoid changing framebuffer just to switch from rt+depth to rt and vice versa. @@ -2951,41 +3027,48 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config) const bool tex_is_fb = config.tex && config.tex == draw_rt; const bool rt_feedbackloop_pass1 = config.ps.IsFeedbackLoopRT() || tex_is_fb; const bool rt_feedbackloop_pass2 = config.alpha_second_pass.ps.IsFeedbackLoopRT() || tex_is_fb; + + const bool ds_feedbackloop_pass1 = config.ps.IsFeedbackLoopDepth(); + const bool ds_feedbackloop_pass2 = config.alpha_second_pass.ps.IsFeedbackLoopDepth(); + + const bool combined_copy_pass1 = rt_feedbackloop_pass1 && ds_feedbackloop_pass1; + const bool combined_copy_pass2 = rt_feedbackloop_pass2 && ds_feedbackloop_pass2; + const bool combined_copy = combined_copy_pass1 || combined_copy_pass2; if (draw_rt && (((config.require_one_barrier || (config.require_full_barrier && m_features.multidraw_fb_copy)) && (rt_feedbackloop_pass1 || rt_feedbackloop_pass2)))) { config.require_one_barrier |= (tex_is_fb && !config.require_full_barrier); - config.alpha_second_pass.require_one_barrier |= (tex_is_fb && !config.require_full_barrier); + config.alpha_second_pass.require_one_barrier |= (tex_is_fb && !config.alpha_second_pass.require_full_barrier); // Requires a copy of the RT. - draw_rt_clone = CreateTexture(rtsize.x, rtsize.y, 1, draw_rt->GetFormat(), true); + if (combined_copy) + draw_rt_clone = CreateRenderTarget(rtsize.x, rtsize.y, draw_rt->GetFormat(), false, true); + else + draw_rt_clone = CreateTexture(rtsize.x, rtsize.y, 1, draw_rt->GetFormat(), true); if (!draw_rt_clone) Console.Warning("D3D11: Failed to allocate temp texture for RT copy."); } - const bool ds_feedbackloop_pass1 = config.ps.IsFeedbackLoopDepth(); - const bool ds_feedbackloop_pass2 = config.alpha_second_pass.ps.IsFeedbackLoopDepth(); if (draw_ds && m_features.depth_feedback && (config.require_one_barrier || (config.require_full_barrier && m_features.multidraw_fb_copy)) && (ds_feedbackloop_pass1 || ds_feedbackloop_pass2)) { // Requires a copy of the DS. - draw_ds_clone = CreateTexture(rtsize.x, rtsize.y, 1, draw_ds->GetFormat(), true); + if (combined_copy) + draw_ds_clone = CreateDepthStencil(rtsize.x, rtsize.y, draw_ds->GetFormat(), false, true); + else + draw_ds_clone = CreateTexture(rtsize.x, rtsize.y, 1, draw_ds->GetFormat(), true); if (!draw_ds_clone) Console.Warning("D3D11: Failed to allocate temp texture for DS copy."); } - OMSetRenderTargets(draw_rt, draw_ds, &config.scissor, read_only_dsv); - SetRenderHWShaderResources(config, primid_texture); - SetupOM(config.depth, OMBlendSelector(config.colormask, config.blend), config.blend.constant); - // Clear stencil as close as possible to the RT bind, to avoid framebuffer swaps. if (config.destination_alpha == GSHWDrawConfig::DestinationAlphaMode::StencilOne && need_barrier) m_ctx->ClearDepthStencilView(*static_cast(draw_ds), D3D11_CLEAR_STENCIL, 0.0f, 1); - SendHWDraw(config, rt_feedbackloop_pass1 ? draw_rt_clone : nullptr, draw_rt, ds_feedbackloop_pass1 ? draw_ds_clone : nullptr, draw_ds, - config.require_one_barrier, config.require_full_barrier); + SendHWDraw(config, rt_feedbackloop_pass1 ? draw_rt_clone : nullptr, draw_rt, ds_feedbackloop_pass1 ? draw_ds_clone : nullptr, draw_ds, read_only_dsv, primid_texture, + config.require_one_barrier, config.require_full_barrier, combined_copy, false); if (config.blend_multi_pass.enable) { @@ -2999,21 +3082,9 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config) if (config.alpha_second_pass.enable) { - if (config.cb_ps.FogColor_AREF.a != config.alpha_second_pass.ps_aref) - { - config.cb_ps.FogColor_AREF.a = config.alpha_second_pass.ps_aref; - SetupPS(config.alpha_second_pass.ps, &config.cb_ps, config.sampler); - } - else - { - // ps cbuffer hasn't changed, so don't bother checking - SetupPS(config.alpha_second_pass.ps, nullptr, config.sampler); - } - const bool one_barrier = config.alpha_second_pass.require_one_barrier && m_features.multidraw_fb_copy; - SetupOM(config.alpha_second_pass.depth, OMBlendSelector(config.alpha_second_pass.colormask, config.blend), config.blend.constant); - SendHWDraw(config, rt_feedbackloop_pass2 ? draw_rt_clone : nullptr, draw_rt, ds_feedbackloop_pass2 ? draw_ds_clone : nullptr, draw_ds, - one_barrier, config.alpha_second_pass.require_full_barrier); + SendHWDraw(config, rt_feedbackloop_pass2 ? draw_rt_clone : nullptr, draw_rt, ds_feedbackloop_pass2 ? draw_ds_clone : nullptr, draw_ds, read_only_dsv, primid_texture, + one_barrier, config.alpha_second_pass.require_full_barrier, combined_copy, config.alpha_second_pass.enable); } if (colclip_rt) @@ -3033,28 +3104,66 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config) } } -void GSDevice11::SendHWDraw(const GSHWDrawConfig& config, - GSTexture* draw_rt_clone, GSTexture* draw_rt, GSTexture* draw_ds_clone, GSTexture* draw_ds, - const bool one_barrier, const bool full_barrier) +void GSDevice11::SendHWDraw(GSHWDrawConfig& config, + GSTexture* draw_rt_clone, GSTexture* draw_rt, GSTexture* draw_ds_clone, GSTexture* draw_ds, ID3D11DepthStencilView* read_only_dsv, GSTexture* primid_texture, + const bool one_barrier, const bool full_barrier, const bool shader_blit, const bool alpha_second_pass) { #ifdef PCSX2_DEVBUILD if ((one_barrier || full_barrier) && !(config.ps.IsFeedbackLoopRT() || config.ps.IsFeedbackLoopDepth())) [[unlikely]] Console.Warning("D3D11: Possible unnecessary copy detected."); #endif + // InitializePipeline must be called after CopyAndBind and before Draw. + auto InitializePipeline = [&]() { + IASetPrimitiveTopology(IAGetPrimitiveTopology(config.topology)); + SetupVS(config.vs, &config.cb_vs); + if (alpha_second_pass) + { + if (config.cb_ps.FogColor_AREF.a != config.alpha_second_pass.ps_aref) + { + config.cb_ps.FogColor_AREF.a = config.alpha_second_pass.ps_aref; + SetupPS(config.alpha_second_pass.ps, &config.cb_ps, config.sampler); + } + else + { + // ps cbuffer hasn't changed, so don't bother checking + SetupPS(config.alpha_second_pass.ps, &config.cb_ps, config.sampler); + } + + SetupOM(config.alpha_second_pass.depth, OMBlendSelector(config.alpha_second_pass.colormask, config.blend), config.blend.constant); + } + else + { + SetupPS(config.ps, &config.cb_ps, config.sampler); + SetupOM(config.depth, OMBlendSelector(config.colormask, config.blend), config.blend.constant); + } + OMSetRenderTargets(draw_rt, draw_ds, &config.scissor, read_only_dsv); + + // Set Shader resources, must be done after OMSetRenderTargets. + SetRenderHWShaderResources(config, primid_texture); - auto CopyAndBind = [&](GSVector4i drawarea) { if (draw_rt_clone) { - CopyRect(draw_rt, draw_rt_clone, drawarea, drawarea.left, drawarea.top); if ((one_barrier || full_barrier)) PSSetShaderResource(2, draw_rt_clone); if (config.tex && config.tex == draw_rt) PSSetShaderResource(0, draw_rt_clone); } if (draw_ds_clone) - { - CopyRect(draw_ds, draw_ds_clone, drawarea, drawarea.left, drawarea.top); PSSetShaderResource(4, draw_ds_clone); + }; + + auto CopyAndBind = [&](GSVector4i drawarea) { + if (shader_blit && (draw_rt_clone || draw_ds_clone)) + { + BlitOnetoOne(draw_rt, draw_rt_clone, draw_ds, draw_ds_clone, config.scissor); + } + else + { + if (draw_rt_clone) + CopyRect(draw_rt, draw_rt_clone, drawarea, drawarea.left, drawarea.top); + + if (draw_ds_clone) + CopyRect(draw_ds, draw_ds_clone, drawarea, drawarea.left, drawarea.top); } }; @@ -3075,6 +3184,7 @@ void GSDevice11::SendHWDraw(const GSHWDrawConfig& config, const GSVector4i original_bbox = (*config.drawlist_bbox)[n].rintersect(config.drawarea); CopyAndBind(ProcessCopyArea(rtsize, original_bbox)); + InitializePipeline(); Draw(config, p, count); p += count; @@ -3086,6 +3196,7 @@ void GSDevice11::SendHWDraw(const GSHWDrawConfig& config, if (one_barrier) CopyAndBind(ProcessCopyArea(rtsize, config.drawarea)); + InitializePipeline(); Draw(config); } diff --git a/pcsx2/GS/Renderers/DX11/GSDevice11.h b/pcsx2/GS/Renderers/DX11/GSDevice11.h index 5729c1ec3e..7c6918a057 100644 --- a/pcsx2/GS/Renderers/DX11/GSDevice11.h +++ b/pcsx2/GS/Renderers/DX11/GSDevice11.h @@ -182,7 +182,11 @@ private: { wil::com_ptr_nothrow il; wil::com_ptr_nothrow vs; + wil::com_ptr_nothrow vs_blit_1to1; wil::com_ptr_nothrow ps[static_cast(ShaderConvert::Count)]; + wil::com_ptr_nothrow ps_blit_1to1_color; + wil::com_ptr_nothrow ps_blit_1to1_depth; + wil::com_ptr_nothrow ps_blit_1to1_combined; wil::com_ptr_nothrow ln; wil::com_ptr_nothrow pt; wil::com_ptr_nothrow dss; @@ -321,6 +325,7 @@ public: void CommitClear(GSTexture* t); void CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r, u32 destX, u32 destY) override; + void BlitOnetoOne(GSTexture* sTexRT, GSTexture* dTexRT, GSTexture* sTexDS, GSTexture* dTexDS, const GSVector4i& scissor); void DoStretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, ID3D11PixelShader* ps, ID3D11Buffer* ps_cb, bool linear); void DoStretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, ID3D11PixelShader* ps, ID3D11Buffer* ps_cb, ID3D11BlendState* bs, bool linear); @@ -346,6 +351,7 @@ public: void IASetInputLayout(ID3D11InputLayout* layout); void IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY topology); + D3D11_PRIMITIVE_TOPOLOGY IAGetPrimitiveTopology(GSHWDrawConfig::Topology topology); void VSSetShader(ID3D11VertexShader* vs, ID3D11Buffer* vs_cb); void VSSetPushConstants(u32 base_vertex, u32 base_index = 0, bool force_update = false); @@ -372,9 +378,9 @@ public: void SetupOM(OMDepthStencilSelector dssel, OMBlendSelector bsel, u8 afix); void RenderHW(GSHWDrawConfig& config) override; - void SendHWDraw(const GSHWDrawConfig& config, - GSTexture* draw_rt_clone, GSTexture* draw_rt, GSTexture* draw_ds_clone, GSTexture* draw_ds, - const bool one_barrier, const bool full_barrier); + void SendHWDraw(GSHWDrawConfig& config, + GSTexture* draw_rt_clone, GSTexture* draw_rt, GSTexture* draw_ds_clone, GSTexture* draw_ds, ID3D11DepthStencilView* read_only_dsv, GSTexture* primid_texture, + const bool one_barrier, const bool full_barrier, const bool shader_blit, const bool alpha_second_pass); void SetRenderHWShaderResources(const GSHWDrawConfig& config, GSTexture* primid_texture); void ClearSamplerCache() override; diff --git a/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.cpp b/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.cpp index 8d82687de7..1f44a8129a 100644 --- a/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.cpp +++ b/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.cpp @@ -2938,7 +2938,7 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config) (rt_feedbackloop_pass1 || rt_feedbackloop_pass2)))) { config.require_one_barrier |= (tex_is_fb && !config.require_full_barrier); - config.alpha_second_pass.require_one_barrier |= (tex_is_fb && !config.require_full_barrier); + config.alpha_second_pass.require_one_barrier |= (tex_is_fb && !config.alpha_second_pass.require_full_barrier); // Requires a copy of the RT. draw_rt_clone = CreateTexture(rtsize.x, rtsize.y, 1, draw_rt->GetFormat(), true);