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