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