From 9f9cde04ec9eda633f2b71cb38af480bc62062ae Mon Sep 17 00:00:00 2001 From: Faith Ekstrand Date: Mon, 16 Jun 2025 09:26:03 -0400 Subject: [PATCH] nir: Add a new load_input_attachment_coord intrinsic This hoists all the annoyance of figuring out the current pixel's input attachment coordinates to the driver. The pass still deals with all the annoyance of turning an image instruciton into a texture instruction but it gives the driver more control over the position. For most drivers, this will be something like ivec3(int(gl_FragCoord.xy), gl_Layer) or similar, some drivers need something more nuanced. Turnip, for instance, needs unscaled coordinates for some attachments and NVK doesn't really want gl_Layer or gl_ViewIndex for the layer. It's better to just have a new system value that drivers can make what they want. Reviewed-by: Alyssa Rosenzweig Part-of: --- src/compiler/nir/nir.h | 1 + src/compiler/nir/nir_divergence_analysis.c | 1 + src/compiler/nir/nir_intrinsics.py | 7 +++ .../nir/nir_lower_input_attachments.c | 49 ++++++++++++------- 4 files changed, 41 insertions(+), 17 deletions(-) diff --git a/src/compiler/nir/nir.h b/src/compiler/nir/nir.h index 381a9f901c8..f7d475d0497 100644 --- a/src/compiler/nir/nir.h +++ b/src/compiler/nir/nir.h @@ -5711,6 +5711,7 @@ typedef struct nir_lower_idiv_options { bool nir_lower_idiv(nir_shader *shader, const nir_lower_idiv_options *options); typedef struct nir_input_attachment_options { + bool use_ia_coord_intrin; bool use_fragcoord_sysval; bool use_layer_id_sysval; bool use_view_id_for_layer; diff --git a/src/compiler/nir/nir_divergence_analysis.c b/src/compiler/nir/nir_divergence_analysis.c index 0b2ecb37d80..1f411a7f26b 100644 --- a/src/compiler/nir/nir_divergence_analysis.c +++ b/src/compiler/nir/nir_divergence_analysis.c @@ -782,6 +782,7 @@ visit_intrinsic(nir_intrinsic_instr *instr, struct divergence_state *state) case nir_intrinsic_load_barycentric_coord_at_sample: case nir_intrinsic_load_barycentric_coord_at_offset: case nir_intrinsic_load_persp_center_rhw_ir3: + case nir_intrinsic_load_input_attachment_coord: case nir_intrinsic_interp_deref_at_offset: case nir_intrinsic_interp_deref_at_sample: case nir_intrinsic_interp_deref_at_centroid: diff --git a/src/compiler/nir/nir_intrinsics.py b/src/compiler/nir/nir_intrinsics.py index 813cbf06d1b..ad2f1bc3e6d 100644 --- a/src/compiler/nir/nir_intrinsics.py +++ b/src/compiler/nir/nir_intrinsics.py @@ -1125,6 +1125,13 @@ barycentric("coord_at_offset", 3, [2]) intrinsic("load_sample_pos_from_id", src_comp=[1], dest_comp=2, flags=[CAN_ELIMINATE, CAN_REORDER]) +# Load input attachment coordinate: +# +# Takes an input attachment index and returns an ivec with the position in +# input attachment space in .xy and the input attachment array index in .z. +intrinsic("load_input_attachment_coord", src_comp=[1], dest_comp=3, + bit_sizes=[32], flags=[CAN_ELIMINATE, CAN_REORDER]) + # Demote a subset of samples given by a specified sample mask. This acts like a # per-sample demote, or an inverted accumulating gl_SampleMask write. intrinsic("demote_samples", src_comp=[1]) diff --git a/src/compiler/nir/nir_lower_input_attachments.c b/src/compiler/nir/nir_lower_input_attachments.c index b4cd7f7ad23..dd10a6839b8 100644 --- a/src/compiler/nir/nir_lower_input_attachments.c +++ b/src/compiler/nir/nir_lower_input_attachments.c @@ -83,6 +83,29 @@ load_layer_id(nir_builder *b, const nir_input_attachment_options *options) return nir_load_var(b, layer_id); } +static nir_def * +load_coord(nir_builder *b, nir_deref_instr *deref, + const nir_input_attachment_options *options) +{ + if (options->use_ia_coord_intrin) { + nir_def *index; + if (deref->deref_type == nir_deref_type_array) { + ASSERTED nir_deref_instr *parent = nir_deref_instr_parent(deref); + assert(parent->deref_type == nir_deref_type_var); + index = deref->arr.index.ssa; + } else { + assert(deref->deref_type == nir_deref_type_var); + index = nir_imm_int(b, 0); + } + + return nir_load_input_attachment_coord(b, index); + } else { + nir_def *pos = nir_f2i32(b, load_frag_coord(b, deref, options)); + nir_def *layer = load_layer_id(b, options); + return nir_vec3(b, nir_channel(b, pos, 0), nir_channel(b, pos, 1), layer); + } +} + static bool try_lower_input_load(nir_builder *b, nir_intrinsic_instr *load, const nir_input_attachment_options *options) @@ -99,14 +122,10 @@ try_lower_input_load(nir_builder *b, nir_intrinsic_instr *load, b->cursor = nir_instr_remove(&load->instr); - nir_def *frag_coord = load_frag_coord(b, deref, options); - frag_coord = nir_f2i32(b, frag_coord); - nir_def *offset = nir_trim_vector(b, load->src[1].ssa, 2); - nir_def *pos = nir_iadd(b, frag_coord, offset); - - nir_def *layer = load_layer_id(b, options); - nir_def *coord = - nir_vec3(b, nir_channel(b, pos, 0), nir_channel(b, pos, 1), layer); + nir_def *offset = nir_vec3(b, nir_channel(b, load->src[1].ssa, 0), + nir_channel(b, load->src[1].ssa, 1), + nir_imm_int(b, 0)); + nir_def *coord = nir_iadd(b, load_coord(b, deref, options), offset); nir_tex_instr *tex = nir_tex_instr_create(b->shader, 3 + multisampled); @@ -174,15 +193,11 @@ try_lower_input_texop(nir_builder *b, nir_tex_instr *tex, b->cursor = nir_before_instr(&tex->instr); - nir_def *frag_coord = load_frag_coord(b, deref, options); - frag_coord = nir_f2i32(b, frag_coord); - - nir_def *offset = nir_trim_vector(b, tex->src[coord_src_idx].src.ssa, 2); - nir_def *pos = nir_iadd(b, frag_coord, offset); - - nir_def *layer = load_layer_id(b, options); - nir_def *coord = nir_vec3(b, nir_channel(b, pos, 0), - nir_channel(b, pos, 1), layer); + nir_def *offset = tex->src[coord_src_idx].src.ssa; + offset = nir_vec3(b, nir_channel(b, offset, 0), + nir_channel(b, offset, 1), + nir_imm_int(b, 0)); + nir_def *coord = nir_iadd(b, load_coord(b, deref, options), offset); tex->coord_components = 3;