diff --git a/hw/xbox/nv2a/nv2a_regs.h b/hw/xbox/nv2a/nv2a_regs.h index 3cce60e542..28bf7778cf 100644 --- a/hw/xbox/nv2a/nv2a_regs.h +++ b/hw/xbox/nv2a/nv2a_regs.h @@ -517,6 +517,9 @@ #define NV_PGRAPH_TEXCTL2_1 0x000019F0 #define NV_PGRAPH_TEXFILTER0 0x000019F4 # define NV_PGRAPH_TEXFILTER0_MIPMAP_LOD_BIAS 0x00001FFF +# define NV_PGRAPH_TEXFILTER0_CONVOLUTION_KERNEL 0x0000E000 +# define NV_PGRAPH_TEXFILTER0_CONVOLUTION_KERNEL_QUINCUNX 1 +# define NV_PGRAPH_TEXFILTER0_CONVOLUTION_KERNEL_GAUSSIAN_3 2 # define NV_PGRAPH_TEXFILTER0_MIN 0x003F0000 # define NV_PGRAPH_TEXFILTER0_MIN_BOX_LOD0 1 # define NV_PGRAPH_TEXFILTER0_MIN_TENT_LOD0 2 diff --git a/hw/xbox/nv2a/pgraph.c b/hw/xbox/nv2a/pgraph.c index 157d1ebef7..4e90170fb8 100644 --- a/hw/xbox/nv2a/pgraph.c +++ b/hw/xbox/nv2a/pgraph.c @@ -63,7 +63,7 @@ static const GLenum pgraph_texture_min_filter_map[] = { GL_LINEAR_MIPMAP_NEAREST, GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR_MIPMAP_LINEAR, - GL_LINEAR, /* TODO: Convolution filter... */ + GL_LINEAR, }; static const GLenum pgraph_texture_mag_filter_map[] = { @@ -3358,6 +3358,21 @@ static void pgraph_bind_shaders(PGRAPHState *pg) state.psh.snorm_tex[i] = (f.gl_internal_format == GL_RGB8_SNORM) || (f.gl_internal_format == GL_RG8_SNORM); + uint32_t filter = pg->regs[NV_PGRAPH_TEXFILTER0 + i*4]; + unsigned int min_filter = GET_MASK(filter, NV_PGRAPH_TEXFILTER0_MIN); + enum ConvolutionFilter kernel = CONVOLUTION_FILTER_DISABLED; + /* FIXME: We do not distinguish between min and mag when + * performing convolution. Just use it if specified for min (common AA + * case). + */ + if (min_filter == NV_PGRAPH_TEXFILTER0_MIN_CONVOLUTION_2D_LOD0) { + int k = GET_MASK(filter, NV_PGRAPH_TEXFILTER0_CONVOLUTION_KERNEL); + assert(k == NV_PGRAPH_TEXFILTER0_CONVOLUTION_KERNEL_QUINCUNX || + k == NV_PGRAPH_TEXFILTER0_CONVOLUTION_KERNEL_GAUSSIAN_3); + kernel = (enum ConvolutionFilter)k; + } + + state.psh.conv_tex[i] = kernel; } ShaderBinding* cached_shader = (ShaderBinding*)g_hash_table_lookup(pg->shader_cache, &state); diff --git a/hw/xbox/nv2a/psh.c b/hw/xbox/nv2a/psh.c index 8c75e12762..bc40eeb2b4 100644 --- a/hw/xbox/nv2a/psh.c +++ b/hw/xbox/nv2a/psh.c @@ -588,6 +588,22 @@ static QString* psh_convert(struct PixelShader *ps) "vec3 dotmap_hilo_hemisphere(vec3 col) {\n" " return col;\n" // FIXME "}\n" + "const float[9] gaussian3x3 = float[9](\n" + " 1.0/16.0, 2.0/16.0, 1.0/16.0,\n" + " 2.0/16.0, 4.0/16.0, 2.0/16.0,\n" + " 1.0/16.0, 2.0/16.0, 1.0/16.0);\n" + "const vec2[9] convolution3x3 = vec2[9](\n" + " vec2(-1.0,-1.0),vec2(0.0,-1.0),vec2(1.0,-1.0),\n" + " vec2(-1.0, 0.0),vec2(0.0, 0.0),vec2(1.0, 0.0),\n" + " vec2(-1.0, 1.0),vec2(0.0, 1.0),vec2(1.0, 1.0));\n" + "vec4 gaussianFilter2DRectProj(sampler2DRect sampler, vec3 texCoord) {\n" + " vec4 sum = vec4(0.0);\n" + " for (int i = 0; i < 9; i++) {\n" + " sum += gaussian3x3[i]*textureProj(sampler,\n" + " texCoord + vec3(convolution3x3[i], 0.0));\n" + " }\n" + " return sum;\n" + "}\n" ); /* Window Clipping */ @@ -658,11 +674,25 @@ static QString* psh_convert(struct PixelShader *ps) qstring_append_fmt(vars, "vec4 t%d = vec4(0.0); /* PS_TEXTUREMODES_NONE */\n", i); break; - case PS_TEXTUREMODES_PROJECT2D: + case PS_TEXTUREMODES_PROJECT2D: { sampler_type = ps->state.rect_tex[i] ? "sampler2DRect" : "sampler2D"; - qstring_append_fmt(vars, "vec4 t%d = textureProj(texSamp%d, pT%d.xyw);\n", - i, i, i); + const char *lookup = "textureProj"; + if ((ps->state.conv_tex[i] == CONVOLUTION_FILTER_GAUSSIAN) + || (ps->state.conv_tex[i] == CONVOLUTION_FILTER_QUINCUNX)) { + /* FIXME: Quincunx looks better than Linear and costs less than + * Gaussian, but Gaussian should be plenty fast so use it for + * now. + */ + if (ps->state.rect_tex[i]) { + lookup = "gaussianFilter2DRectProj"; + } else { + NV2A_UNIMPLEMENTED("Convolution for 2D textures"); + } + } + qstring_append_fmt(vars, "vec4 t%d = %s(texSamp%d, pT%d.xyw);\n", + i, lookup, i, i); break; + } case PS_TEXTUREMODES_PROJECT3D: sampler_type = "sampler3D"; qstring_append_fmt(vars, "vec4 t%d = textureProj(texSamp%d, pT%d.xyzw);\n", diff --git a/hw/xbox/nv2a/psh.h b/hw/xbox/nv2a/psh.h index 1d7ed357c0..aec9c7870f 100644 --- a/hw/xbox/nv2a/psh.h +++ b/hw/xbox/nv2a/psh.h @@ -33,6 +33,12 @@ enum PshAlphaFunc { ALPHA_FUNC_ALWAYS, }; +enum ConvolutionFilter { + CONVOLUTION_FILTER_DISABLED, + CONVOLUTION_FILTER_QUINCUNX, + CONVOLUTION_FILTER_GAUSSIAN, +}; + typedef struct PshState { /* fragment shader - register combiner stuff */ uint32_t combiner_control; @@ -48,6 +54,7 @@ typedef struct PshState { bool snorm_tex[4]; bool compare_mode[4][4]; bool alphakill[4]; + enum ConvolutionFilter conv_tex[4]; bool alpha_test; enum PshAlphaFunc alpha_func;