diff options
Diffstat (limited to 'mesalib/src/glsl')
-rw-r--r-- | mesalib/src/glsl/Makefile.sources | 1 | ||||
-rw-r--r-- | mesalib/src/glsl/ast_to_hir.cpp | 2 | ||||
-rw-r--r-- | mesalib/src/glsl/glsl_lexer.ll | 2 | ||||
-rw-r--r-- | mesalib/src/glsl/glsl_parser.yy | 4 | ||||
-rw-r--r-- | mesalib/src/glsl/ir_optimization.h | 1 | ||||
-rw-r--r-- | mesalib/src/glsl/link_uniforms.cpp | 2 | ||||
-rw-r--r-- | mesalib/src/glsl/linker.cpp | 167 | ||||
-rw-r--r-- | mesalib/src/glsl/lower_output_reads.cpp | 141 |
8 files changed, 263 insertions, 57 deletions
diff --git a/mesalib/src/glsl/Makefile.sources b/mesalib/src/glsl/Makefile.sources index c65bfe4ff..5e80af297 100644 --- a/mesalib/src/glsl/Makefile.sources +++ b/mesalib/src/glsl/Makefile.sources @@ -60,6 +60,7 @@ LIBGLSL_CXX_SOURCES := \ lower_vec_index_to_cond_assign.cpp \ lower_vec_index_to_swizzle.cpp \ lower_vector.cpp \ + lower_output_reads.cpp \ opt_algebraic.cpp \ opt_constant_folding.cpp \ opt_constant_propagation.cpp \ diff --git a/mesalib/src/glsl/ast_to_hir.cpp b/mesalib/src/glsl/ast_to_hir.cpp index d5b04e9d7..f0c921875 100644 --- a/mesalib/src/glsl/ast_to_hir.cpp +++ b/mesalib/src/glsl/ast_to_hir.cpp @@ -1184,7 +1184,7 @@ ast_expression::hir(exec_list *instructions, error_emitted = true; } - type = op[0]->type; + type = error_emitted ? glsl_type::error_type : op[0]->type; result = new(ctx) ir_expression(ir_unop_bit_not, type, op[0], NULL); break; diff --git a/mesalib/src/glsl/glsl_lexer.ll b/mesalib/src/glsl/glsl_lexer.ll index c7cfedd42..936a90726 100644 --- a/mesalib/src/glsl/glsl_lexer.ll +++ b/mesalib/src/glsl/glsl_lexer.ll @@ -148,7 +148,7 @@ HASH ^{SPC}#{SPC} /* Preprocessor tokens. */ ^[ \t]*#[ \t]*$ ; -^[ \t]*#[ \t]*version { BEGIN PP; return VERSION; } +^[ \t]*#[ \t]*version { BEGIN PP; return VERSION_TOK; } ^[ \t]*#[ \t]*extension { BEGIN PP; return EXTENSION; } {HASH}line{SPCP}{INT}{SPCP}{INT}{SPC}$ { /* Eat characters until the first digit is diff --git a/mesalib/src/glsl/glsl_parser.yy b/mesalib/src/glsl/glsl_parser.yy index 8a0377f49..e774b4697 100644 --- a/mesalib/src/glsl/glsl_parser.yy +++ b/mesalib/src/glsl/glsl_parser.yy @@ -118,7 +118,7 @@ static void yyerror(YYLTYPE *loc, _mesa_glsl_parse_state *st, const char *msg) %token INVARIANT %token LOWP MEDIUMP HIGHP SUPERP PRECISION -%token VERSION EXTENSION LINE COLON EOL INTERFACE OUTPUT +%token VERSION_TOK EXTENSION LINE COLON EOL INTERFACE OUTPUT %token PRAGMA_DEBUG_ON PRAGMA_DEBUG_OFF %token PRAGMA_OPTIMIZE_ON PRAGMA_OPTIMIZE_OFF %token PRAGMA_INVARIANT_ALL @@ -246,7 +246,7 @@ translation_unit: version_statement: /* blank - no #version specified: defaults are already set */ - | VERSION INTCONSTANT EOL + | VERSION_TOK INTCONSTANT EOL { bool supported = false; diff --git a/mesalib/src/glsl/ir_optimization.h b/mesalib/src/glsl/ir_optimization.h index 7b32e84f0..085b96903 100644 --- a/mesalib/src/glsl/ir_optimization.h +++ b/mesalib/src/glsl/ir_optimization.h @@ -72,6 +72,7 @@ bool lower_variable_index_to_cond_assign(exec_list *instructions, bool lower_input, bool lower_output, bool lower_temp, bool lower_uniform); bool lower_quadop_vector(exec_list *instructions, bool dont_lower_swz); bool lower_clip_distance(exec_list *instructions); +void lower_output_reads(exec_list *instructions); bool optimize_redundant_jumps(exec_list *instructions); ir_rvalue * diff --git a/mesalib/src/glsl/link_uniforms.cpp b/mesalib/src/glsl/link_uniforms.cpp index c7de480a5..b331db705 100644 --- a/mesalib/src/glsl/link_uniforms.cpp +++ b/mesalib/src/glsl/link_uniforms.cpp @@ -365,9 +365,9 @@ link_assign_uniform_locations(struct gl_shader_program *prog) for (unsigned i = 0; i < num_user_uniforms; i++) { assert(uniforms[i].storage != NULL); } -#endif assert(parcel.values == data_end); +#endif prog->NumUserUniformStorage = num_user_uniforms; prog->UniformStorage = uniforms; diff --git a/mesalib/src/glsl/linker.cpp b/mesalib/src/glsl/linker.cpp index 65870087b..88c81c41b 100644 --- a/mesalib/src/glsl/linker.cpp +++ b/mesalib/src/glsl/linker.cpp @@ -1376,13 +1376,14 @@ demote_shader_inputs_and_outputs(gl_shader *sh, enum ir_variable_mode mode) class tfeedback_decl { public: - bool init(struct gl_shader_program *prog, const void *mem_ctx, - const char *input); + bool init(struct gl_context *ctx, struct gl_shader_program *prog, + const void *mem_ctx, const char *input); static bool is_same(const tfeedback_decl &x, const tfeedback_decl &y); bool assign_location(struct gl_context *ctx, struct gl_shader_program *prog, ir_variable *output_var); bool store(struct gl_shader_program *prog, - struct gl_transform_feedback_info *info, unsigned buffer) const; + struct gl_transform_feedback_info *info, unsigned buffer, + unsigned varying) const; /** @@ -1413,24 +1414,31 @@ public: private: /** * The name that was supplied to glTransformFeedbackVaryings. Used for - * error reporting. + * error reporting and glGetTransformFeedbackVarying(). */ const char *orig_name; /** * The name of the variable, parsed from orig_name. */ - char *var_name; + const char *var_name; /** * True if the declaration in orig_name represents an array. */ - bool is_array; + bool is_subscripted; /** - * If is_array is true, the array index that was specified in orig_name. + * If is_subscripted is true, the subscript that was specified in orig_name. */ - unsigned array_index; + unsigned array_subscript; + + /** + * Which component to extract from the vertex shader output location that + * the linker assigned to this variable. -1 if all components should be + * extracted. + */ + int single_component; /** * The vertex shader output location that the linker assigned for this @@ -1449,6 +1457,15 @@ private: * if this variable is not a matrix. */ unsigned matrix_columns; + + /** Type of the varying returned by glGetTransformFeedbackVarying() */ + GLenum type; + + /** + * If location != -1, the size that should be returned by + * glGetTransformFeedbackVarying(). + */ + unsigned size; }; @@ -1458,8 +1475,8 @@ private: * reported using linker_error(), and false is returned. */ bool -tfeedback_decl::init(struct gl_shader_program *prog, const void *mem_ctx, - const char *input) +tfeedback_decl::init(struct gl_context *ctx, struct gl_shader_program *prog, + const void *mem_ctx, const char *input) { /* We don't have to be pedantic about what is a valid GLSL variable name, * because any variable with an invalid name can't exist in the IR anyway. @@ -1467,23 +1484,36 @@ tfeedback_decl::init(struct gl_shader_program *prog, const void *mem_ctx, this->location = -1; this->orig_name = input; + this->single_component = -1; const char *bracket = strrchr(input, '['); if (bracket) { this->var_name = ralloc_strndup(mem_ctx, input, bracket - input); - if (sscanf(bracket, "[%u]", &this->array_index) == 1) { - this->is_array = true; - return true; + if (sscanf(bracket, "[%u]", &this->array_subscript) != 1) { + linker_error(prog, "Cannot parse transform feedback varying %s", input); + return false; } + this->is_subscripted = true; } else { this->var_name = ralloc_strdup(mem_ctx, input); - this->is_array = false; - return true; + this->is_subscripted = false; } - linker_error(prog, "Cannot parse transform feedback varying %s", input); - return false; + /* For drivers that lower gl_ClipDistance to gl_ClipDistanceMESA, we need + * to convert a request for gl_ClipDistance[n] into a request for a + * component of gl_ClipDistanceMESA[n/4]. + */ + if (ctx->ShaderCompilerOptions[MESA_SHADER_VERTEX].LowerClipDistance && + strcmp(this->var_name, "gl_ClipDistance") == 0) { + this->var_name = "gl_ClipDistanceMESA"; + if (this->is_subscripted) { + this->single_component = this->array_subscript % 4; + this->array_subscript /= 4; + } + } + + return true; } @@ -1496,9 +1526,11 @@ tfeedback_decl::is_same(const tfeedback_decl &x, const tfeedback_decl &y) { if (strcmp(x.var_name, y.var_name) != 0) return false; - if (x.is_array != y.is_array) + if (x.is_subscripted != y.is_subscripted) + return false; + if (x.is_subscripted && x.array_subscript != y.array_subscript) return false; - if (x.is_array && x.array_index != y.array_index) + if (x.single_component != y.single_component) return false; return true; } @@ -1518,37 +1550,42 @@ tfeedback_decl::assign_location(struct gl_context *ctx, { if (output_var->type->is_array()) { /* Array variable */ - if (!this->is_array) { - linker_error(prog, "Transform feedback varying %s found, " - "but it's not an array ([] not expected).", - this->orig_name); - return false; - } - /* Check array bounds. */ - if (this->array_index >= - (unsigned) output_var->type->array_size()) { - linker_error(prog, "Transform feedback varying %s has index " - "%i, but the array size is %i.", - this->orig_name, this->array_index, - output_var->type->array_size()); - return false; - } const unsigned matrix_cols = output_var->type->fields.array->matrix_columns; - this->location = output_var->location + this->array_index * matrix_cols; + + if (this->is_subscripted) { + /* Check array bounds. */ + if (this->array_subscript >= + (unsigned) output_var->type->array_size()) { + linker_error(prog, "Transform feedback varying %s has index " + "%i, but the array size is %i.", + this->orig_name, this->array_subscript, + output_var->type->array_size()); + return false; + } + this->location = + output_var->location + this->array_subscript * matrix_cols; + this->size = 1; + } else { + this->location = output_var->location; + this->size = (unsigned) output_var->type->array_size(); + } this->vector_elements = output_var->type->fields.array->vector_elements; this->matrix_columns = matrix_cols; + this->type = output_var->type->fields.array->gl_type; } else { /* Regular variable (scalar, vector, or matrix) */ - if (this->is_array) { + if (this->is_subscripted) { linker_error(prog, "Transform feedback varying %s found, " "but it's an array ([] expected).", this->orig_name); return false; } this->location = output_var->location; + this->size = 1; this->vector_elements = output_var->type->vector_elements; this->matrix_columns = output_var->type->matrix_columns; + this->type = output_var->type->gl_type; } /* From GL_EXT_transform_feedback: * A program will fail to link if: @@ -1580,7 +1617,7 @@ tfeedback_decl::assign_location(struct gl_context *ctx, bool tfeedback_decl::store(struct gl_shader_program *prog, struct gl_transform_feedback_info *info, - unsigned buffer) const + unsigned buffer, unsigned varying) const { if (!this->is_assigned()) { /* From GL_EXT_transform_feedback: @@ -1594,14 +1631,27 @@ tfeedback_decl::store(struct gl_shader_program *prog, this->orig_name); return false; } - for (unsigned v = 0; v < this->matrix_columns; ++v) { - info->Outputs[info->NumOutputs].OutputRegister = this->location + v; - info->Outputs[info->NumOutputs].NumComponents = this->vector_elements; - info->Outputs[info->NumOutputs].OutputBuffer = buffer; - info->Outputs[info->NumOutputs].DstOffset = info->BufferStride[buffer]; - ++info->NumOutputs; - info->BufferStride[buffer] += this->vector_elements; + for (unsigned index = 0; index < this->size; ++index) { + for (unsigned v = 0; v < this->matrix_columns; ++v) { + unsigned num_components = + this->single_component >= 0 ? 1 : this->vector_elements; + info->Outputs[info->NumOutputs].OutputRegister = + this->location + v + index * this->matrix_columns; + info->Outputs[info->NumOutputs].NumComponents = num_components; + info->Outputs[info->NumOutputs].OutputBuffer = buffer; + info->Outputs[info->NumOutputs].DstOffset = info->BufferStride[buffer]; + info->Outputs[info->NumOutputs].ComponentOffset = + this->single_component >= 0 ? this->single_component : 0; + ++info->NumOutputs; + info->BufferStride[buffer] += num_components; + } } + + info->Varyings[varying].Name = ralloc_strdup(prog, this->orig_name); + info->Varyings[varying].Type = this->type; + info->Varyings[varying].Size = this->size; + info->NumVarying++; + return true; } @@ -1614,12 +1664,12 @@ tfeedback_decl::store(struct gl_shader_program *prog, * is returned. */ static bool -parse_tfeedback_decls(struct gl_shader_program *prog, const void *mem_ctx, - unsigned num_names, char **varying_names, - tfeedback_decl *decls) +parse_tfeedback_decls(struct gl_context *ctx, struct gl_shader_program *prog, + const void *mem_ctx, unsigned num_names, + char **varying_names, tfeedback_decl *decls) { for (unsigned i = 0; i < num_names; ++i) { - if (!decls[i].init(prog, mem_ctx, varying_names[i])) + if (!decls[i].init(ctx, prog, mem_ctx, varying_names[i])) return false; /* From GL_EXT_transform_feedback: * A program will fail to link if: @@ -1865,13 +1915,26 @@ store_tfeedback_info(struct gl_context *ctx, struct gl_shader_program *prog, tfeedback_decl *tfeedback_decls) { unsigned total_tfeedback_components = 0; + bool separate_attribs_mode = + prog->TransformFeedback.BufferMode == GL_SEPARATE_ATTRIBS; + + ralloc_free(prog->LinkedTransformFeedback.Varyings); + memset(&prog->LinkedTransformFeedback, 0, sizeof(prog->LinkedTransformFeedback)); + + prog->LinkedTransformFeedback.NumBuffers = + separate_attribs_mode ? num_tfeedback_decls : 1; + + prog->LinkedTransformFeedback.Varyings = + rzalloc_array(prog->LinkedTransformFeedback.Varyings, + struct gl_transform_feedback_varying_info, + num_tfeedback_decls); + for (unsigned i = 0; i < num_tfeedback_decls; ++i) { - unsigned buffer = - prog->TransformFeedback.BufferMode == GL_SEPARATE_ATTRIBS ? i : 0; + unsigned buffer = separate_attribs_mode ? i : 0; if (!tfeedback_decls[i].store(prog, &prog->LinkedTransformFeedback, - buffer)) + buffer, i)) return false; total_tfeedback_components += tfeedback_decls[i].num_components(); } @@ -2182,7 +2245,7 @@ link_shaders(struct gl_context *ctx, struct gl_shader_program *prog) tfeedback_decls = ralloc_array(mem_ctx, tfeedback_decl, prog->TransformFeedback.NumVarying); - if (!parse_tfeedback_decls(prog, mem_ctx, num_tfeedback_decls, + if (!parse_tfeedback_decls(ctx, prog, mem_ctx, num_tfeedback_decls, prog->TransformFeedback.VaryingNames, tfeedback_decls)) goto done; diff --git a/mesalib/src/glsl/lower_output_reads.cpp b/mesalib/src/glsl/lower_output_reads.cpp new file mode 100644 index 000000000..415b541c3 --- /dev/null +++ b/mesalib/src/glsl/lower_output_reads.cpp @@ -0,0 +1,141 @@ +/* + * Copyright © 2012 Vincent Lejeune + * Copyright © 2012 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "ir.h" +#include "program/hash_table.h" + +/** + * \file lower_output_reads.cpp + * + * In GLSL, shader output variables (such as varyings) can be both read and + * written. However, on some hardware, reading an output register causes + * trouble. + * + * This pass creates temporary shadow copies of every (used) shader output, + * and replaces all accesses to use those instead. It also adds code to the + * main() function to copy the final values to the actual shader outputs. + */ + +class output_read_remover : public ir_hierarchical_visitor { +protected: + /** + * A hash table mapping from the original ir_variable shader outputs + * (ir_var_out mode) to the new temporaries to be used instead. + */ + hash_table *replacements; + + void *mem_ctx; +public: + output_read_remover(); + ~output_read_remover(); + virtual ir_visitor_status visit(class ir_dereference_variable *); + virtual ir_visitor_status visit_leave(class ir_return *); + virtual ir_visitor_status visit_leave(class ir_function_signature *); +}; + +output_read_remover::output_read_remover() +{ + mem_ctx = ralloc_context(NULL); + replacements = + hash_table_ctor(0, hash_table_pointer_hash, hash_table_pointer_compare); +} + +output_read_remover::~output_read_remover() +{ + hash_table_dtor(replacements); + ralloc_free(mem_ctx); +} + +ir_visitor_status +output_read_remover::visit(ir_dereference_variable *ir) +{ + if (ir->var->mode != ir_var_out) + return visit_continue; + + ir_variable *temp = (ir_variable *) hash_table_find(replacements, ir->var); + + /* If we don't have an existing temporary, create one. */ + if (temp == NULL) { + void *var_ctx = ralloc_parent(ir->var); + temp = new(var_ctx) ir_variable(ir->var->type, ir->var->name, + ir_var_temporary); + hash_table_insert(replacements, temp, ir->var); + } + + /* Update the dereference to use the temporary */ + ir->var = temp; + + return visit_continue; +} + +/** + * Create an assignment to copy a temporary value back to the actual output. + */ +static ir_assignment * +copy(void *ctx, ir_variable *output, ir_variable *temp) +{ + ir_dereference_variable *lhs = new(ctx) ir_dereference_variable(output); + ir_dereference_variable *rhs = new(ctx) ir_dereference_variable(temp); + return new(ctx) ir_assignment(lhs, rhs); +} + +/** Insert a copy-back assignment before a "return" statement */ +static void +emit_return_copy(const void *key, void *data, void *closure) +{ + ir_return *ir = (ir_return *) closure; + ir->insert_before(copy(ir, (ir_variable *) key, (ir_variable *) data)); +} + +/** Insert a copy-back assignment at the end of the main() function */ +static void +emit_main_copy(const void *key, void *data, void *closure) +{ + ir_function_signature *sig = (ir_function_signature *) closure; + sig->body.push_tail(copy(sig, (ir_variable *) key, (ir_variable *) data)); +} + +ir_visitor_status +output_read_remover::visit_leave(ir_return *ir) +{ + hash_table_call_foreach(replacements, emit_return_copy, ir); + return visit_continue; +} + +ir_visitor_status +output_read_remover::visit_leave(ir_function_signature *sig) +{ + if (strcmp(sig->function_name(), "main") != 0) + return visit_continue; + + hash_table_call_foreach(replacements, emit_main_copy, sig); + return visit_continue; +} + +void +lower_output_reads(exec_list *instructions) +{ + output_read_remover v; + visit_list_elements(&v, instructions); +} |