1 /* 2 * Copyright 2013 Marek Olk <maraeo (at) 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 "main/core.h" /* for snprintf and ARRAY_SIZE */ 50 #include "ir.h" 51 #include "ir_rvalue_visitor.h" 52 #include "ir_optimization.h" 53 #include "ir_print_visitor.h" 54 #include "compiler/glsl_types.h" 55 #include "link_varyings.h" 56 57 namespace { 58 59 /** 60 * This obtains detailed information about built-in varyings from shader code. 61 */ 62 class varying_info_visitor : public ir_hierarchical_visitor { 63 public: 64 /* "mode" can be either ir_var_shader_in or ir_var_shader_out */ 65 varying_info_visitor(ir_variable_mode mode, bool find_frag_outputs = false) 66 : lower_texcoord_array(true), 67 texcoord_array(NULL), 68 texcoord_usage(0), 69 find_frag_outputs(find_frag_outputs), 70 lower_fragdata_array(true), 71 fragdata_array(NULL), 72 fragdata_usage(0), 73 color_usage(0), 74 tfeedback_color_usage(0), 75 fog(NULL), 76 has_fog(false), 77 tfeedback_has_fog(false), 78 mode(mode) 79 { 80 memset(color, 0, sizeof(color)); 81 memset(backcolor, 0, sizeof(backcolor)); 82 } 83 84 virtual ir_visitor_status visit_enter(ir_dereference_array *ir) 85 { 86 ir_variable *var = ir->variable_referenced(); 87 88 if (!var || var->data.mode != this->mode || !var->type->is_array() || 89 !is_gl_identifier(var->name)) 90 return visit_continue; 91 92 /* Only match gl_FragData[], not gl_SecondaryFragDataEXT[] or 93 * gl_LastFragData[]. 94 */ 95 if (this->find_frag_outputs && strcmp(var->name, "gl_FragData") == 0) { 96 this->fragdata_array = var; 97 98 ir_constant *index = ir->array_index->as_constant(); 99 if (index == NULL) { 100 /* This is variable indexing. */ 101 this->fragdata_usage |= (1 << var->type->array_size()) - 1; 102 this->lower_fragdata_array = false; 103 } 104 else { 105 this->fragdata_usage |= 1 << index->get_uint_component(0); 106 /* Don't lower fragdata array if the output variable 107 * is not a float variable (or float vector) because it will 108 * generate wrong register assignments because of different 109 * data types. 110 */ 111 if (var->type->gl_type != GL_FLOAT && 112 var->type->gl_type != GL_FLOAT_VEC2 && 113 var->type->gl_type != GL_FLOAT_VEC3 && 114 var->type->gl_type != GL_FLOAT_VEC4) 115 this->lower_fragdata_array = false; 116 } 117 118 /* Don't visit the leaves of ir_dereference_array. */ 119 return visit_continue_with_parent; 120 } 121 122 if (!this->find_frag_outputs && var->data.location == VARYING_SLOT_TEX0) { 123 this->texcoord_array = var; 124 125 ir_constant *index = ir->array_index->as_constant(); 126 if (index == NULL) { 127 /* There is variable indexing, we can't lower the texcoord array. 128 */ 129 this->texcoord_usage |= (1 << var->type->array_size()) - 1; 130 this->lower_texcoord_array = false; 131 } 132 else { 133 this->texcoord_usage |= 1 << index->get_uint_component(0); 134 } 135 136 /* Don't visit the leaves of ir_dereference_array. */ 137 return visit_continue_with_parent; 138 } 139 140 return visit_continue; 141 } 142 143 virtual ir_visitor_status visit(ir_dereference_variable *ir) 144 { 145 ir_variable *var = ir->variable_referenced(); 146 147 if (var->data.mode != this->mode || !var->type->is_array()) 148 return visit_continue; 149 150 if (this->find_frag_outputs && var->data.location == FRAG_RESULT_DATA0 && 151 var->data.index == 0) { 152 /* This is a whole array dereference. */ 153 this->fragdata_usage |= (1 << var->type->array_size()) - 1; 154 this->lower_fragdata_array = false; 155 return visit_continue; 156 } 157 158 if (!this->find_frag_outputs && var->data.location == VARYING_SLOT_TEX0) { 159 /* This is a whole array dereference like "gl_TexCoord = x;", 160 * there's probably no point in lowering that. 161 */ 162 this->texcoord_usage |= (1 << var->type->array_size()) - 1; 163 this->lower_texcoord_array = false; 164 } 165 return visit_continue; 166 } 167 168 virtual ir_visitor_status visit(ir_variable *var) 169 { 170 if (var->data.mode != this->mode) 171 return visit_continue; 172 173 /* Nothing to do here for fragment outputs. */ 174 if (this->find_frag_outputs) 175 return visit_continue; 176 177 /* Handle colors and fog. */ 178 switch (var->data.location) { 179 case VARYING_SLOT_COL0: 180 this->color[0] = var; 181 this->color_usage |= 1; 182 break; 183 case VARYING_SLOT_COL1: 184 this->color[1] = var; 185 this->color_usage |= 2; 186 break; 187 case VARYING_SLOT_BFC0: 188 this->backcolor[0] = var; 189 this->color_usage |= 1; 190 break; 191 case VARYING_SLOT_BFC1: 192 this->backcolor[1] = var; 193 this->color_usage |= 2; 194 break; 195 case VARYING_SLOT_FOGC: 196 this->fog = var; 197 this->has_fog = true; 198 break; 199 } 200 201 return visit_continue; 202 } 203 204 void get(exec_list *ir, 205 unsigned num_tfeedback_decls, 206 tfeedback_decl *tfeedback_decls) 207 { 208 /* Handle the transform feedback varyings. */ 209 for (unsigned i = 0; i < num_tfeedback_decls; i++) { 210 if (!tfeedback_decls[i].is_varying()) 211 continue; 212 213 unsigned location = tfeedback_decls[i].get_location(); 214 215 switch (location) { 216 case VARYING_SLOT_COL0: 217 case VARYING_SLOT_BFC0: 218 this->tfeedback_color_usage |= 1; 219 break; 220 case VARYING_SLOT_COL1: 221 case VARYING_SLOT_BFC1: 222 this->tfeedback_color_usage |= 2; 223 break; 224 case VARYING_SLOT_FOGC: 225 this->tfeedback_has_fog = true; 226 break; 227 default: 228 if (location >= VARYING_SLOT_TEX0 && 229 location <= VARYING_SLOT_TEX7) { 230 this->lower_texcoord_array = false; 231 } 232 } 233 } 234 235 /* Process the shader. */ 236 visit_list_elements(this, ir); 237 238 if (!this->texcoord_array) { 239 this->lower_texcoord_array = false; 240 } 241 if (!this->fragdata_array) { 242 this->lower_fragdata_array = false; 243 } 244 } 245 246 bool lower_texcoord_array; 247 ir_variable *texcoord_array; 248 unsigned texcoord_usage; /* bitmask */ 249 250 bool find_frag_outputs; /* false if it's looking for varyings */ 251 bool lower_fragdata_array; 252 ir_variable *fragdata_array; 253 unsigned fragdata_usage; /* bitmask */ 254 255 ir_variable *color[2]; 256 ir_variable *backcolor[2]; 257 unsigned color_usage; /* bitmask */ 258 unsigned tfeedback_color_usage; /* bitmask */ 259 260 ir_variable *fog; 261 bool has_fog; 262 bool tfeedback_has_fog; 263 264 ir_variable_mode mode; 265 }; 266 267 268 /** 269 * This replaces unused varyings with temporary variables. 270 * 271 * If "ir" is the producer, the "external" usage should come from 272 * the consumer. It also works the other way around. If either one is 273 * missing, set the "external" usage to a full mask. 274 */ 275 class replace_varyings_visitor : public ir_rvalue_visitor { 276 public: 277 replace_varyings_visitor(struct gl_linked_shader *sha, 278 const varying_info_visitor *info, 279 unsigned external_texcoord_usage, 280 unsigned external_color_usage, 281 bool external_has_fog) 282 : shader(sha), info(info), new_fog(NULL) 283 { 284 void *const ctx = shader->ir; 285 286 memset(this->new_fragdata, 0, sizeof(this->new_fragdata)); 287 memset(this->new_texcoord, 0, sizeof(this->new_texcoord)); 288 memset(this->new_color, 0, sizeof(this->new_color)); 289 memset(this->new_backcolor, 0, sizeof(this->new_backcolor)); 290 291 const char *mode_str = 292 info->mode == ir_var_shader_in ? "in" : "out"; 293 294 /* Handle texcoord outputs. 295 * 296 * We're going to break down the gl_TexCoord array into separate 297 * variables. First, add declarations of the new variables all 298 * occurrences of gl_TexCoord will be replaced with. 299 */ 300 if (info->lower_texcoord_array) { 301 prepare_array(shader->ir, this->new_texcoord, 302 ARRAY_SIZE(this->new_texcoord), 303 VARYING_SLOT_TEX0, "TexCoord", mode_str, 304 info->texcoord_usage, external_texcoord_usage); 305 } 306 307 /* Handle gl_FragData in the same way like gl_TexCoord. */ 308 if (info->lower_fragdata_array) { 309 prepare_array(shader->ir, this->new_fragdata, 310 ARRAY_SIZE(this->new_fragdata), 311 FRAG_RESULT_DATA0, "FragData", mode_str, 312 info->fragdata_usage, (1 << MAX_DRAW_BUFFERS) - 1); 313 } 314 315 /* Create dummy variables which will replace set-but-unused color and 316 * fog outputs. 317 */ 318 external_color_usage |= info->tfeedback_color_usage; 319 320 for (int i = 0; i < 2; i++) { 321 char name[32]; 322 323 if (!(external_color_usage & (1 << i))) { 324 if (info->color[i]) { 325 snprintf(name, 32, "gl_%s_FrontColor%i_dummy", mode_str, i); 326 this->new_color[i] = 327 new (ctx) ir_variable(glsl_type::vec4_type, name, 328 ir_var_temporary); 329 } 330 331 if (info->backcolor[i]) { 332 snprintf(name, 32, "gl_%s_BackColor%i_dummy", mode_str, i); 333 this->new_backcolor[i] = 334 new (ctx) ir_variable(glsl_type::vec4_type, name, 335 ir_var_temporary); 336 } 337 } 338 } 339 340 if (!external_has_fog && !info->tfeedback_has_fog && 341 info->fog) { 342 char name[32]; 343 344 snprintf(name, 32, "gl_%s_FogFragCoord_dummy", mode_str); 345 this->new_fog = new (ctx) ir_variable(glsl_type::float_type, name, 346 ir_var_temporary); 347 } 348 349 /* Now do the replacing. */ 350 visit_list_elements(this, shader->ir); 351 } 352 353 void prepare_array(exec_list *ir, 354 ir_variable **new_var, 355 int max_elements, unsigned start_location, 356 const char *var_name, const char *mode_str, 357 unsigned usage, unsigned external_usage) 358 { 359 void *const ctx = ir; 360 361 for (int i = max_elements-1; i >= 0; i--) { 362 if (usage & (1 << i)) { 363 char name[32]; 364 365 if (!(external_usage & (1 << i))) { 366 /* This varying is unused in the next stage. Declare 367 * a temporary instead of an output. */ 368 snprintf(name, 32, "gl_%s_%s%i_dummy", mode_str, var_name, i); 369 new_var[i] = 370 new (ctx) ir_variable(glsl_type::vec4_type, name, 371 ir_var_temporary); 372 } 373 else { 374 snprintf(name, 32, "gl_%s_%s%i", mode_str, var_name, i); 375 new_var[i] = 376 new(ctx) ir_variable(glsl_type::vec4_type, name, 377 this->info->mode); 378 new_var[i]->data.location = start_location + i; 379 new_var[i]->data.explicit_location = true; 380 new_var[i]->data.explicit_index = 0; 381 } 382 383 ir->get_head_raw()->insert_before(new_var[i]); 384 } 385 } 386 } 387 388 virtual ir_visitor_status visit(ir_variable *var) 389 { 390 /* Remove the gl_TexCoord array. */ 391 if (this->info->lower_texcoord_array && 392 var == this->info->texcoord_array) { 393 var->remove(); 394 } 395 396 /* Remove the gl_FragData array. */ 397 if (this->info->lower_fragdata_array && 398 var == this->info->fragdata_array) { 399 400 /* Clone variable for program resource list before it is removed. */ 401 if (!shader->fragdata_arrays) 402 shader->fragdata_arrays = new (shader) exec_list; 403 404 shader->fragdata_arrays->push_tail(var->clone(shader, NULL)); 405 406 var->remove(); 407 } 408 409 /* Replace set-but-unused color and fog outputs with dummy variables. */ 410 for (int i = 0; i < 2; i++) { 411 if (var == this->info->color[i] && this->new_color[i]) { 412 var->replace_with(this->new_color[i]); 413 } 414 if (var == this->info->backcolor[i] && 415 this->new_backcolor[i]) { 416 var->replace_with(this->new_backcolor[i]); 417 } 418 } 419 420 if (var == this->info->fog && this->new_fog) { 421 var->replace_with(this->new_fog); 422 } 423 424 return visit_continue; 425 } 426 427 virtual void handle_rvalue(ir_rvalue **rvalue) 428 { 429 if (!*rvalue) 430 return; 431 432 void *ctx = ralloc_parent(*rvalue); 433 434 /* Replace an array dereference gl_TexCoord[i] with a single 435 * variable dereference representing gl_TexCoord[i]. 436 */ 437 if (this->info->lower_texcoord_array) { 438 /* gl_TexCoord[i] occurrence */ 439 ir_dereference_array *const da = (*rvalue)->as_dereference_array(); 440 441 if (da && da->variable_referenced() == 442 this->info->texcoord_array) { 443 unsigned i = da->array_index->as_constant()->get_uint_component(0); 444 445 *rvalue = new(ctx) ir_dereference_variable(this->new_texcoord[i]); 446 return; 447 } 448 } 449 450 /* Same for gl_FragData. */ 451 if (this->info->lower_fragdata_array) { 452 /* gl_FragData[i] occurrence */ 453 ir_dereference_array *const da = (*rvalue)->as_dereference_array(); 454 455 if (da && da->variable_referenced() == this->info->fragdata_array) { 456 unsigned i = da->array_index->as_constant()->get_uint_component(0); 457 458 *rvalue = new(ctx) ir_dereference_variable(this->new_fragdata[i]); 459 return; 460 } 461 } 462 463 /* Replace set-but-unused color and fog outputs with dummy variables. */ 464 ir_dereference_variable *const dv = (*rvalue)->as_dereference_variable(); 465 if (!dv) 466 return; 467 468 ir_variable *var = dv->variable_referenced(); 469 470 for (int i = 0; i < 2; i++) { 471 if (var == this->info->color[i] && this->new_color[i]) { 472 *rvalue = new(ctx) ir_dereference_variable(this->new_color[i]); 473 return; 474 } 475 if (var == this->info->backcolor[i] && 476 this->new_backcolor[i]) { 477 *rvalue = new(ctx) ir_dereference_variable(this->new_backcolor[i]); 478 return; 479 } 480 } 481 482 if (var == this->info->fog && this->new_fog) { 483 *rvalue = new(ctx) ir_dereference_variable(this->new_fog); 484 } 485 } 486 487 virtual ir_visitor_status visit_leave(ir_assignment *ir) 488 { 489 handle_rvalue(&ir->rhs); 490 handle_rvalue(&ir->condition); 491 492 /* We have to use set_lhs when changing the LHS of an assignment. */ 493 ir_rvalue *lhs = ir->lhs; 494 495 handle_rvalue(&lhs); 496 if (lhs != ir->lhs) { 497 ir->set_lhs(lhs); 498 } 499 500 return visit_continue; 501 } 502 503 private: 504 struct gl_linked_shader *shader; 505 const varying_info_visitor *info; 506 ir_variable *new_fragdata[MAX_DRAW_BUFFERS]; 507 ir_variable *new_texcoord[MAX_TEXTURE_COORD_UNITS]; 508 ir_variable *new_color[2]; 509 ir_variable *new_backcolor[2]; 510 ir_variable *new_fog; 511 }; 512 513 } /* anonymous namespace */ 514 515 static void 516 lower_texcoord_array(struct gl_linked_shader *shader, const varying_info_visitor *info) 517 { 518 replace_varyings_visitor(shader, info, 519 (1 << MAX_TEXTURE_COORD_UNITS) - 1, 520 1 | 2, true); 521 } 522 523 static void 524 lower_fragdata_array(struct gl_linked_shader *shader) 525 { 526 varying_info_visitor info(ir_var_shader_out, true); 527 info.get(shader->ir, 0, NULL); 528 529 replace_varyings_visitor(shader, &info, 0, 0, 0); 530 } 531 532 533 void 534 do_dead_builtin_varyings(struct gl_context *ctx, 535 gl_linked_shader *producer, 536 gl_linked_shader *consumer, 537 unsigned num_tfeedback_decls, 538 tfeedback_decl *tfeedback_decls) 539 { 540 /* Lower the gl_FragData array to separate variables. */ 541 if (consumer && consumer->Stage == MESA_SHADER_FRAGMENT) { 542 lower_fragdata_array(consumer); 543 } 544 545 /* Lowering of built-in varyings has no effect with the core context and 546 * GLES2, because they are not available there. 547 */ 548 if (ctx->API == API_OPENGL_CORE || 549 ctx->API == API_OPENGLES2) { 550 return; 551 } 552 553 /* Information about built-in varyings. */ 554 varying_info_visitor producer_info(ir_var_shader_out); 555 varying_info_visitor consumer_info(ir_var_shader_in); 556 557 if (producer) { 558 producer_info.get(producer->ir, num_tfeedback_decls, tfeedback_decls); 559 560 if (!consumer) { 561 /* At least eliminate unused gl_TexCoord elements. */ 562 if (producer_info.lower_texcoord_array) { 563 lower_texcoord_array(producer, &producer_info); 564 } 565 return; 566 } 567 } 568 569 if (consumer) { 570 consumer_info.get(consumer->ir, 0, NULL); 571 572 if (!producer) { 573 /* At least eliminate unused gl_TexCoord elements. */ 574 if (consumer_info.lower_texcoord_array) { 575 lower_texcoord_array(consumer, &consumer_info); 576 } 577 return; 578 } 579 } 580 581 /* Eliminate the outputs unused by the consumer. */ 582 if (producer_info.lower_texcoord_array || 583 producer_info.color_usage || 584 producer_info.has_fog) { 585 replace_varyings_visitor(producer, 586 &producer_info, 587 consumer_info.texcoord_usage, 588 consumer_info.color_usage, 589 consumer_info.has_fog); 590 } 591 592 /* The gl_TexCoord fragment shader inputs can be initialized 593 * by GL_COORD_REPLACE, so we can't eliminate them. 594 * 595 * This doesn't prevent elimination of the gl_TexCoord elements which 596 * are not read by the fragment shader. We want to eliminate those anyway. 597 */ 598 if (consumer->Stage == MESA_SHADER_FRAGMENT) { 599 producer_info.texcoord_usage = (1 << MAX_TEXTURE_COORD_UNITS) - 1; 600 } 601 602 /* Eliminate the inputs uninitialized by the producer. */ 603 if (consumer_info.lower_texcoord_array || 604 consumer_info.color_usage || 605 consumer_info.has_fog) { 606 replace_varyings_visitor(consumer, 607 &consumer_info, 608 producer_info.texcoord_usage, 609 producer_info.color_usage, 610 producer_info.has_fog); 611 } 612 } 613