nir/spirv: Break variable creation out into a helper
authorJason Ekstrand <jason@jlekstrand.net>
Thu, 29 Jun 2017 17:33:32 +0000 (10:33 -0700)
committerJason Ekstrand <jason.ekstrand@intel.com>
Wed, 5 Jul 2017 22:26:53 +0000 (15:26 -0700)
Reviewed-by: Connor Abbott <cwabbott0@gmail.com>
src/compiler/spirv/vtn_variables.c

index 3b474dc3c375dad560b04c9033f344dd1e0277f6..cc3ae0a2d5f6647fe4c278cd41f329127aa48de7 100644 (file)
@@ -1327,210 +1327,222 @@ is_per_vertex_inout(const struct vtn_variable *var, gl_shader_stage stage)
    return false;
 }
 
-void
-vtn_handle_variables(struct vtn_builder *b, SpvOp opcode,
-                     const uint32_t *w, unsigned count)
+static void
+vtn_create_variable(struct vtn_builder *b, struct vtn_value *val,
+                    struct vtn_type *type, SpvStorageClass storage_class,
+                    nir_constant *initializer)
 {
-   switch (opcode) {
-   case SpvOpUndef: {
-      struct vtn_value *val = vtn_push_value(b, w[2], vtn_value_type_undef);
-      val->type = vtn_value(b, w[1], vtn_value_type_type)->type;
-      break;
-   }
+   struct vtn_type *without_array = type;
+   while(glsl_type_is_array(without_array->type))
+      without_array = without_array->array_element;
 
-   case SpvOpVariable: {
-      struct vtn_type *type = vtn_value(b, w[1], vtn_value_type_type)->type;
+   enum vtn_variable_mode mode;
+   nir_variable_mode nir_mode;
+   mode = vtn_storage_class_to_mode(storage_class, without_array, &nir_mode);
 
-      struct vtn_type *without_array = type;
-      while(glsl_type_is_array(without_array->type))
-         without_array = without_array->array_element;
+   switch (mode) {
+   case vtn_variable_mode_ubo:
+      b->shader->info.num_ubos++;
+      break;
+   case vtn_variable_mode_ssbo:
+      b->shader->info.num_ssbos++;
+      break;
+   case vtn_variable_mode_image:
+      b->shader->info.num_images++;
+      break;
+   case vtn_variable_mode_sampler:
+      b->shader->info.num_textures++;
+      break;
+   case vtn_variable_mode_push_constant:
+      b->shader->num_uniforms = vtn_type_block_size(type);
+      break;
+   default:
+      /* No tallying is needed */
+      break;
+   }
 
-      enum vtn_variable_mode mode;
-      nir_variable_mode nir_mode;
-      mode = vtn_storage_class_to_mode(w[3], without_array, &nir_mode);
+   struct vtn_variable *var = rzalloc(b, struct vtn_variable);
+   var->type = type;
+   var->mode = mode;
+
+   assert(val->value_type == vtn_value_type_pointer);
+   val->pointer = vtn_pointer_for_variable(b, var);
+
+   switch (var->mode) {
+   case vtn_variable_mode_local:
+   case vtn_variable_mode_global:
+   case vtn_variable_mode_image:
+   case vtn_variable_mode_sampler:
+   case vtn_variable_mode_workgroup:
+      /* For these, we create the variable normally */
+      var->var = rzalloc(b->shader, nir_variable);
+      var->var->name = ralloc_strdup(var->var, val->name);
+      var->var->type = var->type->type;
+      var->var->data.mode = nir_mode;
 
-      switch (mode) {
-      case vtn_variable_mode_ubo:
-         b->shader->info.num_ubos++;
-         break;
-      case vtn_variable_mode_ssbo:
-         b->shader->info.num_ssbos++;
-         break;
+      switch (var->mode) {
       case vtn_variable_mode_image:
-         b->shader->info.num_images++;
-         break;
       case vtn_variable_mode_sampler:
-         b->shader->info.num_textures++;
-         break;
-      case vtn_variable_mode_push_constant:
-         b->shader->num_uniforms = vtn_type_block_size(type);
+         var->var->interface_type = without_array->type;
          break;
       default:
-         /* No tallying is needed */
+         var->var->interface_type = NULL;
          break;
       }
+      break;
 
-      struct vtn_variable *var = rzalloc(b, struct vtn_variable);
-      var->type = type;
-      var->mode = mode;
+   case vtn_variable_mode_input:
+   case vtn_variable_mode_output: {
+      /* In order to know whether or not we're a per-vertex inout, we need
+       * the patch qualifier.  This means walking the variable decorations
+       * early before we actually create any variables.  Not a big deal.
+       *
+       * GLSLang really likes to place decorations in the most interior
+       * thing it possibly can.  In particular, if you have a struct, it
+       * will place the patch decorations on the struct members.  This
+       * should be handled by the variable splitting below just fine.
+       *
+       * If you have an array-of-struct, things get even more weird as it
+       * will place the patch decorations on the struct even though it's
+       * inside an array and some of the members being patch and others not
+       * makes no sense whatsoever.  Since the only sensible thing is for
+       * it to be all or nothing, we'll call it patch if any of the members
+       * are declared patch.
+       */
+      var->patch = false;
+      vtn_foreach_decoration(b, val, var_is_patch_cb, &var->patch);
+      if (glsl_type_is_array(var->type->type) &&
+          glsl_type_is_struct(without_array->type)) {
+         vtn_foreach_decoration(b, without_array->val,
+                                var_is_patch_cb, &var->patch);
+      }
 
-      struct vtn_value *val = vtn_push_value(b, w[2], vtn_value_type_pointer);
-      val->pointer = vtn_pointer_for_variable(b, var);
+      /* For inputs and outputs, we immediately split structures.  This
+       * is for a couple of reasons.  For one, builtins may all come in
+       * a struct and we really want those split out into separate
+       * variables.  For another, interpolation qualifiers can be
+       * applied to members of the top-level struct ane we need to be
+       * able to preserve that information.
+       */
 
-      switch (var->mode) {
-      case vtn_variable_mode_local:
-      case vtn_variable_mode_global:
-      case vtn_variable_mode_image:
-      case vtn_variable_mode_sampler:
-      case vtn_variable_mode_workgroup:
-         /* For these, we create the variable normally */
+      int array_length = -1;
+      struct vtn_type *interface_type = var->type;
+      if (is_per_vertex_inout(var, b->shader->stage)) {
+         /* In Geometry shaders (and some tessellation), inputs come
+          * in per-vertex arrays.  However, some builtins come in
+          * non-per-vertex, hence the need for the is_array check.  In
+          * any case, there are no non-builtin arrays allowed so this
+          * check should be sufficient.
+          */
+         interface_type = var->type->array_element;
+         array_length = glsl_get_length(var->type->type);
+      }
+
+      if (glsl_type_is_struct(interface_type->type)) {
+         /* It's a struct.  Split it. */
+         unsigned num_members = glsl_get_length(interface_type->type);
+         var->members = ralloc_array(b, nir_variable *, num_members);
+
+         for (unsigned i = 0; i < num_members; i++) {
+            const struct glsl_type *mtype = interface_type->members[i]->type;
+            if (array_length >= 0)
+               mtype = glsl_array_type(mtype, array_length);
+
+            var->members[i] = rzalloc(b->shader, nir_variable);
+            var->members[i]->name =
+               ralloc_asprintf(var->members[i], "%s.%d", val->name, i);
+            var->members[i]->type = mtype;
+            var->members[i]->interface_type =
+               interface_type->members[i]->type;
+            var->members[i]->data.mode = nir_mode;
+            var->members[i]->data.patch = var->patch;
+         }
+      } else {
          var->var = rzalloc(b->shader, nir_variable);
          var->var->name = ralloc_strdup(var->var, val->name);
          var->var->type = var->type->type;
+         var->var->interface_type = interface_type->type;
          var->var->data.mode = nir_mode;
+         var->var->data.patch = var->patch;
+      }
 
-         switch (var->mode) {
-         case vtn_variable_mode_image:
-         case vtn_variable_mode_sampler:
-            var->var->interface_type = without_array->type;
-            break;
-         default:
-            var->var->interface_type = NULL;
-            break;
-         }
-         break;
+      /* For inputs and outputs, we need to grab locations and builtin
+       * information from the interface type.
+       */
+      vtn_foreach_decoration(b, interface_type->val, var_decoration_cb, var);
+      break;
+   }
 
-      case vtn_variable_mode_input:
-      case vtn_variable_mode_output: {
-         /* In order to know whether or not we're a per-vertex inout, we need
-          * the patch qualifier.  This means walking the variable decorations
-          * early before we actually create any variables.  Not a big deal.
-          *
-          * GLSLang really likes to place decorations in the most interior
-          * thing it possibly can.  In particular, if you have a struct, it
-          * will place the patch decorations on the struct members.  This
-          * should be handled by the variable splitting below just fine.
-          *
-          * If you have an array-of-struct, things get even more weird as it
-          * will place the patch decorations on the struct even though it's
-          * inside an array and some of the members being patch and others not
-          * makes no sense whatsoever.  Since the only sensible thing is for
-          * it to be all or nothing, we'll call it patch if any of the members
-          * are declared patch.
-          */
-         var->patch = false;
-         vtn_foreach_decoration(b, val, var_is_patch_cb, &var->patch);
-         if (glsl_type_is_array(var->type->type) &&
-             glsl_type_is_struct(without_array->type)) {
-            vtn_foreach_decoration(b, without_array->val,
-                                   var_is_patch_cb, &var->patch);
-         }
+   case vtn_variable_mode_param:
+      unreachable("Not created through OpVariable");
 
-         /* For inputs and outputs, we immediately split structures.  This
-          * is for a couple of reasons.  For one, builtins may all come in
-          * a struct and we really want those split out into separate
-          * variables.  For another, interpolation qualifiers can be
-          * applied to members of the top-level struct ane we need to be
-          * able to preserve that information.
-          */
+   case vtn_variable_mode_ubo:
+   case vtn_variable_mode_ssbo:
+   case vtn_variable_mode_push_constant:
+      /* These don't need actual variables. */
+      break;
+   }
 
-         int array_length = -1;
-         struct vtn_type *interface_type = var->type;
-         if (is_per_vertex_inout(var, b->shader->stage)) {
-            /* In Geometry shaders (and some tessellation), inputs come
-             * in per-vertex arrays.  However, some builtins come in
-             * non-per-vertex, hence the need for the is_array check.  In
-             * any case, there are no non-builtin arrays allowed so this
-             * check should be sufficient.
-             */
-            interface_type = var->type->array_element;
-            array_length = glsl_get_length(var->type->type);
-         }
+   if (initializer) {
+      var->var->constant_initializer =
+         nir_constant_clone(initializer, var->var);
+   }
 
-         if (glsl_type_is_struct(interface_type->type)) {
-            /* It's a struct.  Split it. */
-            unsigned num_members = glsl_get_length(interface_type->type);
-            var->members = ralloc_array(b, nir_variable *, num_members);
-
-            for (unsigned i = 0; i < num_members; i++) {
-               const struct glsl_type *mtype = interface_type->members[i]->type;
-               if (array_length >= 0)
-                  mtype = glsl_array_type(mtype, array_length);
-
-               var->members[i] = rzalloc(b->shader, nir_variable);
-               var->members[i]->name =
-                  ralloc_asprintf(var->members[i], "%s.%d", val->name, i);
-               var->members[i]->type = mtype;
-               var->members[i]->interface_type =
-                  interface_type->members[i]->type;
-               var->members[i]->data.mode = nir_mode;
-               var->members[i]->data.patch = var->patch;
-            }
-         } else {
-            var->var = rzalloc(b->shader, nir_variable);
-            var->var->name = ralloc_strdup(var->var, val->name);
-            var->var->type = var->type->type;
-            var->var->interface_type = interface_type->type;
-            var->var->data.mode = nir_mode;
-            var->var->data.patch = var->patch;
-         }
+   vtn_foreach_decoration(b, val, var_decoration_cb, var);
 
-         /* For inputs and outputs, we need to grab locations and builtin
-          * information from the interface type.
-          */
-         vtn_foreach_decoration(b, interface_type->val, var_decoration_cb, var);
-         break;
-      }
+   if (var->mode == vtn_variable_mode_image ||
+       var->mode == vtn_variable_mode_sampler) {
+      /* XXX: We still need the binding information in the nir_variable
+       * for these. We should fix that.
+       */
+      var->var->data.binding = var->binding;
+      var->var->data.descriptor_set = var->descriptor_set;
+      var->var->data.index = var->input_attachment_index;
 
-      case vtn_variable_mode_param:
-         unreachable("Not created through OpVariable");
+      if (var->mode == vtn_variable_mode_image)
+         var->var->data.image.format = without_array->image_format;
+   }
 
-      case vtn_variable_mode_ubo:
-      case vtn_variable_mode_ssbo:
-      case vtn_variable_mode_push_constant:
-         /* These don't need actual variables. */
-         break;
+   if (var->mode == vtn_variable_mode_local) {
+      assert(var->members == NULL && var->var != NULL);
+      nir_function_impl_add_variable(b->impl, var->var);
+   } else if (var->var) {
+      nir_shader_add_variable(b->shader, var->var);
+   } else if (var->members) {
+      unsigned count = glsl_get_length(without_array->type);
+      for (unsigned i = 0; i < count; i++) {
+         assert(var->members[i]->data.mode != nir_var_local);
+         nir_shader_add_variable(b->shader, var->members[i]);
       }
+   } else {
+      assert(var->mode == vtn_variable_mode_ubo ||
+             var->mode == vtn_variable_mode_ssbo ||
+             var->mode == vtn_variable_mode_push_constant);
+   }
+}
 
-      if (count > 4) {
-         assert(count == 5);
-         nir_constant *constant =
-            vtn_value(b, w[4], vtn_value_type_constant)->constant;
-         var->var->constant_initializer =
-            nir_constant_clone(constant, var->var);
-      }
+void
+vtn_handle_variables(struct vtn_builder *b, SpvOp opcode,
+                     const uint32_t *w, unsigned count)
+{
+   switch (opcode) {
+   case SpvOpUndef: {
+      struct vtn_value *val = vtn_push_value(b, w[2], vtn_value_type_undef);
+      val->type = vtn_value(b, w[1], vtn_value_type_type)->type;
+      break;
+   }
 
-      vtn_foreach_decoration(b, val, var_decoration_cb, var);
+   case SpvOpVariable: {
+      struct vtn_type *type = vtn_value(b, w[1], vtn_value_type_type)->type;
 
-      if (var->mode == vtn_variable_mode_image ||
-          var->mode == vtn_variable_mode_sampler) {
-         /* XXX: We still need the binding information in the nir_variable
-          * for these. We should fix that.
-          */
-         var->var->data.binding = var->binding;
-         var->var->data.descriptor_set = var->descriptor_set;
-         var->var->data.index = var->input_attachment_index;
+      struct vtn_value *val = vtn_push_value(b, w[2], vtn_value_type_pointer);
 
-         if (var->mode == vtn_variable_mode_image)
-            var->var->data.image.format = without_array->image_format;
-      }
+      SpvStorageClass storage_class = w[3];
+      nir_constant *initializer = NULL;
+      if (count > 4)
+         initializer = vtn_value(b, w[4], vtn_value_type_constant)->constant;
 
-      if (var->mode == vtn_variable_mode_local) {
-         assert(var->members == NULL && var->var != NULL);
-         nir_function_impl_add_variable(b->impl, var->var);
-      } else if (var->var) {
-         nir_shader_add_variable(b->shader, var->var);
-      } else if (var->members) {
-         unsigned count = glsl_get_length(without_array->type);
-         for (unsigned i = 0; i < count; i++) {
-            assert(var->members[i]->data.mode != nir_var_local);
-            nir_shader_add_variable(b->shader, var->members[i]);
-         }
-      } else {
-         assert(var->mode == vtn_variable_mode_ubo ||
-                var->mode == vtn_variable_mode_ssbo ||
-                var->mode == vtn_variable_mode_push_constant);
-      }
+      vtn_create_variable(b, val, type, storage_class, initializer);
       break;
    }