Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/platform-variants.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ if (VARIANT_IS(VARIANT_TG5040_BRICK))
SetRawBrightness(8);

// Use in ternaries
#define PAGE_SCALE (VARIANT_IS(VARIANT_MINI_PLUS_560P) ? 2 : 3)
#define EDGE_PADDING (VARIANT_IS(VARIANT_MINI_PLUS) ? 10 : 5)

// Use in return values
int PLAT_supportsOverscan(void) {
Expand Down
4 changes: 1 addition & 3 deletions workspace/all/common/api.c
Original file line number Diff line number Diff line change
Expand Up @@ -1626,9 +1626,7 @@ void GFX_blitText(TTF_Font* ttf_font, char* str, int leading, SDL_Color color, S

#define MAX_SAMPLE_RATE 48000
#define BATCH_SIZE 100 // Max frames to batch per write
#ifndef SAMPLES
#define SAMPLES 512 // SDL audio buffer size (default)
#endif
// SAMPLES defined in defines.h (default 512)

#define ms SDL_GetTicks // Shorthand for timestamp

Expand Down
64 changes: 39 additions & 25 deletions workspace/all/common/api.h
Original file line number Diff line number Diff line change
Expand Up @@ -180,41 +180,55 @@ extern UI_Layout ui;
void UI_initLayout(int screen_width, int screen_height, float diagonal_inches);

///////////////////////////////
// Video page buffer constants
// Video Buffer Sizing (derived from screen dimensions)
///////////////////////////////

/**
* Double-buffered video system configuration.
* Video buffer sizing system.
*
* MinUI uses overscaled page buffers (FIXED_WIDTH * PAGE_SCALE) to support
* various scaling operations. This allows UI elements to be drawn at high
* resolution before scaling down to the physical screen.
* Buffer dimensions are derived from screen size and scaling constraints,
* not manually configured. This eliminates over-allocation while ensuring
* buffers are always large enough for worst-case scaling scenarios.
*
* When hardware scaling is used (fit=0), minarch renders content larger
* than the screen, then hardware scales it down. The buffer must hold
* this intermediate oversized frame.
*
* The maximum buffer size depends on:
* 1. Integer scale factor: MAX(ceil(screen_w/core_w), ceil(screen_h/core_h))
* 2. Aspect ratio correction: Can expand one dimension by up to 1.5x
*
* Example: GB 160x144 on 640x480 screen
* - Scale 4x -> 640x576 (taller than screen)
* - Aspect correction -> up to ~850x720
* - Hardware scales down to 640x480 for display
*/
#define PAGE_COUNT 2
#ifndef PAGE_SCALE
#define PAGE_SCALE 3 // Default 3x overscaling
#endif
#define PAGE_WIDTH (FIXED_WIDTH * PAGE_SCALE)
#define PAGE_HEIGHT (FIXED_HEIGHT * PAGE_SCALE)
#define PAGE_PITCH (PAGE_WIDTH * FIXED_BPP)
#define PAGE_SIZE (PAGE_PITCH * PAGE_HEIGHT)

///////////////////////////////
/**
* Aspect ratio correction factor.
*
* When core aspect ratio differs from screen aspect ratio, one buffer
* dimension expands. Worst case is 16:9 content on 4:3 screen (or vice versa):
* 16:9 / 4:3 = 1.78 / 1.33 = 1.34x expansion
*
* We use 1.5 for safety margin to handle edge cases.
*/
#define VIDEO_ASPECT_CORRECTION 1.5f

/**
* Platform-specific page buffer configuration.
* Derived video buffer dimensions.
*
* Optionally defined in platform.h for platforms requiring different
* pixel formats (e.g., RGB888 instead of RGB565).
* Buffer is screen size * aspect correction factor, providing enough
* space for any scaling scenario without manual configuration.
*/
// TODO: these only seem to be used by a tmp.pak in trimui (model s)
// used by minarch, optionally defined in platform.h
#ifndef PLAT_PAGE_BPP
#define PLAT_PAGE_BPP FIXED_BPP
#endif
#define PLAT_PAGE_DEPTH (PLAT_PAGE_BPP * 8)
#define PLAT_PAGE_PITCH (PAGE_WIDTH * PLAT_PAGE_BPP)
#define PLAT_PAGE_SIZE (PLAT_PAGE_PITCH * PAGE_HEIGHT)
#define VIDEO_BUFFER_WIDTH ((int)(FIXED_WIDTH * VIDEO_ASPECT_CORRECTION + 0.5f))
#define VIDEO_BUFFER_HEIGHT ((int)(FIXED_HEIGHT * VIDEO_ASPECT_CORRECTION + 0.5f))
#define VIDEO_BUFFER_PITCH (VIDEO_BUFFER_WIDTH * FIXED_BPP)
#define VIDEO_BUFFER_SIZE (VIDEO_BUFFER_PITCH * VIDEO_BUFFER_HEIGHT)

/** Double-buffering for tear-free rendering */
#define VIDEO_BUFFER_COUNT 2
#define VIDEO_BUFFER_TOTAL (VIDEO_BUFFER_SIZE * VIDEO_BUFFER_COUNT)

///////////////////////////////
// SDL pixel format masks
Expand Down
20 changes: 19 additions & 1 deletion workspace/all/minarch/minarch.c
Original file line number Diff line number Diff line change
Expand Up @@ -4554,7 +4554,25 @@ static void selectScaler(int src_w, int src_h, int src_p) {
}
}

// TODO: need to sanity check scale and demands on the buffer
// Bounds check: ensure scaled output fits in video buffer
// VIDEO_BUFFER_WIDTH/HEIGHT are derived from screen size * 1.5 aspect correction
if (dst_w > VIDEO_BUFFER_WIDTH || dst_h > VIDEO_BUFFER_HEIGHT) {
LOG_warn("Scaler output %dx%d exceeds buffer %dx%d, capping", dst_w, dst_h,
VIDEO_BUFFER_WIDTH, VIDEO_BUFFER_HEIGHT);

// Calculate scale factor to fit within buffer
float cap_w = (float)VIDEO_BUFFER_WIDTH / dst_w;
float cap_h = (float)VIDEO_BUFFER_HEIGHT / dst_h;
float cap = (cap_w < cap_h) ? cap_w : cap_h;

dst_w = (int)(dst_w * cap);
dst_h = (int)(dst_h * cap);
dst_p = dst_w * FIXED_BPP;

// Adjust dst offsets proportionally
dst_x = (int)(dst_x * cap);
dst_y = (int)(dst_y * cap);
}

// LOG_info("aspect: %ix%i (%f)", aspect_w,aspect_h,core.aspect_ratio);

Expand Down
1 change: 0 additions & 1 deletion workspace/magicmini/platform/platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,6 @@
#define SDCARD_PATH "/storage/TF2" // Path to secondary SD card slot
#define MUTE_VOLUME_RAW 0 // Raw value for muted volume
#define HAS_NEON // ARM NEON SIMD optimizations available
#define SAMPLES 400 // Audio buffer size (helps reduce fceumm audio underruns)

///////////////////////////////
// Keymon Configuration
Expand Down
3 changes: 1 addition & 2 deletions workspace/miyoomini/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -220,9 +220,8 @@ The 560p variant uses different UI parameters:
|-----------|-------------------|----------------|
| `MAIN_ROW_COUNT` | 6 rows | 8 rows |
| `PADDING` | 10px | 5px |
| `PAGE_SCALE` | 3 | 2 (memory optimization) |

These adjustments maximize visible content on the taller display while managing memory usage.
These adjustments maximize visible content on the taller display.

## Included Tools

Expand Down
24 changes: 13 additions & 11 deletions workspace/miyoomini/platform/platform.c
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,7 @@ SDL_Surface* PLAT_initVideo(void) {

vid.video = SDL_SetVideoMode(FIXED_WIDTH, FIXED_HEIGHT, FIXED_DEPTH, SDL_SWSURFACE);

int buffer_size = ALIGN4K(PAGE_SIZE) * PAGE_COUNT;
int buffer_size = ALIGN4K(VIDEO_BUFFER_SIZE) * VIDEO_BUFFER_COUNT;
MI_SYS_MMA_Alloc(NULL, ALIGN4K(buffer_size), &vid.buffer.padd);
MI_SYS_Mmap(vid.buffer.padd, ALIGN4K(buffer_size), &vid.buffer.vadd, true);

Expand All @@ -445,9 +445,9 @@ SDL_Surface* PLAT_initVideo(void) {
vid.cleared = 0;

vid.screen =
SDL_CreateRGBSurfaceFrom(vid.buffer.vadd + ALIGN4K(vid.page * PAGE_SIZE), vid.width,
SDL_CreateRGBSurfaceFrom(vid.buffer.vadd + ALIGN4K(vid.page * VIDEO_BUFFER_SIZE), vid.width,
vid.height, FIXED_DEPTH, vid.pitch, RGBA_MASK_AUTO);
vid.screen->pixelsPa = vid.buffer.padd + ALIGN4K(vid.page * PAGE_SIZE);
vid.screen->pixelsPa = vid.buffer.padd + ALIGN4K(vid.page * VIDEO_BUFFER_SIZE);
memset(vid.screen->pixels, 0, vid.pitch * vid.height);

// Initialize effect state using shared effect_system
Expand All @@ -472,15 +472,17 @@ void PLAT_quitVideo(void) {

SDL_FreeSurface(vid.screen);

MI_SYS_Munmap(vid.buffer.vadd, ALIGN4K(PAGE_SIZE));
int buffer_size = ALIGN4K(VIDEO_BUFFER_SIZE) * VIDEO_BUFFER_COUNT;
MI_SYS_Munmap(vid.buffer.vadd, ALIGN4K(buffer_size));
MI_SYS_MMA_Free(vid.buffer.padd);

SDL_Quit();
}

void PLAT_clearVideo(SDL_Surface* screen) {
MI_SYS_FlushInvCache(vid.buffer.vadd + ALIGN4K(vid.page * PAGE_SIZE), ALIGN4K(PAGE_SIZE));
MI_SYS_MemsetPa(vid.buffer.padd + ALIGN4K(vid.page * PAGE_SIZE), 0, PAGE_SIZE);
MI_SYS_FlushInvCache(vid.buffer.vadd + ALIGN4K(vid.page * VIDEO_BUFFER_SIZE),
ALIGN4K(VIDEO_BUFFER_SIZE));
MI_SYS_MemsetPa(vid.buffer.padd + ALIGN4K(vid.page * VIDEO_BUFFER_SIZE), 0, VIDEO_BUFFER_SIZE);
SDL_FillRect(screen, NULL, 0);
}

Expand All @@ -503,9 +505,9 @@ SDL_Surface* PLAT_resizeVideo(int w, int h, int pitch) {
SDL_FreeSurface(vid.screen);

vid.screen =
SDL_CreateRGBSurfaceFrom(vid.buffer.vadd + ALIGN4K(vid.page * PAGE_SIZE), vid.width,
vid.height, FIXED_DEPTH, vid.pitch, RGBA_MASK_AUTO);
vid.screen->pixelsPa = vid.buffer.padd + ALIGN4K(vid.page * PAGE_SIZE);
SDL_CreateRGBSurfaceFrom(vid.buffer.vadd + ALIGN4K(vid.page * VIDEO_BUFFER_SIZE),
vid.width, vid.height, FIXED_DEPTH, vid.pitch, RGBA_MASK_AUTO);
vid.screen->pixelsPa = vid.buffer.padd + ALIGN4K(vid.page * VIDEO_BUFFER_SIZE);
memset(vid.screen->pixels, 0, vid.pitch * vid.height);
}

Expand Down Expand Up @@ -690,8 +692,8 @@ void PLAT_flip(SDL_Surface* IGNORED, int sync) {

if (!vid.direct) {
vid.page ^= 1;
vid.screen->pixels = vid.buffer.vadd + ALIGN4K(vid.page * PAGE_SIZE);
vid.screen->pixelsPa = vid.buffer.padd + ALIGN4K(vid.page * PAGE_SIZE);
vid.screen->pixels = vid.buffer.vadd + ALIGN4K(vid.page * VIDEO_BUFFER_SIZE);
vid.screen->pixelsPa = vid.buffer.padd + ALIGN4K(vid.page * VIDEO_BUFFER_SIZE);
}

if (vid.cleared) {
Expand Down
3 changes: 0 additions & 3 deletions workspace/miyoomini/platform/platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,6 @@
#define FIXED_WIDTH (platform_variant.screen_width)
#define FIXED_HEIGHT (platform_variant.screen_height)

// Page buffer overscaling (560p uses less to save memory)
#define PAGE_SCALE (VARIANT_IS(VARIANT_MINI_PLUS_560P) ? 2 : 3)

///////////////////////////////
// Platform-Specific Paths and Settings
///////////////////////////////
Expand Down
1 change: 0 additions & 1 deletion workspace/my355/platform/platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,6 @@ extern int on_hdmi; // Set to 1 when HDMI output is active
#define SDCARD_PATH "/mnt/SDCARD" // Path to SD card mount point
#define MUTE_VOLUME_RAW 0 // Raw value for muted volume
// #define HAS_NEON // NEON support commented out (not available on this SoC)
#define SAMPLES 400 // Audio buffer size (helps reduce fceumm audio underruns)

///////////////////////////////

Expand Down
23 changes: 12 additions & 11 deletions workspace/rg35xx/platform/platform.c
Original file line number Diff line number Diff line change
Expand Up @@ -355,11 +355,11 @@ SDL_Surface* PLAT_initVideo(void) {
vid.height = FIXED_HEIGHT;
vid.pitch = FIXED_PITCH;

vid.fb_info.size = PAGE_SIZE * PAGE_COUNT;
vid.fb_info.size = VIDEO_BUFFER_SIZE * VIDEO_BUFFER_COUNT;
ion_alloc(vid.fd_ion, &vid.fb_info);

vid.screen = SDL_CreateRGBSurfaceFrom(vid.fb_info.vadd + PAGE_SIZE, vid.width, vid.height,
FIXED_DEPTH, vid.pitch, RGBA_MASK_AUTO);
vid.screen = SDL_CreateRGBSurfaceFrom(vid.fb_info.vadd + VIDEO_BUFFER_SIZE, vid.width,
vid.height, FIXED_DEPTH, vid.pitch, RGBA_MASK_AUTO);
memset(vid.screen->pixels, 0, vid.pitch * vid.height);

int vw = (vid.de_mem[DE_PATH_SIZE(0) / 4] & 0xFFFF) + 1;
Expand All @@ -370,7 +370,7 @@ SDL_Surface* PLAT_initVideo(void) {
vid.de_mem[DE_OVL_SR(0) / 4] = vid.de_mem[DE_OVL_SR(2) / 4] =
((0x2000 * vid.width / vw) & 0xFFFF) | ((0x2000 * vid.height / vh) << 16);
vid.de_mem[DE_OVL_STR(0) / 4] = vid.de_mem[DE_OVL_STR(2) / 4] = vid.pitch / 8;
vid.de_mem[DE_OVL_BA0(0) / 4] = (uintptr_t)(vid.fb_info.padd + PAGE_SIZE);
vid.de_mem[DE_OVL_BA0(0) / 4] = (uintptr_t)(vid.fb_info.padd + VIDEO_BUFFER_SIZE);

GFX_setNearestNeighbor(0);

Expand All @@ -391,7 +391,7 @@ void PLAT_quitVideo(void) {
}

void PLAT_clearVideo(SDL_Surface* screen) {
memset(screen->pixels, 0, PAGE_SIZE);
memset(screen->pixels, 0, VIDEO_BUFFER_SIZE);
}

void PLAT_clearAll(void) {
Expand All @@ -405,8 +405,9 @@ SDL_Surface* PLAT_resizeVideo(int w, int h, int pitch) {
vid.pitch = pitch;

SDL_FreeSurface(vid.screen);
vid.screen = SDL_CreateRGBSurfaceFrom(vid.fb_info.vadd + vid.page * PAGE_SIZE, vid.width,
vid.height, FIXED_DEPTH, vid.pitch, RGBA_MASK_AUTO);
vid.screen =
SDL_CreateRGBSurfaceFrom(vid.fb_info.vadd + vid.page * VIDEO_BUFFER_SIZE, vid.width,
vid.height, FIXED_DEPTH, vid.pitch, RGBA_MASK_AUTO);
memset(vid.screen->pixels, 0, vid.pitch * vid.height);

int vw = (vid.de_mem[DE_PATH_SIZE(0) / 4] & 0xFFFF) + 1;
Expand All @@ -417,7 +418,7 @@ SDL_Surface* PLAT_resizeVideo(int w, int h, int pitch) {
vid.de_mem[DE_OVL_SR(0) / 4] = vid.de_mem[DE_OVL_SR(2) / 4] =
((0x2000 * vid.width / vw) & 0xFFFF) | ((0x2000 * vid.height / vh) << 16);
vid.de_mem[DE_OVL_STR(0) / 4] = vid.de_mem[DE_OVL_STR(2) / 4] = vid.pitch / 8;
vid.de_mem[DE_OVL_BA0(0) / 4] = (uintptr_t)(vid.fb_info.padd + vid.page * PAGE_SIZE);
vid.de_mem[DE_OVL_BA0(0) / 4] = (uintptr_t)(vid.fb_info.padd + vid.page * VIDEO_BUFFER_SIZE);

return vid.screen;
}
Expand Down Expand Up @@ -577,14 +578,14 @@ void PLAT_flip(SDL_Surface* IGNORED, int sync) {
}

vid.de_mem[DE_OVL_BA0(0) / 4] = vid.de_mem[DE_OVL_BA0(2) / 4] =
(uintptr_t)(vid.fb_info.padd + vid.page * PAGE_SIZE);
(uintptr_t)(vid.fb_info.padd + vid.page * VIDEO_BUFFER_SIZE);
DE_enableLayer(vid.de_mem);

if (sync)
PLAT_vsync(0);

vid.page ^= 1;
vid.screen->pixels = vid.fb_info.vadd + vid.page * PAGE_SIZE;
vid.screen->pixels = vid.fb_info.vadd + vid.page * VIDEO_BUFFER_SIZE;

if (vid.cleared) {
PLAT_clearVideo(vid.screen);
Expand Down Expand Up @@ -636,7 +637,7 @@ SDL_Surface* PLAT_initOverlay(void) {

ovl.oinfo.mem_off = (uintptr_t)ovl.ov_info.padd - vid.finfo.smem_start;
ovl.oinfo.mem_size = size;
ovl.oinfo.screen_width = PAGE_WIDTH;
ovl.oinfo.screen_width = VIDEO_BUFFER_WIDTH;
ovl.oinfo.color_mode = OWL_DSS_COLOR_ARGB32;
ovl.oinfo.img_width = w;
ovl.oinfo.img_height = h;
Expand Down
1 change: 0 additions & 1 deletion workspace/rg35xxplus/platform/platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,6 @@
#define SDCARD_PATH "/mnt/sdcard" // Path to SD card mount point (lowercase)
#define MUTE_VOLUME_RAW 0 // Raw value for muted volume
#define HAS_NEON // ARM NEON SIMD optimizations available
#define SAMPLES 400 // Audio buffer size (helps reduce fceumm audio underruns)

///////////////////////////////
// Keymon Configuration
Expand Down
1 change: 0 additions & 1 deletion workspace/rgb30/platform/platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,6 @@

#define SDCARD_PATH "/storage/roms" // Path to SD card mount point
#define MUTE_VOLUME_RAW 0 // Raw value for muted volume
#define SAMPLES 400 // Audio buffer size

///////////////////////////////
// Keymon Configuration
Expand Down
20 changes: 11 additions & 9 deletions workspace/trimuismart/platform/platform.c
Original file line number Diff line number Diff line change
Expand Up @@ -223,17 +223,17 @@ SDL_Surface* PLAT_initVideo(void) {
vid.height = FIXED_HEIGHT;
vid.pitch = FIXED_PITCH;

vid.screen_info.size = PAGE_SIZE;
vid.screen_info.size = VIDEO_BUFFER_SIZE;
ion_alloc(vid.ion_fd, &vid.screen_info);
vid.screen = SDL_CreateRGBSurfaceFrom(vid.screen_info.vadd, vid.width, vid.height, FIXED_DEPTH,
vid.pitch, RGBA_MASK_565);

vid.buffer_info.size = PAGE_SIZE * PAGE_COUNT;
vid.buffer_info.size = VIDEO_BUFFER_SIZE * VIDEO_BUFFER_COUNT;
ion_alloc(vid.ion_fd, &vid.buffer_info);

vid.buffer =
SDL_CreateRGBSurfaceFrom(vid.buffer_info.vadd + vid.page * PAGE_SIZE, PAGE_HEIGHT,
PAGE_WIDTH, FIXED_DEPTH, PAGE_HEIGHT * FIXED_BPP, RGBA_MASK_565);
vid.buffer = SDL_CreateRGBSurfaceFrom(vid.buffer_info.vadd + vid.page * VIDEO_BUFFER_SIZE,
VIDEO_BUFFER_HEIGHT, VIDEO_BUFFER_WIDTH, FIXED_DEPTH,
VIDEO_BUFFER_HEIGHT * FIXED_BPP, RGBA_MASK_565);

vid.buffer_config.channel = SCALER_CH;
vid.buffer_config.layer_id = SCALER_LAYER;
Expand Down Expand Up @@ -309,7 +309,7 @@ void PLAT_quitVideo(void) {
void PLAT_clearVideo(SDL_Surface* IGNORED) {
if (!vid.cleared)
memset(vid.screen->pixels, 0, vid.pitch * vid.height);
memset(vid.buffer->pixels, 0, PAGE_SIZE);
memset(vid.buffer->pixels, 0, VIDEO_BUFFER_SIZE);
}

void PLAT_clearAll(void) {
Expand Down Expand Up @@ -458,8 +458,10 @@ void PLAT_flip(SDL_Surface* IGNORED, int sync) {
rotate_16bpp(vid.screen->pixels, vid.buffer->pixels, vid.width, vid.height, vid.pitch,
vid.height * FIXED_BPP);

vid.buffer_config.info.fb.addr[0] = (uintptr_t)vid.buffer_info.padd + vid.page * PAGE_SIZE;
vid.mem_map[OVL_V_TOP_LADD0 / 4] = (uintptr_t)vid.buffer_info.padd + vid.page * PAGE_SIZE;
vid.buffer_config.info.fb.addr[0] =
(uintptr_t)vid.buffer_info.padd + vid.page * VIDEO_BUFFER_SIZE;
vid.mem_map[OVL_V_TOP_LADD0 / 4] =
(uintptr_t)vid.buffer_info.padd + vid.page * VIDEO_BUFFER_SIZE;

if (vid.resized) {
vid.buffer_config.info.fb.size[0].width = vid.height;
Expand All @@ -472,7 +474,7 @@ void PLAT_flip(SDL_Surface* IGNORED, int sync) {
}

vid.page ^= 1;
vid.buffer->pixels = vid.buffer_info.vadd + vid.page * PAGE_SIZE;
vid.buffer->pixels = vid.buffer_info.vadd + vid.page * VIDEO_BUFFER_SIZE;

if (sync)
PLAT_vsync(0);
Expand Down
1 change: 0 additions & 1 deletion workspace/zero28/platform/platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,6 @@

#define SDCARD_PATH "/mnt/SDCARD" // Path to SD card mount point
#define MUTE_VOLUME_RAW 63 // Volume scale is inverted: 63 = mute, 0 = max volume
#define SAMPLES 400 // Audio buffer size (helps reduce fceumm audio underruns)

///////////////////////////////
// Keymon Configuration
Expand Down