diff --git a/runtime/include/display_scanout.h b/runtime/include/display_scanout.h index 904b0160..c02968f8 100644 --- a/runtime/include/display_scanout.h +++ b/runtime/include/display_scanout.h @@ -35,6 +35,20 @@ static inline uint32_t psx_display_interlaced_rows( return interlaced ? rows * 2u : rows; } +/* Canvas-origin clipping belongs only to staged depth24 presentation. + * Ordinary 15-bit renderers read their source rectangle directly. */ +static inline uint32_t psx_display_clip_source_height( + int depth24, uint32_t source_height, uint32_t canvas_height, + uint32_t canvas_origin_y) +{ + if (!depth24) + return source_height; + if (canvas_origin_y >= canvas_height) + return 0u; + const uint32_t available = canvas_height - canvas_origin_y; + return source_height < available ? source_height : available; +} + /* 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. */ diff --git a/runtime/src/gpu.c b/runtime/src/gpu.c index 50e15e67..28b694ed 100644 --- a/runtime/src/gpu.c +++ b/runtime/src/gpu.c @@ -3248,11 +3248,21 @@ void gpu_get_display_info(GpuDisplayInfo* out) { /* Clamp to sane maximums */ if (w > 640) w = 640; - 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; + if (out->depth24) { + if (screen_h > PSX_DISPLAY_PRESENT_MAX_HEIGHT) + screen_h = PSX_DISPLAY_PRESENT_MAX_HEIGHT; + if (screen_origin_y > screen_h) + screen_origin_y = screen_h; + h = psx_display_clip_source_height( + 1, h, screen_h, screen_origin_y); + } else { + /* The active canvas is a depth24 staging contract. Keep direct 15-bit + * source rectangles independent from its origin and 576-row limit. */ + if (h > 512u) h = 512u; + screen_h = h; + screen_origin_y = 0u; + screen_source_skip_y = 0u; + } out->width = w; out->height = h; diff --git a/runtime/tests/test_display_scanout.c b/runtime/tests/test_display_scanout.c index 8cf2b58c..75710d1c 100644 --- a/runtime/tests/test_display_scanout.c +++ b/runtime/tests/test_display_scanout.c @@ -20,6 +20,9 @@ static void test_present_height(void) assert(psx_display_interlaced_rows(288, 0) == 288); assert(psx_display_interlaced_rows(288, 1) == PSX_DISPLAY_PRESENT_MAX_HEIGHT); + assert(psx_display_clip_source_height(0, 450, 512, 126) == 450); + assert(psx_display_clip_source_height(1, 450, 512, 126) == 386); + assert(psx_display_clip_source_height(1, 450, 512, 512) == 0); PsxDisplayVerticalLayout unset = psx_display_vertical_layout(0, 100, 100); assert(!unset.range_set);