Compare commits

...

5 Commits

Author SHA1 Message Date
lightningterror
f6e0d1d368 common-linux: Fix -Wformat warnings. 2025-04-30 22:43:23 +02:00
lightningterror
5bb18bee17 GS/GL: Guard/track PopDebugGroup calls.
Fixes GL_STACK_UNDERFLOW and GL_INVALID_VALUE issues which spammed the log.
2025-04-30 22:43:23 +02:00
lightningterror
d0fb219939 Misc: Fix compiler warnings. 2025-04-30 22:43:23 +02:00
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
6 changed files with 109 additions and 14 deletions

View File

@@ -54,18 +54,18 @@ u64 GetAvailablePhysicalMemory()
while (fgets(line, sizeof(line), file))
{
// Modern kernels provide MemAvailable directly - preferred and most accurate.
if (sscanf(line, "MemAvailable: %llu kB", &mem_available) == 1)
if (sscanf(line, "MemAvailable: %lu kB", &mem_available) == 1)
{
fclose(file);
return mem_available * _1kb;
}
// Fallback values for manual approximation.
sscanf(line, "MemFree: %llu kB", &mem_free);
sscanf(line, "Buffers: %llu kB", &buffers);
sscanf(line, "Cached: %llu kB", &cached);
sscanf(line, "SReclaimable: %llu kB", &sreclaimable);
sscanf(line, "Shmem: %llu kB", &shmem);
sscanf(line, "MemFree: %lu kB", &mem_free);
sscanf(line, "Buffers: %lu kB", &buffers);
sscanf(line, "Cached: %lu kB", &cached);
sscanf(line, "SReclaimable: %lu kB", &sreclaimable);
sscanf(line, "Shmem: %lu kB", &shmem);
}
fclose(file);

View File

@@ -3263,12 +3263,14 @@ 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;
// If we've moved it and the source is expecting to be inside this target, we need to update the region to point to it.
u32 max_region_y = src->m_region.GetMaxY() + new_offset;
int max_region_y = src->m_region.GetMaxY() + new_offset;
if (max_region_y == new_offset)
max_region_y = new_size.y;

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();
@@ -1656,8 +1658,8 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
// PSM equality needed because CreateSource does not handle PSM conversion.
// Only inclusive hit to limit false hits.
GSVector4i rect = block_boundary_rect;
int src_bw = bw;
int src_psm = psm;
u32 src_bw = bw;
u32 src_psm = psm;
// If the input is C16 and it's actually a shuffle of 32bits we need to correct the size.
if ((tex_color_psm & 0xF) <= PSMCT24 && (psm & 0x7) == PSMCT16 && possible_shuffle)
@@ -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);

View File

@@ -2830,6 +2830,9 @@ void GSDeviceOGL::DebugMessageCallback(GLenum gl_source, GLenum gl_type, GLuint
}
}
#ifdef ENABLE_OGL_DEBUG
static int s_debugGroupDepth = 0;
#endif
void GSDeviceOGL::PushDebugGroup(const char* fmt, ...)
{
#ifdef ENABLE_OGL_DEBUG
@@ -2840,18 +2843,26 @@ void GSDeviceOGL::PushDebugGroup(const char* fmt, ...)
va_start(ap, fmt);
const std::string buf(StringUtil::StdStringFromFormatV(fmt, ap));
va_end(ap);
if (!buf.empty())
{
glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0xBAD, -1, buf.c_str());
// Make sure the calls succeed first.
if (glGetError() == GL_NO_ERROR)
s_debugGroupDepth++;
}
#endif
}
void GSDeviceOGL::PopDebugGroup()
{
#ifdef ENABLE_OGL_DEBUG
if (!glPopDebugGroup || !GSConfig.UseDebugDevice)
if (!glPopDebugGroup || !GSConfig.UseDebugDevice || (s_debugGroupDepth <= 0))
return;
glPopDebugGroup();
s_debugGroupDepth--;
#endif
}

View File

@@ -991,7 +991,7 @@ TinyString SDLInputSource::ConvertKeyToString(InputBindingKey key, bool display,
if (it != m_controllers.end())
is_sixaxis = IsControllerSixaxis(*it);
const int joy_axis_Index = key.data - std::size(s_sdl_axis_setting_names);
const size_t joy_axis_Index = key.data - std::size(s_sdl_axis_setting_names);
if (is_sixaxis && key.modifier == InputModifier::FullAxis && key.invert == false &&
joy_axis_Index < std::size(s_sdl_ps3_sxs_pressure_names) && s_sdl_ps3_sxs_pressure_names[joy_axis_Index] != nullptr)
@@ -1112,7 +1112,7 @@ TinyString SDLInputSource::ConvertKeyToIcon(InputBindingKey key)
}
else if (it != m_controllers.end() && IsControllerSixaxis(*it) && key.invert == false)
{
const int joy_axis_Index = key.data - std::size(s_sdl_axis_setting_names);
const size_t joy_axis_Index = key.data - std::size(s_sdl_axis_setting_names);
if (joy_axis_Index < std::size(s_sdl_ps3_pressure_icons) && s_sdl_ps3_pressure_icons[joy_axis_Index] != nullptr)
ret.format("SDL-{} {}", static_cast<u32>(key.source_index), s_sdl_ps3_pressure_icons[joy_axis_Index]);