zink: rework ssbo indexing and binding

this is actually crazy, but there's no other way to do it from the variable.
ideally, nir would have a separate type for atomic counters to simplify this
and then also stop mangling binding/block index during lower_buffers

Reviewed-by: Dave Airlie <airlied@redhat.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/8628>
This commit is contained in:
Mike Blumenkrantz 2020-08-07 19:16:01 -04:00 committed by Marge Bot
parent deeafe47b6
commit b0847a4324
2 changed files with 78 additions and 62 deletions

View File

@ -45,6 +45,8 @@ struct ntv_context {
size_t num_ubos;
SpvId ssbos[PIPE_MAX_SHADER_BUFFERS];
uint32_t ssbo_mask;
uint32_t num_ssbos;
SpvId image_types[PIPE_MAX_SAMPLERS];
SpvId images[PIPE_MAX_SAMPLERS];
SpvId sampler_types[PIPE_MAX_SAMPLERS];
@ -813,47 +815,7 @@ emit_image(struct ntv_context *ctx, struct nir_variable *var)
}
static void
emit_ssbo(struct ntv_context *ctx, struct nir_variable *var)
{
SpvId vec4_type = get_uvec_type(ctx, 32, 4);
SpvId array_type = spirv_builder_type_runtime_array(&ctx->builder, vec4_type);
spirv_builder_emit_array_stride(&ctx->builder, array_type, 16);
SpvId struct_type = spirv_builder_type_struct(&ctx->builder, &array_type, 1);
if (var->name) {
char struct_name[100];
snprintf(struct_name, sizeof(struct_name), "struct_%s", var->name);
spirv_builder_emit_name(&ctx->builder, struct_type, struct_name);
}
spirv_builder_emit_decoration(&ctx->builder, struct_type,
SpvDecorationBlock);
spirv_builder_emit_member_offset(&ctx->builder, struct_type, 0, 0);
SpvId pointer_type = spirv_builder_type_pointer(&ctx->builder,
SpvStorageClassStorageBuffer,
struct_type);
SpvId var_id = spirv_builder_emit_var(&ctx->builder, pointer_type,
SpvStorageClassStorageBuffer);
if (var->name) {
char struct_name[100];
snprintf(struct_name, sizeof(struct_name), "%s", var->name);
spirv_builder_emit_name(&ctx->builder, var_id, var->name);
}
assert(var->data.binding < ARRAY_SIZE(ctx->ssbos));
ctx->ssbos[var->data.binding] = var_id;
spirv_builder_emit_descriptor_set(&ctx->builder, var_id, 0);
int binding = zink_binding(ctx->stage,
VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
var->data.binding);
spirv_builder_emit_binding(&ctx->builder, var_id, binding);
}
static void
emit_ubo(struct ntv_context *ctx, struct nir_variable *var)
emit_bo(struct ntv_context *ctx, struct nir_variable *var)
{
bool is_ubo_array = glsl_type_is_array(var->type) && glsl_type_is_interface(glsl_without_array(var->type));
/* variables accessed inside a uniform block will get merged into a big
@ -861,12 +823,18 @@ emit_ubo(struct ntv_context *ctx, struct nir_variable *var)
*/
if (var->data.location && !is_ubo_array && var->type != var->interface_type)
return;
bool ssbo = var->data.mode == nir_var_mem_ssbo;
uint32_t size = glsl_count_attribute_slots(var->interface_type, false);
SpvId array_type;
SpvId vec4_type = get_uvec_type(ctx, 32, 4);
SpvId array_length = emit_uint_const(ctx, 32, size);
SpvId array_type = spirv_builder_type_array(&ctx->builder, vec4_type,
if (glsl_type_is_unsized_array(var->type))
array_type = spirv_builder_type_runtime_array(&ctx->builder, vec4_type);
else {
uint32_t size = glsl_count_attribute_slots(var->interface_type, false);
SpvId array_length = emit_uint_const(ctx, 32, size);
array_type = spirv_builder_type_array(&ctx->builder, vec4_type,
array_length);
}
spirv_builder_emit_array_stride(&ctx->builder, array_type, 16);
// wrap UBO-array in a struct
@ -882,7 +850,7 @@ emit_ubo(struct ntv_context *ctx, struct nir_variable *var)
spirv_builder_emit_member_offset(&ctx->builder, struct_type, 0, 0);
SpvId pointer_type = spirv_builder_type_pointer(&ctx->builder,
SpvStorageClassUniform,
ssbo ? SpvStorageClassStorageBuffer : SpvStorageClassUniform,
struct_type);
/* if this is a ubo array, create a binding point for each array member:
@ -893,21 +861,57 @@ emit_ubo(struct ntv_context *ctx, struct nir_variable *var)
(also it's just easier)
*/
for (unsigned i = 0; i < (is_ubo_array ? glsl_get_aoa_size(var->type) : 1); i++) {
unsigned size = is_ubo_array ? glsl_get_aoa_size(var->type) : 1;
int base = -1;
for (unsigned i = 0; i < size; i++) {
SpvId var_id = spirv_builder_emit_var(&ctx->builder, pointer_type,
SpvStorageClassUniform);
ssbo ? SpvStorageClassStorageBuffer : SpvStorageClassUniform);
if (var->name) {
char struct_name[100];
snprintf(struct_name, sizeof(struct_name), "%s[%u]", var->name, i);
spirv_builder_emit_name(&ctx->builder, var_id, var->name);
}
assert(ctx->num_ubos < ARRAY_SIZE(ctx->ubos));
ctx->ubos[ctx->num_ubos++] = var_id;
if (ssbo) {
unsigned ssbo_idx = 0;
if (!is_ubo_array && var->data.explicit_binding &&
(glsl_type_is_unsized_array(var->type) || glsl_get_length(var->interface_type) == 1)) {
/* - block ssbos get their binding broken in gl_nir_lower_buffers,
* but also they're totally indistinguishable from lowered counter buffers which have valid bindings
*
* hopefully this is a counter or some other non-block variable, but if not then we're probably fucked
*/
ssbo_idx = var->data.binding;
} else if (base >= 0)
/* we're indexing into a ssbo array and already have the base index */
ssbo_idx = base + i;
else {
if (ctx->ssbo_mask & 1) {
/* 0 index is used, iterate through the used blocks until we find the first unused one */
for (unsigned j = 1; j < ctx->num_ssbos; j++)
if (!(ctx->ssbo_mask & (1 << j))) {
/* we're iterating forward through the blocks, so the first available one should be
* what we're looking for
*/
base = ssbo_idx = j;
break;
}
} else
/* we're iterating forward through the ssbos, so always assign 0 first */
base = ssbo_idx = 0;
assert(ssbo_idx < ctx->num_ssbos);
}
assert(!ctx->ssbos[ssbo_idx]);
ctx->ssbos[ssbo_idx] = var_id;
ctx->ssbo_mask |= 1 << ssbo_idx;
} else {
assert(ctx->num_ubos < ARRAY_SIZE(ctx->ubos));
ctx->ubos[ctx->num_ubos++] = var_id;
}
spirv_builder_emit_descriptor_set(&ctx->builder, var_id, 0);
int binding = zink_binding(ctx->stage,
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
ssbo ? VK_DESCRIPTOR_TYPE_STORAGE_BUFFER : VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
var->data.binding + i);
spirv_builder_emit_binding(&ctx->builder, var_id, binding);
}
@ -916,10 +920,8 @@ emit_ubo(struct ntv_context *ctx, struct nir_variable *var)
static void
emit_uniform(struct ntv_context *ctx, struct nir_variable *var)
{
if (var->data.mode == nir_var_mem_ubo)
emit_ubo(ctx, var);
else if (var->data.mode == nir_var_mem_ssbo)
emit_ssbo(ctx, var);
if (var->data.mode == nir_var_mem_ubo || var->data.mode == nir_var_mem_ssbo)
emit_bo(ctx, var);
else {
assert(var->data.mode == nir_var_uniform);
const struct glsl_type *type = glsl_without_array(var->type);
@ -3231,6 +3233,7 @@ nir_to_spirv(struct nir_shader *s, const struct zink_so_info *so_info,
ctx.stage = s->info.stage;
ctx.so_info = so_info;
ctx.num_ssbos = s->info.num_ssbos;
ctx.shader_slot_map = shader_slot_map;
ctx.shader_slots_reserved = *shader_slots_reserved;
ctx.GLSL_std_450 = spirv_builder_import(&ctx.builder, "GLSL.std.450");

View File

@ -463,6 +463,7 @@ zink_shader_create(struct zink_screen *screen, struct nir_shader *nir,
/* need to set up var->data.binding for UBOs, which means we need to start at
* the "first" UBO, which is at the end of the list
*/
int ssbo_array_index = 0;
foreach_list_typed_reverse(nir_variable, var, node, &nir->variables) {
if (_nir_shader_variable_has_mode(var, nir_var_uniform |
nir_var_mem_ubo |
@ -493,14 +494,26 @@ zink_shader_create(struct zink_screen *screen, struct nir_shader *nir,
ret->num_bindings++;
}
} else if (var->data.mode == nir_var_mem_ssbo) {
int binding = zink_binding(nir->info.stage,
VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
var->data.binding);
ret->bindings[ret->num_bindings].index = var->data.binding;
ret->bindings[ret->num_bindings].binding = binding;
ret->bindings[ret->num_bindings].type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
ret->bindings[ret->num_bindings].size = 1;
ret->num_bindings++;
/* same-ish mechanics as ubos */
bool bo_array = glsl_type_is_array(var->type) && glsl_type_is_interface(glsl_without_array(var->type));
if (var->data.location && !bo_array)
continue;
if (!var->data.explicit_binding) {
var->data.binding = ssbo_array_index;
}
for (unsigned i = 0; i < (bo_array ? glsl_get_aoa_size(var->type) : 1); i++) {
int binding = zink_binding(nir->info.stage,
VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
var->data.binding + i);
if (strcmp(glsl_get_type_name(var->interface_type), "counters"))
ret->bindings[ret->num_bindings].index = ssbo_array_index++;
else
ret->bindings[ret->num_bindings].index = var->data.binding;
ret->bindings[ret->num_bindings].binding = binding;
ret->bindings[ret->num_bindings].type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
ret->bindings[ret->num_bindings].size = 1;
ret->num_bindings++;
}
} else {
assert(var->data.mode == nir_var_uniform);
const struct glsl_type *type = glsl_without_array(var->type);