1/* 2 * Copyright 2023 Alyssa Rosenzweig 3 * Copyright 2023 Valve Corporation 4 * SPDX-License-Identifier: MIT 5 */ 6#include "libagx.h" 7#include <agx_pack.h> 8 9uint3 10libagx_txs(constant struct agx_texture_packed *ptr, uint16_t lod, 11 unsigned nr_comps, bool is_buffer, bool is_1d, bool is_2d, 12 bool is_cube, bool is_array) 13{ 14 agx_unpack(NULL, ptr, TEXTURE, d); 15 16 /* From the Vulkan spec: 17 * 18 * OpImageQuery*... return 0 if the bound descriptor is a null descriptor 19 */ 20 if (d.null) 21 return 0; 22 23 /* Buffer textures are lowered to 2D so the original size is irrecoverable. 24 * Instead, we stash it in the software-defined section. 25 */ 26 if (is_buffer) 27 return d.software_defined; 28 29 /* Load standard dimensions */ 30 uint3 size = (uint3)(d.width, d.height, d.depth); 31 lod += d.first_level; 32 33 /* Linear 2D arrays are special. 34 * 35 * TODO: Optimize this, since linear 2D arrays aren't needed for APIs and 36 * this just gets used internally for blits. 37 */ 38 if (is_2d && is_array && d.layout == AGX_LAYOUT_LINEAR) 39 size.z = d.depth_linear; 40 41 /* 1D Arrays have their second component as the layer count */ 42 if (is_1d && is_array) 43 size.y = size.z; 44 45 /* Adjust for LOD, do not adjust array size */ 46 for (uint c = 0; c < (nr_comps - (uint)is_array); ++c) 47 size[c] = max(size[c] >> lod, 1u); 48 49 /* Cube maps have equal width and height, we save some instructions by only 50 * reading one. Dead code elimination will remove the redundant instructions. 51 */ 52 if (is_cube) 53 size.y = size.x; 54 55 return size; 56} 57 58uint 59libagx_texture_samples(constant struct agx_texture_packed *ptr) 60{ 61 agx_unpack(NULL, ptr, TEXTURE, d); 62 63 /* As above */ 64 if (d.null) 65 return 0; 66 67 /* We may assume the input is multisampled, so just check the samples */ 68 return (d.samples == AGX_SAMPLE_COUNT_2) ? 2 : 4; 69} 70 71uint 72libagx_texture_levels(constant struct agx_texture_packed *ptr) 73{ 74 agx_unpack(NULL, ptr, TEXTURE, d); 75 76 /* As above */ 77 if (d.null) 78 return 0; 79 else 80 return (d.last_level - d.first_level) + 1; 81} 82 83/* 84 * Fix robustness behaviour of txf with out-of-bounds LOD. The hardware 85 * returns the correct out-of-bounds colour for out-of-bounds coordinates, 86 * just not LODs. So translate out-of-bounds LOD into an out-of-bounds 87 * coordinate to get correct behaviour in 1 instruction. 88 * 89 * Returns the fixed X-coordinate. 90 * 91 * TODO: This looks like it might be an erratum workaround on G13 (Apple does 92 * it), maybe check if G15 is affected. 93 */ 94uint 95libagx_lower_txf_robustness(constant struct agx_texture_packed *ptr, ushort lod, 96 uint x) 97{ 98 agx_unpack(NULL, ptr, TEXTURE, d); 99 100 bool oob = (lod > (d.last_level - d.first_level)); 101 return oob ? 0xFFFF : x; 102} 103 104static uint32_t 105calculate_twiddled_coordinates(ushort2 coord, uint16_t tile_w_px, 106 uint16_t tile_h_px, uint32_t width_tl) 107{ 108 /* Modulo by the tile width/height to get the offsets within the tile */ 109 ushort2 tile_mask_vec = (ushort2)(tile_w_px - 1, tile_h_px - 1); 110 uint32_t tile_mask = upsample(tile_mask_vec.y, tile_mask_vec.x); 111 uint32_t coord_xy = upsample(coord.y, coord.x); 112 ushort2 offs_px = as_ushort2(coord_xy & tile_mask); 113 uint32_t offset_within_tile_px = nir_interleave_agx(offs_px.x, offs_px.y); 114 115 /* Get the coordinates of the corner of the tile */ 116 ushort2 tile_px = as_ushort2(coord_xy & ~tile_mask); 117 118 /* tile row start (px) = 119 * (y // tile height) * (# of tiles/row) * (# of pix/tile) = 120 * align_down(y, tile height) / tile height * width_tl *tile width * 121 * tile height = 122 * align_down(y, tile height) * width_tl * tile width 123 */ 124 uint32_t tile_row_start_px = tile_px.y * width_tl * tile_w_px; 125 126 /* tile column start (px) = 127 * (x // tile width) * (# of pix/tile) = 128 * align_down(x, tile width) / tile width * tile width * tile height = 129 * align_down(x, tile width) * tile height 130 */ 131 uint32_t tile_col_start_px = tile_px.x * tile_h_px; 132 133 /* Get the total offset */ 134 return tile_row_start_px + tile_col_start_px + offset_within_tile_px; 135} 136 137uint64_t 138libagx_image_texel_address(constant const struct agx_atomic_software_packed *ptr, 139 uint4 coord, uint sample_idx, 140 uint bytes_per_sample_B, bool is_msaa, 141 bool is_layered, bool return_index) 142{ 143 agx_unpack(NULL, ptr, ATOMIC_SOFTWARE, d); 144 145 /* We do not allow atomics on linear 2D or linear 2D arrays, as there are no 146 * known use cases. So we're twiddled in this path. 147 */ 148 uint total_px = calculate_twiddled_coordinates( 149 convert_ushort2(coord.xy), d.tile_width, d.tile_height, d.tiles_per_row); 150 151 if (is_layered) 152 total_px += coord.z * d.layer_stride_pixels; 153 154 uint sample_count = is_msaa ? d.sample_count : 1; 155 uint total_sa = (total_px * d.sample_count) + sample_idx; 156 157 if (return_index) 158 return total_sa; 159 else 160 return d.base + (uint64_t)(total_sa * bytes_per_sample_B); 161} 162 163uint64_t 164libagx_buffer_texel_address( 165 constant const struct agx_pbe_buffer_software_packed *ptr, uint4 coord, 166 uint bytes_per_pixel_B) 167{ 168 agx_unpack(NULL, ptr, PBE_BUFFER_SOFTWARE, d); 169 return d.base + (uint64_t)(coord.x * bytes_per_pixel_B); 170} 171 172/* Buffer texture lowerings */ 173bool 174libagx_texture_is_rgb32(constant struct agx_texture_packed *ptr) 175{ 176 agx_unpack(NULL, ptr, TEXTURE, d); 177 return d.channels == AGX_CHANNELS_R32G32B32_EMULATED; 178} 179 180uint4 181libagx_texture_load_rgb32(constant struct agx_texture_packed *ptr, uint coord, 182 bool is_float) 183{ 184 agx_unpack(NULL, ptr, TEXTURE, d); 185 global uint3 *data = (global uint3 *)(d.address + 12 * coord); 186 187 return (uint4)(*data, is_float ? as_uint(1.0f) : 1); 188} 189