From dcce54590b304147ceb7d8307823fa2239d5bb17 Mon Sep 17 00:00:00 2001 From: Erik Abair Date: Fri, 19 Sep 2025 08:21:12 -0700 Subject: [PATCH 1/3] nv2a: Handle anisotropic filter setting Implements handling of anisotropic filter level from SET_TEXTURE_CONTROL0. --- hw/xbox/nv2a/nv2a_regs.h | 1 + hw/xbox/nv2a/pgraph/gl/texture.c | 14 +++++++++++--- hw/xbox/nv2a/pgraph/vk/texture.c | 12 +++++++++--- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/hw/xbox/nv2a/nv2a_regs.h b/hw/xbox/nv2a/nv2a_regs.h index 39061c99a41..97fb4c48120 100644 --- a/hw/xbox/nv2a/nv2a_regs.h +++ b/hw/xbox/nv2a/nv2a_regs.h @@ -539,6 +539,7 @@ #define NV_PGRAPH_TEXCTL0_0 0x000019CC # define NV_PGRAPH_TEXCTL0_0_COLORKEYMODE 0x03 # define NV_PGRAPH_TEXCTL0_0_ALPHAKILLEN (1 << 2) +# define NV_PGRAPH_TEXCTL0_0_MAX_ANISOTROPY 0x30 # define NV_PGRAPH_TEXCTL0_0_MAX_LOD_CLAMP 0x0003FFC0 # define NV_PGRAPH_TEXCTL0_0_MIN_LOD_CLAMP 0x3FFC0000 # define NV_PGRAPH_TEXCTL0_0_ENABLE (1 << 30) diff --git a/hw/xbox/nv2a/pgraph/gl/texture.c b/hw/xbox/nv2a/pgraph/gl/texture.c index ead8af51345..9ff1d52998d 100644 --- a/hw/xbox/nv2a/pgraph/gl/texture.c +++ b/hw/xbox/nv2a/pgraph/gl/texture.c @@ -113,7 +113,8 @@ static void apply_texture_parameters(TextureBinding *binding, unsigned int filter, unsigned int address, bool is_bordered, - uint32_t border_color) + uint32_t border_color, + uint32_t max_anisotropy) { unsigned int min_filter = GET_MASK(filter, NV_PGRAPH_TEXFILTER0_MIN); unsigned int mag_filter = GET_MASK(filter, NV_PGRAPH_TEXFILTER0_MAG); @@ -181,6 +182,8 @@ static void apply_texture_parameters(TextureBinding *binding, needs_border_color = needs_border_color || binding->addrp == NV_PGRAPH_TEXADDRESS0_ADDRU_BORDER; } + glTexParameterf(binding->gl_target, GL_TEXTURE_MAX_ANISOTROPY, max_anisotropy); + if (!is_bordered && needs_border_color) { if (!binding->border_color_set || binding->border_color != border_color) { /* FIXME: Color channels might be wrong order */ @@ -219,6 +222,9 @@ void pgraph_gl_bind_textures(NV2AState *d) uint32_t filter = pgraph_reg_r(pg, NV_PGRAPH_TEXFILTER0 + i*4); uint32_t address = pgraph_reg_r(pg, NV_PGRAPH_TEXADDRESS0 + i*4); uint32_t border_color = pgraph_reg_r(pg, NV_PGRAPH_BORDERCOLOR0 + i*4); + uint32_t max_anisotropy = + 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"); @@ -268,7 +274,8 @@ void pgraph_gl_bind_textures(NV2AState *d) filter, address, state.border, - border_color); + border_color, + max_anisotropy); continue; } } @@ -378,7 +385,8 @@ void pgraph_gl_bind_textures(NV2AState *d) filter, address, state.border, - border_color); + border_color, + max_anisotropy); if (r->texture_binding[i]) { if (r->texture_binding[i]->gl_target != binding->gl_target) { diff --git a/hw/xbox/nv2a/pgraph/vk/texture.c b/hw/xbox/nv2a/pgraph/vk/texture.c index 551ea7376c6..69e37559cdf 100644 --- a/hw/xbox/nv2a/pgraph/vk/texture.c +++ b/hw/xbox/nv2a/pgraph/vk/texture.c @@ -1101,6 +1101,13 @@ static void create_texture(PGRAPHState *pg, int texture_idx) pgraph_reg_r(pg, NV_PGRAPH_BORDERCOLOR0 + texture_idx * 4); bool is_indexed = (state.color_format == NV097_SET_TEXTURE_FORMAT_COLOR_SZ_I8_A8R8G8B8); + uint32_t xbox_max_anisotropy = + 1 << (GET_MASK(pgraph_reg_r(pg, NV_PGRAPH_TEXCTL0_0 + texture_idx*4), + NV_PGRAPH_TEXCTL0_0_MAX_ANISOTROPY)); + uint32_t max_anisotropy = + xbox_max_anisotropy <= r->device_props.limits.maxSamplerAnisotropy ? + xbox_max_anisotropy : + r->device_props.limits.maxSamplerAnisotropy; TextureKey key; memset(&key, 0, sizeof(key)); @@ -1354,9 +1361,8 @@ static void create_texture(PGRAPHState *pg, int texture_idx) GET_MASK(address, NV_PGRAPH_TEXADDRESS0_ADDRV)), .addressModeW = (state.dimensionality > 2) ? lookup_texture_address_mode( GET_MASK(address, NV_PGRAPH_TEXADDRESS0_ADDRP)) : 0, - .anisotropyEnable = VK_FALSE, - // .anisotropyEnable = VK_TRUE, - // .maxAnisotropy = properties.limits.maxSamplerAnisotropy, + .anisotropyEnable = max_anisotropy > 1, + .maxAnisotropy = max_anisotropy, .borderColor = vk_border_color, .compareEnable = VK_FALSE, .compareOp = VK_COMPARE_OP_ALWAYS, From cce8e3ace850a39b24b479d403a729ebd8f9267d Mon Sep 17 00:00:00 2001 From: Erik Abair Date: Tue, 23 Sep 2025 16:26:33 -0700 Subject: [PATCH 2/3] SQUASHME: Enable samplerAnisotropy and PR cleanup --- hw/xbox/nv2a/pgraph/vk/instance.c | 1 + hw/xbox/nv2a/pgraph/vk/renderer.h | 1 + hw/xbox/nv2a/pgraph/vk/texture.c | 14 +++++++------- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/hw/xbox/nv2a/pgraph/vk/instance.c b/hw/xbox/nv2a/pgraph/vk/instance.c index 3d993cb7350..f0804f7bdf0 100644 --- a/hw/xbox/nv2a/pgraph/vk/instance.c +++ b/hw/xbox/nv2a/pgraph/vk/instance.c @@ -547,6 +547,7 @@ static bool create_logical_device(PGRAPHState *pg, Error **errp) F(occlusionQueryPrecise, true), F(fillModeNonSolid, true), F(wideLines, false), + F(samplerAnisotropy, false), #undef F // clang-format on }; diff --git a/hw/xbox/nv2a/pgraph/vk/renderer.h b/hw/xbox/nv2a/pgraph/vk/renderer.h index c2233a7b17c..d68e2c4207d 100644 --- a/hw/xbox/nv2a/pgraph/vk/renderer.h +++ b/hw/xbox/nv2a/pgraph/vk/renderer.h @@ -206,6 +206,7 @@ typedef struct TextureKey { uint32_t filter; uint32_t address; uint32_t border_color; + uint32_t max_anisotropy; } TextureKey; typedef struct TextureBinding { diff --git a/hw/xbox/nv2a/pgraph/vk/texture.c b/hw/xbox/nv2a/pgraph/vk/texture.c index 69e37559cdf..0d6abc4dd38 100644 --- a/hw/xbox/nv2a/pgraph/vk/texture.c +++ b/hw/xbox/nv2a/pgraph/vk/texture.c @@ -1101,13 +1101,9 @@ static void create_texture(PGRAPHState *pg, int texture_idx) pgraph_reg_r(pg, NV_PGRAPH_BORDERCOLOR0 + texture_idx * 4); bool is_indexed = (state.color_format == NV097_SET_TEXTURE_FORMAT_COLOR_SZ_I8_A8R8G8B8); - uint32_t xbox_max_anisotropy = + uint32_t max_anisotropy = 1 << (GET_MASK(pgraph_reg_r(pg, NV_PGRAPH_TEXCTL0_0 + texture_idx*4), NV_PGRAPH_TEXCTL0_0_MAX_ANISOTROPY)); - uint32_t max_anisotropy = - xbox_max_anisotropy <= r->device_props.limits.maxSamplerAnisotropy ? - xbox_max_anisotropy : - r->device_props.limits.maxSamplerAnisotropy; TextureKey key; memset(&key, 0, sizeof(key)); @@ -1127,6 +1123,7 @@ static void create_texture(PGRAPHState *pg, int texture_idx) key.filter = filter; key.address = address; key.border_color = border_color_pack32; + key.max_anisotropy = max_anisotropy; bool possibly_dirty = false; bool possibly_dirty_checked = false; @@ -1350,6 +1347,8 @@ static void create_texture(PGRAPHState *pg, int texture_idx) } else if (lod_bias < -r->device_props.limits.maxSamplerLodBias) { lod_bias = -r->device_props.limits.maxSamplerLodBias; } + uint32_t sampler_max_anisotropy = + MIN(r->device_props.limits.maxSamplerAnisotropy, max_anisotropy); VkSamplerCreateInfo sampler_create_info = { .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, @@ -1361,8 +1360,9 @@ static void create_texture(PGRAPHState *pg, int texture_idx) GET_MASK(address, NV_PGRAPH_TEXADDRESS0_ADDRV)), .addressModeW = (state.dimensionality > 2) ? lookup_texture_address_mode( GET_MASK(address, NV_PGRAPH_TEXADDRESS0_ADDRP)) : 0, - .anisotropyEnable = max_anisotropy > 1, - .maxAnisotropy = max_anisotropy, + .anisotropyEnable = r->enabled_physical_device_features.wideLines && + sampler_max_anisotropy > 1, + .maxAnisotropy = sampler_max_anisotropy, .borderColor = vk_border_color, .compareEnable = VK_FALSE, .compareOp = VK_COMPARE_OP_ALWAYS, From 0c506830be0c5bb5738ebcda4511124bcd8ebf67 Mon Sep 17 00:00:00 2001 From: Erik Abair Date: Wed, 24 Sep 2025 15:14:17 -0700 Subject: [PATCH 3/3] SQUASHME: Check for anisotropic extension before use. --- hw/xbox/nv2a/pgraph/gl/renderer.c | 3 +++ hw/xbox/nv2a/pgraph/gl/renderer.h | 4 ++++ hw/xbox/nv2a/pgraph/gl/texture.c | 14 ++++++++++---- hw/xbox/nv2a/pgraph/vk/instance.c | 10 +++++----- hw/xbox/nv2a/pgraph/vk/texture.c | 5 +++-- 5 files changed, 25 insertions(+), 11 deletions(-) diff --git a/hw/xbox/nv2a/pgraph/gl/renderer.c b/hw/xbox/nv2a/pgraph/gl/renderer.c index 36b80294390..f3764f397d1 100644 --- a/hw/xbox/nv2a/pgraph/gl/renderer.c +++ b/hw/xbox/nv2a/pgraph/gl/renderer.c @@ -66,6 +66,9 @@ static void pgraph_gl_init(NV2AState *d, Error **errp) pg->uniform_attrs = 0; pg->swizzle_attrs = 0; + + r->supported_extensions.texture_filter_anisotropic = + glo_check_extension("GL_EXT_texture_filter_anisotropic"); } static void pgraph_gl_finalize(NV2AState *d) diff --git a/hw/xbox/nv2a/pgraph/gl/renderer.h b/hw/xbox/nv2a/pgraph/gl/renderer.h index 1e55ce80ca2..d76e502c8c8 100644 --- a/hw/xbox/nv2a/pgraph/gl/renderer.h +++ b/hw/xbox/nv2a/pgraph/gl/renderer.h @@ -234,6 +234,10 @@ typedef struct PGRAPHGLState { GLfloat supported_aliased_line_width_range[2]; GLfloat supported_smooth_line_width_range[2]; + + struct supported_extensions { + GLboolean texture_filter_anisotropic; + } supported_extensions; } PGRAPHGLState; extern GloContext *g_nv2a_context_render; diff --git a/hw/xbox/nv2a/pgraph/gl/texture.c b/hw/xbox/nv2a/pgraph/gl/texture.c index 9ff1d52998d..eb0e4199fed 100644 --- a/hw/xbox/nv2a/pgraph/gl/texture.c +++ b/hw/xbox/nv2a/pgraph/gl/texture.c @@ -107,7 +107,8 @@ static bool check_texture_possibly_dirty(NV2AState *d, return possibly_dirty; } -static void apply_texture_parameters(TextureBinding *binding, +static void apply_texture_parameters(PGRAPHGLState *r, + TextureBinding *binding, const BasicColorFormatInfo *f, unsigned int dimensionality, unsigned int filter, @@ -182,7 +183,10 @@ static void apply_texture_parameters(TextureBinding *binding, needs_border_color = needs_border_color || binding->addrp == NV_PGRAPH_TEXADDRESS0_ADDRU_BORDER; } - glTexParameterf(binding->gl_target, GL_TEXTURE_MAX_ANISOTROPY, max_anisotropy); + if (r->supported_extensions.texture_filter_anisotropic) { + glTexParameterf(binding->gl_target, GL_TEXTURE_MAX_ANISOTROPY_EXT, + max_anisotropy); + } if (!is_bordered && needs_border_color) { if (!binding->border_color_set || binding->border_color != border_color) { @@ -268,7 +272,8 @@ void pgraph_gl_bind_textures(NV2AState *d) if (reusable) { glBindTexture(r->texture_binding[i]->gl_target, r->texture_binding[i]->gl_texture); - apply_texture_parameters(r->texture_binding[i], + apply_texture_parameters(r, + r->texture_binding[i], &kelvin_color_format_info_map[state.color_format], state.dimensionality, filter, @@ -379,7 +384,8 @@ void pgraph_gl_bind_textures(NV2AState *d) binding->scale = pg->surface_scale_factor; } - apply_texture_parameters(binding, + apply_texture_parameters(r, + binding, &kelvin_color_format_info_map[state.color_format], state.dimensionality, filter, diff --git a/hw/xbox/nv2a/pgraph/vk/instance.c b/hw/xbox/nv2a/pgraph/vk/instance.c index f0804f7bdf0..088972cf268 100644 --- a/hw/xbox/nv2a/pgraph/vk/instance.c +++ b/hw/xbox/nv2a/pgraph/vk/instance.c @@ -540,14 +540,14 @@ static bool create_logical_device(PGRAPHState *pg, Error **errp) .enabled = &r->enabled_physical_device_features.n, \ .required = req, \ } - F(shaderClipDistance, true), - F(geometryShader, true), - F(shaderTessellationAndGeometryPointSize, true), F(depthClamp, true), - F(occlusionQueryPrecise, true), F(fillModeNonSolid, true), - F(wideLines, false), + F(geometryShader, true), + F(occlusionQueryPrecise, true), F(samplerAnisotropy, false), + F(shaderClipDistance, true), + F(shaderTessellationAndGeometryPointSize, true), + F(wideLines, false), #undef F // clang-format on }; diff --git a/hw/xbox/nv2a/pgraph/vk/texture.c b/hw/xbox/nv2a/pgraph/vk/texture.c index 0d6abc4dd38..b3686f87c0b 100644 --- a/hw/xbox/nv2a/pgraph/vk/texture.c +++ b/hw/xbox/nv2a/pgraph/vk/texture.c @@ -1360,8 +1360,9 @@ static void create_texture(PGRAPHState *pg, int texture_idx) GET_MASK(address, NV_PGRAPH_TEXADDRESS0_ADDRV)), .addressModeW = (state.dimensionality > 2) ? lookup_texture_address_mode( GET_MASK(address, NV_PGRAPH_TEXADDRESS0_ADDRP)) : 0, - .anisotropyEnable = r->enabled_physical_device_features.wideLines && - sampler_max_anisotropy > 1, + .anisotropyEnable = + r->enabled_physical_device_features.samplerAnisotropy && + sampler_max_anisotropy > 1, .maxAnisotropy = sampler_max_anisotropy, .borderColor = vk_border_color, .compareEnable = VK_FALSE,