glsl: allow linking of tessellation shaders.
[mesa.git] / src / glsl / link_interface_blocks.cpp
index a7fceb9bad63437d39d0c3b44d8e27ec33d3527c..936e2e0ba21c6e51206f8d91e410392c0e1fb243 100644 (file)
@@ -50,17 +50,20 @@ struct interface_block_definition
     * represents either the interface instance (for named interfaces), or a
     * member of the interface (for unnamed interfaces).
     */
-   explicit interface_block_definition(const ir_variable *var)
-      : type(var->get_interface_type()),
-        instance_name(NULL),
-        array_size(-1)
+   explicit interface_block_definition(ir_variable *var)
+      : var(var),
+        type(var->get_interface_type()),
+        instance_name(NULL)
    {
       if (var->is_interface_instance()) {
          instance_name = var->name;
-         if (var->type->is_array())
-            array_size = var->type->length;
       }
+      explicitly_declared = (var->data.how_declared != ir_var_declared_implicitly);
    }
+   /**
+    * Interface block ir_variable
+    */
+   ir_variable *var;
 
    /**
     * Interface block type
@@ -73,10 +76,10 @@ struct interface_block_definition
    const char *instance_name;
 
    /**
-    * For an interface block array, the array size (or 0 if unsized).
-    * Otherwise -1.
+    * True if this interface block was explicitly declared in the shader;
+    * false if it was an implicitly declared built-in interface block.
     */
-   int array_size;
+   bool explicitly_declared;
 };
 
 
