Mesa (master): linker: Modify cross_validate_outputs_to_inputs to match using explicit locations

Ian Romanick idr at kemper.freedesktop.org
Fri May 2 14:31:43 UTC 2014


Module: Mesa
Branch: master
Commit: 7ff937e5793dc8709c916e043b41142033c8e69e
URL:    http://cgit.freedesktop.org/mesa/mesa/commit/?id=7ff937e5793dc8709c916e043b41142033c8e69e

Author: Ian Romanick <ian.d.romanick at intel.com>
Date:   Wed Oct  2 12:39:29 2013 -0700

linker: Modify cross_validate_outputs_to_inputs to match using explicit locations

This will be used for GL_ARB_separate_shader_objects.  That extension
not only allows separable shaders to rendezvous by location, but it also
allows traditionally linked shaders to rendezvous by location.  The spec
says:

    36. How does the behavior of input/output interface matching differ
        between separable programs and non-separable programs?

        RESOLVED: The rules for matching individual variables or block
        members between stages are identical for separable and
        non-separable programs, with one exception -- matching variables
        of different type with the same location, as discussed in issue
        34, applies only to separable programs.

        However, the ability to enforce matching requirements differs
        between program types.  In non-separable programs, both sides of
        an interface are contained in the same linked program.  In this
        case, if the linker detects a mismatch, it will generate a link
        error.

v2: Make sure consumer_inputs_with_locations is initialized when
consumer is NULL.  Noticed by Chia-I.

v3: Rebase on removal of ir_variable::user_location.

v4: Replace a (stale) FINISHME with some good explanation comments from
Eric.

Signed-off-by: Ian Romanick <ian.d.romanick at intel.com>
Reviewed-by: Eric Anholt <eric at anholt.net>

---

 src/glsl/link_varyings.cpp       |   88 ++++++++++++++++++++++++++++++++++----
 src/glsl/tests/varyings_test.cpp |   35 +++++++++------
 2 files changed, 103 insertions(+), 20 deletions(-)

