GS/DX11: Implement shader copies for feedback loops.

This commit is contained in:
lightningterror
2026-03-11 17:11:51 +01:00
parent 1ddc283ea2
commit bb0d63d9ae
4 changed files with 133 additions and 28 deletions
+21
View File
@@ -61,6 +61,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 +86,11 @@ PS_OUTPUT ps_copy(PS_INPUT input)
return output;
}
float4 ps_blit_1to1(float4 pos : SV_Position) : SV_Target
{
return Texture.Load(int3(pos.xy, 0));
}
float ps_depth_copy(PS_INPUT input) : SV_Depth
{
return sample_c(input.t).r;
+105 -26
View File
@@ -222,6 +222,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)));
@@ -229,6 +235,10 @@ bool GSDevice11::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
return false;
}
m_convert.ps_ps_blit_1to1 = m_shader_cache.GetPixelShader(m_dev.get(), *convert_hlsl, nullptr, "ps_blit_1to1");
if (!m_convert.ps_ps_blit_1to1)
return false;
shader = ReadShaderSource("shaders/dx11/present.fx");
if (!shader.has_value())
return false;
@@ -1370,7 +1380,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);
@@ -1390,6 +1399,48 @@ void GSDevice11::DoStretchRect(GSTexture* sTex, const GSVector4& sRect, GSTextur
DrawPrimitive();
}
void GSDevice11::BlitOnetoOne(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r)
{
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
CommitClear(sTex);
// preemptively bind srv if possible
if (m_state.cached_rt_view != sTex && m_state.cached_dsv != sTex)
PSSetShaderResource(0, sTex);
// ps unbind conflicting srvs
PSUnbindConflictingSRVs(dTex);
// om
OMSetRenderTargets(dTex, nullptr, &r);
OMSetDepthStencilState(m_convert.dss.get(), 0);
OMSetBlendState(nullptr, 0);
// ia
IASetInputLayout(nullptr);
IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
// vs
VSSetShader(m_convert.vs_blit_1to1.get(), nullptr);
// ps
PSSetShaderResource(0, sTex);
PSSetShader(m_convert.ps_ps_blit_1to1.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);
@@ -2416,6 +2467,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)
@@ -2757,15 +2823,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.
// TODO: On older feature levels read only depth is not supported, we can fallback to a copy but a better solution
// is to wait for Depth feedback loops, in this case we can use it always, unbind the dsv and do testing in shader
@@ -2800,22 +2857,21 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
if (config.pal)
PSSetShaderResource(1, config.pal);
SetupVS(config.vs, &config.cb_vs);
SetupPS(config.ps, &config.cb_ps, config.sampler);
if (primid_texture)
{
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));
IASetPrimitiveTopology(IAGetPrimitiveTopology(config.topology));
SetupVS(config.vs, &config.cb_vs);
SetupPS(config.ps, &config.cb_ps, config.sampler);
SetupOM(dss, blend, 0);
OMSetRenderTargets(primid_texture, config.ds, &config.scissor, read_only_dsv);
DrawIndexedPrimitive();
config.ps.date = 3;
config.alpha_second_pass.ps.date = 3;
SetupPS(config.ps, nullptr, config.sampler);
PSSetShaderResource(3, primid_texture);
}
@@ -2842,20 +2898,24 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
{
// Requires a copy of the RT.
// Used as "bind rt" flag when texture barrier is unsupported for tex is fb.
draw_rt_clone = CreateTexture(rtsize.x, rtsize.y, 1, draw_rt->GetFormat(), true);
// Drawarea threshold, if the size exceeds 512 then prefer gpu copy, otherwise shader copy.
constexpr int area_size_x_y = 512 * 512;
const bool multipass = config.blend_multi_pass.enable || config.alpha_second_pass.enable;
if (multipass || (config.drawarea.width() * config.drawarea.height() > area_size_x_y))
draw_rt_clone = CreateTexture(rtsize.x, rtsize.y,1, draw_rt->GetFormat(), true);
else
draw_rt_clone = CreateRenderTarget(rtsize.x, rtsize.y, draw_rt->GetFormat(), false, true);
if (!draw_rt_clone)
Console.Warning("D3D11: Failed to allocate temp texture for RT copy.");
}
OMSetRenderTargets(draw_rt, draw_ds, &config.scissor, read_only_dsv);
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 && multidraw_fb_copy)
m_ctx->ClearDepthStencilView(*static_cast<GSTexture11*>(draw_ds), D3D11_CLEAR_STENCIL, 0.0f, 1);
SendHWDraw(config, draw_rt_clone, draw_rt, config.require_one_barrier, config.require_full_barrier, false);
SendHWDraw(config, draw_rt_clone, draw_rt, draw_ds, read_only_dsv, config.require_one_barrier, config.require_full_barrier, false);
if (config.blend_multi_pass.enable)
{
@@ -2881,7 +2941,7 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
}
SetupOM(config.alpha_second_pass.depth, OMBlendSelector(config.alpha_second_pass.colormask, config.blend), config.blend.constant);
SendHWDraw(config, draw_rt_clone, draw_rt, config.alpha_second_pass.require_one_barrier, config.alpha_second_pass.require_full_barrier, true);
SendHWDraw(config, draw_rt_clone, draw_rt, draw_ds, read_only_dsv, config.alpha_second_pass.require_one_barrier, config.alpha_second_pass.require_full_barrier, true);
}
if (draw_rt_clone)
@@ -2907,8 +2967,26 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
}
}
void GSDevice11::SendHWDraw(const GSHWDrawConfig& config, GSTexture* draw_rt_clone, GSTexture* draw_rt, const bool one_barrier, const bool full_barrier, const bool skip_first_barrier)
void GSDevice11::SendHWDraw(GSHWDrawConfig& config, GSTexture* draw_rt_clone, GSTexture* draw_rt, GSTexture* draw_ds, ID3D11DepthStencilView* read_only_dsv,
const bool one_barrier, const bool full_barrier, const bool skip_first_barrier)
{
auto SetupPipeline = [&]() {
IASetPrimitiveTopology(IAGetPrimitiveTopology(config.topology));
SetupVS(config.vs, &config.cb_vs);
SetupPS(config.ps, config.ps.date == 3 ? nullptr : &config.cb_ps, config.sampler);
SetupOM(config.depth, OMBlendSelector(config.colormask, config.blend), config.blend.constant);
PSUnbindConflictingSRVs(draw_rt);
OMSetRenderTargets(draw_rt, draw_ds, &config.scissor, read_only_dsv);
if (config.tex)
PSSetShaderResource(0, config.tex);
if (config.tex && config.tex == draw_rt)
PSSetShaderResource(0, draw_rt_clone);
if (one_barrier || full_barrier)
PSSetShaderResource(2, draw_rt_clone);
};
if (draw_rt_clone)
{
#ifdef PCSX2_DEVBUILD
@@ -2917,11 +2995,10 @@ void GSDevice11::SendHWDraw(const GSHWDrawConfig& config, GSTexture* draw_rt_clo
#endif
auto CopyAndBind = [&](GSVector4i drawarea) {
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 == config.rt)
PSSetShaderResource(0, draw_rt_clone);
if (draw_rt_clone->GetType() == GSTexture::Type::Texture)
CopyRect(draw_rt, draw_rt_clone, drawarea, drawarea.left, drawarea.top);
else
BlitOnetoOne(draw_rt, draw_rt_clone, drawarea);
};
const GSVector4i rtsize(0, 0, draw_rt->GetWidth(), draw_rt->GetHeight());
@@ -2941,6 +3018,7 @@ void GSDevice11::SendHWDraw(const GSHWDrawConfig& config, GSTexture* draw_rt_clo
const GSVector4i original_bbox = (*config.drawlist_bbox)[n].rintersect(config.drawarea);
CopyAndBind(ProcessCopyArea(rtsize, original_bbox));
SetupPipeline();
DrawIndexedPrimitive(p, count);
p += count;
}
@@ -2953,5 +3031,6 @@ void GSDevice11::SendHWDraw(const GSHWDrawConfig& config, GSTexture* draw_rt_clo
CopyAndBind(ProcessCopyArea(rtsize, config.drawarea));
}
SetupPipeline();
DrawIndexedPrimitive();
}
+6 -1
View File
@@ -177,7 +177,9 @@ 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_ps_blit_1to1;
wil::com_ptr_nothrow<ID3D11SamplerState> ln;
wil::com_ptr_nothrow<ID3D11SamplerState> pt;
wil::com_ptr_nothrow<ID3D11DepthStencilState> dss;
@@ -308,6 +310,7 @@ public:
void CommitClear(GSTexture* t);
void CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r, u32 destX, u32 destY) override;
void BlitOnetoOne(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r);
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);
@@ -332,6 +335,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);
@@ -354,7 +358,8 @@ 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, const bool one_barrier, const bool full_barrier, const bool skip_first_barrier);
void SendHWDraw(GSHWDrawConfig& config, GSTexture* draw_rt_clone, GSTexture* draw_rt, GSTexture* draw_ds, ID3D11DepthStencilView* read_only_dsv,
const bool one_barrier, const bool full_barrier, const bool skip_first_barrier);
void ClearSamplerCache() override;
+1 -1
View File
@@ -3,4 +3,4 @@
/// Version number for GS and other shaders. Increment whenever any of the contents of the
/// shaders change, to invalidate the cache.
static constexpr u32 SHADER_CACHE_VERSION = 83;
static constexpr u32 SHADER_CACHE_VERSION = 84;