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