i965: Split code to set ctx->Const values into a helper function.
[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 occurences 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
47 #include "main/imports.h" /* for snprintf */
48 #include "ir.h"
49 #include "ir_rvalue_visitor.h"
50 #include "ir_optimization.h"
51 #include "ir_print_visitor.h"
52 #include "glsl_types.h"
53 #include "link_varyings.h"
54
55
56 /**
57 * This obtains detailed information about built-in varyings from shader code.
58 */
59 class varying_info_visitor : public ir_hierarchical_visitor {
60 public:
61 /* "mode" can be either ir_var_shader_in or ir_var_shader_out */
62 varying_info_visitor(ir_variable_mode mode)
63 : lower_texcoord_array(true),
64 texcoord_array(NULL),
65 texcoord_usage(0),
66 color_usage(0),
67 tfeedback_color_usage(0),
68 fog(NULL),
69 has_fog(false),
70 tfeedback_has_fog(false),
71 mode(mode)
72 {
73 memset(color, 0, sizeof(color));
74 memset(backcolor, 0, sizeof(backcolor));
75 }
76
77 virtual ir_visitor_status visit_enter(ir_dereference_array *ir)
78 {
79 ir_variable *var = ir->variable_referenced();
80
81 if (var && var->mode == this->mode &&
82 var->location == VARYING_SLOT_TEX0) {
83 this->texcoord_array = var;
84
85 ir_constant *index = ir->array_index->as_constant();
86 if (index == NULL) {
87 /* There is variable indexing, we can't lower the texcoord array.
88 */
89 this->texcoord_usage |= (1 << var->type->array_size()) - 1;
90 this->lower_texcoord_array = false;
91 }
92 else {
93 this->texcoord_usage |= 1 << index->get_uint_component(0);
94 }
95
96 /* Don't visit the leaves of ir_dereference_array. */
97 return visit_continue_with_parent;
98 }
99
100 return visit_continue;
101 }
102
103 virtual ir_visitor_status visit(ir_dereference_variable *ir)
104 {
105 ir_variable *var = ir->variable_referenced();
106
107 if (var->mode == this->mode && var->type->is_array() &&
108 var->location == VARYING_SLOT_TEX0) {
109 /* This is a whole array dereference like "gl_TexCoord = x;",
110 * there's probably no point in lowering that.
111 */
112 this->texcoord_usage |= (1 << var->type->array_size()) - 1;
113 this->lower_texcoord_array = false;
114 }
115 return visit_continue;
116 }
117
118 virtual ir_visitor_status visit(ir_variable *var)
119 {
120 if (var->mode != this->mode)
121 return visit_continue;
122
123 /* Handle colors and fog. */
124 switch (var->location) {
125 case VARYING_SLOT_COL0:
126 this->color[0] = var;
127 this->color_usage |= 1;
128 break;
129 case VARYING_SLOT_COL1:
130 this->color[1] = var;
131 this->color_usage |= 2;
132 break;
133 case VARYING_SLOT_BFC0:
134 this->backcolor[0] = var;
135 this->color_usage |= 1;
136 break;
137 case VARYING_SLOT_BFC1:
138 this->backcolor[1] = var;
139 this->color_usage |= 2;
140 break;
141 case VARYING_SLOT_FOGC:
142 this->fog = var;
143 this->has_fog = true;
144 break;
145 }
146
147 return visit_continue;
148 }
149
150 void get(exec_list *ir,
151 unsigned num_tfeedback_decls,
152 tfeedback_decl *tfeedback_decls)
153 {
154 /* Handle the transform feedback varyings. */
155 for (unsigned i = 0; i < num_tfeedback_decls; i++) {
156 if (!tfeedback_decls[i].is_varying())
157 continue;
158
159 unsigned location = tfeedback_decls[i].get_location();
160
161 switch (location) {
162 case VARYING_SLOT_COL0:
163 case VARYING_SLOT_BFC0:
164 this->tfeedback_color_usage |= 1;
165 break;
166 case VARYING_SLOT_COL1:
167 case VARYING_SLOT_BFC1:
168 this->tfeedback_color_usage |= 2;
169 break;
170 case VARYING_SLOT_FOGC:
171 this->tfeedback_has_fog = true;
172 break;
173 default:
174 if (location >= VARYING_SLOT_TEX0 &&
175 location <= VARYING_SLOT_TEX7) {
176 this->lower_texcoord_array = false;
177 }
178 }
179 }
180
181 /* Process the shader. */
182 visit_list_elements(this, ir);
183
184 if (!this->texcoord_array) {
185 this->lower_texcoord_array = false;
186 }
187 }
188
189 bool lower_texcoord_array;
190 ir_variable *texcoord_array;
191 unsigned texcoord_usage; /* bitmask */
192
193 ir_variable *color[2];
194 ir_variable *backcolor[2];
195 unsigned color_usage; /* bitmask */
196 unsigned tfeedback_color_usage; /* bitmask */
197
198 ir_variable *fog;
199 bool has_fog;
200 bool tfeedback_has_fog;
201
202 ir_variable_mode mode;
203 };
204
205
206 /**
207 * This replaces unused varyings with temporary variables.
208 *
209 * If "ir" is the producer, the "external" usage should come from
210 * the consumer. It also works the other way around. If either one is
211 * missing, set the "external" usage to a full mask.
212 */
213 class replace_varyings_visitor : public ir_rvalue_visitor {
214 public:
215 replace_varyings_visitor(exec_list *ir,
216 const varying_info_visitor *info,
217 unsigned external_texcoord_usage,
218 unsigned external_color_usage,
219 bool external_has_fog)
220 : info(info), new_fog(NULL)
221 {
222 void *const ctx = ir;
223
224 memset(this->new_texcoord, 0, sizeof(this->new_texcoord));
225 memset(this->new_color, 0, sizeof(this->new_color));
226 memset(this->new_backcolor, 0, sizeof(this->new_backcolor));
227
228 const char *mode_str =
229 info->mode == ir_var_shader_in ? "in" : "out";
230
231 /* Handle texcoord outputs.
232 *
233 * We're going to break down the gl_TexCoord array into separate
234 * variables. First, add declarations of the new variables all
235 * occurences of gl_TexCoord will be replaced with.
236 */
237 if (info->lower_texcoord_array) {
238 for (int i = MAX_TEXTURE_COORD_UNITS-1; i >= 0; i--) {
239 if (info->texcoord_usage & (1 << i)) {
240 char name[32];
241
242 if (!(external_texcoord_usage & (1 << i))) {
243 /* This varying is unused in the next stage. Declare
244 * a temporary instead of an output. */
245 snprintf(name, 32, "gl_%s_TexCoord%i_dummy", mode_str, i);
246 this->new_texcoord[i] =
247 new (ctx) ir_variable(glsl_type::vec4_type, name,
248 ir_var_temporary);
249 }
250 else {
251 snprintf(name, 32, "gl_%s_TexCoord%i", mode_str, i);
252 this->new_texcoord[i] =
253 new(ctx) ir_variable(glsl_type::vec4_type, name,
254 info->mode);
255 this->new_texcoord[i]->location = VARYING_SLOT_TEX0 + i;
256 this->new_texcoord[i]->explicit_location = true;
257 this->new_texcoord[i]->explicit_index = 0;
258 }
259
260 ir->head->insert_before(new_texcoord[i]);
261 }
262 }
263 }
264
265 /* Create dummy variables which will replace set-but-unused color and
266 * fog outputs.
267 */
268 external_color_usage |= info->tfeedback_color_usage;
269
270 for (int i = 0; i < 2; i++) {
271 char name[32];
272
273 if (!(external_color_usage & (1 << i))) {
274 if (info->color[i]) {
275 snprintf(name, 32, "gl_%s_FrontColor%i_dummy", mode_str, i);
276 this->new_color[i] =
277 new (ctx) ir_variable(glsl_type::vec4_type, name,
278 ir_var_temporary);
279 }
280
281 if (info->backcolor[i]) {
282 snprintf(name, 32, "gl_%s_BackColor%i_dummy", mode_str, i);
283 this->new_backcolor[i] =
284 new (ctx) ir_variable(glsl_type::vec4_type, name,
285 ir_var_temporary);
286 }
287 }
288 }
289
290 if (!external_has_fog && !info->tfeedback_has_fog &&
291 info->fog) {
292 char name[32];
293
294 snprintf(name, 32, "gl_%s_FogFragCoord_dummy", mode_str);
295 this->new_fog = new (ctx) ir_variable(glsl_type::float_type, name,
296 ir_var_temporary);
297 }
298
299 /* Now do the replacing. */
300 visit_list_elements(this, ir);
301 }
302
303 virtual ir_visitor_status visit(ir_variable *var)
304 {
305 /* Remove the gl_TexCoord array. */
306 if (this->info->lower_texcoord_array &&
307 var == this->info->texcoord_array) {
308 var->remove();
309 }
310
311 /* Replace set-but-unused color and fog outputs with dummy variables. */
312 for (int i = 0; i < 2; i++) {
313 if (var == this->info->color[i] && this->new_color[i]) {
314 var->replace_with(this->new_color[i]);
315 }
316 if (var == this->info->backcolor[i] &&
317 this->new_backcolor[i]) {
318 var->replace_with(this->new_backcolor[i]);
319 }
320 }
321
322 if (var == this->info->fog && this->new_fog) {
323 var->replace_with(this->new_fog);
324 }
325
326 return visit_continue;
327 }
328
329 virtual void handle_rvalue(ir_rvalue **rvalue)
330 {
331 if (!*rvalue)
332 return;
333
334 void *ctx = ralloc_parent(*rvalue);
335
336 /* Replace an array dereference gl_TexCoord[i] with a single
337 * variable dereference representing gl_TexCoord[i].
338 */
339 if (this->info->lower_texcoord_array) {
340 /* gl_TexCoord[i] occurence */
341 ir_dereference_array *const da = (*rvalue)->as_dereference_array();
342
343 if (da && da->variable_referenced() ==
344 this->info->texcoord_array) {
345 unsigned i = da->array_index->as_constant()->get_uint_component(0);
346
347 *rvalue = new(ctx) ir_dereference_variable(this->new_texcoord[i]);
348 return;
349 }
350 }
351
352 /* Replace set-but-unused color and fog outputs with dummy variables. */
353 ir_dereference_variable *const dv = (*rvalue)->as_dereference_variable();
354 if (!dv)
355 return;
356
357 ir_variable *var = dv->variable_referenced();
358
359 for (int i = 0; i < 2; i++) {
360 if (var == this->info->color[i] && this->new_color[i]) {
361 *rvalue = new(ctx) ir_dereference_variable(this->new_color[i]);
362 return;
363 }
364 if (var == this->info->backcolor[i] &&
365 this->new_backcolor[i]) {
366 *rvalue = new(ctx) ir_dereference_variable(this->new_backcolor[i]);
367 return;
368 }
369 }
370
371 if (var == this->info->fog && this->new_fog) {
372 *rvalue = new(ctx) ir_dereference_variable(this->new_fog);
373 }
374 }
375
376 virtual ir_visitor_status visit_leave(ir_assignment *ir)
377 {
378 handle_rvalue(&ir->rhs);
379 handle_rvalue(&ir->condition);
380
381 /* We have to use set_lhs when changing the LHS of an assignment. */
382 ir_rvalue *lhs = ir->lhs;
383
384 handle_rvalue(&lhs);
385 if (lhs != ir->lhs) {
386 ir->set_lhs(lhs);
387 }
388
389 return visit_continue;
390 }
391
392 private:
393 const varying_info_visitor *info;
394 struct ir_variable *new_texcoord[MAX_TEXTURE_COORD_UNITS];
395 struct ir_variable *new_color[2];
396 struct ir_variable *new_backcolor[2];
397 struct ir_variable *new_fog;
398 };
399
400
401 static void
402 lower_texcoord_array(exec_list *ir, const varying_info_visitor *info)
403 {
404 replace_varyings_visitor(ir, info,
405 (1 << MAX_TEXTURE_COORD_UNITS) - 1,
406 1 | 2, true);
407 }
408
409
410 void
411 do_dead_builtin_varyings(struct gl_context *ctx,
412 exec_list *producer, exec_list *consumer,
413 unsigned num_tfeedback_decls,
414 tfeedback_decl *tfeedback_decls)
415 {
416 /* This optimization has no effect with the core context and GLES2, because
417 * the built-in varyings we're eliminating here are not available there.
418 *
419 * EXT_separate_shader_objects doesn't allow this optimization,
420 * because a program object can be bound partially (e.g. only one
421 * stage of a program object can be bound).
422 */
423 if (ctx->API == API_OPENGL_CORE ||
424 ctx->API == API_OPENGLES2 ||
425 ctx->Extensions.EXT_separate_shader_objects) {
426 return;
427 }
428
429 /* Information about built-in varyings. */
430 varying_info_visitor producer_info(ir_var_shader_out);
431 varying_info_visitor consumer_info(ir_var_shader_in);
432
433 if (producer) {
434 producer_info.get(producer, num_tfeedback_decls, tfeedback_decls);
435
436 if (!consumer) {
437 /* At least eliminate unused gl_TexCoord elements. */
438 if (producer_info.lower_texcoord_array) {
439 lower_texcoord_array(producer, &producer_info);
440 }
441 return;
442 }
443 }
444
445 if (consumer) {
446 consumer_info.get(consumer, 0, NULL);
447
448 if (!producer) {
449 /* At least eliminate unused gl_TexCoord elements. */
450 if (consumer_info.lower_texcoord_array) {
451 lower_texcoord_array(consumer, &consumer_info);
452 }
453 return;
454 }
455 }
456
457 /* Eliminate the varyings unused by the other shader. */
458 if (producer_info.lower_texcoord_array ||
459 producer_info.color_usage ||
460 producer_info.has_fog) {
461 replace_varyings_visitor(producer,
462 &producer_info,
463 consumer_info.texcoord_usage,
464 consumer_info.color_usage,
465 consumer_info.has_fog);
466 }
467
468 if (consumer_info.lower_texcoord_array ||
469 consumer_info.color_usage ||
470 consumer_info.has_fog) {
471 replace_varyings_visitor(consumer,
472 &consumer_info,
473 producer_info.texcoord_usage,
474 producer_info.color_usage,
475 producer_info.has_fog);
476 }
477 }