<div dir="ltr">This patch was sent out for review before the Mesa 10.0 branch but didn't land until after the branch was made.  It should be cherry-picked to stable, since it's a bug fix to functionality that is new in Mesa 10.0.<br>
<div><br><div class="gmail_quote">---------- Forwarded message ----------<br>From: <b class="gmail_sendername">Paul Berry</b> <span dir="ltr"><<a href="mailto:stereotype441@gmail.com">stereotype441@gmail.com</a>></span><br>
Date: 30 October 2013 16:33<br>Subject: [PATCH] glsl: Rework interface block linking.<br>To: <a href="mailto:mesa-dev@lists.freedesktop.org">mesa-dev@lists.freedesktop.org</a><br><br><br>Previously, when doing intrastage and interstage interface block<br>

linking, we only checked the interface type; this prevented us from<br>
catching some link errors.<br>
<br>
We now check the following additional constraints:<br>
<br>
- For intrastage linking, the presence/absence of interface names must<br>
  match.<br>
<br>
- For shader ins/outs, the interface names themselves must match when<br>
  doing intrastage linking (note: it's not clear from the spec whether<br>
  this is necessary, but Mesa's implementation currently relies on<br>
  it).<br>
<br>
- Array vs. nonarray must be consistent, taking into account the<br>
  special rules for vertex-geometry linkage.<br>
<br>
- Array sizes must be consistent (exception: during intrastage<br>
  linking, an unsized array matches a sized array).<br>
<br>
Note: validate_interstage_interface_blocks currently handles both<br>
uniforms and in/out variables.  As a result, if all three shader types<br>
are present (VS, GS, and FS), and a uniform interface block is<br>
mentioned in the VS and FS but not the GS, it won't be validated.  I<br>
plan to address this in later patches.<br>
<br>
Fixes the following piglit tests in spec/glsl-1.50/linker:<br>
- interface-blocks-vs-fs-array-size-mismatch<br>
- interface-vs-array-to-fs-unnamed<br>
- interface-vs-unnamed-to-fs-array<br>
- intrastage-interface-unnamed-array<br>
---<br>
 src/glsl/link_interface_blocks.cpp | 280 ++++++++++++++++++++++++++++++++++---<br>
 1 file changed, 260 insertions(+), 20 deletions(-)<br>
<br>
diff --git a/src/glsl/link_interface_blocks.cpp b/src/glsl/link_interface_blocks.cpp<br>
index 4f1c9d3..58c9538 100644<br>
--- a/src/glsl/link_interface_blocks.cpp<br>
+++ b/src/glsl/link_interface_blocks.cpp<br>
@@ -30,13 +30,219 @@<br>
 #include "glsl_symbol_table.h"<br>
 #include "linker.h"<br>
 #include "main/macros.h"<br>
