1/* 2 * Copyright © 2011 Intel Corporation 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 lower_distance.cpp 26 * 27 * This pass accounts for the difference between the way 28 * gl_ClipDistance is declared in standard GLSL (as an array of 29 * floats), and the way it is frequently implemented in hardware (as 30 * a pair of vec4s, with four clip distances packed into each). 31 * 32 * The declaration of gl_ClipDistance is replaced with a declaration 33 * of gl_ClipDistanceMESA, and any references to gl_ClipDistance are 34 * translated to refer to gl_ClipDistanceMESA with the appropriate 35 * swizzling of array indices. For instance: 36 * 37 * gl_ClipDistance[i] 38 * 39 * is translated into: 40 * 41 * gl_ClipDistanceMESA[i>>2][i&3] 42 * 43 * Since some hardware may not internally represent gl_ClipDistance as a pair 44 * of vec4's, this lowering pass is optional. To enable it, set the 45 * LowerCombinedClipCullDistance flag in gl_shader_compiler_options to true. 46 */ 47 48#include "main/macros.h" 49#include "glsl_symbol_table.h" 50#include "ir_rvalue_visitor.h" 51#include "ir.h" 52#include "program/prog_instruction.h" /* For WRITEMASK_* */ 53#include "main/shader_types.h" 54 55#define GLSL_CLIP_VAR_NAME "gl_ClipDistanceMESA" 56 57namespace { 58 59class lower_distance_visitor : public ir_rvalue_visitor { 60public: 61 explicit lower_distance_visitor(gl_shader_stage shader_stage, 62 const char *in_name, int total_size, 63 int offset) 64 : progress(false), old_distance_out_var(NULL), 65 old_distance_in_var(NULL), new_distance_out_var(NULL), 66 new_distance_in_var(NULL), shader_stage(shader_stage), 67 in_name(in_name), total_size(total_size), offset(offset) 68 { 69 } 70 71 explicit lower_distance_visitor(gl_shader_stage shader_stage, 72 const char *in_name, 73 const lower_distance_visitor *orig, 74 int offset) 75 : progress(false), 76 old_distance_out_var(NULL), 77 old_distance_in_var(NULL), 78 new_distance_out_var(orig->new_distance_out_var), 79 new_distance_in_var(orig->new_distance_in_var), 80 shader_stage(shader_stage), 81 in_name(in_name), 82 total_size(orig->total_size), 83 offset(offset) 84 { 85 } 86 87 virtual ir_visitor_status visit(ir_variable *); 88 void create_indices(ir_rvalue*, ir_rvalue *&, ir_rvalue *&); 89 bool is_distance_vec8(ir_rvalue *ir); 90 ir_rvalue *lower_distance_vec8(ir_rvalue *ir); 91 virtual ir_visitor_status visit_leave(ir_assignment *); 92 void visit_new_assignment(ir_assignment *ir); 93 virtual ir_visitor_status visit_leave(ir_call *); 94 95 virtual void handle_rvalue(ir_rvalue **rvalue); 96 97 void fix_lhs(ir_assignment *); 98 99 bool progress; 100 101 /** 102 * Pointer to the declaration of gl_ClipDistance, if found. 103 * 104 * Note: 105 * 106 * - the in_var is for geometry and both tessellation shader inputs only. 107 * 108 * - since gl_ClipDistance is available in tessellation control, 109 * tessellation evaluation and geometry shaders as both an input 110 * and an output, it's possible for both old_distance_out_var 111 * and old_distance_in_var to be non-null. 112 */ 113 ir_variable *old_distance_out_var; 114 ir_variable *old_distance_in_var; 115 116 /** 117 * Pointer to the newly-created gl_ClipDistanceMESA variable. 118 */ 119 ir_variable *new_distance_out_var; 120 ir_variable *new_distance_in_var; 121 122 /** 123 * Type of shader we are compiling (e.g. MESA_SHADER_VERTEX) 124 */ 125 const gl_shader_stage shader_stage; 126 const char *in_name; 127 int total_size; 128 int offset; 129}; 130 131} /* anonymous namespace */ 132 133/** 134 * Replace any declaration of 'in_name' as an array of floats with a 135 * declaration of gl_ClipDistanceMESA as an array of vec4's. 136 */ 137ir_visitor_status 138lower_distance_visitor::visit(ir_variable *ir) 139{ 140 ir_variable **old_var; 141 ir_variable **new_var; 142 143 if (!ir->name || strcmp(ir->name, in_name) != 0) 144 return visit_continue; 145 assert (ir->type->is_array()); 146 147 if (ir->data.mode == ir_var_shader_out) { 148 if (this->old_distance_out_var) 149 return visit_continue; 150 old_var = &old_distance_out_var; 151 new_var = &new_distance_out_var; 152 } else if (ir->data.mode == ir_var_shader_in) { 153 if (this->old_distance_in_var) 154 return visit_continue; 155 old_var = &old_distance_in_var; 156 new_var = &new_distance_in_var; 157 } else { 158 unreachable("not reached"); 159 } 160 161 this->progress = true; 162 163 *old_var = ir; 164 165 if (!(*new_var)) { 166 unsigned new_size = (total_size + 3) / 4; 167 168 /* Clone the old var so that we inherit all of its properties */ 169 *new_var = ir->clone(ralloc_parent(ir), NULL); 170 (*new_var)->name = ralloc_strdup(*new_var, GLSL_CLIP_VAR_NAME); 171 (*new_var)->data.location = VARYING_SLOT_CLIP_DIST0; 172 173 if (!ir->type->fields.array->is_array()) { 174 /* gl_ClipDistance (used for vertex, tessellation evaluation and 175 * geometry output, and fragment input). 176 */ 177 assert((ir->data.mode == ir_var_shader_in && 178 this->shader_stage == MESA_SHADER_FRAGMENT) || 179 (ir->data.mode == ir_var_shader_out && 180 (this->shader_stage == MESA_SHADER_VERTEX || 181 this->shader_stage == MESA_SHADER_TESS_EVAL || 182 this->shader_stage == MESA_SHADER_GEOMETRY))); 183 184 assert (ir->type->fields.array == glsl_type::float_type); 185 (*new_var)->data.max_array_access = new_size - 1; 186 187 /* And change the properties that we need to change */ 188 (*new_var)->type = glsl_type::get_array_instance(glsl_type::vec4_type, 189 new_size); 190 } else { 191 /* 2D gl_ClipDistance (used for tessellation control, tessellation 192 * evaluation and geometry input, and tessellation control output). 193 */ 194 assert((ir->data.mode == ir_var_shader_in && 195 (this->shader_stage == MESA_SHADER_GEOMETRY || 196 this->shader_stage == MESA_SHADER_TESS_EVAL)) || 197 this->shader_stage == MESA_SHADER_TESS_CTRL); 198 199 assert (ir->type->fields.array->fields.array == glsl_type::float_type); 200 201 /* And change the properties that we need to change */ 202 (*new_var)->type = glsl_type::get_array_instance( 203 glsl_type::get_array_instance(glsl_type::vec4_type, 204 new_size), 205 ir->type->array_size()); 206 } 207 ir->replace_with(*new_var); 208 } else { 209 ir->remove(); 210 } 211 212 return visit_continue; 213} 214 215 216/** 217 * Create the necessary GLSL rvalues to index into gl_ClipDistanceMESA based 218 * on the rvalue previously used to index into gl_ClipDistance. 219 * 220 * \param array_index Selects one of the vec4's in gl_ClipDistanceMESA 221 * \param swizzle_index Selects a component within the vec4 selected by 222 * array_index. 223 */ 224void 225lower_distance_visitor::create_indices(ir_rvalue *old_index, 226 ir_rvalue *&array_index, 227 ir_rvalue *&swizzle_index) 228{ 229 void *ctx = ralloc_parent(old_index); 230 231 /* Make sure old_index is a signed int so that the bitwise "shift" and 232 * "and" operations below type check properly. 233 */ 234 if (old_index->type != glsl_type::int_type) { 235 assert (old_index->type == glsl_type::uint_type); 236 old_index = new(ctx) ir_expression(ir_unop_u2i, old_index); 237 } 238 239 ir_constant *old_index_constant = 240 old_index->constant_expression_value(ctx); 241 if (old_index_constant) { 242 /* gl_ClipDistance is being accessed via a constant index. Don't bother 243 * creating expressions to calculate the lowered indices. Just create 244 * constants. 245 */ 246 int const_val = old_index_constant->get_int_component(0) + offset; 247 array_index = new(ctx) ir_constant(const_val / 4); 248 swizzle_index = new(ctx) ir_constant(const_val % 4); 249 } else { 250 /* Create a variable to hold the value of old_index (so that we 251 * don't compute it twice). 252 */ 253 ir_variable *old_index_var = new(ctx) ir_variable( 254 glsl_type::int_type, "distance_index", ir_var_temporary); 255 this->base_ir->insert_before(old_index_var); 256 this->base_ir->insert_before(new(ctx) ir_assignment( 257 new(ctx) ir_dereference_variable(old_index_var), old_index)); 258 259 /* Create the expression distance_index / 4. Do this as a bit 260 * shift because that's likely to be more efficient. 261 */ 262 array_index = new(ctx) ir_expression( 263 ir_binop_rshift, 264 new(ctx) ir_expression(ir_binop_add, 265 new(ctx) ir_dereference_variable(old_index_var), 266 new(ctx) ir_constant(offset)), 267 new(ctx) ir_constant(2)); 268 269 /* Create the expression distance_index % 4. Do this as a bitwise 270 * AND because that's likely to be more efficient. 271 */ 272 swizzle_index = new(ctx) ir_expression( 273 ir_binop_bit_and, 274 new(ctx) ir_expression(ir_binop_add, 275 new(ctx) ir_dereference_variable(old_index_var), 276 new(ctx) ir_constant(offset)), 277 new(ctx) ir_constant(3)); 278 } 279} 280 281 282/** 283 * Determine whether the given rvalue describes an array of 8 floats that 284 * needs to be lowered to an array of 2 vec4's; that is, determine whether it 285 * matches one of the following patterns: 286 * 287 * - gl_ClipDistance (if gl_ClipDistance is 1D) 288 * - gl_ClipDistance[i] (if gl_ClipDistance is 2D) 289 */ 290bool 291lower_distance_visitor::is_distance_vec8(ir_rvalue *ir) 292{ 293 /* Note that geometry shaders contain gl_ClipDistance both as an input 294 * (which is a 2D array) and an output (which is a 1D array), so it's 295 * possible for both this->old_distance_out_var and 296 * this->old_distance_in_var to be non-NULL in the same shader. 297 */ 298 299 if (!ir->type->is_array()) 300 return false; 301 if (ir->type->fields.array != glsl_type::float_type) 302 return false; 303 304 if (this->old_distance_out_var) { 305 if (ir->variable_referenced() == this->old_distance_out_var) 306 return true; 307 } 308 if (this->old_distance_in_var) { 309 assert(this->shader_stage == MESA_SHADER_TESS_CTRL || 310 this->shader_stage == MESA_SHADER_TESS_EVAL || 311 this->shader_stage == MESA_SHADER_GEOMETRY || 312 this->shader_stage == MESA_SHADER_FRAGMENT); 313 314 if (ir->variable_referenced() == this->old_distance_in_var) 315 return true; 316 } 317 return false; 318} 319 320 321/** 322 * If the given ir satisfies is_distance_vec8(), return new ir 323 * representing its lowered equivalent. That is, map: 324 * 325 * - gl_ClipDistance => gl_ClipDistanceMESA (if gl_ClipDistance is 1D) 326 * - gl_ClipDistance[i] => gl_ClipDistanceMESA[i] (if gl_ClipDistance is 2D) 327 * 328 * Otherwise return NULL. 329 */ 330ir_rvalue * 331lower_distance_visitor::lower_distance_vec8(ir_rvalue *ir) 332{ 333 if (!ir->type->is_array()) 334 return NULL; 335 if (ir->type->fields.array != glsl_type::float_type) 336 return NULL; 337 338 ir_variable **new_var = NULL; 339 if (this->old_distance_out_var) { 340 if (ir->variable_referenced() == this->old_distance_out_var) 341 new_var = &this->new_distance_out_var; 342 } 343 if (this->old_distance_in_var) { 344 if (ir->variable_referenced() == this->old_distance_in_var) 345 new_var = &this->new_distance_in_var; 346 } 347 if (new_var == NULL) 348 return NULL; 349 350 if (ir->as_dereference_variable()) { 351 return new(ralloc_parent(ir)) ir_dereference_variable(*new_var); 352 } else { 353 ir_dereference_array *array_ref = ir->as_dereference_array(); 354 assert(array_ref); 355 assert(array_ref->array->as_dereference_variable()); 356 357 return new(ralloc_parent(ir)) 358 ir_dereference_array(*new_var, array_ref->array_index); 359 } 360} 361 362 363void 364lower_distance_visitor::handle_rvalue(ir_rvalue **rv) 365{ 366 if (*rv == NULL) 367 return; 368 369 ir_dereference_array *const array_deref = (*rv)->as_dereference_array(); 370 if (array_deref == NULL) 371 return; 372 373 /* Replace any expression that indexes one of the floats in gl_ClipDistance 374 * with an expression that indexes into one of the vec4's in 375 * gl_ClipDistanceMESA and accesses the appropriate component. 376 */ 377 ir_rvalue *lowered_vec8 = 378 this->lower_distance_vec8(array_deref->array); 379 if (lowered_vec8 != NULL) { 380 this->progress = true; 381 ir_rvalue *array_index; 382 ir_rvalue *swizzle_index; 383 this->create_indices(array_deref->array_index, array_index, swizzle_index); 384 void *mem_ctx = ralloc_parent(array_deref); 385 386 ir_dereference_array *const new_array_deref = 387 new(mem_ctx) ir_dereference_array(lowered_vec8, array_index); 388 389 ir_expression *const expr = 390 new(mem_ctx) ir_expression(ir_binop_vector_extract, 391 new_array_deref, 392 swizzle_index); 393 394 *rv = expr; 395 } 396} 397 398void 399lower_distance_visitor::fix_lhs(ir_assignment *ir) 400{ 401 if (ir->lhs->ir_type == ir_type_expression) { 402 void *mem_ctx = ralloc_parent(ir); 403 ir_expression *const expr = (ir_expression *) ir->lhs; 404 405 /* The expression must be of the form: 406 * 407 * (vector_extract gl_ClipDistanceMESA[i], j). 408 */ 409 assert(expr->operation == ir_binop_vector_extract); 410 assert(expr->operands[0]->ir_type == ir_type_dereference_array); 411 assert(expr->operands[0]->type == glsl_type::vec4_type); 412 413 ir_dereference *const new_lhs = (ir_dereference *) expr->operands[0]; 414 ir->rhs = new(mem_ctx) ir_expression(ir_triop_vector_insert, 415 glsl_type::vec4_type, 416 new_lhs->clone(mem_ctx, NULL), 417 ir->rhs, 418 expr->operands[1]); 419 ir->set_lhs(new_lhs); 420 ir->write_mask = WRITEMASK_XYZW; 421 } 422} 423 424/** 425 * Replace any assignment having the 1D gl_ClipDistance (undereferenced) as 426 * its LHS or RHS with a sequence of assignments, one for each component of 427 * the array. Each of these assignments is lowered to refer to 428 * gl_ClipDistanceMESA as appropriate. 429 * 430 * We need to do a similar replacement for 2D gl_ClipDistance, however since 431 * it's an input, the only case we need to address is where a 1D slice of it 432 * is the entire RHS of an assignment, e.g.: 433 * 434 * foo = gl_in[i].gl_ClipDistance 435 */ 436ir_visitor_status 437lower_distance_visitor::visit_leave(ir_assignment *ir) 438{ 439 /* First invoke the base class visitor. This causes handle_rvalue() to be 440 * called on ir->rhs and ir->condition. 441 */ 442 ir_rvalue_visitor::visit_leave(ir); 443 444 if (this->is_distance_vec8(ir->lhs) || 445 this->is_distance_vec8(ir->rhs)) { 446 /* LHS or RHS of the assignment is the entire 1D gl_ClipDistance array 447 * (or a 1D slice of a 2D gl_ClipDistance input array). Since we are 448 * reshaping gl_ClipDistance from an array of floats to an array of 449 * vec4's, this isn't going to work as a bulk assignment anymore, so 450 * unroll it to element-by-element assignments and lower each of them. 451 * 452 * Note: to unroll into element-by-element assignments, we need to make 453 * clones of the LHS and RHS. This is safe because expressions and 454 * l-values are side-effect free. 455 */ 456 void *ctx = ralloc_parent(ir); 457 int array_size = ir->lhs->type->array_size(); 458 for (int i = 0; i < array_size; ++i) { 459 ir_dereference_array *new_lhs = new(ctx) ir_dereference_array( 460 ir->lhs->clone(ctx, NULL), new(ctx) ir_constant(i)); 461 ir_dereference_array *new_rhs = new(ctx) ir_dereference_array( 462 ir->rhs->clone(ctx, NULL), new(ctx) ir_constant(i)); 463 this->handle_rvalue((ir_rvalue **) &new_rhs); 464 465 /* Handle the LHS after creating the new assignment. This must 466 * happen in this order because handle_rvalue may replace the old LHS 467 * with an ir_expression of ir_binop_vector_extract. Since this is 468 * not a valide l-value, this will cause an assertion in the 469 * ir_assignment constructor to fail. 470 * 471 * If this occurs, replace the mangled LHS with a dereference of the 472 * vector, and replace the RHS with an ir_triop_vector_insert. 473 */ 474 ir_assignment *const assign = new(ctx) ir_assignment(new_lhs, new_rhs); 475 this->handle_rvalue((ir_rvalue **) &assign->lhs); 476 this->fix_lhs(assign); 477 478 this->base_ir->insert_before(assign); 479 } 480 ir->remove(); 481 482 return visit_continue; 483 } 484 485 /* Handle the LHS as if it were an r-value. Normally 486 * rvalue_visit(ir_assignment *) only visits the RHS, but we need to lower 487 * expressions in the LHS as well. 488 * 489 * This may cause the LHS to get replaced with an ir_expression of 490 * ir_binop_vector_extract. If this occurs, replace it with a dereference 491 * of the vector, and replace the RHS with an ir_triop_vector_insert. 492 */ 493 handle_rvalue((ir_rvalue **)&ir->lhs); 494 this->fix_lhs(ir); 495 496 return rvalue_visit(ir); 497} 498 499 500/** 501 * Set up base_ir properly and call visit_leave() on a newly created 502 * ir_assignment node. This is used in cases where we have to insert an 503 * ir_assignment in a place where we know the hierarchical visitor won't see 504 * it. 505 */ 506void 507lower_distance_visitor::visit_new_assignment(ir_assignment *ir) 508{ 509 ir_instruction *old_base_ir = this->base_ir; 510 this->base_ir = ir; 511 ir->accept(this); 512 this->base_ir = old_base_ir; 513} 514 515 516/** 517 * If a 1D gl_ClipDistance variable appears as an argument in an ir_call 518 * expression, replace it with a temporary variable, and make sure the ir_call 519 * is preceded and/or followed by assignments that copy the contents of the 520 * temporary variable to and/or from gl_ClipDistance. Each of these 521 * assignments is then lowered to refer to gl_ClipDistanceMESA. 522 * 523 * We need to do a similar replacement for 2D gl_ClipDistance, however since 524 * it's an input, the only case we need to address is where a 1D slice of it 525 * is passed as an "in" parameter to an ir_call, e.g.: 526 * 527 * foo(gl_in[i].gl_ClipDistance) 528 */ 529ir_visitor_status 530lower_distance_visitor::visit_leave(ir_call *ir) 531{ 532 void *ctx = ralloc_parent(ir); 533 534 const exec_node *formal_param_node = ir->callee->parameters.get_head_raw(); 535 const exec_node *actual_param_node = ir->actual_parameters.get_head_raw(); 536 while (!actual_param_node->is_tail_sentinel()) { 537 ir_variable *formal_param = (ir_variable *) formal_param_node; 538 ir_rvalue *actual_param = (ir_rvalue *) actual_param_node; 539 540 /* Advance formal_param_node and actual_param_node now so that we can 541 * safely replace actual_param with another node, if necessary, below. 542 */ 543 formal_param_node = formal_param_node->next; 544 actual_param_node = actual_param_node->next; 545 546 if (this->is_distance_vec8(actual_param)) { 547 /* User is trying to pass the whole 1D gl_ClipDistance array (or a 1D 548 * slice of a 2D gl_ClipDistance array) to a function call. Since we 549 * are reshaping gl_ClipDistance from an array of floats to an array 550 * of vec4's, this isn't going to work anymore, so use a temporary 551 * array instead. 552 */ 553 ir_variable *temp_clip_distance = new(ctx) ir_variable( 554 actual_param->type, "temp_clip_distance", ir_var_temporary); 555 this->base_ir->insert_before(temp_clip_distance); 556 actual_param->replace_with( 557 new(ctx) ir_dereference_variable(temp_clip_distance)); 558 if (formal_param->data.mode == ir_var_function_in 559 || formal_param->data.mode == ir_var_function_inout) { 560 /* Copy from gl_ClipDistance to the temporary before the call. 561 * Since we are going to insert this copy before the current 562 * instruction, we need to visit it afterwards to make sure it 563 * gets lowered. 564 */ 565 ir_assignment *new_assignment = new(ctx) ir_assignment( 566 new(ctx) ir_dereference_variable(temp_clip_distance), 567 actual_param->clone(ctx, NULL)); 568 this->base_ir->insert_before(new_assignment); 569 this->visit_new_assignment(new_assignment); 570 } 571 if (formal_param->data.mode == ir_var_function_out 572 || formal_param->data.mode == ir_var_function_inout) { 573 /* Copy from the temporary to gl_ClipDistance after the call. 574 * Since visit_list_elements() has already decided which 575 * instruction it's going to visit next, we need to visit 576 * afterwards to make sure it gets lowered. 577 */ 578 ir_assignment *new_assignment = new(ctx) ir_assignment( 579 actual_param->clone(ctx, NULL), 580 new(ctx) ir_dereference_variable(temp_clip_distance)); 581 this->base_ir->insert_after(new_assignment); 582 this->visit_new_assignment(new_assignment); 583 } 584 } 585 } 586 587 return rvalue_visit(ir); 588} 589 590namespace { 591class lower_distance_visitor_counter : public ir_rvalue_visitor { 592public: 593 explicit lower_distance_visitor_counter(void) 594 : in_clip_size(0), in_cull_size(0), 595 out_clip_size(0), out_cull_size(0) 596 { 597 } 598 599 virtual ir_visitor_status visit(ir_variable *); 600 virtual void handle_rvalue(ir_rvalue **rvalue); 601 602 int in_clip_size; 603 int in_cull_size; 604 int out_clip_size; 605 int out_cull_size; 606}; 607 608} 609/** 610 * Count gl_ClipDistance and gl_CullDistance sizes. 611 */ 612ir_visitor_status 613lower_distance_visitor_counter::visit(ir_variable *ir) 614{ 615 int *clip_size, *cull_size; 616 617 if (!ir->name) 618 return visit_continue; 619 620 if (ir->data.mode == ir_var_shader_out) { 621 clip_size = &out_clip_size; 622 cull_size = &out_cull_size; 623 } else if (ir->data.mode == ir_var_shader_in) { 624 clip_size = &in_clip_size; 625 cull_size = &in_cull_size; 626 } else 627 return visit_continue; 628 629 if (ir->type->is_unsized_array()) 630 return visit_continue; 631 632 if (*clip_size == 0) { 633 if (!strcmp(ir->name, "gl_ClipDistance")) { 634 if (!ir->type->fields.array->is_array()) 635 *clip_size = ir->type->array_size(); 636 else 637 *clip_size = ir->type->fields.array->array_size(); 638 } 639 } 640 641 if (*cull_size == 0) { 642 if (!strcmp(ir->name, "gl_CullDistance")) { 643 if (!ir->type->fields.array->is_array()) 644 *cull_size = ir->type->array_size(); 645 else 646 *cull_size = ir->type->fields.array->array_size(); 647 } 648 } 649 return visit_continue; 650} 651 652void 653lower_distance_visitor_counter::handle_rvalue(ir_rvalue **) 654{ 655 return; 656} 657 658bool 659lower_clip_cull_distance(struct gl_shader_program *prog, 660 struct gl_linked_shader *shader) 661{ 662 int clip_size, cull_size; 663 664 lower_distance_visitor_counter count; 665 visit_list_elements(&count, shader->ir); 666 667 clip_size = MAX2(count.in_clip_size, count.out_clip_size); 668 cull_size = MAX2(count.in_cull_size, count.out_cull_size); 669 670 if (clip_size == 0 && cull_size == 0) 671 return false; 672 673 lower_distance_visitor v(shader->Stage, "gl_ClipDistance", clip_size + cull_size, 0); 674 visit_list_elements(&v, shader->ir); 675 676 lower_distance_visitor v2(shader->Stage, "gl_CullDistance", &v, clip_size); 677 visit_list_elements(&v2, shader->ir); 678 679 if (v2.new_distance_out_var) 680 shader->symbols->add_variable(v2.new_distance_out_var); 681 if (v2.new_distance_in_var) 682 shader->symbols->add_variable(v2.new_distance_in_var); 683 684 return v2.progress; 685} 686