mirror of https://github.com/PCSX2/pcsx2.git
GS: Use tex-is-fb for sprites/shuffles
This commit is contained in:
parent
7ba6e4adc3
commit
36defdfbe9
|
@ -28,6 +28,7 @@ GSRendererHW::GSRendererHW()
|
|||
, m_tc(new GSTextureCache())
|
||||
, m_src(nullptr)
|
||||
, m_reset(false)
|
||||
, m_tex_is_fb(false)
|
||||
, m_channel_shuffle(false)
|
||||
, m_userhacks_tcoffset(false)
|
||||
, m_userhacks_tcoffset_x(0)
|
||||
|
@ -120,10 +121,41 @@ void GSRendererHW::PurgeTextureCache()
|
|||
m_tc->RemoveAll();
|
||||
}
|
||||
|
||||
bool GSRendererHW::IsPossibleTextureShuffle(GSTextureCache::Source* src) const
|
||||
bool GSRendererHW::UpdateTexIsFB(GSTextureCache::Target* dst, const GIFRegTEX0& TEX0)
|
||||
{
|
||||
if (GSConfig.AccurateBlendingUnit == AccBlendLevel::Minimum || !g_gs_device->Features().texture_barrier)
|
||||
return false;
|
||||
|
||||
// Texture is actually the frame buffer. Stencil emulation to compute shadow (Jak series/tri-ace game)
|
||||
// Will hit the "m_ps_sel.tex_is_fb = 1" path in the draw
|
||||
if (m_vt.m_primclass == GS_TRIANGLE_CLASS)
|
||||
{
|
||||
if (m_context->FRAME.FBMSK == 0x00FFFFFF && TEX0.TBP0 == m_context->FRAME.Block())
|
||||
m_tex_is_fb = true;
|
||||
}
|
||||
else if (m_vt.m_primclass == GS_SPRITE_CLASS)
|
||||
{
|
||||
if (TEX0.TBP0 == m_context->FRAME.Block())
|
||||
{
|
||||
m_tex_is_fb = IsPossibleTextureShuffle(dst, TEX0);
|
||||
|
||||
if (!m_tex_is_fb && !m_vt.IsLinear())
|
||||
{
|
||||
// Make sure that we're not sampling away from the area we're rendering.
|
||||
const GSVector4 diff(m_vt.m_min.p.xyxy(m_vt.m_max.p) - m_vt.m_min.t.xyxy(m_vt.m_max.t));
|
||||
if ((diff < GSVector4(1.0f)).alltrue())
|
||||
m_tex_is_fb = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return m_tex_is_fb;
|
||||
}
|
||||
|
||||
bool GSRendererHW::IsPossibleTextureShuffle(GSTextureCache::Target* dst, const GIFRegTEX0& TEX0) const
|
||||
{
|
||||
return (PRIM->TME && m_vt.m_primclass == GS_SPRITE_CLASS &&
|
||||
src->m_32_bits_fmt && GSLocalMemory::m_psm[src->m_TEX0.PSM].bpp == 16 &&
|
||||
dst->m_32_bits_fmt && GSLocalMemory::m_psm[TEX0.PSM].bpp == 16 &&
|
||||
GSLocalMemory::m_psm[m_context->FRAME.PSM].bpp == 16);
|
||||
}
|
||||
|
||||
|
@ -1320,6 +1352,7 @@ void GSRendererHW::Draw()
|
|||
|
||||
m_src = nullptr;
|
||||
m_texture_shuffle = false;
|
||||
m_tex_is_fb = false;
|
||||
|
||||
if (PRIM->TME)
|
||||
{
|
||||
|
@ -2256,21 +2289,8 @@ void GSRendererHW::EmulateChannelShuffle(const GSTextureCache::Source* tex)
|
|||
m_conf.tex = *tex->m_from_target;
|
||||
if (m_conf.tex)
|
||||
{
|
||||
if (m_conf.tex == m_conf.rt)
|
||||
{
|
||||
// sample from fb instead
|
||||
m_conf.tex = nullptr;
|
||||
m_conf.ps.tex_is_fb = true;
|
||||
m_conf.require_one_barrier = true;
|
||||
}
|
||||
else if (m_conf.tex == m_conf.ds)
|
||||
{
|
||||
// if depth testing is disabled, we don't need to copy, and can just unbind the depth buffer
|
||||
// no need for a barrier for GL either, since it's not bound to depth and texture concurrently
|
||||
// otherwise, the backend should recognise the hazard, and copy the buffer (D3D/Vulkan).
|
||||
if (m_conf.depth.ztst == ZTST_ALWAYS)
|
||||
m_conf.ds = nullptr;
|
||||
}
|
||||
// Identify when we're sampling the current buffer, defer fixup for later.
|
||||
m_tex_is_fb |= (m_conf.tex == m_conf.rt || m_conf.tex == m_conf.ds);
|
||||
}
|
||||
|
||||
// Replace current draw with a fullscreen sprite
|
||||
|
@ -3070,6 +3090,41 @@ void GSRendererHW::EmulateTextureSampler(const GSTextureCache::Source* tex)
|
|||
if (!m_channel_shuffle)
|
||||
m_conf.tex = tex->m_texture;
|
||||
m_conf.pal = tex->m_palette;
|
||||
|
||||
// Detect framebuffer read that will need special handling
|
||||
if (m_tex_is_fb)
|
||||
{
|
||||
if (m_conf.tex == m_conf.rt)
|
||||
{
|
||||
// This pattern is used by several games to emulate a stencil (shadow)
|
||||
// Ratchet & Clank, Jak do alpha integer multiplication (tfx) which is mostly equivalent to +1/-1
|
||||
// Tri-Ace (Star Ocean 3/RadiataStories/VP2) uses a palette to handle the +1/-1
|
||||
GL_DBG("Source and Target are the same! Let's sample the framebuffer");
|
||||
m_conf.tex = nullptr;
|
||||
m_conf.ps.tex_is_fb = true;
|
||||
if (m_prim_overlap == PRIM_OVERLAP_NO || !g_gs_device->Features().texture_barrier)
|
||||
m_conf.require_one_barrier = true;
|
||||
else
|
||||
m_conf.require_full_barrier = true;
|
||||
}
|
||||
else if (m_conf.tex == m_conf.ds)
|
||||
{
|
||||
// if depth testing is disabled, we don't need to copy, and can just unbind the depth buffer
|
||||
// no need for a barrier for GL either, since it's not bound to depth and texture concurrently
|
||||
// otherwise, the backend should recognise the hazard, and copy the buffer (D3D/Vulkan).
|
||||
if (m_conf.depth.ztst == ZTST_ALWAYS)
|
||||
{
|
||||
m_conf.ds = nullptr;
|
||||
m_tex_is_fb = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// weird... we detected a fb read, but didn't end up using it?
|
||||
DevCon.WriteLn("Tex-is-FB set but not used?");
|
||||
m_tex_is_fb = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GSRendererHW::EmulateATST(float& AREF, GSHWDrawConfig::PSSelector& ps, bool pass_2)
|
||||
|
@ -3172,27 +3227,6 @@ void GSRendererHW::DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Sourc
|
|||
else
|
||||
m_prim_overlap = PRIM_OVERLAP_UNKNOW;
|
||||
|
||||
// Detect framebuffer read that will need special handling
|
||||
if (features.texture_barrier && (m_context->FRAME.Block() == m_context->TEX0.TBP0) && PRIM->TME && GSConfig.AccurateBlendingUnit != AccBlendLevel::Minimum)
|
||||
{
|
||||
const u32 fb_mask = GSLocalMemory::m_psm[m_context->FRAME.PSM].fmsk;
|
||||
if (((m_context->FRAME.FBMSK & fb_mask) == (fb_mask & 0x00FFFFFF)) && (m_vt.m_primclass == GS_TRIANGLE_CLASS))
|
||||
{
|
||||
// This pattern is used by several games to emulate a stencil (shadow)
|
||||
// Ratchet & Clank, Jak do alpha integer multiplication (tfx) which is mostly equivalent to +1/-1
|
||||
// Tri-Ace (Star Ocean 3/RadiataStories/VP2) uses a palette to handle the +1/-1
|
||||
GL_DBG("Source and Target are the same! Let's sample the framebuffer");
|
||||
m_conf.ps.tex_is_fb = 1;
|
||||
m_conf.require_full_barrier = !features.framebuffer_fetch;
|
||||
}
|
||||
else if (m_prim_overlap != PRIM_OVERLAP_NO)
|
||||
{
|
||||
// Note: It is fine if the texture fits in a single GS page. First access will cache
|
||||
// the page in the GS texture buffer.
|
||||
GL_INS("ERROR: Source and Target are the same!");
|
||||
}
|
||||
}
|
||||
|
||||
EmulateTextureShuffleAndFbmask();
|
||||
|
||||
// DATE: selection of the algorithm. Must be done before blending because GL42 is not compatible with blending
|
||||
|
@ -3635,13 +3669,6 @@ void GSRendererHW::DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Sourc
|
|||
g_gs_device->RenderHW(m_conf);
|
||||
}
|
||||
|
||||
bool GSRendererHW::IsDummyTexture() const
|
||||
{
|
||||
// Texture is actually the frame buffer. Stencil emulation to compute shadow (Jak series/tri-ace game)
|
||||
// Will hit the "m_ps_sel.tex_is_fb = 1" path in the draw
|
||||
return g_gs_device->Features().texture_barrier && (m_context->FRAME.Block() == m_context->TEX0.TBP0) && PRIM->TME && GSConfig.AccurateBlendingUnit != AccBlendLevel::Minimum && m_vt.m_primclass == GS_TRIANGLE_CLASS && (m_context->FRAME.FBMSK == 0x00FFFFFF);
|
||||
}
|
||||
|
||||
bool GSRendererHW::CanUseSwPrimRender(bool no_rt, bool no_ds, bool draw_sprite_tex)
|
||||
{
|
||||
// Master enable.
|
||||
|
|
|
@ -151,6 +151,7 @@ private:
|
|||
GSTextureCache::Source* m_src;
|
||||
|
||||
bool m_reset;
|
||||
bool m_tex_is_fb;
|
||||
bool m_channel_shuffle;
|
||||
bool m_userhacks_tcoffset;
|
||||
float m_userhacks_tcoffset_x;
|
||||
|
@ -201,8 +202,8 @@ public:
|
|||
void PurgeTextureCache() override;
|
||||
|
||||
// Called by the texture cache to know if current texture is useful
|
||||
bool IsDummyTexture() const;
|
||||
bool UpdateTexIsFB(GSTextureCache::Target* src, const GIFRegTEX0& TEX0);
|
||||
|
||||
// Called by the texture cache when optimizing the copy range for sources
|
||||
bool IsPossibleTextureShuffle(GSTextureCache::Source* src) const;
|
||||
bool IsPossibleTextureShuffle(GSTextureCache::Target* dst, const GIFRegTEX0& TEX0) const;
|
||||
};
|
||||
|
|
|
@ -1458,7 +1458,7 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
|
|||
AttachPaletteToSource(src, psm.pal, true);
|
||||
}
|
||||
}
|
||||
else if (dst && GSRendererHW::GetInstance()->IsDummyTexture())
|
||||
else if (dst && GSRendererHW::GetInstance()->UpdateTexIsFB(dst, TEX0))
|
||||
{
|
||||
// This shortcut is a temporary solution. It isn't a good solution
|
||||
// as it won't work with Channel Shuffle/Texture Shuffle pattern
|
||||
|
@ -1470,21 +1470,23 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
|
|||
// Be aware that you can't use StrechRect between BeginScene/EndScene.
|
||||
// So it could be tricky to put in the middle of the DrawPrims
|
||||
|
||||
// Texture is created to keep code compatibility
|
||||
GSTexture* dTex = g_gs_device->CreateTexture(tw, th, false, GSTexture::Format::Color, true);
|
||||
|
||||
// Keep a trace of origin of the texture
|
||||
src->m_texture = dTex;
|
||||
src->m_texture = dst->m_texture;
|
||||
src->m_target = true;
|
||||
src->m_shared_texture = true;
|
||||
src->m_from_target = &dst->m_texture;
|
||||
src->m_from_target_TEX0 = dst->m_TEX0;
|
||||
src->m_end_block = dst->m_end_block;
|
||||
src->m_texture->SetScale(dst->m_texture->GetScale());
|
||||
src->m_32_bits_fmt = dst->m_32_bits_fmt;
|
||||
|
||||
// Even if we sample the framebuffer directly we might need the palette
|
||||
// to handle the format conversion on GPU
|
||||
if (psm.pal > 0)
|
||||
AttachPaletteToSource(src, psm.pal, true);
|
||||
|
||||
// This will get immediately invalidated.
|
||||
m_temporary_source = src;
|
||||
}
|
||||
else if (dst)
|
||||
{
|
||||
|
@ -1626,7 +1628,7 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
|
|||
{
|
||||
// if it looks like a texture shuffle, we might read up to +/- 8 pixels on either side.
|
||||
GSVector4 adjusted_src_range(*src_range);
|
||||
if (GSRendererHW::GetInstance()->IsPossibleTextureShuffle(src))
|
||||
if (GSRendererHW::GetInstance()->IsPossibleTextureShuffle(dst, TEX0))
|
||||
adjusted_src_range += GSVector4(-8.0f, 0.0f, 8.0f, 0.0f);
|
||||
|
||||
// don't forget to scale the copy range
|
||||
|
|
|
@ -1956,7 +1956,6 @@ VkShaderModule GSDeviceVK::GetTFXFragmentShader(const GSHWDrawConfig::PSSelector
|
|||
AddMacro(ss, "PS_CHANNEL_FETCH", sel.channel);
|
||||
AddMacro(ss, "PS_URBAN_CHAOS_HLE", sel.urban_chaos_hle);
|
||||
AddMacro(ss, "PS_TALES_OF_ABYSS_HLE", sel.tales_of_abyss_hle);
|
||||
AddMacro(ss, "PS_TEX_IS_FB", sel.tex_is_fb);
|
||||
AddMacro(ss, "PS_INVALID_TEX0", sel.invalid_tex0);
|
||||
AddMacro(ss, "PS_AEM", sel.aem);
|
||||
AddMacro(ss, "PS_TFX", sel.tfx);
|
||||
|
@ -2979,22 +2978,32 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
|
|||
}
|
||||
}
|
||||
|
||||
if (config.tex && config.tex == config.ds)
|
||||
if (config.tex)
|
||||
{
|
||||
// requires a copy of the depth buffer. this is mainly for ico.
|
||||
copy_ds = static_cast<GSTextureVK*>(CreateDepthStencil(rtsize.x, rtsize.y, GSTexture::Format::DepthStencil, false));
|
||||
if (copy_ds)
|
||||
if (config.tex == config.ds)
|
||||
{
|
||||
EndRenderPass();
|
||||
// requires a copy of the depth buffer. this is mainly for ico.
|
||||
copy_ds = static_cast<GSTextureVK*>(CreateDepthStencil(rtsize.x, rtsize.y, GSTexture::Format::DepthStencil, false));
|
||||
if (copy_ds)
|
||||
{
|
||||
EndRenderPass();
|
||||
|
||||
GL_PUSH("Copy depth to temp texture for shuffle {%d,%d %dx%d}",
|
||||
config.drawarea.left, config.drawarea.top,
|
||||
config.drawarea.width(), config.drawarea.height());
|
||||
GL_PUSH("Copy depth to temp texture for shuffle {%d,%d %dx%d}",
|
||||
config.drawarea.left, config.drawarea.top,
|
||||
config.drawarea.width(), config.drawarea.height());
|
||||
|
||||
CopyRect(config.ds, copy_ds, config.drawarea, config.drawarea.left, config.drawarea.top);
|
||||
PSSetShaderResource(0, copy_ds, true);
|
||||
CopyRect(config.ds, copy_ds, config.drawarea, config.drawarea.left, config.drawarea.top);
|
||||
PSSetShaderResource(0, copy_ds, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
// clear texture binding when it's bound to RT or DS
|
||||
if (!config.tex && m_tfx_textures[0] &&
|
||||
((!pipe.feedback_loop && config.rt && static_cast<GSTextureVK*>(config.rt)->GetView() == m_tfx_textures[0]) ||
|
||||
(config.ds && static_cast<GSTextureVK*>(config.ds)->GetView() == m_tfx_textures[0])))
|
||||
{
|
||||
PSSetShaderResource(0, nullptr, false);
|
||||
}
|
||||
|
||||
const bool render_area_okay =
|
||||
(!hdr_rt && DATE_rp != DATE_RENDER_PASS_STENCIL_ONE && CheckRenderPassArea(render_area));
|
||||
|
|
Loading…
Reference in New Issue