+#include "program/hash_table.h"<br>
+<br>
+<br>
+namespace {<br>
+<br>
+/**<br>
+ * Information about a single interface block definition that we need to keep<br>
+ * track of in order to check linkage rules.<br>
+ *<br>
+ * Note: this class is expected to be short lived, so it doesn't make copies<br>
+ * of the strings it references; it simply borrows the pointers from the<br>
+ * ir_variable class.<br>
+ */<br>
+struct interface_block_definition<br>
+{<br>
+   /**<br>
+    * Extract an interface block definition from an ir_variable that<br>
+    * represents either the interface instance (for named interfaces), or a<br>
+    * member of the interface (for unnamed interfaces).<br>
+    */<br>
+   explicit interface_block_definition(const ir_variable *var)<br>
+      : type(var->get_interface_type()),<br>
+        instance_name(NULL),<br>
+        array_size(-1)<br>
+   {<br>
+      if (var->is_interface_instance()) {<br>
+         instance_name = var->name;<br>
+         if (var->type->is_array())<br>
+            array_size = var->type->length;<br>
+      }<br>
+   }<br>
+<br>
+   /**<br>
+    * Interface block type<br>
+    */<br>
+   const glsl_type *type;<br>
+<br>
+   /**<br>
+    * For a named interface block, the instance name.  Otherwise NULL.<br>
+    */<br>
+   const char *instance_name;<br>
+<br>
+   /**<br>
+    * For an interface block array, the array size (or 0 if unsized).<br>
+    * Otherwise -1.<br>
+    */<br>
+   int array_size;<br>
+};<br>
+<br>
+<br>
+/**<br>
+ * Check if two interfaces match, according to intrastage interface matching<br>
+ * rules.  If they do, and the first interface uses an unsized array, it will<br>
+ * be updated to reflect the array size declared in the second interface.<br>
+ */<br>
+bool<br>
+intrastage_match(interface_block_definition *a,<br>
+                 const interface_block_definition *b,<br>
+                 ir_variable_mode mode)<br>
+{<br>
+   /* Types must match. */<br>
+   if (a->type != b->type)<br>
+      return false;<br>
+<br>
+   /* Presence/absence of interface names must match. */<br>
+   if ((a->instance_name == NULL) != (b->instance_name == NULL))<br>
+      return false;<br>
+<br>
+   /* For uniforms, instance names need not match.  For shader ins/outs,<br>
+    * it's not clear from the spec whether they need to match, but<br>
+    * Mesa's implementation relies on them matching.<br>
+    */<br>
+   if (a->instance_name != NULL && mode != ir_var_uniform &&<br>
+       strcmp(a->instance_name, b->instance_name) != 0) {<br>
+      return false;<br>
+   }<br>
+<br>
+   /* Array vs. nonarray must be consistent, and sizes must be<br>
+    * consistent, with the exception that unsized arrays match sized<br>
+    * arrays.<br>
+    */<br>
+   if (a->array_size == -1) {<br>
+      if (b->array_size != -1)<br>
+         return false;<br>
+   } else if (a->array_size == 0) {<br>
+      if (b->array_size == -1)<br>
+         return false;<br>
+      else if (b->array_size != 0) {<br>
+         /* An unsized array matches a sized array.  Record the size to<br>
+          * match against future declarations.<br>
+          */<br>
+         a->array_size = b->array_size;<br>
+      }<br>
+   } else {<br>
+      /* An unsized array matches a sized array. */<br>
+      if (b->array_size != 0 && a->array_size != b->array_size)<br>
+         return false;<br>
+   }<br>
+   return true;<br>
+}<br>
+<br>
+<br>
+/**<br>
+ * Check if two interfaces match, according to interstage (in/out) interface<br>
+ * matching rules.<br>
+ *<br>
+ * If \c extra_array_level is true, then vertex-to-geometry shader matching<br>
+ * rules are enforced (i.e. a successful match requires the consumer interface<br>
+ * to be an array and the producer interface to be a non-array).<br>
+ */<br>
+bool<br>
+interstage_match(const interface_block_definition *producer,<br>
+                 const interface_block_definition *consumer,<br>
+                 bool extra_array_level)<br>
+{<br>
+   /* Unsized arrays should not occur during interstage linking.  They<br>
+    * should have all been assigned a size by link_intrastage_shaders.<br>
+    */<br>
+   assert(consumer->array_size != 0);<br>
+   assert(producer->array_size != 0);<br>
+<br>
+   /* Types must match. */<br>
+   if (consumer->type != producer->type)<br>
+      return false;<br>
+   if (extra_array_level) {<br>
+      /* Consumer must be an array, and producer must not. */<br>
+      if (consumer->array_size == -1)<br>
+         return false;<br>
+      if (producer->array_size != -1)<br>
+         return false;<br>
+   } else {<br>
+      /* Array vs. nonarray must be consistent, and sizes must be consistent.<br>
+       * Since unsized arrays have been ruled out, we can check this by just<br>
+       * making sure the sizes are equal.<br>
+       */<br>
+      if (consumer->array_size != producer->array_size)<br>
+         return false;<br>
+   }<br>
+   return true;<br>
+}<br>
+<br>
+<br>
+/**<br>
+ * This class keeps track of a mapping from an interface block name to the<br>
+ * necessary information about that interface block to determine whether to<br>
+ * generate a link error.<br>
+ *<br>
+ * Note: this class is expected to be short lived, so it doesn't make copies<br>
+ * of the strings it references; it simply borrows the pointers from the<br>
+ * ir_variable class.<br>
+ */<br>
+class interface_block_definitions<br>
+{<br>
+public:<br>
+   interface_block_definitions()<br>
+      : mem_ctx(ralloc_context(NULL)),<br>
+        ht(hash_table_ctor(0, hash_table_string_hash,<br>
+                           hash_table_string_compare))<br>
+   {<br>
+   }<br>
+<br>
+   ~interface_block_definitions()<br>
+   {<br>
+      hash_table_dtor(ht);<br>
+      ralloc_free(mem_ctx);<br>
+   }<br>
+<br>
+   /**<br>
+    * Lookup the interface definition having the given block name.  Return<br>
+    * NULL if none is found.<br>
+    */<br>
+   interface_block_definition *lookup(const char *block_name)<br>
+   {<br>
+      return (interface_block_definition *) hash_table_find(ht, block_name);<br>
+   }<br>
+<br>
+   /**<br>
+    * Add a new interface definition.<br>
+    */<br>
+   void store(const interface_block_definition &def)<br>
+   {<br>
+      interface_block_definition *hash_entry =<br>
+         rzalloc(mem_ctx, interface_block_definition);<br>
+      *hash_entry = def;<br>
+      hash_table_insert(ht, hash_entry, def.type->name);<br>
+   }<br>
+<br>
+private:<br>
+   /**<br>
+    * Ralloc context for data structures allocated by this class.<br>
+    */<br>
+   void *mem_ctx;<br>
+<br>
+   /**<br>
+    * Hash table mapping interface block name to an \c<br>
+    * interface_block_definition struct.  interface_block_definition structs<br>
+    * are allocated using \c mem_ctx.<br>
+    */<br>
+   hash_table *ht;<br>
+};<br>
+<br>
+<br>
+}; /* anonymous namespace */<br>
+<br>
<br>
 void<br>
 validate_intrastage_interface_blocks(struct gl_shader_program *prog,<br>
                                      const gl_shader **shader_list,<br>
                                      unsigned num_shaders)<br>
 {<br>
-   glsl_symbol_table interfaces;<br>
+   interface_block_definitions in_interfaces;<br>
+   interface_block_definitions out_interfaces;<br>
+   interface_block_definitions uniform_interfaces;<br>
<br>
    for (unsigned int i = 0; i < num_shaders; i++) {<br>
       if (shader_list[i] == NULL)<br>
@@ -52,17 +258,36 @@ validate_intrastage_interface_blocks(struct gl_shader_program *prog,<br>
          if (iface_type == NULL)<br>
             continue;<br>
<br>
-         const glsl_type *old_iface_type =<br>
-            interfaces.get_interface(iface_type->name,<br>
-                                     (enum ir_variable_mode) var->mode);<br>
+         interface_block_definitions *definitions;<br>
+         switch (var->mode) {<br>
+         case ir_var_shader_in:<br>
+            definitions = &in_interfaces;<br>
+            break;<br>
+         case ir_var_shader_out:<br>
+            definitions = &out_interfaces;<br>
+            break;<br>
+         case ir_var_uniform:<br>
+            definitions = &uniform_interfaces;<br>
+            break;<br>
+         default:<br>
+            /* Only in, out, and uniform interfaces are legal, so we should<br>
+             * never get here.<br>
+             */<br>
+            assert(!"illegal interface type");<br>
+            continue;<br>
+         }<br>
<br>
-         if (old_iface_type == NULL) {<br>
+         const interface_block_definition def(var);<br>
+         interface_block_definition *prev_def =<br>
+            definitions->lookup(iface_type->name);<br>
+<br>
+         if (prev_def == NULL) {<br>
             /* This is the first time we've seen the interface, so save<br>
-             * it into our symbol table.<br>
+             * it into the appropriate data structure.<br>
              */<br>
-            interfaces.add_interface(iface_type->name, iface_type,<br>
-                                     (enum ir_variable_mode) var->mode);<br>
-         } else if (old_iface_type != iface_type) {<br>
+            definitions->store(def);<br>
+         } else if (!intrastage_match(prev_def, &def,<br>
+                                      (ir_variable_mode) var->mode)) {<br>
             linker_error(prog, "definitions of interface block `%s' do not"<br>
                          " match\n", iface_type->name);<br>
             return;<br>
@@ -76,7 +301,9 @@ validate_interstage_interface_blocks(struct gl_shader_program *prog,<br>
                                      const gl_shader *producer,<br>
                                      const gl_shader *consumer)<br>
 {<br>
-   glsl_symbol_table interfaces;<br>
+   interface_block_definitions inout_interfaces;<br>
+   interface_block_definitions uniform_interfaces;<br>
+   bool extra_array_level = consumer->Type == GL_GEOMETRY_SHADER;<br>
<br>
    /* Add non-output interfaces from the consumer to the symbol table. */<br>
    foreach_list(node, consumer->ir) {<br>
@@ -84,9 +311,10 @@ validate_interstage_interface_blocks(struct gl_shader_program *prog,<br>
       if (!var || !var->get_interface_type() || var->mode == ir_var_shader_out)<br>
          continue;<br>
<br>
-      interfaces.add_interface(var->get_interface_type()->name,<br>
-                               var->get_interface_type(),<br>
-                               (enum ir_variable_mode) var->mode);<br>
+      interface_block_definitions *definitions = var->mode == ir_var_uniform ?<br>
+         &uniform_interfaces : &inout_interfaces;<br>
+      const interface_block_definition def(var);<br>
+      definitions->store(def);<br>
    }<br>
<br>
    /* Verify that the producer's interfaces match. */<br>
@@ -95,17 +323,29 @@ validate_interstage_interface_blocks(struct gl_shader_program *prog,<br>
       if (!var || !var->get_interface_type() || var->mode == ir_var_shader_in)<br>
          continue;<br>
<br>
-      enum ir_variable_mode consumer_mode =<br>
-         var->mode == ir_var_uniform ? ir_var_uniform : ir_var_shader_in;<br>
-      const glsl_type *expected_type =<br>
-         interfaces.get_interface(var->get_interface_type()->name,<br>
-                                  consumer_mode);<br>
+      interface_block_definitions *definitions = var->mode == ir_var_uniform ?<br>
+         &uniform_interfaces : &inout_interfaces;<br>
+      interface_block_definition *consumer_def =<br>
+         definitions->lookup(var->get_interface_type()->name);<br>
<br>
       /* The consumer doesn't use this output block.  Ignore it. */<br>
-      if (expected_type == NULL)<br>
+      if (consumer_def == NULL)<br>
          continue;<br>
<br>
-      if (var->get_interface_type() != expected_type) {<br>
+      const interface_block_definition producer_def(var);<br>
+      bool match;<br>
+      if (var->mode == ir_var_uniform) {<br>
+         /* Uniform matching rules are the same for interstage and intrastage<br>
+          * linking.<br>
+          */<br>
+         match = intrastage_match(consumer_def, &producer_def,<br>
+                                  (ir_variable_mode) var->mode);<br>
+      } else {<br>
+         match = interstage_match(&producer_def, consumer_def,<br>
+                                  extra_array_level);<br>
+      }<br>
+<br>
+      if (!match) {<br>
          linker_error(prog, "definitions of interface block `%s' do not "<br>
                       "match\n", var->get_interface_type()->name);<br>
          return;<br>
<span class="HOEnZb"><font color="#888888">--<br>
1.8.4.2<br>
<br>
</font></span></div><br></div></div>