Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | RSS feed

  1. /*
  2.  * Copyright © 2013 Marek Olšák <maraeo@gmail.com>
  3.  *
  4.  * Permission is hereby granted, free of charge, to any person obtaining a
  5.  * copy of this software and associated documentation files (the "Software"),
  6.  * to deal in the Software without restriction, including without limitation
  7.  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  8.  * and/or sell copies of the Software, and to permit persons to whom the
  9.  * Software is furnished to do so, subject to the following conditions:
  10.  *
  11.  * The above copyright notice and this permission notice (including the next
  12.  * paragraph) shall be included in all copies or substantial portions of the
  13.  * Software.
  14.  *
  15.  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16.  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17.  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
  18.  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19.  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  20.  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  21.  * DEALINGS IN THE SOFTWARE.
  22.  */
  23.  
  24. /**
  25.  * \file opt_dead_builtin_varyings.cpp
  26.  *
  27.  * This eliminates the built-in shader outputs which are either not written
  28.  * at all or not used by the next stage. It also eliminates unused elements
  29.  * of gl_TexCoord inputs, which reduces the overall varying usage.
  30.  * The varyings handled here are the primary and secondary color, the fog,
  31.  * and the texture coordinates (gl_TexCoord).
  32.  *
  33.  * This pass is necessary, because the Mesa GLSL linker cannot eliminate
  34.  * built-in varyings like it eliminates user-defined varyings, because
  35.  * the built-in varyings have pre-assigned locations. Also, the elimination
  36.  * of unused gl_TexCoord elements requires its own lowering pass anyway.
  37.  *
  38.  * It's implemented by replacing all occurences of dead varyings with
  39.  * temporary variables, which creates dead code. It is recommended to run
  40.  * a dead-code elimination pass after this.
  41.  *
  42.  * If any texture coordinate slots can be eliminated, the gl_TexCoord array is
  43.  * broken down into separate vec4 variables with locations equal to
  44.  * VARYING_SLOT_TEX0 + i.
  45.  */
  46.  
  47. #include "main/imports.h" /* for snprintf */
  48. #include "ir.h"
  49. #include "ir_rvalue_visitor.h"
  50. #include "ir_optimization.h"
  51. #include "ir_print_visitor.h"
  52. #include "glsl_types.h"
  53. #include "link_varyings.h"
  54.  
  55.  
  56. /**
  57.  * This obtains detailed information about built-in varyings from shader code.
  58.  */
  59. class varying_info_visitor : public ir_hierarchical_visitor {
  60. public:
  61.    /* "mode" can be either ir_var_shader_in or ir_var_shader_out */
  62.    varying_info_visitor(ir_variable_mode mode)
  63.       : lower_texcoord_array(true),
  64.         texcoord_array(NULL),
  65.         texcoord_usage(0),
  66.         color_usage(0),
  67.         tfeedback_color_usage(0),
  68.         fog(NULL),
  69.         has_fog(false),
  70.         tfeedback_has_fog(false),
  71.         mode(mode)
  72.    {
  73.       memset(color, 0, sizeof(color));
  74.       memset(backcolor, 0, sizeof(backcolor));
  75.    }
  76.  
  77.    virtual ir_visitor_status visit_enter(ir_dereference_array *ir)
  78.    {
  79.       ir_variable *var = ir->variable_referenced();
  80.  
  81.       if (var && var->mode == this->mode &&
  82.           var->location == VARYING_SLOT_TEX0) {
  83.          this->texcoord_array = var;
  84.  
  85.          ir_constant *index = ir->array_index->as_constant();
  86.          if (index == NULL) {
  87.             /* There is variable indexing, we can't lower the texcoord array.
  88.              */
  89.             this->texcoord_usage |= (1 << var->type->array_size()) - 1;
  90.             this->lower_texcoord_array = false;
  91.          }
  92.          else {
  93.             this->texcoord_usage |= 1 << index->get_uint_component(0);
  94.          }
  95.  
  96.          /* Don't visit the leaves of ir_dereference_array. */
  97.          return visit_continue_with_parent;
  98.       }
  99.  
  100.       return visit_continue;
  101.    }
  102.  
  103.    virtual ir_visitor_status visit(ir_dereference_variable *ir)
  104.    {
  105.       ir_variable *var = ir->variable_referenced();
  106.  
  107.       if (var->mode == this->mode && var->type->is_array() &&
  108.           var->location == VARYING_SLOT_TEX0) {
  109.          /* This is a whole array dereference like "gl_TexCoord = x;",
  110.           * there's probably no point in lowering that.
  111.           */
  112.          this->texcoord_usage |= (1 << var->type->array_size()) - 1;
  113.          this->lower_texcoord_array = false;
  114.       }
  115.       return visit_continue;
  116.    }
  117.  
  118.    virtual ir_visitor_status visit(ir_variable *var)
  119.    {
  120.       if (var->mode != this->mode)
  121.          return visit_continue;
  122.  
  123.       /* Handle colors and fog. */
  124.       switch (var->location) {
  125.       case VARYING_SLOT_COL0:
  126.          this->color[0] = var;
  127.          this->color_usage |= 1;
  128.          break;
  129.       case VARYING_SLOT_COL1:
  130.          this->color[1] = var;
  131.          this->color_usage |= 2;
  132.          break;
  133.       case VARYING_SLOT_BFC0:
  134.          this->backcolor[0] = var;
  135.          this->color_usage |= 1;
  136.          break;
  137.       case VARYING_SLOT_BFC1:
  138.          this->backcolor[1] = var;
  139.          this->color_usage |= 2;
  140.          break;
  141.       case VARYING_SLOT_FOGC:
  142.          this->fog = var;
  143.          this->has_fog = true;
  144.          break;
  145.       }
  146.  
  147.       return visit_continue;
  148.    }
  149.  
  150.    void get(exec_list *ir,
  151.             unsigned num_tfeedback_decls,
  152.             tfeedback_decl *tfeedback_decls)
  153.    {
  154.       /* Handle the transform feedback varyings. */
  155.       for (unsigned i = 0; i < num_tfeedback_decls; i++) {
  156.          if (!tfeedback_decls[i].is_varying())
  157.             continue;
  158.  
  159.          unsigned location = tfeedback_decls[i].get_location();
  160.  
  161.          switch (location) {
  162.          case VARYING_SLOT_COL0:
  163.          case VARYING_SLOT_BFC0:
  164.             this->tfeedback_color_usage |= 1;
  165.             break;
  166.          case VARYING_SLOT_COL1:
  167.          case VARYING_SLOT_BFC1:
  168.             this->tfeedback_color_usage |= 2;
  169.             break;
  170.          case VARYING_SLOT_FOGC:
  171.             this->tfeedback_has_fog = true;
  172.             break;
  173.          default:
  174.             if (location >= VARYING_SLOT_TEX0 &&
  175.                 location <= VARYING_SLOT_TEX7) {
  176.                this->lower_texcoord_array = false;
  177.             }
  178.          }
  179.       }
  180.  
  181.       /* Process the shader. */
  182.       visit_list_elements(this, ir);
  183.  
  184.       if (!this->texcoord_array) {
  185.          this->lower_texcoord_array = false;
  186.       }
  187.    }
  188.  
  189.    bool lower_texcoord_array;
  190.    ir_variable *texcoord_array;
  191.    unsigned texcoord_usage; /* bitmask */
  192.  
  193.    ir_variable *color[2];
  194.    ir_variable *backcolor[2];
  195.    unsigned color_usage; /* bitmask */
  196.    unsigned tfeedback_color_usage; /* bitmask */
  197.  
  198.    ir_variable *fog;
  199.    bool has_fog;
  200.    bool tfeedback_has_fog;
  201.  
  202.    ir_variable_mode mode;
  203. };
  204.  
  205.  
  206. /**
  207.  * This replaces unused varyings with temporary variables.
  208.  *
  209.  * If "ir" is the producer, the "external" usage should come from
  210.  * the consumer. It also works the other way around. If either one is
  211.  * missing, set the "external" usage to a full mask.
  212.  */
  213. class replace_varyings_visitor : public ir_rvalue_visitor {
  214. public:
  215.    replace_varyings_visitor(exec_list *ir,
  216.                             const varying_info_visitor *info,
  217.                             unsigned external_texcoord_usage,
  218.                             unsigned external_color_usage,
  219.                             bool external_has_fog)
  220.       : info(info), new_fog(NULL)
  221.    {
  222.       void *const ctx = ir;
  223.  
  224.       memset(this->new_texcoord, 0, sizeof(this->new_texcoord));
  225.       memset(this->new_color, 0, sizeof(this->new_color));
  226.       memset(this->new_backcolor, 0, sizeof(this->new_backcolor));
  227.  
  228.       const char *mode_str =
  229.          info->mode == ir_var_shader_in ? "in" : "out";
  230.  
  231.       /* Handle texcoord outputs.
  232.        *
  233.        * We're going to break down the gl_TexCoord array into separate
  234.        * variables. First, add declarations of the new variables all
  235.        * occurences of gl_TexCoord will be replaced with.
  236.        */
  237.       if (info->lower_texcoord_array) {
  238.          for (int i = MAX_TEXTURE_COORD_UNITS-1; i >= 0; i--) {
  239.             if (info->texcoord_usage & (1 << i)) {
  240.                char name[32];
  241.  
  242.                if (!(external_texcoord_usage & (1 << i))) {
  243.                   /* This varying is unused in the next stage. Declare
  244.                    * a temporary instead of an output. */
  245.                   snprintf(name, 32, "gl_%s_TexCoord%i_dummy", mode_str, i);
  246.                   this->new_texcoord[i] =
  247.                      new (ctx) ir_variable(glsl_type::vec4_type, name,
  248.                                            ir_var_temporary);
  249.                }
  250.                else {
  251.                   snprintf(name, 32, "gl_%s_TexCoord%i", mode_str, i);
  252.                   this->new_texcoord[i] =
  253.                      new(ctx) ir_variable(glsl_type::vec4_type, name,
  254.                                           info->mode);
  255.                   this->new_texcoord[i]->location = VARYING_SLOT_TEX0 + i;
  256.                   this->new_texcoord[i]->explicit_location = true;
  257.                   this->new_texcoord[i]->explicit_index = 0;
  258.                }
  259.  
  260.                ir->head->insert_before(new_texcoord[i]);
  261.             }
  262.          }
  263.       }
  264.  
  265.       /* Create dummy variables which will replace set-but-unused color and
  266.        * fog outputs.
  267.        */
  268.       external_color_usage |= info->tfeedback_color_usage;
  269.  
  270.       for (int i = 0; i < 2; i++) {
  271.          char name[32];
  272.  
  273.          if (!(external_color_usage & (1 << i))) {
  274.             if (info->color[i]) {
  275.                snprintf(name, 32, "gl_%s_FrontColor%i_dummy", mode_str, i);
  276.                this->new_color[i] =
  277.                   new (ctx) ir_variable(glsl_type::vec4_type, name,
  278.                                         ir_var_temporary);
  279.             }
  280.  
  281.             if (info->backcolor[i]) {
  282.                snprintf(name, 32, "gl_%s_BackColor%i_dummy", mode_str, i);
  283.                this->new_backcolor[i] =
  284.                   new (ctx) ir_variable(glsl_type::vec4_type, name,
  285.                                         ir_var_temporary);
  286.             }
  287.          }
  288.       }
  289.  
  290.       if (!external_has_fog && !info->tfeedback_has_fog &&
  291.           info->fog) {
  292.          char name[32];
  293.  
  294.          snprintf(name, 32, "gl_%s_FogFragCoord_dummy", mode_str);
  295.          this->new_fog = new (ctx) ir_variable(glsl_type::float_type, name,
  296.                                                ir_var_temporary);
  297.       }
  298.  
  299.       /* Now do the replacing. */
  300.       visit_list_elements(this, ir);
  301.    }
  302.  
  303.    virtual ir_visitor_status visit(ir_variable *var)
  304.    {
  305.       /* Remove the gl_TexCoord array. */
  306.       if (this->info->lower_texcoord_array &&
  307.           var == this->info->texcoord_array) {
  308.          var->remove();
  309.       }
  310.  
  311.       /* Replace set-but-unused color and fog outputs with dummy variables. */
  312.       for (int i = 0; i < 2; i++) {
  313.          if (var == this->info->color[i] && this->new_color[i]) {
  314.             var->replace_with(this->new_color[i]);
  315.          }
  316.          if (var == this->info->backcolor[i] &&
  317.              this->new_backcolor[i]) {
  318.             var->replace_with(this->new_backcolor[i]);
  319.          }
  320.       }
  321.  
  322.       if (var == this->info->fog && this->new_fog) {
  323.          var->replace_with(this->new_fog);
  324.       }
  325.  
  326.       return visit_continue;
  327.    }
  328.  
  329.    virtual void handle_rvalue(ir_rvalue **rvalue)
  330.    {
  331.       if (!*rvalue)
  332.          return;
  333.  
  334.       void *ctx = ralloc_parent(*rvalue);
  335.  
  336.       /* Replace an array dereference gl_TexCoord[i] with a single
  337.        * variable dereference representing gl_TexCoord[i].
  338.        */
  339.       if (this->info->lower_texcoord_array) {
  340.          /* gl_TexCoord[i] occurence */
  341.          ir_dereference_array *const da = (*rvalue)->as_dereference_array();
  342.  
  343.          if (da && da->variable_referenced() ==
  344.              this->info->texcoord_array) {
  345.             unsigned i = da->array_index->as_constant()->get_uint_component(0);
  346.  
  347.             *rvalue = new(ctx) ir_dereference_variable(this->new_texcoord[i]);
  348.             return;
  349.          }
  350.       }
  351.  
  352.       /* Replace set-but-unused color and fog outputs with dummy variables. */
  353.       ir_dereference_variable *const dv = (*rvalue)->as_dereference_variable();
  354.       if (!dv)
  355.          return;
  356.  
  357.       ir_variable *var = dv->variable_referenced();
  358.  
  359.       for (int i = 0; i < 2; i++) {
  360.          if (var == this->info->color[i] && this->new_color[i]) {
  361.             *rvalue = new(ctx) ir_dereference_variable(this->new_color[i]);
  362.             return;
  363.          }
  364.          if (var == this->info->backcolor[i] &&
  365.              this->new_backcolor[i]) {
  366.             *rvalue = new(ctx) ir_dereference_variable(this->new_backcolor[i]);
  367.             return;
  368.          }
  369.       }
  370.  
  371.       if (var == this->info->fog && this->new_fog) {
  372.          *rvalue = new(ctx) ir_dereference_variable(this->new_fog);
  373.       }
  374.    }
  375.  
  376.    virtual ir_visitor_status visit_leave(ir_assignment *ir)
  377.    {
  378.       handle_rvalue(&ir->rhs);
  379.       handle_rvalue(&ir->condition);
  380.  
  381.       /* We have to use set_lhs when changing the LHS of an assignment. */
  382.       ir_rvalue *lhs = ir->lhs;
  383.  
  384.       handle_rvalue(&lhs);
  385.       if (lhs != ir->lhs) {
  386.          ir->set_lhs(lhs);
  387.       }
  388.  
  389.       return visit_continue;
  390.    }
  391.  
  392. private:
  393.    const varying_info_visitor *info;
  394.    struct ir_variable *new_texcoord[MAX_TEXTURE_COORD_UNITS];
  395.    struct ir_variable *new_color[2];
  396.    struct ir_variable *new_backcolor[2];
  397.    struct ir_variable *new_fog;
  398. };
  399.  
  400.  
  401. static void
  402. lower_texcoord_array(exec_list *ir, const varying_info_visitor *info)
  403. {
  404.    replace_varyings_visitor(ir, info,
  405.                             (1 << MAX_TEXTURE_COORD_UNITS) - 1,
  406.                             1 | 2, true);
  407. }
  408.  
  409.  
  410. void
  411. do_dead_builtin_varyings(struct gl_context *ctx,
  412.                          gl_shader *producer, gl_shader *consumer,
  413.                          unsigned num_tfeedback_decls,
  414.                          tfeedback_decl *tfeedback_decls)
  415. {
  416.    /* This optimization has no effect with the core context and GLES2, because
  417.     * the built-in varyings we're eliminating here are not available there.
  418.     *
  419.     * EXT_separate_shader_objects doesn't allow this optimization,
  420.     * because a program object can be bound partially (e.g. only one
  421.     * stage of a program object can be bound).
  422.     */
  423.    if (ctx->API == API_OPENGL_CORE ||
  424.        ctx->API == API_OPENGLES2 ||
  425.        ctx->Extensions.EXT_separate_shader_objects) {
  426.       return;
  427.    }
  428.  
  429.    /* Information about built-in varyings. */
  430.    varying_info_visitor producer_info(ir_var_shader_out);
  431.    varying_info_visitor consumer_info(ir_var_shader_in);
  432.  
  433.    if (producer) {
  434.       producer_info.get(producer->ir, num_tfeedback_decls, tfeedback_decls);
  435.  
  436.       if (!consumer) {
  437.          /* At least eliminate unused gl_TexCoord elements. */
  438.          if (producer_info.lower_texcoord_array) {
  439.             lower_texcoord_array(producer->ir, &producer_info);
  440.          }
  441.          return;
  442.       }
  443.    }
  444.  
  445.    if (consumer) {
  446.       consumer_info.get(consumer->ir, 0, NULL);
  447.  
  448.       if (!producer) {
  449.          /* At least eliminate unused gl_TexCoord elements. */
  450.          if (consumer_info.lower_texcoord_array) {
  451.             lower_texcoord_array(consumer->ir, &consumer_info);
  452.          }
  453.          return;
  454.       }
  455.    }
  456.  
  457.    /* Eliminate the outputs unused by the consumer. */
  458.    if (producer_info.lower_texcoord_array ||
  459.        producer_info.color_usage ||
  460.        producer_info.has_fog) {
  461.       replace_varyings_visitor(producer->ir,
  462.                                &producer_info,
  463.                                consumer_info.texcoord_usage,
  464.                                consumer_info.color_usage,
  465.                                consumer_info.has_fog);
  466.    }
  467.  
  468.    /* The gl_TexCoord fragment shader inputs can be initialized
  469.     * by GL_COORD_REPLACE, so we can't eliminate them.
  470.     *
  471.     * This doesn't prevent elimination of the gl_TexCoord elements which
  472.     * are not read by the fragment shader. We want to eliminate those anyway.
  473.     */
  474.    if (consumer->Type == GL_FRAGMENT_SHADER) {
  475.       producer_info.texcoord_usage = (1 << MAX_TEXTURE_COORD_UNITS) - 1;
  476.    }
  477.  
  478.    /* Eliminate the inputs uninitialized by the producer. */
  479.    if (consumer_info.lower_texcoord_array ||
  480.        consumer_info.color_usage ||
  481.        consumer_info.has_fog) {
  482.       replace_varyings_visitor(consumer->ir,
  483.                                &consumer_info,
  484.                                producer_info.texcoord_usage,
  485.                                producer_info.color_usage,
  486.                                producer_info.has_fog);
  487.    }
  488. }
  489.