mirror of https://github.com/PCSX2/pcsx2.git
GS-HW: Make memory clear work for Burnout 3
This commit is contained in:
parent
15a82e2324
commit
353124d82d
|
@ -536,9 +536,9 @@ REG64_(GIFReg, ALPHA)
|
||||||
u8 FIX;
|
u8 FIX;
|
||||||
u8 _PAD2[3];
|
u8 _PAD2[3];
|
||||||
REG_END2
|
REG_END2
|
||||||
// opaque => output will be Cs/As
|
// opaque => output will be Cs/As/zero
|
||||||
__forceinline bool IsOpaque() const { return ((A == B || (C == 2 && FIX == 0)) && D == 0) || (A == 0 && B == D && C == 2 && FIX == 0x80); }
|
__forceinline bool IsOpaque() const { return ((A == B || (C == 2 && FIX == 0)) && D == 0) || (A == 0 && B == D && C == 2 && FIX == 0x80) || (C == 2 && D != 1 && FIX == 0x00); }
|
||||||
__forceinline bool IsOpaque(int amin, int amax) const { return ((A == B || amax == 0) && D == 0) || (A == 0 && B == D && amin == 0x80 && amax == 0x80); }
|
__forceinline bool IsOpaque(int amin, int amax) const { return ((A == B || amax == 0) && D == 0) || (A == 0 && B == D && amin == 0x80 && amax == 0x80) || (C == 2 && D != 1 && FIX == 0x00); }
|
||||||
__forceinline bool IsCd() { return (A == B) && (D == 1); }
|
__forceinline bool IsCd() { return (A == B) && (D == 1); }
|
||||||
REG_END2
|
REG_END2
|
||||||
|
|
||||||
|
|
|
@ -1693,7 +1693,7 @@ void GSRendererHW::Draw()
|
||||||
{
|
{
|
||||||
// Constant Direct Write without texture/test/blending (aka a GS mem clear)
|
// Constant Direct Write without texture/test/blending (aka a GS mem clear)
|
||||||
if ((m_vt.m_primclass == GS_SPRITE_CLASS) && !PRIM->TME // Direct write
|
if ((m_vt.m_primclass == GS_SPRITE_CLASS) && !PRIM->TME // Direct write
|
||||||
&& (!PRIM->ABE || m_context->ALPHA.IsOpaque()) // No transparency
|
&& (!PRIM->ABE || IsOpaque()) // No transparency
|
||||||
&& (m_context->FRAME.FBMSK == 0) // no color mask
|
&& (m_context->FRAME.FBMSK == 0) // no color mask
|
||||||
&& !m_context->TEST.ATE // no alpha test
|
&& !m_context->TEST.ATE // no alpha test
|
||||||
&& (!m_context->TEST.ZTE || m_context->TEST.ZTST == ZTST_ALWAYS) // no depth test
|
&& (!m_context->TEST.ZTE || m_context->TEST.ZTST == ZTST_ALWAYS) // no depth test
|
||||||
|
@ -1701,18 +1701,32 @@ void GSRendererHW::Draw()
|
||||||
&& m_r.x == 0 && m_r.y == 0) // Likely full buffer write
|
&& m_r.x == 0 && m_r.y == 0) // Likely full buffer write
|
||||||
{
|
{
|
||||||
// Likely doing a huge single page width clear, which never goes well. (Superman)
|
// Likely doing a huge single page width clear, which never goes well. (Superman)
|
||||||
if ((m_r.w > 1024) && context->FRAME.FBW == 1)
|
// Burnout 3 does a 32x1024 double width clear on its reflection targets.
|
||||||
|
const bool clear_height_valid = (m_r.w >= 1024);
|
||||||
|
if (clear_height_valid && context->FRAME.FBW == 1)
|
||||||
{
|
{
|
||||||
m_r.z = GetResolution().x;
|
m_r.w = GetFramebufferHeight();
|
||||||
m_r.w = GetResolution().y;
|
m_r.z = GetFramebufferWidth();
|
||||||
context->FRAME.FBW = (m_r.z + 63) / 64;
|
context->FRAME.FBW = (m_r.z + 63) / 64;
|
||||||
}
|
}
|
||||||
if (OI_GsMemClear() && m_r.w > 1024)
|
|
||||||
|
// Superman does a clear to white, not black, on its depth buffer.
|
||||||
|
// Since we don't preload depth, OI_GsMemClear() won't work here, since we invalidate the target later
|
||||||
|
// on. So, instead, let the draw go through with the expanded rectangle, and copy color->depth.
|
||||||
|
const bool is_zero_clear = (((GSLocalMemory::m_psm[m_context->FRAME.PSM].fmt == 0) ?
|
||||||
|
m_vertex.buff[1].RGBAQ.U32[0] :
|
||||||
|
(m_vertex.buff[1].RGBAQ.U32[0] & ~0xFF000000)) == 0);
|
||||||
|
if (is_zero_clear && OI_GsMemClear() && clear_height_valid)
|
||||||
{
|
{
|
||||||
m_tc->InvalidateVideoMem(context->offset.fb, m_r, false, true);
|
m_tc->InvalidateVideoMem(context->offset.fb, m_r, false, true);
|
||||||
m_tc->InvalidateVideoMemType(GSTextureCache::RenderTarget, context->FRAME.Block());
|
m_tc->InvalidateVideoMemType(GSTextureCache::RenderTarget, context->FRAME.Block());
|
||||||
m_tc->InvalidateVideoMem(context->offset.zb, m_r, false, false);
|
|
||||||
m_tc->InvalidateVideoMemType(GSTextureCache::DepthStencil, context->ZBUF.Block());
|
if (m_context->ZBUF.ZMSK == 0)
|
||||||
|
{
|
||||||
|
m_tc->InvalidateVideoMem(context->offset.zb, m_r, false, false);
|
||||||
|
m_tc->InvalidateVideoMemType(GSTextureCache::DepthStencil, context->ZBUF.Block());
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1831,7 +1845,7 @@ void GSRendererHW::Draw()
|
||||||
{
|
{
|
||||||
// Constant Direct Write without texture/test/blending (aka a GS mem clear)
|
// Constant Direct Write without texture/test/blending (aka a GS mem clear)
|
||||||
if ((m_vt.m_primclass == GS_SPRITE_CLASS) && !PRIM->TME // Direct write
|
if ((m_vt.m_primclass == GS_SPRITE_CLASS) && !PRIM->TME // Direct write
|
||||||
&& (!PRIM->ABE || m_context->ALPHA.IsOpaque()) // No transparency
|
&& (!PRIM->ABE || IsOpaque()) // No transparency
|
||||||
&& (m_context->FRAME.FBMSK == 0) // no color mask
|
&& (m_context->FRAME.FBMSK == 0) // no color mask
|
||||||
&& !m_context->TEST.ATE // no alpha test
|
&& !m_context->TEST.ATE // no alpha test
|
||||||
&& (!m_context->TEST.ZTE || m_context->TEST.ZTST == ZTST_ALWAYS) // no depth test
|
&& (!m_context->TEST.ZTE || m_context->TEST.ZTST == ZTST_ALWAYS) // no depth test
|
||||||
|
@ -4129,7 +4143,6 @@ GSRendererHW::Hacks::Hacks()
|
||||||
m_oi_list.push_back(HackEntry<OI_Ptr>(CRC::MetalSlug6, CRC::RegionCount, &GSRendererHW::OI_MetalSlug6));
|
m_oi_list.push_back(HackEntry<OI_Ptr>(CRC::MetalSlug6, CRC::RegionCount, &GSRendererHW::OI_MetalSlug6));
|
||||||
m_oi_list.push_back(HackEntry<OI_Ptr>(CRC::RozenMaidenGebetGarden, CRC::RegionCount, &GSRendererHW::OI_RozenMaidenGebetGarden));
|
m_oi_list.push_back(HackEntry<OI_Ptr>(CRC::RozenMaidenGebetGarden, CRC::RegionCount, &GSRendererHW::OI_RozenMaidenGebetGarden));
|
||||||
m_oi_list.push_back(HackEntry<OI_Ptr>(CRC::SonicUnleashed, CRC::RegionCount, &GSRendererHW::OI_SonicUnleashed));
|
m_oi_list.push_back(HackEntry<OI_Ptr>(CRC::SonicUnleashed, CRC::RegionCount, &GSRendererHW::OI_SonicUnleashed));
|
||||||
m_oi_list.push_back(HackEntry<OI_Ptr>(CRC::SuperManReturns, CRC::RegionCount, &GSRendererHW::OI_SuperManReturns));
|
|
||||||
m_oi_list.push_back(HackEntry<OI_Ptr>(CRC::ArTonelico2, CRC::RegionCount, &GSRendererHW::OI_ArTonelico2));
|
m_oi_list.push_back(HackEntry<OI_Ptr>(CRC::ArTonelico2, CRC::RegionCount, &GSRendererHW::OI_ArTonelico2));
|
||||||
m_oi_list.push_back(HackEntry<OI_Ptr>(CRC::Jak2, CRC::RegionCount, &GSRendererHW::OI_JakGames));
|
m_oi_list.push_back(HackEntry<OI_Ptr>(CRC::Jak2, CRC::RegionCount, &GSRendererHW::OI_JakGames));
|
||||||
m_oi_list.push_back(HackEntry<OI_Ptr>(CRC::Jak3, CRC::RegionCount, &GSRendererHW::OI_JakGames));
|
m_oi_list.push_back(HackEntry<OI_Ptr>(CRC::Jak3, CRC::RegionCount, &GSRendererHW::OI_JakGames));
|
||||||
|
@ -4240,7 +4253,7 @@ void GSRendererHW::OI_DoubleHalfClear(GSTextureCache::Target*& rt, GSTextureCach
|
||||||
}
|
}
|
||||||
// Striped double clear done by Powerdrome and Snoopy Vs Red Baron, it will clear in 32 pixel stripes half done by the Z and half done by the FRAME
|
// Striped double clear done by Powerdrome and Snoopy Vs Red Baron, it will clear in 32 pixel stripes half done by the Z and half done by the FRAME
|
||||||
else if (rt && !ds && m_context->FRAME.FBP == m_context->ZBUF.ZBP && (m_context->FRAME.PSM & 0x30) != (m_context->ZBUF.PSM & 0x30)
|
else if (rt && !ds && m_context->FRAME.FBP == m_context->ZBUF.ZBP && (m_context->FRAME.PSM & 0x30) != (m_context->ZBUF.PSM & 0x30)
|
||||||
&& (m_context->FRAME.PSM & 0xF) == (m_context->ZBUF.PSM & 0xF) && (u32)(GSVector4i(m_vt.m_max.p).z) == 0)
|
&& (m_context->FRAME.PSM & 0xF) == (m_context->ZBUF.PSM & 0xF) && m_vt.m_eq.z == 1)
|
||||||
{
|
{
|
||||||
const GSVertex* v = &m_vertex.buff[0];
|
const GSVertex* v = &m_vertex.buff[0];
|
||||||
|
|
||||||
|
@ -4274,7 +4287,7 @@ bool GSRendererHW::OI_GsMemClear()
|
||||||
r.z += 32;
|
r.z += 32;
|
||||||
// Limit the hack to a single full buffer clear. Some games might use severals column to clear a screen
|
// Limit the hack to a single full buffer clear. Some games might use severals column to clear a screen
|
||||||
// but hopefully it will be enough.
|
// but hopefully it will be enough.
|
||||||
if (r.width() < ((static_cast<int>(m_context->FRAME.FBW) - 1) * 64) || r.height() <= 128)
|
if (m_r.width() < ((static_cast<int>(m_context->FRAME.FBW) - 1) * 64) || r.height() <= 128)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
GL_INS("OI_GsMemClear (%d,%d => %d,%d)", r.x, r.y, r.z, r.w);
|
GL_INS("OI_GsMemClear (%d,%d => %d,%d)", r.x, r.y, r.z, r.w);
|
||||||
|
@ -4704,32 +4717,6 @@ bool GSRendererHW::OI_PointListPalette(GSTexture* rt, GSTexture* ds, GSTextureCa
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GSRendererHW::OI_SuperManReturns(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
|
|
||||||
{
|
|
||||||
// Instead to use a fullscreen rectangle they use a 32 pixels, 4096 pixels with a FBW of 1.
|
|
||||||
// Technically the FB wrap/overlap on itself...
|
|
||||||
const GSDrawingContext* ctx = m_context;
|
|
||||||
#ifndef NDEBUG
|
|
||||||
GSVertex* v = &m_vertex.buff[0];
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!(ctx->FRAME.FBP == ctx->ZBUF.ZBP && !PRIM->TME && !ctx->ZBUF.ZMSK && !ctx->FRAME.FBMSK && m_vt.m_eq.rgba == 0xFFFF))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// Please kill those crazy devs!
|
|
||||||
ASSERT(m_vertex.next == 2);
|
|
||||||
ASSERT(m_vt.m_primclass == GS_SPRITE_CLASS);
|
|
||||||
ASSERT((v->RGBAQ.A << 24 | v->RGBAQ.B << 16 | v->RGBAQ.G << 8 | v->RGBAQ.R) == (int)v->XYZ.Z);
|
|
||||||
|
|
||||||
// Do a direct write
|
|
||||||
g_gs_device->ClearRenderTarget(rt, GSVector4(m_vt.m_min.c));
|
|
||||||
|
|
||||||
m_tc->InvalidateVideoMemType(GSTextureCache::DepthStencil, ctx->FRAME.Block());
|
|
||||||
GL_INS("OI_SuperManReturns");
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GSRendererHW::OI_ArTonelico2(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
|
bool GSRendererHW::OI_ArTonelico2(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
|
||||||
{
|
{
|
||||||
// world map clipping
|
// world map clipping
|
||||||
|
|
|
@ -54,7 +54,6 @@ private:
|
||||||
bool OI_RozenMaidenGebetGarden(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
|
bool OI_RozenMaidenGebetGarden(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
|
||||||
bool OI_SonicUnleashed(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
|
bool OI_SonicUnleashed(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
|
||||||
bool OI_PointListPalette(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
|
bool OI_PointListPalette(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
|
||||||
bool OI_SuperManReturns(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
|
|
||||||
bool OI_ArTonelico2(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
|
bool OI_ArTonelico2(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
|
||||||
bool OI_JakGames(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
|
bool OI_JakGames(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
|
||||||
bool OI_BurnoutGames(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
|
bool OI_BurnoutGames(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
|
||||||
|
|
|
@ -713,6 +713,8 @@ void GSTextureCache::ScaleTargetForDisplay(Target* t, const GIFRegTEX0& dispfb,
|
||||||
|
|
||||||
// Take that into consideration to find the extent of the target which will be sampled.
|
// Take that into consideration to find the extent of the target which will be sampled.
|
||||||
GSTexture* old_texture = t->m_texture;
|
GSTexture* old_texture = t->m_texture;
|
||||||
|
const int old_width = static_cast<int>(static_cast<float>(old_texture->GetWidth()) / old_texture->GetScale().x);
|
||||||
|
const int old_height = static_cast<int>(static_cast<float>(old_texture->GetHeight()) / old_texture->GetScale().y);
|
||||||
const int needed_height = std::min(real_h + y_offset, GSRendererHW::MAX_FRAMEBUFFER_HEIGHT);
|
const int needed_height = std::min(real_h + y_offset, GSRendererHW::MAX_FRAMEBUFFER_HEIGHT);
|
||||||
const int scaled_needed_height = std::max(static_cast<int>(static_cast<float>(needed_height) * old_texture->GetScale().y), old_texture->GetHeight());
|
const int scaled_needed_height = std::max(static_cast<int>(static_cast<float>(needed_height) * old_texture->GetScale().y), old_texture->GetHeight());
|
||||||
const int needed_width = std::min(real_w, static_cast<int>(dispfb.TBW * 64));
|
const int needed_width = std::min(real_w, static_cast<int>(dispfb.TBW * 64));
|
||||||
|
@ -737,9 +739,22 @@ void GSTextureCache::ScaleTargetForDisplay(Target* t, const GIFRegTEX0& dispfb,
|
||||||
g_gs_device->Recycle(old_texture);
|
g_gs_device->Recycle(old_texture);
|
||||||
t->m_texture = new_texture;
|
t->m_texture = new_texture;
|
||||||
|
|
||||||
const GSVector4i newrect = GSVector4i(0, 0, t->m_TEX0.TBW * 64, needed_height);
|
|
||||||
// We unconditionally preload the frame here, because otherwise we'll end up with blackness for one frame (when the expand happens).
|
// We unconditionally preload the frame here, because otherwise we'll end up with blackness for one frame (when the expand happens).
|
||||||
AddDirtyRectTarget(t, newrect, t->m_TEX0.PSM, t->m_TEX0.TBW);
|
const int preload_width = t->m_TEX0.TBW * 64;
|
||||||
|
if (old_width < preload_width && old_height < needed_height)
|
||||||
|
{
|
||||||
|
const GSVector4i right(old_width, 0, preload_width, needed_height);
|
||||||
|
const GSVector4i bottom(0, old_height, old_width, needed_height);
|
||||||
|
AddDirtyRectTarget(t, right, t->m_TEX0.PSM, t->m_TEX0.TBW);
|
||||||
|
AddDirtyRectTarget(t, bottom, t->m_TEX0.PSM, t->m_TEX0.TBW);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const GSVector4i newrect = GSVector4i((old_height < scaled_needed_height) ? 0 : old_width,
|
||||||
|
(old_width < preload_width) ? 0 : old_height,
|
||||||
|
preload_width, needed_height);
|
||||||
|
AddDirtyRectTarget(t, newrect, t->m_TEX0.PSM, t->m_TEX0.TBW);
|
||||||
|
}
|
||||||
|
|
||||||
// Inject the new height back into the cache.
|
// Inject the new height back into the cache.
|
||||||
GetTargetHeight(t->m_TEX0.TBP0, t->m_TEX0.TBW, t->m_TEX0.PSM, static_cast<u32>(needed_height));
|
GetTargetHeight(t->m_TEX0.TBP0, t->m_TEX0.TBW, t->m_TEX0.PSM, static_cast<u32>(needed_height));
|
||||||
|
|
Loading…
Reference in New Issue