GS: Improve state load determinism

CLUT wasn't force reloaded, so if the game didn't write to TEX0 before
its first draw after loading a state, you got either the CLUT before the
state was loaded, or random/uninitialized garbage.

Do the same for reset as well, except zero it out in that case.
This commit is contained in:
Stenzek 2024-03-28 00:59:58 +10:00 committed by Connor McLaughlin
parent a317e9c038
commit 2eab0f9757
3 changed files with 28 additions and 12 deletions

View File

@ -13,8 +13,6 @@
GSClut::GSClut(GSLocalMemory* mem)
: m_mem(mem)
{
static constexpr u32 CLUT_ALLOC_SIZE = 4096 * 2;
// 1k + 1k for mirrored area simulating wrapping memory
m_clut = static_cast<u16*>(_aligned_malloc(CLUT_ALLOC_SIZE, VECTOR_ALIGNMENT));
if (!m_clut)
@ -94,10 +92,8 @@ GSClut::GSClut(GSLocalMemory* mem)
GSClut::~GSClut()
{
if (m_gpu_clut4)
delete m_gpu_clut4;
if (m_gpu_clut8)
delete m_gpu_clut8;
delete m_gpu_clut4;
delete m_gpu_clut8;
_aligned_free(m_clut);
}
@ -130,6 +126,16 @@ void GSClut::SetNextCLUTTEX0(u64 TEX0)
m_write.next_tex0 = TEX0;
}
void GSClut::Reset()
{
std::memset(m_CBP, 0, sizeof(m_CBP));
std::memset(m_clut, 0, CLUT_ALLOC_SIZE);
m_write = {};
m_write.dirty = 1;
m_read = {};
m_read.dirty = true;
}
bool GSClut::InvalidateRange(u32 start_block, u32 end_block, bool is_draw)
{
if (m_write.dirty & 2)

View File

@ -13,16 +13,18 @@ class GSTexture;
class alignas(32) GSClut final : public GSAlignedClass<32>
{
static constexpr u32 CLUT_ALLOC_SIZE = 4096 * 2;
static const GSVector4i m_bm;
static const GSVector4i m_gm;
static const GSVector4i m_rm;
GSLocalMemory* m_mem;
u32 m_CBP[2];
u16* m_clut;
u32* m_buff32;
u64* m_buff64;
u32 m_CBP[2] = {};
u16* m_clut = nullptr;
u32* m_buff32 = nullptr;
u64* m_buff64 = nullptr;
struct alignas(32) WriteState
{
@ -31,7 +33,7 @@ class alignas(32) GSClut final : public GSAlignedClass<32>
u8 dirty;
u64 next_tex0;
bool IsDirty(const GIFRegTEX0& TEX0, const GIFRegTEXCLUT& TEXCLUT);
} m_write;
} m_write = {};
struct alignas(32) ReadState
{
@ -42,7 +44,7 @@ class alignas(32) GSClut final : public GSAlignedClass<32>
int amin, amax;
bool IsDirty(const GIFRegTEX0& TEX0);
bool IsDirty(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA);
} m_read;
} m_read = {};
GSTexture* m_gpu_clut4 = nullptr;
GSTexture* m_gpu_clut8 = nullptr;
@ -96,6 +98,7 @@ public:
__fi GSTexture* GetGPUTexture() const { return m_current_gpu_clut; }
void Reset();
bool InvalidateRange(u32 start_block, u32 end_block, bool is_draw = false);
u8 IsInvalid();
void ClearDrawInvalidity();

View File

@ -140,6 +140,7 @@ void GSState::Reset(bool hardware_reset)
memset(&m_v, 0, sizeof(m_v));
m_env.Reset();
m_mem.m_clut.Reset();
PRIM = &m_env.PRIM;
@ -181,6 +182,7 @@ void GSState::Reset(bool hardware_reset)
m_index.tail = 0;
m_scanmask_used = 0;
m_texflush_flag = false;
m_channel_shuffle = false;
m_dirty_gs_regs = 0;
m_backed_up_ctx = -1;
@ -2708,6 +2710,7 @@ int GSState::Defrost(const freezeData* fd)
ReadState(&m_q, data);
m_prev_env = m_env;
PRIM = &m_env.PRIM;
UpdateContext();
@ -2725,6 +2728,10 @@ int GSState::Defrost(const freezeData* fd)
UpdateScissor();
// Force CLUT to be reloaded.
m_mem.m_clut.Reset();
(PRIM->CTXT == 0) ? ApplyTEX0<0>(m_context->TEX0) : ApplyTEX0<1>(m_context->TEX0);
g_perfmon.SetFrame(5000);
ResetPCRTC();