diff options
Diffstat (limited to 'mesalib/src/glsl')
-rw-r--r-- | mesalib/src/glsl/Makefile.sources | 1 | ||||
-rw-r--r-- | mesalib/src/glsl/ir_optimization.h | 4 | ||||
-rw-r--r-- | mesalib/src/glsl/link_varyings.cpp | 67 | ||||
-rw-r--r-- | mesalib/src/glsl/link_varyings.h | 9 | ||||
-rw-r--r-- | mesalib/src/glsl/linker.cpp | 135 | ||||
-rw-r--r-- | mesalib/src/glsl/opt_dead_builtin_varyings.cpp | 477 |
6 files changed, 601 insertions, 92 deletions
diff --git a/mesalib/src/glsl/Makefile.sources b/mesalib/src/glsl/Makefile.sources index acd19d1ff..979c4165f 100644 --- a/mesalib/src/glsl/Makefile.sources +++ b/mesalib/src/glsl/Makefile.sources @@ -82,6 +82,7 @@ LIBGLSL_FILES = \ $(GLSL_SRCDIR)/opt_constant_variable.cpp \ $(GLSL_SRCDIR)/opt_copy_propagation.cpp \ $(GLSL_SRCDIR)/opt_copy_propagation_elements.cpp \ + $(GLSL_SRCDIR)/opt_dead_builtin_varyings.cpp \ $(GLSL_SRCDIR)/opt_dead_code.cpp \ $(GLSL_SRCDIR)/opt_dead_code_local.cpp \ $(GLSL_SRCDIR)/opt_dead_functions.cpp \ diff --git a/mesalib/src/glsl/ir_optimization.h b/mesalib/src/glsl/ir_optimization.h index d38d5e303..fad6f1bfe 100644 --- a/mesalib/src/glsl/ir_optimization.h +++ b/mesalib/src/glsl/ir_optimization.h @@ -76,6 +76,10 @@ bool do_constant_variable_unlinked(exec_list *instructions); bool do_copy_propagation(exec_list *instructions); bool do_copy_propagation_elements(exec_list *instructions); bool do_constant_propagation(exec_list *instructions); +void do_dead_builtin_varyings(struct gl_context *ctx, + exec_list *producer, exec_list *consumer, + unsigned num_tfeedback_decls, + class tfeedback_decl *tfeedback_decls); bool do_dead_code(exec_list *instructions, bool uniform_locations_assigned); bool do_dead_code_local(exec_list *instructions); bool do_dead_code_unlinked(exec_list *instructions); diff --git a/mesalib/src/glsl/link_varyings.cpp b/mesalib/src/glsl/link_varyings.cpp index 4fdbdc199..51cbdaa0e 100644 --- a/mesalib/src/glsl/link_varyings.cpp +++ b/mesalib/src/glsl/link_varyings.cpp @@ -1113,16 +1113,12 @@ assign_varying_locations(struct gl_context *ctx, } } - unsigned varying_vectors = 0; - if (consumer) { foreach_list(node, consumer->ir) { ir_variable *const var = ((ir_instruction *) node)->as_variable(); - if ((var == NULL) || (var->mode != ir_var_shader_in)) - continue; - - if (var->is_unmatched_generic_inout) { + if (var && var->mode == ir_var_shader_in && + var->is_unmatched_generic_inout) { if (prog->Version <= 120) { /* On page 25 (page 31 of the PDF) of the GLSL 1.20 spec: * @@ -1148,45 +1144,46 @@ assign_varying_locations(struct gl_context *ctx, * value is written by the previous stage. */ var->mode = ir_var_auto; - } else if (is_varying_var(consumer->Type, var)) { - /* The packing rules are used for vertex shader inputs are also - * used for fragment shader inputs. - */ - varying_vectors += count_attribute_slots(var->type); } } } + return true; +} + +bool +check_against_varying_limit(struct gl_context *ctx, + struct gl_shader_program *prog, + gl_shader *consumer) +{ + unsigned varying_vectors = 0; + + foreach_list(node, consumer->ir) { + ir_variable *const var = ((ir_instruction *) node)->as_variable(); + + if (var && var->mode == ir_var_shader_in && + is_varying_var(consumer->Type, var)) { + /* The packing rules used for vertex shader inputs are also + * used for fragment shader inputs. + */ + varying_vectors += count_attribute_slots(var->type); + } + } + if (ctx->API == API_OPENGLES2 || prog->IsES) { if (varying_vectors > ctx->Const.MaxVarying) { - if (ctx->Const.GLSLSkipStrictMaxVaryingLimitCheck) { - linker_warning(prog, "shader uses too many varying vectors " - "(%u > %u), but the driver will try to optimize " - "them out; this is non-portable out-of-spec " - "behavior\n", - varying_vectors, ctx->Const.MaxVarying); - } else { - linker_error(prog, "shader uses too many varying vectors " - "(%u > %u)\n", - varying_vectors, ctx->Const.MaxVarying); - return false; - } + linker_error(prog, "shader uses too many varying vectors " + "(%u > %u)\n", + varying_vectors, ctx->Const.MaxVarying); + return false; } } else { const unsigned float_components = varying_vectors * 4; if (float_components > ctx->Const.MaxVarying * 4) { - if (ctx->Const.GLSLSkipStrictMaxVaryingLimitCheck) { - linker_warning(prog, "shader uses too many varying components " - "(%u > %u), but the driver will try to optimize " - "them out; this is non-portable out-of-spec " - "behavior\n", - float_components, ctx->Const.MaxVarying * 4); - } else { - linker_error(prog, "shader uses too many varying components " - "(%u > %u)\n", - float_components, ctx->Const.MaxVarying * 4); - return false; - } + linker_error(prog, "shader uses too many varying components " + "(%u > %u)\n", + float_components, ctx->Const.MaxVarying * 4); + return false; } } diff --git a/mesalib/src/glsl/link_varyings.h b/mesalib/src/glsl/link_varyings.h index ee1010a7b..7f7be353b 100644 --- a/mesalib/src/glsl/link_varyings.h +++ b/mesalib/src/glsl/link_varyings.h @@ -125,6 +125,10 @@ public: return this->vector_elements * this->matrix_columns * this->size; } + unsigned get_location() const { + return this->location; + } + private: /** * The name that was supplied to glTransformFeedbackVaryings. Used for @@ -232,4 +236,9 @@ assign_varying_locations(struct gl_context *ctx, unsigned num_tfeedback_decls, tfeedback_decl *tfeedback_decls); +bool +check_against_varying_limit(struct gl_context *ctx, + struct gl_shader_program *prog, + gl_shader *consumer); + #endif /* GLSL_LINK_VARYINGS_H */ diff --git a/mesalib/src/glsl/linker.cpp b/mesalib/src/glsl/linker.cpp index c168e47e0..ba97ade25 100644 --- a/mesalib/src/glsl/linker.cpp +++ b/mesalib/src/glsl/linker.cpp @@ -1514,31 +1514,31 @@ static bool check_resources(struct gl_context *ctx, struct gl_shader_program *prog) { static const char *const shader_names[MESA_SHADER_TYPES] = { - "vertex", "fragment", "geometry" + "vertex", "geometry", "fragment" }; const unsigned max_samplers[MESA_SHADER_TYPES] = { ctx->Const.VertexProgram.MaxTextureImageUnits, - ctx->Const.FragmentProgram.MaxTextureImageUnits, - ctx->Const.GeometryProgram.MaxTextureImageUnits + ctx->Const.GeometryProgram.MaxTextureImageUnits, + ctx->Const.FragmentProgram.MaxTextureImageUnits }; const unsigned max_default_uniform_components[MESA_SHADER_TYPES] = { ctx->Const.VertexProgram.MaxUniformComponents, - ctx->Const.FragmentProgram.MaxUniformComponents, - ctx->Const.GeometryProgram.MaxUniformComponents + ctx->Const.GeometryProgram.MaxUniformComponents, + ctx->Const.FragmentProgram.MaxUniformComponents }; const unsigned max_combined_uniform_components[MESA_SHADER_TYPES] = { ctx->Const.VertexProgram.MaxCombinedUniformComponents, - ctx->Const.FragmentProgram.MaxCombinedUniformComponents, - ctx->Const.GeometryProgram.MaxCombinedUniformComponents + ctx->Const.GeometryProgram.MaxCombinedUniformComponents, + ctx->Const.FragmentProgram.MaxCombinedUniformComponents }; const unsigned max_uniform_blocks[MESA_SHADER_TYPES] = { ctx->Const.VertexProgram.MaxUniformBlocks, - ctx->Const.FragmentProgram.MaxUniformBlocks, ctx->Const.GeometryProgram.MaxUniformBlocks, + ctx->Const.FragmentProgram.MaxUniformBlocks }; for (unsigned i = 0; i < MESA_SHADER_TYPES; i++) { @@ -1836,9 +1836,9 @@ link_shaders(struct gl_context *ctx, struct gl_shader_program *prog) goto done; } - unsigned prev; - for (prev = 0; prev < MESA_SHADER_TYPES; prev++) { - if (prog->_LinkedShaders[prev] != NULL) + unsigned first; + for (first = 0; first < MESA_SHADER_TYPES; first++) { + if (prog->_LinkedShaders[first] != NULL) break; } @@ -1850,7 +1850,7 @@ link_shaders(struct gl_context *ctx, struct gl_shader_program *prog) * non-zero, but the program object has no vertex or geometry * shader; */ - if (prev >= MESA_SHADER_FRAGMENT) { + if (first >= MESA_SHADER_FRAGMENT) { linker_error(prog, "Transform feedback varyings specified, but " "no vertex or geometry shader is present."); goto done; @@ -1864,69 +1864,90 @@ link_shaders(struct gl_context *ctx, struct gl_shader_program *prog) goto done; } - for (unsigned i = prev + 1; i < MESA_SHADER_TYPES; i++) { - if (prog->_LinkedShaders[i] == NULL) - continue; - - if (!assign_varying_locations( - ctx, mem_ctx, prog, prog->_LinkedShaders[prev], prog->_LinkedShaders[i], - i == MESA_SHADER_FRAGMENT ? num_tfeedback_decls : 0, - tfeedback_decls)) - goto done; - - prev = i; + /* Linking the stages in the opposite order (from fragment to vertex) + * ensures that inter-shader outputs written to in an earlier stage are + * eliminated if they are (transitively) not used in a later stage. + */ + int last, next; + for (last = MESA_SHADER_TYPES-1; last >= 0; last--) { + if (prog->_LinkedShaders[last] != NULL) + break; } - if (prev != MESA_SHADER_FRAGMENT && num_tfeedback_decls != 0) { - /* There was no fragment shader, but we still have to assign varying - * locations for use by transform feedback. - */ - if (!assign_varying_locations( - ctx, mem_ctx, prog, prog->_LinkedShaders[prev], NULL, num_tfeedback_decls, - tfeedback_decls)) - goto done; - } + if (last >= 0 && last < MESA_SHADER_FRAGMENT) { + gl_shader *const sh = prog->_LinkedShaders[last]; - if (!store_tfeedback_info(ctx, prog, num_tfeedback_decls, tfeedback_decls)) - goto done; + if (num_tfeedback_decls != 0) { + /* There was no fragment shader, but we still have to assign varying + * locations for use by transform feedback. + */ + if (!assign_varying_locations(ctx, mem_ctx, prog, + sh, NULL, + num_tfeedback_decls, tfeedback_decls)) + goto done; + } - if (prog->_LinkedShaders[MESA_SHADER_VERTEX] != NULL) { - demote_shader_inputs_and_outputs(prog->_LinkedShaders[MESA_SHADER_VERTEX], - ir_var_shader_out); + do_dead_builtin_varyings(ctx, sh->ir, NULL, + num_tfeedback_decls, tfeedback_decls); - /* Eliminate code that is now dead due to unused vertex outputs being - * demoted. + demote_shader_inputs_and_outputs(sh, ir_var_shader_out); + + /* Eliminate code that is now dead due to unused outputs being demoted. */ - while (do_dead_code(prog->_LinkedShaders[MESA_SHADER_VERTEX]->ir, false)) - ; + while (do_dead_code(sh->ir, false)) + ; } + else if (first == MESA_SHADER_FRAGMENT) { + /* If the program only contains a fragment shader... + */ + gl_shader *const sh = prog->_LinkedShaders[first]; - if (prog->_LinkedShaders[MESA_SHADER_GEOMETRY] != NULL) { - gl_shader *const sh = prog->_LinkedShaders[MESA_SHADER_GEOMETRY]; + do_dead_builtin_varyings(ctx, NULL, sh->ir, + num_tfeedback_decls, tfeedback_decls); demote_shader_inputs_and_outputs(sh, ir_var_shader_in); - demote_shader_inputs_and_outputs(sh, ir_var_shader_out); - /* Eliminate code that is now dead due to unused geometry outputs being - * demoted. - */ - while (do_dead_code(prog->_LinkedShaders[MESA_SHADER_GEOMETRY]->ir, false)) - ; + while (do_dead_code(sh->ir, false)) + ; } - if (prog->_LinkedShaders[MESA_SHADER_FRAGMENT] != NULL) { - gl_shader *const sh = prog->_LinkedShaders[MESA_SHADER_FRAGMENT]; + next = last; + for (int i = next - 1; i >= 0; i--) { + if (prog->_LinkedShaders[i] == NULL) + continue; - demote_shader_inputs_and_outputs(sh, ir_var_shader_in); + gl_shader *const sh_i = prog->_LinkedShaders[i]; + gl_shader *const sh_next = prog->_LinkedShaders[next]; + + if (!assign_varying_locations(ctx, mem_ctx, prog, sh_i, sh_next, + next == MESA_SHADER_FRAGMENT ? num_tfeedback_decls : 0, + tfeedback_decls)) + goto done; - /* Eliminate code that is now dead due to unused fragment inputs being - * demoted. This shouldn't actually do anything other than remove - * declarations of the (now unused) global variables. + do_dead_builtin_varyings(ctx, sh_i->ir, sh_next->ir, + next == MESA_SHADER_FRAGMENT ? num_tfeedback_decls : 0, + tfeedback_decls); + + demote_shader_inputs_and_outputs(sh_i, ir_var_shader_out); + demote_shader_inputs_and_outputs(sh_next, ir_var_shader_in); + + /* Eliminate code that is now dead due to unused outputs being demoted. */ - while (do_dead_code(prog->_LinkedShaders[MESA_SHADER_FRAGMENT]->ir, false)) - ; + while (do_dead_code(sh_i->ir, false)) + ; + while (do_dead_code(sh_next->ir, false)) + ; + + /* This must be done after all dead varyings are eliminated. */ + if (!check_against_varying_limit(ctx, prog, sh_next)) + goto done; + + next = i; } + if (!store_tfeedback_info(ctx, prog, num_tfeedback_decls, tfeedback_decls)) + goto done; + update_array_sizes(prog); link_assign_uniform_locations(prog); store_fragdepth_layout(prog); diff --git a/mesalib/src/glsl/opt_dead_builtin_varyings.cpp b/mesalib/src/glsl/opt_dead_builtin_varyings.cpp new file mode 100644 index 000000000..2e813d24e --- /dev/null +++ b/mesalib/src/glsl/opt_dead_builtin_varyings.cpp @@ -0,0 +1,477 @@ +/* + * Copyright © 2013 Marek Olšák <maraeo@gmail.com> + * + * 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. + */ + +/** + * \file opt_dead_builtin_varyings.cpp + * + * This eliminates the built-in shader outputs which are either not written + * at all or not used by the next stage. It also eliminates unused elements + * of gl_TexCoord inputs, which reduces the overall varying usage. + * The varyings handled here are the primary and secondary color, the fog, + * and the texture coordinates (gl_TexCoord). + * + * This pass is necessary, because the Mesa GLSL linker cannot eliminate + * built-in varyings like it eliminates user-defined varyings, because + * the built-in varyings have pre-assigned locations. Also, the elimination + * of unused gl_TexCoord elements requires its own lowering pass anyway. + * + * It's implemented by replacing all occurences of dead varyings with + * temporary variables, which creates dead code. It is recommended to run + * a dead-code elimination pass after this. + * + * If any texture coordinate slots can be eliminated, the gl_TexCoord array is + * broken down into separate vec4 variables with locations equal to + * VARYING_SLOT_TEX0 + i. + */ + +#include "main/imports.h" /* for snprintf */ +#include "ir.h" +#include "ir_rvalue_visitor.h" +#include "ir_optimization.h" +#include "ir_print_visitor.h" +#include "glsl_types.h" +#include "link_varyings.h" + + +/** + * This obtains detailed information about built-in varyings from shader code. + */ +class varying_info_visitor : public ir_hierarchical_visitor { +public: + /* "mode" can be either ir_var_shader_in or ir_var_shader_out */ + varying_info_visitor(ir_variable_mode mode) + : lower_texcoord_array(true), + texcoord_array(NULL), + texcoord_usage(0), + color_usage(0), + tfeedback_color_usage(0), + fog(NULL), + has_fog(false), + tfeedback_has_fog(false), + mode(mode) + { + memset(color, 0, sizeof(color)); + memset(backcolor, 0, sizeof(backcolor)); + } + + virtual ir_visitor_status visit_enter(ir_dereference_array *ir) + { + ir_variable *var = ir->variable_referenced(); + + if (var && var->mode == this->mode && + var->location == VARYING_SLOT_TEX0) { + this->texcoord_array = var; + + ir_constant *index = ir->array_index->as_constant(); + if (index == NULL) { + /* There is variable indexing, we can't lower the texcoord array. + */ + this->texcoord_usage |= (1 << var->type->array_size()) - 1; + this->lower_texcoord_array = false; + } + else { + this->texcoord_usage |= 1 << index->get_uint_component(0); + } + + /* Don't visit the leaves of ir_dereference_array. */ + return visit_continue_with_parent; + } + + return visit_continue; + } + + virtual ir_visitor_status visit(ir_dereference_variable *ir) + { + ir_variable *var = ir->variable_referenced(); + + if (var->mode == this->mode && var->type->is_array() && + var->location == VARYING_SLOT_TEX0) { + /* This is a whole array dereference like "gl_TexCoord = x;", + * there's probably no point in lowering that. + */ + this->texcoord_usage |= (1 << var->type->array_size()) - 1; + this->lower_texcoord_array = false; + } + return visit_continue; + } + + virtual ir_visitor_status visit(ir_variable *var) + { + if (var->mode != this->mode) + return visit_continue; + + /* Handle colors and fog. */ + switch (var->location) { + case VARYING_SLOT_COL0: + this->color[0] = var; + this->color_usage |= 1; + break; + case VARYING_SLOT_COL1: + this->color[1] = var; + this->color_usage |= 2; + break; + case VARYING_SLOT_BFC0: + this->backcolor[0] = var; + this->color_usage |= 1; + break; + case VARYING_SLOT_BFC1: + this->backcolor[1] = var; + this->color_usage |= 2; + break; + case VARYING_SLOT_FOGC: + this->fog = var; + this->has_fog = true; + break; + } + + return visit_continue; + } + + void get(exec_list *ir, + unsigned num_tfeedback_decls, + tfeedback_decl *tfeedback_decls) + { + /* Handle the transform feedback varyings. */ + for (unsigned i = 0; i < num_tfeedback_decls; i++) { + if (!tfeedback_decls[i].is_varying()) + continue; + + unsigned location = tfeedback_decls[i].get_location(); + + switch (location) { + case VARYING_SLOT_COL0: + case VARYING_SLOT_BFC0: + this->tfeedback_color_usage |= 1; + break; + case VARYING_SLOT_COL1: + case VARYING_SLOT_BFC1: + this->tfeedback_color_usage |= 2; + break; + case VARYING_SLOT_FOGC: + this->tfeedback_has_fog = true; + break; + default: + if (location >= VARYING_SLOT_TEX0 && + location <= VARYING_SLOT_TEX7) { + this->lower_texcoord_array = false; + } + } + } + + /* Process the shader. */ + visit_list_elements(this, ir); + + if (!this->texcoord_array) { + this->lower_texcoord_array = false; + } + } + + bool lower_texcoord_array; + ir_variable *texcoord_array; + unsigned texcoord_usage; /* bitmask */ + + ir_variable *color[2]; + ir_variable *backcolor[2]; + unsigned color_usage; /* bitmask */ + unsigned tfeedback_color_usage; /* bitmask */ + + ir_variable *fog; + bool has_fog; + bool tfeedback_has_fog; + + ir_variable_mode mode; +}; + + +/** + * This replaces unused varyings with temporary variables. + * + * If "ir" is the producer, the "external" usage should come from + * the consumer. It also works the other way around. If either one is + * missing, set the "external" usage to a full mask. + */ +class replace_varyings_visitor : public ir_rvalue_visitor { +public: + replace_varyings_visitor(exec_list *ir, + const varying_info_visitor *info, + unsigned external_texcoord_usage, + unsigned external_color_usage, + bool external_has_fog) + : info(info), new_fog(NULL) + { + void *const ctx = ir; + + memset(this->new_texcoord, 0, sizeof(this->new_texcoord)); + memset(this->new_color, 0, sizeof(this->new_color)); + memset(this->new_backcolor, 0, sizeof(this->new_backcolor)); + + const char *mode_str = + info->mode == ir_var_shader_in ? "in" : "out"; + + /* Handle texcoord outputs. + * + * We're going to break down the gl_TexCoord array into separate + * variables. First, add declarations of the new variables all + * occurences of gl_TexCoord will be replaced with. + */ + if (info->lower_texcoord_array) { + for (int i = MAX_TEXTURE_COORD_UNITS-1; i >= 0; i--) { + if (info->texcoord_usage & (1 << i)) { + char name[32]; + + if (!(external_texcoord_usage & (1 << i))) { + /* This varying is unused in the next stage. Declare + * a temporary instead of an output. */ + snprintf(name, 32, "gl_%s_TexCoord%i_dummy", mode_str, i); + this->new_texcoord[i] = + new (ctx) ir_variable(glsl_type::vec4_type, name, + ir_var_temporary); + } + else { + snprintf(name, 32, "gl_%s_TexCoord%i", mode_str, i); + this->new_texcoord[i] = + new(ctx) ir_variable(glsl_type::vec4_type, name, + info->mode); + this->new_texcoord[i]->location = VARYING_SLOT_TEX0 + i; + this->new_texcoord[i]->explicit_location = true; + this->new_texcoord[i]->explicit_index = 0; + } + + ir->head->insert_before(new_texcoord[i]); + } + } + } + + /* Create dummy variables which will replace set-but-unused color and + * fog outputs. + */ + external_color_usage |= info->tfeedback_color_usage; + + for (int i = 0; i < 2; i++) { + char name[32]; + + if (!(external_color_usage & (1 << i))) { + if (info->color[i]) { + snprintf(name, 32, "gl_%s_FrontColor%i_dummy", mode_str, i); + this->new_color[i] = + new (ctx) ir_variable(glsl_type::vec4_type, name, + ir_var_temporary); + } + + if (info->backcolor[i]) { + snprintf(name, 32, "gl_%s_BackColor%i_dummy", mode_str, i); + this->new_backcolor[i] = + new (ctx) ir_variable(glsl_type::vec4_type, name, + ir_var_temporary); + } + } + } + + if (!external_has_fog && !info->tfeedback_has_fog && + info->fog) { + char name[32]; + + snprintf(name, 32, "gl_%s_FogFragCoord_dummy", mode_str); + this->new_fog = new (ctx) ir_variable(glsl_type::float_type, name, + ir_var_temporary); + } + + /* Now do the replacing. */ + visit_list_elements(this, ir); + } + + virtual ir_visitor_status visit(ir_variable *var) + { + /* Remove the gl_TexCoord array. */ + if (this->info->lower_texcoord_array && + var == this->info->texcoord_array) { + var->remove(); + } + + /* Replace set-but-unused color and fog outputs with dummy variables. */ + for (int i = 0; i < 2; i++) { + if (var == this->info->color[i] && this->new_color[i]) { + var->replace_with(this->new_color[i]); + } + if (var == this->info->backcolor[i] && + this->new_backcolor[i]) { + var->replace_with(this->new_backcolor[i]); + } + } + + if (var == this->info->fog && this->new_fog) { + var->replace_with(this->new_fog); + } + + return visit_continue; + } + + virtual void handle_rvalue(ir_rvalue **rvalue) + { + if (!*rvalue) + return; + + void *ctx = ralloc_parent(*rvalue); + + /* Replace an array dereference gl_TexCoord[i] with a single + * variable dereference representing gl_TexCoord[i]. + */ + if (this->info->lower_texcoord_array) { + /* gl_TexCoord[i] occurence */ + ir_dereference_array *const da = (*rvalue)->as_dereference_array(); + + if (da && da->variable_referenced() == + this->info->texcoord_array) { + unsigned i = da->array_index->as_constant()->get_uint_component(0); + + *rvalue = new(ctx) ir_dereference_variable(this->new_texcoord[i]); + return; + } + } + + /* Replace set-but-unused color and fog outputs with dummy variables. */ + ir_dereference_variable *const dv = (*rvalue)->as_dereference_variable(); + if (!dv) + return; + + ir_variable *var = dv->variable_referenced(); + + for (int i = 0; i < 2; i++) { + if (var == this->info->color[i] && this->new_color[i]) { + *rvalue = new(ctx) ir_dereference_variable(this->new_color[i]); + return; + } + if (var == this->info->backcolor[i] && + this->new_backcolor[i]) { + *rvalue = new(ctx) ir_dereference_variable(this->new_backcolor[i]); + return; + } + } + + if (var == this->info->fog && this->new_fog) { + *rvalue = new(ctx) ir_dereference_variable(this->new_fog); + } + } + + virtual ir_visitor_status visit_leave(ir_assignment *ir) + { + handle_rvalue(&ir->rhs); + handle_rvalue(&ir->condition); + + /* We have to use set_lhs when changing the LHS of an assignment. */ + ir_rvalue *lhs = ir->lhs; + + handle_rvalue(&lhs); + if (lhs != ir->lhs) { + ir->set_lhs(lhs); + } + + return visit_continue; + } + +private: + const varying_info_visitor *info; + struct ir_variable *new_texcoord[MAX_TEXTURE_COORD_UNITS]; + struct ir_variable *new_color[2]; + struct ir_variable *new_backcolor[2]; + struct ir_variable *new_fog; +}; + + +static void +lower_texcoord_array(exec_list *ir, const varying_info_visitor *info) +{ + replace_varyings_visitor(ir, info, + (1 << MAX_TEXTURE_COORD_UNITS) - 1, + 1 | 2, true); +} + + +void +do_dead_builtin_varyings(struct gl_context *ctx, + exec_list *producer, exec_list *consumer, + unsigned num_tfeedback_decls, + tfeedback_decl *tfeedback_decls) +{ + /* This optimization has no effect with the core context and GLES2, because + * the built-in varyings we're eliminating here are not available there. + * + * EXT_separate_shader_objects doesn't allow this optimization, + * because a program object can be bound partially (e.g. only one + * stage of a program object can be bound). + */ + if (ctx->API == API_OPENGL_CORE || + ctx->API == API_OPENGLES2 || + ctx->Extensions.EXT_separate_shader_objects) { + return; + } + + /* Information about built-in varyings. */ + varying_info_visitor producer_info(ir_var_shader_out); + varying_info_visitor consumer_info(ir_var_shader_in); + + if (producer) { + producer_info.get(producer, num_tfeedback_decls, tfeedback_decls); + + if (!consumer) { + /* At least eliminate unused gl_TexCoord elements. */ + if (producer_info.lower_texcoord_array) { + lower_texcoord_array(producer, &producer_info); + } + return; + } + } + + if (consumer) { + consumer_info.get(consumer, 0, NULL); + + if (!producer) { + /* At least eliminate unused gl_TexCoord elements. */ + if (consumer_info.lower_texcoord_array) { + lower_texcoord_array(consumer, &consumer_info); + } + return; + } + } + + /* Eliminate the varyings unused by the other shader. */ + if (producer_info.lower_texcoord_array || + producer_info.color_usage || + producer_info.has_fog) { + replace_varyings_visitor(producer, + &producer_info, + consumer_info.texcoord_usage, + consumer_info.color_usage, + consumer_info.has_fog); + } + + if (consumer_info.lower_texcoord_array || + consumer_info.color_usage || + consumer_info.has_fog) { + replace_varyings_visitor(consumer, + &consumer_info, + producer_info.texcoord_usage, + producer_info.color_usage, + producer_info.has_fog); + } +} |