diff --git a/src/glsl/link_varyings.cpp b/src/glsl/link_varyings.cpp
index e5c2751..45f1b10 100644
--- a/src/glsl/link_varyings.cpp
+++ b/src/glsl/link_varyings.cpp
@@ -172,6 +172,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 +182,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 +240,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);
@@ -1051,8 +1091,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[MAX_VARYING])
 {
+   memset(consumer_inputs_with_locations,
+          0,
+          sizeof(consumer_inputs_with_locations[0]) * MAX_VARYING);
+
    foreach_list(node, ir) {
       ir_variable *const input_var = ((ir_instruction *) node)->as_variable();
 
@@ -1060,7 +1105,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 +1151,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[MAX_VARYING])
 {
    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,
@@ -1210,6 +1277,9 @@ 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[MAX_VARYING] = {
+      NULL,
+   };
 
    /* Operate in a total of four passes.
     *
@@ -1236,7 +1306,8 @@ assign_varying_locations(struct gl_context *ctx,
        && !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);
@@ -1258,7 +1329,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
diff --git a/src/glsl/tests/varyings_test.cpp b/src/glsl/tests/varyings_test.cpp
index 1749112..8a188a7 100644
--- a/src/glsl/tests/varyings_test.cpp
+++ b/src/glsl/tests/varyings_test.cpp
@@ -38,13 +38,15 @@ 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[MAX_VARYING]);
 
 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[MAX_VARYING]);
 }
 
 class link_varyings : public ::testing::Test {
@@ -68,6 +70,7 @@ public:
    hash_table *consumer_interface_inputs;
 
    const glsl_type *simple_interface;
+   ir_variable *junk[MAX_VARYING];
 };
 
 link_varyings::link_varyings()
@@ -164,7 +167,8 @@ TEST_F(link_varyings, single_simple_input)
    ASSERT_TRUE(linker::populate_consumer_input_sets(mem_ctx,
                                                     &ir,
                                                     consumer_inputs,
-                                                    consumer_interface_inputs));
+                                                    consumer_interface_inputs,
+                                                    junk));
 
    EXPECT_EQ((void *) v, hash_table_find(consumer_inputs, "a"));
    EXPECT_EQ(1u, num_elements(consumer_inputs));
@@ -190,7 +194,8 @@ TEST_F(link_varyings, gl_ClipDistance)
    ASSERT_TRUE(linker::populate_consumer_input_sets(mem_ctx,
                                                     &ir,
                                                     consumer_inputs,
-                                                    consumer_interface_inputs));
+                                                    consumer_interface_inputs,
+                                                    junk));
 
    EXPECT_EQ((void *) clipdistance,
              hash_table_find(consumer_inputs, "gl_ClipDistance"));
@@ -212,8 +217,8 @@ TEST_F(link_varyings, single_interface_input)
    ASSERT_TRUE(linker::populate_consumer_input_sets(mem_ctx,
                                                     &ir,
                                                     consumer_inputs,
-                                                    consumer_interface_inputs));
-
+                                                    consumer_interface_inputs,
+                                                    junk));
    char *const full_name = interface_field_name(simple_interface);
 
    EXPECT_EQ((void *) v, hash_table_find(consumer_interface_inputs, full_name));
@@ -243,7 +248,8 @@ TEST_F(link_varyings, one_interface_and_one_simple_input)
    ASSERT_TRUE(linker::populate_consumer_input_sets(mem_ctx,
                                                     &ir,
                                                     consumer_inputs,
-                                                    consumer_interface_inputs));
+                                                    consumer_interface_inputs,
+                                                    junk));
 
    char *const iface_field_name = interface_field_name(simple_interface);
 
@@ -269,7 +275,8 @@ TEST_F(link_varyings, invalid_interface_input)
    EXPECT_FALSE(linker::populate_consumer_input_sets(mem_ctx,
                                                     &ir,
                                                     consumer_inputs,
-                                                    consumer_interface_inputs));
+                                                     consumer_interface_inputs,
+                                                     junk));
 }
 
 TEST_F(link_varyings, interface_field_doesnt_match_noninterface)
@@ -288,7 +295,8 @@ TEST_F(link_varyings, interface_field_doesnt_match_noninterface)
    ASSERT_TRUE(linker::populate_consumer_input_sets(mem_ctx,
                                                     &ir,
                                                     consumer_inputs,
-                                                    consumer_interface_inputs));
+                                                    consumer_interface_inputs,
+                                                    junk));
 
    /* Create an output variable, "v", that is part of an interface block named
     * "a".  They should not match.
@@ -304,7 +312,8 @@ TEST_F(link_varyings, interface_field_doesnt_match_noninterface)
       linker::get_matching_input(mem_ctx,
                                  out_v,
                                  consumer_inputs,
-                                 consumer_interface_inputs);
+                                 consumer_interface_inputs,
+                                 junk);
 
    EXPECT_EQ(NULL, match);
 }
@@ -328,7 +337,8 @@ TEST_F(link_varyings, interface_field_doesnt_match_noninterface_vice_versa)
    ASSERT_TRUE(linker::populate_consumer_input_sets(mem_ctx,
                                                     &ir,
                                                     consumer_inputs,
-                                                    consumer_interface_inputs));
+                                                    consumer_interface_inputs,
+                                                    junk));
 
    /* Create an output variable "a.v".  They should not match.
     */
@@ -341,7 +351,8 @@ TEST_F(link_varyings, interface_field_doesnt_match_noninterface_vice_versa)
       linker::get_matching_input(mem_ctx,
                                  out_v,
                                  consumer_inputs,
-                                 consumer_interface_inputs);
+                                 consumer_interface_inputs,
+                                 junk);
 
    EXPECT_EQ(NULL, match);
 }




More information about the mesa-commit mailing list