glsl: Add linker support for explicit attribute locations
authorIan Romanick <ian.d.romanick@intel.com>
Fri, 8 Oct 2010 00:21:22 +0000 (17:21 -0700)
committerIan Romanick <ian.d.romanick@intel.com>
Fri, 8 Oct 2010 21:21:23 +0000 (14:21 -0700)
src/glsl/ast_to_hir.cpp
src/glsl/ir_variable.cpp
src/glsl/linker.cpp

index 47fe7a32c3ca2f4ff0151edf9b0a769b034d351a..6e8a1fd1daaaa194812296bc11da1c8f622a714e 100644 (file)
@@ -1668,9 +1668,21 @@ apply_type_qualifier_to_variable(const struct ast_type_qualifier *qual,
                          string);
       } else {
         var->explicit_location = true;
-        var->location = (state->target == vertex_shader)
-           ? (qual->location + VERT_ATTRIB_GENERIC0)
-           : (qual->location + FRAG_RESULT_DATA0);
+
+        /* This bit of silliness is needed because invalid explicit locations
+         * are supposed to be flagged during linking.  Small negative values
+         * biased by VERT_ATTRIB_GENERIC0 or FRAG_RESULT_DATA0 could alias
+         * built-in values (e.g., -16+VERT_ATTRIB_GENERIC0 = VERT_ATTRIB_POS).
+         * The linker needs to be able to differentiate these cases.  This
+         * ensures that negative values stay negative.
+         */
+        if (qual->location >= 0) {
+           var->location = (state->target == vertex_shader)
+              ? (qual->location + VERT_ATTRIB_GENERIC0)
+              : (qual->location + FRAG_RESULT_DATA0);
+        } else {
+           var->location = qual->location;
+        }
       }
    }
 
index 1eff740ef96f8a837866d2760febb8ca76b5232f..ddc3bb0b9f391e3f516843ccd385f0074882d26b 100644 (file)
@@ -52,6 +52,7 @@ add_variable(const char *name, enum ir_variable_mode mode, int slot,
    }
 
    var->location = slot;
+   var->explicit_location = (slot >= 0);
 
    /* Once the variable is created an initialized, add it to the symbol table
     * and add the declaration to the IR stream.
index bddf8788b47a3f01d03f91040ca43511d5a10dd2..c612fe546637f02b460a0872a80e5ccbff298897 100644 (file)
@@ -191,7 +191,7 @@ invalidate_variable_locations(gl_shader *sh, enum ir_variable_mode mode,
 
       /* Only assign locations for generic attributes / varyings / etc.
        */
-      if (var->location >= generic_base)
+      if ((var->location >= generic_base) && !var->explicit_location)
          var->location = -1;
    }
 }
@@ -365,6 +365,19 @@ cross_validate_globals(struct gl_shader_program *prog,
               }
            }
 
+           if (var->explicit_location) {
+              if (existing->explicit_location
+                  && (var->location != existing->location)) {
+                    linker_error_printf(prog, "explicit locations for %s "
+                                        "`%s' have differing values\n",
+                                        mode_string(var), var->name);
+                    return false;
+              }
+
+              existing->location = var->location;
+              existing->explicit_location = true;
+           }
+
            /* FINISHME: Handle non-constant initializers.
             */
            if (var->constant_value != NULL) {
@@ -1186,6 +1199,24 @@ assign_attribute_locations(gl_shader_program *prog, unsigned max_attribute_index
       if ((var == NULL) || (var->mode != ir_var_in))
         continue;
 
+      if (var->explicit_location) {
+        const unsigned slots = count_attribute_slots(var->type);
+        const unsigned use_mask = (1 << slots) - 1;
+        const int attr = var->location - VERT_ATTRIB_GENERIC0;
+
+        if ((var->location >= (int)(max_attribute_index + VERT_ATTRIB_GENERIC0))
+            || (var->location < 0)) {
+           linker_error_printf(prog,
+                               "invalid explicit location %d specified for "
+                               "`%s'\n",
+                               (var->location < 0) ? var->location : attr,
+                               var->name);
+           return false;
+        } else if (var->location >= VERT_ATTRIB_GENERIC0) {
+           used_locations |= (use_mask << attr);
+        }
+      }
+
       /* The location was explicitly assigned, nothing to do here.
        */
       if (var->location != -1)