From 48c1720da8caadb04a44cddd56139f943d9a8d83 Mon Sep 17 00:00:00 2001 From: Matt Borgerson Date: Mon, 1 Mar 2021 14:33:31 -0700 Subject: [PATCH] nv2a: Support PVIDEO overlays again --- hw/xbox/nv2a/nv2a.c | 100 ---------------------------------------- hw/xbox/nv2a/nv2a_int.h | 4 ++ hw/xbox/nv2a/pgraph.c | 100 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 103 insertions(+), 101 deletions(-) diff --git a/hw/xbox/nv2a/nv2a.c b/hw/xbox/nv2a/nv2a.c index 9272b894a3..3425d04aa0 100644 --- a/hw/xbox/nv2a/nv2a.c +++ b/hw/xbox/nv2a/nv2a.c @@ -155,105 +155,6 @@ void nv2a_reg_log_write(int block, hwaddr addr, uint64_t val) } #endif -#if 0 -/* FIXME: Probably totally wrong */ -static inline unsigned int rgb_to_pixel8(unsigned int r, unsigned int g, - unsigned int b) -{ - return ((r >> 5) << 5) | ((g >> 5) << 2) | (b >> 6); -} -static inline unsigned int rgb_to_pixel16(unsigned int r, unsigned int g, - unsigned int b) -{ - return ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3); -} -static inline unsigned int rgb_to_pixel32(unsigned int r, unsigned int g, - unsigned int b) -{ - return (r << 16) | (g << 8) | b; -} - -static void nv2a_overlay_draw_line(VGACommonState *vga, uint8_t *line, int y) -{ - NV2A_DPRINTF("nv2a_overlay_draw_line\n"); - - NV2AState *d = container_of(vga, NV2AState, vga); - DisplaySurface *surface = qemu_console_surface(d->vga.con); - - int surf_bpp = surface_bytes_per_pixel(surface); - int surf_width = surface_width(surface); - - if (!(d->pvideo.regs[NV_PVIDEO_BUFFER] & NV_PVIDEO_BUFFER_0_USE)) return; - - hwaddr base = d->pvideo.regs[NV_PVIDEO_BASE]; - hwaddr limit = d->pvideo.regs[NV_PVIDEO_LIMIT]; - hwaddr offset = d->pvideo.regs[NV_PVIDEO_OFFSET]; - - int in_width = GET_MASK(d->pvideo.regs[NV_PVIDEO_SIZE_IN], - NV_PVIDEO_SIZE_IN_WIDTH); - int in_height = GET_MASK(d->pvideo.regs[NV_PVIDEO_SIZE_IN], - NV_PVIDEO_SIZE_IN_HEIGHT); - int in_s = GET_MASK(d->pvideo.regs[NV_PVIDEO_POINT_IN], - NV_PVIDEO_POINT_IN_S); - int in_t = GET_MASK(d->pvideo.regs[NV_PVIDEO_POINT_IN], - NV_PVIDEO_POINT_IN_T); - int in_pitch = GET_MASK(d->pvideo.regs[NV_PVIDEO_FORMAT], - NV_PVIDEO_FORMAT_PITCH); - 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); - - int out_width = GET_MASK(d->pvideo.regs[NV_PVIDEO_SIZE_OUT], - NV_PVIDEO_SIZE_OUT_WIDTH); - int out_height = GET_MASK(d->pvideo.regs[NV_PVIDEO_SIZE_OUT], - NV_PVIDEO_SIZE_OUT_HEIGHT); - int out_x = GET_MASK(d->pvideo.regs[NV_PVIDEO_POINT_OUT], - NV_PVIDEO_POINT_OUT_X); - int out_y = GET_MASK(d->pvideo.regs[NV_PVIDEO_POINT_OUT], - NV_PVIDEO_POINT_OUT_Y); - - - if (y < out_y || y >= out_y + out_height) return; - - // TODO: scaling, color keys - - int in_y = y - out_y; - if (in_y >= in_height) return; - - assert(offset + in_pitch * (in_y + 1) <= limit); - uint8_t *in_line = d->vram_ptr + base + offset + in_pitch * in_y; - - int x; - for (x=0; x= surf_width) break; - int ix = in_s + x; - if (ix >= in_width) break; - - uint8_t r,g,b; - convert_yuy2_to_rgb(in_line, ix, &r, &g, &b); - - // unsigned int pixel = vga->rgb_to_pixel(r, g, b); - switch (surf_bpp) { - case 1: - ((uint8_t*)line)[ox] = (uint8_t)rgb_to_pixel8(r,g,b); - break; - case 2: - ((uint16_t*)line)[ox] = (uint16_t)rgb_to_pixel16(r,g,b); - break; - case 4: - ((uint32_t*)line)[ox] = (uint32_t)rgb_to_pixel32(r,g,b); - break; - default: - assert(false); - break; - } - } -} -#endif - static int nv2a_get_bpp(VGACommonState *s) { NV2AState *d = container_of(s, NV2AState, vga); @@ -432,7 +333,6 @@ static void nv2a_realize(PCIDevice *dev, Error **errp) vga_common_init(vga, OBJECT(dev)); vga->get_bpp = nv2a_get_bpp; vga->get_offsets = nv2a_get_offsets; - // vga->overlay_draw_line = nv2a_overlay_draw_line; d->hw_ops = *vga->hw_ops; d->hw_ops.gfx_update = nv2a_vga_gfx_update; diff --git a/hw/xbox/nv2a/nv2a_int.h b/hw/xbox/nv2a/nv2a_int.h index 1cc88768dd..c777570f75 100644 --- a/hw/xbox/nv2a/nv2a_int.h +++ b/hw/xbox/nv2a/nv2a_int.h @@ -248,6 +248,10 @@ typedef struct PGRAPHState { struct disp_rndr { GLuint fbo, vao, vbo, prog; GLuint tex_loc; + GLuint pvideo_tex; + GLint pvideo_enable_loc; + GLint pvideo_tex_loc; + GLint pvideo_pos_loc; } disp_rndr; /* subchannels state we're not sure the location of... */ diff --git a/hw/xbox/nv2a/pgraph.c b/hw/xbox/nv2a/pgraph.c index cb4c49a786..19f806a79f 100644 --- a/hw/xbox/nv2a/pgraph.c +++ b/hw/xbox/nv2a/pgraph.c @@ -3773,19 +3773,35 @@ static void pgraph_init_display_renderer(NV2AState *d) " gl_Position = vec4(x, y, 0, 1);\n" "}\n"; // FIXME: gamma correction - // FIXME: video overlay + const char *fs = "#version 330\n" "uniform sampler2D tex;\n" + "uniform bool pvideo_enable;\n" + "uniform sampler2D pvideo_tex;\n" + "uniform vec4 pvideo_pos;\n" "layout(location = 0) out vec4 out_Color;\n" "void main()\n" "{\n" " vec2 texCoord = gl_FragCoord.xy/textureSize(tex,0).xy;\n" " out_Color.rgba = texture(tex, texCoord);\n" + " if (pvideo_enable) {\n" + " vec4 extent = vec4(pvideo_pos.xy, pvideo_pos.xy + pvideo_pos.zw);\n" + " bvec4 clip = bvec4(lessThan(gl_FragCoord.xy, extent.xy),\n" + " greaterThan(gl_FragCoord.xy, extent.zw));\n" + " if (!any(clip)) {\n" + " vec2 spos = vec2(gl_FragCoord.x, textureSize(tex,0).y-gl_FragCoord.y);\n" + " vec2 coord = (spos-pvideo_pos.xy)/pvideo_pos.zw;\n" + " out_Color.rgba = texture(pvideo_tex, coord);\n" + " }\n" + " }\n" "}\n"; pg->disp_rndr.prog = pgraph_compile_shader(vs, fs); pg->disp_rndr.tex_loc = glGetUniformLocation(pg->disp_rndr.prog, "tex"); + pg->disp_rndr.pvideo_enable_loc = glGetUniformLocation(pg->disp_rndr.prog, "pvideo_enable"); + pg->disp_rndr.pvideo_tex_loc = glGetUniformLocation(pg->disp_rndr.prog, "pvideo_tex"); + pg->disp_rndr.pvideo_pos_loc = glGetUniformLocation(pg->disp_rndr.prog, "pvideo_pos"); glGenVertexArrays(1, &pg->disp_rndr.vao); glBindVertexArray(pg->disp_rndr.vao); @@ -3793,9 +3809,90 @@ static void pgraph_init_display_renderer(NV2AState *d) glBindBuffer(GL_ARRAY_BUFFER, pg->disp_rndr.vbo); glBufferData(GL_ARRAY_BUFFER, 0, NULL, GL_STATIC_DRAW); glGenFramebuffers(1, &pg->disp_rndr.fbo); + glGenTextures(1, &pg->disp_rndr.pvideo_tex); assert(glGetError() == GL_NO_ERROR); } +static uint8_t *convert_texture_data__CR8YB8CB8YA8(const uint8_t *data, + unsigned int width, + unsigned int height, + unsigned int pitch) +{ + uint8_t *converted_data = (uint8_t *)g_malloc(width * height * 4); + int x, y; + for (y = 0; y < height; y++) { + const uint8_t *line = &data[y * pitch]; + for (x = 0; x < width; x++) { + uint8_t *pixel = &converted_data[(y * width + x) * 4]; + /* FIXME: Actually needs uyvy? */ + convert_yuy2_to_rgb(line, x, &pixel[0], &pixel[1], &pixel[2]); + pixel[3] = 255; + } + } + return converted_data; +} + +static void pgraph_render_display_pvideo_overlay(NV2AState *d) +{ + bool enabled = d->pvideo.regs[NV_PVIDEO_BUFFER] & NV_PVIDEO_BUFFER_0_USE; + glUniform1ui(d->pgraph.disp_rndr.pvideo_enable_loc, enabled); + if (!enabled) { + return; + } + + hwaddr base = d->pvideo.regs[NV_PVIDEO_BASE]; + hwaddr limit = d->pvideo.regs[NV_PVIDEO_LIMIT]; + hwaddr offset = d->pvideo.regs[NV_PVIDEO_OFFSET]; + + int in_width = + GET_MASK(d->pvideo.regs[NV_PVIDEO_SIZE_IN], NV_PVIDEO_SIZE_IN_WIDTH); + int in_height = + GET_MASK(d->pvideo.regs[NV_PVIDEO_SIZE_IN], NV_PVIDEO_SIZE_IN_HEIGHT); + + /* FIXME + int in_s = GET_MASK(d->pvideo.regs[NV_PVIDEO_POINT_IN], + NV_PVIDEO_POINT_IN_S); + int in_t = GET_MASK(d->pvideo.regs[NV_PVIDEO_POINT_IN], + NV_PVIDEO_POINT_IN_T); + */ + int in_pitch = + GET_MASK(d->pvideo.regs[NV_PVIDEO_FORMAT], NV_PVIDEO_FORMAT_PITCH); + 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); + + int out_width = + GET_MASK(d->pvideo.regs[NV_PVIDEO_SIZE_OUT], NV_PVIDEO_SIZE_OUT_WIDTH); + int out_height = + GET_MASK(d->pvideo.regs[NV_PVIDEO_SIZE_OUT], NV_PVIDEO_SIZE_OUT_HEIGHT); + int out_x = + GET_MASK(d->pvideo.regs[NV_PVIDEO_POINT_OUT], NV_PVIDEO_POINT_OUT_X); + int out_y = + GET_MASK(d->pvideo.regs[NV_PVIDEO_POINT_OUT], NV_PVIDEO_POINT_OUT_Y); + + /* TODO: color keys */ + assert(offset + in_pitch * in_height <= limit); + hwaddr end = base + offset + in_pitch * in_height; + assert(end <= memory_region_size(d->vram)); + + glActiveTexture(GL_TEXTURE0 + 1); + glBindTexture(GL_TEXTURE_2D, g_nv2a->pgraph.disp_rndr.pvideo_tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + uint8_t *tex_rgba = convert_texture_data__CR8YB8CB8YA8( + d->vram_ptr + base + offset, in_width, in_height, in_pitch); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, in_width, in_height, 0, GL_RGBA, + GL_UNSIGNED_BYTE, tex_rgba); + g_free(tex_rgba); + glUniform1i(d->pgraph.disp_rndr.pvideo_tex_loc, 1); + glUniform4f(d->pgraph.disp_rndr.pvideo_pos_loc, + out_x, out_y, out_width, out_height); +} + static void pgraph_render_display(NV2AState *d, SurfaceBinding *surface) { glActiveTexture(GL_TEXTURE0); @@ -3819,6 +3916,7 @@ static void pgraph_render_display(NV2AState *d, SurfaceBinding *surface) glBindBuffer(GL_ARRAY_BUFFER, d->pgraph.disp_rndr.vbo); glUseProgram(d->pgraph.disp_rndr.prog); glProgramUniform1i(d->pgraph.disp_rndr.prog, d->pgraph.disp_rndr.tex_loc, 0); + pgraph_render_display_pvideo_overlay(d); glViewport(0, 0, surface->width, surface->height); glColorMask(true, true, true, true);