2 * Copyright © 2013 Marek Olšák <maraeo@gmail.com>
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:
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
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.
25 * \file opt_dead_builtin_varyings.cpp
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).
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.
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.
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.
47 #include "main/imports.h" /* for snprintf */
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"
58 * This obtains detailed information about built-in varyings from shader code.
60 class varying_info_visitor
: public ir_hierarchical_visitor
{
62 /* "mode" can be either ir_var_shader_in or ir_var_shader_out */
63 varying_info_visitor(ir_variable_mode mode
)
64 : lower_texcoord_array(true),
68 tfeedback_color_usage(0),
71 tfeedback_has_fog(false),
74 memset(color
, 0, sizeof(color
));
75 memset(backcolor
, 0, sizeof(backcolor
));
78 virtual ir_visitor_status
visit_enter(ir_dereference_array
*ir
)
80 ir_variable
*var
= ir
->variable_referenced();
82 if (var
&& var
->mode
== this->mode
&&
83 var
->location
== VARYING_SLOT_TEX0
) {
84 this->texcoord_array
= var
;
86 ir_constant
*index
= ir
->array_index
->as_constant();
88 /* There is variable indexing, we can't lower the texcoord array.
90 this->texcoord_usage
|= (1 << var
->type
->array_size()) - 1;
91 this->lower_texcoord_array
= false;
94 this->texcoord_usage
|= 1 << index
->get_uint_component(0);
97 /* Don't visit the leaves of ir_dereference_array. */
98 return visit_continue_with_parent
;
101 return visit_continue
;
104 virtual ir_visitor_status
visit(ir_dereference_variable
*ir
)
106 ir_variable
*var
= ir
->variable_referenced();
108 if (var
->mode
== this->mode
&& var
->type
->is_array() &&
109 var
->location
== VARYING_SLOT_TEX0
) {
110 /* This is a whole array dereference like "gl_TexCoord = x;",
111 * there's probably no point in lowering that.
113 this->texcoord_usage
|= (1 << var
->type
->array_size()) - 1;
114 this->lower_texcoord_array
= false;
116 return visit_continue
;
119 virtual ir_visitor_status
visit(ir_variable
*var
)
121 if (var
->mode
!= this->mode
)
122 return visit_continue
;
124 /* Handle colors and fog. */
125 switch (var
->location
) {
126 case VARYING_SLOT_COL0
:
127 this->color
[0] = var
;
128 this->color_usage
|= 1;
130 case VARYING_SLOT_COL1
:
131 this->color
[1] = var
;
132 this->color_usage
|= 2;
134 case VARYING_SLOT_BFC0
:
135 this->backcolor
[0] = var
;
136 this->color_usage
|= 1;
138 case VARYING_SLOT_BFC1
:
139 this->backcolor
[1] = var
;
140 this->color_usage
|= 2;
142 case VARYING_SLOT_FOGC
:
144 this->has_fog
= true;
148 return visit_continue
;
151 void get(exec_list
*ir
,
152 unsigned num_tfeedback_decls
,
153 tfeedback_decl
*tfeedback_decls
)
155 /* Handle the transform feedback varyings. */
156 for (unsigned i
= 0; i
< num_tfeedback_decls
; i
++) {
157 if (!tfeedback_decls
[i
].is_varying())
160 unsigned location
= tfeedback_decls
[i
].get_location();
163 case VARYING_SLOT_COL0
:
164 case VARYING_SLOT_BFC0
:
165 this->tfeedback_color_usage
|= 1;
167 case VARYING_SLOT_COL1
:
168 case VARYING_SLOT_BFC1
:
169 this->tfeedback_color_usage
|= 2;
171 case VARYING_SLOT_FOGC
:
172 this->tfeedback_has_fog
= true;
175 if (location
>= VARYING_SLOT_TEX0
&&
176 location
<= VARYING_SLOT_TEX7
) {
177 this->lower_texcoord_array
= false;
182 /* Process the shader. */
183 visit_list_elements(this, ir
);
185 if (!this->texcoord_array
) {
186 this->lower_texcoord_array
= false;
190 bool lower_texcoord_array
;
191 ir_variable
*texcoord_array
;
192 unsigned texcoord_usage
; /* bitmask */
194 ir_variable
*color
[2];
195 ir_variable
*backcolor
[2];
196 unsigned color_usage
; /* bitmask */
197 unsigned tfeedback_color_usage
; /* bitmask */
201 bool tfeedback_has_fog
;
203 ir_variable_mode mode
;
208 * This replaces unused varyings with temporary variables.
210 * If "ir" is the producer, the "external" usage should come from
211 * the consumer. It also works the other way around. If either one is
212 * missing, set the "external" usage to a full mask.
214 class replace_varyings_visitor
: public ir_rvalue_visitor
{
216 replace_varyings_visitor(exec_list
*ir
,
217 const varying_info_visitor
*info
,
218 unsigned external_texcoord_usage
,
219 unsigned external_color_usage
,
220 bool external_has_fog
)
221 : info(info
), new_fog(NULL
)
223 void *const ctx
= ir
;
225 memset(this->new_texcoord
, 0, sizeof(this->new_texcoord
));
226 memset(this->new_color
, 0, sizeof(this->new_color
));
227 memset(this->new_backcolor
, 0, sizeof(this->new_backcolor
));
229 const char *mode_str
=
230 info
->mode
== ir_var_shader_in
? "in" : "out";
232 /* Handle texcoord outputs.
234 * We're going to break down the gl_TexCoord array into separate
235 * variables. First, add declarations of the new variables all
236 * occurences of gl_TexCoord will be replaced with.
238 if (info
->lower_texcoord_array
) {
239 for (int i
= MAX_TEXTURE_COORD_UNITS
-1; i
>= 0; i
--) {
240 if (info
->texcoord_usage
& (1 << i
)) {
243 if (!(external_texcoord_usage
& (1 << i
))) {
244 /* This varying is unused in the next stage. Declare
245 * a temporary instead of an output. */
246 snprintf(name
, 32, "gl_%s_TexCoord%i_dummy", mode_str
, i
);
247 this->new_texcoord
[i
] =
248 new (ctx
) ir_variable(glsl_type::vec4_type
, name
,
252 snprintf(name
, 32, "gl_%s_TexCoord%i", mode_str
, i
);
253 this->new_texcoord
[i
] =
254 new(ctx
) ir_variable(glsl_type::vec4_type
, name
,
256 this->new_texcoord
[i
]->location
= VARYING_SLOT_TEX0
+ i
;
257 this->new_texcoord
[i
]->explicit_location
= true;
258 this->new_texcoord
[i
]->explicit_index
= 0;
261 ir
->head
->insert_before(new_texcoord
[i
]);
266 /* Create dummy variables which will replace set-but-unused color and
269 external_color_usage
|= info
->tfeedback_color_usage
;
271 for (int i
= 0; i
< 2; i
++) {
274 if (!(external_color_usage
& (1 << i
))) {
275 if (info
->color
[i
]) {
276 snprintf(name
, 32, "gl_%s_FrontColor%i_dummy", mode_str
, i
);
278 new (ctx
) ir_variable(glsl_type::vec4_type
, name
,
282 if (info
->backcolor
[i
]) {
283 snprintf(name
, 32, "gl_%s_BackColor%i_dummy", mode_str
, i
);
284 this->new_backcolor
[i
] =
285 new (ctx
) ir_variable(glsl_type::vec4_type
, name
,
291 if (!external_has_fog
&& !info
->tfeedback_has_fog
&&
295 snprintf(name
, 32, "gl_%s_FogFragCoord_dummy", mode_str
);
296 this->new_fog
= new (ctx
) ir_variable(glsl_type::float_type
, name
,
300 /* Now do the replacing. */
301 visit_list_elements(this, ir
);
304 virtual ir_visitor_status
visit(ir_variable
*var
)
306 /* Remove the gl_TexCoord array. */
307 if (this->info
->lower_texcoord_array
&&
308 var
== this->info
->texcoord_array
) {
312 /* Replace set-but-unused color and fog outputs with dummy variables. */
313 for (int i
= 0; i
< 2; i
++) {
314 if (var
== this->info
->color
[i
] && this->new_color
[i
]) {
315 var
->replace_with(this->new_color
[i
]);
317 if (var
== this->info
->backcolor
[i
] &&
318 this->new_backcolor
[i
]) {
319 var
->replace_with(this->new_backcolor
[i
]);
323 if (var
== this->info
->fog
&& this->new_fog
) {
324 var
->replace_with(this->new_fog
);
327 return visit_continue
;
330 virtual void handle_rvalue(ir_rvalue
**rvalue
)
335 void *ctx
= ralloc_parent(*rvalue
);
337 /* Replace an array dereference gl_TexCoord[i] with a single
338 * variable dereference representing gl_TexCoord[i].
340 if (this->info
->lower_texcoord_array
) {
341 /* gl_TexCoord[i] occurence */
342 ir_dereference_array
*const da
= (*rvalue
)->as_dereference_array();
344 if (da
&& da
->variable_referenced() ==
345 this->info
->texcoord_array
) {
346 unsigned i
= da
->array_index
->as_constant()->get_uint_component(0);
348 *rvalue
= new(ctx
) ir_dereference_variable(this->new_texcoord
[i
]);
353 /* Replace set-but-unused color and fog outputs with dummy variables. */
354 ir_dereference_variable
*const dv
= (*rvalue
)->as_dereference_variable();
358 ir_variable
*var
= dv
->variable_referenced();
360 for (int i
= 0; i
< 2; i
++) {
361 if (var
== this->info
->color
[i
] && this->new_color
[i
]) {
362 *rvalue
= new(ctx
) ir_dereference_variable(this->new_color
[i
]);
365 if (var
== this->info
->backcolor
[i
] &&
366 this->new_backcolor
[i
]) {
367 *rvalue
= new(ctx
) ir_dereference_variable(this->new_backcolor
[i
]);
372 if (var
== this->info
->fog
&& this->new_fog
) {
373 *rvalue
= new(ctx
) ir_dereference_variable(this->new_fog
);
377 virtual ir_visitor_status
visit_leave(ir_assignment
*ir
)
379 handle_rvalue(&ir
->rhs
);
380 handle_rvalue(&ir
->condition
);
382 /* We have to use set_lhs when changing the LHS of an assignment. */
383 ir_rvalue
*lhs
= ir
->lhs
;
386 if (lhs
!= ir
->lhs
) {
390 return visit_continue
;
394 const varying_info_visitor
*info
;
395 ir_variable
*new_texcoord
[MAX_TEXTURE_COORD_UNITS
];
396 ir_variable
*new_color
[2];
397 ir_variable
*new_backcolor
[2];
398 ir_variable
*new_fog
;
401 } /* anonymous namespace */
404 lower_texcoord_array(exec_list
*ir
, const varying_info_visitor
*info
)
406 replace_varyings_visitor(ir
, info
,
407 (1 << MAX_TEXTURE_COORD_UNITS
) - 1,
413 do_dead_builtin_varyings(struct gl_context
*ctx
,
414 gl_shader
*producer
, gl_shader
*consumer
,
415 unsigned num_tfeedback_decls
,
416 tfeedback_decl
*tfeedback_decls
)
418 /* This optimization has no effect with the core context and GLES2, because
419 * the built-in varyings we're eliminating here are not available there.
421 * EXT_separate_shader_objects doesn't allow this optimization,
422 * because a program object can be bound partially (e.g. only one
423 * stage of a program object can be bound).
425 if (ctx
->API
== API_OPENGL_CORE
||
426 ctx
->API
== API_OPENGLES2
||
427 ctx
->Extensions
.EXT_separate_shader_objects
) {
431 /* Information about built-in varyings. */
432 varying_info_visitor
producer_info(ir_var_shader_out
);
433 varying_info_visitor
consumer_info(ir_var_shader_in
);
436 producer_info
.get(producer
->ir
, num_tfeedback_decls
, tfeedback_decls
);
439 /* At least eliminate unused gl_TexCoord elements. */
440 if (producer_info
.lower_texcoord_array
) {
441 lower_texcoord_array(producer
->ir
, &producer_info
);
448 consumer_info
.get(consumer
->ir
, 0, NULL
);
451 /* At least eliminate unused gl_TexCoord elements. */
452 if (consumer_info
.lower_texcoord_array
) {
453 lower_texcoord_array(consumer
->ir
, &consumer_info
);
459 /* Eliminate the outputs unused by the consumer. */
460 if (producer_info
.lower_texcoord_array
||
461 producer_info
.color_usage
||
462 producer_info
.has_fog
) {
463 replace_varyings_visitor(producer
->ir
,
465 consumer_info
.texcoord_usage
,
466 consumer_info
.color_usage
,
467 consumer_info
.has_fog
);
470 /* The gl_TexCoord fragment shader inputs can be initialized
471 * by GL_COORD_REPLACE, so we can't eliminate them.
473 * This doesn't prevent elimination of the gl_TexCoord elements which
474 * are not read by the fragment shader. We want to eliminate those anyway.
476 if (consumer
->Type
== GL_FRAGMENT_SHADER
) {
477 producer_info
.texcoord_usage
= (1 << MAX_TEXTURE_COORD_UNITS
) - 1;
480 /* Eliminate the inputs uninitialized by the producer. */
481 if (consumer_info
.lower_texcoord_array
||
482 consumer_info
.color_usage
||
483 consumer_info
.has_fog
) {
484 replace_varyings_visitor(consumer
->ir
,
486 producer_info
.texcoord_usage
,
487 producer_info
.color_usage
,
488 producer_info
.has_fog
);