Subversion Repositories Kolibri OS

Rev

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 occurrences 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.  * The same is done for the gl_FragData fragment shader output.
  47.  */
  48.  
  49. #include "main/core.h" /* for snprintf and ARRAY_SIZE */
  50. #include "ir.h"
  51. #include "ir_rvalue_visitor.h"
  52. #include "ir_optimization.h"
  53. #include "ir_print_visitor.h"
  54. #include "glsl_types.h"
  55. #include "link_varyings.h"
  56.  
  57. namespace {
  58.  
  59. /**
  60.  * This obtains detailed information about built-in varyings from shader code.
  61.  */
  62. class varying_info_visitor : public ir_hierarchical_visitor {
  63. public:
  64.    /* "mode" can be either ir_var_shader_in or ir_var_shader_out */
  65.    varying_info_visitor(ir_variable_mode mode, bool find_frag_outputs = false)
  66.       : lower_texcoord_array(true),
  67.         texcoord_array(NULL),
  68.         texcoord_usage(0),
  69.         find_frag_outputs(find_frag_outputs),
  70.         lower_fragdata_array(true),
  71.         fragdata_array(NULL),
  72.         fragdata_usage(0),
  73.         color_usage(0),
  74.         tfeedback_color_usage(0),
  75.         fog(NULL),
  76.         has_fog(false),
  77.         tfeedback_has_fog(false),
  78.         mode(mode)
  79.    {
  80.       memset(color, 0, sizeof(color));
  81.       memset(backcolor, 0, sizeof(backcolor));
  82.    }
  83.  
  84.    virtual ir_visitor_status visit_enter(ir_dereference_array *ir)
  85.    {
  86.       ir_variable *var = ir->variable_referenced();
  87.  
  88.       if (!var || var->data.mode != this->mode)
  89.          return visit_continue;
  90.  
  91.       if (this->find_frag_outputs && var->data.location == FRAG_RESULT_DATA0) {
  92.          this->fragdata_array = var;
  93.  
  94.          ir_constant *index = ir->array_index->as_constant();
  95.          if (index == NULL) {
  96.             /* This is variable indexing. */
  97.             this->fragdata_usage |= (1 << var->type->array_size()) - 1;
  98.             this->lower_fragdata_array = false;
  99.          }
  100.          else {
  101.             this->fragdata_usage |= 1 << index->get_uint_component(0);
  102.             /* Don't lower fragdata array if the output variable
  103.              * is not a float variable (or float vector) because it will
  104.              * generate wrong register assignments because of different
  105.              * data types.
  106.              */
  107.             if (var->type->gl_type != GL_FLOAT &&
  108.                 var->type->gl_type != GL_FLOAT_VEC2 &&
  109.                 var->type->gl_type != GL_FLOAT_VEC3 &&
  110.                 var->type->gl_type != GL_FLOAT_VEC4)
  111.                this->lower_fragdata_array = false;
  112.          }
  113.  
  114.          /* Don't visit the leaves of ir_dereference_array. */
  115.          return visit_continue_with_parent;
  116.       }
  117.  
  118.       if (!this->find_frag_outputs && var->data.location == VARYING_SLOT_TEX0) {
  119.          this->texcoord_array = var;
  120.  
  121.          ir_constant *index = ir->array_index->as_constant();
  122.          if (index == NULL) {
  123.             /* There is variable indexing, we can't lower the texcoord array.
  124.              */
  125.             this->texcoord_usage |= (1 << var->type->array_size()) - 1;
  126.             this->lower_texcoord_array = false;
  127.          }
  128.          else {
  129.             this->texcoord_usage |= 1 << index->get_uint_component(0);
  130.          }
  131.  
  132.          /* Don't visit the leaves of ir_dereference_array. */
  133.          return visit_continue_with_parent;
  134.       }
  135.  
  136.       return visit_continue;
  137.    }
  138.  
  139.    virtual ir_visitor_status visit(ir_dereference_variable *ir)
  140.    {
  141.       ir_variable *var = ir->variable_referenced();
  142.  
  143.       if (var->data.mode != this->mode || !var->type->is_array())
  144.          return visit_continue;
  145.  
  146.       if (this->find_frag_outputs && var->data.location == FRAG_RESULT_DATA0) {
  147.          /* This is a whole array dereference. */
  148.          this->fragdata_usage |= (1 << var->type->array_size()) - 1;
  149.          this->lower_fragdata_array = false;
  150.          return visit_continue;
  151.       }
  152.  
  153.       if (!this->find_frag_outputs && var->data.location == VARYING_SLOT_TEX0) {
  154.          /* This is a whole array dereference like "gl_TexCoord = x;",
  155.           * there's probably no point in lowering that.
  156.           */
  157.          this->texcoord_usage |= (1 << var->type->array_size()) - 1;
  158.          this->lower_texcoord_array = false;
  159.       }
  160.       return visit_continue;
  161.    }
  162.  
  163.    virtual ir_visitor_status visit(ir_variable *var)
  164.    {
  165.       if (var->data.mode != this->mode)
  166.          return visit_continue;
  167.  
  168.       /* Nothing to do here for fragment outputs. */
  169.       if (this->find_frag_outputs)
  170.          return visit_continue;
  171.  
  172.       /* Handle colors and fog. */
  173.       switch (var->data.location) {
  174.       case VARYING_SLOT_COL0:
  175.          this->color[0] = var;
  176.          this->color_usage |= 1;
  177.          break;
  178.       case VARYING_SLOT_COL1:
  179.          this->color[1] = var;
  180.          this->color_usage |= 2;
  181.          break;
  182.       case VARYING_SLOT_BFC0:
  183.          this->backcolor[0] = var;
  184.          this->color_usage |= 1;
  185.          break;
  186.       case VARYING_SLOT_BFC1:
  187.          this->backcolor[1] = var;
  188.          this->color_usage |= 2;
  189.          break;
  190.       case VARYING_SLOT_FOGC:
  191.          this->fog = var;
  192.          this->has_fog = true;
  193.          break;
  194.       }
  195.  
  196.       return visit_continue;
  197.    }
  198.  
  199.    void get(exec_list *ir,
  200.             unsigned num_tfeedback_decls,
  201.             tfeedback_decl *tfeedback_decls)
  202.    {
  203.       /* Handle the transform feedback varyings. */
  204.       for (unsigned i = 0; i < num_tfeedback_decls; i++) {
  205.          if (!tfeedback_decls[i].is_varying())
  206.             continue;
  207.  
  208.          unsigned location = tfeedback_decls[i].get_location();
  209.  
  210.          switch (location) {
  211.          case VARYING_SLOT_COL0:
  212.          case VARYING_SLOT_BFC0:
  213.             this->tfeedback_color_usage |= 1;
  214.             break;
  215.          case VARYING_SLOT_COL1:
  216.          case VARYING_SLOT_BFC1:
  217.             this->tfeedback_color_usage |= 2;
  218.             break;
  219.          case VARYING_SLOT_FOGC:
  220.             this->tfeedback_has_fog = true;
  221.             break;
  222.          default:
  223.             if (location >= VARYING_SLOT_TEX0 &&
  224.                 location <= VARYING_SLOT_TEX7) {
  225.                this->lower_texcoord_array = false;
  226.             }
  227.          }
  228.       }
  229.  
  230.       /* Process the shader. */
  231.       visit_list_elements(this, ir);
  232.  
  233.       if (!this->texcoord_array) {
  234.          this->lower_texcoord_array = false;
  235.       }
  236.       if (!this->fragdata_array) {
  237.          this->lower_fragdata_array = false;
  238.       }
  239.    }
  240.  
  241.    bool lower_texcoord_array;
  242.    ir_variable *texcoord_array;
  243.    unsigned texcoord_usage; /* bitmask */
  244.  
  245.    bool find_frag_outputs; /* false if it's looking for varyings */
  246.    bool lower_fragdata_array;
  247.    ir_variable *fragdata_array;
  248.    unsigned fragdata_usage; /* bitmask */
  249.  
  250.    ir_variable *color[2];
  251.    ir_variable *backcolor[2];
  252.    unsigned color_usage; /* bitmask */
  253.    unsigned tfeedback_color_usage; /* bitmask */
  254.  
  255.    ir_variable *fog;
  256.    bool has_fog;
  257.    bool tfeedback_has_fog;
  258.  
  259.    ir_variable_mode mode;
  260. };
  261.  
  262.  
  263. /**
  264.  * This replaces unused varyings with temporary variables.
  265.  *
  266.  * If "ir" is the producer, the "external" usage should come from
  267.  * the consumer. It also works the other way around. If either one is
  268.  * missing, set the "external" usage to a full mask.
  269.  */
  270. class replace_varyings_visitor : public ir_rvalue_visitor {
  271. public:
  272.    replace_varyings_visitor(exec_list *ir,
  273.                             const varying_info_visitor *info,
  274.                             unsigned external_texcoord_usage,
  275.                             unsigned external_color_usage,
  276.                             bool external_has_fog)
  277.       : info(info), new_fog(NULL)
  278.    {
  279.       void *const ctx = ir;
  280.  
  281.       memset(this->new_fragdata, 0, sizeof(this->new_fragdata));
  282.       memset(this->new_texcoord, 0, sizeof(this->new_texcoord));
  283.       memset(this->new_color, 0, sizeof(this->new_color));
  284.       memset(this->new_backcolor, 0, sizeof(this->new_backcolor));
  285.  
  286.       const char *mode_str =
  287.          info->mode == ir_var_shader_in ? "in" : "out";
  288.  
  289.       /* Handle texcoord outputs.
  290.        *
  291.        * We're going to break down the gl_TexCoord array into separate
  292.        * variables. First, add declarations of the new variables all
  293.        * occurrences of gl_TexCoord will be replaced with.
  294.        */
  295.       if (info->lower_texcoord_array) {
  296.          prepare_array(ir, this->new_texcoord, ARRAY_SIZE(this->new_texcoord),
  297.                        VARYING_SLOT_TEX0, "TexCoord", mode_str,
  298.                        info->texcoord_usage, external_texcoord_usage);
  299.       }
  300.  
  301.       /* Handle gl_FragData in the same way like gl_TexCoord. */
  302.       if (info->lower_fragdata_array) {
  303.          prepare_array(ir, this->new_fragdata, ARRAY_SIZE(this->new_fragdata),
  304.                        FRAG_RESULT_DATA0, "FragData", mode_str,
  305.                        info->fragdata_usage, (1 << MAX_DRAW_BUFFERS) - 1);
  306.       }
  307.  
  308.       /* Create dummy variables which will replace set-but-unused color and
  309.        * fog outputs.
  310.        */
  311.       external_color_usage |= info->tfeedback_color_usage;
  312.  
  313.       for (int i = 0; i < 2; i++) {
  314.          char name[32];
  315.  
  316.          if (!(external_color_usage & (1 << i))) {
  317.             if (info->color[i]) {
  318.                snprintf(name, 32, "gl_%s_FrontColor%i_dummy", mode_str, i);
  319.                this->new_color[i] =
  320.                   new (ctx) ir_variable(glsl_type::vec4_type, name,
  321.                                         ir_var_temporary);
  322.             }
  323.  
  324.             if (info->backcolor[i]) {
  325.                snprintf(name, 32, "gl_%s_BackColor%i_dummy", mode_str, i);
  326.                this->new_backcolor[i] =
  327.                   new (ctx) ir_variable(glsl_type::vec4_type, name,
  328.                                         ir_var_temporary);
  329.             }
  330.          }
  331.       }
  332.  
  333.       if (!external_has_fog && !info->tfeedback_has_fog &&
  334.           info->fog) {
  335.          char name[32];
  336.  
  337.          snprintf(name, 32, "gl_%s_FogFragCoord_dummy", mode_str);
  338.          this->new_fog = new (ctx) ir_variable(glsl_type::float_type, name,
  339.                                                ir_var_temporary);
  340.       }
  341.  
  342.       /* Now do the replacing. */
  343.       visit_list_elements(this, ir);
  344.    }
  345.  
  346.    void prepare_array(exec_list *ir,
  347.                       ir_variable **new_var,
  348.                       int max_elements, unsigned start_location,
  349.                       const char *var_name, const char *mode_str,
  350.                       unsigned usage, unsigned external_usage)
  351.    {
  352.       void *const ctx = ir;
  353.  
  354.       for (int i = max_elements-1; i >= 0; i--) {
  355.          if (usage & (1 << i)) {
  356.             char name[32];
  357.  
  358.             if (!(external_usage & (1 << i))) {
  359.                /* This varying is unused in the next stage. Declare
  360.                 * a temporary instead of an output. */
  361.                snprintf(name, 32, "gl_%s_%s%i_dummy", mode_str, var_name, i);
  362.                new_var[i] =
  363.                   new (ctx) ir_variable(glsl_type::vec4_type, name,
  364.                                         ir_var_temporary);
  365.             }
  366.             else {
  367.                snprintf(name, 32, "gl_%s_%s%i", mode_str, var_name, i);
  368.                new_var[i] =
  369.                   new(ctx) ir_variable(glsl_type::vec4_type, name,
  370.                                        this->info->mode);
  371.                new_var[i]->data.location = start_location + i;
  372.                new_var[i]->data.explicit_location = true;
  373.                new_var[i]->data.explicit_index = 0;
  374.             }
  375.  
  376.             ir->head->insert_before(new_var[i]);
  377.          }
  378.       }
  379.    }
  380.  
  381.    virtual ir_visitor_status visit(ir_variable *var)
  382.    {
  383.       /* Remove the gl_TexCoord array. */
  384.       if (this->info->lower_texcoord_array &&
  385.           var == this->info->texcoord_array) {
  386.          var->remove();
  387.       }
  388.  
  389.       /* Remove the gl_FragData array. */
  390.       if (this->info->lower_fragdata_array &&
  391.           var == this->info->fragdata_array) {
  392.          var->remove();
  393.       }
  394.  
  395.       /* Replace set-but-unused color and fog outputs with dummy variables. */
  396.       for (int i = 0; i < 2; i++) {
  397.          if (var == this->info->color[i] && this->new_color[i]) {
  398.             var->replace_with(this->new_color[i]);
  399.          }
  400.          if (var == this->info->backcolor[i] &&
  401.              this->new_backcolor[i]) {
  402.             var->replace_with(this->new_backcolor[i]);
  403.          }
  404.       }
  405.  
  406.       if (var == this->info->fog && this->new_fog) {
  407.          var->replace_with(this->new_fog);
  408.       }
  409.  
  410.       return visit_continue;
  411.    }
  412.  
  413.    virtual void handle_rvalue(ir_rvalue **rvalue)
  414.    {
  415.       if (!*rvalue)
  416.          return;
  417.  
  418.       void *ctx = ralloc_parent(*rvalue);
  419.  
  420.       /* Replace an array dereference gl_TexCoord[i] with a single
  421.        * variable dereference representing gl_TexCoord[i].
  422.        */
  423.       if (this->info->lower_texcoord_array) {
  424.          /* gl_TexCoord[i] occurrence */
  425.          ir_dereference_array *const da = (*rvalue)->as_dereference_array();
  426.  
  427.          if (da && da->variable_referenced() ==
  428.              this->info->texcoord_array) {
  429.             unsigned i = da->array_index->as_constant()->get_uint_component(0);
  430.  
  431.             *rvalue = new(ctx) ir_dereference_variable(this->new_texcoord[i]);
  432.             return;
  433.          }
  434.       }
  435.  
  436.       /* Same for gl_FragData. */
  437.       if (this->info->lower_fragdata_array) {
  438.          /* gl_FragData[i] occurrence */
  439.          ir_dereference_array *const da = (*rvalue)->as_dereference_array();
  440.  
  441.          if (da && da->variable_referenced() == this->info->fragdata_array) {
  442.             unsigned i = da->array_index->as_constant()->get_uint_component(0);
  443.  
  444.             *rvalue = new(ctx) ir_dereference_variable(this->new_fragdata[i]);
  445.             return;
  446.          }
  447.       }
  448.  
  449.       /* Replace set-but-unused color and fog outputs with dummy variables. */
  450.       ir_dereference_variable *const dv = (*rvalue)->as_dereference_variable();
  451.       if (!dv)
  452.          return;
  453.  
  454.       ir_variable *var = dv->variable_referenced();
  455.  
  456.       for (int i = 0; i < 2; i++) {
  457.          if (var == this->info->color[i] && this->new_color[i]) {
  458.             *rvalue = new(ctx) ir_dereference_variable(this->new_color[i]);
  459.             return;
  460.          }
  461.          if (var == this->info->backcolor[i] &&
  462.              this->new_backcolor[i]) {
  463.             *rvalue = new(ctx) ir_dereference_variable(this->new_backcolor[i]);
  464.             return;
  465.          }
  466.       }
  467.  
  468.       if (var == this->info->fog && this->new_fog) {
  469.          *rvalue = new(ctx) ir_dereference_variable(this->new_fog);
  470.       }
  471.    }
  472.  
  473.    virtual ir_visitor_status visit_leave(ir_assignment *ir)
  474.    {
  475.       handle_rvalue(&ir->rhs);
  476.       handle_rvalue(&ir->condition);
  477.  
  478.       /* We have to use set_lhs when changing the LHS of an assignment. */
  479.       ir_rvalue *lhs = ir->lhs;
  480.  
  481.       handle_rvalue(&lhs);
  482.       if (lhs != ir->lhs) {
  483.          ir->set_lhs(lhs);
  484.       }
  485.  
  486.       return visit_continue;
  487.    }
  488.  
  489. private:
  490.    const varying_info_visitor *info;
  491.    ir_variable *new_fragdata[MAX_DRAW_BUFFERS];
  492.    ir_variable *new_texcoord[MAX_TEXTURE_COORD_UNITS];
  493.    ir_variable *new_color[2];
  494.    ir_variable *new_backcolor[2];
  495.    ir_variable *new_fog;
  496. };
  497.  
  498. } /* anonymous namespace */
  499.  
  500. static void
  501. lower_texcoord_array(exec_list *ir, const varying_info_visitor *info)
  502. {
  503.    replace_varyings_visitor(ir, info,
  504.                             (1 << MAX_TEXTURE_COORD_UNITS) - 1,
  505.                             1 | 2, true);
  506. }
  507.  
  508. static void
  509. lower_fragdata_array(exec_list *ir)
  510. {
  511.    varying_info_visitor info(ir_var_shader_out, true);
  512.    info.get(ir, 0, NULL);
  513.  
  514.    replace_varyings_visitor(ir, &info, 0, 0, 0);
  515. }
  516.  
  517.  
  518. void
  519. do_dead_builtin_varyings(struct gl_context *ctx,
  520.                          gl_shader *producer, gl_shader *consumer,
  521.                          unsigned num_tfeedback_decls,
  522.                          tfeedback_decl *tfeedback_decls)
  523. {
  524.    /* Lower the gl_FragData array to separate variables. */
  525.    if (consumer && consumer->Stage == MESA_SHADER_FRAGMENT) {
  526.       lower_fragdata_array(consumer->ir);
  527.    }
  528.  
  529.    /* Lowering of built-in varyings has no effect with the core context and
  530.     * GLES2, because they are not available there.
  531.     */
  532.    if (ctx->API == API_OPENGL_CORE ||
  533.        ctx->API == API_OPENGLES2) {
  534.       return;
  535.    }
  536.  
  537.    /* Information about built-in varyings. */
  538.    varying_info_visitor producer_info(ir_var_shader_out);
  539.    varying_info_visitor consumer_info(ir_var_shader_in);
  540.  
  541.    if (producer) {
  542.       producer_info.get(producer->ir, num_tfeedback_decls, tfeedback_decls);
  543.  
  544.       if (!consumer) {
  545.          /* At least eliminate unused gl_TexCoord elements. */
  546.          if (producer_info.lower_texcoord_array) {
  547.             lower_texcoord_array(producer->ir, &producer_info);
  548.          }
  549.          return;
  550.       }
  551.    }
  552.  
  553.    if (consumer) {
  554.       consumer_info.get(consumer->ir, 0, NULL);
  555.  
  556.       if (!producer) {
  557.          /* At least eliminate unused gl_TexCoord elements. */
  558.          if (consumer_info.lower_texcoord_array) {
  559.             lower_texcoord_array(consumer->ir, &consumer_info);
  560.          }
  561.          return;
  562.       }
  563.    }
  564.  
  565.    /* Eliminate the outputs unused by the consumer. */
  566.    if (producer_info.lower_texcoord_array ||
  567.        producer_info.color_usage ||
  568.        producer_info.has_fog) {
  569.       replace_varyings_visitor(producer->ir,
  570.                                &producer_info,
  571.                                consumer_info.texcoord_usage,
  572.                                consumer_info.color_usage,
  573.                                consumer_info.has_fog);
  574.    }
  575.  
  576.    /* The gl_TexCoord fragment shader inputs can be initialized
  577.     * by GL_COORD_REPLACE, so we can't eliminate them.
  578.     *
  579.     * This doesn't prevent elimination of the gl_TexCoord elements which
  580.     * are not read by the fragment shader. We want to eliminate those anyway.
  581.     */
  582.    if (consumer->Stage == MESA_SHADER_FRAGMENT) {
  583.       producer_info.texcoord_usage = (1 << MAX_TEXTURE_COORD_UNITS) - 1;
  584.    }
  585.  
  586.    /* Eliminate the inputs uninitialized by the producer. */
  587.    if (consumer_info.lower_texcoord_array ||
  588.        consumer_info.color_usage ||
  589.        consumer_info.has_fog) {
  590.       replace_varyings_visitor(consumer->ir,
  591.                                &consumer_info,
  592.                                producer_info.texcoord_usage,
  593.                                producer_info.color_usage,
  594.                                producer_info.has_fog);
  595.    }
  596. }
  597.