patch FMV playback, fix artifact flicker on cutover to new video

This commit is contained in:
Alex Vanderveen
2026-07-23 01:22:11 -04:00
parent 1a73e9826c
commit 1347e49dfc
3 changed files with 59 additions and 37 deletions
+4
View File
@@ -44,6 +44,10 @@ uint32_t gpu_display_pixel_argb(const GpuDisplayInfo* di, uint32_t x, uint32_t y
* trailing margin without shrinking the CRTC-derived width globally. */
uint32_t gpu_depth24_rgb_limit(uint32_t display_x, uint32_t crtc_w);
void gpu_depth24_upload_span_reset(void);
/* MotK intro cuts retarget GP1(07h). While hold > 0, present should skip
* Swap (keep prior frame) so stale trailing VRAM never flashes. One tick
* per vblank; returns non-zero while the hold is still active after tick. */
int gpu_depth24_present_hold_tick(void);
/* GP1(06h)/GP1(07h)/GP1(08h) fields for debug (gpu_state). */
void gpu_get_crtc_debug(uint32_t *x1, uint32_t *x2, uint32_t *y1, uint32_t *y2,
uint32_t *hres1_out, uint32_t *hres2_out);
+28 -4
View File
@@ -1655,6 +1655,8 @@ static uint16_t vram_write_pixels[1024 * 512];
/* Depth24 CPU→VRAM upload span (halfwords, exclusive end). See
* gpu_depth24_rgb_limit — declared early so gpu_reset_state can clear it. */
static uint32_t s_d24_upload_x1 = 0;
static int s_d24_present_hold = 0; /* vblanks to skip Swap after GP1(07h) */
static uint32_t s_d24_prev_disp_h = 0; /* last GP1(07h) band height */
static void depth24_note_upload(uint32_t x, uint32_t w);
static void gp0_commit_cpu_to_vram(void) {
@@ -1892,6 +1894,8 @@ static void gpu_reset_state(int clear_vram) {
s_ws_fmv_frame_cache = 0xFFFFFFFFu;
s_ws_fmv_cached = 0;
s_d24_upload_x1 = 0;
s_d24_present_hold = 0;
s_d24_prev_disp_h = 0;
}
void gpu_init(void) {
@@ -2070,9 +2074,12 @@ static uint8_t gpu_vram_byte(uint32_t byte_x, uint32_t y) {
/* Depth24: note/query/reset the CPU→VRAM upload span tracked above. Used to
* hide trailing RGB columns when a movie blit doesn't fill the full CRTC
* width — MotK's Star Wars crawl leaves ~8px of stale VRAM on the right. */
* width — MotK's Star Wars crawl leaves ~8px of stale VRAM on the right.
* Only FB-class A0s (w >= 256 halfwords) grow the span; texture uploads must
* not collapse it. During present-hold, ignore updates entirely. */
static void depth24_note_upload(uint32_t x, uint32_t w) {
if (!(display_depth & 1u) || w == 0u) return;
if (!(display_depth & 1u) || w < 256u) return;
if (s_d24_present_hold > 0) return;
uint32_t x1 = x + w;
if (x1 > 1024u) x1 = 1024u;
if (x1 > s_d24_upload_x1) s_d24_upload_x1 = x1;
@@ -2093,6 +2100,12 @@ void gpu_depth24_upload_span_reset(void) {
s_d24_upload_x1 = 0;
}
int gpu_depth24_present_hold_tick(void) {
if (s_d24_present_hold <= 0) return 0;
s_d24_present_hold--;
return 1;
}
/* ---- Present-time screen-colour LUT (verified-enhancement, opt-in) -------
*
* PRESENT-TIME ONLY. This sits on the 15-bit-scanout -> RGB888 conversion that
@@ -4192,8 +4205,19 @@ static void gp1_v_display_range(uint32_t val) {
/* GP1(07h): Vertical display range
* bits 0-9: Y1
* bits 10-19: Y2 */
v_display_y1 = val & 0x3FF;
v_display_y2 = (val >> 10) & 0x3FF;
uint32_t y1 = val & 0x3FF;
uint32_t y2 = (val >> 10) & 0x3FF;
uint32_t h = (y2 > y1) ? (y2 - y1) : 0u;
/* MotK intro→crawl retargets the band while staying in depth24. Stale
* trailing RGB from the prior movie would flash for a frame or two —
* reset the upload span and hold present (skip Swap) for 3 vblanks. */
if ((display_depth & 1u) && s_d24_prev_disp_h != 0u && h != s_d24_prev_disp_h) {
s_d24_upload_x1 = 0;
s_d24_present_hold = 3;
}
v_display_y1 = y1;
v_display_y2 = y2;
if (h != 0u) s_d24_prev_disp_h = h;
}
static void gp1_display_mode(uint32_t val) {
+27 -33
View File
@@ -2998,36 +2998,23 @@ static void load_transition_note(int read_active, int load_active,
prev_turbo = turbo_active;
}
/* Depth24 FMV: last ~8 RGB columns can be stale/chroma junk while CRTC width
* stays full (e.g. MotK 512). Present width is never shrunk — cropping the
* GL/SDL rect left a flickering black pillar when upload coverage varied.
/* Depth24 FMV: trailing RGB columns can be stale while CRTC width stays full
* (MotK 512). Present width is never shrunk — cropping the GL/SDL rect left a
* flickering black pillar when upload coverage varied.
*
* Do NOT replicate the last good column: on MotK's starfield intro that turns
* a single tinted edge pixel into an 8-wide horizontal streak (flickering
* stretch into the pillar). Black-fill the margin instead. Require a dense
* chroma signal so sparse stars never trip the repair. */
static void depth24_fix_trailing_margin(uint32_t *buf, uint32_t w, uint32_t h) {
if (!buf || w < 24u || h == 0u) return;
const uint32_t margin = 8u;
const uint32_t edge = w - margin;
const uint32_t total = margin * h;
uint32_t junk_px = 0;
for (uint32_t x = edge; x < w; x++) {
for (uint32_t y = 0; y < h; y++) {
uint32_t p = buf[y * w + x];
int r = (int)((p >> 16) & 255u);
int g = (int)((p >> 8) & 255u);
int b = (int)(p & 255u);
int m = (r + g + b) / 3;
int ch = (r > m ? r - m : m - r) + (g > m ? g - m : m - g) +
(b > m ? b - m : m - b);
if (ch > 40) junk_px++;
}
}
/* ~12% of margin texels — sparse stars stay; dense chroma fringe cleans. */
if (total == 0u || junk_px * 100u < total * 12u) return;
* Always black-fill the last 8 RGB cols. Also blank beyond the tracked FB A0
* span when it falls short of the CRTC (movie cut / partial cover). Do NOT
* replicate the last good column — that smeared MotK's starfield into an
* 8-wide streak. */
static void depth24_fix_trailing_margin(uint32_t *buf, uint32_t w, uint32_t h,
uint32_t display_x) {
if (!buf || w < 8u || h == 0u) return;
uint32_t start = w - 8u;
uint32_t lim = gpu_depth24_rgb_limit(display_x, w);
if (lim > 0u && lim < w && lim < start)
start = lim;
for (uint32_t y = 0; y < h; y++) {
for (uint32_t x = edge; x < w; x++)
for (uint32_t x = start; x < w; x++)
buf[y * w + x] = 0xFF000000u;
}
}
@@ -3437,6 +3424,11 @@ static void sdl_vblank_present(void) {
/* Mod hooks. Run after all normal input sampling. */
mod_call_frame_hooks();
/* Depth24 GP1(07h) retarget (MotK intro→crawl): keep the prior Swap for a
* few vblanks so stale trailing VRAM never flashes on the right edge. */
if (gpu_depth24_present_hold_tick())
return;
/* Engage widescreen at game entry: BIOS boot stays authentic 4:3. */
if (!g_ws_engaged) {
extern int fntrace_is_game_started(void);
@@ -3576,9 +3568,10 @@ static void sdl_vblank_present(void) {
for (uint32_t x = 0; x < present_w; x++)
sdl_pixel_buf[y * present_w + x] =
gpu_display_pixel_argb(&di, x, y);
/* Trailing margin: replicate last good column into junk cols
* inside the full-width buffer — never shrink present width. */
depth24_fix_trailing_margin(sdl_pixel_buf, present_w, h);
/* Trailing margin: blank junk cols inside the full-width
* buffer — never shrink present width. */
depth24_fix_trailing_margin(sdl_pixel_buf, present_w, h,
di.display_x);
vk_renderer_present_cpu(sdl_pixel_buf, (int)present_w, (int)h,
0 /* nearest */, fmv_frame ? 1 : 0);
} else if (wide_present &&
@@ -3640,11 +3633,12 @@ static void sdl_vblank_present(void) {
}
}
/* Depth24 trailing margin: MotK CRTC is 512 RGB but the last ~8 cols
/* Depth24 trailing margin: MotK CRTC is 512 RGB but trailing cols
* can be stale. Fix pixels in-place at full present_w — never crop the
* GL/SDL draw width (that caused a flickering black pillar). */
if (di.depth24 && active_scale == 1 && !wide_present)
depth24_fix_trailing_margin(sdl_pixel_buf, present_w, h);
depth24_fix_trailing_margin(sdl_pixel_buf, present_w, h,
di.display_x);
smooth_60_present(sdl_pixel_buf,
present_w * (uint32_t)active_scale,