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 _PAD2[3];
|
||||
REG_END2
|
||||
// opaque => output will be Cs/As
|
||||
__forceinline bool IsOpaque() const { return ((A == B || (C == 2 && FIX == 0)) && D == 0) || (A == 0 && B == D && C == 2 && FIX == 0x80); }
|
||||
__forceinline bool IsOpaque(int amin, int amax) const { return ((A == B || amax == 0) && D == 0) || (A == 0 && B == D && amin == 0x80 && amax == 0x80); }
|
||||
// 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) || (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) || (C == 2 && D != 1 && FIX == 0x00); }
|
||||
__forceinline bool IsCd() { return (A == B) && (D == 1); }
|
||||
REG_END2
|
||||
|
||||
|
|
|
@ -1693,7 +1693,7 @@ void GSRendererHW::Draw()
|
|||
{
|
||||
// Constant Direct Write without texture/test/blending (aka a GS mem clear)
|
||||
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->TEST.ATE // no alpha 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
|
||||
{
|
||||
// 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 = GetResolution().y;
|
||||
m_r.w = GetFramebufferHeight();
|
||||
m_r.z = GetFramebufferWidth();
|
||||
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->InvalidateVideoMemType(GSTextureCache::RenderTarget, context->FRAME.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;
|
||||
}
|
||||
}
|
||||
|
@ -1831,7 +1845,7 @@ void GSRendererHW::Draw()
|
|||
{
|
||||
// Constant Direct Write without texture/test/blending (aka a GS mem clear)
|
||||
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->TEST.ATE // no alpha 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::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::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::Jak2, 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
|
||||
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];
|
||||
|
||||
|
@ -4274,7 +4287,7 @@ bool GSRendererHW::OI_GsMemClear()
|
|||
r.z += 32;
|
||||
// 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.
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
// world map clipping
|
||||
|
|
|
@ -54,7 +54,6 @@ private:
|
|||
bool OI_RozenMaidenGebetGarden(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_SuperManReturns(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_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.
|
||||
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 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));
|
||||
|
@ -737,9 +739,22 @@ void GSTextureCache::ScaleTargetForDisplay(Target* t, const GIFRegTEX0& dispfb,
|
|||
g_gs_device->Recycle(old_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).
|
||||
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.
|
||||
GetTargetHeight(t->m_TEX0.TBP0, t->m_TEX0.TBW, t->m_TEX0.PSM, static_cast<u32>(needed_height));
|
||||
|
|
Loading…
Reference in New Issue