a proper fix to djui cursor dl corruption (#1018)

An alternative solution to https://github.com/coop-deluxe/sm64coopdx/pull/1014.
Which keeps smooth cursor rendering, while adding safe guards.
This makes djui interactions always update at 30hz instead of 60hz, to avoid more chances of display list corruption.

The cursor gfx is now placed in it's own static buffer to allow it to take up extra room if needed. If the 20 dl command buffer size is too small, we'll hear about it because it'll throw a system error.
I doubt it'll trigger though, in my testing it never needed more than 8.
This commit is contained in:
Isaac0-dev 2025-11-18 08:44:14 +10:00 committed by GitHub
parent 48bfef7d87
commit 8b92817055
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 26 additions and 15 deletions

View file

@ -69,7 +69,6 @@ void djui_shutdown(void) {
void patch_djui_before(void) {
sDjuiRendered60fps = false;
sSavedDisplayListHead = NULL;
djui_cursor_interp_before();
}
void patch_djui_interpolated(UNUSED f32 delta) {
@ -211,6 +210,11 @@ void djui_render(void) {
djui_cursor_update();
djui_base_render(&gDjuiConsole->base);
djui_interactable_update();
// Be careful! Djui interactables update at 30hz to avoid display list corruption.
if (!sDjuiRendered60fps) {
djui_interactable_update();
}
djui_gfx_displaylist_end();
}

View file

@ -4,6 +4,8 @@
#include "pc/gfx/gfx_window_manager_api.h"
#include "pc/pc_main.h"
#define CURSOR_GFX_MAX_SIZE 20
extern ALIGNED8 u8 gd_texture_hand_open[];
extern ALIGNED8 u8 gd_texture_hand_closed[];
@ -17,10 +19,10 @@ static f32 sSavedMouseY = 0;
f32 gCursorX = 0;
f32 gCursorY = 0;
static Gfx sDjuiCursorGfx[CURSOR_GFX_MAX_SIZE] = { 0 };
static f32 sPrevCursorX = 0;
static f32 sPrevCursorY = 0;
static Gfx* sSavedDisplayListHead = NULL;
static bool sInterpCursor = false;
void djui_cursor_set_visible(bool visible) {
if (sMouseCursor) {
@ -161,25 +163,31 @@ static void djui_cursor_update_position(void) {
#endif
}
void djui_cursor_interp_before(void) {
sSavedDisplayListHead = NULL;
sInterpCursor = false;
static void djui_cursor_render_cursor(void) {
gDisplayListHead = sDjuiCursorGfx;
djui_base_render(&sMouseCursor->base);
gSPEndDisplayList(gDisplayListHead++);
if (gDisplayListHead - sDjuiCursorGfx >= CURSOR_GFX_MAX_SIZE) {
sys_fatal("CURSOR_GFX_MAX_SIZE is too small! %lu", gDisplayListHead - sDjuiCursorGfx);
}
}
// This isn't actually interpolation, it just updates the cursor at a faster rate
void djui_cursor_interp(void) {
djui_cursor_update_position();
if (sInterpCursor && (sPrevCursorX != gCursorX || sPrevCursorY != gCursorY)) {
if (sSavedDisplayListHead == NULL) { return; }
gDisplayListHead = sSavedDisplayListHead;
djui_base_render(&sMouseCursor->base);
if (sPrevCursorX != gCursorX || sPrevCursorY != gCursorY) {
djui_cursor_render_cursor();
}
}
void djui_cursor_update(void) {
djui_cursor_update_position();
sSavedDisplayListHead = gDisplayListHead;
djui_base_render(&sMouseCursor->base);
sInterpCursor = gDisplayListHead != sSavedDisplayListHead; // Check that we actually rendered something
Gfx *savedDisplayListHead = gDisplayListHead;
djui_cursor_render_cursor();
gDisplayListHead = savedDisplayListHead;
gSPDisplayList(gDisplayListHead++, sDjuiCursorGfx);
}
void djui_cursor_create(void) {

View file

@ -9,7 +9,6 @@ void djui_cursor_set_visible(bool visible);
bool djui_cursor_inside_base(struct DjuiBase* base);
void djui_cursor_input_controlled_center(struct DjuiBase* base);
void djui_cursor_move(s8 xDir, s8 yDir);
void djui_cursor_interp_before(void);
void djui_cursor_interp(void);
void djui_cursor_update(void);
void djui_cursor_create(void);