diff options
-rw-r--r-- | src/glsl/ast_to_hir.cpp | 18 | ||||
-rw-r--r-- | src/glsl/ir_variable.cpp | 1 | ||||
-rw-r--r-- | src/glsl/linker.cpp | 33 |
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) |