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 "ir.h" 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" 57 58namespace { 59 60/** 61 * This obtains detailed information about built-in varyings from shader code. 62 */ 63class varying_info_visitor : public ir_hierarchical_visitor { 64public: 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), 68 texcoord_array(NULL), 69 texcoord_usage(0), 70 find_frag_outputs(find_frag_outputs), 71 lower_fragdata_array(true), 72 fragdata_array(NULL), 73 fragdata_usage(0), 74 color_usage(0), 75 tfeedback_color_usage(0), 76 fog(NULL), 77 has_fog(false), 78 tfeedback_has_fog(false), 79 mode(mode) 80 { 81 memset(color, 0, sizeof(color)); 82 memset(backcolor, 0, sizeof(backcolor)); 83 } 84 85 virtual ir_visitor_status visit_enter(ir_dereference_array *ir) 86 { 87 ir_variable *var = ir->variable_referenced(); 88 89 if (!var || var->data.mode != this->mode || !var->type->is_array() || 90 !is_gl_identifier(var->name)) 91 return visit_continue; 92 93 /* Only match gl_FragData[], not gl_SecondaryFragDataEXT[] or 94 * gl_LastFragData[]. 95 */ 96 if (this->find_frag_outputs && strcmp(var->name, "gl_FragData") == 0) { 97 this->fragdata_array = var; 98 99 ir_constant *index = ir->array_index->as_constant(); 100 if (index == NULL) { 101 /* This is variable indexing. */ 102 this->fragdata_usage |= (1 << var->type->array_size()) - 1; 103 this->lower_fragdata_array = false; 104 } 105 else { 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 110 * data types. 111 */ 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; 117 } 118 119 /* Don't visit the leaves of ir_dereference_array. */ 120 return visit_continue_with_parent; 121 } 122 123 if (!this->find_frag_outputs && var->data.location == VARYING_SLOT_TEX0) { 124 this->texcoord_array = var; 125 126 ir_constant *index = ir->array_index->as_constant(); 127 if (index == NULL) { 128 /* There is variable indexing, we can't lower the texcoord array. 129 */ 130 this->texcoord_usage |= (1 << var->type->array_size()) - 1; 131 this->lower_texcoord_array = false; 132 } 133 else { 134 this->texcoord_usage |= 1 << index->get_uint_component(0); 135 } 136 137 /* Don't visit the leaves of ir_dereference_array. */ 138 return visit_continue_with_parent; 139 } 140 141 return visit_continue; 142 } 143 144 virtual ir_visitor_status visit(ir_dereference_variable *ir) 145 { 146 ir_variable *var = ir->variable_referenced(); 147 148 if (var->data.mode != this->mode || !var->type->is_array()) 149 return visit_continue; 150 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; 157 } 158 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. 162 */ 163 this->texcoord_usage |= (1 << var->type->array_size()) - 1; 164 this->lower_texcoord_array = false; 165 } 166 return visit_continue; 167 } 168 169 virtual ir_visitor_status visit(ir_variable *var) 170 { 171 if (var->data.mode != this->mode) 172 return visit_continue; 173 174 /* Nothing to do here for fragment outputs. */ 175 if (this->find_frag_outputs) 176 return visit_continue; 177 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; 183 break; 184 case VARYING_SLOT_COL1: 185 this->color[1] = var; 186 this->color_usage |= 2; 187 break; 188 case VARYING_SLOT_BFC0: 189 this->backcolor[0] = var; 190 this->color_usage |= 1; 191 break; 192 case VARYING_SLOT_BFC1: 193 this->backcolor[1] = var; 194 this->color_usage |= 2; 195 break; 196 case VARYING_SLOT_FOGC: 197 this->fog = var; 198 this->has_fog = true; 199 break; 200 } 201 202 return visit_continue; 203 } 204 205 void get(exec_list *ir, 206 unsigned num_tfeedback_decls, 207 tfeedback_decl *tfeedback_decls) 208 { 209 /* Handle the transform feedback varyings. */ 210 for (unsigned i = 0; i < num_tfeedback_decls; i++) { 211 if (!tfeedback_decls[i].is_varying()) 212 continue; 213 214 unsigned location = tfeedback_decls[i].get_location(); 215 216 switch (location) { 217 case VARYING_SLOT_COL0: 218 case VARYING_SLOT_BFC0: 219 this->tfeedback_color_usage |= 1; 220 break; 221 case VARYING_SLOT_COL1: 222 case VARYING_SLOT_BFC1: 223 this->tfeedback_color_usage |= 2; 224 break; 225 case VARYING_SLOT_FOGC: 226 this->tfeedback_has_fog = true; 227 break; 228 default: 229 if (location >= VARYING_SLOT_TEX0 && 230 location <= VARYING_SLOT_TEX7) { 231 this->lower_texcoord_array = false; 232 } 233 } 234 } 235 236 /* Process the shader. */ 237 visit_list_elements(this, ir); 238 239 if (!this->texcoord_array) { 240 this->lower_texcoord_array = false; 241 } 242 if (!this->fragdata_array) { 243 this->lower_fragdata_array = false; 244 } 245 } 246 247 bool lower_texcoord_array; 248 ir_variable *texcoord_array; 249 unsigned texcoord_usage; /* bitmask */ 250 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 */ 255 256 ir_variable *color[2]; 257 ir_variable *backcolor[2]; 258 unsigned color_usage; /* bitmask */ 259 unsigned tfeedback_color_usage; /* bitmask */ 260 261 ir_variable *fog; 262 bool has_fog; 263 bool tfeedback_has_fog; 264 265 ir_variable_mode mode; 266}; 267 268 269/** 270 * This replaces unused varyings with temporary variables. 271 * 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. 275 */ 276class replace_varyings_visitor : public ir_rvalue_visitor { 277public: 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) 284 { 285 void *const ctx = shader->ir; 286 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)); 291 292 const char *mode_str = 293 info->mode == ir_var_shader_in ? "in" : "out"; 294 295 /* Handle texcoord outputs. 296 * 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. 300 */ 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); 306 } 307 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); 314 } 315 316 /* Create dummy variables which will replace set-but-unused color and 317 * fog outputs. 318 */ 319 external_color_usage |= info->tfeedback_color_usage; 320 321 for (int i = 0; i < 2; i++) { 322 char name[32]; 323 324 if (!(external_color_usage & (1 << i))) { 325 if (info->color[i]) { 326 snprintf(name, 32, "gl_%s_FrontColor%i_dummy", mode_str, i); 327 this->new_color[i] = 328 new (ctx) ir_variable(glsl_type::vec4_type, name, 329 ir_var_temporary); 330 } 331 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, 336 ir_var_temporary); 337 } 338 } 339 } 340 341 if (!external_has_fog && !info->tfeedback_has_fog && 342 info->fog) { 343 char name[32]; 344 345 snprintf(name, 32, "gl_%s_FogFragCoord_dummy", mode_str); 346 this->new_fog = new (ctx) ir_variable(glsl_type::float_type, name, 347 ir_var_temporary); 348 } 349 350 /* Now do the replacing. */ 351 visit_list_elements(this, shader->ir); 352 } 353 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) 359 { 360 void *const ctx = ir; 361 362 for (int i = max_elements-1; i >= 0; i--) { 363 if (usage & (1 << i)) { 364 char name[32]; 365 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); 370 new_var[i] = 371 new (ctx) ir_variable(glsl_type::vec4_type, name, 372 ir_var_temporary); 373 } 374 else { 375 snprintf(name, 32, "gl_%s_%s%i", mode_str, var_name, i); 376 new_var[i] = 377 new(ctx) ir_variable(glsl_type::vec4_type, name, 378 this->info->mode); 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; 382 } 383 384 ir->get_head_raw()->insert_before(new_var[i]); 385 } 386 } 387 } 388 389 virtual ir_visitor_status visit(ir_variable *var) 390 { 391 /* Remove the gl_TexCoord array. */ 392 if (this->info->lower_texcoord_array && 393 var == this->info->texcoord_array) { 394 var->remove(); 395 } 396 397 /* Remove the gl_FragData array. */ 398 if (this->info->lower_fragdata_array && 399 var == this->info->fragdata_array) { 400 401 /* Clone variable for program resource list before it is removed. */ 402 if (!shader->fragdata_arrays) 403 shader->fragdata_arrays = new (shader) exec_list; 404 405 shader->fragdata_arrays->push_tail(var->clone(shader, NULL)); 406 407 var->remove(); 408 } 409 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]); 414 } 415 if (var == this->info->backcolor[i] && 416 this->new_backcolor[i]) { 417 var->replace_with(this->new_backcolor[i]); 418 } 419 } 420 421 if (var == this->info->fog && this->new_fog) { 422 var->replace_with(this->new_fog); 423 } 424 425 return visit_continue; 426 } 427 428 virtual void handle_rvalue(ir_rvalue **rvalue) 429 { 430 if (!*rvalue) 431 return; 432 433 void *ctx = ralloc_parent(*rvalue); 434 435 /* Replace an array dereference gl_TexCoord[i] with a single 436 * variable dereference representing gl_TexCoord[i]. 437 */ 438 if (this->info->lower_texcoord_array) { 439 /* gl_TexCoord[i] occurrence */ 440 ir_dereference_array *const da = (*rvalue)->as_dereference_array(); 441 442 if (da && da->variable_referenced() == 443 this->info->texcoord_array) { 444 unsigned i = da->array_index->as_constant()->get_uint_component(0); 445 446 *rvalue = new(ctx) ir_dereference_variable(this->new_texcoord[i]); 447 return; 448 } 449 } 450 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(); 455 456 if (da && da->variable_referenced() == this->info->fragdata_array) { 457 unsigned i = da->array_index->as_constant()->get_uint_component(0); 458 459 *rvalue = new(ctx) ir_dereference_variable(this->new_fragdata[i]); 460 return; 461 } 462 } 463 464 /* Replace set-but-unused color and fog outputs with dummy variables. */ 465 ir_dereference_variable *const dv = (*rvalue)->as_dereference_variable(); 466 if (!dv) 467 return; 468 469 ir_variable *var = dv->variable_referenced(); 470 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]); 474 return; 475 } 476 if (var == this->info->backcolor[i] && 477 this->new_backcolor[i]) { 478 *rvalue = new(ctx) ir_dereference_variable(this->new_backcolor[i]); 479 return; 480 } 481 } 482 483 if (var == this->info->fog && this->new_fog) { 484 *rvalue = new(ctx) ir_dereference_variable(this->new_fog); 485 } 486 } 487 488 virtual ir_visitor_status visit_leave(ir_assignment *ir) 489 { 490 handle_rvalue(&ir->rhs); 491 handle_rvalue(&ir->condition); 492 493 /* We have to use set_lhs when changing the LHS of an assignment. */ 494 ir_rvalue *lhs = ir->lhs; 495 496 handle_rvalue(&lhs); 497 if (lhs != ir->lhs) { 498 ir->set_lhs(lhs); 499 } 500 501 return visit_continue; 502 } 503 504private: 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; 512}; 513 514} /* anonymous namespace */ 515 516static void 517lower_texcoord_array(struct gl_linked_shader *shader, const varying_info_visitor *info) 518{ 519 replace_varyings_visitor(shader, info, 520 (1 << MAX_TEXTURE_COORD_UNITS) - 1, 521 1 | 2, true); 522} 523 524static void 525lower_fragdata_array(struct gl_linked_shader *shader) 526{ 527 varying_info_visitor info(ir_var_shader_out, true); 528 info.get(shader->ir, 0, NULL); 529 530 replace_varyings_visitor(shader, &info, 0, 0, false); 531} 532 533 534void 535do_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) 540{ 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); 545 } 546 547 /* Lowering of built-in varyings has no effect with the core context and 548 * GLES2, because they are not available there. 549 */ 550 if (ctx->API == API_OPENGL_CORE || 551 ctx->API == API_OPENGLES2) { 552 return; 553 } 554 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); 558 559 if (producer) { 560 producer_info.get(producer->ir, num_tfeedback_decls, tfeedback_decls); 561 562 if (producer->Stage == MESA_SHADER_TESS_CTRL) 563 producer_info.lower_texcoord_array = false; 564 565 if (!consumer) { 566 /* At least eliminate unused gl_TexCoord elements. */ 567 if (producer_info.lower_texcoord_array) { 568 lower_texcoord_array(producer, &producer_info); 569 } 570 return; 571 } 572 } 573 574 if (consumer) { 575 consumer_info.get(consumer->ir, 0, NULL); 576 577 if (consumer->Stage != MESA_SHADER_FRAGMENT) 578 consumer_info.lower_texcoord_array = false; 579 580 if (!producer) { 581 /* At least eliminate unused gl_TexCoord elements. */ 582 if (consumer_info.lower_texcoord_array) { 583 lower_texcoord_array(consumer, &consumer_info); 584 } 585 return; 586 } 587 } 588 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, 594 &producer_info, 595 consumer_info.texcoord_usage, 596 consumer_info.color_usage, 597 consumer_info.has_fog); 598 } 599 600 /* The gl_TexCoord fragment shader inputs can be initialized 601 * by GL_COORD_REPLACE, so we can't eliminate them. 602 * 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. 605 */ 606 if (consumer->Stage == MESA_SHADER_FRAGMENT) { 607 producer_info.texcoord_usage = (1 << MAX_TEXTURE_COORD_UNITS) - 1; 608 } 609 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, 615 &consumer_info, 616 producer_info.texcoord_usage, 617 producer_info.color_usage, 618 producer_info.has_fog); 619 } 620} 621