Mesa (master): zink: rework ssbo indexing and binding

GitLab Mirror gitlab-mirror at kemper.freedesktop.org
Thu Jan 28 14:41:40 UTC 2021


Module: Mesa
Branch: master
Commit: b0847a43245c6efdd17ad4bd03ef52c72167fa65
URL:    http://cgit.freedesktop.org/mesa/mesa/commit/?id=b0847a43245c6efdd17ad4bd03ef52c72167fa65

Author: Mike Blumenkrantz <michael.blumenkrantz at gmail.com>
Date:   Fri Aug  7 19:16:01 2020 -0400

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 at redhat.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/8628>

---

 .../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);



More information about the mesa-commit mailing list