GBA: Optimise framebuffer clearing

Measuring in mgBA, time taken reduced from ~52 ms to ~3ms
This commit is contained in:
UnknownShadow200 2025-12-13 12:12:24 +11:00
parent 4bd8d682c9
commit 162bb983ab
6 changed files with 109 additions and 33 deletions

View File

@ -36,15 +36,11 @@ DEPFILES := $(OBJS:%.o=%.d)
# Code generation
#---------------------------------------------------------------------------------
ARCH = -mthumb -mthumb-interwork
CFLAGS = -g -Wall -O2 -DPLAT_GBA -ffunction-sections -fdata-sections -mcpu=arm7tdmi -mtune=arm7tdmi $(ARCH)
CFLAGS = -g -gdwarf-4 -Wall -O2 -DPLAT_GBA -ffunction-sections -fdata-sections -mcpu=arm7tdmi -mtune=arm7tdmi $(ARCH)
ASFLAGS = -g $(ARCH)
LDFLAGS = -specs=gba.specs -g $(ARCH)
LIBGBA := $(DEVKITPRO)/libgba
INCLUDES += $(foreach dir, $(LIBGBA), -I$(dir)/include)
LDFLAGS += $(foreach dir, $(LIBGBA), -L$(dir)/lib)
#---------------------------------------------------------------------------------
# Compiler tools

View File

@ -129,6 +129,11 @@ void Gfx_SetAlphaArgBlend(cc_bool enabled) { }
static void ClearColorBuffer(void) {
int i, x, y, size = fb_width * fb_height;
#ifdef CC_BUILD_GBA
/* in mGBA, fast clear takes ~2ms compared to ~52ms of code below */
extern void VRAM_FastClear(BitmapCol color);
VRAM_FastClear(clearColor);
#else
if (cb_stride == fb_width) {
for (i = 0; i < size; i++) colorBuffer[i] = clearColor;
} else {
@ -140,6 +145,7 @@ static void ClearColorBuffer(void) {
}
}
}
#endif
}
void Gfx_ClearBuffers(GfxBuffers buffers) {

79
src/gba/AsmFuncs.S Normal file
View File

@ -0,0 +1,79 @@
#define BEG_THUMB_FUNC(name_) \
.global name_; \
.thumb_func; \
.align 2; \
name_:
#define BEG_ARM_FUNC(name_) \
.global name_; \
.arm; \
.align 4; \
name_:
#define END_FUNC(name_) \
.size name_, . - name_; \
.type name_, %function;
// ===============================
// FUNCTIONS IN TEXT SECTION
// ===============================
.section text
// ===============================
// FUNCTIONS IN IWRAM SECTION
// ===============================
.section .iwram,"ax",%progbits
#define R_CUR r0 // r0 = beg address (incremented in function)
#define R_END r1 // r1 = end address
#define R_VAL r2 // r2 = value to fill
BEG_ARM_FUNC(fastset_256_bytes)
// Spill callee saved registers + LR onto stack
stmfd sp!, {r4-r9, lr}
// Clone 'value' for 'store multi CPU registers' loop
mov r3, R_VAL
mov r4, R_VAL
mov r5, R_VAL
mov r6, R_VAL
mov r7, R_VAL
mov r8, R_VAL
mov r9, R_VAL
fastset_loop:
cmp R_CUR, R_END
stmltia R_CUR!, {r2-r9}
stmltia R_CUR!, {r2-r9}
stmltia R_CUR!, {r2-r9}
stmltia R_CUR!, {r2-r9}
stmltia R_CUR!, {r2-r9}
stmltia R_CUR!, {r2-r9}
stmltia R_CUR!, {r2-r9}
stmltia R_CUR!, {r2-r9}
blt fastset_loop
// Restore saved CPU registers + set PC to LR
ldmfd sp!, {r4-r9, pc}
END_FUNC(fastset_256_bytes)
// ===============================
// FUNCTIONS IN EWRAM SECTION
// ===============================
.section .ewram,"ax",%progbits
BEG_THUMB_FUNC(nocash_log)
// nocash looks for this specific pattern
mov r12, r12
b nocash_log_return
.short 0x6464, 0x0000
.global nocash_msg // Platform_GBA writes directly to this
nocash_msg:
.space 82
nocash_log_return:
bx lr
END_FUNC(nocash_log)

View File

@ -1,22 +0,0 @@
.global nocash_log
.global nocash_msg
// BEG nocash_log
.section .ewram,"ax",%progbits
.thumb_func
.align 2
// nocash looks for this specific pattern
nocash_log:
mov r12, r12
b nocash_log_return
.short 0x6464, 0x0000
nocash_msg:
.space 82
nocash_log_return:
bx lr
.size nocash_log, .-nocash_log
.type nocash_log, %function
// END nocash_log

View File

@ -13,9 +13,8 @@
#include "../Options.h"
#include "../Animations.h"
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include "gbadefs.h"
#include "../../third_party/tinyalloc/tinyalloc.c"
@ -203,9 +202,10 @@ void DateTime_CurrentLocal(struct cc_datetime* t) {
void CrashHandler_Install(void) {
}
void Process_Abort2(cc_result result, const char* raw_msg) {
Platform_LogConst(raw_msg);
_exit(0);
Process_Exit(0);
}
@ -333,5 +333,10 @@ cc_result Platform_SetDefaultCurrentDirectory(int argc, char **argv) {
return 0;
}
void Process_Exit(cc_result code) { _exit(code); }
extern void bios_soft_reset(void);
void Process_Exit(cc_result code) {
//*(vu8*)0x03007FFA = 0x00; // controls reset address
// TODO jump to start_vector instead?
for (;;) { __asm__ volatile(""); }
}

View File

@ -108,17 +108,20 @@ void Gamepads_Init(void) {
Input.Sources |= INPUT_SOURCE_GAMEPAD;
}
#define PAUSE_MASK (KEY_A|KEY_B|KEY_START|KEY_SELECT)
void Gamepads_Process(float delta) {
int port = Gamepad_Connect(0x5BA, pad_defaults);
int mods = ~REG_KEYINPUT;
// TODO see comment in Platform_GBA.c, doesn't work anyways
//if ((mods & PAUSE_MASK) == PAUSE_MASK)
// Process_Exit(0);
Gamepad_SetButton(port, CCPAD_L, mods & KEY_L);
Gamepad_SetButton(port, CCPAD_R, mods & KEY_R);
Gamepad_SetButton(port, CCPAD_1, mods & KEY_A);
Gamepad_SetButton(port, CCPAD_2, mods & KEY_B);
//Gamepad_SetButton(port, CCPAD_3, mods & KEY_X);
//Gamepad_SetButton(port, CCPAD_4, mods & KEY_Y);
Gamepad_SetButton(port, CCPAD_START, mods & KEY_START);
Gamepad_SetButton(port, CCPAD_SELECT, mods & KEY_SELECT);
@ -133,6 +136,15 @@ void Gamepads_Process(float delta) {
/*########################################################################################################################*
*------------------------------------------------------Framebuffer--------------------------------------------------------*
*#########################################################################################################################*/
extern void fastset_256_bytes(char* beg, char* end, int value);
void VRAM_FastClear(BitmapCol col) {
int value = (col << 16) | col;
char* vram = (char*)MEM_VRAM;
fastset_256_bytes(vram, vram + SCREEN_WIDTH * SCREEN_HEIGHT * 2, value);
}
void Window_AllocFramebuffer(struct Bitmap* bmp, int width, int height) {
bmp->scan0 = (BitmapCol*)MEM_VRAM;
bmp->width = width;