From e922c2cabcdb7410932d6a101710e62799f0df2a Mon Sep 17 00:00:00 2001 From: Emma Anholt Date: Thu, 6 Feb 2025 20:22:37 +0000 Subject: [PATCH] nir,spirv: Add support for SPV_QCOM_image_processing. Initial work was done by Mark Collins, which I significantly rewrote. Signed-off-by: Mark Collins Part-of: --- src/compiler/nir/nir.c | 32 +++++++++- src/compiler/nir/nir.h | 34 ++++++++++- src/compiler/nir/nir_divergence_analysis.c | 2 + .../nir/nir_lower_non_uniform_access.c | 2 + src/compiler/nir/nir_opt_gcm.c | 2 + src/compiler/nir/nir_print.c | 37 ++++++++++++ src/compiler/nir/nir_validate.c | 19 +++++- src/compiler/spirv/spirv_to_nir.c | 58 ++++++++++++++++++- src/compiler/spirv/vtn_variables.c | 4 ++ 9 files changed, 185 insertions(+), 5 deletions(-) diff --git a/src/compiler/nir/nir.c b/src/compiler/nir/nir.c index 2a6d1b856ac..6e1122155c9 100644 --- a/src/compiler/nir/nir.c +++ b/src/compiler/nir/nir.c @@ -3435,6 +3435,12 @@ nir_tex_instr_result_size(const nir_tex_instr *instr) case nir_texop_custom_border_color_agx: return 4; + case nir_texop_sample_weighted_qcom: + case nir_texop_box_filter_qcom: + case nir_texop_block_match_sad_qcom: + case nir_texop_block_match_ssd_qcom: + return 4; + default: if (instr->is_shadow && instr->is_new_style_shadow) return 1; @@ -3473,6 +3479,10 @@ nir_tex_instr_is_query(const nir_tex_instr *instr) case nir_texop_samples_identical: case nir_texop_fragment_mask_fetch_amd: case nir_texop_fragment_fetch_amd: + case nir_texop_sample_weighted_qcom: + case nir_texop_box_filter_qcom: + case nir_texop_block_match_sad_qcom: + case nir_texop_block_match_ssd_qcom: return false; default: UNREACHABLE("Invalid texture opcode"); @@ -3499,6 +3509,7 @@ nir_tex_instr_src_type(const nir_tex_instr *instr, unsigned src) { switch (instr->src[src].src_type) { case nir_tex_src_coord: + case nir_tex_src_ref_coord: switch (instr->op) { case nir_texop_txf: case nir_texop_txf_ms: @@ -3507,6 +3518,8 @@ nir_tex_instr_src_type(const nir_tex_instr *instr, unsigned src) case nir_texop_samples_identical: case nir_texop_fragment_fetch_amd: case nir_texop_fragment_mask_fetch_amd: + case nir_texop_block_match_sad_qcom: + case nir_texop_block_match_ssd_qcom: return nir_type_int; default: @@ -3535,6 +3548,7 @@ nir_tex_instr_src_type(const nir_tex_instr *instr, unsigned src) case nir_tex_src_ddy: case nir_tex_src_backend1: case nir_tex_src_backend2: + case nir_tex_src_box_size: return nir_type_float; case nir_tex_src_offset: @@ -3552,6 +3566,11 @@ nir_tex_instr_src_type(const nir_tex_instr *instr, unsigned src) case nir_tex_src_sampler_offset: case nir_tex_src_texture_handle: case nir_tex_src_sampler_handle: + case nir_tex_src_texture_2_deref: + case nir_tex_src_sampler_2_deref: + case nir_tex_src_texture_2_handle: + case nir_tex_src_sampler_2_handle: + case nir_tex_src_block_size: return nir_type_uint; case nir_num_tex_src_types: @@ -3564,13 +3583,24 @@ nir_tex_instr_src_type(const nir_tex_instr *instr, unsigned src) unsigned nir_tex_instr_src_size(const nir_tex_instr *instr, unsigned src) { - if (instr->src[src].src_type == nir_tex_src_coord) + if (instr->src[src].src_type == nir_tex_src_coord ) return instr->coord_components; /* The MCS value is expected to be a vec4 returned by a txf_ms_mcs_intel */ if (instr->src[src].src_type == nir_tex_src_ms_mcs_intel) return 4; + if (instr->src[src].src_type == nir_tex_src_box_size) + return 2; + + /* These are vec2s at the spirv level, but get lowered to 16_16 packed values + * in the backend, so they don't have a single known size + */ + if (instr->src[src].src_type == nir_tex_src_ref_coord || + instr->src[src].src_type == nir_tex_src_block_size) { + return 0; + } + if (instr->src[src].src_type == nir_tex_src_ddx || instr->src[src].src_type == nir_tex_src_ddy) { diff --git a/src/compiler/nir/nir.h b/src/compiler/nir/nir.h index fd1c4797a04..e600b13ee64 100644 --- a/src/compiler/nir/nir.h +++ b/src/compiler/nir/nir.h @@ -2384,6 +2384,15 @@ typedef enum nir_tex_src_type { /** Second backend-specific vec4 tex src argument, see nir_tex_src_backend1. */ nir_tex_src_backend2, + /** VK_QCOM_image_processing sources */ + nir_tex_src_ref_coord, + nir_tex_src_texture_2_deref, + nir_tex_src_sampler_2_deref, + nir_tex_src_texture_2_handle, + nir_tex_src_sampler_2_handle, + nir_tex_src_block_size, + nir_tex_src_box_size, + nir_num_tex_src_types } nir_tex_src_type; @@ -2450,6 +2459,26 @@ typedef enum nir_texop { nir_texop_tex_type_nv, /** Maps to TXQ.SAMPLER_POS */ nir_texop_sample_pos_nv, + /** + * Returns the weighted average of a region of texels in the texture, using + * the filter kernel sampled from ref_texture. (VK_QCOM_image_processing) + */ + nir_texop_sample_weighted_qcom, + /** + * Returns the result of a weighted average of the texels in a box of size + * box_size centered at coord. (VK_QCOM_image_processing) + */ + nir_texop_box_filter_qcom, + /** + * Returns the result of SAD/SSD block matching on 2D textures. + * (VK_QCOM_image_processing) + * + * The textures are always 2D dim, and the coord and ref_coord texture + * sources are ivec2s. The size of the block is specified in the block_size + * texture source (uvec2 from SPIRV, packed u32 in the backend) + */ + nir_texop_block_match_sad_qcom, + nir_texop_block_match_ssd_qcom, } nir_texop; /** Represents a texture instruction */ @@ -2493,7 +2522,10 @@ typedef struct nir_tex_instr { /** Number of sources */ unsigned num_srcs; - /** Number of components in the coordinate, if any */ + /** Number of components in the coordinate, if any. + * + * This applies to the nir_tex_src_coord and nir_tex_src_ref_coord src types. + */ unsigned coord_components; /** True if the texture instruction acts on an array texture */ diff --git a/src/compiler/nir/nir_divergence_analysis.c b/src/compiler/nir/nir_divergence_analysis.c index c4340584fd4..60f7880fe0b 100644 --- a/src/compiler/nir/nir_divergence_analysis.c +++ b/src/compiler/nir/nir_divergence_analysis.c @@ -1057,12 +1057,14 @@ visit_tex(nir_tex_instr *instr, struct divergence_state *state) for (unsigned i = 0; i < instr->num_srcs; i++) { switch (instr->src[i].src_type) { case nir_tex_src_sampler_deref: + case nir_tex_src_sampler_2_deref: case nir_tex_src_sampler_handle: case nir_tex_src_sampler_offset: is_divergent |= src_divergent(instr->src[i].src, state) && instr->sampler_non_uniform; break; case nir_tex_src_texture_deref: + case nir_tex_src_texture_2_deref: case nir_tex_src_texture_handle: case nir_tex_src_texture_offset: is_divergent |= src_divergent(instr->src[i].src, state) && diff --git a/src/compiler/nir/nir_lower_non_uniform_access.c b/src/compiler/nir/nir_lower_non_uniform_access.c index f1834c08f51..19e48d6397d 100644 --- a/src/compiler/nir/nir_lower_non_uniform_access.c +++ b/src/compiler/nir/nir_lower_non_uniform_access.c @@ -221,6 +221,7 @@ lower_non_uniform_tex_access(struct nu_state *state, nir_tex_instr *tex, case nir_tex_src_texture_offset: case nir_tex_src_texture_handle: case nir_tex_src_texture_deref: + case nir_tex_src_texture_2_deref: if (!tex->texture_non_uniform) continue; if (!(opts->types & nir_lower_non_uniform_texture_access)) @@ -232,6 +233,7 @@ lower_non_uniform_tex_access(struct nu_state *state, nir_tex_instr *tex, case nir_tex_src_sampler_offset: case nir_tex_src_sampler_handle: case nir_tex_src_sampler_deref: + case nir_tex_src_sampler_2_deref: if (!tex->sampler_non_uniform) continue; if (!(opts->types & nir_lower_non_uniform_texture_access)) diff --git a/src/compiler/nir/nir_opt_gcm.c b/src/compiler/nir/nir_opt_gcm.c index d2b92c11b75..d6e8a038e69 100644 --- a/src/compiler/nir/nir_opt_gcm.c +++ b/src/compiler/nir/nir_opt_gcm.c @@ -334,10 +334,12 @@ gcm_pin_instructions(nir_function_impl *impl, struct gcm_state *state) nir_tex_src *src = &tex->src[i]; switch (src->src_type) { case nir_tex_src_texture_deref: + case nir_tex_src_texture_2_deref: if (!tex->texture_non_uniform && !is_binding_uniform(src->src)) instr->pass_flags = GCM_INSTR_PINNED; break; case nir_tex_src_sampler_deref: + case nir_tex_src_sampler_2_deref: if (!tex->sampler_non_uniform && !is_binding_uniform(src->src)) instr->pass_flags = GCM_INSTR_PINNED; break; diff --git a/src/compiler/nir/nir_print.c b/src/compiler/nir/nir_print.c index 1d5a00fad14..5c9c90582f1 100644 --- a/src/compiler/nir/nir_print.c +++ b/src/compiler/nir/nir_print.c @@ -1188,6 +1188,10 @@ vulkan_descriptor_type_name(VkDescriptorType type) return "inline-UBO"; case VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR: return "accel-struct"; + case VK_DESCRIPTOR_TYPE_SAMPLE_WEIGHT_IMAGE_QCOM: + return "sample-weight-image"; + case VK_DESCRIPTOR_TYPE_BLOCK_MATCH_IMAGE_QCOM: + return "block-match-image"; default: return "unknown"; } @@ -1903,6 +1907,18 @@ print_tex_instr(nir_tex_instr *instr, print_state *state) case nir_texop_sample_pos_nv: fprintf(fp, "sample_pos_nv "); break; + case nir_texop_sample_weighted_qcom: + fprintf(fp, "sample_weighted_qcom "); + break; + case nir_texop_box_filter_qcom: + fprintf(fp, "box_filter_qcom "); + break; + case nir_texop_block_match_sad_qcom: + fprintf(fp, "block_match_sad_qcom "); + break; + case nir_texop_block_match_ssd_qcom: + fprintf(fp, "block_match_ssd_qcom "); + break; default: UNREACHABLE("Invalid texture operation"); break; @@ -1927,6 +1943,9 @@ print_tex_instr(nir_tex_instr *instr, print_state *state) case nir_tex_src_coord: fprintf(fp, "(coord)"); break; + case nir_tex_src_ref_coord: + fprintf(fp, "(ref_coord)"); + break; case nir_tex_src_projector: fprintf(fp, "(projector)"); break; @@ -1975,10 +1994,16 @@ print_tex_instr(nir_tex_instr *instr, print_state *state) has_texture_deref = true; fprintf(fp, "(texture_deref)"); break; + case nir_tex_src_texture_2_deref: + fprintf(fp, "(texture_2_deref)"); + break; case nir_tex_src_sampler_deref: has_sampler_deref = true; fprintf(fp, "(sampler_deref)"); break; + case nir_tex_src_sampler_2_deref: + fprintf(fp, "(sampler_2_deref)"); + break; case nir_tex_src_texture_offset: fprintf(fp, "(texture_offset)"); break; @@ -1988,9 +2013,21 @@ print_tex_instr(nir_tex_instr *instr, print_state *state) case nir_tex_src_texture_handle: fprintf(fp, "(texture_handle)"); break; + case nir_tex_src_texture_2_handle: + fprintf(fp, "(texture_2_handle)"); + break; case nir_tex_src_sampler_handle: fprintf(fp, "(sampler_handle)"); break; + case nir_tex_src_sampler_2_handle: + fprintf(fp, "(ref_sampler_handle)"); + break; + case nir_tex_src_block_size: + fprintf(fp, "(block_size)"); + break; + case nir_tex_src_box_size: + fprintf(fp, "(box_size)"); + break; case nir_tex_src_plane: fprintf(fp, "(plane)"); break; diff --git a/src/compiler/nir/nir_validate.c b/src/compiler/nir/nir_validate.c index f1b6de1af00..dfc728f405d 100644 --- a/src/compiler/nir/nir_validate.c +++ b/src/compiler/nir/nir_validate.c @@ -981,7 +981,8 @@ validate_tex_instr(nir_tex_instr *instr, validate_state *state) validate_assert(state, instr->op == nir_texop_txd); break; - case nir_tex_src_texture_deref: { + case nir_tex_src_texture_deref: + case nir_tex_src_texture_2_deref: { nir_deref_instr *deref = nir_src_as_deref(instr->src[i].src); if (!validate_assert(state, deref)) break; @@ -990,7 +991,8 @@ validate_tex_instr(nir_tex_instr *instr, validate_state *state) break; } - case nir_tex_src_sampler_deref: { + case nir_tex_src_sampler_deref: + case nir_tex_src_sampler_2_deref: { nir_deref_instr *deref = nir_src_as_deref(instr->src[i].src); if (!validate_assert(state, deref)) break; @@ -1016,7 +1018,18 @@ validate_tex_instr(nir_tex_instr *instr, validate_state *state) break; } + case nir_tex_src_block_size: + validate_assert(state, + instr->op == nir_texop_block_match_sad_qcom || + instr->op == nir_texop_block_match_ssd_qcom); + break; + + case nir_tex_src_box_size: + validate_assert(state, instr->op == nir_texop_box_filter_qcom); + break; + case nir_tex_src_coord: + case nir_tex_src_ref_coord: case nir_tex_src_projector: case nir_tex_src_offset: case nir_tex_src_min_lod: @@ -1026,6 +1039,8 @@ validate_tex_instr(nir_tex_instr *instr, validate_state *state) case nir_tex_src_plane: case nir_tex_src_texture_handle: case nir_tex_src_sampler_handle: + case nir_tex_src_texture_2_handle: + case nir_tex_src_sampler_2_handle: break; default: diff --git a/src/compiler/spirv/spirv_to_nir.c b/src/compiler/spirv/spirv_to_nir.c index ed920d5c05b..d3b2c4c3b19 100644 --- a/src/compiler/spirv/spirv_to_nir.c +++ b/src/compiler/spirv/spirv_to_nir.c @@ -203,6 +203,9 @@ static const struct spirv_capabilities implemented_capabilities = { .SubgroupVoteKHR = true, .Tessellation = true, .TessellationPointSize = true, + .TextureBlockMatchQCOM = true, + .TextureBoxFilterQCOM = true, + .TextureSampleWeightedQCOM = true, .TransformFeedback = true, .UniformAndStorageBuffer8BitAccess = true, .UniformBufferArrayDynamicIndexing = true, @@ -3565,6 +3568,22 @@ vtn_handle_texture(struct vtn_builder *b, SpvOp opcode, dest_type = nir_type_uint32; break; + case SpvOpImageSampleWeightedQCOM: + texop = nir_texop_sample_weighted_qcom; + break; + + case SpvOpImageBoxFilterQCOM: + texop = nir_texop_box_filter_qcom; + break; + + case SpvOpImageBlockMatchSADQCOM: + texop = nir_texop_block_match_sad_qcom; + break; + + case SpvOpImageBlockMatchSSDQCOM: + texop = nir_texop_block_match_ssd_qcom; + break; + default: vtn_fail_with_opcode("Unhandled opcode", opcode); } @@ -3583,6 +3602,10 @@ vtn_handle_texture(struct vtn_builder *b, SpvOp opcode, case nir_texop_txd: case nir_texop_tg4: case nir_texop_lod: + case nir_texop_sample_weighted_qcom: + case nir_texop_box_filter_qcom: + case nir_texop_block_match_sad_qcom: + case nir_texop_block_match_ssd_qcom: vtn_fail_if(sampler == NULL, "%s requires an image of type OpTypeSampledImage", spirv_op_to_string(opcode)); @@ -3653,7 +3676,11 @@ vtn_handle_texture(struct vtn_builder *b, SpvOp opcode, case SpvOpImageSparseDrefGather: case SpvOpImageQueryLod: case SpvOpFragmentFetchAMD: - case SpvOpFragmentMaskFetchAMD: { + case SpvOpFragmentMaskFetchAMD: + case SpvOpImageSampleWeightedQCOM: + case SpvOpImageBoxFilterQCOM: + case SpvOpImageBlockMatchSADQCOM: + case SpvOpImageBlockMatchSSDQCOM: { /* All these types have the coordinate as their first real argument */ coord_components = glsl_get_sampler_dim_coordinate_components(sampler_dim); @@ -3767,6 +3794,31 @@ vtn_handle_texture(struct vtn_builder *b, SpvOp opcode, if (opcode == SpvOpFragmentFetchAMD) (*p++) = vtn_tex_src(b, w[idx++], nir_tex_src_ms_index); + /* For SpvOpImageBoxFilterQCOM, we always have a box size */ + if (opcode == SpvOpImageBoxFilterQCOM) + (*p++) = vtn_tex_src(b, w[idx++], nir_tex_src_box_size); + + /* For SpvOpImageSampleWeightedQCOM and block matching, we have a secondary sampled image. */ + if (opcode == SpvOpImageSampleWeightedQCOM || + opcode == SpvOpImageBlockMatchSADQCOM || + opcode == SpvOpImageBlockMatchSSDQCOM) { + struct vtn_value *sampled_val = vtn_untyped_value(b, w[idx]); + if (sampled_val->type->base_type == vtn_base_type_sampled_image) { + struct vtn_sampled_image si = vtn_get_sampled_image(b, w[idx]); + (*p++) = nir_tex_src_for_ssa(nir_tex_src_texture_2_deref, &si.image->def); + (*p++) = nir_tex_src_for_ssa(nir_tex_src_sampler_2_deref, &si.sampler->def); + } else { + vtn_err("Failed to look up sampled image for QCOM_image_processing"); + } + idx++; + } + + if (opcode == SpvOpImageBlockMatchSADQCOM || + opcode == SpvOpImageBlockMatchSSDQCOM) { + (*p++) = vtn_tex_src(b, w[idx++], nir_tex_src_ref_coord); + (*p++) = vtn_tex_src(b, w[idx++], nir_tex_src_block_size); + } + /* Now we need to handle some number of optional arguments */ struct vtn_value *gather_offsets = NULL; uint32_t operands = SpvImageOperandsMaskNone; @@ -6594,6 +6646,10 @@ vtn_handle_body_instruction(struct vtn_builder *b, SpvOp opcode, case SpvOpImageDrefGather: case SpvOpImageSparseDrefGather: case SpvOpImageQueryLod: + case SpvOpImageSampleWeightedQCOM: + case SpvOpImageBoxFilterQCOM: + case SpvOpImageBlockMatchSADQCOM: + case SpvOpImageBlockMatchSSDQCOM: vtn_handle_texture(b, opcode, w, count); break; diff --git a/src/compiler/spirv/vtn_variables.c b/src/compiler/spirv/vtn_variables.c index 53856a1595c..3e5d0ca2f00 100644 --- a/src/compiler/spirv/vtn_variables.c +++ b/src/compiler/spirv/vtn_variables.c @@ -1561,6 +1561,10 @@ apply_var_decoration(struct vtn_builder *b, "NodeMaxPayloadsAMDX decoration only allowed in compute shaders"); break; + case SpvDecorationWeightTextureQCOM: + case SpvDecorationBlockMatchTextureQCOM: + break; + default: vtn_fail_with_decoration("Unhandled decoration", dec->decoration); }