aboutsummaryrefslogtreecommitdiff
path: root/mesalib/src/glsl/ast_to_hir.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'mesalib/src/glsl/ast_to_hir.cpp')
-rw-r--r--mesalib/src/glsl/ast_to_hir.cpp70
1 files changed, 70 insertions, 0 deletions
diff --git a/mesalib/src/glsl/ast_to_hir.cpp b/mesalib/src/glsl/ast_to_hir.cpp
index 820c86c5e..e24914b83 100644
--- a/mesalib/src/glsl/ast_to_hir.cpp
+++ b/mesalib/src/glsl/ast_to_hir.cpp
@@ -57,6 +57,10 @@
#include "program/hash_table.h"
#include "ir.h"
+static void
+detect_conflicting_assignments(struct _mesa_glsl_parse_state *state,
+ exec_list *instructions);
+
void
_mesa_ast_to_hir(exec_list *instructions, struct _mesa_glsl_parse_state *state)
{
@@ -87,6 +91,7 @@ _mesa_ast_to_hir(exec_list *instructions, struct _mesa_glsl_parse_state *state)
ast->hir(instructions, state);
detect_recursion_unlinked(state, instructions);
+ detect_conflicting_assignments(state, instructions);
state->toplevel_ir = NULL;
}
@@ -672,6 +677,10 @@ do_assignment(exec_list *instructions, struct _mesa_glsl_parse_state *state,
void *ctx = state;
bool error_emitted = (lhs->type->is_error() || rhs->type->is_error());
+ ir_variable *lhs_var = lhs->variable_referenced();
+ if (lhs_var)
+ lhs_var->assigned = true;
+
if (!error_emitted) {
if (non_lvalue_description != NULL) {
_mesa_glsl_error(&lhs_loc, state,
@@ -4013,3 +4022,64 @@ ast_struct_specifier::hir(exec_list *instructions,
*/
return NULL;
}
+
+static void
+detect_conflicting_assignments(struct _mesa_glsl_parse_state *state,
+ exec_list *instructions)
+{
+ bool gl_FragColor_assigned = false;
+ bool gl_FragData_assigned = false;
+ bool user_defined_fs_output_assigned = false;
+ ir_variable *user_defined_fs_output = NULL;
+
+ /* It would be nice to have proper location information. */
+ YYLTYPE loc;
+ memset(&loc, 0, sizeof(loc));
+
+ foreach_list(node, instructions) {
+ ir_variable *var = ((ir_instruction *)node)->as_variable();
+
+ if (!var)
+ continue;
+
+ if (strcmp(var->name, "gl_FragColor") == 0)
+ gl_FragColor_assigned = var->assigned;
+ else if (strcmp(var->name, "gl_FragData") == 0)
+ gl_FragData_assigned = var->assigned;
+ else if (strncmp(var->name, "gl_", 3) != 0) {
+ if (state->target == fragment_shader &&
+ (var->mode == ir_var_out || var->mode == ir_var_inout)) {
+ user_defined_fs_output_assigned = true;
+ user_defined_fs_output = var;
+ }
+ }
+ }
+
+ /* From the GLSL 1.30 spec:
+ *
+ * "If a shader statically assigns a value to gl_FragColor, it
+ * may not assign a value to any element of gl_FragData. If a
+ * shader statically writes a value to any element of
+ * gl_FragData, it may not assign a value to
+ * gl_FragColor. That is, a shader may assign values to either
+ * gl_FragColor or gl_FragData, but not both. Multiple shaders
+ * linked together must also consistently write just one of
+ * these variables. Similarly, if user declared output
+ * variables are in use (statically assigned to), then the
+ * built-in variables gl_FragColor and gl_FragData may not be
+ * assigned to. These incorrect usages all generate compile
+ * time errors."
+ */
+ if (gl_FragColor_assigned && gl_FragData_assigned) {
+ _mesa_glsl_error(&loc, state, "fragment shader writes to both "
+ "`gl_FragColor' and `gl_FragData'\n");
+ } else if (gl_FragColor_assigned && user_defined_fs_output_assigned) {
+ _mesa_glsl_error(&loc, state, "fragment shader writes to both "
+ "`gl_FragColor' and `%s'\n",
+ user_defined_fs_output->name);
+ } else if (gl_FragData_assigned && user_defined_fs_output_assigned) {
+ _mesa_glsl_error(&loc, state, "fragment shader writes to both "
+ "`gl_FragData' and `%s'\n",
+ user_defined_fs_output->name);
+ }
+}