Skip to content

Commit 3fd33f0

Browse files
When VRAM is exhausted, flush gsKit draw commands before evicting an atlas that was used in the current frame to avoid graphics corruption
This also adds a "[THRASHING]" debug text when we tried evicting a texture atlas that was being used in the current frame
1 parent 7028db5 commit 3fd33f0

File tree

3 files changed

+22
-3
lines changed

3 files changed

+22
-3
lines changed

src/ps2/gs_renderer.c

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,7 @@ static uint32_t countFreeChunks(GsRenderer* gs) {
296296

297297
// Find the atlas with the oldest lastUsed time (LRU victim).
298298
// Returns the atlasId, or -1 if no loaded atlases.
299-
static int16_t findLRUVictim(GsRenderer* gs) {
299+
static int16_t findLRUVictim(GsRenderer* gs, bool* wasUsedOnThisFrame) {
300300
uint64_t oldest = UINT64_MAX;
301301
int16_t victimAtlas = -1;
302302
forEach(VRAMChunk, chunk, gs->chunks, gs->chunkCount) {
@@ -305,6 +305,8 @@ static int16_t findLRUVictim(GsRenderer* gs) {
305305
victimAtlas = chunk->atlasId;
306306
}
307307
}
308+
if (victimAtlas != -1)
309+
*wasUsedOnThisFrame = oldest == gs->frameCounter;
308310
return victimAtlas;
309311
}
310312

@@ -351,10 +353,20 @@ static int32_t allocateChunks(GsRenderer* gs, int chunksNeeded) {
351353

352354
// Attempt 2: evict LRU victims one at a time until space is found
353355
repeat(gs->chunkCount, attempts) {
354-
int16_t victim = findLRUVictim(gs);
356+
bool wasUsedOnThisFrame = false;
357+
358+
int16_t victim = findLRUVictim(gs, &wasUsedOnThisFrame);
355359
if (0 > victim)
356360
break;
357361

362+
// We only need to flush if the victim was used on this frame
363+
// If it wasn't, then we can evict with no care in the world
364+
if (wasUsedOnThisFrame) {
365+
fprintf(stderr, "GsRenderer: Flushing draw queue before VRAM evicting because atlas was used on the current frame\n");
366+
gs->evictedAtlasUsedInCurrentFrame = true;
367+
gsKit_queue_exec(gs->gsGlobal);
368+
}
369+
358370
evictAtlas(gs, victim);
359371

360372
idx = findConsecutiveFreeChunks(gs, chunksNeeded);
@@ -363,6 +375,11 @@ static int32_t allocateChunks(GsRenderer* gs, int chunksNeeded) {
363375
return idx;
364376
}
365377

378+
// At this point we are lost, just flush and hope for the best
379+
gs->evictedAtlasUsedInCurrentFrame = true;
380+
fprintf(stderr, "GsRenderer: Flushing draw queue before VRAM defrag\n");
381+
gsKit_queue_exec(gs->gsGlobal);
382+
366383
// Attempt 3: defrag - evict ALL and let them reload on demand
367384
// Handles fragmentation where enough free chunks exist but aren't consecutive
368385
if (countFreeChunks(gs) >= (uint32_t) chunksNeeded) {
@@ -901,6 +918,7 @@ static void gsBeginFrame(Renderer* renderer, MAYBE_UNUSED int32_t gameW, MAYBE_U
901918
GsRenderer* gs = (GsRenderer*) renderer;
902919
gs->zCounter = 1;
903920
gs->frameCounter++;
921+
gs->evictedAtlasUsedInCurrentFrame = false;
904922
}
905923

906924
static void gsEndFrame(MAYBE_UNUSED Renderer* renderer) {

src/ps2/gs_renderer.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ typedef struct {
117117
uint16_t atlasCount; // Number of atlas IDs from ATLAS.BIN header
118118
uint8_t* atlasBpp; // Bits per pixel per atlas (4 or 8), from ATLAS.BIN [atlasCount]
119119
uint64_t frameCounter; // Incremented each frame for LRU tracking
120+
bool evictedAtlasUsedInCurrentFrame; // Used for debugging, true if a atlas that was used on the current frame was evicted (VRAM thrashing)
120121

121122
// EE RAM atlas cache (stores compressed atlas data to avoid repeated CDVD reads)
122123
uint8_t* eeCache; // 4 MiB contiguous buffer

src/ps2/main.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -674,7 +674,7 @@ int main(int argc, char* argv[]) {
674674
if (gsRenderer->eeCacheEntries[ai].atlasId >= 0) eeramAtlasCount++;
675675
}
676676

677-
snprintf(debugText, sizeof(debugText), "Tick: %.2fms\nFree: %d bytes\nVRAM Free: %lu bytes\nRoom Speed: %u%s\nAtlas: (%u, %u, %u)", tickTime, freeBytes, (unsigned long) vramFreeBytes, roomSpeed, speedCapRemoved ? " [UNCAPPED]" : "", vramAtlasCount, eeramAtlasCount, gsRenderer->atlasCount);
677+
snprintf(debugText, sizeof(debugText), "Tick: %.2fms\nFree: %d bytes\nVRAM Free: %lu bytes\nRoom Speed: %u%s\nAtlas: (%u, %u, %u)%s", tickTime, freeBytes, (unsigned long) vramFreeBytes, roomSpeed, speedCapRemoved ? " [UNCAPPED]" : "", vramAtlasCount, eeramAtlasCount, gsRenderer->atlasCount, gsRenderer->evictedAtlasUsedInCurrentFrame ? " [THRASHING]" : "");
678678
gsKit_fontm_print_scaled(gsGlobal, gsFontM, 10.0f, 10.0f, 10, 0.6f, debugColor, debugText);
679679
}
680680

0 commit comments

Comments
 (0)