summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/glsl/ast_to_hir.cpp18
-rw-r--r--src/glsl/ir_variable.cpp1
-rw-r--r--src/glsl/linker.cpp33
3 files changed, 48 insertions, 4 deletions
diff --git a/src/glsl/ast_to_hir.cpp b/src/glsl/ast_to_hir.cpp
index 47fe7a32c3c..6e8a1fd1daa 100644
--- a/src/glsl/ast_to_hir.cpp
+++ b/src/glsl/ast_to_hir.cpp
@@ -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;
+ }
}
}
diff --git a/src/glsl/ir_variable.cpp b/src/glsl/ir_variable.cpp
index 1eff740ef96..ddc3bb0b9f3 100644
--- a/src/glsl/ir_variable.cpp
+++ b/src/glsl/ir_variable.cpp
@@ -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.
diff --git a/src/glsl/linker.cpp b/src/glsl/linker.cpp
index bddf8788b47..c612fe54663 100644
--- a/src/glsl/linker.cpp
+++ b/src/glsl/linker.cpp
@@ -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)