From e72fed3faa93067e0fcf453258908af8c081125f Mon Sep 17 00:00:00 2001 From: Connor Abbott Date: Tue, 3 Dec 2024 18:02:59 -0500 Subject: [PATCH] ir3: Support min_lod tex source Use the .clp modifier. In order to fix dEQP-VK.glsl.texture_functions.textureoffsetclamp.* we need to add a workaround for an empirically-discovered problem. Part-of: --- src/freedreno/ir3/ir3_compiler_nir.c | 19 +++++++++---- src/freedreno/ir3/ir3_nir.c | 41 ++++++++++++++++++++++++++++ src/freedreno/ir3/ir3_print.c | 2 ++ 3 files changed, 57 insertions(+), 5 deletions(-) diff --git a/src/freedreno/ir3/ir3_compiler_nir.c b/src/freedreno/ir3/ir3_compiler_nir.c index beefe102ef6..3be738b5a52 100644 --- a/src/freedreno/ir3/ir3_compiler_nir.c +++ b/src/freedreno/ir3/ir3_compiler_nir.c @@ -3599,12 +3599,12 @@ static void emit_tex(struct ir3_context *ctx, nir_tex_instr *tex) { struct ir3_builder *b = &ctx->build; - struct ir3_instruction **dst, *sam, *src0[12], *src1[4]; + struct ir3_instruction **dst, *sam, *src0[12], *src1[5]; struct ir3_instruction *const *coord, *const *off, *const *ddx, *const *ddy; - struct ir3_instruction *lod, *compare, *proj, *sample_index; + struct ir3_instruction *lod, *compare, *proj, *sample_index, *min_lod; struct tex_src_info info = {0}; bool has_bias = false, has_lod = false, has_proj = false, has_off = false; - bool lod_zero = false; + bool lod_zero = false, has_min_lod = false; unsigned i, coords, flags, ncomp; unsigned nsrc0 = 0, nsrc1 = 0; type_t type; @@ -3613,7 +3613,7 @@ emit_tex(struct ir3_context *ctx, nir_tex_instr *tex) ncomp = tex->def.num_components; coord = off = ddx = ddy = NULL; - lod = proj = compare = sample_index = NULL; + lod = proj = compare = sample_index = min_lod = NULL; dst = ir3_get_def(ctx, &tex->def, ncomp); @@ -3658,6 +3658,10 @@ emit_tex(struct ir3_context *ctx, nir_tex_instr *tex) case nir_tex_src_ms_index: sample_index = ir3_get_src(ctx, &tex->src[i].src)[0]; break; + case nir_tex_src_min_lod: + min_lod = ir3_get_src(ctx, &tex->src[i].src)[0]; + has_min_lod = true; + break; case nir_tex_src_texture_offset: case nir_tex_src_sampler_offset: case nir_tex_src_texture_handle: @@ -3831,7 +3835,7 @@ emit_tex(struct ir3_context *ctx, nir_tex_instr *tex) * - lod * - bias */ - if (has_off | has_lod | has_bias) { + if (has_off | has_lod | has_bias | has_min_lod) { if (has_off) { unsigned off_coords = coords; if (tex->sampler_dim == GLSL_SAMPLER_DIM_CUBE) @@ -3845,6 +3849,11 @@ emit_tex(struct ir3_context *ctx, nir_tex_instr *tex) if (has_lod | has_bias) src1[nsrc1++] = lod; + + if (has_min_lod) { + src1[nsrc1++] = min_lod; + flags |= IR3_INSTR_CLP; + } } type = get_tex_dest_type(tex); diff --git a/src/freedreno/ir3/ir3_nir.c b/src/freedreno/ir3/ir3_nir.c index b917a23368b..309378c5778 100644 --- a/src/freedreno/ir3/ir3_nir.c +++ b/src/freedreno/ir3/ir3_nir.c @@ -13,6 +13,8 @@ #include "ir3_nir.h" #include "ir3_shader.h" +#include "nir_builtin_builder.h" + /* For use by binning_pass shaders, where const_state is const, but expected * to be already set up when we compiled the corresponding non-binning variant */ @@ -626,6 +628,44 @@ ir3_nir_lower_sparse_residency(nir_shader *shader) nir_metadata_control_flow, NULL); } +/* The hardware implementiation of min LOD clamp is broken when the given LOD + * clamp value (min_lod) is greater than levelCount - 1 (that is, when it would + * be clamped by the hardware to avoid accessing an out-of-bounds level). + * Instead of clamping the clamped LOD afterwards, it just returns zero. Because + * the LOD would always be clamped to levelCount - 1 in this case, we can just + * clamp min_lod to levelCount - 1 and get the same result while avoiding the + * hardware bug. + */ +static bool +ir3_nir_min_lod_workaround_cb(struct nir_builder *b, nir_tex_instr *tex, void *_data) +{ + int src_idx = nir_tex_instr_src_index(tex, nir_tex_src_min_lod); + if (src_idx < 0) + return false; + + b->cursor = nir_before_instr(&tex->instr); + + nir_def *level_count = nir_build_texture_query(b, tex, + nir_texop_query_levels, 1, + nir_type_uint32, + false, false); + + nir_def *src = tex->src[src_idx].src.ssa; + src = nir_fmin(b, src, nir_i2fN(b, nir_iadd_imm(b, level_count, -1), + src->bit_size)); + nir_src_rewrite(&tex->src[src_idx].src, src); + + return true; +} + +static bool +ir3_nir_min_lod_workaround(nir_shader *shader) +{ + return nir_shader_tex_pass( + shader, ir3_nir_min_lod_workaround_cb, + nir_metadata_control_flow, NULL); +} + void ir3_finalize_nir(struct ir3_compiler *compiler, const struct ir3_shader_nir_options *options, @@ -666,6 +706,7 @@ ir3_finalize_nir(struct ir3_compiler *compiler, OPT(s, nir_lower_load_const_to_scalar); NIR_PASS(_, s, ir3_nir_lower_sparse_residency); + NIR_PASS(_, s, ir3_nir_min_lod_workaround); if (compiler->array_index_add_half) OPT(s, ir3_nir_lower_array_sampler); diff --git a/src/freedreno/ir3/ir3_print.c b/src/freedreno/ir3/ir3_print.c index 0674385e94c..35e3c491a3a 100644 --- a/src/freedreno/ir3/ir3_print.c +++ b/src/freedreno/ir3/ir3_print.c @@ -194,6 +194,8 @@ print_instr_name(struct log_stream *stream, struct ir3_instruction *instr, mesa_log_stream_printf(stream, ".u"); if (instr->flags & IR3_INSTR_RCK) mesa_log_stream_printf(stream, ".rck"); + if (instr->flags & IR3_INSTR_CLP) + mesa_log_stream_printf(stream, ".clp"); if (instr->opc == OPC_LDC) mesa_log_stream_printf(stream, ".offset%d", instr->cat6.d); if (instr->opc == OPC_LDC_K)