From c5fdc6faa8ca8db16dafedac2a3b32e7786522ec Mon Sep 17 00:00:00 2001 From: Faith Ekstrand Date: Thu, 4 Dec 2025 11:37:51 -0500 Subject: [PATCH] pan: Add a pass to resize I/O load/stores as needed by the varying layout Reviewed-by: Lorenzo Rossi Acked-by: Eric R. Smith Part-of: --- src/panfrost/compiler/meson.build | 1 + src/panfrost/compiler/pan_nir.h | 3 + .../compiler/pan_nir_resize_varying_io.c | 133 ++++++++++++++++++ 3 files changed, 137 insertions(+) create mode 100644 src/panfrost/compiler/pan_nir_resize_varying_io.c diff --git a/src/panfrost/compiler/meson.build b/src/panfrost/compiler/meson.build index 928ba06dea4..3966b69710f 100644 --- a/src/panfrost/compiler/meson.build +++ b/src/panfrost/compiler/meson.build @@ -18,6 +18,7 @@ libpanfrost_compiler_files = files( 'pan_nir_lower_texel_buffer_index.c', 'pan_nir_lower_vertex_id.c', 'pan_nir_lower_xfb.c', + 'pan_nir_resize_varying_io.c', ) subdir('bifrost') diff --git a/src/panfrost/compiler/pan_nir.h b/src/panfrost/compiler/pan_nir.h index 3f08686492b..b699e97f9ff 100644 --- a/src/panfrost/compiler/pan_nir.h +++ b/src/panfrost/compiler/pan_nir.h @@ -84,4 +84,7 @@ uint32_t pan_nir_collect_noperspective_varyings_fs(nir_shader *s); void pan_nir_collect_varyings(nir_shader *s, struct pan_shader_info *info); +bool pan_nir_resize_varying_io(nir_shader *nir, + const struct pan_varying_layout *varying_layout); + #endif /* __PAN_NIR_H__ */ diff --git a/src/panfrost/compiler/pan_nir_resize_varying_io.c b/src/panfrost/compiler/pan_nir_resize_varying_io.c new file mode 100644 index 00000000000..506d473fa97 --- /dev/null +++ b/src/panfrost/compiler/pan_nir_resize_varying_io.c @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2025 Collabora, Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "pan_nir.h" +#include "nir_builder.h" + +static bool +resize_io_intr(nir_builder *b, nir_intrinsic_instr *intr, void *data) +{ + const struct pan_varying_layout *varying_layout = data; + + bool is_load; + switch (intr->intrinsic) { + case nir_intrinsic_store_output: + case nir_intrinsic_store_per_view_output: + if (b->shader->info.stage != MESA_SHADER_VERTEX) + return false; + is_load = false; + break; + + case nir_intrinsic_load_input: + case nir_intrinsic_load_interpolated_input: + if (b->shader->info.stage != MESA_SHADER_FRAGMENT) + return false; + is_load = true; + break; + + default: + return false; + } + + nir_io_semantics sem = nir_intrinsic_io_semantics(intr); + const struct pan_varying_slot *slot = + pan_varying_layout_find_slot(varying_layout, sem.location); + if (slot == NULL) { + if (is_load) { + b->cursor = nir_after_instr(&intr->instr); + + nir_def *zero = nir_imm_zero(b, intr->def.num_components, + intr->def.bit_size); + nir_def_replace(&intr->def, zero); + } else { + assert(!"We should never have a mismatch on outputs"); + nir_instr_remove(&intr->instr); + } + return true; + } + + nir_alu_type data_type; + if (is_load) { + data_type = nir_intrinsic_dest_type(intr); + assert(intr->def.bit_size == nir_alu_type_get_type_size(data_type)); + } else { + data_type = nir_intrinsic_src_type(intr); + assert(nir_src_bit_size(intr->src[0]) == + nir_alu_type_get_type_size(data_type)); + } + + const unsigned slot_bit_size = nir_alu_type_get_type_size(slot->alu_type); + + /* We trust the base type in the shader and only adjust the bit size */ + const nir_alu_type slot_type = + nir_alu_type_get_base_type(data_type) | slot_bit_size; + + if (slot_bit_size == nir_alu_type_get_type_size(data_type)) { + if (!sem.medium_precision) + return false; + + /* There's nothing to actually lower but we still want to smash off + * mediump so the back-end doesn't screw anything up on us. + * + * TODO: This is a hack to work around the back-end. It really + * shouldn't care and should just do whatever load it's told. + */ + sem.medium_precision = false; + nir_intrinsic_set_io_semantics(intr, sem); + return true; + } + + sem.medium_precision = false; + nir_intrinsic_set_io_semantics(intr, sem); + + if (is_load) { + b->cursor = nir_after_instr(&intr->instr); + + intr->def.bit_size = slot_bit_size; + nir_intrinsic_set_dest_type(intr, slot_type); + + data = nir_type_convert(b, &intr->def, slot_type, data_type, + nir_rounding_mode_undef); + + nir_def_rewrite_uses_after(&intr->def, data); + } else { + b->cursor = nir_before_instr(&intr->instr); + + nir_def *data = nir_type_convert(b, intr->src[0].ssa, data_type, + slot_type, nir_rounding_mode_undef); + + nir_src_rewrite(&intr->src[0], data); + nir_intrinsic_set_src_type(intr, slot_type); + } + + return true; +} + +bool +pan_nir_resize_varying_io(nir_shader *nir, + const struct pan_varying_layout *varying_layout) +{ + return nir_shader_intrinsics_pass(nir, resize_io_intr, + nir_metadata_control_flow, + (void *)varying_layout); +}