diff --git a/src/asahi/compiler/agx_compile.c b/src/asahi/compiler/agx_compile.c index 0a0ac758fc3..fb2cc3825c4 100644 --- a/src/asahi/compiler/agx_compile.c +++ b/src/asahi/compiler/agx_compile.c @@ -2007,23 +2007,33 @@ agx_emit_alu(agx_builder *b, nir_alu_instr *instr) } static enum agx_lod_mode -agx_lod_mode_for_nir(nir_texop op, bool biased, bool lod_is_zero) +agx_lod_mode_for_nir(nir_texop op, bool biased, bool min_lod, bool lod_is_zero) { switch (op) { case nir_texop_tex: case nir_texop_tg4: + /* We could support this for tex, but it's never actually seen because tex + * is always turned into txb to implement sampler LOD bias in Vulkan. + */ + assert(!min_lod && "unimplemented"); + return AGX_LOD_MODE_AUTO_LOD; case nir_texop_txb: - return AGX_LOD_MODE_AUTO_LOD_BIAS; + return min_lod ? AGX_LOD_MODE_AUTO_LOD_BIAS_MIN + : AGX_LOD_MODE_AUTO_LOD_BIAS; case nir_texop_lod: + assert(!min_lod); return biased ? AGX_LOD_MODE_AUTO_LOD_BIAS : AGX_LOD_MODE_AUTO_LOD; case nir_texop_txd: - return AGX_LOD_MODE_LOD_GRAD; + return min_lod ? AGX_LOD_MODE_LOD_GRAD_MIN : AGX_LOD_MODE_LOD_GRAD; case nir_texop_txl: + assert(!min_lod); return AGX_LOD_MODE_LOD_MIN; case nir_texop_txf: + assert(!min_lod); return lod_is_zero ? AGX_LOD_MODE_AUTO_LOD : AGX_LOD_MODE_LOD_MIN; case nir_texop_txf_ms: + assert(!min_lod); assert(lod_is_zero && "no mipmapping"); return AGX_LOD_MODE_AUTO_LOD; default: @@ -2055,7 +2065,8 @@ agx_emit_tex(agx_builder *b, nir_tex_instr *instr) agx_index coords = agx_null(), bindless = agx_immediate(0), texture = agx_immediate(instr->texture_index), sampler = agx_immediate(0), lod = agx_immediate(0), - compare = agx_null(), packed_offset = agx_null(); + compare = agx_null(), packed_offset = agx_null(), + min_lod = agx_null(); bool lod_is_zero = true; @@ -2078,6 +2089,11 @@ agx_emit_tex(agx_builder *b, nir_tex_instr *instr) nir_src_as_uint(instr->src[i].src) == 0; break; + case nir_tex_src_min_lod: + assert(index.size == AGX_SIZE_16); + min_lod = index; + break; + case nir_tex_src_comparator: assert(index.size == AGX_SIZE_32); compare = index; @@ -2099,20 +2115,37 @@ agx_emit_tex(agx_builder *b, nir_tex_instr *instr) int y_idx = nir_tex_instr_src_index(instr, nir_tex_src_ddy); assert(y_idx >= 0 && "we only handle gradients"); + int min_idx = nir_tex_instr_src_index(instr, nir_tex_src_min_lod); + bool has_min = min_idx >= 0; + agx_index min; + unsigned n = nir_tex_instr_src_size(instr, y_idx); assert((n == 2 || n == 3) && "other sizes not supported"); agx_index index2 = agx_src_index(&instr->src[y_idx].src); + if (has_min) { + min = agx_src_index(&instr->src[min_idx].src); + + /* Undef extend to 32-bit since our IR is iffy */ + min = agx_vec2(b, min, agx_undef(AGX_SIZE_16)); + min.channels_m1--; + min.size = AGX_SIZE_32; + } + /* We explicitly don't cache about the split cache for this */ - lod = agx_vec_temp(b->shader, AGX_SIZE_32, 2 * n); - agx_instr *I = agx_collect_to(b, lod, 2 * n); + unsigned chans = (2 * n) + (has_min ? 1 : 0); + lod = agx_vec_temp(b->shader, AGX_SIZE_32, chans); + agx_instr *I = agx_collect_to(b, lod, chans); for (unsigned i = 0; i < n; ++i) { I->src[(2 * i) + 0] = agx_emit_extract(b, index, i); I->src[(2 * i) + 1] = agx_emit_extract(b, index2, i); } + if (has_min) + I->src[2 * n] = min; + break; } @@ -2127,11 +2160,14 @@ agx_emit_tex(agx_builder *b, nir_tex_instr *instr) enum agx_lod_mode lod_mode = agx_lod_mode_for_nir( instr->op, nir_tex_instr_src_index(instr, nir_tex_src_bias) >= 0, - lod_is_zero); + nir_tex_instr_src_index(instr, nir_tex_src_min_lod) >= 0, lod_is_zero); if (lod_mode == AGX_LOD_MODE_AUTO_LOD) { /* Ignored logically but asserted 0 */ lod = agx_immediate(0); + } else if (lod_mode == AGX_LOD_MODE_AUTO_LOD_BIAS_MIN) { + /* Combine min with lod */ + lod = agx_vec2(b, lod, min_lod); } agx_index dst = agx_def_index(&instr->def); diff --git a/src/asahi/compiler/agx_compiler.h b/src/asahi/compiler/agx_compiler.h index c48897b73da..f457c5e231b 100644 --- a/src/asahi/compiler/agx_compiler.h +++ b/src/asahi/compiler/agx_compiler.h @@ -296,9 +296,11 @@ enum agx_lod_mode { AGX_LOD_MODE_AUTO_LOD_BIAS_UNIFORM = 1, AGX_LOD_MODE_LOD_MIN_UNIFORM = 2, AGX_LOD_MODE_AUTO_LOD_BIAS = 5, - AGX_LOD_MODE_LOD_MIN = 6, AGX_LOD_MODE_LOD_GRAD = 4, - AGX_LOD_MODE_LOD_GRAD_MIN = 12 + AGX_LOD_MODE_LOD_MIN = 6, + AGX_LOD_MODE_AUTO_LOD_BIAS_MIN_UNIFORM = 9, + AGX_LOD_MODE_LOD_GRAD_MIN = 12, + AGX_LOD_MODE_AUTO_LOD_BIAS_MIN = 13, }; /* Forward declare for branch target */ diff --git a/src/asahi/compiler/agx_validate.c b/src/asahi/compiler/agx_validate.c index 31b17a223b4..eb2c5b64969 100644 --- a/src/asahi/compiler/agx_validate.c +++ b/src/asahi/compiler/agx_validate.c @@ -298,23 +298,32 @@ agx_read_registers(const agx_instr *I, unsigned s) return agx_coordinate_registers(I); } else if (s == 1) { /* LOD */ - if (I->lod_mode == AGX_LOD_MODE_LOD_GRAD) { + if (I->lod_mode == AGX_LOD_MODE_LOD_GRAD || + I->lod_mode == AGX_LOD_MODE_LOD_GRAD_MIN) { + + /* Technically only 16-bit but we model as 32-bit to keep the IR + * simple, since the gradient is otherwise 32-bit. + */ + unsigned min = I->lod_mode == AGX_LOD_MODE_LOD_GRAD_MIN ? 2 : 0; + switch (I->dim) { case AGX_DIM_1D: case AGX_DIM_1D_ARRAY: - return 2 * 2 * 1; + return (2 * 2 * 1) + min; case AGX_DIM_2D: case AGX_DIM_2D_ARRAY: case AGX_DIM_2D_MS_ARRAY: case AGX_DIM_2D_MS: - return 2 * 2 * 2; + return (2 * 2 * 2) + min; case AGX_DIM_CUBE: case AGX_DIM_CUBE_ARRAY: case AGX_DIM_3D: - return 2 * 2 * 3; + return (2 * 2 * 3) + min; } unreachable("Invalid texture dimension"); + } else if (I->lod_mode == AGX_LOD_MODE_AUTO_LOD_BIAS_MIN) { + return 2; } else { return 1; } diff --git a/src/asahi/lib/agx_nir_lower_texture.c b/src/asahi/lib/agx_nir_lower_texture.c index e6b40a27a73..99db4abb953 100644 --- a/src/asahi/lib/agx_nir_lower_texture.c +++ b/src/asahi/lib/agx_nir_lower_texture.c @@ -747,6 +747,7 @@ agx_nir_lower_texture(nir_shader *s) [nir_tex_src_lod] = {true, 16}, [nir_tex_src_bias] = {true, 16}, [nir_tex_src_ms_index] = {true, 16}, + [nir_tex_src_min_lod] = {true, 16}, [nir_tex_src_texture_offset] = {true, 16}, [nir_tex_src_sampler_offset] = {true, 16}, };