@@ -88,11 +91,18 @@ struct interface_block_definition
 bool
 intrastage_match(interface_block_definition *a,
                  const interface_block_definition *b,
-                 ir_variable_mode mode)
+                 ir_variable_mode mode,
+                 struct gl_shader_program *prog)
 {
    /* Types must match. */
-   if (a->type != b->type)
-      return false;
+   if (a->type != b->type) {
+      /* Exception: if both the interface blocks are implicitly declared,
+       * don't force their types to match.  They might mismatch due to the two
+       * shaders using different GLSL versions, and that's ok.
+       */
+      if (a->explicitly_declared || b->explicitly_declared)
+         return false;
+   }
 
    /* Presence/absence of interface names must match. */
    if ((a->instance_name == NULL) != (b->instance_name == NULL))
@@ -102,23 +112,19 @@ intrastage_match(interface_block_definition *a,
     * it's not clear from the spec whether they need to match, but
     * Mesa's implementation relies on them matching.
     */
-   if (a->instance_name != NULL && mode != ir_var_uniform &&
+   if (a->instance_name != NULL &&
+       mode != ir_var_uniform && mode != ir_var_shader_storage &&
        strcmp(a->instance_name, b->instance_name) != 0) {
       return false;
    }
 
-   /* Array vs. nonarray must be consistent, and sizes must be
-    * consistent, with the exception that unsized arrays match sized
-    * arrays.
+   /* If a block is an array then it must match across the shader.
+    * Unsized arrays are also processed and matched agaist sized arrays.
     */
-   if ((a->array_size == -1) != (b->array_size == -1))
+   if (b->var->type != a->var->type &&
+       (b->instance_name != NULL || a->instance_name != NULL) &&
+       !validate_intrastage_arrays(prog, b->var, a->var))
       return false;
-   if (b->array_size != 0) {
-      if (a->array_size == 0)
-         a->array_size = b->array_size;
-      else if (a->array_size != b->array_size)
-         return false;
-   }
 
    return true;
 }
@@ -128,9 +134,9 @@ intrastage_match(interface_block_definition *a,
  * Check if two interfaces match, according to interstage (in/out) interface
  * matching rules.
  *
- * If \c extra_array_level is true, then vertex-to-geometry shader matching
- * rules are enforced (i.e. a successful match requires the consumer interface
- * to be an array and the producer interface to be a non-array).
+ * If \c extra_array_level is true, the consumer interface is required to be
+ * an array and the producer interface is required to be a non-array.
+ * This is used for tessellation control and geometry shader consumers.
  */
 bool
 interstage_match(const interface_block_definition *producer,
@@ -140,26 +146,39 @@ interstage_match(const interface_block_definition *producer,
    /* Unsized arrays should not occur during interstage linking.  They
     * should have all been assigned a size by link_intrastage_shaders.
     */
-   assert(consumer->array_size != 0);
-   assert(producer->array_size != 0);
+   assert(!consumer->var->type->is_unsized_array());
+   assert(!producer->var->type->is_unsized_array());
 
    /* Types must match. */
-   if (consumer->type != producer->type)
-      return false;
-   if (extra_array_level) {
-      /* Consumer must be an array, and producer must not. */
-      if (consumer->array_size == -1)
-         return false;
-      if (producer->array_size != -1)
+   if (consumer->type != producer->type) {
+      /* Exception: if both the interface blocks are implicitly declared,
+       * don't force their types to match.  They might mismatch due to the two
+       * shaders using different GLSL versions, and that's ok.
+       */
+      if (consumer->explicitly_declared || producer->explicitly_declared)
          return false;
+   }
+
+   /* Ignore outermost array if geom shader */
+   const glsl_type *consumer_instance_type;
+   if (extra_array_level) {
+      consumer_instance_type = consumer->var->type->fields.array;
    } else {
-      /* Array vs. nonarray must be consistent, and sizes must be consistent.
-       * Since unsized arrays have been ruled out, we can check this by just
-       * making sure the sizes are equal.
-       */
-      if (consumer->array_size != producer->array_size)
+      consumer_instance_type = consumer->var->type;
+   }
+
+   /* If a block is an array then it must match across shaders.
+    * Since unsized arrays have been ruled out, we can check this by just
+    * making sure the types are equal.
+    */
+   if ((consumer->instance_name != NULL &&
+        consumer_instance_type->is_array()) ||
+       (producer->instance_name != NULL &&
+        producer->var->type->is_array())) {
+      if (consumer_instance_type != producer->var->type)
          return false;
    }
+
    return true;
 }
 
@@ -235,13 +254,14 @@ validate_intrastage_interface_blocks(struct gl_shader_program *prog,
    interface_block_definitions in_interfaces;
    interface_block_definitions out_interfaces;
    interface_block_definitions uniform_interfaces;
+   interface_block_definitions buffer_interfaces;
 
    for (unsigned int i = 0; i < num_shaders; i++) {
       if (shader_list[i] == NULL)
          continue;
 
-      foreach_list(node, shader_list[i]->ir) {
-         ir_variable *var = ((ir_instruction *) node)->as_variable();
+      foreach_in_list(ir_instruction, node, shader_list[i]->ir) {
+         ir_variable *var = node->as_variable();
          if (!var)
             continue;
 
@@ -251,7 +271,7 @@ validate_intrastage_interface_blocks(struct gl_shader_program *prog,
             continue;
 
          interface_block_definitions *definitions;
-         switch (var->mode) {
+         switch (var->data.mode) {
          case ir_var_shader_in:
             definitions = &in_interfaces;
             break;
@@ -261,6 +281,9 @@ validate_intrastage_interface_blocks(struct gl_shader_program *prog,
          case ir_var_uniform:
             definitions = &uniform_interfaces;
             break;
+         case ir_var_shader_storage:
+            definitions = &buffer_interfaces;
+            break;
          default:
             /* Only in, out, and uniform interfaces are legal, so we should
              * never get here.
@@ -279,7 +302,8 @@ validate_intrastage_interface_blocks(struct gl_shader_program *prog,
              */
             definitions->store(def);
          } else if (!intrastage_match(prev_def, &def,
-                                      (ir_variable_mode) var->mode)) {
+                                      (ir_variable_mode) var->data.mode,
+                                      prog)) {
             linker_error(prog, "definitions of interface block `%s' do not"
                          " match\n", iface_type->name);
             return;
@@ -289,57 +313,85 @@ validate_intrastage_interface_blocks(struct gl_shader_program *prog,
 }
 
 void
-validate_interstage_interface_blocks(struct gl_shader_program *prog,
-                                     const gl_shader *producer,
-                                     const gl_shader *consumer)
+validate_interstage_inout_blocks(struct gl_shader_program *prog,
+                                 const gl_shader *producer,
+                                 const gl_shader *consumer)
 {
-   interface_block_definitions inout_interfaces;
-   interface_block_definitions uniform_interfaces;
-   const bool extra_array_level = consumer->Type == GL_GEOMETRY_SHADER;
-
-   /* Add non-output interfaces from the consumer to the symbol table. */
-   foreach_list(node, consumer->ir) {
-      ir_variable *var = ((ir_instruction *) node)->as_variable();
-      if (!var || !var->get_interface_type() || var->mode == ir_var_shader_out)
+   interface_block_definitions definitions;
+   /* VS -> GS, VS -> TCS, VS -> TES, TES -> GS */
+   const bool extra_array_level = (producer->Stage == MESA_SHADER_VERTEX &&
+                                   consumer->Stage != MESA_SHADER_FRAGMENT) ||
+                                  consumer->Stage == MESA_SHADER_GEOMETRY;
+
+   /* Add input interfaces from the consumer to the symbol table. */
+   foreach_in_list(ir_instruction, node, consumer->ir) {
+      ir_variable *var = node->as_variable();
+      if (!var || !var->get_interface_type() || var->data.mode != ir_var_shader_in)
          continue;
 
-      interface_block_definitions *definitions = var->mode == ir_var_uniform ?
-         &uniform_interfaces : &inout_interfaces;
-      definitions->store(interface_block_definition(var));
+      definitions.store(interface_block_definition(var));
    }
 
-   /* Verify that the producer's interfaces match. */
-   foreach_list(node, producer->ir) {
-      ir_variable *var = ((ir_instruction *) node)->as_variable();
-      if (!var || !var->get_interface_type() || var->mode == ir_var_shader_in)
+   /* Verify that the producer's output interfaces match. */
+   foreach_in_list(ir_instruction, node, producer->ir) {
+      ir_variable *var = node->as_variable();
+      if (!var || !var->get_interface_type() || var->data.mode != ir_var_shader_out)
          continue;
 
-      interface_block_definitions *definitions = var->mode == ir_var_uniform ?
-         &uniform_interfaces : &inout_interfaces;
       interface_block_definition *consumer_def =
-         definitions->lookup(var->get_interface_type()->name);
+         definitions.lookup(var->get_interface_type()->name);
 
       /* The consumer doesn't use this output block.  Ignore it. */
       if (consumer_def == NULL)
          continue;
 
       const interface_block_definition producer_def(var);
-      bool match;
-      if (var->mode == ir_var_uniform) {
-         /* Uniform matching rules are the same for interstage and intrastage
-          * linking.
-          */
-         match = intrastage_match(consumer_def, &producer_def,
-                                  (ir_variable_mode) var->mode);
-      } else {
-         match = interstage_match(&producer_def, consumer_def,
-                                  extra_array_level);
-      }
 
-      if (!match) {
+      if (!interstage_match(&producer_def, consumer_def, extra_array_level)) {
          linker_error(prog, "definitions of interface block `%s' do not "
                       "match\n", var->get_interface_type()->name);
          return;
       }
    }
 }
+
+
+void
+validate_interstage_uniform_blocks(struct gl_shader_program *prog,
+                                   gl_shader **stages, int num_stages)
+{
+   interface_block_definitions definitions;
+
+   for (int i = 0; i < num_stages; i++) {
+      if (stages[i] == NULL)
+         continue;
+
+      const gl_shader *stage = stages[i];
+      foreach_in_list(ir_instruction, node, stage->ir) {
+         ir_variable *var = node->as_variable();
+         if (!var || !var->get_interface_type() ||
+             (var->data.mode != ir_var_uniform &&
+              var->data.mode != ir_var_shader_storage))
+            continue;
+
+         interface_block_definition *old_def =
+            definitions.lookup(var->get_interface_type()->name);
+         const interface_block_definition new_def(var);
+         if (old_def == NULL) {
+            definitions.store(new_def);
+         } else {
+            /* Interstage uniform matching rules are the same as intrastage
+             * uniform matchin rules (for uniforms, it is as though all
+             * shaders are in the same shader stage).
+             */
+            if (!intrastage_match(old_def, &new_def,
+                                  (ir_variable_mode) var->data.mode,
+                                  prog)) {
+               linker_error(prog, "definitions of interface block `%s' do not "
+                            "match\n", var->get_interface_type()->name);
+               return;
+            }
+         }
+      }
+   }
+}