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 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.
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.
46 * The same is done for the gl_FragData fragment shader output.
50 #include "ir_rvalue_visitor.h"
51 #include "ir_optimization.h"
52 #include "ir_print_visitor.h"
53 #include "compiler/glsl_types.h"
54 #include "link_varyings.h"
55 #include "main/mtypes.h"
56 #include "util/u_string.h"
61 * This obtains detailed information about built-in varyings from shader code.
63 class varying_info_visitor
: public ir_hierarchical_visitor
{
65 /* "mode" can be either ir_var_shader_in or ir_var_shader_out */
66 varying_info_visitor(ir_variable_mode mode
, bool find_frag_outputs
= false)
67 : lower_texcoord_array(true),
70 find_frag_outputs(find_frag_outputs
),
71 lower_fragdata_array(true),
75 tfeedback_color_usage(0),
78 tfeedback_has_fog(false),
81 memset(color
, 0, sizeof(color
));
82 memset(backcolor
, 0, sizeof(backcolor
));
85 virtual ir_visitor_status
visit_enter(ir_dereference_array
*ir
)
87 ir_variable
*var
= ir
->variable_referenced();
89 if (!var
|| var
->data
.mode
!= this->mode
|| !var
->type
->is_array() ||
90 !is_gl_identifier(var
->name
))
91 return visit_continue
;
93 /* Only match gl_FragData[], not gl_SecondaryFragDataEXT[] or
96 if (this->find_frag_outputs
&& strcmp(var
->name
, "gl_FragData") == 0) {
97 this->fragdata_array
= var
;
99 ir_constant
*index
= ir
->array_index
->as_constant();
101 /* This is variable indexing. */
102 this->fragdata_usage
|= (1 << var
->type
->array_size()) - 1;
103 this->lower_fragdata_array
= false;
106 this->fragdata_usage
|= 1 << index
->get_uint_component(0);
107 /* Don't lower fragdata array if the output variable
108 * is not a float variable (or float vector) because it will
109 * generate wrong register assignments because of different
112 if (var
->type
->gl_type
!= GL_FLOAT
&&
113 var
->type
->gl_type
!= GL_FLOAT_VEC2
&&
114 var
->type
->gl_type
!= GL_FLOAT_VEC3
&&
115 var
->type
->gl_type
!= GL_FLOAT_VEC4
)
116 this->lower_fragdata_array
= false;
119 /* Don't visit the leaves of ir_dereference_array. */
120 return visit_continue_with_parent
;
123 if (!this->find_frag_outputs
&& var
->data
.location
== VARYING_SLOT_TEX0
) {
124 this->texcoord_array
= var
;
126 ir_constant
*index
= ir
->array_index
->as_constant();
128 /* There is variable indexing, we can't lower the texcoord array.
130 this->texcoord_usage
|= (1 << var
->type
->array_size()) - 1;
131 this->lower_texcoord_array
= false;
134 this->texcoord_usage
|= 1 << index
->get_uint_component(0);
137 /* Don't visit the leaves of ir_dereference_array. */
138 return visit_continue_with_parent
;
141 return visit_continue
;
144 virtual ir_visitor_status
visit(ir_dereference_variable
*ir
)
146 ir_variable
*var
= ir
->variable_referenced();
148 if (var
->data
.mode
!= this->mode
|| !var
->type
->is_array())
149 return visit_continue
;
151 if (this->find_frag_outputs
&& var
->data
.location
== FRAG_RESULT_DATA0
&&
152 var
->data
.index
== 0) {
153 /* This is a whole array dereference. */
154 this->fragdata_usage
|= (1 << var
->type
->array_size()) - 1;
155 this->lower_fragdata_array
= false;
156 return visit_continue
;
159 if (!this->find_frag_outputs
&& var
->data
.location
== VARYING_SLOT_TEX0
) {
160 /* This is a whole array dereference like "gl_TexCoord = x;",
161 * there's probably no point in lowering that.
163 this->texcoord_usage
|= (1 << var
->type
->array_size()) - 1;
164 this->lower_texcoord_array
= false;
166 return visit_continue
;
169 virtual ir_visitor_status
visit(ir_variable
*var
)
171 if (var
->data
.mode
!= this->mode
)
172 return visit_continue
;
174 /* Nothing to do here for fragment outputs. */
175 if (this->find_frag_outputs
)
176 return visit_continue
;
178 /* Handle colors and fog. */
179 switch (var
->data
.location
) {
180 case VARYING_SLOT_COL0
:
181 this->color
[0] = var
;
182 this->color_usage
|= 1;
184 case VARYING_SLOT_COL1
:
185 this->color
[1] = var
;
186 this->color_usage
|= 2;
188 case VARYING_SLOT_BFC0
:
189 this->backcolor
[0] = var
;
190 this->color_usage
|= 1;
192 case VARYING_SLOT_BFC1
:
193 this->backcolor
[1] = var
;
194 this->color_usage
|= 2;
196 case VARYING_SLOT_FOGC
:
198 this->has_fog
= true;
202 return visit_continue
;
205 void get(exec_list
*ir
,
206 unsigned num_tfeedback_decls
,
207 tfeedback_decl
*tfeedback_decls
)
209 /* Handle the transform feedback varyings. */
210 for (unsigned i
= 0; i
< num_tfeedback_decls
; i
++) {
211 if (!tfeedback_decls
[i
].is_varying())
214 unsigned location
= tfeedback_decls
[i
].get_location();
217 case VARYING_SLOT_COL0
:
218 case VARYING_SLOT_BFC0
:
219 this->tfeedback_color_usage
|= 1;
221 case VARYING_SLOT_COL1
:
222 case VARYING_SLOT_BFC1
:
223 this->tfeedback_color_usage
|= 2;
225 case VARYING_SLOT_FOGC
:
226 this->tfeedback_has_fog
= true;
229 if (location
>= VARYING_SLOT_TEX0
&&
230 location
<= VARYING_SLOT_TEX7
) {
231 this->lower_texcoord_array
= false;
236 /* Process the shader. */
237 visit_list_elements(this, ir
);
239 if (!this->texcoord_array
) {
240 this->lower_texcoord_array
= false;
242 if (!this->fragdata_array
) {
243 this->lower_fragdata_array
= false;
247 bool lower_texcoord_array
;
248 ir_variable
*texcoord_array
;
249 unsigned texcoord_usage
; /* bitmask */
251 bool find_frag_outputs
; /* false if it's looking for varyings */
252 bool lower_fragdata_array
;
253 ir_variable
*fragdata_array
;
254 unsigned fragdata_usage
; /* bitmask */
256 ir_variable
*color
[2];
257 ir_variable
*backcolor
[2];
258 unsigned color_usage
; /* bitmask */
259 unsigned tfeedback_color_usage
; /* bitmask */
263 bool tfeedback_has_fog
;
265 ir_variable_mode mode
;
270 * This replaces unused varyings with temporary variables.
272 * If "ir" is the producer, the "external" usage should come from
273 * the consumer. It also works the other way around. If either one is
274 * missing, set the "external" usage to a full mask.
276 class replace_varyings_visitor
: public ir_rvalue_visitor
{
278 replace_varyings_visitor(struct gl_linked_shader
*sha
,
279 const varying_info_visitor
*info
,
280 unsigned external_texcoord_usage
,
281 unsigned external_color_usage
,
282 bool external_has_fog
)
283 : shader(sha
), info(info
), new_fog(NULL
)
285 void *const ctx
= shader
->ir
;
287 memset(this->new_fragdata
, 0, sizeof(this->new_fragdata
));
288 memset(this->new_texcoord
, 0, sizeof(this->new_texcoord
));
289 memset(this->new_color
, 0, sizeof(this->new_color
));
290 memset(this->new_backcolor
, 0, sizeof(this->new_backcolor
));
292 const char *mode_str
=
293 info
->mode
== ir_var_shader_in
? "in" : "out";
295 /* Handle texcoord outputs.
297 * We're going to break down the gl_TexCoord array into separate
298 * variables. First, add declarations of the new variables all
299 * occurrences of gl_TexCoord will be replaced with.
301 if (info
->lower_texcoord_array
) {
302 prepare_array(shader
->ir
, this->new_texcoord
,
303 ARRAY_SIZE(this->new_texcoord
),
304 VARYING_SLOT_TEX0
, "TexCoord", mode_str
,
305 info
->texcoord_usage
, external_texcoord_usage
);
308 /* Handle gl_FragData in the same way like gl_TexCoord. */
309 if (info
->lower_fragdata_array
) {
310 prepare_array(shader
->ir
, this->new_fragdata
,
311 ARRAY_SIZE(this->new_fragdata
),
312 FRAG_RESULT_DATA0
, "FragData", mode_str
,
313 info
->fragdata_usage
, (1 << MAX_DRAW_BUFFERS
) - 1);
316 /* Create dummy variables which will replace set-but-unused color and
319 external_color_usage
|= info
->tfeedback_color_usage
;
321 for (int i
= 0; i
< 2; i
++) {
324 if (!(external_color_usage
& (1 << i
))) {
325 if (info
->color
[i
]) {
326 snprintf(name
, 32, "gl_%s_FrontColor%i_dummy", mode_str
, i
);
328 new (ctx
) ir_variable(glsl_type::vec4_type
, name
,
332 if (info
->backcolor
[i
]) {
333 snprintf(name
, 32, "gl_%s_BackColor%i_dummy", mode_str
, i
);
334 this->new_backcolor
[i
] =
335 new (ctx
) ir_variable(glsl_type::vec4_type
, name
,
341 if (!external_has_fog
&& !info
->tfeedback_has_fog
&&
345 snprintf(name
, 32, "gl_%s_FogFragCoord_dummy", mode_str
);
346 this->new_fog
= new (ctx
) ir_variable(glsl_type::float_type
, name
,
350 /* Now do the replacing. */
351 visit_list_elements(this, shader
->ir
);
354 void prepare_array(exec_list
*ir
,
355 ir_variable
**new_var
,
356 int max_elements
, unsigned start_location
,
357 const char *var_name
, const char *mode_str
,
358 unsigned usage
, unsigned external_usage
)
360 void *const ctx
= ir
;
362 for (int i
= max_elements
-1; i
>= 0; i
--) {
363 if (usage
& (1 << i
)) {
366 if (!(external_usage
& (1 << i
))) {
367 /* This varying is unused in the next stage. Declare
368 * a temporary instead of an output. */
369 snprintf(name
, 32, "gl_%s_%s%i_dummy", mode_str
, var_name
, i
);
371 new (ctx
) ir_variable(glsl_type::vec4_type
, name
,
375 snprintf(name
, 32, "gl_%s_%s%i", mode_str
, var_name
, i
);
377 new(ctx
) ir_variable(glsl_type::vec4_type
, name
,
379 new_var
[i
]->data
.location
= start_location
+ i
;
380 new_var
[i
]->data
.explicit_location
= true;
381 new_var
[i
]->data
.explicit_index
= 0;
384 ir
->get_head_raw()->insert_before(new_var
[i
]);
389 virtual ir_visitor_status
visit(ir_variable
*var
)
391 /* Remove the gl_TexCoord array. */
392 if (this->info
->lower_texcoord_array
&&
393 var
== this->info
->texcoord_array
) {
397 /* Remove the gl_FragData array. */
398 if (this->info
->lower_fragdata_array
&&
399 var
== this->info
->fragdata_array
) {
401 /* Clone variable for program resource list before it is removed. */
402 if (!shader
->fragdata_arrays
)
403 shader
->fragdata_arrays
= new (shader
) exec_list
;
405 shader
->fragdata_arrays
->push_tail(var
->clone(shader
, NULL
));
410 /* Replace set-but-unused color and fog outputs with dummy variables. */
411 for (int i
= 0; i
< 2; i
++) {
412 if (var
== this->info
->color
[i
] && this->new_color
[i
]) {
413 var
->replace_with(this->new_color
[i
]);
415 if (var
== this->info
->backcolor
[i
] &&
416 this->new_backcolor
[i
]) {
417 var
->replace_with(this->new_backcolor
[i
]);
421 if (var
== this->info
->fog
&& this->new_fog
) {
422 var
->replace_with(this->new_fog
);
425 return visit_continue
;
428 virtual void handle_rvalue(ir_rvalue
**rvalue
)
433 void *ctx
= ralloc_parent(*rvalue
);
435 /* Replace an array dereference gl_TexCoord[i] with a single
436 * variable dereference representing gl_TexCoord[i].
438 if (this->info
->lower_texcoord_array
) {
439 /* gl_TexCoord[i] occurrence */
440 ir_dereference_array
*const da
= (*rvalue
)->as_dereference_array();
442 if (da
&& da
->variable_referenced() ==
443 this->info
->texcoord_array
) {
444 unsigned i
= da
->array_index
->as_constant()->get_uint_component(0);
446 *rvalue
= new(ctx
) ir_dereference_variable(this->new_texcoord
[i
]);
451 /* Same for gl_FragData. */
452 if (this->info
->lower_fragdata_array
) {
453 /* gl_FragData[i] occurrence */
454 ir_dereference_array
*const da
= (*rvalue
)->as_dereference_array();
456 if (da
&& da
->variable_referenced() == this->info
->fragdata_array
) {
457 unsigned i
= da
->array_index
->as_constant()->get_uint_component(0);
459 *rvalue
= new(ctx
) ir_dereference_variable(this->new_fragdata
[i
]);
464 /* Replace set-but-unused color and fog outputs with dummy variables. */
465 ir_dereference_variable
*const dv
= (*rvalue
)->as_dereference_variable();
469 ir_variable
*var
= dv
->variable_referenced();
471 for (int i
= 0; i
< 2; i
++) {
472 if (var
== this->info
->color
[i
] && this->new_color
[i
]) {
473 *rvalue
= new(ctx
) ir_dereference_variable(this->new_color
[i
]);
476 if (var
== this->info
->backcolor
[i
] &&
477 this->new_backcolor
[i
]) {
478 *rvalue
= new(ctx
) ir_dereference_variable(this->new_backcolor
[i
]);
483 if (var
== this->info
->fog
&& this->new_fog
) {
484 *rvalue
= new(ctx
) ir_dereference_variable(this->new_fog
);
488 virtual ir_visitor_status
visit_leave(ir_assignment
*ir
)
490 handle_rvalue(&ir
->rhs
);
491 handle_rvalue(&ir
->condition
);
493 /* We have to use set_lhs when changing the LHS of an assignment. */
494 ir_rvalue
*lhs
= ir
->lhs
;
497 if (lhs
!= ir
->lhs
) {
501 return visit_continue
;
505 struct gl_linked_shader
*shader
;
506 const varying_info_visitor
*info
;
507 ir_variable
*new_fragdata
[MAX_DRAW_BUFFERS
];
508 ir_variable
*new_texcoord
[MAX_TEXTURE_COORD_UNITS
];
509 ir_variable
*new_color
[2];
510 ir_variable
*new_backcolor
[2];
511 ir_variable
*new_fog
;
514 } /* anonymous namespace */
517 lower_texcoord_array(struct gl_linked_shader
*shader
, const varying_info_visitor
*info
)
519 replace_varyings_visitor(shader
, info
,
520 (1 << MAX_TEXTURE_COORD_UNITS
) - 1,
525 lower_fragdata_array(struct gl_linked_shader
*shader
)
527 varying_info_visitor
info(ir_var_shader_out
, true);
528 info
.get(shader
->ir
, 0, NULL
);
530 replace_varyings_visitor(shader
, &info
, 0, 0, 0);
535 do_dead_builtin_varyings(struct gl_context
*ctx
,
536 gl_linked_shader
*producer
,
537 gl_linked_shader
*consumer
,
538 unsigned num_tfeedback_decls
,
539 tfeedback_decl
*tfeedback_decls
)
541 /* Lower the gl_FragData array to separate variables. */
542 if (consumer
&& consumer
->Stage
== MESA_SHADER_FRAGMENT
&&
543 !ctx
->Const
.ShaderCompilerOptions
[MESA_SHADER_FRAGMENT
].NirOptions
) {
544 lower_fragdata_array(consumer
);
547 /* Lowering of built-in varyings has no effect with the core context and
548 * GLES2, because they are not available there.
550 if (ctx
->API
== API_OPENGL_CORE
||
551 ctx
->API
== API_OPENGLES2
) {
555 /* Information about built-in varyings. */
556 varying_info_visitor
producer_info(ir_var_shader_out
);
557 varying_info_visitor
consumer_info(ir_var_shader_in
);
560 producer_info
.get(producer
->ir
, num_tfeedback_decls
, tfeedback_decls
);
562 if (producer
->Stage
== MESA_SHADER_TESS_CTRL
)
563 producer_info
.lower_texcoord_array
= false;
566 /* At least eliminate unused gl_TexCoord elements. */
567 if (producer_info
.lower_texcoord_array
) {
568 lower_texcoord_array(producer
, &producer_info
);
575 consumer_info
.get(consumer
->ir
, 0, NULL
);
577 if (consumer
->Stage
!= MESA_SHADER_FRAGMENT
)
578 consumer_info
.lower_texcoord_array
= false;
581 /* At least eliminate unused gl_TexCoord elements. */
582 if (consumer_info
.lower_texcoord_array
) {
583 lower_texcoord_array(consumer
, &consumer_info
);
589 /* Eliminate the outputs unused by the consumer. */
590 if (producer_info
.lower_texcoord_array
||
591 producer_info
.color_usage
||
592 producer_info
.has_fog
) {
593 replace_varyings_visitor(producer
,
595 consumer_info
.texcoord_usage
,
596 consumer_info
.color_usage
,
597 consumer_info
.has_fog
);
600 /* The gl_TexCoord fragment shader inputs can be initialized
601 * by GL_COORD_REPLACE, so we can't eliminate them.
603 * This doesn't prevent elimination of the gl_TexCoord elements which
604 * are not read by the fragment shader. We want to eliminate those anyway.
606 if (consumer
->Stage
== MESA_SHADER_FRAGMENT
) {
607 producer_info
.texcoord_usage
= (1 << MAX_TEXTURE_COORD_UNITS
) - 1;
610 /* Eliminate the inputs uninitialized by the producer. */
611 if (consumer_info
.lower_texcoord_array
||
612 consumer_info
.color_usage
||
613 consumer_info
.has_fog
) {
614 replace_varyings_visitor(consumer
,
616 producer_info
.texcoord_usage
,
617 producer_info
.color_usage
,
618 producer_info
.has_fog
);