GS/HW: Implement moves in texture cache

This commit is contained in:
Connor McLaughlin 2022-04-25 22:31:30 +10:00 committed by refractionpcsx2
parent b45360b49e
commit d552f8dc43
5 changed files with 89 additions and 1 deletions

View File

@ -313,7 +313,8 @@ public:
virtual void InvalidateVideoMem(const GIFRegBITBLTBUF& BITBLTBUF, const GSVector4i& r) {}
virtual void InvalidateLocalMem(const GIFRegBITBLTBUF& BITBLTBUF, const GSVector4i& r, bool clut = false) {}
void Move();
virtual void Move();
void Write(const u8* mem, int len);
void Read(u8* mem, int len);
void InitReadFIFO(u8* mem, int len);

View File

@ -799,6 +799,26 @@ void GSRendererHW::InvalidateLocalMem(const GIFRegBITBLTBUF& BITBLTBUF, const GS
m_tc->InvalidateLocalMem(m_mem.GetOffset(BITBLTBUF.SBP, BITBLTBUF.SBW, BITBLTBUF.SPSM), r);
}
void GSRendererHW::Move()
{
int sx = m_env.TRXPOS.SSAX;
int sy = m_env.TRXPOS.SSAY;
int dx = m_env.TRXPOS.DSAX;
int dy = m_env.TRXPOS.DSAY;
const int w = m_env.TRXREG.RRW;
const int h = m_env.TRXREG.RRH;
if (m_tc->Move(m_env.BITBLTBUF.SBP, m_env.BITBLTBUF.SBW, m_env.BITBLTBUF.SPSM, sx, sy,
m_env.BITBLTBUF.DBP, m_env.BITBLTBUF.DBW, m_env.BITBLTBUF.DPSM, dx, dy, w, h))
{
// Handled entirely in TC, no need to update local memory.
return;
}
GSRenderer::Move();
}
u16 GSRendererHW::Interpolate_UV(float alpha, int t0, int t1)
{
const float t = (1.0f - alpha) * t0 + alpha * t1;

View File

@ -190,6 +190,7 @@ public:
GSTexture* GetFeedbackOutput() override;
void InvalidateVideoMem(const GIFRegBITBLTBUF& BITBLTBUF, const GSVector4i& r) override;
void InvalidateLocalMem(const GIFRegBITBLTBUF& BITBLTBUF, const GSVector4i& r, bool clut = false) override;
void Move() override;
void Draw() override;
void PurgeTextureCache() override;

View File

@ -1045,6 +1045,68 @@ void GSTextureCache::InvalidateLocalMem(const GSOffset& off, const GSVector4i& r
// TODO: ds
}
bool GSTextureCache::Move(u32 SBP, u32 SBW, u32 SPSM, int sx, int sy, u32 DBP, u32 DBW, u32 DPSM, int dx, int dy, int w, int h)
{
// TODO: In theory we could do channel swapping on the GPU, but we haven't found anything which needs it so far.
// Same with SBP == DBP, but this behavior could change based on direction?
if (SPSM != DPSM || SBP == DBP)
{
GL_CACHE("Skipping HW move from 0x%X to 0x%X with SPSM=%u DPSM=%u", SBP, DBP, SPSM, DPSM);
return false;
}
// DX11/12 is a bit lame and can't partial copy depth targets. We could do this with a blit instead,
// but so far haven't seen anything which needs it.
if (GSConfig.Renderer == GSRendererType::DX11 || GSConfig.Renderer == GSRendererType::DX12)
{
if (GSLocalMemory::m_psm[SPSM].depth || GSLocalMemory::m_psm[DPSM].depth)
return false;
}
// Look for an exact match on the targets.
GSTextureCache::Target* src = GetExactTarget(SBP, SBW, SPSM);
GSTextureCache::Target* dst = GetExactTarget(DBP, DBW, DPSM);
if (!src || !dst || src->m_texture->GetScale() != dst->m_texture->GetScale())
return false;
// Scale coordinates.
const GSVector2 scale(src->m_texture->GetScale());
const int scaled_sx = static_cast<int>(sx * scale.x);
const int scaled_sy = static_cast<int>(sy * scale.y);
const int scaled_dx = static_cast<int>(dx * scale.x);
const int scaled_dy = static_cast<int>(dy * scale.y);
const int scaled_w = static_cast<int>(w * scale.x);
const int scaled_h = static_cast<int>(h * scale.y);
// Make sure the copy doesn't go out of bounds (it shouldn't).
if ((scaled_sx + scaled_w) > src->m_texture->GetWidth() || (scaled_sy + scaled_h) > src->m_texture->GetHeight() ||
(scaled_dx + scaled_w) > dst->m_texture->GetWidth() || (scaled_dy + scaled_h) > dst->m_texture->GetHeight())
{
return false;
}
g_gs_device->CopyRect(src->m_texture, dst->m_texture,
GSVector4i(scaled_sx, scaled_sy, scaled_sx + scaled_w, scaled_sy + scaled_h),
scaled_dx, scaled_dy);
// Invalidate any sources that overlap with the target (since they're now stale).
InvalidateVideoMem(g_gs_renderer->m_mem.GetOffset(DBP, DBW, DPSM), GSVector4i(dx, dy, dx + w, dy + h), false);
return true;
}
GSTextureCache::Target* GSTextureCache::GetExactTarget(u32 BP, u32 BW, u32 PSM) const
{
auto& rts = m_dst[GSLocalMemory::m_psm[PSM].depth ? DepthStencil : RenderTarget];
for (auto it = rts.begin(); it != rts.end(); ++it) // Iterate targets from MRU to LRU.
{
Target* t = *it;
if (t->m_TEX0.TBP0 == BP && t->m_TEX0.TBW == BW && t->m_TEX0.PSM == PSM)
return t;
}
return nullptr;
}
// Hack: remove Target that are strictly included in current rt. Typically uses for FMV
// For example, game is rendered at 0x800->0x1000, fmv will be uploaded to 0x0->0x2800
// FIXME In theory, we ought to report the data from the sub rt to the main rt. But let's

View File

@ -283,6 +283,9 @@ protected:
Source* CreateSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, Target* t = NULL, bool half_right = false, int x_offset = 0, int y_offset = 0, const GSVector2i* lod = nullptr, const GSVector4i* src_range = nullptr);
Target* CreateTarget(const GIFRegTEX0& TEX0, int w, int h, int type, const bool clear);
/// Looks up a target in the cache, and only returns it if the BP/BW/PSM match exactly.
Target* GetExactTarget(u32 BP, u32 BW, u32 PSM) const;
HashCacheEntry* LookupHashCache(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, bool& paltex, const u32* clut, const GSVector2i* lod);
static void PreloadTexture(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, GSLocalMemory& mem, bool paltex, GSTexture* tex, u32 level);
@ -312,6 +315,7 @@ public:
void InvalidateVideoMemSubTarget(GSTextureCache::Target* rt);
void InvalidateVideoMem(const GSOffset& off, const GSVector4i& r, bool target = true);
void InvalidateLocalMem(const GSOffset& off, const GSVector4i& r);
bool Move(u32 SBP, u32 SBW, u32 SPSM, int sx, int sy, u32 DBP, u32 DBW, u32 DPSM, int dx, int dy, int w, int h);
void IncAge();