From b0847a43245c6efdd17ad4bd03ef52c72167fa65 Mon Sep 17 00:00:00 2001 From: Mike Blumenkrantz Date: Fri, 7 Aug 2020 19:16:01 -0400 Subject: [PATCH] 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 Part-of: --- .../drivers/zink/nir_to_spirv/nir_to_spirv.c | 111 +++++++++--------- src/gallium/drivers/zink/zink_compiler.c | 29 +++-- 2 files changed, 78 insertions(+), 62 deletions(-) diff --git a/src/gallium/drivers/zink/nir_to_spirv/nir_to_spirv.c b/src/gallium/drivers/zink/nir_to_spirv/nir_to_spirv.c index 4ea5d0ec80c..d6cf1a255ac 100644 --- a/src/gallium/drivers/zink/nir_to_spirv/nir_to_spirv.c +++ b/src/gallium/drivers/zink/nir_to_spirv/nir_to_spirv.c @@ -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"); diff --git a/src/gallium/drivers/zink/zink_compiler.c b/src/gallium/drivers/zink/zink_compiler.c index 12823382b61..55e49c72185 100644 --- a/src/gallium/drivers/zink/zink_compiler.c +++ b/src/gallium/drivers/zink/zink_compiler.c @@ -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);