asahi: use 2D descriptors for cubes

fixes arb_shader_image_load_store-invalid case imageLoad/address bounds test/imageCube/rgba32f

this is also better codegen since it avoids the wacko division by 6. although it
creates a div by 6 in imageSize, that's better because that one is much more
likely to hoist to the preamble. probably should've done this from the start.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/26614>
This commit is contained in:
Alyssa Rosenzweig 2023-11-20 11:10:08 -04:00
parent 86c4a72767
commit b74d2dcd57
3 changed files with 50 additions and 31 deletions

View file

@ -17,6 +17,7 @@
#include "agx_nir.h"
#include "nir.h"
#include "nir_intrinsics.h"
#include "nir_intrinsics_indices.h"
/* Alignment for shader programs. I'm not sure what the optimal value is. */
#define AGX_CODE_ALIGN 0x100
@ -912,8 +913,20 @@ agx_emit_image_load(agx_builder *b, agx_index dst, nir_intrinsic_instr *intr)
agx_extract_nir_src(b, intr->src[1], 3),
};
/* Get the image dimension. Cubes are lowered to 2D, since they are logically
* equivalent for imageLoad, but out-of-bounds behaviour for cubes on G13
* is wrong according to Piglit's arb_shader_image_load_store-invalid.
*
* This requires a matching transform in the driver.
*/
enum glsl_sampler_dim dim = nir_intrinsic_image_dim(intr);
bool is_array = nir_intrinsic_image_array(intr);
if (dim == GLSL_SAMPLER_DIM_CUBE) {
dim = GLSL_SAMPLER_DIM_2D;
is_array = true;
}
bool is_ms = dim == GLSL_SAMPLER_DIM_MS;
unsigned coord_comps = glsl_get_sampler_dim_coordinate_components(dim);
if (is_array && is_ms) {
@ -951,8 +964,16 @@ agx_emit_image_load(agx_builder *b, agx_index dst, nir_intrinsic_instr *intr)
static agx_instr *
agx_emit_image_store(agx_builder *b, nir_intrinsic_instr *instr)
{
/* See remarks in agx_emit_image_load */
enum glsl_sampler_dim glsl_dim = nir_intrinsic_image_dim(instr);
enum agx_dim dim = agx_tex_dim(glsl_dim, nir_intrinsic_image_array(instr));
bool is_array = nir_intrinsic_image_array(instr);
if (glsl_dim == GLSL_SAMPLER_DIM_CUBE) {
glsl_dim = GLSL_SAMPLER_DIM_2D;
is_array = true;
}
enum agx_dim dim = agx_tex_dim(glsl_dim, is_array);
assert(glsl_dim != GLSL_SAMPLER_DIM_MS && "needs to be lowered");
agx_index base, index;
@ -973,7 +994,7 @@ agx_emit_image_store(agx_builder *b, nir_intrinsic_instr *instr)
assert(lod.size == AGX_SIZE_16);
int coord_components = glsl_get_sampler_dim_coordinate_components(glsl_dim);
if (nir_intrinsic_image_array(instr))
if (is_array)
coord_components++;
agx_index coord_comps[4] = {};

View file

@ -12,6 +12,7 @@
#include "agx_compiler.h"
#include "agx_internal_formats.h"
#include "agx_nir.h"
#include "glsl_types.h"
#include "libagx_shaders.h"
#include "nir_builder_opcodes.h"
#include "nir_intrinsics.h"
@ -359,7 +360,15 @@ txs_for_image(nir_builder *b, nir_intrinsic_instr *intr,
nir_def_init(&tex->instr, &tex->def, num_components, bit_size);
nir_builder_instr_insert(b, &tex->instr);
return &tex->def;
nir_def *res = &tex->def;
/* Cube images are implemented as 2D arrays, so we need to divide here. */
if (tex->sampler_dim == GLSL_SAMPLER_DIM_CUBE && res->num_components > 2) {
nir_def *divided = nir_udiv_imm(b, nir_channel(b, res, 2), 6);
res = nir_vector_insert_imm(b, res, divided, 2);
}
return res;
}
static nir_def *
@ -431,25 +440,6 @@ lower_1d_image(nir_builder *b, nir_intrinsic_instr *intr)
nir_intrinsic_set_image_dim(intr, GLSL_SAMPLER_DIM_2D);
}
/*
* AGX needs the face and the layer specified separately. This matches how NIR
* texture instructions work, but not how NIR image intrinsics work. Here we
* lower by dividing the combined layer-face into separate components which the
* compiler can consume.
*/
static void
lower_cube_array_image(nir_builder *b, nir_intrinsic_instr *intr)
{
nir_def *x = nir_channel(b, intr->src[1].ssa, 0);
nir_def *y = nir_channel(b, intr->src[1].ssa, 1);
nir_def *z = nir_channel(b, intr->src[1].ssa, 2);
nir_def *face = nir_umod_imm(b, z, 6);
nir_def *layer = nir_udiv_imm(b, z, 6);
nir_src_rewrite(&intr->src[1], nir_vec4(b, x, y, face, layer));
}
static bool
lower_images(nir_builder *b, nir_intrinsic_instr *intr, UNUSED void *data)
{
@ -472,12 +462,6 @@ lower_images(nir_builder *b, nir_intrinsic_instr *intr, UNUSED void *data)
lower_buffer_image(b, intr);
return true;
case GLSL_SAMPLER_DIM_CUBE:
if (nir_intrinsic_image_array(intr))
lower_cube_array_image(b, intr);
return true;
default:
return true;
}

View file

@ -639,6 +639,12 @@ agx_translate_sample_count(unsigned samples)
}
}
static bool
target_is_cube(enum pipe_texture_target target)
{
return target == PIPE_TEXTURE_CUBE || target == PIPE_TEXTURE_CUBE_ARRAY;
}
static void
agx_pack_texture(void *out, struct agx_resource *rsrc,
enum pipe_format format /* override */,
@ -737,8 +743,7 @@ agx_pack_texture(void *out, struct agx_resource *rsrc,
unsigned layers =
state->u.tex.last_layer - state->u.tex.first_layer + 1;
if ((state->target == PIPE_TEXTURE_CUBE) ||
(state->target == PIPE_TEXTURE_CUBE_ARRAY))
if (target_is_cube(state->target))
layers /= 6;
if (rsrc->layout.tiling == AIL_TILING_LINEAR &&
@ -1206,8 +1211,10 @@ agx_batch_upload_pbe(struct agx_batch *batch, struct agx_pbe_packed *out,
/* To reduce shader variants, spilled layered render targets are accessed as
* 2D Arrays regardless of the actual target, so force in that case.
*
* Likewise, cubes are accessed as arrays for consistency with NIR.
*/
if (arrays_as_2d && target_is_array(target))
if ((arrays_as_2d && target_is_array(target)) || target_is_cube(target))
target = PIPE_TEXTURE_2D_ARRAY;
unsigned level = is_buffer ? 0 : view->u.tex.level;
@ -2502,6 +2509,13 @@ agx_upload_textures(struct agx_batch *batch, struct agx_compiled_shader *cs,
agx_batch_track_image(batch, view);
struct pipe_sampler_view sampler_view = util_image_to_sampler_view(view);
/* For the texture descriptor, lower cubes to 2D arrays. This matches the
* transform done in the compiler.
*/
if (target_is_cube(sampler_view.target))
sampler_view.target = PIPE_TEXTURE_2D_ARRAY;
agx_pack_texture(texture, agx_resource(view->resource), view->format,
&sampler_view, true);
agx_batch_upload_pbe(batch, pbe, view, false, false);