nir/spirv: Add a missing break statement
[mesa.git] / src / 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 "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 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(struct gl_shader *sha,
273 const varying_info_visitor *info,
274 unsigned external_texcoord_usage,
275 unsigned external_color_usage,
276 bool external_has_fog)
277 : shader(sha), info(info), new_fog(NULL)
278 {
279 void *const ctx = shader->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(shader->ir, this->new_texcoord,
297 ARRAY_SIZE(this->new_texcoord),
298 VARYING_SLOT_TEX0, "TexCoord", mode_str,
299 info->texcoord_usage, external_texcoord_usage);
300 }
301
302 /* Handle gl_FragData in the same way like gl_TexCoord. */
303 if (info->lower_fragdata_array) {
304 prepare_array(shader->ir, this->new_fragdata,
305 ARRAY_SIZE(this->new_fragdata),
306 FRAG_RESULT_DATA0, "FragData", mode_str,
307 info->fragdata_usage, (1 << MAX_DRAW_BUFFERS) - 1);
308 }
309
310 /* Create dummy variables which will replace set-but-unused color and
311 * fog outputs.
312 */
313 external_color_usage |= info->tfeedback_color_usage;
314
315 for (int i = 0; i < 2; i++) {
316 char name[32];
317
318 if (!(external_color_usage & (1 << i))) {
319 if (info->color[i]) {
320 snprintf(name, 32, "gl_%s_FrontColor%i_dummy", mode_str, i);
321 this->new_color[i] =
322 new (ctx) ir_variable(glsl_type::vec4_type, name,
323 ir_var_temporary);
324 }
325
326 if (info->backcolor[i]) {
327 snprintf(name, 32, "gl_%s_BackColor%i_dummy", mode_str, i);
328 this->new_backcolor[i] =
329 new (ctx) ir_variable(glsl_type::vec4_type, name,
330 ir_var_temporary);
331 }
332 }
333 }
334
335 if (!external_has_fog && !info->tfeedback_has_fog &&
336 info->fog) {
337 char name[32];
338
339 snprintf(name, 32, "gl_%s_FogFragCoord_dummy", mode_str);
340 this->new_fog = new (ctx) ir_variable(glsl_type::float_type, name,
341 ir_var_temporary);
342 }
343
344 /* Now do the replacing. */
345 visit_list_elements(this, shader->ir);
346 }
347
348 void prepare_array(exec_list *ir,
349 ir_variable **new_var,
350 int max_elements, unsigned start_location,
351 const char *var_name, const char *mode_str,
352 unsigned usage, unsigned external_usage)
353 {
354 void *const ctx = ir;
355
356 for (int i = max_elements-1; i >= 0; i--) {
357 if (usage & (1 << i)) {
358 char name[32];
359
360 if (!(external_usage & (1 << i))) {
361 /* This varying is unused in the next stage. Declare
362 * a temporary instead of an output. */
363 snprintf(name, 32, "gl_%s_%s%i_dummy", mode_str, var_name, i);
364 new_var[i] =
365 new (ctx) ir_variable(glsl_type::vec4_type, name,
366 ir_var_temporary);
367 }
368 else {
369 snprintf(name, 32, "gl_%s_%s%i", mode_str, var_name, i);
370 new_var[i] =
371 new(ctx) ir_variable(glsl_type::vec4_type, name,
372 this->info->mode);
373 new_var[i]->data.location = start_location + i;
374 new_var[i]->data.explicit_location = true;
375 new_var[i]->data.explicit_index = 0;
376 }
377
378 ir->head->insert_before(new_var[i]);
379 }
380 }
381 }
382
383 virtual ir_visitor_status visit(ir_variable *var)
384 {
385 /* Remove the gl_TexCoord array. */
386 if (this->info->lower_texcoord_array &&
387 var == this->info->texcoord_array) {
388 var->remove();
389 }
390
391 /* Remove the gl_FragData array. */
392 if (this->info->lower_fragdata_array &&
393 var == this->info->fragdata_array) {
394
395 /* Clone variable for program resource list before it is removed. */
396 if (!shader->fragdata_arrays)
397 shader->fragdata_arrays = new (shader) exec_list;
398
399 shader->fragdata_arrays->push_tail(var->clone(shader, NULL));
400
401 var->remove();
402 }
403
404 /* Replace set-but-unused color and fog outputs with dummy variables. */
405 for (int i = 0; i < 2; i++) {
406 if (var == this->info->color[i] && this->new_color[i]) {
407 var->replace_with(this->new_color[i]);
408 }
409 if (var == this->info->backcolor[i] &&
410 this->new_backcolor[i]) {
411 var->replace_with(this->new_backcolor[i]);
412 }
413 }
414
415 if (var == this->info->fog && this->new_fog) {
416 var->replace_with(this->new_fog);
417 }
418
419 return visit_continue;
420 }
421
422 virtual void handle_rvalue(ir_rvalue **rvalue)
423 {
424 if (!*rvalue)
425 return;
426
427 void *ctx = ralloc_parent(*rvalue);
428
429 /* Replace an array dereference gl_TexCoord[i] with a single
430 * variable dereference representing gl_TexCoord[i].
431 */
432 if (this->info->lower_texcoord_array) {
433 /* gl_TexCoord[i] occurrence */
434 ir_dereference_array *const da = (*rvalue)->as_dereference_array();
435
436 if (da && da->variable_referenced() ==
437 this->info->texcoord_array) {
438 unsigned i = da->array_index->as_constant()->get_uint_component(0);
439
440 *rvalue = new(ctx) ir_dereference_variable(this->new_texcoord[i]);
441 return;
442 }
443 }
444
445 /* Same for gl_FragData. */
446 if (this->info->lower_fragdata_array) {
447 /* gl_FragData[i] occurrence */
448 ir_dereference_array *const da = (*rvalue)->as_dereference_array();
449
450 if (da && da->variable_referenced() == this->info->fragdata_array) {
451 unsigned i = da->array_index->as_constant()->get_uint_component(0);
452
453 *rvalue = new(ctx) ir_dereference_variable(this->new_fragdata[i]);
454 return;
455 }
456 }
457
458 /* Replace set-but-unused color and fog outputs with dummy variables. */
459 ir_dereference_variable *const dv = (*rvalue)->as_dereference_variable();
460 if (!dv)
461 return;
462
463 ir_variable *var = dv->variable_referenced();
464
465 for (int i = 0; i < 2; i++) {
466 if (var == this->info->color[i] && this->new_color[i]) {
467 *rvalue = new(ctx) ir_dereference_variable(this->new_color[i]);
468 return;
469 }
470 if (var == this->info->backcolor[i] &&
471 this->new_backcolor[i]) {
472 *rvalue = new(ctx) ir_dereference_variable(this->new_backcolor[i]);
473 return;
474 }
475 }
476
477 if (var == this->info->fog && this->new_fog) {
478 *rvalue = new(ctx) ir_dereference_variable(this->new_fog);
479 }
480 }
481
482 virtual ir_visitor_status visit_leave(ir_assignment *ir)
483 {
484 handle_rvalue(&ir->rhs);
485 handle_rvalue(&ir->condition);
486
487 /* We have to use set_lhs when changing the LHS of an assignment. */
488 ir_rvalue *lhs = ir->lhs;
489
490 handle_rvalue(&lhs);
491 if (lhs != ir->lhs) {
492 ir->set_lhs(lhs);
493 }
494
495 return visit_continue;
496 }
497
498 private:
499 struct gl_shader *shader;
500 const varying_info_visitor *info;
501 ir_variable *new_fragdata[MAX_DRAW_BUFFERS];
502 ir_variable *new_texcoord[MAX_TEXTURE_COORD_UNITS];
503 ir_variable *new_color[2];
504 ir_variable *new_backcolor[2];
505 ir_variable *new_fog;
506 };
507
508 } /* anonymous namespace */
509
510 static void
511 lower_texcoord_array(struct gl_shader *shader, const varying_info_visitor *info)
512 {
513 replace_varyings_visitor(shader, info,
514 (1 << MAX_TEXTURE_COORD_UNITS) - 1,
515 1 | 2, true);
516 }
517
518 static void
519 lower_fragdata_array(struct gl_shader *shader)
520 {
521 varying_info_visitor info(ir_var_shader_out, true);
522 info.get(shader->ir, 0, NULL);
523
524 replace_varyings_visitor(shader, &info, 0, 0, 0);
525 }
526
527
528 void
529 do_dead_builtin_varyings(struct gl_context *ctx,
530 gl_shader *producer, gl_shader *consumer,
531 unsigned num_tfeedback_decls,
532 tfeedback_decl *tfeedback_decls)
533 {
534 /* Lower the gl_FragData array to separate variables. */
535 if (consumer && consumer->Stage == MESA_SHADER_FRAGMENT) {
536 lower_fragdata_array(consumer);
537 }
538
539 /* Lowering of built-in varyings has no effect with the core context and
540 * GLES2, because they are not available there.
541 */
542 if (ctx->API == API_OPENGL_CORE ||
543 ctx->API == API_OPENGLES2) {
544 return;
545 }
546
547 /* Information about built-in varyings. */
548 varying_info_visitor producer_info(ir_var_shader_out);
549 varying_info_visitor consumer_info(ir_var_shader_in);
550
551 if (producer) {
552 producer_info.get(producer->ir, num_tfeedback_decls, tfeedback_decls);
553
554 if (!consumer) {
555 /* At least eliminate unused gl_TexCoord elements. */
556 if (producer_info.lower_texcoord_array) {
557 lower_texcoord_array(producer, &producer_info);
558 }
559 return;
560 }
561 }
562
563 if (consumer) {
564 consumer_info.get(consumer->ir, 0, NULL);
565
566 if (!producer) {
567 /* At least eliminate unused gl_TexCoord elements. */
568 if (consumer_info.lower_texcoord_array) {
569 lower_texcoord_array(consumer, &consumer_info);
570 }
571 return;
572 }
573 }
574
575 /* Eliminate the outputs unused by the consumer. */
576 if (producer_info.lower_texcoord_array ||
577 producer_info.color_usage ||
578 producer_info.has_fog) {
579 replace_varyings_visitor(producer,
580 &producer_info,
581 consumer_info.texcoord_usage,
582 consumer_info.color_usage,
583 consumer_info.has_fog);
584 }
585
586 /* The gl_TexCoord fragment shader inputs can be initialized
587 * by GL_COORD_REPLACE, so we can't eliminate them.
588 *
589 * This doesn't prevent elimination of the gl_TexCoord elements which
590 * are not read by the fragment shader. We want to eliminate those anyway.
591 */
592 if (consumer->Stage == MESA_SHADER_FRAGMENT) {
593 producer_info.texcoord_usage = (1 << MAX_TEXTURE_COORD_UNITS) - 1;
594 }
595
596 /* Eliminate the inputs uninitialized by the producer. */
597 if (consumer_info.lower_texcoord_array ||
598 consumer_info.color_usage ||
599 consumer_info.has_fog) {
600 replace_varyings_visitor(consumer,
601 &consumer_info,
602 producer_info.texcoord_usage,
603 producer_info.color_usage,
604 producer_info.has_fog);
605 }
606 }