diff --git a/plugins/GSdx/Renderers/HW/GSRendererHW.cpp b/plugins/GSdx/Renderers/HW/GSRendererHW.cpp index d1eb19be95e79..0ab047011c396 100644 --- a/plugins/GSdx/Renderers/HW/GSRendererHW.cpp +++ b/plugins/GSdx/Renderers/HW/GSRendererHW.cpp @@ -1278,12 +1278,12 @@ GSRendererHW::Hacks::Hacks() m_oi_list.push_back(HackEntry(CRC::SuperManReturns, CRC::RegionCount, &GSRendererHW::OI_SuperManReturns)); m_oi_list.push_back(HackEntry(CRC::ArTonelico2, CRC::RegionCount, &GSRendererHW::OI_ArTonelico2)); m_oi_list.push_back(HackEntry(CRC::ItadakiStreet, CRC::RegionCount, &GSRendererHW::OI_ItadakiStreet)); + m_oi_list.push_back(HackEntry(CRC::Jak2, CRC::RegionCount, &GSRendererHW::OI_JakGames)); + m_oi_list.push_back(HackEntry(CRC::Jak3, CRC::RegionCount, &GSRendererHW::OI_JakGames)); + m_oi_list.push_back(HackEntry(CRC::JakX, CRC::RegionCount, &GSRendererHW::OI_JakGames)); m_oo_list.push_back(HackEntry(CRC::DBZBT2, CRC::RegionCount, &GSRendererHW::OO_DBZBT2)); m_oo_list.push_back(HackEntry(CRC::MajokkoALaMode2, CRC::RegionCount, &GSRendererHW::OO_MajokkoALaMode2)); - m_oo_list.push_back(HackEntry(CRC::Jak2, CRC::RegionCount, &GSRendererHW::OO_JakGames)); - m_oo_list.push_back(HackEntry(CRC::Jak3, CRC::RegionCount, &GSRendererHW::OO_JakGames)); - m_oo_list.push_back(HackEntry(CRC::JakX, CRC::RegionCount, &GSRendererHW::OO_JakGames)); m_cu_list.push_back(HackEntry(CRC::DBZBT2, CRC::RegionCount, &GSRendererHW::CU_DBZBT2)); m_cu_list.push_back(HackEntry(CRC::MajokkoALaMode2, CRC::RegionCount, &GSRendererHW::CU_MajokkoALaMode2)); @@ -1863,6 +1863,169 @@ bool GSRendererHW::OI_ItadakiStreet(GSTexture* rt, GSTexture* ds, GSTextureCache return true; } +bool GSRendererHW::OI_JakGames(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t) +{ + // Jak hair, beard and eyebrows are being rendered to palette. + // Cannot perform GPU render to FB as reading back is both slow + // and clashes with texture inside FB reading needed for correct eye rendering. + // So do CPU rendering instead, which consists in copying palette data from source + // buffer to destination buffer, without any blending/tfx. + + GSVector4i r = GSVector4i(m_vt.m_min.p.xyxy(m_vt.m_max.p)).rintersect(GSVector4i(m_context->scissor.in)); + GSVector4i r_p = GSVector4i(0, 0, 16, 16); + + if (!PRIM->FST && PRIM->TME && (r == r_p).alltrue() && m_context->TEX0.TBW == 1 && m_context->TEX0.TW == 4 && m_context->TEX0.TH == 4 && m_context->TEX0.PSM == PSM_PSMCT32) + { + // No rasterization required + ASSERT(m_vt.m_eq.rgba == 0xffff); + ASSERT(m_vt.m_eq.z == 0x1); + ASSERT(m_vt.m_eq.q == 0x1); + + // Game will render a texture directly into a palette. + uint32 FBP = m_context->FRAME.Block(); + + // Setup registers for SW rendering + GIFRegBITBLTBUF bitbltbuf; + + bitbltbuf.SBP = m_context->TEX0.TBP0; + bitbltbuf.SBW = 1; + bitbltbuf.SPSM = PSM_PSMCT32; + + bitbltbuf.DBP = FBP; + bitbltbuf.DBW = 1; + bitbltbuf.DPSM = PSM_PSMCT32; + + GIFRegTRXPOS trxpos; + + trxpos.DIRX = 0; + trxpos.DIRY = 0; + trxpos.DSAX = 0; + trxpos.DSAY = 0; + trxpos.SSAX = 0; + trxpos.SSAY = 0; + + GIFRegTRXREG trxreg; + + trxreg.RRH = 16; + trxreg.RRW = 16; + + // SW rendering code, mainly taken from GSState::Move() + + int sx = trxpos.SSAX; + int sy = trxpos.SSAY; + int dx = trxpos.DSAX; + int dy = trxpos.DSAY; + int w = trxreg.RRW; + int h = trxreg.RRH; + + GL_INS("OI_JakGames CPU palette rendering: 0x%x W:%d F:%s => 0x%x W:%d F:%s (DIR %d%d), sPos(%d %d) dPos(%d %d) size(%d %d)", + bitbltbuf.SBP, bitbltbuf.SBW, psm_str(bitbltbuf.SPSM), + bitbltbuf.DBP, bitbltbuf.DBW, psm_str(bitbltbuf.DPSM), + trxpos.DIRX, trxpos.DIRY, + sx, sy, dx, dy, w, h); + + InvalidateLocalMem(bitbltbuf, GSVector4i(sx, sy, sx + w, sy + h)); + InvalidateVideoMem(bitbltbuf, GSVector4i(dx, dy, dx + w, dy + h)); + + int xinc = 1; + int yinc = 1; + + if (trxpos.DIRX) { sx += w - 1; dx += w - 1; xinc = -1; } + if (trxpos.DIRY) { sy += h - 1; dy += h - 1; yinc = -1; } + + GSOffset* RESTRICT spo = m_mem.GetOffset(bitbltbuf.SBP, bitbltbuf.SBW, bitbltbuf.SPSM); + GSOffset* RESTRICT dpo = m_mem.GetOffset(bitbltbuf.DBP, bitbltbuf.DBW, bitbltbuf.DPSM); + + int* RESTRICT scol = &spo->pixel.col[0][sx]; + int* RESTRICT dcol = &dpo->pixel.col[0][dx]; + + GSVector4i vc = m_vt.m_min.c; + + uint8 tex0_tcc = m_context->TEX0.TCC; + uint8 alpha_b = m_context->ALPHA.B; + + for (int y = 0; y < h; y++, sy += yinc, dy += yinc) + { + uint32* RESTRICT s = &m_mem.m_vm32[spo->pixel.row[sy]]; + uint32* RESTRICT d = &m_mem.m_vm32[dpo->pixel.row[dy]]; + + for (int x = 0; x < w; x++) + { + // Read source pixel color + GSVector4i sc; + uint32 sc_packed = s[scol[x]]; + sc.r = sc_packed & 0xff; + sc.g = (sc_packed >> 8) & 0xff; + sc.b = (sc_packed >> 16) & 0xff; + sc.a = (sc_packed >> 24) & 0xff; + + // Apply TFX + ASSERT(m_context->TEX0.TFX == 0); + sc.r = ((sc.r * vc.r) >> 7) & 0xff; + sc.g = ((sc.g * vc.g) >> 7) & 0xff; + sc.b = ((sc.b * vc.b) >> 7) & 0xff; + sc.a = tex0_tcc == 0 ? vc.a : ((sc.a * vc.a) >> 7) & 0xff; + + // No FOG + ASSERT(m_env.PRIM.FGE == 0); + + // Read destination pixel color + GSVector4i dc; + uint32 dc_packed = d[dcol[x]]; + dc.r = dc_packed & 0xff; + dc.g = (dc_packed >> 8) & 0xff; + dc.b = (dc_packed >> 16) & 0xff; + dc.a = (dc_packed >> 24) & 0xff; + + // Blending + ASSERT(m_context->ALPHA.A == 0); + ASSERT(alpha_b == 1 || alpha_b == 2); + ASSERT(m_context->ALPHA.C == 0); + ASSERT(m_context->ALPHA.D == 1); + ASSERT(m_context->ALPHA.FIX == 0); + + if (alpha_b == 1) + { + // (Cs - Cd) * As + Cd + dc.r = (((sc.r - dc.r) * sc.a) >> 7) + dc.r; + dc.g = (((sc.g - dc.g) * sc.a) >> 7) + dc.g; + dc.b = (((sc.b - dc.b) * sc.a) >> 7) + dc.b; + } + else if (alpha_b == 2) + { + // (Cs - 0) * As + Cd + dc.r = ((sc.r * sc.a) >> 7) + dc.r; + dc.g = ((sc.g * sc.a) >> 7) + dc.g; + dc.b = ((sc.b * sc.a) >> 7) + dc.b; + } + else + { + GL_INS("OI_JakGames CPU palette rendering: ERROR Unsupported blending ALPHA.B == %d\n", alpha_b); + ASSERT(false); + } + + // Clamping + ASSERT(m_env.COLCLAMP.CLAMP == 1); + dc.r = dc.r & 0xff; + dc.g = dc.g & 0xff; + dc.b = dc.b & 0xff; + + // No Alpha Correction + ASSERT(m_context->FBA.FBA == 0); + dc.a = sc.a; + + dc_packed = (dc.a << 24) | (dc.b << 16) | (dc.g << 8) | dc.r; + + d[dcol[x]] = dc_packed; + } + } + + return false; // skip current draw + } + + return true; +} + // OO (others output?) hacks: invalidate extra local memory after the draw call void GSRendererHW::OO_DBZBT2() @@ -1902,27 +2065,6 @@ void GSRendererHW::OO_MajokkoALaMode2() } } -void GSRendererHW::OO_JakGames() -{ - // FIXME might need a CU_Jak too - GSVector4i r = GSVector4i(m_vt.m_min.p.xyxy(m_vt.m_max.p)).rintersect(GSVector4i(m_context->scissor.in)); - GSVector4i r_p = GSVector4i(0, 0, 16, 16); - - if(!PRIM->FST && PRIM->TME && (r == r_p).alltrue() && m_context->TEX0.TW == 4 && m_context->TEX0.TH == 4 && m_context->TEX0.PSM == PSM_PSMCT32) { - // Game will render a texture directly into a palette. - uint32 FBP = m_context->FRAME.Block(); - GL_INS("OO_JakGames read back 0x%x", FBP); - - GIFRegBITBLTBUF BITBLTBUF; - - BITBLTBUF.SBP = FBP; - BITBLTBUF.SBW = 1; - BITBLTBUF.SPSM = PSM_PSMCT32; - - InvalidateLocalMem(BITBLTBUF, GSVector4i(0, 0, 16, 16)); - } -} - // Can Upscale hacks: disable upscaling for some draw calls bool GSRendererHW::CU_DBZBT2() diff --git a/plugins/GSdx/Renderers/HW/GSRendererHW.h b/plugins/GSdx/Renderers/HW/GSRendererHW.h index e307fb7bcd4e0..2cbdec6b62cb0 100644 --- a/plugins/GSdx/Renderers/HW/GSRendererHW.h +++ b/plugins/GSdx/Renderers/HW/GSRendererHW.h @@ -62,10 +62,10 @@ class GSRendererHW : public GSRenderer bool OI_SuperManReturns(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t); bool OI_ArTonelico2(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t); bool OI_ItadakiStreet(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t); + bool OI_JakGames(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t); void OO_DBZBT2(); void OO_MajokkoALaMode2(); - void OO_JakGames(); bool CU_DBZBT2(); bool CU_MajokkoALaMode2();