linker: Sort shader I/O variables into a canonical order
authorIan Romanick <ian.d.romanick@intel.com>
Tue, 11 Feb 2014 23:38:23 +0000 (15:38 -0800)
committerIan Romanick <ian.d.romanick@intel.com>
Fri, 2 May 2014 14:19:40 +0000 (07:19 -0700)
v2: Rebase on removal of ir_variable::user_location.

Signed-off-by: Ian Romanick <ian.d.romanick@intel.com>
Reviewed-by: Eric Anholt <eric@anholt.net>
src/glsl/link_varyings.cpp

index 85a8b90d7d66ca387ca320d740c71c71769c79f4..e5c27516f5691096aabce8c6f79f423e074119be 100644 (file)
@@ -1110,6 +1110,66 @@ get_matching_input(void *mem_ctx,
 
 }
 
+static int
+io_variable_cmp(const void *_a, const void *_b)
+{
+   const ir_variable *const a = *(const ir_variable **) _a;
+   const ir_variable *const b = *(const ir_variable **) _b;
+
+   if (a->data.explicit_location && b->data.explicit_location)
+      return b->data.location - a->data.location;
+
+   if (a->data.explicit_location && !b->data.explicit_location)
+      return 1;
+
+   if (!a->data.explicit_location && b->data.explicit_location)
+      return -1;
+
+   return -strcmp(a->name, b->name);
+}
+
+/**
+ * Sort the shader IO variables into canonical order
+ */
+static void
+canonicalize_shader_io(exec_list *ir, enum ir_variable_mode io_mode)
+{
+   ir_variable *var_table[MAX_PROGRAM_OUTPUTS * 4];
+   unsigned num_variables = 0;
+
+   foreach_list(node, ir) {
+      ir_variable *const var = ((ir_instruction *) node)->as_variable();
+
+      if (var == NULL || var->data.mode != io_mode)
+         continue;
+
+      /* If we have already encountered more I/O variables that could
+       * successfully link, bail.
+       */
+      if (num_variables == ARRAY_SIZE(var_table))
+         return;
+
+      var_table[num_variables++] = var;
+   }
+
+   if (num_variables == 0)
+      return;
+
+   /* Sort the list in reverse order (io_variable_cmp handles this).  Later
+    * we're going to push the variables on to the IR list as a stack, so we
+    * want the last variable (in canonical order) to be first in the list.
+    */
+   qsort(var_table, num_variables, sizeof(var_table[0]), io_variable_cmp);
+
+   /* Remove the variable from it's current location in the IR, and put it at
+    * the front.
+    */
+   for (unsigned i = 0; i < num_variables; i++) {
+      var_table[i]->remove();
+      ir->push_head(var_table[i]);
+   }
+}
+
 /**
  * Assign locations for all variables that are produced in one pipeline stage
  * (the "producer") and consumed in the next stage (the "consumer").
@@ -1151,16 +1211,26 @@ assign_varying_locations(struct gl_context *ctx,
    hash_table *consumer_interface_inputs
       = hash_table_ctor(0, hash_table_string_hash, hash_table_string_compare);
 
-   /* Operate in a total of three passes.
+   /* Operate in a total of four passes.
     *
-    * 1. Assign locations for any matching inputs and outputs.
+    * 1. Sort inputs / outputs into a canonical order.  This is necessary so
+    *    that inputs / outputs of separable shaders will be assigned
+    *    predictable locations regardless of the order in which declarations
+    *    appeared in the shader source.
     *
-    * 2. Mark output variables in the producer that do not have locations as
+    * 2. Assign locations for any matching inputs and outputs.
+    *
+    * 3. Mark output variables in the producer that do not have locations as
     *    not being outputs.  This lets the optimizer eliminate them.
     *
-    * 3. Mark input variables in the consumer that do not have locations as
+    * 4. Mark input variables in the consumer that do not have locations as
     *    not being inputs.  This lets the optimizer eliminate them.
     */
+   if (consumer)
+      canonicalize_shader_io(consumer->ir, ir_var_shader_in);
+
+   if (producer)
+      canonicalize_shader_io(producer->ir, ir_var_shader_out);
 
    if (consumer
        && !linker::populate_consumer_input_sets(mem_ctx,