nv2a: Handle PVIDEO ds_dx and dt_dy registers

ds_dx and dt_dy describe how the PVIDEO content should be scaled to fit the
output area. Each is calculated via `((in - 1) << 20) / (out - 1)`, this
commit calculates the full frame scale (in / out) and applies that when
determining the texture coordinates for the overlay.
This commit is contained in:
Erik Abair 2022-07-11 10:50:01 -07:00 committed by mborgerson
parent 9fd957d950
commit f29c2ff42c
3 changed files with 43 additions and 14 deletions

View File

@ -266,7 +266,9 @@ typedef struct PGRAPHState {
GLuint pvideo_tex;
GLint pvideo_enable_loc;
GLint pvideo_tex_loc;
GLint pvideo_in_pos_loc;
GLint pvideo_pos_loc;
GLint pvideo_scale_loc;
GLint pvideo_color_key_enable_loc;
GLint pvideo_color_key_loc;
GLint palette_loc[256];

View File

@ -655,6 +655,7 @@
# define NV_PVIDEO_POINT_IN_T 0xFFFE0000
#define NV_PVIDEO_DS_DX 0x00000938
#define NV_PVIDEO_DT_DY 0x00000940
# define NV_PVIDEO_DIN_DOUT_UNITY 0x00100000
#define NV_PVIDEO_POINT_OUT 0x00000948
# define NV_PVIDEO_POINT_OUT_X 0x00000FFF
# define NV_PVIDEO_POINT_OUT_Y 0x0FFF0000

View File

@ -21,6 +21,8 @@
#include "nv2a_int.h"
#include <math.h>
#include "nv2a_vsh_emulator.h"
#include "s3tc.h"
#include "ui/xemu-settings.h"
@ -4998,7 +5000,9 @@ static void pgraph_init_display_renderer(NV2AState *d)
"uniform sampler2D tex;\n"
"uniform bool pvideo_enable;\n"
"uniform sampler2D pvideo_tex;\n"
"uniform vec2 pvideo_in_pos;\n"
"uniform vec4 pvideo_pos;\n"
"uniform vec3 pvideo_scale;\n"
"uniform bool pvideo_color_key_enable;\n"
"uniform vec4 pvideo_color_key;\n"
"uniform vec2 display_size;\n"
@ -5012,15 +5016,14 @@ static void pgraph_init_display_renderer(NV2AState *d)
" out_Color.rgba = texture(tex, texCoord);\n"
" if (pvideo_enable) {\n"
" vec2 screenCoord = gl_FragCoord.xy - 0.5;\n"
" vec4 extent = vec4(pvideo_pos.xy, pvideo_pos.xy + pvideo_pos.zw);\n"
" bvec4 clip = bvec4(lessThan(screenCoord, extent.xy),\n"
" greaterThan(screenCoord, extent.zw));\n"
" if (!any(clip)) {\n"
" vec2 videoCoord = (screenCoord - pvideo_pos.xy) / pvideo_pos.zw;\n"
" videoCoord.y *= -1.0;\n"
" if (!pvideo_color_key_enable || out_Color.rgba == pvideo_color_key) {\n"
" out_Color.rgba = texture(pvideo_tex, videoCoord);\n"
" }\n"
" vec4 output_region = vec4(pvideo_pos.xy, pvideo_pos.xy + pvideo_pos.zw);\n"
" bvec4 clip = bvec4(lessThan(screenCoord, output_region.xy),\n"
" greaterThan(screenCoord, output_region.zw));\n"
" if (!any(clip) && (!pvideo_color_key_enable || out_Color.rgba == pvideo_color_key)) {\n"
" vec2 out_xy = (screenCoord - pvideo_pos.xy) * pvideo_scale.z;\n"
" vec2 in_st = (pvideo_in_pos + out_xy * pvideo_scale.xy) / textureSize(pvideo_tex, 0);\n"
" in_st.y *= -1.0;\n"
" out_Color.rgba = texture(pvideo_tex, in_st);\n"
" }\n"
" }\n"
"}\n";
@ -5029,7 +5032,9 @@ static void pgraph_init_display_renderer(NV2AState *d)
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_in_pos_loc = glGetUniformLocation(pg->disp_rndr.prog, "pvideo_in_pos");
pg->disp_rndr.pvideo_pos_loc = glGetUniformLocation(pg->disp_rndr.prog, "pvideo_pos");
pg->disp_rndr.pvideo_scale_loc = glGetUniformLocation(pg->disp_rndr.prog, "pvideo_scale");
pg->disp_rndr.pvideo_color_key_enable_loc = glGetUniformLocation(pg->disp_rndr.prog, "pvideo_color_key_enable");
pg->disp_rndr.pvideo_color_key_loc = glGetUniformLocation(pg->disp_rndr.prog, "pvideo_color_key");
pg->disp_rndr.display_size_loc = glGetUniformLocation(pg->disp_rndr.prog, "display_size");
@ -5064,6 +5069,14 @@ static uint8_t *convert_texture_data__CR8YB8CB8YA8(const uint8_t *data,
return converted_data;
}
static inline float pvideo_calculate_scale(unsigned int din_dout,
unsigned int output_size)
{
float calculated_in = din_dout * (output_size - 1);
calculated_in = floorf(calculated_in / (1 << 20) + 0.5f);
return (calculated_in + 1.0f) / output_size;
}
static void pgraph_render_display_pvideo_overlay(NV2AState *d)
{
PGRAPHState *pg = &d->pgraph;
@ -5076,7 +5089,7 @@ static void pgraph_render_display_pvideo_overlay(NV2AState *d)
// stopping it.
// Since the value seems to be set to 0xFFFFFFFF only in cases where the
// content is not valid, it is probably good enough to treat it as an
// implicit stop.
// implicit stop.
bool enabled = (d->pvideo.regs[NV_PVIDEO_BUFFER] & NV_PVIDEO_BUFFER_0_USE)
&& d->pvideo.regs[NV_PVIDEO_SIZE_IN] != 0xFFFFFFFF;
glUniform1ui(d->pgraph.disp_rndr.pvideo_enable_loc, enabled);
@ -5093,12 +5106,11 @@ static void pgraph_render_display_pvideo_overlay(NV2AState *d)
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 =
@ -5109,15 +5121,26 @@ static void pgraph_render_display_pvideo_overlay(NV2AState *d)
unsigned int out_height =
GET_MASK(d->pvideo.regs[NV_PVIDEO_SIZE_OUT], NV_PVIDEO_SIZE_OUT_HEIGHT);
float scale_x = 1.0f;
float scale_y = 1.0f;
unsigned int ds_dx = d->pvideo.regs[NV_PVIDEO_DS_DX];
unsigned int dt_dy = d->pvideo.regs[NV_PVIDEO_DT_DY];
if (ds_dx != NV_PVIDEO_DIN_DOUT_UNITY) {
scale_x = pvideo_calculate_scale(ds_dx, out_width);
}
if (dt_dy != NV_PVIDEO_DIN_DOUT_UNITY) {
scale_y = pvideo_calculate_scale(dt_dy, 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;
in_width = floorf((float)out_width * scale_x + 0.5f);
}
if (in_height > out_height) {
in_height = out_height;
in_height = floorf((float)out_height * scale_y + 0.5f);
}
/* TODO: support other color formats */
@ -5164,8 +5187,11 @@ static void pgraph_render_display_pvideo_overlay(NV2AState *d)
GL_UNSIGNED_BYTE, tex_rgba);
g_free(tex_rgba);
glUniform1i(d->pgraph.disp_rndr.pvideo_tex_loc, 1);
glUniform2f(d->pgraph.disp_rndr.pvideo_in_pos_loc, in_s, in_t);
glUniform4f(d->pgraph.disp_rndr.pvideo_pos_loc,
out_x, out_y, out_width, out_height);
glUniform3f(d->pgraph.disp_rndr.pvideo_scale_loc,
scale_x, scale_y, 1.0f / pg->surface_scale_factor);
}
static void pgraph_render_display(NV2AState *d, SurfaceBinding *surface)