From 52d1e7414dffec8660dd10880b37d679287afaf9 Mon Sep 17 00:00:00 2001 From: Dominic Masters Date: Thu, 20 Aug 2026 12:43:43 -0500 Subject: [PATCH] Convert debug UI overlays to uilabel uiconsole, uifps, and uiplayerpos now render through uilabel instead of calling textDraw per character every frame. Each owns its label(s) plus backing text/sprite buffers and marks them dirty only when their content or position actually changes. Also drops console.h's per-line buffer from 512 to 128 chars, since the sprite cache backing each console line label scales with it. Co-Authored-By: Claude Sonnet 5 --- src/dusk/console/console.h | 2 +- src/dusk/ui/debug/uiconsole.c | 29 +++++++++++----- src/dusk/ui/debug/uiconsole.h | 16 +++++++++ src/dusk/ui/debug/uifps.c | 60 ++++++++++++++++++++------------- src/dusk/ui/debug/uifps.h | 23 ++++++++++++- src/dusk/ui/debug/uiplayerpos.c | 31 ++++++++++++----- src/dusk/ui/debug/uiplayerpos.h | 19 +++++++++++ 7 files changed, 137 insertions(+), 43 deletions(-) diff --git a/src/dusk/console/console.h b/src/dusk/console/console.h index d1f90a7d..8f0a8fc6 100644 --- a/src/dusk/console/console.h +++ b/src/dusk/console/console.h @@ -10,7 +10,7 @@ #include "dusk.h" #include "thread/thread.h" -#define CONSOLE_LINE_MAX 512 +#define CONSOLE_LINE_MAX 128 #define CONSOLE_HISTORY_MAX 16 #define CONSOLE_EXEC_BUFFER_MAX 32 diff --git a/src/dusk/ui/debug/uiconsole.c b/src/dusk/ui/debug/uiconsole.c index 549cf000..2bb69b11 100644 --- a/src/dusk/ui/debug/uiconsole.c +++ b/src/dusk/ui/debug/uiconsole.c @@ -6,23 +6,34 @@ */ #include "uiconsole.h" -#include "console/console.h" #include "display/screen/screen.h" #include "display/text/text.h" #include "display/spritebatch/spritebatch.h" +uiconsole_t UICONSOLE; + +errorret_t uiConsoleInit(void) { + float_t lineH = (float_t)FONT_DEFAULT.tileset->tileHeight; + + for(uint32_t i = 0; i < CONSOLE_HISTORY_MAX; i++) { + uiLabelInit( + &UICONSOLE.labels[i], + CONSOLE.line[i], + UICONSOLE.sprites[i], CONSOLE_LINE_MAX + ); + uiLabelSetX(&UICONSOLE.labels[i], (float_t)SCREEN.scanX); + uiLabelSetY(&UICONSOLE.labels[i], (float_t)SCREEN.scanY + lineH * (float_t)i); + } + + errorOk(); +} + errorret_t uiConsoleDraw(void) { if(!CONSOLE.visible) errorOk(); - float_t lineH = (float_t)FONT_DEFAULT.tileset->tileHeight; for(uint32_t i = 0; i < CONSOLE_HISTORY_MAX; i++) { - errorChain(textDraw( - (float_t)SCREEN.scanX, - (float_t)SCREEN.scanY + lineH * (float_t)i, - CONSOLE.line[i], - COLOR_RED, - &FONT_DEFAULT - )); + UICONSOLE.labels[i].dirty = true; + errorChain(uiLabelRender(&UICONSOLE.labels[i], COLOR_RED)); } return spriteBatchFlush(); } diff --git a/src/dusk/ui/debug/uiconsole.h b/src/dusk/ui/debug/uiconsole.h index 2a227f74..be745ab3 100644 --- a/src/dusk/ui/debug/uiconsole.h +++ b/src/dusk/ui/debug/uiconsole.h @@ -7,6 +7,22 @@ #pragma once #include "error/error.h" +#include "console/console.h" +#include "ui/widget/uilabel.h" + +typedef struct { + uilabel_t labels[CONSOLE_HISTORY_MAX]; + spritebatchsprite_t sprites[CONSOLE_HISTORY_MAX][CONSOLE_LINE_MAX]; +} uiconsole_t; + +extern uiconsole_t UICONSOLE; + +/** + * Initializes the console's history labels. + * + * @return Any error that occurs. + */ +errorret_t uiConsoleInit(void); /** * Renders the console history into the scan-safe area. diff --git a/src/dusk/ui/debug/uifps.c b/src/dusk/ui/debug/uifps.c index 901e3b09..d5df8ac5 100644 --- a/src/dusk/ui/debug/uifps.c +++ b/src/dusk/ui/debug/uifps.c @@ -1,22 +1,48 @@ /** * Copyright (c) 2026 Dominic Masters - * + * * This software is released under the MIT License. * https://opensource.org/licenses/MIT */ #include "uifps.h" #include "time/time.h" +#include "util/string.h" #include "display/spritebatch/spritebatch.h" -#include "display/text/text.h" +#include "display/color.h" #include "display/screen/screen.h" #include "engine/engine.h" uifps_t UIFPS; -errorret_t uiFPSDraw() { - char_t fpsText[32]; +errorret_t uiFPSInit() { + uiLabelInit( + &UIFPS.fpsLabel, + UIFPS.fpsText, + UIFPS.fpsSprites, UI_FPS_SPRITES_MAX + ); + uiLabelSetX(&UIFPS.fpsLabel, (float_t)SCREEN.scanX); + uiLabelSetY(&UIFPS.fpsLabel, (float_t)SCREEN.scanY); + uiLabelInit( + &UIFPS.versionLabel, + UIFPS.versionText, + UIFPS.versionSprites, UI_FPS_VERSION_SPRITES_MAX + ); + stringCopy(UIFPS.versionText, ENGINE.version, UI_FPS_VERSION_TEXT_MAX - 1); + UIFPS.versionLabel.dirty = true; + uiLabelRebuffer(&UIFPS.versionLabel); + uiLabelSetX(&UIFPS.versionLabel, (float_t)( + SCREEN.scanX + SCREEN.scanWidth - UIFPS.versionLabel.width + )); + uiLabelSetY(&UIFPS.versionLabel, (float_t)( + SCREEN.scanY + SCREEN.scanHeight - UIFPS.versionLabel.height + )); + + errorOk(); +} + +errorret_t uiFPSDraw() { // Get now. dusktimeepoch_t now = timeGetEpoch(); double_t delta = now.time - UIFPS.lastTick.time; @@ -33,13 +59,14 @@ errorret_t uiFPSDraw() { UIFPS.fpsAverage = alpha * fps + (1.0f - alpha) * UIFPS.fpsAverage; } - snprintf( - fpsText, - sizeof(fpsText), + stringFormat( + UIFPS.fpsText, + UI_FPS_TEXT_MAX - 1, "%.1f/%.1fms", UIFPS.fpsAverage, delta * 1000.0f ); + UIFPS.fpsLabel.dirty = true; color_t textColor; if(fps >= 55.0f) { @@ -50,23 +77,10 @@ errorret_t uiFPSDraw() { textColor = COLOR_RED; } - errorChain(textDraw( - (float_t)SCREEN.scanX, - (float_t)SCREEN.scanY, - fpsText, textColor, - &FONT_DEFAULT - )); + errorChain(uiLabelRender(&UIFPS.fpsLabel, textColor)); errorChain(spriteBatchFlush()); - int32_t versionWidth, versionHeight; - textMeasure(ENGINE.version, &FONT_DEFAULT, &versionWidth, &versionHeight); - errorChain(textDraw( - (float_t)(SCREEN.scanX + SCREEN.scanWidth - versionWidth), - (float_t)(SCREEN.scanY + SCREEN.scanHeight - versionHeight), - ENGINE.version, - color(255, 255, 255, 128), - &FONT_DEFAULT - )); + errorChain(uiLabelRender(&UIFPS.versionLabel, color(255, 255, 255, 128))); return spriteBatchFlush(); -} \ No newline at end of file +} diff --git a/src/dusk/ui/debug/uifps.h b/src/dusk/ui/debug/uifps.h index 6f9c4592..fda1345b 100644 --- a/src/dusk/ui/debug/uifps.h +++ b/src/dusk/ui/debug/uifps.h @@ -8,17 +8,38 @@ #pragma once #include "error/error.h" #include "time/timeepoch.h" +#include "ui/widget/uilabel.h" + +#define UI_FPS_TEXT_MAX 32 +#define UI_FPS_SPRITES_MAX UI_FPS_TEXT_MAX +#define UI_FPS_VERSION_TEXT_MAX 32 +#define UI_FPS_VERSION_SPRITES_MAX UI_FPS_VERSION_TEXT_MAX typedef struct { dusktimeepoch_t lastTick; float_t fpsAverage; + + uilabel_t fpsLabel; + char_t fpsText[UI_FPS_TEXT_MAX]; + spritebatchsprite_t fpsSprites[UI_FPS_SPRITES_MAX]; + + uilabel_t versionLabel; + char_t versionText[UI_FPS_VERSION_TEXT_MAX]; + spritebatchsprite_t versionSprites[UI_FPS_VERSION_SPRITES_MAX]; } uifps_t; extern uifps_t UIFPS; +/** + * Initializes the FPS counter's labels. + * + * @return Any error that occurs. + */ +errorret_t uiFPSInit(); + /** * Draws the FPS counter on the screen, and also does the update (for now). - * + * * @return Any error that occurs. */ errorret_t uiFPSDraw(); \ No newline at end of file diff --git a/src/dusk/ui/debug/uiplayerpos.c b/src/dusk/ui/debug/uiplayerpos.c index 29cc8a75..8e7e2e65 100644 --- a/src/dusk/ui/debug/uiplayerpos.c +++ b/src/dusk/ui/debug/uiplayerpos.c @@ -6,6 +6,7 @@ */ #include "uiplayerpos.h" +#include "util/string.h" #include "display/screen/screen.h" #include "display/text/text.h" #include "display/spritebatch/spritebatch.h" @@ -13,6 +14,22 @@ #include "rpg/entity/entitytype.h" #include "rpg/overworld/worldpos.h" +uiplayerpos_t UIPLAYERPOS; + +errorret_t uiPlayerPosInit() { + uiLabelInit( + &UIPLAYERPOS.label, + UIPLAYERPOS.text, + UIPLAYERPOS.sprites, UI_PLAYERPOS_SPRITES_MAX + ); + uiLabelSetX(&UIPLAYERPOS.label, (float_t)SCREEN.scanX); + uiLabelSetY(&UIPLAYERPOS.label, (float_t)SCREEN.scanY + + (float_t)FONT_DEFAULT.tileset->tileHeight + ); + + errorOk(); +} + errorret_t uiPlayerPosDraw() { entity_t *player = NULL; for(uint8_t i = 0; i < ENTITY_COUNT; i++) { @@ -26,10 +43,9 @@ errorret_t uiPlayerPosDraw() { chunkpos_t chunkPos; worldPosToChunkPos(&player->position, &chunkPos); - char_t text[64]; - snprintf( - text, - sizeof(text), + stringFormat( + UIPLAYERPOS.text, + UI_PLAYERPOS_TEXT_MAX - 1, "%d,%d,%d[%d,%d,%d]", (int_t)player->position.x, (int_t)player->position.y, @@ -38,11 +54,8 @@ errorret_t uiPlayerPosDraw() { (int_t)chunkPos.y, (int_t)chunkPos.z ); + UIPLAYERPOS.label.dirty = true; - float_t y = (float_t)SCREEN.scanY + - (float_t)FONT_DEFAULT.tileset->tileHeight; - errorChain(textDraw( - (float_t)SCREEN.scanX, y, text, COLOR_GREEN, &FONT_DEFAULT - )); + errorChain(uiLabelRender(&UIPLAYERPOS.label, COLOR_GREEN)); return spriteBatchFlush(); } diff --git a/src/dusk/ui/debug/uiplayerpos.h b/src/dusk/ui/debug/uiplayerpos.h index 88cde124..0c53a289 100644 --- a/src/dusk/ui/debug/uiplayerpos.h +++ b/src/dusk/ui/debug/uiplayerpos.h @@ -7,6 +7,25 @@ #pragma once #include "error/error.h" +#include "ui/widget/uilabel.h" + +#define UI_PLAYERPOS_TEXT_MAX 64 +#define UI_PLAYERPOS_SPRITES_MAX UI_PLAYERPOS_TEXT_MAX + +typedef struct { + uilabel_t label; + char_t text[UI_PLAYERPOS_TEXT_MAX]; + spritebatchsprite_t sprites[UI_PLAYERPOS_SPRITES_MAX]; +} uiplayerpos_t; + +extern uiplayerpos_t UIPLAYERPOS; + +/** + * Initializes the player position label. + * + * @return Any error that occurs. + */ +errorret_t uiPlayerPosInit(); /** * Draws the player world and chunk position on screen.