GS/DX11: Merge depth + color copies in to one combined shader blit copy.

This commit is contained in:
lightningterror
2026-03-11 17:11:51 +01:00
parent 3000e113e2
commit fbe5bbeaa6
4 changed files with 215 additions and 51 deletions
+48 -1
View File
@@ -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;
+157 -46
View File
@@ -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<ShaderConvert>(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<GSTexture11*>(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);
}
+9 -3
View File
@@ -182,7 +182,11 @@ private:
{
wil::com_ptr_nothrow<ID3D11InputLayout> il;
wil::com_ptr_nothrow<ID3D11VertexShader> vs;
wil::com_ptr_nothrow<ID3D11VertexShader> vs_blit_1to1;
wil::com_ptr_nothrow<ID3D11PixelShader> ps[static_cast<int>(ShaderConvert::Count)];
wil::com_ptr_nothrow<ID3D11PixelShader> ps_blit_1to1_color;
wil::com_ptr_nothrow<ID3D11PixelShader> ps_blit_1to1_depth;
wil::com_ptr_nothrow<ID3D11PixelShader> ps_blit_1to1_combined;
wil::com_ptr_nothrow<ID3D11SamplerState> ln;
wil::com_ptr_nothrow<ID3D11SamplerState> pt;
wil::com_ptr_nothrow<ID3D11DepthStencilState> 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;
+1 -1
View File
@@ -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);