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_tc(new GSTextureCache())
|
||||||
, m_src(nullptr)
|
, m_src(nullptr)
|
||||||
, m_reset(false)
|
, m_reset(false)
|
||||||
|
, m_tex_is_fb(false)
|
||||||
, m_channel_shuffle(false)
|
, m_channel_shuffle(false)
|
||||||
, m_userhacks_tcoffset(false)
|
, m_userhacks_tcoffset(false)
|
||||||
, m_userhacks_tcoffset_x(0)
|
, m_userhacks_tcoffset_x(0)
|
||||||
|
@ -120,10 +121,41 @@ void GSRendererHW::PurgeTextureCache()
|
||||||
m_tc->RemoveAll();
|
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 &&
|
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);
|
GSLocalMemory::m_psm[m_context->FRAME.PSM].bpp == 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1320,6 +1352,7 @@ void GSRendererHW::Draw()
|
||||||
|
|
||||||
m_src = nullptr;
|
m_src = nullptr;
|
||||||
m_texture_shuffle = false;
|
m_texture_shuffle = false;
|
||||||
|
m_tex_is_fb = false;
|
||||||
|
|
||||||
if (PRIM->TME)
|
if (PRIM->TME)
|
||||||
{
|
{
|
||||||
|
@ -2256,21 +2289,8 @@ void GSRendererHW::EmulateChannelShuffle(const GSTextureCache::Source* tex)
|
||||||
m_conf.tex = *tex->m_from_target;
|
m_conf.tex = *tex->m_from_target;
|
||||||
if (m_conf.tex)
|
if (m_conf.tex)
|
||||||
{
|
{
|
||||||
if (m_conf.tex == m_conf.rt)
|
// 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);
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace current draw with a fullscreen sprite
|
// Replace current draw with a fullscreen sprite
|
||||||
|
@ -3070,6 +3090,41 @@ void GSRendererHW::EmulateTextureSampler(const GSTextureCache::Source* tex)
|
||||||
if (!m_channel_shuffle)
|
if (!m_channel_shuffle)
|
||||||
m_conf.tex = tex->m_texture;
|
m_conf.tex = tex->m_texture;
|
||||||
m_conf.pal = tex->m_palette;
|
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)
|
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
|
else
|
||||||
m_prim_overlap = PRIM_OVERLAP_UNKNOW;
|
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();
|
EmulateTextureShuffleAndFbmask();
|
||||||
|
|
||||||
// DATE: selection of the algorithm. Must be done before blending because GL42 is not compatible with blending
|
// 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);
|
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)
|
bool GSRendererHW::CanUseSwPrimRender(bool no_rt, bool no_ds, bool draw_sprite_tex)
|
||||||
{
|
{
|
||||||
// Master enable.
|
// Master enable.
|
||||||
|
|
|
@ -151,6 +151,7 @@ private:
|
||||||
GSTextureCache::Source* m_src;
|
GSTextureCache::Source* m_src;
|
||||||
|
|
||||||
bool m_reset;
|
bool m_reset;
|
||||||
|
bool m_tex_is_fb;
|
||||||
bool m_channel_shuffle;
|
bool m_channel_shuffle;
|
||||||
bool m_userhacks_tcoffset;
|
bool m_userhacks_tcoffset;
|
||||||
float m_userhacks_tcoffset_x;
|
float m_userhacks_tcoffset_x;
|
||||||
|
@ -201,8 +202,8 @@ public:
|
||||||
void PurgeTextureCache() override;
|
void PurgeTextureCache() override;
|
||||||
|
|
||||||
// Called by the texture cache to know if current texture is useful
|
// 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
|
// 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);
|
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
|
// This shortcut is a temporary solution. It isn't a good solution
|
||||||
// as it won't work with Channel Shuffle/Texture Shuffle pattern
|
// 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.
|
// Be aware that you can't use StrechRect between BeginScene/EndScene.
|
||||||
// So it could be tricky to put in the middle of the DrawPrims
|
// 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
|
// Keep a trace of origin of the texture
|
||||||
src->m_texture = dTex;
|
src->m_texture = dst->m_texture;
|
||||||
src->m_target = true;
|
src->m_target = true;
|
||||||
|
src->m_shared_texture = true;
|
||||||
src->m_from_target = &dst->m_texture;
|
src->m_from_target = &dst->m_texture;
|
||||||
src->m_from_target_TEX0 = dst->m_TEX0;
|
src->m_from_target_TEX0 = dst->m_TEX0;
|
||||||
src->m_end_block = dst->m_end_block;
|
src->m_end_block = dst->m_end_block;
|
||||||
src->m_texture->SetScale(dst->m_texture->GetScale());
|
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
|
// Even if we sample the framebuffer directly we might need the palette
|
||||||
// to handle the format conversion on GPU
|
// to handle the format conversion on GPU
|
||||||
if (psm.pal > 0)
|
if (psm.pal > 0)
|
||||||
AttachPaletteToSource(src, psm.pal, true);
|
AttachPaletteToSource(src, psm.pal, true);
|
||||||
|
|
||||||
|
// This will get immediately invalidated.
|
||||||
|
m_temporary_source = src;
|
||||||
}
|
}
|
||||||
else if (dst)
|
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.
|
// if it looks like a texture shuffle, we might read up to +/- 8 pixels on either side.
|
||||||
GSVector4 adjusted_src_range(*src_range);
|
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);
|
adjusted_src_range += GSVector4(-8.0f, 0.0f, 8.0f, 0.0f);
|
||||||
|
|
||||||
// don't forget to scale the copy range
|
// 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_CHANNEL_FETCH", sel.channel);
|
||||||
AddMacro(ss, "PS_URBAN_CHAOS_HLE", sel.urban_chaos_hle);
|
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_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_INVALID_TEX0", sel.invalid_tex0);
|
||||||
AddMacro(ss, "PS_AEM", sel.aem);
|
AddMacro(ss, "PS_AEM", sel.aem);
|
||||||
AddMacro(ss, "PS_TFX", sel.tfx);
|
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.
|
if (config.tex == config.ds)
|
||||||
copy_ds = static_cast<GSTextureVK*>(CreateDepthStencil(rtsize.x, rtsize.y, GSTexture::Format::DepthStencil, false));
|
|
||||||
if (copy_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}",
|
GL_PUSH("Copy depth to temp texture for shuffle {%d,%d %dx%d}",
|
||||||
config.drawarea.left, config.drawarea.top,
|
config.drawarea.left, config.drawarea.top,
|
||||||
config.drawarea.width(), config.drawarea.height());
|
config.drawarea.width(), config.drawarea.height());
|
||||||
|
|
||||||
CopyRect(config.ds, copy_ds, config.drawarea, config.drawarea.left, config.drawarea.top);
|
CopyRect(config.ds, copy_ds, config.drawarea, config.drawarea.left, config.drawarea.top);
|
||||||
PSSetShaderResource(0, copy_ds, true);
|
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 =
|
const bool render_area_okay =
|
||||||
(!hdr_rt && DATE_rp != DATE_RENDER_PASS_STENCIL_ONE && CheckRenderPassArea(render_area));
|
(!hdr_rt && DATE_rp != DATE_RENDER_PASS_STENCIL_ONE && CheckRenderPassArea(render_area));
|
||||||
|
|
Loading…
Reference in New Issue