GS: targets come from a pool too, and a palette's CLUT is part of it

Following the source pool (ae056015c), the other two per-draw
allocations in the texture cache.

Targets: made with new and deleted in eighteen places while a game runs
- every time a render target or depth buffer is created, resized, or
invalidated. A target is 160 bytes, so a pool of TARGET_POOL_SIZE slots
is 24 KB, one allocation, and taking or returning one is an index off a
free list. Same fallback as sources: more live targets than the pool
holds come from the allocator, so the size is a budget.

Palettes: the CLUT was a 64-byte or 1 KB allocation made in the
constructor and freed in the destructor, one per palette, and a game
that animates its CLUT makes them constantly - Tomb Raider Legend's
scope view is one. It is an array in the object now, always 256 entries
so the object is one shape. A palette is 1048 bytes rather than 32 plus
an allocation; at the map's cap of 1024 per map that is 2 MB against 1
MB of separate blocks, and in practice a game holds a fraction of the
cap. The allocation per palette is gone.

What is left allocating per draw in this file: the shared_ptr around a
Palette, which needs the refcount made explicit before the object can be
pooled, and the standard containers. Both are the next step.

GSTextureCache.cpp, GSRendererHW.cpp and GSHwHack.cpp compile with the
Makefile's flags. Not run.
This commit is contained in:
LibretroAdmin authored and libretroadmin committed 2026-09-20 01:51:12 +02:00
1 parent ae056015c3
commit da4d6052a8
2 files changed
+63 -4

No files matched your search

+48 -3
View File
@@ -5484,6 +5484,54 @@ static size_t s_source_stride;
static u16 s_source_free[GSTextureCache::SOURCE_POOL_SIZE];
static u32 s_source_free_count;
/* The same, for targets. Two pools rather than one shared by size: a
* slot is whatever its class is, so neither needs a size check beyond
* the one below, and a target cannot land in a source's slot. */
static u8* s_target_pool;
static size_t s_target_stride;
static u16 s_target_free[GSTextureCache::TARGET_POOL_SIZE];
static u32 s_target_free_count;
void* GSTextureCache::Target::operator new(size_t size)
{
if (!s_target_pool)
{
u32 i;
s_target_stride = (size + 31) & ~(size_t)31;
s_target_pool = (u8*)memalign_alloc(32, s_target_stride * TARGET_POOL_SIZE);
if (!s_target_pool)
{
s_target_stride = 0;
return memalign_alloc(32, size);
}
for (i = 0; i < TARGET_POOL_SIZE; i++)
s_target_free[i] = (u16)(TARGET_POOL_SIZE - 1 - i);
s_target_free_count = TARGET_POOL_SIZE;
}
if (s_target_free_count && size <= s_target_stride)
return s_target_pool + (size_t)s_target_free[--s_target_free_count] * s_target_stride;
return memalign_alloc(32, size);
}
void GSTextureCache::Target::operator delete(void* p)
{
if (!p)
return;
if (s_target_pool && (u8*)p >= s_target_pool
&& (u8*)p < s_target_pool + s_target_stride * TARGET_POOL_SIZE)
{
const size_t off = (size_t)((u8*)p - s_target_pool);
if (s_target_free_count < TARGET_POOL_SIZE)
s_target_free[s_target_free_count++] = (u16)(off / s_target_stride);
return;
}
memalign_free(p);
}
void* GSTextureCache::Source::operator new(size_t size)
{
if (!s_source_pool)
@@ -6525,7 +6573,6 @@ GSTextureCache::Palette::Palette(const u32* clut, u16 pal, bool need_gs_texture)
, m_pal(pal)
{
const u16 palette_size = pal * sizeof(u32);
m_clut = (u32*)memalign_alloc(64, palette_size);
memcpy(m_clut, clut, palette_size);
if (need_gs_texture)
{
@@ -6555,8 +6602,6 @@ GSTextureCache::Palette::~Palette()
g_texture_cache->m_source_memory_usage -= m_tex_palette->GetMemUsage();
g_gs_device->Recycle(m_tex_palette);
}
memalign_free(m_clut);
}
std::pair<u8, u8> GSTextureCache::Palette::GetAlphaMinMax(u8 min_index, u8 max_index) const
+15 -1
View File
@@ -183,7 +183,11 @@ public:
private:
private:
u32* m_clut;
/* Sixteen or two hundred and fifty-six entries, in the object.
* It was a 64-byte or 1 KB allocation per palette, made in the
* constructor and freed in the destructor, for something a game
* that animates its CLUT churns constantly. */
u32 m_clut[256];
GSTexture* m_tex_palette;
u16 m_pal;
std::pair<u8, u8> m_alpha_minmax;
@@ -222,9 +226,19 @@ public:
bool operator()(const PaletteKey& lhs, const PaletteKey& rhs) const;
};
/* Targets live longer than sources and there are far fewer of them -
* a render target and a depth buffer per buffer in flight, plus what
* the game switches between - but they are still made and destroyed
* while a game runs. */
static constexpr u32 TARGET_POOL_SIZE = 128;
class Target : public Surface
{
public:
/* Out of a pool, as Source is. */
static void* operator new(size_t size);
static void operator delete(void* p);
const int m_type = 0;
int m_alpha_max = 0;
int m_alpha_min = 0;