From 4d995d11098ed87fe120b2a86d45154110e5cfe6 Mon Sep 17 00:00:00 2001 From: izzy2lost Date: Mon, 30 Mar 2026 05:39:48 -0400 Subject: [PATCH] Handle signed texture sampler channels in shaders --- hw/xbox/nv2a/pgraph/gl/texture.c | 6 --- hw/xbox/nv2a/pgraph/glsl/psh.c | 68 +++++++++++++++++++++++++++----- hw/xbox/nv2a/pgraph/glsl/psh.h | 1 + hw/xbox/nv2a/pgraph/texture.h | 28 +++++++++++++ hw/xbox/nv2a/pgraph/vk/texture.c | 9 ----- 5 files changed, 87 insertions(+), 25 deletions(-) diff --git a/hw/xbox/nv2a/pgraph/gl/texture.c b/hw/xbox/nv2a/pgraph/gl/texture.c index 9cde178e66..12df12b7c2 100644 --- a/hw/xbox/nv2a/pgraph/gl/texture.c +++ b/hw/xbox/nv2a/pgraph/gl/texture.c @@ -701,12 +701,6 @@ void pgraph_gl_bind_textures(NV2AState *d) 1 << (GET_MASK(pgraph_reg_r(pg, NV_PGRAPH_TEXCTL0_0 + i*4), NV_PGRAPH_TEXCTL0_0_MAX_ANISOTROPY)); - /* Check for unsupported features */ - if (filter & NV_PGRAPH_TEXFILTER0_ASIGNED) NV2A_UNIMPLEMENTED("NV_PGRAPH_TEXFILTER0_ASIGNED"); - if (filter & NV_PGRAPH_TEXFILTER0_RSIGNED) NV2A_UNIMPLEMENTED("NV_PGRAPH_TEXFILTER0_RSIGNED"); - if (filter & NV_PGRAPH_TEXFILTER0_GSIGNED) NV2A_UNIMPLEMENTED("NV_PGRAPH_TEXFILTER0_GSIGNED"); - if (filter & NV_PGRAPH_TEXFILTER0_BSIGNED) NV2A_UNIMPLEMENTED("NV_PGRAPH_TEXFILTER0_BSIGNED"); - TextureShape state = pgraph_get_texture_shape(pg, i); hwaddr texture_vram_offset, palette_vram_offset = 0; size_t length, palette_length = 0; diff --git a/hw/xbox/nv2a/pgraph/glsl/psh.c b/hw/xbox/nv2a/pgraph/glsl/psh.c index 2f3af6a68e..a254db8630 100644 --- a/hw/xbox/nv2a/pgraph/glsl/psh.c +++ b/hw/xbox/nv2a/pgraph/glsl/psh.c @@ -29,6 +29,7 @@ #include "qemu/osdep.h" #include "hw/xbox/nv2a/debug.h" #include "hw/xbox/nv2a/pgraph/pgraph.h" +#include "hw/xbox/nv2a/pgraph/texture.h" #include "psh.h" DEF_UNIFORM_INFO_ARR(PshUniform, PSH_UNIFORM_DECL_X) @@ -195,6 +196,8 @@ void pgraph_glsl_set_psh_state(PGRAPHState *pg, PshState *state) state->shadow_map[i] = f.depth; uint32_t filter = pgraph_reg_r(pg, NV_PGRAPH_TEXFILTER0 + i * 4); + state->tex_signed[i] = + pgraph_get_texture_signed_component_mask_from_filter(filter); 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 @@ -802,6 +805,31 @@ static void define_colorkey_comparator(MString *preflight) // clang-format on } +static bool texture_channels_are_signed(const struct PixelShader *ps, int tex, + uint8_t mask) +{ + return (ps->state->tex_signed[tex] & mask) == mask; +} + +static void apply_signed_texture_remap(const struct PixelShader *ps, + MString *vars, int tex) +{ + uint8_t mask = ps->state->tex_signed[tex]; + + if (mask & PGRAPH_TEXTURE_SIGNED_R) { + mstring_append_fmt(vars, "t%d.r = sign3(t%d.r);\n", tex, tex); + } + if (mask & PGRAPH_TEXTURE_SIGNED_G) { + mstring_append_fmt(vars, "t%d.g = sign3(t%d.g);\n", tex, tex); + } + if (mask & PGRAPH_TEXTURE_SIGNED_B) { + mstring_append_fmt(vars, "t%d.b = sign3(t%d.b);\n", tex, tex); + } + if (mask & PGRAPH_TEXTURE_SIGNED_A) { + mstring_append_fmt(vars, "t%d.a = sign3(t%d.a);\n", tex, tex); + } +} + static MString* psh_convert(struct PixelShader *ps) { MString *preflight = mstring_new(); @@ -1173,7 +1201,10 @@ static MString* psh_convert(struct PixelShader *ps) case PS_TEXTUREMODES_BUMPENVMAP: assert(i >= 1); - if (ps->state->snorm_tex[ps->input_tex[i]]) { + if (ps->state->snorm_tex[ps->input_tex[i]] || + texture_channels_are_signed( + ps, ps->input_tex[i], + PGRAPH_TEXTURE_SIGNED_G | PGRAPH_TEXTURE_SIGNED_B)) { /* Input color channels already signed (FIXME: May not always want signed textures in this case) */ mstring_append_fmt(vars, "vec2 dsdt%d = t%d.bg;\n", i, ps->input_tex[i]); @@ -1199,15 +1230,28 @@ static MString* psh_convert(struct PixelShader *ps) case PS_TEXTUREMODES_BUMPENVMAP_LUM: assert(i >= 1); - if (ps->state->snorm_tex[ps->input_tex[i]]) { - /* Input color channels already signed (FIXME: May not always want signed textures in this case) */ - mstring_append_fmt(vars, "vec3 dsdtl%d = vec3(t%d.bg, sign3_to_0_to_1(t%d.r));\n", - i, ps->input_tex[i], ps->input_tex[i]); - } else { - /* Convert to signed (FIXME: loss of accuracy due to filtering/interpolation) */ - mstring_append_fmt(vars, "vec3 dsdtl%d = vec3(sign3(t%d.b), sign3(t%d.g), t%d.r);\n", - i, ps->input_tex[i], ps->input_tex[i], ps->input_tex[i]); - } + { + bool bg_signed = + ps->state->snorm_tex[ps->input_tex[i]] || + texture_channels_are_signed( + ps, ps->input_tex[i], + PGRAPH_TEXTURE_SIGNED_G | PGRAPH_TEXTURE_SIGNED_B); + bool r_signed = + ps->state->snorm_tex[ps->input_tex[i]] || + texture_channels_are_signed( + ps, ps->input_tex[i], PGRAPH_TEXTURE_SIGNED_R); + g_autofree gchar *b_expr = g_strdup_printf( + bg_signed ? "t%d.b" : "sign3(t%d.b)", ps->input_tex[i]); + g_autofree gchar *g_expr = g_strdup_printf( + bg_signed ? "t%d.g" : "sign3(t%d.g)", ps->input_tex[i]); + g_autofree gchar *r_expr = g_strdup_printf( + r_signed ? "sign3_to_0_to_1(t%d.r)" : "t%d.r", + ps->input_tex[i]); + + mstring_append_fmt(vars, + "vec3 dsdtl%d = vec3(%s, %s, %s);\n", i, + b_expr, g_expr, r_expr); + } mstring_append_fmt(vars, "dsdtl%d.st = bumpMat[%d] * dsdtl%d.st;\n", i, i, i); @@ -1403,6 +1447,10 @@ static MString* psh_convert(struct PixelShader *ps) mstring_append(vars, "}\n"); } + if (!ps->state->shadow_map[i] && !ps->state->tex_x8y24[i]) { + apply_signed_texture_remap(ps, vars, i); + } + if (ps->state->rect_tex[i]) { mstring_append_fmt(preflight, "vec2 norm%d(vec2 coord) {\n" diff --git a/hw/xbox/nv2a/pgraph/glsl/psh.h b/hw/xbox/nv2a/pgraph/glsl/psh.h index ea3045ab32..e18cac86c5 100644 --- a/hw/xbox/nv2a/pgraph/glsl/psh.h +++ b/hw/xbox/nv2a/pgraph/glsl/psh.h @@ -47,6 +47,7 @@ typedef struct PshState { bool point_sprite; bool rect_tex[4]; bool snorm_tex[4]; + uint8_t tex_signed[4]; bool compare_mode[4][4]; bool alphakill[4]; int colorkey_mode[4]; diff --git a/hw/xbox/nv2a/pgraph/texture.h b/hw/xbox/nv2a/pgraph/texture.h index 343bcf0e3b..1d3697a3f1 100644 --- a/hw/xbox/nv2a/pgraph/texture.h +++ b/hw/xbox/nv2a/pgraph/texture.h @@ -50,8 +50,36 @@ typedef struct BasicColorFormatInfo { bool depth; } BasicColorFormatInfo; +enum PgraphTextureSignedChannel { + PGRAPH_TEXTURE_SIGNED_R = 1 << 0, + PGRAPH_TEXTURE_SIGNED_G = 1 << 1, + PGRAPH_TEXTURE_SIGNED_B = 1 << 2, + PGRAPH_TEXTURE_SIGNED_A = 1 << 3, +}; + extern const BasicColorFormatInfo kelvin_color_format_info_map[66]; +static inline uint8_t +pgraph_get_texture_signed_component_mask_from_filter(uint32_t filter) +{ + uint8_t mask = 0; + + if (filter & NV_PGRAPH_TEXFILTER0_RSIGNED) { + mask |= PGRAPH_TEXTURE_SIGNED_R; + } + if (filter & NV_PGRAPH_TEXFILTER0_GSIGNED) { + mask |= PGRAPH_TEXTURE_SIGNED_G; + } + if (filter & NV_PGRAPH_TEXFILTER0_BSIGNED) { + mask |= PGRAPH_TEXTURE_SIGNED_B; + } + if (filter & NV_PGRAPH_TEXFILTER0_ASIGNED) { + mask |= PGRAPH_TEXTURE_SIGNED_A; + } + + return mask; +} + uint8_t *pgraph_convert_texture_data(const TextureShape s, const uint8_t *data, const uint8_t *palette_data, unsigned int width, unsigned int height, diff --git a/hw/xbox/nv2a/pgraph/vk/texture.c b/hw/xbox/nv2a/pgraph/vk/texture.c index 20f7191bdf..9282e0a8ad 100644 --- a/hw/xbox/nv2a/pgraph/vk/texture.c +++ b/hw/xbox/nv2a/pgraph/vk/texture.c @@ -1703,15 +1703,6 @@ static void create_texture(PGRAPHState *pg, int texture_idx) } } - if (filter & NV_PGRAPH_TEXFILTER0_ASIGNED) - NV2A_UNIMPLEMENTED("NV_PGRAPH_TEXFILTER0_ASIGNED"); - if (filter & NV_PGRAPH_TEXFILTER0_RSIGNED) - NV2A_UNIMPLEMENTED("NV_PGRAPH_TEXFILTER0_RSIGNED"); - if (filter & NV_PGRAPH_TEXFILTER0_GSIGNED) - NV2A_UNIMPLEMENTED("NV_PGRAPH_TEXFILTER0_GSIGNED"); - if (filter & NV_PGRAPH_TEXFILTER0_BSIGNED) - NV2A_UNIMPLEMENTED("NV_PGRAPH_TEXFILTER0_BSIGNED"); - VkFilter vk_min_filter, vk_mag_filter; unsigned int mag_filter = GET_MASK(filter, NV_PGRAPH_TEXFILTER0_MAG); assert(mag_filter < ARRAY_SIZE(pgraph_texture_mag_filter_vk_map));