glsl: Modify array_sizing_visitor to handle unnamed interface blocks.
authorPaul Berry <stereotype441@gmail.com>
Wed, 25 Sep 2013 21:07:37 +0000 (14:07 -0700)
committerPaul Berry <stereotype441@gmail.com>
Wed, 9 Oct 2013 23:49:48 +0000 (16:49 -0700)
We were already setting the array size of unsized arrays that appeared
inside unnamed interface blocks, but we weren't updating
ir_variable::interface_type to reflect the new array size, causing
bogus link errors.

This patch causes array_sizing_visitor to keep track of all the
unnamed interface types it sees, and the ir_variables corresponding to
each one.  After the visitor runs, a new function,
fixup_unnamed_interface_types(), adjusts each unnamed interface type
to correctly correspond with the array sizes in the ir_variables.

Fixes piglit tests:
- spec/glsl-1.50/execution/unsized-in-unnamed-interface-block-gs
- spec/glsl-1.50/execution/unsized-in-unnamed-interface-block-multiple

Reviewed-by: Jordan Justen <jordan.l.justen@intel.com>
src/glsl/ir.h
src/glsl/linker.cpp

index 2a0afada1ea87156d011b8a04f98bbb203c9fa41..0f8e9a15d96616975c21fdac8c56393777475039 100644 (file)
@@ -406,8 +406,8 @@ public:
 
    /**
     * Change this->interface_type on a variable that previously had a
-    * different interface_type.  This is used during linking to set the size
-    * of arrays in interface blocks.
+    * different, but compatible, interface_type.  This is used during linking
+    * to set the size of arrays in interface blocks.
     */
    void change_interface_type(const struct glsl_type *type)
    {
index db1fe9a0d85fc4be063be0ade998c1e6122dacaf..9095a401540a3f4a0a49f612055396a2fe4610d8 100644 (file)
@@ -1033,6 +1033,19 @@ get_main_function_signature(gl_shader *sh)
  */
 class array_sizing_visitor : public ir_hierarchical_visitor {
 public:
+   array_sizing_visitor()
+      : mem_ctx(ralloc_context(NULL)),
+        unnamed_interfaces(hash_table_ctor(0, hash_table_pointer_hash,
+                                           hash_table_pointer_compare))
+   {
+   }
+
+   ~array_sizing_visitor()
+   {
+      hash_table_dtor(this->unnamed_interfaces);
+      ralloc_free(this->mem_ctx);
+   }
+
    virtual ir_visitor_status visit(ir_variable *var)
    {
       fixup_type(&var->type, var->max_array_access);
@@ -1053,10 +1066,38 @@ public:
             var->type =
                glsl_type::get_array_instance(new_type, var->type->length);
          }
+      } else if (const glsl_type *ifc_type = var->get_interface_type()) {
+         /* Store a pointer to the variable in the unnamed_interfaces
+          * hashtable.
+          */
+         ir_variable **interface_vars = (ir_variable **)
+            hash_table_find(this->unnamed_interfaces, ifc_type);
+         if (interface_vars == NULL) {
+            interface_vars = rzalloc_array(mem_ctx, ir_variable *,
+                                           ifc_type->length);
+            hash_table_insert(this->unnamed_interfaces, interface_vars,
+                              ifc_type);
+         }
+         unsigned index = ifc_type->field_index(var->name);
+         assert(index < ifc_type->length);
+         assert(interface_vars[index] == NULL);
+         interface_vars[index] = var;
       }
       return visit_continue;
    }
 
+   /**
+    * For each unnamed interface block that was discovered while running the
+    * visitor, adjust the interface type to reflect the newly assigned array
+    * sizes, and fix up the ir_variable nodes to point to the new interface
+    * type.
+    */
+   void fixup_unnamed_interface_types()
+   {
+      hash_table_call_foreach(this->unnamed_interfaces,
+                              fixup_unnamed_interface_type, NULL);
+   }
+
 private:
    /**
     * If the type pointed to by \c type represents an unsized array, replace
@@ -1109,6 +1150,50 @@ private:
       delete [] fields;
       return new_ifc_type;
    }
+
+   static void fixup_unnamed_interface_type(const void *key, void *data,
+                                            void *)
+   {
+      const glsl_type *ifc_type = (const glsl_type *) key;
+      ir_variable **interface_vars = (ir_variable **) data;
+      unsigned num_fields = ifc_type->length;
+      glsl_struct_field *fields = new glsl_struct_field[num_fields];
+      memcpy(fields, ifc_type->fields.structure,
+             num_fields * sizeof(*fields));
+      bool interface_type_changed = false;
+      for (unsigned i = 0; i < num_fields; i++) {
+         if (interface_vars[i] != NULL &&
+             fields[i].type != interface_vars[i]->type) {
+            fields[i].type = interface_vars[i]->type;
+            interface_type_changed = true;
+         }
+      }
+      if (!interface_type_changed) {
+         delete [] fields;
+         return;
+      }
+      glsl_interface_packing packing =
+         (glsl_interface_packing) ifc_type->interface_packing;
+      const glsl_type *new_ifc_type =
+         glsl_type::get_interface_instance(fields, num_fields, packing,
+                                           ifc_type->name);
+      delete [] fields;
+      for (unsigned i = 0; i < num_fields; i++) {
+         if (interface_vars[i] != NULL)
+            interface_vars[i]->change_interface_type(new_ifc_type);
+      }
+   }
+
+   /**
+    * Memory context used to allocate the data in \c unnamed_interfaces.
+    */
+   void *mem_ctx;
+
+   /**
+    * Hash table from const glsl_type * to an array of ir_variable *'s
+    * pointing to the ir_variables constituting each unnamed interface block.
+    */
+   hash_table *unnamed_interfaces;
 };
 
 /**
@@ -1383,6 +1468,7 @@ link_intrastage_shaders(void *mem_ctx,
     */
    array_sizing_visitor v;
    v.run(linked->ir);
+   v.fixup_unnamed_interface_types();
 
    return linked;
 }