mirror of https://github.com/PCSX2/pcsx2.git
GS/HW: Cache target widths as well as heights
Avoids redundant resizes. Also align widths to page sizes, like heights.
This commit is contained in:
parent
65d78eff57
commit
8af2d17d1f
|
@ -725,11 +725,11 @@ GSVector2i GSRendererHW::GetTargetSize(const GSTextureCache::Source* tex)
|
|||
{
|
||||
// Don't blindly expand out to the scissor size if we're not drawing to it.
|
||||
// e.g. Burnout 3, God of War II, etc.
|
||||
u32 min_height = std::min<u32>(static_cast<u32>(m_context->scissor.in.w), m_r.w);
|
||||
int height = std::min<int>(static_cast<int>(m_context->scissor.in.w), m_r.w);
|
||||
|
||||
// If the draw is less than a page high, FBW=0 is the same as FBW=1.
|
||||
const GSLocalMemory::psm_t& frame_psm = GSLocalMemory::m_psm[m_context->FRAME.PSM];
|
||||
u32 width = std::min(std::max<u32>(m_context->FRAME.FBW, 1) * 64u, static_cast<u32>(m_context->scissor.in.z));
|
||||
int width = std::min(std::max<int>(m_context->FRAME.FBW, 1) * 64, static_cast<int>(m_context->scissor.in.z));
|
||||
if (m_context->FRAME.FBW == 0 && m_r.w > frame_psm.pgs.y)
|
||||
{
|
||||
GL_INS("FBW=0 when drawing more than 1 page in height (PSM %s, PGS %dx%d).", psm_str(m_context->FRAME.PSM),
|
||||
|
@ -743,12 +743,13 @@ GSVector2i GSRendererHW::GetTargetSize(const GSTextureCache::Source* tex)
|
|||
const int page_y = frame_psm.pgs.y - 1;
|
||||
|
||||
// Round up the page as channel shuffles are generally done in pages at a time
|
||||
width = (std::max(static_cast<u32>(PCRTCDisplays.GetResolution().x), width) + page_x) & ~page_x;
|
||||
min_height = (std::max(static_cast<u32>(PCRTCDisplays.GetResolution().y), min_height) + page_y) & ~page_y;
|
||||
width = (std::max(PCRTCDisplays.GetResolution().x, width) + page_x) & ~page_x;
|
||||
height = (std::max(PCRTCDisplays.GetResolution().y, height) + page_y) & ~page_y;
|
||||
}
|
||||
|
||||
// Align to page size. Since FRAME/Z has to always start on a page boundary, in theory no two should overlap.
|
||||
min_height = Common::AlignUpPow2(min_height, frame_psm.pgs.y);
|
||||
width = Common::AlignUpPow2(width, frame_psm.pgs.x);
|
||||
height = Common::AlignUpPow2(height, frame_psm.pgs.y);
|
||||
|
||||
// Early detection of texture shuffles. These double the input height because they're interpreting 64x32 C32 pages as 64x64 C16.
|
||||
// Why? Well, we don't want to be doubling the heights of targets, but also we don't want to align C32 targets to 64 instead of 32.
|
||||
|
@ -767,21 +768,21 @@ GSVector2i GSRendererHW::GetTargetSize(const GSTextureCache::Source* tex)
|
|||
// Games such as Midnight Club 3 draw headlights with a texture shuffle, but instead of doubling the height, they doubled the width.
|
||||
if (tex_width_pgs == half_draw_width_pgs)
|
||||
{
|
||||
GL_CACHE("Halving width due to texture shuffle with double width, %dx%d -> %dx%d", width, min_height, width / 2, min_height);
|
||||
GL_CACHE("Halving width due to texture shuffle with double width, %dx%d -> %dx%d", width, height, width / 2, height);
|
||||
width /= 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
GL_CACHE("Halving height due to texture shuffle, %dx%d -> %dx%d", width, min_height, width, min_height / 2);
|
||||
min_height /= 2;
|
||||
GL_CACHE("Halving height due to texture shuffle, %dx%d -> %dx%d", width, height, width, height / 2);
|
||||
height /= 2;
|
||||
}
|
||||
}
|
||||
|
||||
u32 height = g_texture_cache->GetTargetHeight(m_context->FRAME.Block(), m_context->FRAME.FBW, m_context->FRAME.PSM, min_height);
|
||||
const GSVector2i size = g_texture_cache->GetTargetSize(m_context->FRAME.Block(), m_context->FRAME.FBW, m_context->FRAME.PSM, width, height);
|
||||
|
||||
GL_INS("Target size for %x %u %u: %ux%u", m_context->FRAME.Block(), m_context->FRAME.FBW, m_context->FRAME.PSM, width, height);
|
||||
GL_INS("Target size for %x %u %u: %ux%u", m_context->FRAME.Block(), m_context->FRAME.FBW, m_context->FRAME.PSM, size.x, size.y);
|
||||
|
||||
return GSVector2i(width, height);
|
||||
return size;
|
||||
}
|
||||
|
||||
bool GSRendererHW::IsPossibleChannelShuffle() const
|
||||
|
|
|
@ -825,7 +825,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
|
|||
const GSVector4i valid_rect = { t->m_valid.x, t->m_valid.y, t->m_valid.z + std::max(0, size_delta.x), t->m_valid.w + std::max(0, size_delta.y) };
|
||||
t->UpdateValidity(valid_rect);
|
||||
t->UpdateValidBits(GSLocalMemory::m_psm[t->m_TEX0.PSM].fmsk);
|
||||
GetTargetHeight(TEX0.TBP0, TEX0.TBW, TEX0.PSM, t->m_valid.w);
|
||||
GetTargetSize(TEX0.TBP0, TEX0.TBW, TEX0.PSM, t->m_valid.z, t->m_valid.w);
|
||||
const int new_w = std::max(t->m_unscaled_size.x, t->m_valid.z);
|
||||
const int new_h = std::max(t->m_unscaled_size.y, t->m_valid.w);
|
||||
t->ResizeTexture(new_w, new_h);
|
||||
|
@ -1506,8 +1506,8 @@ void GSTextureCache::ScaleTargetForDisplay(Target* t, const GIFRegTEX0& dispfb,
|
|||
AddDirtyRectTarget(t, newrect, t->m_TEX0.PSM, t->m_TEX0.TBW, rgba);
|
||||
}
|
||||
|
||||
// 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));
|
||||
// Inject the new size back into the cache.
|
||||
GetTargetSize(t->m_TEX0.TBP0, t->m_TEX0.TBW, t->m_TEX0.PSM, 0, static_cast<u32>(needed_height));
|
||||
}
|
||||
|
||||
bool GSTextureCache::PrepareDownloadTexture(u32 width, u32 height, GSTexture::Format format, std::unique_ptr<GSDownloadTexture>* tex)
|
||||
|
@ -2366,9 +2366,8 @@ bool GSTextureCache::Move(u32 SBP, u32 SBW, u32 SPSM, int sx, int sy, u32 DBP, u
|
|||
new_TEX0.TBW = DBW;
|
||||
new_TEX0.PSM = DPSM;
|
||||
|
||||
const int real_height = GetTargetHeight(DBP, DBW, DPSM, h);
|
||||
dst = LookupTarget(new_TEX0, GSVector2i(static_cast<int>(Common::AlignUpPow2(w, 64)),
|
||||
static_cast<int>(real_height)), src->m_scale, src->m_type, true);
|
||||
const GSVector2i target_size = GetTargetSize(DBP, DBW, DPSM, Common::AlignUpPow2(w, 64), h);
|
||||
dst = LookupTarget(new_TEX0, target_size, src->m_scale, src->m_type, true);
|
||||
if (dst)
|
||||
{
|
||||
dst->UpdateValidity(GSVector4i(dx, dy, dx + w, dy + h));
|
||||
|
@ -2415,7 +2414,7 @@ bool GSTextureCache::Move(u32 SBP, u32 SBW, u32 SPSM, int sx, int sy, u32 DBP, u
|
|||
// We don't recycle the old texture here, because the height cache will track the new size,
|
||||
// so the old size won't get created again.
|
||||
GL_INS("Resize %dx%d target to %dx%d for move", dst->m_unscaled_size.x, dst->m_unscaled_size.y, dst->m_unscaled_size.x, new_height);
|
||||
GetTargetHeight(DBP, DBW, DPSM, new_height);
|
||||
GetTargetSize(DBP, DBW, DPSM, 0, new_height);
|
||||
|
||||
if (!dst->ResizeTexture(dst->m_unscaled_size.x, new_height, false))
|
||||
{
|
||||
|
@ -2591,12 +2590,13 @@ GSTextureCache::Target* GSTextureCache::GetTargetWithSharedBits(u32 BP, u32 PSM)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
u32 GSTextureCache::GetTargetHeight(u32 bp, u32 fbw, u32 psm, u32 min_height)
|
||||
GSVector2i GSTextureCache::GetTargetSize(u32 bp, u32 fbw, u32 psm, s32 min_width, s32 min_height)
|
||||
{
|
||||
TargetHeightElem search = {};
|
||||
search.bp = bp;
|
||||
search.fbw = fbw;
|
||||
search.psm = psm;
|
||||
search.width = min_width;
|
||||
search.height = min_height;
|
||||
|
||||
for (auto it = m_target_heights.begin(); it != m_target_heights.end(); ++it)
|
||||
|
@ -2604,21 +2604,24 @@ u32 GSTextureCache::GetTargetHeight(u32 bp, u32 fbw, u32 psm, u32 min_height)
|
|||
TargetHeightElem& elem = const_cast<TargetHeightElem&>(*it);
|
||||
if (elem.bits == search.bits)
|
||||
{
|
||||
if (elem.height < min_height)
|
||||
if (elem.width < min_width || elem.height < min_height)
|
||||
{
|
||||
DbgCon.WriteLn("Expand height at %x %u %u from %u to %u", bp, fbw, psm, elem.height, min_height);
|
||||
elem.height = min_height;
|
||||
DbgCon.WriteLn("Expand size at %x %u %u from %ux%u to %ux%u", bp, fbw, psm, elem.width, elem.height,
|
||||
min_width, min_height);
|
||||
}
|
||||
|
||||
elem.width = std::max(elem.width, min_width);
|
||||
elem.height = std::max(elem.height, min_height);
|
||||
|
||||
m_target_heights.MoveFront(it.Index());
|
||||
elem.age = 0;
|
||||
return elem.height;
|
||||
return GSVector2i(elem.width, elem.height);
|
||||
}
|
||||
}
|
||||
|
||||
DbgCon.WriteLn("New height at %x %u %u: %u", bp, fbw, psm, min_height);
|
||||
DbgCon.WriteLn("New size at %x %u %u: %ux%u", bp, fbw, psm, min_width, min_height);
|
||||
m_target_heights.push_front(search);
|
||||
return min_height;
|
||||
return GSVector2i(min_width, min_height);
|
||||
}
|
||||
|
||||
bool GSTextureCache::Has32BitTarget(u32 bp)
|
||||
|
@ -2762,6 +2765,9 @@ void GSTextureCache::IncAge()
|
|||
// Original maxage was 4 here, Xenosaga 2 needs at least 240, else it flickers on scene transitions.
|
||||
static constexpr int max_rt_age = 400; // ffx intro scene changes leave the old image untouched for a couple of frames and only then start using it
|
||||
|
||||
// Toss and recompute sizes after 2 seconds of not being used. Should be sufficient for most loading screens.
|
||||
static constexpr int max_size_age = 120;
|
||||
|
||||
for (int type = 0; type < 2; type++)
|
||||
{
|
||||
auto& list = m_dst[type];
|
||||
|
@ -2797,7 +2803,7 @@ void GSTextureCache::IncAge()
|
|||
for (auto it = m_target_heights.begin(); it != m_target_heights.end();)
|
||||
{
|
||||
TargetHeightElem& elem = const_cast<TargetHeightElem&>(*it);
|
||||
if (elem.age >= max_rt_age)
|
||||
if (elem.age >= max_size_age)
|
||||
{
|
||||
it = m_target_heights.erase(it);
|
||||
}
|
||||
|
|
|
@ -345,7 +345,8 @@ public:
|
|||
};
|
||||
};
|
||||
|
||||
u32 height;
|
||||
s32 width;
|
||||
s32 height;
|
||||
u32 age;
|
||||
};
|
||||
|
||||
|
@ -451,7 +452,7 @@ public:
|
|||
Target* GetExactTarget(u32 BP, u32 BW, u32 PSM) const;
|
||||
Target* GetTargetWithSharedBits(u32 BP, u32 PSM) const;
|
||||
|
||||
u32 GetTargetHeight(u32 bp, u32 fbw, u32 psm, u32 min_height);
|
||||
GSVector2i GetTargetSize(u32 bp, u32 fbw, u32 psm, s32 min_width, s32 min_height);
|
||||
bool Has32BitTarget(u32 bp);
|
||||
|
||||
void InvalidateVideoMemType(int type, u32 bp);
|
||||
|
|
Loading…
Reference in New Issue