From c33d96127cf3d4bcb0c72482ae875a9ccd0df62b Mon Sep 17 00:00:00 2001 From: Erik Abair Date: Tue, 12 Apr 2022 15:01:56 -0700 Subject: [PATCH] nv2a: Implement shadow samplers --- hw/xbox/nv2a/nv2a_int.h | 2 + hw/xbox/nv2a/nv2a_regs.h | 9 +++ hw/xbox/nv2a/pgraph.c | 27 +++++++-- hw/xbox/nv2a/pgraph_methods.h | 1 + hw/xbox/nv2a/psh.c | 106 ++++++++++++++++++++++++++++------ hw/xbox/nv2a/psh.h | 14 +++++ 6 files changed, 137 insertions(+), 22 deletions(-) diff --git a/hw/xbox/nv2a/nv2a_int.h b/hw/xbox/nv2a/nv2a_int.h index f8dcacecf3..a52ea0145b 100644 --- a/hw/xbox/nv2a/nv2a_int.h +++ b/hw/xbox/nv2a/nv2a_int.h @@ -304,6 +304,8 @@ typedef struct PGRAPHState { bool texture_matrix_enable[NV2A_MAX_TEXTURES]; + enum PshShadowDepthFunc shadow_depth_func; + GLuint gl_framebuffer; GLuint gl_display_buffer; diff --git a/hw/xbox/nv2a/nv2a_regs.h b/hw/xbox/nv2a/nv2a_regs.h index 05de51bbbe..370d8728cf 100644 --- a/hw/xbox/nv2a/nv2a_regs.h +++ b/hw/xbox/nv2a/nv2a_regs.h @@ -1218,6 +1218,15 @@ # define NV097_SET_COMBINER_COLOR_OCW 0x00001E40 # define NV097_SET_COMBINER_CONTROL 0x00001E60 # define NV097_SET_SHADOW_ZSLOPE_THRESHOLD 0x00001E68 +# define NV097_SET_SHADOW_DEPTH_FUNC 0x00001E6C +# define NV097_SET_SHADOW_DEPTH_FUNC_NEVER 0x00000000 +# define NV097_SET_SHADOW_DEPTH_FUNC_LESS 0x00000001 +# define NV097_SET_SHADOW_DEPTH_FUNC_EQUAL 0x00000002 +# define NV097_SET_SHADOW_DEPTH_FUNC_LEQUAL 0x00000003 +# define NV097_SET_SHADOW_DEPTH_FUNC_GREATER 0x00000004 +# define NV097_SET_SHADOW_DEPTH_FUNC_NOTEQUAL 0x00000005 +# define NV097_SET_SHADOW_DEPTH_FUNC_GEQUAL 0x00000006 +# define NV097_SET_SHADOW_DEPTH_FUNC_ALWAYS 0x00000007 # define NV097_SET_SHADER_STAGE_PROGRAM 0x00001E70 # define NV097_SET_DOT_RGBMAPPING 0X00001E74 # define NV097_SET_SHADER_OTHER_STAGE_INPUT 0x00001E78 diff --git a/hw/xbox/nv2a/pgraph.c b/hw/xbox/nv2a/pgraph.c index ae9f4114cd..3a3bf4a33a 100644 --- a/hw/xbox/nv2a/pgraph.c +++ b/hw/xbox/nv2a/pgraph.c @@ -219,6 +219,7 @@ typedef struct ColorFormatInfo { GLenum gl_format; GLenum gl_type; GLenum gl_swizzle_mask[4]; + bool depth; } ColorFormatInfo; static const ColorFormatInfo kelvin_color_format_map[66] = { @@ -300,14 +301,20 @@ static const ColorFormatInfo kelvin_color_format_map[66] = { {2, true, GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV}, [NV097_SET_TEXTURE_FORMAT_COLOR_LC_IMAGE_YB8CR8YA8CB8] = {2, true, GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV}, - [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_DEPTH_X8_Y24_FIXED] = - {4, true, GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8}, + [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_DEPTH_Y16_FIXED] = - {2, false, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT}, + {2, false, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, + {GL_RED, GL_ZERO, GL_ZERO, GL_ZERO}, true}, + [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_DEPTH_X8_Y24_FIXED] = + {4, true, GL_DEPTH_COMPONENT, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, + {GL_RED, GL_ONE, GL_ZERO, GL_ZERO}, true}, [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_DEPTH_Y16_FIXED] = - {2, true, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT}, + {2, true, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, + {GL_RED, GL_ZERO, GL_ZERO, GL_ZERO}, true}, [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_DEPTH_Y16_FLOAT] = - {2, true, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_FLOAT}, + {2, true, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_FLOAT, + {GL_RED, GL_ZERO, GL_ZERO, GL_ZERO}, true}, + [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_Y16] = {2, true, GL_R16, GL_RED, GL_UNSIGNED_SHORT, {GL_RED, GL_RED, GL_RED, GL_ONE}}, @@ -3545,6 +3552,11 @@ DEF_METHOD(NV097, SET_SHADOW_ZSLOPE_THRESHOLD) assert(parameter == 0x7F800000); /* FIXME: Unimplemented */ } +DEF_METHOD(NV097, SET_SHADOW_DEPTH_FUNC) +{ + pg->shadow_depth_func = parameter; +} + DEF_METHOD(NV097, SET_SHADER_STAGE_PROGRAM) { pg->regs[NV_PGRAPH_SHADERPROG] = parameter; @@ -3845,6 +3857,7 @@ void pgraph_init(NV2AState *d) pg->shader_cache = g_hash_table_new(shader_hash, shader_equal); + pg->shadow_depth_func = SHADOW_DEPTH_FUNC_NEVER; pg->material_alpha = 0.0f; for (i=0; iregs[NV_PGRAPH_SETUPRASTER] & NV_PGRAPH_SETUPRASTER_POINTSMOOTHENABLE; + state.psh.shadow_depth_func = pg->shadow_depth_func; + state.fixed_function = fixed_function; /* fixed function stuff */ @@ -4385,6 +4400,8 @@ 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); + state.psh.shadow_map[i] = f.depth; + 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; diff --git a/hw/xbox/nv2a/pgraph_methods.h b/hw/xbox/nv2a/pgraph_methods.h index c5fe209904..1cf947c6e4 100644 --- a/hw/xbox/nv2a/pgraph_methods.h +++ b/hw/xbox/nv2a/pgraph_methods.h @@ -177,6 +177,7 @@ DEF_METHOD(NV097, SET_SHADER_CLIP_PLANE_MODE) DEF_METHOD_RANGE(NV097, SET_COMBINER_COLOR_OCW, 8) DEF_METHOD(NV097, SET_COMBINER_CONTROL) DEF_METHOD(NV097, SET_SHADOW_ZSLOPE_THRESHOLD) +DEF_METHOD(NV097, SET_SHADOW_DEPTH_FUNC) DEF_METHOD(NV097, SET_SHADER_STAGE_PROGRAM) DEF_METHOD(NV097, SET_DOT_RGBMAPPING) DEF_METHOD(NV097, SET_SHADER_OTHER_STAGE_INPUT) diff --git a/hw/xbox/nv2a/psh.c b/hw/xbox/nv2a/psh.c index 9a7b1de649..0c19addc4e 100644 --- a/hw/xbox/nv2a/psh.c +++ b/hw/xbox/nv2a/psh.c @@ -558,6 +558,64 @@ static void add_final_stage_code(struct PixelShader *ps, struct FCInputInfo fina ps->varE = ps->varF = NULL; } +static const char sampler2D[] = "sampler2D"; +static const char sampler3D[] = "sampler3D"; +static const char samplerCube[] = "samplerCube"; +static const char sampler2DRect[] = "sampler2DRect"; + +static const char* get_sampler_type(enum PS_TEXTUREMODES mode, const PshState *state, int i) +{ + switch (mode) { + default: + case PS_TEXTUREMODES_NONE: + return NULL; + + case PS_TEXTUREMODES_PROJECT2D: + case PS_TEXTUREMODES_BUMPENVMAP: + case PS_TEXTUREMODES_BUMPENVMAP_LUM: + case PS_TEXTUREMODES_DOT_ST: + if (state->shadow_map[i]) { + fprintf(stderr, "Shadow map support not implemented for mode %d\n", mode); + assert(!"Shadow map support not implemented for this mode"); + } + return state->rect_tex[i] ? sampler2DRect : sampler2D; + + case PS_TEXTUREMODES_PROJECT3D: + case PS_TEXTUREMODES_DOT_STR_3D: + if (state->shadow_map[i]) { + return state->rect_tex[i] ? sampler2DRect : sampler2D; + } + return sampler3D; + + case PS_TEXTUREMODES_CUBEMAP: + case PS_TEXTUREMODES_DOT_RFLCT_DIFF: + case PS_TEXTUREMODES_DOT_RFLCT_SPEC: + case PS_TEXTUREMODES_DOT_STR_CUBE: + if (state->shadow_map[i]) { + fprintf(stderr, "Shadow map support not implemented for mode %d\n", mode); + assert(!"Shadow map support not implemented for this mode"); + } + return samplerCube; + + case PS_TEXTUREMODES_DPNDNT_AR: + case PS_TEXTUREMODES_DPNDNT_GB: + if (state->shadow_map[i]) { + fprintf(stderr, "Shadow map support not implemented for mode %d\n", mode); + assert(!"Shadow map support not implemented for this mode"); + } + return sampler2D; + } +} + +static const char *shadow_comparison_map[] = { + [SHADOW_DEPTH_FUNC_LESS] = "<", + [SHADOW_DEPTH_FUNC_EQUAL] = "==", + [SHADOW_DEPTH_FUNC_LEQUAL] = "<=", + [SHADOW_DEPTH_FUNC_GREATER] = ">", + [SHADOW_DEPTH_FUNC_NOTEQUAL] = "!=", + [SHADOW_DEPTH_FUNC_GEQUAL] = ">=", +}; + static MString* psh_convert(struct PixelShader *ps) { int i; @@ -699,7 +757,7 @@ static MString* psh_convert(struct PixelShader *ps) for (i = 0; i < 4; i++) { - const char *sampler_type = NULL; + const char *sampler_type = get_sampler_type(ps->tex_modes[i], &ps->state, i); assert(ps->dot_map[i] < 8); const char *dotmap_func = dotmap_funcs[ps->dot_map[i]]; @@ -713,7 +771,6 @@ static MString* psh_convert(struct PixelShader *ps) i); break; case PS_TEXTUREMODES_PROJECT2D: { - sampler_type = ps->state.rect_tex[i] ? "sampler2DRect" : "sampler2D"; const char *lookup = "textureProj"; if ((ps->state.conv_tex[i] == CONVOLUTION_FILTER_GAUSSIAN) || (ps->state.conv_tex[i] == CONVOLUTION_FILTER_QUINCUNX)) { @@ -733,12 +790,38 @@ static MString* psh_convert(struct PixelShader *ps) break; } case PS_TEXTUREMODES_PROJECT3D: - sampler_type = "sampler3D"; - mstring_append_fmt(vars, "vec4 t%d = textureProj(texSamp%d, pT%d.xyzw);\n", - i, i, i); + if (ps->state.shadow_map[i]) { + if (ps->state.shadow_depth_func == SHADOW_DEPTH_FUNC_NEVER) { + mstring_append_fmt(vars, "vec4 t%d = vec4(0.0);\n", i); + } else if (ps->state.shadow_depth_func == SHADOW_DEPTH_FUNC_ALWAYS) { + mstring_append_fmt(vars, "vec4 t%d = vec4(1.0);\n", i); + } else { + mstring_append_fmt(vars, + "pT%d.xy *= texScale%d;\n" + "vec4 t%d_depth = textureProj(texSamp%d, pT%d.xyw);\n" + "float t%d_max_depth;\n" + "if (t%d_depth.y > 0) {\n" + " t%d_max_depth = 0xFFFFFF;\n" + "} else {\n" + " t%d_max_depth = 0xFFFF; // TODO: Support float max.\n" + "}\n" + "t%d_depth.x *= t%d_max_depth;\n" + "pT%d.z = clamp(pT%d.z / pT%d.w, 0, t%d_max_depth);\n", + i, i, i, i, i, + i, i, i, i, i, + i, i, i, i, i); + + const char *comparison = shadow_comparison_map[ps->state.shadow_depth_func]; + mstring_append_fmt(vars, + "vec4 t%d = vec4(t%d_depth.x %s pT%d.z ? 1.0 : 0.0);\n", + i, i, comparison, i); + } + } else { + mstring_append_fmt(vars, "vec4 t%d = textureProj(texSamp%d, pT%d.xyzw);\n", + i, i, i); + } break; case PS_TEXTUREMODES_CUBEMAP: - sampler_type = "samplerCube"; mstring_append_fmt(vars, "vec4 t%d = texture(texSamp%d, pT%d.xyz / pT%d.w);\n", i, i, i, i); break; @@ -758,7 +841,6 @@ static MString* psh_convert(struct PixelShader *ps) } case PS_TEXTUREMODES_BUMPENVMAP: assert(i >= 1); - sampler_type = ps->state.rect_tex[i] ? "sampler2DRect" : "sampler2D"; mstring_append_fmt(preflight, "uniform mat2 bumpMat%d;\n", i); if (ps->state.snorm_tex[ps->input_tex[i]]) { @@ -778,7 +860,6 @@ static MString* psh_convert(struct PixelShader *ps) break; case PS_TEXTUREMODES_BUMPENVMAP_LUM: assert(i >= 1); - sampler_type = ps->state.rect_tex[i] ? "sampler2DRect" : "sampler2D"; mstring_append_fmt(preflight, "uniform float bumpScale%d;\n", i); mstring_append_fmt(preflight, "uniform float bumpOffset%d;\n", i); mstring_append_fmt(preflight, "uniform mat2 bumpMat%d;\n", i); @@ -808,7 +889,6 @@ static MString* psh_convert(struct PixelShader *ps) break; case PS_TEXTUREMODES_DOT_ST: assert(i >= 2); - sampler_type = ps->state.rect_tex[i] ? "sampler2DRect" : "sampler2D"; mstring_append_fmt(vars, "/* PS_TEXTUREMODES_DOT_ST */\n"); mstring_append_fmt(vars, "float dot%d = dot(pT%d.xyz, %s(t%d.rgb));\n", i, i, dotmap_func, ps->input_tex[i]); @@ -825,7 +905,6 @@ static MString* psh_convert(struct PixelShader *ps) break; case PS_TEXTUREMODES_DOT_RFLCT_DIFF: assert(i == 2); - sampler_type = "samplerCube"; mstring_append_fmt(vars, "/* PS_TEXTUREMODES_DOT_RFLCT_DIFF */\n"); mstring_append_fmt(vars, "float dot%d = dot(pT%d.xyz, %s(t%d.rgb));\n", i, i, dotmap_func, ps->input_tex[i]); @@ -839,7 +918,6 @@ static MString* psh_convert(struct PixelShader *ps) break; case PS_TEXTUREMODES_DOT_RFLCT_SPEC: assert(i == 3); - sampler_type = "samplerCube"; mstring_append_fmt(vars, "/* PS_TEXTUREMODES_DOT_RFLCT_SPEC */\n"); mstring_append_fmt(vars, "float dot%d = dot(pT%d.xyz, %s(t%d.rgb));\n", i, i, dotmap_func, ps->input_tex[i]); @@ -854,7 +932,6 @@ static MString* psh_convert(struct PixelShader *ps) break; case PS_TEXTUREMODES_DOT_STR_3D: assert(i == 3); - sampler_type = "sampler3D"; mstring_append_fmt(vars, "/* PS_TEXTUREMODES_DOT_STR_3D */\n"); mstring_append_fmt(vars, "float dot%d = dot(pT%d.xyz, %s(t%d.rgb));\n", i, i, dotmap_func, ps->input_tex[i]); @@ -863,7 +940,6 @@ static MString* psh_convert(struct PixelShader *ps) break; case PS_TEXTUREMODES_DOT_STR_CUBE: assert(i == 3); - sampler_type = "samplerCube"; mstring_append_fmt(vars, "/* PS_TEXTUREMODES_DOT_STR_CUBE */\n"); mstring_append_fmt(vars, "float dot%d = dot(pT%d.xyz, %s(t%d.rgb));\n", i, i, dotmap_func, ps->input_tex[i]); @@ -873,14 +949,12 @@ static MString* psh_convert(struct PixelShader *ps) case PS_TEXTUREMODES_DPNDNT_AR: assert(i >= 1); assert(!ps->state.rect_tex[i]); - sampler_type = "sampler2D"; mstring_append_fmt(vars, "vec4 t%d = texture(texSamp%d, t%d.ar);\n", i, i, ps->input_tex[i]); break; case PS_TEXTUREMODES_DPNDNT_GB: assert(i >= 1); assert(!ps->state.rect_tex[i]); - sampler_type = "sampler2D"; mstring_append_fmt(vars, "vec4 t%d = texture(texSamp%d, t%d.gb);\n", i, i, ps->input_tex[i]); break; @@ -1063,7 +1137,5 @@ MString *psh_translate(const PshState state) ps.final_input.inv_r0 = flags & PS_FINALCOMBINERSETTING_COMPLEMENT_R0; } - - return psh_convert(&ps); } diff --git a/hw/xbox/nv2a/psh.h b/hw/xbox/nv2a/psh.h index d73ce38303..1d7ccc7bdc 100644 --- a/hw/xbox/nv2a/psh.h +++ b/hw/xbox/nv2a/psh.h @@ -33,6 +33,17 @@ enum PshAlphaFunc { ALPHA_FUNC_ALWAYS, }; +enum PshShadowDepthFunc { + SHADOW_DEPTH_FUNC_NEVER, + SHADOW_DEPTH_FUNC_LESS, + SHADOW_DEPTH_FUNC_EQUAL, + SHADOW_DEPTH_FUNC_LEQUAL, + SHADOW_DEPTH_FUNC_GREATER, + SHADOW_DEPTH_FUNC_NOTEQUAL, + SHADOW_DEPTH_FUNC_GEQUAL, + SHADOW_DEPTH_FUNC_ALWAYS, +}; + enum ConvolutionFilter { CONVOLUTION_FILTER_DISABLED, CONVOLUTION_FILTER_QUINCUNX, @@ -57,6 +68,9 @@ typedef struct PshState { bool alphakill[4]; enum ConvolutionFilter conv_tex[4]; + bool shadow_map[4]; + enum PshShadowDepthFunc shadow_depth_func; + bool alpha_test; enum PshAlphaFunc alpha_func;