GSdx-hw TC: improve search tex in RT

Fixes eyes rendering in Jak games both ingame and in cutscenes.
Previous method yielded no eyes in Jak 2/3/X cutscenes.

- Generalized offset search logic with caching system (works for BW > 1
and any PSM, limited to PSMCT32 for now)
- CRC Flags mechanism for default behavior enabling in Jak games
This commit is contained in:
Alessandro Vetere 2019-08-11 14:43:08 +02:00 committed by lightningterror
parent 7c42928b57
commit 9e687895e1
4 changed files with 122 additions and 44 deletions

View File

@ -440,18 +440,18 @@ CRC::Game CRC::m_games[] =
{0XE8F7BAB6, SuperManReturns, EU, 0}, {0XE8F7BAB6, SuperManReturns, EU, 0},
{0x06A7506A, SacredBlaze, JP, 0}, {0x06A7506A, SacredBlaze, JP, 0},
{0x4CE7FB04, ItadakiStreet, JP, 0}, {0x4CE7FB04, ItadakiStreet, JP, 0},
{0x9C712FF0, Jak1, EU, 0}, // Jak and Daxter: The Precursor Legacy {0x9C712FF0, Jak1, EU, TextureInsideRt}, // Jak and Daxter: The Precursor Legacy
{0x472E7699, Jak1, US, 0}, {0x472E7699, Jak1, US, TextureInsideRt},
{0x2479F4A9, Jak2, EU, 0}, {0x2479F4A9, Jak2, EU, TextureInsideRt},
{0x9184AAF1, Jak2, US, 0}, {0x9184AAF1, Jak2, US, TextureInsideRt},
{0xA2034C69, Jak2, US, 0}, // Demo {0xA2034C69, Jak2, US, TextureInsideRt}, // Demo
{0x12804727, Jak3, EU, 0}, {0x12804727, Jak3, EU, TextureInsideRt},
{0x644CFD03, Jak3, US, 0}, {0x644CFD03, Jak3, US, TextureInsideRt},
{0x23F8D35B, Jak3, NoRegion, 0}, // EU Preview, US Internal test build {0x23F8D35B, Jak3, NoRegion, TextureInsideRt}, // EU Preview, US Internal test build
{0xDF659E77, JakX, EU, 0}, // Jak X: Combat Racing {0xDF659E77, JakX, EU, TextureInsideRt}, // Jak X: Combat Racing
{0xC20596DB, JakX, EU, 0}, // Beta Trial Disc, v0.01 {0xC20596DB, JakX, EU, TextureInsideRt}, // Beta Trial Disc, v0.01
{0x3091E6FB, JakX, US, 0}, {0x3091E6FB, JakX, US, TextureInsideRt},
{0xDA366A53, JakX, US, 0}, // Public Beta v.1 {0xDA366A53, JakX, US, TextureInsideRt}, // Public Beta v.1
{0x4653CA3E, HarleyDavidson, US, 0}, {0x4653CA3E, HarleyDavidson, US, 0},
// Games list for Automatic Mipmapping // Games list for Automatic Mipmapping
// Basic mipmapping // Basic mipmapping

View File

@ -185,6 +185,7 @@ public:
enum Flags enum Flags
{ {
PointListPalette = 1, PointListPalette = 1,
TextureInsideRt = 2,
}; };
struct Game struct Game

View File

