r600g: handle PIPE_QUERY_GPU_FINISHED
[mesa.git] / src / glsl / link_varyings.cpp
index 85a8b90d7d66ca387ca320d740c71c71769c79f4..6863298573fb335b04c692def37a63a67d269b46 100644 (file)
@@ -77,8 +77,7 @@ cross_validate_types_and_qualifiers(struct gl_shader_program *prog,
        *     correspondence between the vertex language and the
        *     fragment language."
        */
-      if (!output->type->is_array()
-          || (strncmp("gl_", output->name, 3) != 0)) {
+      if (!output->type->is_array() || !is_gl_identifier(output->name)) {
          linker_error(prog,
                       "%s shader output `%s' declared as type `%s', "
                       "but %s shader input declared as type `%s'\n",
@@ -172,6 +171,7 @@ cross_validate_outputs_to_inputs(struct gl_shader_program *prog,
                                 gl_shader *producer, gl_shader *consumer)
 {
    glsl_symbol_table parameters;
+   ir_variable *explicit_locations[MAX_VARYING] = { NULL, };
 
    /* Find all shader outputs in the "producer" stage.
     */
@@ -181,7 +181,26 @@ cross_validate_outputs_to_inputs(struct gl_shader_program *prog,
       if ((var == NULL) || (var->data.mode != ir_var_shader_out))
         continue;
 
-      parameters.add_variable(var);
+      if (!var->data.explicit_location
+          || var->data.location < VARYING_SLOT_VAR0)
+         parameters.add_variable(var);
+      else {
+         /* User-defined varyings with explicit locations are handled
+          * differently because they do not need to have matching names.
+          */
+         const unsigned idx = var->data.location - VARYING_SLOT_VAR0;
+
+         if (explicit_locations[idx] != NULL) {
+            linker_error(prog,
+                         "%s shader has multiple outputs explicitly "
+                         "assigned to location %d\n",
+                         _mesa_shader_stage_to_string(producer->Stage),
+                         idx);
+            return;
+         }
+
+         explicit_locations[idx] = var;
+      }
    }
 
 
@@ -220,7 +239,27 @@ cross_validate_outputs_to_inputs(struct gl_shader_program *prog,
                                              front_color, back_color,
                                              consumer->Stage, producer->Stage);
       } else {
-         ir_variable *const output = parameters.get_variable(input->name);
+         /* The rules for connecting inputs and outputs change in the presence
+          * of explicit locations.  In this case, we no longer care about the
+          * names of the variables.  Instead, we care only about the
+          * explicitly assigned location.
+          */
+         ir_variable *output = NULL;
+         if (input->data.explicit_location
+             && input->data.location >= VARYING_SLOT_VAR0) {
+            output = explicit_locations[input->data.location - VARYING_SLOT_VAR0];
+
+            if (output == NULL) {
+               linker_error(prog,
+                            "%s shader input `%s' with explicit location "
+                            "has no matching output\n",
+                            _mesa_shader_stage_to_string(consumer->Stage),
+                            input->name);
+            }
+         } else {
+            output = parameters.get_variable(input->name);
+         }
+
          if (output != NULL) {
             cross_validate_types_and_qualifiers(prog, input, output,
                                                 consumer->Stage, producer->Stage);
@@ -952,7 +991,7 @@ varying_matches::match_comparator(const void *x_generic, const void *y_generic)
  * varyings, but excludes variables such as gl_FrontFacing and gl_FragCoord.
  */
 static bool
-is_varying_var(gl_shader_stage stage, const ir_variable *var)
+var_counts_against_varying_limit(gl_shader_stage stage, const ir_variable *var)
 {
    /* Only fragment shaders will take a varying variable as an input */
    if (stage == MESA_SHADER_FRAGMENT &&
@@ -1051,8 +1090,13 @@ namespace linker {
 bool
 populate_consumer_input_sets(void *mem_ctx, exec_list *ir,
                              hash_table *consumer_inputs,
-                             hash_table *consumer_interface_inputs)
+                             hash_table *consumer_interface_inputs,
+                             ir_variable *consumer_inputs_with_locations[VARYING_SLOT_MAX])
 {
+   memset(consumer_inputs_with_locations,
+          0,
+          sizeof(consumer_inputs_with_locations[0]) * VARYING_SLOT_MAX);
+
    foreach_list(node, ir) {
       ir_variable *const input_var = ((ir_instruction *) node)->as_variable();
 
@@ -1060,7 +1104,26 @@ populate_consumer_input_sets(void *mem_ctx, exec_list *ir,
          if (input_var->type->is_interface())
             return false;
 
-         if (input_var->get_interface_type() != NULL) {
+         if (input_var->data.explicit_location) {
+            /* assign_varying_locations only cares about finding the
+             * ir_variable at the start of a contiguous location block.
+             *
+             *     - For !producer, consumer_inputs_with_locations isn't used.
+             *
+             *     - For !consumer, consumer_inputs_with_locations is empty.
+             *
+             * For consumer && producer, if you were trying to set some
+             * ir_variable to the middle of a location block on the other side
+             * of producer/consumer, cross_validate_outputs_to_inputs() should
+             * be link-erroring due to either type mismatch or location
+             * overlaps.  If the variables do match up, then they've got a
+             * matching data.location and you only looked at
+             * consumer_inputs_with_locations[var->data.location], not any
+             * following entries for the array/structure.
+             */
+            consumer_inputs_with_locations[input_var->data.location] =
+               input_var;
+         } else if (input_var->get_interface_type() != NULL) {
             char *const iface_field_name =
                ralloc_asprintf(mem_ctx, "%s.%s",
                                input_var->get_interface_type()->name,
@@ -1087,11 +1150,14 @@ ir_variable *
 get_matching_input(void *mem_ctx,
                    const ir_variable *output_var,
                    hash_table *consumer_inputs,
-                   hash_table *consumer_interface_inputs)
+                   hash_table *consumer_interface_inputs,
+                   ir_variable *consumer_inputs_with_locations[VARYING_SLOT_MAX])
 {
    ir_variable *input_var;
 
-   if (output_var->get_interface_type() != NULL) {
+   if (output_var->data.explicit_location) {
+      input_var = consumer_inputs_with_locations[output_var->data.location];
+   } else if (output_var->get_interface_type() != NULL) {
       char *const iface_field_name =
          ralloc_asprintf(mem_ctx, "%s.%s",
                          output_var->get_interface_type()->name,
@@ -1110,6 +1176,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").
@@ -1150,23 +1276,37 @@ assign_varying_locations(struct gl_context *ctx,
       = hash_table_ctor(0, hash_table_string_hash, hash_table_string_compare);
    hash_table *consumer_interface_inputs
       = hash_table_ctor(0, hash_table_string_hash, hash_table_string_compare);
+   ir_variable *consumer_inputs_with_locations[VARYING_SLOT_MAX] = {
+      NULL,
+   };
 
-   /* 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,
                                                 consumer->ir,
                                                 consumer_inputs,
-                                                consumer_interface_inputs)) {
+                                                consumer_interface_inputs,
+                                                consumer_inputs_with_locations)) {
       assert(!"populate_consumer_input_sets failed");
       hash_table_dtor(tfeedback_candidates);
       hash_table_dtor(consumer_inputs);
@@ -1188,7 +1328,8 @@ assign_varying_locations(struct gl_context *ctx,
 
          ir_variable *const input_var =
             linker::get_matching_input(mem_ctx, output_var, consumer_inputs,
-                                       consumer_interface_inputs);
+                                       consumer_interface_inputs,
+                                       consumer_inputs_with_locations);
 
          /* If a matching input variable was found, add this ouptut (and the
           * input) to the set.  If this is a separable program and there is no
@@ -1320,7 +1461,7 @@ check_against_output_limit(struct gl_context *ctx,
       ir_variable *const var = ((ir_instruction *) node)->as_variable();
 
       if (var && var->data.mode == ir_var_shader_out &&
-          is_varying_var(producer->Stage, var)) {
+          var_counts_against_varying_limit(producer->Stage, var)) {
          output_vectors += var->type->count_attribute_slots();
       }
    }
@@ -1359,7 +1500,7 @@ check_against_input_limit(struct gl_context *ctx,
       ir_variable *const var = ((ir_instruction *) node)->as_variable();
 
       if (var && var->data.mode == ir_var_shader_in &&
-          is_varying_var(consumer->Stage, var)) {
+          var_counts_against_varying_limit(consumer->Stage, var)) {
          input_vectors += var->type->count_attribute_slots();
       }
    }