mirror of https://github.com/PCSX2/pcsx2.git
GS-hw: fix burnout black sky and no clouds.
Achieved with a combination of OI+OO hacks that operate during level loading to consolidate the rendering results in the GS local memory. * OI_BurnoutGames to perform small draws directly in local memory: ** improved OI_PointListPalette to draw clouds CLUT, ** improved SwSpriteRender to draw sky texture; * OO_BurnoutGames used to download clouds texture from GPU.
This commit is contained in:
parent
b315625a94
commit
d5be095482
|
@ -17,8 +17,6 @@
|
|||
#include "GSRendererHW.h"
|
||||
#include "GS/GSGL.h"
|
||||
|
||||
const float GSRendererHW::SSR_UV_TOLERANCE = 1e-3f;
|
||||
|
||||
GSRendererHW::GSRendererHW()
|
||||
: m_width(default_rt_size.x)
|
||||
, m_height(default_rt_size.y)
|
||||
|
@ -863,36 +861,30 @@ void GSRendererHW::SwSpriteRender()
|
|||
|
||||
const bool texture_mapping_enabled = PRIM->TME;
|
||||
|
||||
// Setup registers for SW rendering
|
||||
GIFRegBITBLTBUF bitbltbuf = {};
|
||||
const GSVector4i r = m_r;
|
||||
|
||||
if (texture_mapping_enabled)
|
||||
{
|
||||
bitbltbuf.SBP = m_context->TEX0.TBP0;
|
||||
bitbltbuf.SBW = m_context->TEX0.TBW;
|
||||
bitbltbuf.SPSM = m_context->TEX0.PSM;
|
||||
}
|
||||
|
||||
bitbltbuf.DBP = m_context->FRAME.Block();
|
||||
bitbltbuf.DBW = m_context->FRAME.FBW;
|
||||
bitbltbuf.DPSM = m_context->FRAME.PSM;
|
||||
|
||||
ASSERT(m_r.x == 0 && m_r.y == 0); // No rendering region offset
|
||||
ASSERT(!PRIM->TME || (abs(m_vt.m_min.t.x) <= SSR_UV_TOLERANCE && abs(m_vt.m_min.t.y) <= SSR_UV_TOLERANCE)); // No input texture offset, if any
|
||||
ASSERT(!PRIM->TME || (abs(m_vt.m_max.t.x - m_r.z) <= SSR_UV_TOLERANCE && abs(m_vt.m_max.t.y - m_r.w) <= SSR_UV_TOLERANCE)); // No input texture min/mag, if any
|
||||
ASSERT(!PRIM->TME || (m_vt.m_max.t.x <= (1 << m_context->TEX0.TW) && m_vt.m_max.t.y <= (1 << m_context->TEX0.TH))); // No texture UV wrap, if any
|
||||
const int tw = 1 << m_context->TEX0.TW;
|
||||
const int th = 1 << m_context->TEX0.TH;
|
||||
const float meas_tw = m_vt.m_max.t.x - m_vt.m_min.t.x;
|
||||
const float meas_th = m_vt.m_max.t.y - m_vt.m_min.t.y;
|
||||
ASSERT(!PRIM->TME || (abs(meas_tw - r.width()) <= SSR_UV_TOLERANCE && abs(meas_th - r.height()) <= SSR_UV_TOLERANCE)); // No input texture min/mag, if any.
|
||||
ASSERT(!PRIM->TME || (abs(m_vt.m_min.t.x) <= SSR_UV_TOLERANCE && abs(m_vt.m_min.t.y) <= SSR_UV_TOLERANCE && abs(meas_tw - tw) <= SSR_UV_TOLERANCE && abs(meas_th - th) <= SSR_UV_TOLERANCE)); // No texture UV wrap, if any.
|
||||
|
||||
GIFRegTRXPOS trxpos = {};
|
||||
|
||||
trxpos.DSAX = 0;
|
||||
trxpos.DSAY = 0;
|
||||
trxpos.SSAX = 0;
|
||||
trxpos.SSAY = 0;
|
||||
trxpos.DSAX = r.x;
|
||||
trxpos.DSAY = r.y;
|
||||
trxpos.SSAX = static_cast<int>(m_vt.m_min.t.x / 2) * 2; // Rounded down to closest even integer.
|
||||
trxpos.SSAY = static_cast<int>(m_vt.m_min.t.y / 2) * 2;
|
||||
|
||||
ASSERT(r.x % 2 == 0 && r.y % 2 == 0);
|
||||
|
||||
GIFRegTRXREG trxreg = {};
|
||||
|
||||
trxreg.RRW = m_r.z;
|
||||
trxreg.RRH = m_r.w;
|
||||
trxreg.RRW = r.width();
|
||||
trxreg.RRH = r.height();
|
||||
|
||||
ASSERT(r.width() % 2 == 0 && r.height() % 2 == 0);
|
||||
|
||||
// SW rendering code, mainly taken from GSState::Move(), TRXPOS.DIR{X,Y} management excluded
|
||||
|
||||
|
@ -903,20 +895,16 @@ void GSRendererHW::SwSpriteRender()
|
|||
const int w = trxreg.RRW;
|
||||
const int h = trxreg.RRH;
|
||||
|
||||
GL_INS("SwSpriteRender: Dest 0x%x W:%d F:%s, size(%d %d)", bitbltbuf.DBP, bitbltbuf.DBW, psm_str(bitbltbuf.DPSM), w, h);
|
||||
GL_INS("SwSpriteRender: Dest 0x%x W:%d F:%s, size(%d %d)", m_context->FRAME.Block(), m_context->FRAME.FBW, psm_str(m_context->FRAME.PSM), w, h);
|
||||
|
||||
if (texture_mapping_enabled)
|
||||
InvalidateLocalMem(bitbltbuf, GSVector4i(sx, sy, sx + w, sy + h));
|
||||
InvalidateVideoMem(bitbltbuf, GSVector4i(dx, dy, dx + w, dy + h));
|
||||
|
||||
GSOffset spo = texture_mapping_enabled ? m_mem.GetOffset(bitbltbuf.SBP, bitbltbuf.SBW, bitbltbuf.SPSM) : GSOffset();
|
||||
GSOffset dpo = m_mem.GetOffset(bitbltbuf.DBP, bitbltbuf.DBW, bitbltbuf.DPSM);
|
||||
const GSOffset& spo = m_context->offset.tex;
|
||||
const GSOffset& dpo = m_context->offset.fb;
|
||||
|
||||
const bool alpha_blending_enabled = PRIM->ABE;
|
||||
|
||||
const GSVertex& v = m_vertex.buff[m_index.buff[m_index.tail - 1]]; // Last vertex.
|
||||
const GSVector4i vc = GSVector4i(v.RGBAQ.R, v.RGBAQ.G, v.RGBAQ.B, v.RGBAQ.A) // 0x000000AA000000BB000000GG000000RR
|
||||
.ps32(); // 0x00AA00BB00GG00RR00AA00BB00GG00RR
|
||||
.ps32(); // 0x00AA00BB00GG00RR00AA00BB00GG00RR
|
||||
|
||||
const GSVector4i a_mask = GSVector4i::xff000000().u8to16(); // 0x00FF00000000000000FF000000000000
|
||||
|
||||
|
@ -925,28 +913,36 @@ void GSRendererHW::SwSpriteRender()
|
|||
|
||||
const u8 tex0_tfx = m_context->TEX0.TFX;
|
||||
const u8 tex0_tcc = m_context->TEX0.TCC;
|
||||
const u8 alpha_a = m_context->ALPHA.A;
|
||||
const u8 alpha_b = m_context->ALPHA.B;
|
||||
const u8 alpha_c = m_context->ALPHA.C;
|
||||
const u8 alpha_d = m_context->ALPHA.D;
|
||||
const u8 alpha_fix = m_context->ALPHA.FIX;
|
||||
|
||||
if (texture_mapping_enabled)
|
||||
m_tc->InvalidateLocalMem(spo, GSVector4i(sx, sy, sx + w, sy + h));
|
||||
constexpr bool invalidate_local_mem_before_fb_read = false;
|
||||
if (invalidate_local_mem_before_fb_read && (alpha_blending_enabled || fb_mask_enabled))
|
||||
m_tc->InvalidateLocalMem(dpo, m_r);
|
||||
|
||||
for (int y = 0; y < h; y++, ++sy, ++dy)
|
||||
{
|
||||
auto spa = texture_mapping_enabled ? spo.paMulti(m_mem.m_vm32, sx, sy) : GSOffset::PAPtrHelper<u32>();
|
||||
auto dpa = dpo.paMulti(m_mem.m_vm32, dx, dy);
|
||||
const auto& spa = spo.paMulti(m_mem.m_vm32, sx, sy);
|
||||
const auto& dpa = dpo.paMulti(m_mem.m_vm32, dx, dy);
|
||||
|
||||
ASSERT(w % 2 == 0);
|
||||
|
||||
for (int x = 0; x < w; x += 2)
|
||||
{
|
||||
u32* di = dpa.value(x);
|
||||
ASSERT(*di + 1 == *dpa.value(x + 1)); // Destination pixel pair is adjacent in memory
|
||||
ASSERT(di + 1 == dpa.value(x + 1)); // Destination pixel pair is adjacent in memory
|
||||
|
||||
GSVector4i sc;
|
||||
if (texture_mapping_enabled)
|
||||
{
|
||||
u32* si = spa.value(x);
|
||||
const u32* si = spa.value(x);
|
||||
// Read 2 source pixel colors
|
||||
ASSERT((*si + 1) == *spa.value(x + 1)); // Source pixel pair is adjacent in memory
|
||||
ASSERT(si + 1 == spa.value(x + 1)); // Source pixel pair is adjacent in memory
|
||||
sc = GSVector4i::loadl(si).u8to16(); // 0x00AA00BB00GG00RR00aa00bb00gg00rr
|
||||
|
||||
// Apply TFX
|
||||
|
@ -974,31 +970,14 @@ void GSRendererHW::SwSpriteRender()
|
|||
if (alpha_blending_enabled)
|
||||
{
|
||||
// Blending
|
||||
ASSERT(m_context->ALPHA.A == 0);
|
||||
ASSERT(alpha_b == 1 || alpha_b == 2);
|
||||
ASSERT(m_context->ALPHA.D == 1);
|
||||
|
||||
// Flag C
|
||||
GSVector4i sc_alpha_vec;
|
||||
|
||||
if (alpha_c == 2)
|
||||
sc_alpha_vec = GSVector4i(alpha_fix).xxxx().ps32();
|
||||
else
|
||||
sc_alpha_vec = (alpha_c == 0 ? sc : dc0)
|
||||
.yyww() // 0x00AA00BB00AA00BB00aa00bb00aa00bb
|
||||
.srl32(16) // 0x000000AA000000AA000000aa000000aa
|
||||
.ps32() // 0x00AA00AA00aa00aa00AA00AA00aa00aa
|
||||
.xxyy(); // 0x00AA00AA00AA00AA00aa00aa00aa00aa
|
||||
|
||||
switch (alpha_b)
|
||||
{
|
||||
case 1:
|
||||
dc = sc.sub16(dc0).mul16l(sc_alpha_vec).sra16(7).add16(dc0); // (((Cs - Cd) * C) >> 7) + Cd, must use sra16 due to signed 16 bit values
|
||||
break;
|
||||
default:
|
||||
dc = sc.mul16l(sc_alpha_vec).sra16(7).add16(dc0); // (((Cs - 0) * C) >> 7) + Cd, must use sra16 due to signed 16 bit values
|
||||
break;
|
||||
}
|
||||
const GSVector4i A = alpha_a == 0 ? sc : alpha_a == 1 ? dc0 : GSVector4i::zero();
|
||||
const GSVector4i B = alpha_b == 0 ? sc : alpha_b == 1 ? dc0 : GSVector4i::zero();
|
||||
const GSVector4i C = alpha_c == 2 ? GSVector4i(alpha_fix).xxxx().ps32() : (alpha_c == 0 ? sc : dc0).yyww() // 0x00AA00BB00AA00BB00aa00bb00aa00bb
|
||||
.srl32(16) // 0x000000AA000000AA000000aa000000aa
|
||||
.ps32() // 0x00AA00AA00aa00aa00AA00AA00aa00aa
|
||||
.xxyy(); // 0x00AA00AA00AA00AA00aa00aa00aa00aa
|
||||
const GSVector4i D = alpha_d == 0 ? sc : alpha_d == 1 ? dc0 : GSVector4i::zero();
|
||||
dc = A.sub16(B).mul16l(C).sra16(7).add16(D); // (((A - B) * C) >> 7) + D, must use sra16 due to signed 16 bit values.
|
||||
// dc alpha channels (dc.u16[3], dc.u16[7]) dirty
|
||||
}
|
||||
else
|
||||
|
@ -1026,16 +1005,22 @@ void GSRendererHW::SwSpriteRender()
|
|||
GSVector4i::storel(di, dc);
|
||||
}
|
||||
}
|
||||
|
||||
m_tc->InvalidateVideoMem(dpo, m_r);
|
||||
m_mem.m_clut.Invalidate();
|
||||
}
|
||||
|
||||
bool GSRendererHW::CanUseSwSpriteRender(bool allow_64x64_sprite)
|
||||
bool GSRendererHW::CanUseSwSpriteRender()
|
||||
{
|
||||
const bool r_0_0_64_64 = allow_64x64_sprite ? (m_r == GSVector4i(0, 0, 64, 64)).alltrue() : false;
|
||||
if (r_0_0_64_64 && !allow_64x64_sprite) // Rendering region 64x64 support is enabled via parameter
|
||||
return false;
|
||||
const bool r_0_0_16_16 = (m_r == GSVector4i(0, 0, 16, 16)).alltrue();
|
||||
if (!r_0_0_16_16 && !r_0_0_64_64) // Rendering region is 16x16 or 64x64, without offset
|
||||
return false;
|
||||
const GSVector4i r = m_r;
|
||||
if (r.x % 2 != 0 || r.y % 2 != 0)
|
||||
return false; // Even offset.
|
||||
const int w = r.width();
|
||||
const int h = r.height();
|
||||
if (w % 2 != 0 || h % 2 != 0)
|
||||
return false; // Even size.
|
||||
if (w > 64 || h > 64)
|
||||
return false; // Small draw.
|
||||
if (PRIM->PRIM != GS_SPRITE
|
||||
&& ((PRIM->IIP && m_vt.m_eq.rgba != 0xffff)
|
||||
|| (PRIM->TME && !PRIM->FST && m_vt.m_eq.q != 0x1)
|
||||
|
@ -1043,7 +1028,7 @@ bool GSRendererHW::CanUseSwSpriteRender(bool allow_64x64_sprite)
|
|||
return false;
|
||||
if (m_vt.m_primclass != GS_TRIANGLE_CLASS && m_vt.m_primclass != GS_SPRITE_CLASS) // Triangle or sprite class prims
|
||||
return false;
|
||||
if (PRIM->PRIM != GS_TRIANGLESTRIP && PRIM->PRIM != GS_SPRITE) // Triangle strip or sprite draw
|
||||
if (PRIM->PRIM != GS_TRIANGLESTRIP && PRIM->PRIM != GS_SPRITE) // Triangle strip or sprite draw
|
||||
return false;
|
||||
if (m_vt.m_primclass == GS_TRIANGLE_CLASS && (PRIM->PRIM != GS_TRIANGLESTRIP || m_vertex.tail != 4)) // If triangle class, strip draw with 4 vertices (two prims, emulating single sprite prim)
|
||||
return false;
|
||||
|
@ -1060,15 +1045,18 @@ bool GSRendererHW::CanUseSwSpriteRender(bool allow_64x64_sprite)
|
|||
|
||||
if (m_context->TEX0.PSM != PSM_PSMCT32) // Input texture format is 32 bit color
|
||||
return false;
|
||||
if (IsMipMapDraw()) // No mipmapping
|
||||
return false;
|
||||
if (abs(m_vt.m_min.t.x) > SSR_UV_TOLERANCE || abs(m_vt.m_min.t.y) > SSR_UV_TOLERANCE) // No horizontal nor vertical offset
|
||||
return false;
|
||||
if (abs(m_vt.m_max.t.x - m_r.z) > SSR_UV_TOLERANCE || abs(m_vt.m_max.t.y - m_r.w) > SSR_UV_TOLERANCE) // No texture width or height mag/min
|
||||
if (IsMipMapDraw()) // No mipmapping.
|
||||
return false;
|
||||
const int tw = 1 << m_context->TEX0.TW;
|
||||
const int th = 1 << m_context->TEX0.TH;
|
||||
if (m_vt.m_max.t.x > tw || m_vt.m_max.t.y > th) // No UV wrapping
|
||||
const float meas_tw = m_vt.m_max.t.x - m_vt.m_min.t.x;
|
||||
const float meas_th = m_vt.m_max.t.y - m_vt.m_min.t.y;
|
||||
if (abs(m_vt.m_min.t.x) > SSR_UV_TOLERANCE ||
|
||||
abs(m_vt.m_min.t.y) > SSR_UV_TOLERANCE ||
|
||||
abs(meas_tw - tw) > SSR_UV_TOLERANCE ||
|
||||
abs(meas_th - th) > SSR_UV_TOLERANCE) // No UV wrapping.
|
||||
return false;
|
||||
if (abs(meas_tw - w) > SSR_UV_TOLERANCE || abs(meas_th - h) > SSR_UV_TOLERANCE) // No texture width or height mag/min.
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1751,7 +1739,14 @@ GSRendererHW::Hacks::Hacks()
|
|||
m_oi_list.push_back(HackEntry<OI_Ptr>(CRC::Jak3, CRC::RegionCount, &GSRendererHW::OI_JakGames));
|
||||
m_oi_list.push_back(HackEntry<OI_Ptr>(CRC::JakX, CRC::RegionCount, &GSRendererHW::OI_JakGames));
|
||||
|
||||
m_oi_list.push_back(HackEntry<OI_Ptr>(CRC::BurnoutTakedown, CRC::RegionCount, &GSRendererHW::OI_BurnoutGames));
|
||||
m_oi_list.push_back(HackEntry<OI_Ptr>(CRC::BurnoutRevenge, CRC::RegionCount, &GSRendererHW::OI_BurnoutGames));
|
||||
m_oi_list.push_back(HackEntry<OI_Ptr>(CRC::BurnoutDominator, CRC::RegionCount, &GSRendererHW::OI_BurnoutGames));
|
||||
|
||||
m_oo_list.push_back(HackEntry<OO_Ptr>(CRC::MajokkoALaMode2, CRC::RegionCount, &GSRendererHW::OO_MajokkoALaMode2));
|
||||
m_oo_list.push_back(HackEntry<OO_Ptr>(CRC::BurnoutTakedown, CRC::RegionCount, &GSRendererHW::OO_BurnoutGames));
|
||||
m_oo_list.push_back(HackEntry<OO_Ptr>(CRC::BurnoutRevenge, CRC::RegionCount, &GSRendererHW::OO_BurnoutGames));
|
||||
m_oo_list.push_back(HackEntry<OO_Ptr>(CRC::BurnoutDominator, CRC::RegionCount, &GSRendererHW::OO_BurnoutGames));
|
||||
|
||||
m_cu_list.push_back(HackEntry<CU_Ptr>(CRC::MajokkoALaMode2, CRC::RegionCount, &GSRendererHW::CU_MajokkoALaMode2));
|
||||
m_cu_list.push_back(HackEntry<CU_Ptr>(CRC::TalesOfAbyss, CRC::RegionCount, &GSRendererHW::CU_TalesOfAbyss));
|
||||
|
@ -2011,8 +2006,11 @@ bool GSRendererHW::OI_DBZBTGames(GSTexture* rt, GSTexture* ds, GSTextureCache::S
|
|||
if (t && t->m_from_target) // Avoid slow framebuffer readback
|
||||
return true;
|
||||
|
||||
if (!((m_r == GSVector4i(0, 0, 16, 16)).alltrue() || (m_r == GSVector4i(0, 0, 64, 64)).alltrue()))
|
||||
return true; // Only 16x16 or 64x64 draws.
|
||||
|
||||
// Sprite rendering
|
||||
if (!CanUseSwSpriteRender(true))
|
||||
if (!CanUseSwSpriteRender())
|
||||
return true;
|
||||
|
||||
SwSpriteRender();
|
||||
|
@ -2235,60 +2233,53 @@ bool GSRendererHW::OI_SonicUnleashed(GSTexture* rt, GSTexture* ds, GSTextureCach
|
|||
|
||||
bool GSRendererHW::OI_PointListPalette(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
|
||||
{
|
||||
if (m_vt.m_primclass == GS_POINT_CLASS && !PRIM->TME)
|
||||
const size_t n_vertices = m_vertex.next;
|
||||
const int w = m_r.width();
|
||||
const int h = m_r.height();
|
||||
if (m_vt.m_primclass == GS_POINT_CLASS && w <= 64 // Small draws.
|
||||
&& h <= 64 // Small draws.
|
||||
&& n_vertices <= 256 // Small draws.
|
||||
&& PRIM->ABE // Alpha blending.
|
||||
&& !PRIM->TME // No texturing please.
|
||||
&& m_context->FRAME.PSM == PSM_PSMCT32 // Only 32-bit pixel format (CLUT format).
|
||||
&& !PRIM->FGE // No FOG.
|
||||
&& !PRIM->AA1 // No antialiasing.
|
||||
&& !PRIM->FIX // Normal fragment value control.
|
||||
&& !m_env.DTHE.DTHE // No dithering.
|
||||
&& !m_context->TEST.ATE // No alpha test.
|
||||
&& !m_context->TEST.DATE // No destination alpha test.
|
||||
&& (!m_context->DepthRead() && !m_context->DepthWrite()) // No depth handling.
|
||||
&& !m_context->TEX0.CSM // No CLUT usage.
|
||||
&& !m_env.PABE.PABE // No PABE.
|
||||
&& m_context->FBA.FBA == 0 // No Alpha Correction.
|
||||
&& m_context->FRAME.FBMSK == 0 // No frame buffer masking.
|
||||
&& m_context->ALPHA.A == m_context->ALPHA.B // (A - B) == 0 in blending equation, makes C value irrelevant.
|
||||
&& m_context->ALPHA.D == 0 // Copy source RGB(A) color into frame buffer.
|
||||
)
|
||||
{
|
||||
const u32 FBP = m_context->FRAME.Block();
|
||||
const u32 FBW = m_context->FRAME.FBW;
|
||||
|
||||
if (FBP >= 0x03f40 && (FBP & 0x1f) == 0)
|
||||
GL_INS("PointListPalette - m_r = <%d, %d => %d, %d>, n_vertices = %zu, FBP = 0x%x, FBW = %u", m_r.x, m_r.y, m_r.z, m_r.w, n_vertices, FBP, FBW);
|
||||
const GSVertex* RESTRICT v = m_vertex.buff;
|
||||
const int ox(m_context->XYOFFSET.OFX);
|
||||
const int oy(m_context->XYOFFSET.OFY);
|
||||
for (size_t i = 0; i < n_vertices; ++i)
|
||||
{
|
||||
if (m_vertex.next == 16)
|
||||
{
|
||||
GSVertex* RESTRICT v = m_vertex.buff;
|
||||
|
||||
for (int i = 0; i < 16; i++, v++)
|
||||
{
|
||||
u32 c = v->RGBAQ.U32[0];
|
||||
const u32 a = c >> 24;
|
||||
|
||||
c = (a >= 0x80 ? 0xff000000 : (a << 25)) | (c & 0x00ffffff);
|
||||
|
||||
v->RGBAQ.U32[0] = c;
|
||||
|
||||
m_mem.WritePixel32(i & 7, i >> 3, c, FBP, FBW);
|
||||
}
|
||||
|
||||
m_mem.m_clut.Invalidate();
|
||||
|
||||
return false;
|
||||
}
|
||||
else if (m_vertex.next == 256)
|
||||
{
|
||||
GSVertex* RESTRICT v = m_vertex.buff;
|
||||
|
||||
for (int i = 0; i < 256; i++, v++)
|
||||
{
|
||||
u32 c = v->RGBAQ.U32[0];
|
||||
const u32 a = c >> 24;
|
||||
|
||||
c = (a >= 0x80 ? 0xff000000 : (a << 25)) | (c & 0x00ffffff);
|
||||
|
||||
v->RGBAQ.U32[0] = c;
|
||||
|
||||
m_mem.WritePixel32(i & 15, i >> 4, c, FBP, FBW);
|
||||
}
|
||||
|
||||
m_mem.m_clut.Invalidate();
|
||||
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
ASSERT(0);
|
||||
}
|
||||
const GSVertex& vi = v[i];
|
||||
const GIFRegXYZ& xyz = vi.XYZ;
|
||||
const int x = (int(xyz.X) - ox) / 16;
|
||||
const int y = (int(xyz.Y) - oy) / 16;
|
||||
if (x < m_r.x || x > m_r.z)
|
||||
continue;
|
||||
if (y < m_r.y || y > m_r.w)
|
||||
continue;
|
||||
const u32 c = vi.RGBAQ.U32[0];
|
||||
m_mem.WritePixel32(x, y, c, FBP, FBW);
|
||||
}
|
||||
m_tc->InvalidateVideoMem(m_context->offset.fb, m_r);
|
||||
m_mem.m_clut.Invalidate();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -2358,7 +2349,10 @@ bool GSRendererHW::OI_ArTonelico2(GSTexture* rt, GSTexture* ds, GSTextureCache::
|
|||
|
||||
bool GSRendererHW::OI_JakGames(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
|
||||
{
|
||||
if (!CanUseSwSpriteRender(false))
|
||||
if (!(m_r == GSVector4i(0, 0, 16, 16)).alltrue())
|
||||
return true; // Only 16x16 draws.
|
||||
|
||||
if (!CanUseSwSpriteRender())
|
||||
return true;
|
||||
|
||||
// Render 16x16 palette via CPU.
|
||||
|
@ -2367,6 +2361,23 @@ bool GSRendererHW::OI_JakGames(GSTexture* rt, GSTexture* ds, GSTextureCache::Sou
|
|||
return false; // Skip current draw.
|
||||
}
|
||||
|
||||
bool GSRendererHW::OI_BurnoutGames(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
|
||||
{
|
||||
if (!OI_PointListPalette(rt, ds, t))
|
||||
return false; // Render point list palette.
|
||||
|
||||
if (t && t->m_from_target) // Avoid slow framebuffer readback
|
||||
return true;
|
||||
|
||||
if (!CanUseSwSpriteRender())
|
||||
return true;
|
||||
|
||||
// Render palette via CPU.
|
||||
SwSpriteRender();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// OO (others output?) hacks: invalidate extra local memory after the draw call
|
||||
|
||||
void GSRendererHW::OO_MajokkoALaMode2()
|
||||
|
@ -2387,6 +2398,41 @@ void GSRendererHW::OO_MajokkoALaMode2()
|
|||
}
|
||||
}
|
||||
|
||||
void GSRendererHW::OO_BurnoutGames()
|
||||
{
|
||||
const GIFRegTEX0& TEX0 = m_context->TEX0;
|
||||
const GIFRegALPHA& ALPHA = m_context->ALPHA;
|
||||
const GIFRegFRAME& FRAME = m_context->FRAME;
|
||||
if (PRIM->PRIM == GS_SPRITE
|
||||
&& !PRIM->IIP
|
||||
&& PRIM->TME
|
||||
&& !PRIM->FGE
|
||||
&& PRIM->ABE
|
||||
&& !PRIM->AA1
|
||||
&& !PRIM->FST
|
||||
&& !PRIM->FIX
|
||||
&& TEX0.TBW == 16
|
||||
&& TEX0.TW == 10
|
||||
&& TEX0.TCC
|
||||
&& !TEX0.TFX
|
||||
&& TEX0.PSM == PSM_PSMT8
|
||||
&& TEX0.CPSM == PSM_PSMCT32
|
||||
&& !TEX0.CSM
|
||||
&& TEX0.TH == 8
|
||||
&& ALPHA.A == ALPHA.B
|
||||
&& ALPHA.D == 0
|
||||
&& FRAME.FBW == 16
|
||||
&& FRAME.PSM == PSM_PSMCT32)
|
||||
{
|
||||
// Readback clouds being rendered during level loading.
|
||||
// Later the alpha channel from the 32 bit frame buffer is used as an 8 bit indexed texture to draw
|
||||
// the clouds on top of the sky at each frame.
|
||||
// Burnout 3 PAL 50Hz: 0x3ba0 => 0x1e80.
|
||||
GL_INS("OO_BurnoutGames - Readback clouds renderered from TEX0.TBP0 = 0x%04x (TEX0.CBP = 0x%04x) to FBP = 0x%04x", TEX0.TBP0, TEX0.CBP, FRAME.Block());
|
||||
m_tc->InvalidateLocalMem(m_context->offset.fb, m_r);
|
||||
}
|
||||
}
|
||||
|
||||
// Can Upscale hacks: disable upscaling for some draw calls
|
||||
|
||||
bool GSRendererHW::CU_MajokkoALaMode2()
|
||||
|
|
|
@ -35,7 +35,7 @@ private:
|
|||
bool m_userhacks_enabled_gs_mem_clear;
|
||||
bool m_userHacks_merge_sprite;
|
||||
|
||||
static const float SSR_UV_TOLERANCE;
|
||||
static constexpr float SSR_UV_TOLERANCE = 1.0f;
|
||||
|
||||
#pragma region hacks
|
||||
|
||||
|
@ -59,8 +59,10 @@ private:
|
|||
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);
|
||||
|
||||
void OO_MajokkoALaMode2();
|
||||
void OO_BurnoutGames();
|
||||
|
||||
bool CU_MajokkoALaMode2();
|
||||
bool CU_TalesOfAbyss();
|
||||
|
@ -136,7 +138,7 @@ private:
|
|||
float alpha0(int L, int X0, int X1);
|
||||
float alpha1(int L, int X0, int X1);
|
||||
void SwSpriteRender();
|
||||
bool CanUseSwSpriteRender(bool allow_64x64_sprite);
|
||||
bool CanUseSwSpriteRender();
|
||||
|
||||
template <bool linear>
|
||||
void RoundSpriteOffset();
|
||||
|
|
Loading…
Reference in New Issue