Files
J1coding 4391670f5b GS: make the software JIT cache overflow path real
The software rasterizer has a recovery path for running out of code space:
SetupDraw returns false, the caller resets the cache and asks again. It has
never been able to run.

ReserveMemory only had a pxAssert, which is compiled out of a release build,
so it always handed back a pointer. GetDefaultFunction has no other way to
fail, so SetupDraw could not return false, so ResetCodeCache never ran. What
happens instead is that the bump pointer walks off the end of the reserve,
and since the software renderer sits last in the code arena, that is the end
of the arena.

It would not have worked if it had run, either. Clear emptied the codegen map
and rewound the pointer but left the active map holding pointers into memory
about to be handed out again, so the next lookup would have jumped into
whatever replaced it.

So: ReserveMemory reports full, Clear drops the active map along with the
codegen map, and a null is deliberately not cached on the way out. That last
one matters more than it looks. The active map is consulted before anything
else, so an entry cached during the failure would have survived the reset
that was supposed to fix it and gone on answering null for that selector for
the rest of the run.

Nothing here is iOS specific. It reads the same on every platform, we are
just the ones with a reason to have been looking.
2026-07-30 17:38:45 +02:00

42 lines
965 B
C++

// SPDX-FileCopyrightText: 2002-2026 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#include "GS/Renderers/Common/GSFunctionMap.h"
#include "Memory.h"
namespace GSCodeReserve
{
static u8* s_memory_base;
static u8* s_memory_end;
static u8* s_memory_ptr;
}
void GSCodeReserve::ResetMemory()
{
s_memory_base = SysMemory::GetSWRec();
s_memory_end = SysMemory::GetSWRecEnd();
s_memory_ptr = s_memory_base;
}
size_t GSCodeReserve::GetMemoryUsed()
{
return s_memory_ptr - s_memory_base;
}
u8* GSCodeReserve::ReserveMemory(size_t size)
{
// Null means full, and the caller resets the cache and asks again. This
// used to be an assert, so a release build walked off the end of the
// reserve instead, which is the end of the whole code arena.
if ((s_memory_ptr + size) > s_memory_end)
return nullptr;
return s_memory_ptr;
}
void GSCodeReserve::CommitMemory(size_t size)
{
pxAssert((s_memory_ptr + size) <= s_memory_end);
s_memory_ptr += size;
}