diff --git a/src/broadcom/compiler/meson.build b/src/broadcom/compiler/meson.build index 9380841cace..a98545154e2 100644 --- a/src/broadcom/compiler/meson.build +++ b/src/broadcom/compiler/meson.build @@ -25,6 +25,7 @@ libbroadcom_compiler_files = files( 'v3d_nir_lower_line_smooth.c', 'v3d_nir_lower_load_store_bitsize.c', 'v3d_nir_lower_logic_ops.c', + 'v3d_nir_lower_null_descriptors.c', 'v3d_nir_lower_scratch.c', 'v3d_nir_lower_txf_ms.c', 'v3d_nir_lower_load_output.c', diff --git a/src/broadcom/compiler/nir_to_vir.c b/src/broadcom/compiler/nir_to_vir.c index 43d5f13206e..9e606e8b419 100644 --- a/src/broadcom/compiler/nir_to_vir.c +++ b/src/broadcom/compiler/nir_to_vir.c @@ -896,7 +896,19 @@ ntq_get_alu_src(struct v3d_compile *c, nir_alu_instr *instr, static struct qreg ntq_minify(struct v3d_compile *c, struct qreg size, struct qreg level) { - return vir_MAX(c, vir_SHR(c, size, level), vir_uniform_ui(c, 1)); + /* For valid textures, a minified level is at least 1. + * However, with VK_KHR_robustness2 nullDescriptor we represent null + * resources with a base size of 0. + */ + struct qreg minified = vir_MAX(c, vir_SHR(c, size, level), + vir_uniform_ui(c, 1)); + + if (!c->key->null_descriptor) + return minified; + + vir_set_pf(c, vir_MOV_dest(c, vir_nop_reg(), size), V3D_QPU_PF_PUSHZ); + return vir_MOV(c, vir_SEL(c, V3D_QPU_COND_IFNA, minified, + vir_uniform_ui(c, 0))); } static void diff --git a/src/broadcom/compiler/v3d_compiler.h b/src/broadcom/compiler/v3d_compiler.h index b4b289636c0..26d6924b0bd 100644 --- a/src/broadcom/compiler/v3d_compiler.h +++ b/src/broadcom/compiler/v3d_compiler.h @@ -417,6 +417,7 @@ struct v3d_key { bool robust_storage_access; bool robust_image_access; bool robust_image_access_2; + bool null_descriptor; }; struct v3d_fs_key { @@ -1229,6 +1230,7 @@ bool v3d_nir_lower_logic_ops(nir_shader *s, struct v3d_compile *c); bool v3d_nir_lower_scratch(nir_shader *s); bool v3d_nir_lower_txf_ms(nir_shader *s); bool v3d_nir_lower_image_load_store(nir_shader *s, struct v3d_compile *c); +bool v3d_nir_lower_null_descriptors(nir_shader *s); bool v3d_nir_lower_global_2x32(nir_shader *s); bool v3d_nir_lower_load_store_bitsize(nir_shader *s); bool v3d_nir_lower_algebraic(struct nir_shader *shader, const struct v3d_compile *c); diff --git a/src/broadcom/compiler/v3d_nir_lower_null_descriptors.c b/src/broadcom/compiler/v3d_nir_lower_null_descriptors.c new file mode 100644 index 00000000000..45cd6fd2d81 --- /dev/null +++ b/src/broadcom/compiler/v3d_nir_lower_null_descriptors.c @@ -0,0 +1,146 @@ +/* + * Copyright © 2026 Raspberry Pi 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 "v3d_compiler.h" + +#include "compiler/nir/nir.h" +#include "compiler/nir/nir_builder.h" +#include "compiler/nir/nir_builtin_builder.h" + +static nir_def * +get_is_null(nir_builder *b, nir_instr *instr, nir_def **def) +{ + *def = NULL; + + if (instr->type == nir_instr_type_tex) { + nir_tex_instr *tex = nir_instr_as_tex(instr); + *def = &tex->def; + nir_def *size = nir_build_texture_query(b, tex, nir_texop_txs, + 1, nir_type_uint32, + false, true); + if (size->num_components > 1) + size = nir_channel(b, size, 0); + + return nir_ieq_imm(b, size, 0); + } + + if (instr->type == nir_instr_type_intrinsic) { + nir_intrinsic_instr *intr = nir_instr_as_intrinsic(instr); + + if (nir_intrinsic_infos[intr->intrinsic].has_dest) + *def = &intr->def; + + nir_src *index_src = nir_get_io_index_src(intr); + + switch (intr->intrinsic) { + case nir_intrinsic_load_ubo: + return nir_ieq_imm(b, nir_get_ubo_size(b, 32, index_src->ssa), 0); + + case nir_intrinsic_load_ssbo: + case nir_intrinsic_ssbo_atomic: + case nir_intrinsic_ssbo_atomic_swap: + case nir_intrinsic_store_ssbo: + return nir_ieq_imm(b, nir_get_ssbo_size(b, 32, index_src->ssa), 0); + + case nir_intrinsic_image_load: + case nir_intrinsic_image_atomic: + case nir_intrinsic_image_atomic_swap: + case nir_intrinsic_image_store: { + nir_def *size = nir_image_size( + b, 1, 32, + index_src->ssa, nir_imm_int(b, 0), + .image_array = nir_intrinsic_image_array(intr), + .image_dim = nir_intrinsic_image_dim(intr)); + return nir_ieq_imm(b, size, 0); + } + + default: + return NULL; + } + } + + return NULL; +} + +static bool +lower_instr(nir_builder *b, nir_instr *instr, UNUSED void *data) +{ + b->cursor = nir_before_instr(instr); + + nir_def *def; + nir_def *is_null = get_is_null(b, instr, &def); + if (!is_null) + return false; + + if (instr->type == nir_instr_type_tex) { + /* We use bcsel instead of if/else+phi for texture ops because + * it has proven to lead to better performance than if/else + * blocks. When null descriptor functionality is enabled and a + * null texture is bound by the application, the driver will + * program a dummy BO to ensure valid access from the texture + * instruction and the bcsel below will discard the result and + * replace it with 0. + */ + b->cursor = nir_after_instr(instr); + nir_def *zero = nir_imm_zero(b, def->num_components, + def->bit_size); + nir_def *result = nir_bcsel(b, is_null, zero, def); + + nir_def_rewrite_uses_after(def, result); + + return true; + } + + nir_def *zero = NULL; + nir_if *nif = nir_push_if(b, nir_inot(b, is_null)); + nir_instr_remove(instr); + nir_builder_instr_insert(b, instr); + if (def) { + nir_push_else(b, nif); + zero = nir_imm_zero(b, def->num_components, def->bit_size); + } + nir_pop_if(b, nif); + + if (def) { + nir_def *phi = nir_if_phi(b, def, zero); + + /* We can't use nir_def_rewrite_uses_after on phis, + * so use the global version and fixup the phi manually. + */ + nir_def_rewrite_uses(def, phi); + + nir_instr *phi_instr = nir_def_instr(phi); + nir_phi_instr *phi_as_phi = nir_instr_as_phi(phi_instr); + nir_phi_src *phi_src = nir_phi_get_src_from_block(phi_as_phi, instr->block); + nir_src_rewrite(&phi_src->src, def); + } + + return true; +} + +bool +v3d_nir_lower_null_descriptors(nir_shader *s) +{ + return nir_shader_instructions_pass(s, lower_instr, + nir_metadata_none, NULL); +} diff --git a/src/broadcom/compiler/vir.c b/src/broadcom/compiler/vir.c index 866cdcb2673..939d7f10974 100644 --- a/src/broadcom/compiler/vir.c +++ b/src/broadcom/compiler/vir.c @@ -1924,6 +1924,9 @@ v3d_attempt_compile(struct v3d_compile *c) NIR_PASS(_, c->s, v3d_nir_lower_image_load_store, c); + if (c->key->null_descriptor) + NIR_PASS(_, c->s, v3d_nir_lower_null_descriptors); + NIR_PASS(_, c->s, nir_opt_idiv_const, 8); nir_lower_idiv_options idiv_options = { .allow_fp16 = true,