@ -58,12 +58,16 @@ GSTextureCache::GSTextureCache(GSRenderer* r)
// isn't enough in custom resolution) // isn't enough in custom resolution)
// Test: onimusha 3 PAL 60Hz // Test: onimusha 3 PAL 60Hz
m_temp = (uint8*)_aligned_malloc(9 * 1024 * 1024, 32); m_temp = (uint8*)_aligned_malloc(9 * 1024 * 1024, 32);
m_texture_inside_rt_cache.reserve(m_texture_inside_rt_cache_size);
} }
GSTextureCache::~GSTextureCache() GSTextureCache::~GSTextureCache()
{ {
RemoveAll(); RemoveAll();
m_texture_inside_rt_cache.clear();
_aligned_free(m_temp); _aligned_free(m_temp);
} }
@ -262,6 +266,8 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
// (Simply not doing this code at all makes a lot of previsouly missing stuff show (but breaks pretty much everything // (Simply not doing this code at all makes a lot of previsouly missing stuff show (but breaks pretty much everything
// else.) // else.)
bool texture_inside_rt = ShallSearchTextureInsideRt();
for(auto t : m_dst[RenderTarget]) { for(auto t : m_dst[RenderTarget]) {
if(t->m_used && t->m_dirty.empty()) { if(t->m_used && t->m_dirty.empty()) {
// Typical bug (MGS3 blue cloud): // Typical bug (MGS3 blue cloud):
@ -298,44 +304,93 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
break; break;
} else if (m_texture_inside_rt && psm == PSM_PSMCT32 && bw == 1 && bp_end < t->m_end_block && t->m_TEX0.TBP0 < bp) { } else if (texture_inside_rt && psm == PSM_PSMCT32 && t->m_TEX0.PSM == psm && t->m_TEX0.TBP0 < bp && t->m_end_block >= bp) {
// Note bw == 1 until we find a generic formulae below // Only PSMCT32 to limit false hits
// Check if it is possible to hit with valid <x,y> offset on the given Target
// Fixes Jak eyes rendering
bool valid_offset_may_exist = true;
// CACHE SEARCH: <x,y> offset
for (auto& el : m_texture_inside_rt_cache)
{
if (el.psm == psm && el.bp == bp && el.bp_end == bp_end && el.bw == bw &&
el.t_tex0_tbp0 == t->m_TEX0.TBP0 && el.m_end_block == t->m_end_block)
{
if (el.has_valid_offset)
{
// CACHE HIT: <x,y> offset found
dst = t; dst = t;
x_offset = el.x_offset;
y_offset = el.y_offset;
}
else
{
// CACHE HIT: No valid <x,y> offset exists
valid_offset_may_exist = false;
}
uint32 delta = bp - t->m_TEX0.TBP0; break;
uint32 delta_p = delta / 32; }
uint32 delta_b = delta % 32; }
// FIXME if (dst != nullptr)
x_offset = (delta_p % bw) * psm_s.pgs.x; break;
y_offset = (delta_p / bw) * psm_s.pgs.y;
static int block32_offset_x[32] = { if (!valid_offset_may_exist)
0, 1, 0, 1, continue;
2, 3, 2, 3,
0, 1, 0, 1,
2, 3, 2, 3,
4, 5, 4, 5,
6, 7, 6, 7,
4, 5, 4, 5,
6, 7, 6, 7,
};
static int block32_offset_y[32] = { // CACHE MISS
0, 0, 1, 1,
0, 0, 1, 1,
2, 2, 3, 3,
2, 2, 3, 3,
0, 0, 1, 1,
0, 0, 1, 1,
2, 2, 3, 3,
2, 2, 3, 3,
};
x_offset += block32_offset_x[delta_b] * psm_s.bs.x; // SWEEP SEARCH: <x,y> offset
y_offset += block32_offset_y[delta_b] * psm_s.bs.y;
GL_INS("WARNING middle of framebuffer 0x%x => 0x%x. Offset %d,%d", t->m_TEX0.TBP0, t->m_end_block, x_offset, y_offset); TexInsideRtCacheEntry entry = { psm, bp, bp_end, bw, t->m_TEX0.TBP0, t->m_end_block, false, 0, 0 };
for (int candidate_x_offset = 0; candidate_x_offset < t->m_valid.z; ++candidate_x_offset)
{
for (int candidate_y_offset = 0; candidate_y_offset < t->m_valid.w; ++candidate_y_offset)
{
if (candidate_x_offset == 0 && candidate_y_offset == 0)
continue;
uint32 candidate_bp = psm_s.bn(candidate_x_offset, candidate_y_offset, t->m_TEX0.TBP0, bw);
if (bp == candidate_bp && bp_end <= t->m_end_block)
{
// SWEEP HIT: <x,y> offset found
dst = t;
x_offset = candidate_x_offset;
y_offset = candidate_y_offset;
// Add result to cache
while (m_texture_inside_rt_cache.size() > m_texture_inside_rt_cache_size)
{
GL_PERF("TC tex in rt: Size of cache %d too big, clearing it.", m_texture_inside_rt_cache.size());
m_texture_inside_rt_cache.clear();
}
entry.has_valid_offset = true;
entry.x_offset = x_offset;
entry.y_offset = y_offset;
m_texture_inside_rt_cache.emplace_back(entry);
GL_CACHE("TC tex in rt: Cached HIT element (size %d), BW: %d, PSM %s, rt 0x%x <%d,%d> + off <%d,%d> -> 0x%x <%d,%d> (END: 0x%x)",
m_texture_inside_rt_cache.size(), bw, psm_str(psm), t->m_TEX0.TBP0, t->m_valid.z, t->m_valid.w, x_offset, y_offset, bp, tw, th, bp_end);
break;
}
}
if (dst != nullptr)
break;
}
if (dst != nullptr)
break;
// SWEEP MISS: no valid <x,y> offset found
while (m_texture_inside_rt_cache.size() > m_texture_inside_rt_cache_size)
{
GL_PERF("TC tex in rt: Size of cache %d too big, clearing it.", m_texture_inside_rt_cache.size());
m_texture_inside_rt_cache.clear();
}
GL_CACHE("TC tex in rt: Cached MISS element (size %d), BW: %d, PSM %s, rt 0x%x <%d,%d> -/-> 0x%x <%d,%d> (END: 0x%x)",
m_texture_inside_rt_cache.size(), bw, psm_str(psm), t->m_TEX0.TBP0, t->m_valid.z, t->m_valid.w, bp, tw, th, bp_end);
m_texture_inside_rt_cache.emplace_back(entry);
} }
} }
} }
@ -428,6 +483,11 @@ void GSTextureCache::ScaleTexture(GSTexture* texture)
texture->SetScale(scale_factor); texture->SetScale(scale_factor);
} }
bool GSTextureCache::ShallSearchTextureInsideRt()
{
return m_texture_inside_rt || (m_renderer->m_game.flags & CRC::Flags::TextureInsideRt);
}
GSTextureCache::Target* GSTextureCache::LookupTarget(const GIFRegTEX0& TEX0, int w, int h, int type, bool used, uint32 fbmask) GSTextureCache::Target* GSTextureCache::LookupTarget(const GIFRegTEX0& TEX0, int w, int h, int type, bool used, uint32 fbmask)
{ {
const GSLocalMemory::psm_t& psm_s = GSLocalMemory::m_psm[TEX0.PSM]; const GSLocalMemory::psm_t& psm_s = GSLocalMemory::m_psm[TEX0.PSM];

View File

@ -185,6 +185,19 @@ public:
void RemoveAt(Source* s); void RemoveAt(Source* s);
}; };
struct TexInsideRtCacheEntry
{
uint32 psm;
uint32 bp;
uint32 bp_end;
uint32 bw;
uint32 t_tex0_tbp0;
uint32 m_end_block;
bool has_valid_offset;
int x_offset;
int y_offset;
};
protected: protected:
GSRenderer* m_renderer; GSRenderer* m_renderer;
PaletteMap m_palette_map; PaletteMap m_palette_map;
@ -199,6 +212,8 @@ protected:
static bool m_disable_partial_invalidation; static bool m_disable_partial_invalidation;
bool m_texture_inside_rt; bool m_texture_inside_rt;
static bool m_wrap_gs_mem; static bool m_wrap_gs_mem;
uint8 m_texture_inside_rt_cache_size = 255;
std::vector<TexInsideRtCacheEntry> m_texture_inside_rt_cache;
virtual Source* CreateSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, Target* t = NULL, bool half_right = false, int x_offset = 0, int y_offset = 0); virtual Source* CreateSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, Target* t = NULL, bool half_right = false, int x_offset = 0, int y_offset = 0);
virtual Target* CreateTarget(const GIFRegTEX0& TEX0, int w, int h, int type); virtual Target* CreateTarget(const GIFRegTEX0& TEX0, int w, int h, int type);
@ -231,6 +246,8 @@ public:
bool UserHacks_HalfPixelOffset; bool UserHacks_HalfPixelOffset;
void ScaleTexture(GSTexture* texture); void ScaleTexture(GSTexture* texture);
bool ShallSearchTextureInsideRt();
const char* to_string(int type) { const char* to_string(int type) {
return (type == DepthStencil) ? "Depth" : "Color"; return (type == DepthStencil) ? "Depth" : "Color";
} }