mirror of https://github.com/PCSX2/pcsx2.git
GS: Fix edge case of broken alpha with no-DSB+ATST
This commit is contained in:
parent
0592abd31b
commit
3f31a4d25b
|
@ -477,6 +477,33 @@ bool GSDevice::ResizeTarget(GSTexture** t)
|
|||
return ResizeTexture(t, GSTexture::Type::RenderTarget, s.x, s.y);
|
||||
}
|
||||
|
||||
void GSDevice::SetHWDrawConfigForAlphaPass(GSHWDrawConfig::PSSelector* ps,
|
||||
GSHWDrawConfig::ColorMaskSelector* cms,
|
||||
GSHWDrawConfig::BlendState* bs,
|
||||
GSHWDrawConfig::DepthStencilSelector* dss)
|
||||
{
|
||||
// only need to compute the alpha component (allow the shader to optimize better)
|
||||
ps->no_ablend = false;
|
||||
ps->only_alpha = true;
|
||||
|
||||
// definitely don't need to compute software blend (this may get rid of some barriers)
|
||||
ps->blend_a = ps->blend_b = ps->blend_c = ps->blend_d = 0;
|
||||
|
||||
// only write alpha (RGB=0,A=1)
|
||||
cms->wrgba = (1 << 3);
|
||||
|
||||
// no need for hardware blending, since we're not writing RGB
|
||||
bs->enable = false;
|
||||
|
||||
// if depth writes are on, we can optimize to an EQUAL test, otherwise we leave the tests alone
|
||||
// since the alpha channel isn't blended, the last fragment wins and this'll be okay
|
||||
if (dss->zwe)
|
||||
{
|
||||
dss->zwe = false;
|
||||
dss->ztst = ZTST_GEQUAL;
|
||||
}
|
||||
}
|
||||
|
||||
GSAdapter::operator std::string() const
|
||||
{
|
||||
char buf[sizeof "12345678:12345678:12345678:12345678"];
|
||||
|
|
|
@ -557,6 +557,8 @@ struct alignas(16) GSHWDrawConfig
|
|||
DestinationAlphaMode destination_alpha;
|
||||
bool datm : 1;
|
||||
bool line_expand : 1;
|
||||
bool separate_alpha_pass : 1;
|
||||
bool second_separate_alpha_pass : 1;
|
||||
|
||||
struct AlphaPass
|
||||
{
|
||||
|
@ -569,7 +571,6 @@ struct alignas(16) GSHWDrawConfig
|
|||
static_assert(sizeof(AlphaPass) == 24, "alpha pass is 24 bytes");
|
||||
|
||||
AlphaPass alpha_second_pass;
|
||||
AlphaPass alpha_third_pass;
|
||||
|
||||
VSConstantBuffer cb_vs;
|
||||
PSConstantBuffer cb_ps;
|
||||
|
@ -775,6 +776,12 @@ public:
|
|||
return (IsDualSourceBlendFactor(m_blendMap[index].src) ||
|
||||
IsDualSourceBlendFactor(m_blendMap[index].dst));
|
||||
}
|
||||
|
||||
/// Alters the pipeline configuration for drawing the separate alpha pass.
|
||||
static void SetHWDrawConfigForAlphaPass(GSHWDrawConfig::PSSelector* ps,
|
||||
GSHWDrawConfig::ColorMaskSelector* cms,
|
||||
GSHWDrawConfig::BlendState* bs,
|
||||
GSHWDrawConfig::DepthStencilSelector* dss);
|
||||
};
|
||||
|
||||
struct GSAdapter
|
||||
|
|
|
@ -1348,6 +1348,16 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
|
|||
|
||||
DrawIndexedPrimitive();
|
||||
|
||||
if (config.separate_alpha_pass)
|
||||
{
|
||||
GSHWDrawConfig::BlendState sap_blend = {};
|
||||
SetHWDrawConfigForAlphaPass(&config.ps, &config.colormask, &sap_blend, &config.depth);
|
||||
SetupOM(config.depth, convertSel(config.colormask, sap_blend), config.blend.constant);
|
||||
SetupPS(config.ps, &config.cb_ps, config.sampler);
|
||||
|
||||
DrawIndexedPrimitive();
|
||||
}
|
||||
|
||||
if (config.alpha_second_pass.enable)
|
||||
{
|
||||
preprocessSel(config.alpha_second_pass.ps);
|
||||
|
@ -1365,6 +1375,16 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
|
|||
SetupOM(config.alpha_second_pass.depth, convertSel(config.alpha_second_pass.colormask, config.blend), config.blend.constant);
|
||||
|
||||
DrawIndexedPrimitive();
|
||||
|
||||
if (config.second_separate_alpha_pass)
|
||||
{
|
||||
GSHWDrawConfig::BlendState sap_blend = {};
|
||||
SetHWDrawConfigForAlphaPass(&config.alpha_second_pass.ps, &config.alpha_second_pass.colormask, &sap_blend, &config.alpha_second_pass.depth);
|
||||
SetupOM(config.alpha_second_pass.depth, convertSel(config.alpha_second_pass.colormask, sap_blend), config.blend.constant);
|
||||
SetupPS(config.alpha_second_pass.ps, &config.cb_ps, config.sampler);
|
||||
|
||||
DrawIndexedPrimitive();
|
||||
}
|
||||
}
|
||||
|
||||
EndScene();
|
||||
|
|
|
@ -1821,41 +1821,30 @@ void GSRendererNew::DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Sour
|
|||
|
||||
if (blending_alpha_pass)
|
||||
{
|
||||
// ensure alpha blend output is disabled on both passes
|
||||
// write alpha blend as the single alpha output
|
||||
m_conf.ps.no_ablend = true;
|
||||
|
||||
// if we're doing RGBA then Z alpha testing, we can't combine Z and A, and we need a third pass :(
|
||||
if (ate_RGBA_then_Z)
|
||||
// there's a case we can skip this: RGB_then_ZA alternate handling.
|
||||
// but otherwise, we need to write alpha separately.
|
||||
if (m_conf.colormask.wa)
|
||||
{
|
||||
// move second pass to third pass, since we want to write A first
|
||||
std::memcpy(&m_conf.alpha_third_pass, &m_conf.alpha_second_pass, sizeof(m_conf.alpha_third_pass));
|
||||
m_conf.alpha_third_pass.ps.no_ablend = true;
|
||||
m_conf.alpha_second_pass.enable = false;
|
||||
}
|
||||
|
||||
if (!m_conf.alpha_second_pass.enable)
|
||||
{
|
||||
m_conf.alpha_second_pass.enable = true;
|
||||
memcpy(&m_conf.alpha_second_pass.ps, &m_conf.ps, sizeof(m_conf.ps));
|
||||
memcpy(&m_conf.alpha_second_pass.colormask, &m_conf.colormask, sizeof(m_conf.colormask));
|
||||
memcpy(&m_conf.alpha_second_pass.depth, &m_conf.depth, sizeof(m_conf.depth));
|
||||
|
||||
// disable alpha writes on first pass
|
||||
m_conf.colormask.wa = false;
|
||||
m_conf.separate_alpha_pass = true;
|
||||
}
|
||||
|
||||
// only need to compute the alpha component (allow the shader to optimize better)
|
||||
m_conf.alpha_second_pass.ps.no_ablend = false;
|
||||
m_conf.alpha_second_pass.ps.only_alpha = true;
|
||||
m_conf.alpha_second_pass.colormask.wr = m_conf.alpha_second_pass.colormask.wg = m_conf.alpha_second_pass.colormask.wb = false;
|
||||
m_conf.alpha_second_pass.colormask.wa = true;
|
||||
|
||||
// if depth writes are on, we can optimize to an EQUAL test, otherwise we leave the tests alone
|
||||
// since the alpha channel isn't blended, the last fragment wins and this'll be okay
|
||||
if (m_conf.alpha_second_pass.depth.zwe)
|
||||
// do we need to do this for the failed alpha fragments?
|
||||
if (m_conf.alpha_second_pass.enable)
|
||||
{
|
||||
m_conf.alpha_second_pass.depth.zwe = false;
|
||||
m_conf.alpha_second_pass.depth.ztst = ZTST_GEQUAL;
|
||||
// there's also a case we can skip here: when we're not writing RGB, there's
|
||||
// no blending, so we can just write the normal alpha!
|
||||
const u8 second_pass_wrgba = m_conf.alpha_second_pass.colormask.wrgba;
|
||||
if ((second_pass_wrgba & (1 << 3)) != 0 && second_pass_wrgba != (1 << 3))
|
||||
{
|
||||
// this sucks. potentially up to 4 passes. but no way around it when we don't have dual-source blend.
|
||||
m_conf.alpha_second_pass.ps.no_ablend = true;
|
||||
m_conf.alpha_second_pass.colormask.wa = false;
|
||||
m_conf.second_separate_alpha_pass = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1961,6 +1961,25 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
|
|||
|
||||
SendHWDraw(config);
|
||||
|
||||
if (config.separate_alpha_pass)
|
||||
{
|
||||
GSHWDrawConfig::BlendState dummy_bs;
|
||||
SetHWDrawConfigForAlphaPass(&psel.ps, &config.colormask, &dummy_bs, &config.depth);
|
||||
SetupPipeline(psel);
|
||||
OMSetColorMaskState(config.alpha_second_pass.colormask);
|
||||
SetupOM(config.alpha_second_pass.depth);
|
||||
OMSetBlendState();
|
||||
SendHWDraw(config);
|
||||
|
||||
// restore blend state if we're doing a second pass
|
||||
if (config.alpha_second_pass.enable)
|
||||
{
|
||||
OMSetBlendState(config.blend.enable, s_gl_blend_factors[config.blend.src_factor],
|
||||
s_gl_blend_factors[config.blend.dst_factor], s_gl_blend_ops[config.blend.op],
|
||||
config.blend.constant_enable, config.blend.constant);
|
||||
}
|
||||
}
|
||||
|
||||
if (config.alpha_second_pass.enable)
|
||||
{
|
||||
// cbuffer will definitely be dirty if aref changes, no need to check it
|
||||
|
@ -1975,25 +1994,18 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
|
|||
SetupPipeline(psel);
|
||||
OMSetColorMaskState(config.alpha_second_pass.colormask);
|
||||
SetupOM(config.alpha_second_pass.depth);
|
||||
|
||||
SendHWDraw(config);
|
||||
}
|
||||
if (config.alpha_third_pass.enable)
|
||||
{
|
||||
// cbuffer will definitely be dirty if aref changes, no need to check it
|
||||
if (config.cb_ps.FogColor_AREF.a != config.alpha_third_pass.ps_aref)
|
||||
|
||||
if (config.second_separate_alpha_pass)
|
||||
{
|
||||
config.cb_ps.FogColor_AREF.a = config.alpha_third_pass.ps_aref;
|
||||
WriteToStreamBuffer(m_fragment_uniform_stream_buffer.get(), g_ps_cb_index,
|
||||
m_uniform_buffer_alignment, &config.cb_ps, sizeof(config.cb_ps));
|
||||
GSHWDrawConfig::BlendState dummy_bs;
|
||||
SetHWDrawConfigForAlphaPass(&psel.ps, &config.colormask, &dummy_bs, &config.depth);
|
||||
SetupPipeline(psel);
|
||||
OMSetColorMaskState(config.alpha_second_pass.colormask);
|
||||
SetupOM(config.alpha_second_pass.depth);
|
||||
OMSetBlendState();
|
||||
SendHWDraw(config);
|
||||
}
|
||||
|
||||
psel.ps = config.alpha_third_pass.ps;
|
||||
SetupPipeline(psel);
|
||||
OMSetColorMaskState(config.alpha_third_pass.colormask);
|
||||
SetupOM(config.alpha_third_pass.depth);
|
||||
|
||||
SendHWDraw(config);
|
||||
}
|
||||
|
||||
if (config.destination_alpha == GSHWDrawConfig::DestinationAlphaMode::PrimIDTracking)
|
||||
|
|
|
@ -3026,7 +3026,15 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
|
|||
|
||||
// now we can do the actual draw
|
||||
if (BindDrawPipeline(pipe))
|
||||
{
|
||||
SendHWDraw(config, draw_rt);
|
||||
if (config.separate_alpha_pass)
|
||||
{
|
||||
SetHWDrawConfigForAlphaPass(&pipe.ps, &pipe.cms, &pipe.bs, &pipe.dss);
|
||||
if (BindDrawPipeline(pipe))
|
||||
SendHWDraw(config, draw_rt);
|
||||
}
|
||||
}
|
||||
|
||||
// and the alpha pass
|
||||
if (config.alpha_second_pass.enable)
|
||||
|
@ -3041,23 +3049,17 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
|
|||
pipe.ps = config.alpha_second_pass.ps;
|
||||
pipe.cms = config.alpha_second_pass.colormask;
|
||||
pipe.dss = config.alpha_second_pass.depth;
|
||||
pipe.bs = config.blend;
|
||||
if (BindDrawPipeline(pipe))
|
||||
SendHWDraw(config, draw_rt);
|
||||
}
|
||||
if (config.alpha_third_pass.enable)
|
||||
{
|
||||
// cbuffer will definitely be dirty if aref changes, no need to check it
|
||||
if (config.cb_ps.FogColor_AREF.a != config.alpha_third_pass.ps_aref)
|
||||
{
|
||||
config.cb_ps.FogColor_AREF.a = config.alpha_third_pass.ps_aref;
|
||||
SetPSConstantBuffer(config.cb_ps);
|
||||
}
|
||||
|
||||
pipe.ps = config.alpha_third_pass.ps;
|
||||
pipe.cms = config.alpha_third_pass.colormask;
|
||||
pipe.dss = config.alpha_third_pass.depth;
|
||||
if (BindDrawPipeline(pipe))
|
||||
SendHWDraw(config, draw_rt);
|
||||
if (config.second_separate_alpha_pass)
|
||||
{
|
||||
SetHWDrawConfigForAlphaPass(&pipe.ps, &pipe.cms, &pipe.bs, &pipe.dss);
|
||||
if (BindDrawPipeline(pipe))
|
||||
SendHWDraw(config, draw_rt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (copy_ds)
|
||||
|
|
Loading…
Reference in New Issue