| /* |
| * Copyright © 2024 Intel Corporation |
| * SPDX-License-Identifier: MIT |
| */ |
| |
| #include "isl/isl.h" |
| |
| #include "brw_nir.h" |
| #include "compiler/nir/nir_builder.h" |
| |
| struct lower_state { |
| const struct intel_device_info *devinfo; |
| enum isl_tiling tiling; |
| }; |
| |
| struct coord_swizzle { |
| uint32_t addr_bit; |
| uint32_t coord_bit; |
| uint32_t n_bits; |
| }; |
| |
| #define EMPTY_SWIZZLE ((struct coord_swizzle) { \ |
| .addr_bit = UINT32_MAX, \ |
| .coord_bit = UINT32_MAX, \ |
| .n_bits = 0, \ |
| }) |
| |
| static nir_def * |
| add_swizzle(nir_builder *b, |
| nir_def *old_offset, |
| nir_def *coord, |
| struct coord_swizzle swizzle) |
| { |
| if (swizzle.n_bits == 0) |
| return old_offset; |
| |
| nir_def *masked_coord = |
| nir_iand_imm(b, coord, |
| ((1u << swizzle.n_bits) - 1) << swizzle.coord_bit); |
| |
| nir_def *bits; |
| if (swizzle.addr_bit > swizzle.coord_bit) |
| bits = nir_ishl_imm(b, masked_coord, swizzle.addr_bit - swizzle.coord_bit); |
| else if (swizzle.addr_bit < swizzle.coord_bit) |
| bits = nir_ushr_imm(b, masked_coord, swizzle.coord_bit - swizzle.addr_bit); |
| else |
| bits = masked_coord; |
| |
| return old_offset != NULL ? nir_ior(b, old_offset, bits) : bits; |
| } |
| |
| /** |
| * Adjust application coordinates to a vec3 suited for detiling. |
| * |
| * The detiling algorithm rely on u,v,r components but application coordinates |
| * can skip a component for example for 1DArray image, we'll be handed a |
| * vec2 : u,r |
| */ |
| static nir_def * |
| adjust_coords(nir_builder *b, nir_def *in_coords, |
| enum glsl_sampler_dim dim, bool is_array) |
| { |
| nir_def *coords[3]; |
| unsigned i = 0, n_dims = glsl_get_sampler_dim_coordinate_components(dim); |
| for (; i < n_dims; i++) |
| coords[i] = nir_channel(b, in_coords, i); |
| for (; i < 2; i++) |
| coords[i] = nir_imm_int(b, 0); |
| if (i < 3) { |
| coords[i++] = is_array ? nir_channel(b, in_coords, n_dims) : |
| nir_imm_int(b, 0); |
| } |
| |
| return nir_vec(b, coords, ARRAY_SIZE(coords)); |
| } |
| |
| static nir_def * |
| load_image_param(nir_builder *b, nir_def *surface_handle, unsigned index) |
| { |
| unsigned num_components, bit_size; |
| switch (index) { |
| case ISL_SURF_PARAM_BASE_ADDRESSS: |
| bit_size = 64; |
| num_components = 1; |
| break; |
| case ISL_SURF_PARAM_TILE_MODE: |
| case ISL_SURF_PARAM_PITCH: |
| case ISL_SURF_PARAM_QPITCH: |
| bit_size = 32; |
| num_components = 1; |
| break; |
| default: |
| unreachable("Invalid param offset"); |
| } |
| |
| return nir_image_deref_load_param_intel(b, num_components, bit_size, |
| surface_handle, |
| .base = index); |
| } |
| |
| static nir_def * |
| image_linear_address(nir_builder *b, |
| unsigned bpp, |
| nir_def **coords, |
| nir_def *pitch, |
| nir_def *qpitch) |
| { |
| nir_def *qpitch_in_bytes = nir_imul(b, qpitch, nir_imax_imm(b, pitch, bpp / 8)); |
| return nir_iadd(b, |
| nir_iadd(b, |
| nir_imul(b, coords[2], qpitch_in_bytes), |
| nir_imul(b, coords[1], pitch)), |
| coords[0]); |
| } |
| |
| static enum isl_surf_dim |
| glsl_sampler_dim_to_isl(enum glsl_sampler_dim dim) |
| { |
| return dim == GLSL_SAMPLER_DIM_1D ? ISL_SURF_DIM_1D : |
| dim == GLSL_SAMPLER_DIM_3D ? ISL_SURF_DIM_3D : |
| ISL_SURF_DIM_2D; |
| } |
| |
| static nir_def * |
| image_tiled_address(nir_builder *b, |
| enum isl_tiling tiling, |
| enum glsl_sampler_dim dim, |
| unsigned bpp, |
| nir_def **coords, |
| nir_def *pitch, |
| nir_def *qpitch) |
| { |
| struct isl_tile_info tile_info; |
| isl_tiling_get_info(tiling, |
| glsl_sampler_dim_to_isl(dim), |
| ISL_MSAA_LAYOUT_NONE, |
| bpp, |
| 1, |
| &tile_info); |
| |
| /* Compute the intra tile offset using the swizzle (swizzles use u,v,r,p |
| * but we do not support msaa images) |
| */ |
| nir_def *tiled_addr = NULL; |
| for (unsigned c = 0; c < 3; c++) { |
| struct coord_swizzle swizzle = EMPTY_SWIZZLE; |
| for (unsigned i = ffs(bpp / 8) - 1; i < tile_info.swiz_count; i++) { |
| /* Bit is not for this component, ignore */ |
| if (ISL_ADDR_SWIZ_COMPONENT(tile_info.swiz[i]) != c) { |
| tiled_addr = add_swizzle(b, tiled_addr, coords[c], swizzle); |
| swizzle = EMPTY_SWIZZLE; |
| continue; |
| } |
| |
| const uint32_t coord_bit = ISL_ADDR_SWIZ_INDEX(tile_info.swiz[i]); |
| |
| swizzle.addr_bit = MIN2(i, swizzle.addr_bit); |
| swizzle.coord_bit = MIN2(coord_bit, swizzle.coord_bit); |
| swizzle.n_bits++; |
| } |
| |
| tiled_addr = add_swizzle(b, tiled_addr, coords[c], swizzle); |
| } |
| |
| /* Look at the used bits in the swizzle to figure out the tiling |
| * coefficients. |
| */ |
| isl_tile_extent coefficients = |
| isl_swizzle_get_tile_coefficients(tile_info.swiz, |
| tile_info.swiz_count, |
| bpp / 8); |
| |
| /* Apply the generic tile id computation as described in the PRMs Volume |
| * 5: Memory Data Formats: |
| * |
| * "TileID = [(r » Cr) * (QPitch » Cv) + (v » Cv)] * (Pitch » Cu) + (u » Cu)" |
| */ |
| nir_def *tile_id = |
| nir_iadd(b, |
| nir_imul(b, |
| nir_iadd(b, |
| nir_imul(b, |
| nir_ushr_imm(b, coords[2], coefficients.d), |
| nir_ushr_imm(b, qpitch, coefficients.h)), |
| nir_ushr_imm(b, coords[1], coefficients.h)), |
| nir_ushr_imm(b, pitch, coefficients.w)), |
| nir_ushr_imm(b, coords[0], coefficients.w)); |
| |
| return nir_iadd(b, tiled_addr, |
| nir_imul_imm(b, tile_id, |
| tile_info.phys_extent_B.w * |
| tile_info.phys_extent_B.h)); |
| } |
| |
| |
| /** Calculate the offset in memory of the texel given by \p coord. |
| * |
| * This is meant to be used with untyped surface messages to access a tiled |
| * surface, what involves taking into account the tiling. |
| * |
| */ |
| static nir_def * |
| image_address(nir_builder *b, |
| const struct intel_device_info *devinfo, |
| enum isl_tiling tiling, |
| enum glsl_sampler_dim dim, |
| bool is_array, |
| enum pipe_format format, |
| nir_def *surface_handle, |
| nir_def *coords_vec) |
| { |
| const struct util_format_description *desc = |
| util_format_description(format); |
| const unsigned bpp = desc->block.bits; |
| |
| coords_vec = adjust_coords(b, coords_vec, dim, is_array); |
| assert(coords_vec->num_components == 3); |
| |
| /* Ignore the bottom bits based on the format bpp */ |
| const unsigned start_bit = ffs(bpp / 8) - 1; |
| |
| /* Build coords with the x component in bytes */ |
| nir_def *coords[3]; |
| coords[0] = nir_ishl_imm(b, nir_channel(b, coords_vec, 0), start_bit); |
| for (unsigned c = 1; c < ARRAY_SIZE(coords); c++) |
| coords[c] = nir_channel(b, coords_vec, c); |
| |
| nir_def *pitch = |
| load_image_param(b, surface_handle, ISL_SURF_PARAM_PITCH); |
| nir_def *qpitch = |
| load_image_param(b, surface_handle, ISL_SURF_PARAM_QPITCH); |
| |
| if (!isl_tiling_supports_dimensions(devinfo, tiling, |
| glsl_sampler_dim_to_isl(dim))) |
| return image_linear_address(b, bpp, coords, pitch, qpitch); |
| |
| nir_def *linear_addr = NULL; |
| nir_def *tiled_addr = NULL; |
| |
| nir_def *tile_mode = |
| load_image_param(b, surface_handle, ISL_SURF_PARAM_TILE_MODE); |
| nir_push_if(b, nir_ieq_imm(b, tile_mode, 0)); |
| { |
| linear_addr = image_linear_address(b, bpp, coords, pitch, qpitch); |
| } |
| nir_push_else(b, NULL); |
| { |
| tiled_addr = image_tiled_address(b, tiling, dim, bpp, |
| coords, pitch, qpitch); |
| } |
| nir_pop_if(b, NULL); |
| |
| nir_def *addr = nir_if_phi(b, linear_addr, tiled_addr); |
| |
| return addr; |
| } |
| |
| static bool |
| brw_nir_lower_texel_address_instr(nir_builder *b, |
| nir_intrinsic_instr *intrin, |
| void *data) |
| { |
| struct lower_state *state = data; |
| |
| switch (intrin->intrinsic) { |
| case nir_intrinsic_image_texel_address: |
| case nir_intrinsic_image_deref_texel_address: |
| case nir_intrinsic_bindless_image_texel_address: |
| break; |
| |
| default: |
| return false; |
| } |
| |
| b->cursor = nir_instr_remove(&intrin->instr); |
| |
| nir_def *addr = nir_iadd( |
| b, |
| load_image_param(b, intrin->src[0].ssa, ISL_SURF_PARAM_BASE_ADDRESSS), |
| nir_u2u64(b, |
| image_address(b, |
| state->devinfo, |
| state->tiling, |
| nir_intrinsic_image_dim(intrin), |
| nir_intrinsic_image_array(intrin), |
| nir_intrinsic_format(intrin), |
| intrin->src[0].ssa, |
| intrin->src[1].ssa))); |
| |
| nir_def_rewrite_uses(&intrin->def, addr); |
| |
| return true; |
| } |
| |
| bool |
| brw_nir_lower_texel_address(nir_shader *shader, |
| const struct intel_device_info *devinfo, |
| enum isl_tiling tiling) |
| { |
| struct lower_state state = { .devinfo = devinfo, .tiling = tiling, }; |
| |
| return nir_shader_intrinsics_pass(shader, |
| brw_nir_lower_texel_address_instr, |
| nir_metadata_none, |
| &state); |
| } |