From a675666051c009fd15026367da297b0a1fd9b50c Mon Sep 17 00:00:00 2001 From: Erik Abair Date: Fri, 8 Jul 2022 09:35:57 -0700 Subject: [PATCH] nv2a: Handle SIZE_IN > SIZE_OUT case `NV_PVIDEO_SIZE_IN` is set to 0xFFFFFFFF during initialization and teardown of the PVIDEO overlay. In some cases this can happen before the overlay is explicitly stopped, leading to an assert. On hardware SIZE_IN values larger than SIZE_OUT are capped (content is not scaled). Fixes #330 [Test](https://github.com/abaire/nxdk_pgraph_tests/blob/main/src/tests/pvideo_tests.cpp) --- hw/xbox/nv2a/nv2a.c | 1 + hw/xbox/nv2a/pgraph.c | 22 +++++++++++++++++----- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/hw/xbox/nv2a/nv2a.c b/hw/xbox/nv2a/nv2a.c index d862bd951c..989140b23c 100644 --- a/hw/xbox/nv2a/nv2a.c +++ b/hw/xbox/nv2a/nv2a.c @@ -278,6 +278,7 @@ static void nv2a_reset(NV2AState *d) memset(d->pfifo.regs, 0, sizeof(d->pfifo.regs)); memset(d->pgraph.regs, 0, sizeof(d->pgraph.regs)); + memset(d->pvideo.regs, 0, sizeof(d->pvideo.regs)); d->pcrtc.start = 0; d->pramdac.core_clock_coeff = 0x00011C01; /* 189MHz...? */ diff --git a/hw/xbox/nv2a/pgraph.c b/hw/xbox/nv2a/pgraph.c index 1618c2a692..1e2d27e3aa 100644 --- a/hw/xbox/nv2a/pgraph.c +++ b/hw/xbox/nv2a/pgraph.c @@ -5054,8 +5054,9 @@ static uint8_t *convert_texture_data__CR8YB8CB8YA8(const uint8_t *data, int x, y; for (y = 0; y < height; y++) { const uint8_t *line = &data[y * pitch]; + const uint32_t row_offset = y * width; for (x = 0; x < width; x++) { - uint8_t *pixel = &converted_data[(y * width + x) * 4]; + uint8_t *pixel = &converted_data[(row_offset + x) * 4]; convert_yuy2_to_rgb(line, x, &pixel[0], &pixel[1], &pixel[2]); pixel[3] = 255; } @@ -5093,14 +5094,25 @@ static void pgraph_render_display_pvideo_overlay(NV2AState *d) int in_color = GET_MASK(d->pvideo.regs[NV_PVIDEO_FORMAT], NV_PVIDEO_FORMAT_COLOR); - /* TODO: support other color formats */ - assert(in_color == NV_PVIDEO_FORMAT_COLOR_LE_CR8YB8CB8YA8); - assert(in_pitch >= in_width * 2); - unsigned int out_width = GET_MASK(d->pvideo.regs[NV_PVIDEO_SIZE_OUT], NV_PVIDEO_SIZE_OUT_WIDTH); unsigned int out_height = GET_MASK(d->pvideo.regs[NV_PVIDEO_SIZE_OUT], NV_PVIDEO_SIZE_OUT_HEIGHT); + + // On HW, setting NV_PVIDEO_SIZE_IN larger than NV_PVIDEO_SIZE_OUT results + // in them being capped to the output size, content is not scaled. This is + // particularly important as NV_PVIDEO_SIZE_IN may be set to 0xFFFFFFFF + // during initialization or teardown. + if (in_width > out_width) { + in_width = out_width; + } + if (in_height > out_height) { + in_height = out_height; + } + + /* TODO: support other color formats */ + assert(in_color == NV_PVIDEO_FORMAT_COLOR_LE_CR8YB8CB8YA8); + unsigned int out_x = GET_MASK(d->pvideo.regs[NV_PVIDEO_POINT_OUT], NV_PVIDEO_POINT_OUT_X); unsigned int out_y =