Preserve interlaced PAL scanout height

This commit is contained in:
Alexandros Mandravillis
2026-08-31 03:17:48 +03:00
parent 0ff78cc4ca
commit 4676707ae0
4 changed files with 35 additions and 15 deletions
+11
View File
@@ -5,6 +5,11 @@
#include <stdint.h>
/* PAL active video is 288 lines per field and 576 rows when the interlaced
* display bit is set. CPU presentation buffers must preserve that full
* canvas even though PS1 VRAM itself remains 512 rows and wraps on access. */
#define PSX_DISPLAY_PRESENT_MAX_HEIGHT 576u
typedef struct {
uint32_t canvas_height;
uint32_t canvas_origin_y;
@@ -24,6 +29,12 @@ static inline uint32_t psx_display_present_height(
return depth24 && canvas_height != 0u ? canvas_height : source_height;
}
static inline uint32_t psx_display_interlaced_rows(
uint32_t rows, int interlaced)
{
return interlaced ? rows * 2u : rows;
}
/* Preserve the legacy default for an unset/degenerate GP1(07h) range. A
* programmed increasing range that has no active-video intersection owns a
* zero height instead, so presentation fails closed to black. */
+8 -5
View File
@@ -3232,10 +3232,11 @@ void gpu_get_display_info(GpuDisplayInfo* out) {
uint32_t screen_origin_y = vertical.valid ? vertical.canvas_origin_y : 0u;
uint32_t screen_source_skip_y = vertical.valid ? vertical.source_skip_y : 0u;
if (vres) {
h *= 2; /* 480i */
screen_h *= 2;
screen_origin_y *= 2;
screen_source_skip_y *= 2;
h = psx_display_interlaced_rows(h, 1);
screen_h = psx_display_interlaced_rows(screen_h, 1);
screen_origin_y = psx_display_interlaced_rows(screen_origin_y, 1);
screen_source_skip_y = psx_display_interlaced_rows(
screen_source_skip_y, 1);
}
/* 24-bit scanout uses the same CRTC pixel width as 15-bit (DuckStation /
@@ -3247,7 +3248,9 @@ void gpu_get_display_info(GpuDisplayInfo* out) {
/* Clamp to sane maximums */
if (w > 640) w = 640;
if (screen_h > 512) screen_h = 512;
const uint32_t max_screen_h = out->depth24
? PSX_DISPLAY_PRESENT_MAX_HEIGHT : 512u;
if (screen_h > max_screen_h) screen_h = max_screen_h;
if (screen_origin_y > screen_h) screen_origin_y = screen_h;
if (h > screen_h - screen_origin_y) h = screen_h - screen_origin_y;
+13 -10
View File
@@ -393,9 +393,8 @@ static void apply_offline_pad_count(int game_players, bool multitap_enabled)
n = 2;
g_offline_pad_count = n;
}
/* ARGB8888 staging buffer. Sized for the active internal resolution:
* 640*scale x 512*scale. Allocated once the supersampling scale is known
* (sized for the native 640x512 when supersampling is off). */
/* ARGB8888 staging buffer. The 576-row maximum preserves the full interlaced
* PAL active canvas. Allocated once the supersampling scale is known. */
static uint32_t* sdl_pixel_buf = nullptr;
typedef void (*ModFrameHook)(void);
@@ -1880,7 +1879,8 @@ static int ensure_sw_sdl_present(void) {
sdl_renderer,
SDL_PIXELFORMAT_ARGB8888,
SDL_TEXTUREACCESS_STREAMING,
640 * tex_scale, 512 * tex_scale);
640 * tex_scale,
(int)PSX_DISPLAY_PRESENT_MAX_HEIGHT * tex_scale);
if (!sdl_texture) {
std::fprintf(stderr,
"psxrecomp: netplay SW present: SDL_CreateTexture failed: %s\n",
@@ -1893,7 +1893,8 @@ static int ensure_sw_sdl_present(void) {
if (!sdl_pixel_buf) {
const int tex_scale = netplay_cpu_auth_gpu() ? 1 : g_video_scale;
sdl_pixel_buf = (uint32_t*)std::malloc(
(size_t)640 * tex_scale * 512 * tex_scale * sizeof(uint32_t));
(size_t)640 * tex_scale * PSX_DISPLAY_PRESENT_MAX_HEIGHT *
tex_scale * sizeof(uint32_t));
if (!sdl_pixel_buf) {
std::fprintf(stderr, "psxrecomp: netplay SW present: staging alloc failed\n");
return -1;
@@ -7294,7 +7295,7 @@ static NetplayVblankEpilogue sdl_vblank_present_body(void) {
* show that as a thin white strip; Wayland may not). Pad one black row
* so any residual linear fringe is black, matching the letterbox. */
const int tex_scale = netplay_cpu_auth_gpu() ? 1 : g_video_scale;
const int tex_h = 512 * tex_scale;
const int tex_h = (int)PSX_DISPLAY_PRESENT_MAX_HEIGHT * tex_scale;
if (src_w > 0 && src_h > 0 && src_h < tex_h) {
static uint32_t s_black_pad[640 * 4]; /* covers g_video_scale <= 4 */
const int pad_cap = (int)(sizeof(s_black_pad) / sizeof(s_black_pad[0]));
@@ -13706,12 +13707,13 @@ session_reboot:
}
}
/* Staging buffer + backing texture are sized for the internal resolution
* (640x512 native, times the supersampling factor). Netplay: 1×. */
/* Staging buffer + backing texture preserve the 576-row interlaced PAL
* canvas, times the supersampling factor. Netplay: 1×. */
{
const int tex_scale = netplay_cpu_auth_gpu() ? 1 : g_video_scale;
sdl_pixel_buf = (uint32_t*)std::malloc(
(size_t)640 * tex_scale * 512 * tex_scale * sizeof(uint32_t));
(size_t)640 * tex_scale * PSX_DISPLAY_PRESENT_MAX_HEIGHT *
tex_scale * sizeof(uint32_t));
if (!sdl_pixel_buf) {
std::fprintf(stderr, "failed to allocate %dx staging buffer\n", tex_scale);
return 1;
@@ -13724,7 +13726,8 @@ session_reboot:
sdl_renderer,
SDL_PIXELFORMAT_ARGB8888,
SDL_TEXTUREACCESS_STREAMING,
640 * tex_scale, 512 * tex_scale
640 * tex_scale,
(int)PSX_DISPLAY_PRESENT_MAX_HEIGHT * tex_scale
);
if (!sdl_texture) {
std::fprintf(stderr, "SDL_CreateTexture failed: %s\n", SDL_GetError());
+3
View File
@@ -17,6 +17,9 @@ static void test_present_height(void)
assert(psx_display_present_height(0, 225, 288) == 225);
assert(psx_display_present_height(1, 225, 288) == 288);
assert(psx_display_present_height(1, 225, 0) == 225);
assert(psx_display_interlaced_rows(288, 0) == 288);
assert(psx_display_interlaced_rows(288, 1) ==
PSX_DISPLAY_PRESENT_MAX_HEIGHT);
PsxDisplayVerticalLayout unset = psx_display_vertical_layout(0, 100, 100);
assert(!unset.range_set);