[1/3] spirv: Handle location decorations on block interface members

Submitted by Neil Roberts on March 28, 2018, 9 a.m.

Details

Message ID 20180328090029.24723-1-nroberts@igalia.com
State New
Headers show
Series "Series without cover letter" ( rev: 1 ) in Mesa

Not browsing as part of any series.

Commit Message

Neil Roberts March 28, 2018, 9 a.m.
Previously the code was taking any location decoration on the block
and using that to calculate the member locations for all of the
members. I think this was assuming that there would only be one
location decoration for the entire block. According to the Vulkan spec
it is possible to add location decorations to individual members:

“If the structure type is a Block but without a Location, then each of
 its members must have a Location decoration. If it is a Block with a
 Location decoration, then its members are assigned consecutive
 locations in declaration order, starting from the first member which
 is initially the Block. Any member with its own Location decoration
 is assigned that location. Each remaining member is assigned the
 location after the immediately preceding member in declaration
 order.”

This patch makes it instead keep track of which members have been
assigned an explicit location. It also has a space to store the
location for the struct as a whole. Once all the decorations have been
processed it iterates over each member to fill in the missing
locations using the rules described above.
---

There are tests for this on the tests branch of VkRunner:

https://github.com/Igalia/vkrunner/tree/tests

./src/vkrunner examples/block-*.shader_test

 src/compiler/spirv/vtn_private.h   |  6 ++++
 src/compiler/spirv/vtn_variables.c | 60 ++++++++++++++++++++++++++++++--------
 2 files changed, 54 insertions(+), 12 deletions(-)

Patch hide | download patch | download mbox

diff --git a/src/compiler/spirv/vtn_private.h b/src/compiler/spirv/vtn_private.h
index 70f660fbd48..c268b093656 100644
--- a/src/compiler/spirv/vtn_private.h
+++ b/src/compiler/spirv/vtn_private.h
@@ -457,6 +457,12 @@  struct vtn_variable {
    nir_variable *var;
    nir_variable **members;
 
+   /* If the variable is a struct with a location set on it then this will be
+    * stored here. This will be used to calculate locations for members that
+    * don’t have their own explicit location.
+    */
+   int base_location;
+
    int shared_location;
 
    /**
diff --git a/src/compiler/spirv/vtn_variables.c b/src/compiler/spirv/vtn_variables.c
index b2897407fb1..29e9838d5d2 100644
--- a/src/compiler/spirv/vtn_variables.c
+++ b/src/compiler/spirv/vtn_variables.c
@@ -1541,18 +1541,14 @@  var_decoration_cb(struct vtn_builder *b, struct vtn_value *val, int member,
     */
    if (dec->decoration == SpvDecorationLocation) {
       unsigned location = dec->literals[0];
-      bool is_vertex_input;
       if (b->shader->info.stage == MESA_SHADER_FRAGMENT &&
           vtn_var->mode == vtn_variable_mode_output) {
-         is_vertex_input = false;
          location += FRAG_RESULT_DATA0;
       } else if (b->shader->info.stage == MESA_SHADER_VERTEX &&
                  vtn_var->mode == vtn_variable_mode_input) {
-         is_vertex_input = true;
          location += VERT_ATTRIB_GENERIC0;
       } else if (vtn_var->mode == vtn_variable_mode_input ||
                  vtn_var->mode == vtn_variable_mode_output) {
-         is_vertex_input = false;
          location += vtn_var->patch ? VARYING_SLOT_PATCH0 : VARYING_SLOT_VAR0;
       } else {
          vtn_warn("Location must be on input or output variable");
@@ -1565,14 +1561,10 @@  var_decoration_cb(struct vtn_builder *b, struct vtn_value *val, int member,
       } else {
          /* This handles the structure member case */
          assert(vtn_var->members);
-         unsigned length =
-            glsl_get_length(glsl_without_array(vtn_var->type->type));
-         for (unsigned i = 0; i < length; i++) {
-            vtn_var->members[i]->data.location = location;
-            location +=
-               glsl_count_attribute_slots(vtn_var->members[i]->interface_type,
-                                          is_vertex_input);
-         }
+         if (member == -1)
+            vtn_var->base_location = location;
+         else
+            vtn_var->members[member]->data.location = location;
       }
       return;
    } else {
@@ -1756,6 +1748,40 @@  is_per_vertex_inout(const struct vtn_variable *var, gl_shader_stage stage)
    return false;
 }
 
+static void
+add_missing_member_locations(struct vtn_variable *var,
+                             bool is_vertex_input)
+{
+   unsigned length =
+      glsl_get_length(glsl_without_array(var->type->type));
+   int location = var->base_location;
+
+   for (unsigned i = 0; i < length; i++) {
+      /* From the Vulkan spec:
+       *
+       * “If the structure type is a Block but without a Location, then each
+       *  of its members must have a Location decoration.”
+       */
+      assert(var->base_location != -1 ||
+             var->members[i]->data.location != -1);
+
+      /* From the Vulkan spec:
+       *
+       * “Any member with its own Location decoration is assigned that
+       *  location. Each remaining member is assigned the location after the
+       *  immediately preceding member in declaration order.”
+       */
+      if (var->members[i]->data.location != -1)
+         location = var->members[i]->data.location;
+      else
+         var->members[i]->data.location = location;
+
+      location +=
+         glsl_count_attribute_slots(var->members[i]->interface_type,
+                                    is_vertex_input);
+   }
+}
+
 static void
 vtn_create_variable(struct vtn_builder *b, struct vtn_value *val,
                     struct vtn_type *ptr_type, SpvStorageClass storage_class,
@@ -1796,6 +1822,7 @@  vtn_create_variable(struct vtn_builder *b, struct vtn_value *val,
    struct vtn_variable *var = rzalloc(b, struct vtn_variable);
    var->type = type;
    var->mode = mode;
+   var->base_location = -1;
 
    vtn_assert(val->value_type == vtn_value_type_pointer);
    val->pointer = vtn_pointer_for_variable(b, var, ptr_type);
@@ -1900,6 +1927,7 @@  vtn_create_variable(struct vtn_builder *b, struct vtn_value *val,
                interface_type->members[i]->type;
             var->members[i]->data.mode = nir_mode;
             var->members[i]->data.patch = var->patch;
+            var->members[i]->data.location = -1;
 
             if (initializer) {
                assert(i < initializer->num_elements);
@@ -1944,6 +1972,14 @@  vtn_create_variable(struct vtn_builder *b, struct vtn_value *val,
 
    vtn_foreach_decoration(b, val, var_decoration_cb, var);
 
+   if ((var->mode == vtn_variable_mode_input ||
+        var->mode == vtn_variable_mode_output) &&
+       var->members) {
+      bool is_vertex_input = (b->shader->info.stage == MESA_SHADER_VERTEX &&
+                              var->mode == vtn_variable_mode_input);
+      add_missing_member_locations(var, is_vertex_input);
+   }
+
    if (var->mode == vtn_variable_mode_image ||
        var->mode == vtn_variable_mode_sampler) {
       /* XXX: We still need the binding information in the nir_variable