Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
5563 | serge | 1 | /* |
2 | * Copyright © 2013 Marek Olšák |
||
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 | gl_shader *producer, gl_shader *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->ir, 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->ir, &producer_info); |
||
440 | } |
||
441 | return; |
||
442 | } |
||
443 | } |
||
444 | |||
445 | if (consumer) { |
||
446 | consumer_info.get(consumer->ir, 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->ir, &consumer_info); |
||
452 | } |
||
453 | return; |
||
454 | } |
||
455 | } |
||
456 | |||
457 | /* Eliminate the outputs unused by the consumer. */ |
||
458 | if (producer_info.lower_texcoord_array || |
||
459 | producer_info.color_usage || |
||
460 | producer_info.has_fog) { |
||
461 | replace_varyings_visitor(producer->ir, |
||
462 | &producer_info, |
||
463 | consumer_info.texcoord_usage, |
||
464 | consumer_info.color_usage, |
||
465 | consumer_info.has_fog); |
||
466 | } |
||
467 | |||
468 | /* The gl_TexCoord fragment shader inputs can be initialized |
||
469 | * by GL_COORD_REPLACE, so we can't eliminate them. |
||
470 | * |
||
471 | * This doesn't prevent elimination of the gl_TexCoord elements which |
||
472 | * are not read by the fragment shader. We want to eliminate those anyway. |
||
473 | */ |
||
474 | if (consumer->Type == GL_FRAGMENT_SHADER) { |
||
475 | producer_info.texcoord_usage = (1 << MAX_TEXTURE_COORD_UNITS) - 1; |
||
476 | } |
||
477 | |||
478 | /* Eliminate the inputs uninitialized by the producer. */ |
||
479 | if (consumer_info.lower_texcoord_array || |
||
480 | consumer_info.color_usage || |
||
481 | consumer_info.has_fog) { |
||
482 | replace_varyings_visitor(consumer->ir, |
||
483 | &consumer_info, |
||
484 | producer_info.texcoord_usage, |
||
485 | producer_info.color_usage, |
||
486 | producer_info.has_fog); |
||
487 | } |
||
488 | }><>><>>>><>>><>><>=>>><>><>><> |