Compare commits

...

2 Commits

Author SHA1 Message Date
refractionpcsx2
e2b04726cf GS/HW: Fix a crash on close and old target mem leak 2025-04-30 22:10:35 +02:00
refractionpcsx2
b293cdc9ba GS/HW: Merge contained targets when expanding target backward. 2025-04-30 22:10:35 +02:00
3 changed files with 86 additions and 2 deletions

View File

@@ -3263,6 +3263,8 @@ void GSRendererHW::Draw()
const GSVector4i new_drect = GSVector4i(0, new_offset * rt->m_scale, new_size.x * rt->m_scale, new_size.y * rt->m_scale);
rt->ResizeTexture(new_size.x, new_size.y, true, true, new_drect);
g_texture_cache->CombineAlignedInsideTargets(rt, src);
if (src && src->m_from_target && src->m_from_target == rt && src->m_target_direct)
{
src->m_texture = rt->m_texture;

View File

@@ -77,6 +77,8 @@ void GSTextureCache::ReadbackAll()
void GSTextureCache::RemoveAll(bool sources, bool targets, bool hash_cache)
{
InvalidateTemporaryZ();
if (sources || targets)
{
m_src.RemoveAll();
@@ -2062,6 +2064,60 @@ GSVector2i GSTextureCache::ScaleRenderTargetSize(const GSVector2i& sz, float sca
static_cast<int>(std::ceil(static_cast<float>(sz.y) * scale)));
}
void GSTextureCache::CombineAlignedInsideTargets(Target* target, GSTextureCache::Source* src)
{
auto& list = m_dst[target->m_type];
for (auto i = list.begin(); i != list.end();)
{
Target* t = *i;
if (t != target)
{
// Target not contained, skip it.
if (t->m_TEX0.TBP0 < target->m_TEX0.TBP0 || t->m_end_block > target->m_end_block)
{
i++;
continue;
}
// Formats match
if (t->m_TEX0.TBW == target->m_TEX0.TBW && GSLocalMemory::m_psm[t->m_TEX0.PSM].bpp == GSLocalMemory::m_psm[target->m_TEX0.PSM].bpp)
{
const GSLocalMemory::psm_t& t_psm = GSLocalMemory::m_psm[t->m_TEX0.PSM];
const u32 page_offset = ((t->m_TEX0.TBP0 - target->m_TEX0.TBP0) >> 5) % std::max(1U, t->m_TEX0.TBW);
const u32 page_width = (t->m_valid.z + (t_psm.pgs.x - 1)) / t_psm.pgs.x;
if ((page_offset + page_width) <= target->m_TEX0.TBW)
{
const u32 vertical_offset = (((t->m_TEX0.TBP0 - target->m_TEX0.TBP0) >> 5) / std::max(1U, t->m_TEX0.TBW)) * t_psm.pgs.y;
const u32 horizontal_offset = page_offset * t_psm.pgs.x;
const GSVector4i target_drect_unscaled = t->m_valid + GSVector4i(horizontal_offset, vertical_offset).xyxy();
const GSVector4 target_drect = GSVector4(target_drect_unscaled) * target->m_scale;
g_gs_device->StretchRect(t->m_texture, GSVector4(0, 0, 1, 1), target->m_texture, target_drect, (target->m_type == RenderTarget) ? ShaderConvert::COPY : ShaderConvert::DEPTH_COPY, t->m_scale < target->m_scale);
target->UpdateValidity(target_drect_unscaled);
if (src && src->m_from_target == t)
{
src->m_texture = t->m_texture;
src->m_shared_texture = false;
src->m_target_direct = false;
t->m_texture = nullptr;
}
InvalidateSourcesFromTarget(t);
i = list.erase(i);
delete t;
continue;
}
}
}
i++;
}
}
GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVector2i& size, float scale, int type,
bool used, u32 fbmask, bool is_frame, bool preload, bool preserve_rgb, bool preserve_alpha, const GSVector4i draw_rect,
bool is_shuffle, bool possible_clear, bool preserve_scale, GSTextureCache::Source* src, GSTextureCache::Target* ds, int offset)
@@ -3135,11 +3191,32 @@ bool GSTextureCache::PreloadTarget(GIFRegTEX0 TEX0, const GSVector2i& size, cons
auto j = i;
Target* t = *j;
if (dst != t && t->m_TEX0.PSM == dst->m_TEX0.PSM && dst->m_TEX0.TBW == t->m_TEX0.TBW && t->Overlaps(dst->m_TEX0.TBP0, dst->m_TEX0.TBW, dst->m_TEX0.PSM, dst->m_valid) &&
if (dst != t && t->m_TEX0.PSM == dst->m_TEX0.PSM && t->Overlaps(dst->m_TEX0.TBP0, dst->m_TEX0.TBW, dst->m_TEX0.PSM, dst->m_valid) &&
static_cast<int>(((t->m_TEX0.TBP0 - dst->m_TEX0.TBP0) / 32) % std::max(dst->m_TEX0.TBW, 1U)) <= std::max(0, static_cast<int>(dst->m_TEX0.TBW - t->m_TEX0.TBW)))
{
const u32 buffer_width = std::max(1U, dst->m_TEX0.TBW);
if (buffer_width != std::max(1U, t->m_TEX0.TBW))
{
// Check if this got messed with at some point, if it did just nuke it.
if (t->m_valid.width() == dst->m_valid.width())
{
// Not correct, but it's better than a null reference.
if (src && src->m_target_direct && src->m_from_target == t)
{
DevCon.Warning("Replacing source target, texture may be invalid");
src->m_texture = dst->m_texture;
src->m_from_target = dst;
}
InvalidateSourcesFromTarget(t);
i = list.erase(j);
delete t;
}
else
i++;
continue;
}
// If the two targets are misaligned, it's likely a relocation, so we can just kill the old target.
// Kill targets that are overlapping new targets, but ignore the copy if the old target is dirty because we favour GS memory.
if (((((t->m_TEX0.TBP0 - dst->m_TEX0.TBP0) >> 5) % buffer_width) != 0) && !t->m_dirty.empty())
@@ -3268,7 +3345,9 @@ bool GSTextureCache::PreloadTarget(GIFRegTEX0 TEX0, const GSVector2i& size, cons
{
if (GSUtil::GetChannelMask(dst->m_TEX0.PSM) == 0x7 && (t->m_valid_alpha_high || t->m_valid_alpha_low))
{
t->m_valid_rgb = false;
if (GSLocalMemory::m_psm[dst->m_TEX0.PSM].depth == GSLocalMemory::m_psm[t->m_TEX0.PSM].depth)
t->m_valid_rgb = false;
i++;
continue;
}
@@ -3322,6 +3401,8 @@ bool GSTextureCache::PreloadTarget(GIFRegTEX0 TEX0, const GSVector2i& size, cons
i = list.erase(j);
delete t;
}
continue;
}
}

View File

@@ -497,6 +497,7 @@ public:
Source* LookupDepthSource(const bool is_depth, const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GIFRegCLAMP& CLAMP, const GSVector4i& r, const bool possible_shuffle, const bool linear, const u32 frame_fbp = 0xFFFFFFFF, bool req_color = true, bool req_alpha = true, bool palette = false);
Target* FindTargetOverlap(Target* target, int type, int psm);
void CombineAlignedInsideTargets(Target* target, GSTextureCache::Source* src = nullptr);
Target* LookupTarget(GIFRegTEX0 TEX0, const GSVector2i& size, float scale, int type, bool used = true, u32 fbmask = 0,
bool is_frame = false, bool preload = GSConfig.PreloadFrameWithGSData, bool preserve_rgb = true, bool preserve_alpha = true,
const GSVector4i draw_rc = GSVector4i::zero(), bool is_shuffle = false, bool possible_clear = false, bool preserve_scale = false, GSTextureCache::Source* src = nullptr, GSTextureCache::Target* ds = nullptr, int offset = -1);