Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
5564 | serge | 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_clip_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 | * LowerClipDistance flag in gl_shader_compiler_options to true. |
||
46 | */ |
||
47 | |||
48 | #include "glsl_symbol_table.h" |
||
49 | #include "ir_rvalue_visitor.h" |
||
50 | #include "ir.h" |
||
51 | #include "program/prog_instruction.h" /* For WRITEMASK_* */ |
||
52 | |||
53 | namespace { |
||
54 | |||
55 | class lower_clip_distance_visitor : public ir_rvalue_visitor { |
||
56 | public: |
||
57 | explicit lower_clip_distance_visitor(gl_shader_stage shader_stage) |
||
58 | : progress(false), old_clip_distance_1d_var(NULL), |
||
59 | old_clip_distance_2d_var(NULL), new_clip_distance_1d_var(NULL), |
||
60 | new_clip_distance_2d_var(NULL), shader_stage(shader_stage) |
||
61 | { |
||
62 | } |
||
63 | |||
64 | virtual ir_visitor_status visit(ir_variable *); |
||
65 | void create_indices(ir_rvalue*, ir_rvalue *&, ir_rvalue *&); |
||
66 | bool is_clip_distance_vec8(ir_rvalue *ir); |
||
67 | ir_rvalue *lower_clip_distance_vec8(ir_rvalue *ir); |
||
68 | virtual ir_visitor_status visit_leave(ir_assignment *); |
||
69 | void visit_new_assignment(ir_assignment *ir); |
||
70 | virtual ir_visitor_status visit_leave(ir_call *); |
||
71 | |||
72 | virtual void handle_rvalue(ir_rvalue **rvalue); |
||
73 | |||
74 | void fix_lhs(ir_assignment *); |
||
75 | |||
76 | bool progress; |
||
77 | |||
78 | /** |
||
79 | * Pointer to the declaration of gl_ClipDistance, if found. |
||
80 | * |
||
81 | * Note: |
||
82 | * |
||
83 | * - the 2d_var is for geometry shader input only. |
||
84 | * |
||
85 | * - since gl_ClipDistance is available in geometry shaders as both an |
||
86 | * input and an output, it's possible for both old_clip_distance_1d_var |
||
87 | * and old_clip_distance_2d_var to be non-null. |
||
88 | */ |
||
89 | ir_variable *old_clip_distance_1d_var; |
||
90 | ir_variable *old_clip_distance_2d_var; |
||
91 | |||
92 | /** |
||
93 | * Pointer to the newly-created gl_ClipDistanceMESA variable. |
||
94 | */ |
||
95 | ir_variable *new_clip_distance_1d_var; |
||
96 | ir_variable *new_clip_distance_2d_var; |
||
97 | |||
98 | /** |
||
99 | * Type of shader we are compiling (e.g. MESA_SHADER_VERTEX) |
||
100 | */ |
||
101 | const gl_shader_stage shader_stage; |
||
102 | }; |
||
103 | |||
104 | } /* anonymous namespace */ |
||
105 | |||
106 | /** |
||
107 | * Replace any declaration of gl_ClipDistance as an array of floats with a |
||
108 | * declaration of gl_ClipDistanceMESA as an array of vec4's. |
||
109 | */ |
||
110 | ir_visitor_status |
||
111 | lower_clip_distance_visitor::visit(ir_variable *ir) |
||
112 | { |
||
113 | if (!ir->name || strcmp(ir->name, "gl_ClipDistance") != 0) |
||
114 | return visit_continue; |
||
115 | assert (ir->type->is_array()); |
||
116 | |||
117 | if (!ir->type->element_type()->is_array()) { |
||
118 | /* 1D gl_ClipDistance (used for vertex and geometry output, and fragment |
||
119 | * input). |
||
120 | */ |
||
121 | if (this->old_clip_distance_1d_var) |
||
122 | return visit_continue; |
||
123 | |||
124 | this->progress = true; |
||
125 | this->old_clip_distance_1d_var = ir; |
||
126 | assert (ir->type->element_type() == glsl_type::float_type); |
||
127 | unsigned new_size = (ir->type->array_size() + 3) / 4; |
||
128 | |||
129 | /* Clone the old var so that we inherit all of its properties */ |
||
130 | this->new_clip_distance_1d_var = ir->clone(ralloc_parent(ir), NULL); |
||
131 | |||
132 | /* And change the properties that we need to change */ |
||
133 | this->new_clip_distance_1d_var->name |
||
134 | = ralloc_strdup(this->new_clip_distance_1d_var, |
||
135 | "gl_ClipDistanceMESA"); |
||
136 | this->new_clip_distance_1d_var->type |
||
137 | = glsl_type::get_array_instance(glsl_type::vec4_type, new_size); |
||
138 | this->new_clip_distance_1d_var->data.max_array_access |
||
139 | = ir->data.max_array_access / 4; |
||
140 | |||
141 | ir->replace_with(this->new_clip_distance_1d_var); |
||
142 | } else { |
||
143 | /* 2D gl_ClipDistance (used for geometry input). */ |
||
144 | assert(ir->data.mode == ir_var_shader_in && |
||
145 | this->shader_stage == MESA_SHADER_GEOMETRY); |
||
146 | if (this->old_clip_distance_2d_var) |
||
147 | return visit_continue; |
||
148 | |||
149 | this->progress = true; |
||
150 | this->old_clip_distance_2d_var = ir; |
||
151 | assert (ir->type->element_type()->element_type() == glsl_type::float_type); |
||
152 | unsigned new_size = (ir->type->element_type()->array_size() + 3) / 4; |
||
153 | |||
154 | /* Clone the old var so that we inherit all of its properties */ |
||
155 | this->new_clip_distance_2d_var = ir->clone(ralloc_parent(ir), NULL); |
||
156 | |||
157 | /* And change the properties that we need to change */ |
||
158 | this->new_clip_distance_2d_var->name |
||
159 | = ralloc_strdup(this->new_clip_distance_2d_var, "gl_ClipDistanceMESA"); |
||
160 | this->new_clip_distance_2d_var->type = glsl_type::get_array_instance( |
||
161 | glsl_type::get_array_instance(glsl_type::vec4_type, |
||
162 | new_size), |
||
163 | ir->type->array_size()); |
||
164 | this->new_clip_distance_2d_var->data.max_array_access |
||
165 | = ir->data.max_array_access / 4; |
||
166 | |||
167 | ir->replace_with(this->new_clip_distance_2d_var); |
||
168 | } |
||
169 | return visit_continue; |
||
170 | } |
||
171 | |||
172 | |||
173 | /** |
||
174 | * Create the necessary GLSL rvalues to index into gl_ClipDistanceMESA based |
||
175 | * on the rvalue previously used to index into gl_ClipDistance. |
||
176 | * |
||
177 | * \param array_index Selects one of the vec4's in gl_ClipDistanceMESA |
||
178 | * \param swizzle_index Selects a component within the vec4 selected by |
||
179 | * array_index. |
||
180 | */ |
||
181 | void |
||
182 | lower_clip_distance_visitor::create_indices(ir_rvalue *old_index, |
||
183 | ir_rvalue *&array_index, |
||
184 | ir_rvalue *&swizzle_index) |
||
185 | { |
||
186 | void *ctx = ralloc_parent(old_index); |
||
187 | |||
188 | /* Make sure old_index is a signed int so that the bitwise "shift" and |
||
189 | * "and" operations below type check properly. |
||
190 | */ |
||
191 | if (old_index->type != glsl_type::int_type) { |
||
192 | assert (old_index->type == glsl_type::uint_type); |
||
193 | old_index = new(ctx) ir_expression(ir_unop_u2i, old_index); |
||
194 | } |
||
195 | |||
196 | ir_constant *old_index_constant = old_index->constant_expression_value(); |
||
197 | if (old_index_constant) { |
||
198 | /* gl_ClipDistance is being accessed via a constant index. Don't bother |
||
199 | * creating expressions to calculate the lowered indices. Just create |
||
200 | * constants. |
||
201 | */ |
||
202 | int const_val = old_index_constant->get_int_component(0); |
||
203 | array_index = new(ctx) ir_constant(const_val / 4); |
||
204 | swizzle_index = new(ctx) ir_constant(const_val % 4); |
||
205 | } else { |
||
206 | /* Create a variable to hold the value of old_index (so that we |
||
207 | * don't compute it twice). |
||
208 | */ |
||
209 | ir_variable *old_index_var = new(ctx) ir_variable( |
||
210 | glsl_type::int_type, "clip_distance_index", ir_var_temporary); |
||
211 | this->base_ir->insert_before(old_index_var); |
||
212 | this->base_ir->insert_before(new(ctx) ir_assignment( |
||
213 | new(ctx) ir_dereference_variable(old_index_var), old_index)); |
||
214 | |||
215 | /* Create the expression clip_distance_index / 4. Do this as a bit |
||
216 | * shift because that's likely to be more efficient. |
||
217 | */ |
||
218 | array_index = new(ctx) ir_expression( |
||
219 | ir_binop_rshift, new(ctx) ir_dereference_variable(old_index_var), |
||
220 | new(ctx) ir_constant(2)); |
||
221 | |||
222 | /* Create the expression clip_distance_index % 4. Do this as a bitwise |
||
223 | * AND because that's likely to be more efficient. |
||
224 | */ |
||
225 | swizzle_index = new(ctx) ir_expression( |
||
226 | ir_binop_bit_and, new(ctx) ir_dereference_variable(old_index_var), |
||
227 | new(ctx) ir_constant(3)); |
||
228 | } |
||
229 | } |
||
230 | |||
231 | |||
232 | /** |
||
233 | * Determine whether the given rvalue describes an array of 8 floats that |
||
234 | * needs to be lowered to an array of 2 vec4's; that is, determine whether it |
||
235 | * matches one of the following patterns: |
||
236 | * |
||
237 | * - gl_ClipDistance (if gl_ClipDistance is 1D) |
||
238 | * - gl_ClipDistance[i] (if gl_ClipDistance is 2D) |
||
239 | */ |
||
240 | bool |
||
241 | lower_clip_distance_visitor::is_clip_distance_vec8(ir_rvalue *ir) |
||
242 | { |
||
243 | /* Note that geometry shaders contain gl_ClipDistance both as an input |
||
244 | * (which is a 2D array) and an output (which is a 1D array), so it's |
||
245 | * possible for both this->old_clip_distance_1d_var and |
||
246 | * this->old_clip_distance_2d_var to be non-NULL in the same shader. |
||
247 | */ |
||
248 | |||
249 | if (this->old_clip_distance_1d_var) { |
||
250 | ir_dereference_variable *var_ref = ir->as_dereference_variable(); |
||
251 | if (var_ref && var_ref->var == this->old_clip_distance_1d_var) |
||
252 | return true; |
||
253 | } |
||
254 | if (this->old_clip_distance_2d_var) { |
||
255 | /* 2D clip distance is only possible as a geometry input */ |
||
256 | assert(this->shader_stage == MESA_SHADER_GEOMETRY); |
||
257 | |||
258 | ir_dereference_array *array_ref = ir->as_dereference_array(); |
||
259 | if (array_ref) { |
||
260 | ir_dereference_variable *var_ref = |
||
261 | array_ref->array->as_dereference_variable(); |
||
262 | if (var_ref && var_ref->var == this->old_clip_distance_2d_var) |
||
263 | return true; |
||
264 | } |
||
265 | } |
||
266 | return false; |
||
267 | } |
||
268 | |||
269 | |||
270 | /** |
||
271 | * If the given ir satisfies is_clip_distance_vec8(), return new ir |
||
272 | * representing its lowered equivalent. That is, map: |
||
273 | * |
||
274 | * - gl_ClipDistance => gl_ClipDistanceMESA (if gl_ClipDistance is 1D) |
||
275 | * - gl_ClipDistance[i] => gl_ClipDistanceMESA[i] (if gl_ClipDistance is 2D) |
||
276 | * |
||
277 | * Otherwise return NULL. |
||
278 | */ |
||
279 | ir_rvalue * |
||
280 | lower_clip_distance_visitor::lower_clip_distance_vec8(ir_rvalue *ir) |
||
281 | { |
||
282 | if (this->old_clip_distance_1d_var) { |
||
283 | ir_dereference_variable *var_ref = ir->as_dereference_variable(); |
||
284 | if (var_ref && var_ref->var == this->old_clip_distance_1d_var) { |
||
285 | return new(ralloc_parent(ir)) |
||
286 | ir_dereference_variable(this->new_clip_distance_1d_var); |
||
287 | } |
||
288 | } |
||
289 | if (this->old_clip_distance_2d_var) { |
||
290 | /* 2D clip distance is only possible as a geometry input */ |
||
291 | assert(this->shader_stage == MESA_SHADER_GEOMETRY); |
||
292 | |||
293 | ir_dereference_array *array_ref = ir->as_dereference_array(); |
||
294 | if (array_ref) { |
||
295 | ir_dereference_variable *var_ref = |
||
296 | array_ref->array->as_dereference_variable(); |
||
297 | if (var_ref && var_ref->var == this->old_clip_distance_2d_var) { |
||
298 | return new(ralloc_parent(ir)) |
||
299 | ir_dereference_array(this->new_clip_distance_2d_var, |
||
300 | array_ref->array_index); |
||
301 | } |
||
302 | } |
||
303 | } |
||
304 | return NULL; |
||
305 | } |
||
306 | |||
307 | |||
308 | void |
||
309 | lower_clip_distance_visitor::handle_rvalue(ir_rvalue **rv) |
||
310 | { |
||
311 | if (*rv == NULL) |
||
312 | return; |
||
313 | |||
314 | ir_dereference_array *const array_deref = (*rv)->as_dereference_array(); |
||
315 | if (array_deref == NULL) |
||
316 | return; |
||
317 | |||
318 | /* Replace any expression that indexes one of the floats in gl_ClipDistance |
||
319 | * with an expression that indexes into one of the vec4's in |
||
320 | * gl_ClipDistanceMESA and accesses the appropriate component. |
||
321 | */ |
||
322 | ir_rvalue *lowered_vec8 = |
||
323 | this->lower_clip_distance_vec8(array_deref->array); |
||
324 | if (lowered_vec8 != NULL) { |
||
325 | this->progress = true; |
||
326 | ir_rvalue *array_index; |
||
327 | ir_rvalue *swizzle_index; |
||
328 | this->create_indices(array_deref->array_index, array_index, swizzle_index); |
||
329 | void *mem_ctx = ralloc_parent(array_deref); |
||
330 | |||
331 | ir_dereference_array *const new_array_deref = |
||
332 | new(mem_ctx) ir_dereference_array(lowered_vec8, array_index); |
||
333 | |||
334 | ir_expression *const expr = |
||
335 | new(mem_ctx) ir_expression(ir_binop_vector_extract, |
||
336 | new_array_deref, |
||
337 | swizzle_index); |
||
338 | |||
339 | *rv = expr; |
||
340 | } |
||
341 | } |
||
342 | |||
343 | void |
||
344 | lower_clip_distance_visitor::fix_lhs(ir_assignment *ir) |
||
345 | { |
||
346 | if (ir->lhs->ir_type == ir_type_expression) { |
||
347 | void *mem_ctx = ralloc_parent(ir); |
||
348 | ir_expression *const expr = (ir_expression *) ir->lhs; |
||
349 | |||
350 | /* The expression must be of the form: |
||
351 | * |
||
352 | * (vector_extract gl_ClipDistanceMESA[i], j). |
||
353 | */ |
||
354 | assert(expr->operation == ir_binop_vector_extract); |
||
355 | assert(expr->operands[0]->ir_type == ir_type_dereference_array); |
||
356 | assert(expr->operands[0]->type == glsl_type::vec4_type); |
||
357 | |||
358 | ir_dereference *const new_lhs = (ir_dereference *) expr->operands[0]; |
||
359 | ir->rhs = new(mem_ctx) ir_expression(ir_triop_vector_insert, |
||
360 | glsl_type::vec4_type, |
||
361 | new_lhs->clone(mem_ctx, NULL), |
||
362 | ir->rhs, |
||
363 | expr->operands[1]); |
||
364 | ir->set_lhs(new_lhs); |
||
365 | ir->write_mask = WRITEMASK_XYZW; |
||
366 | } |
||
367 | } |
||
368 | |||
369 | /** |
||
370 | * Replace any assignment having the 1D gl_ClipDistance (undereferenced) as |
||
371 | * its LHS or RHS with a sequence of assignments, one for each component of |
||
372 | * the array. Each of these assignments is lowered to refer to |
||
373 | * gl_ClipDistanceMESA as appropriate. |
||
374 | * |
||
375 | * We need to do a similar replacement for 2D gl_ClipDistance, however since |
||
376 | * it's an input, the only case we need to address is where a 1D slice of it |
||
377 | * is the entire RHS of an assignment, e.g.: |
||
378 | * |
||
379 | * foo = gl_in[i].gl_ClipDistance |
||
380 | */ |
||
381 | ir_visitor_status |
||
382 | lower_clip_distance_visitor::visit_leave(ir_assignment *ir) |
||
383 | { |
||
384 | /* First invoke the base class visitor. This causes handle_rvalue() to be |
||
385 | * called on ir->rhs and ir->condition. |
||
386 | */ |
||
387 | ir_rvalue_visitor::visit_leave(ir); |
||
388 | |||
389 | if (this->is_clip_distance_vec8(ir->lhs) || |
||
390 | this->is_clip_distance_vec8(ir->rhs)) { |
||
391 | /* LHS or RHS of the assignment is the entire 1D gl_ClipDistance array |
||
392 | * (or a 1D slice of a 2D gl_ClipDistance input array). Since we are |
||
393 | * reshaping gl_ClipDistance from an array of floats to an array of |
||
394 | * vec4's, this isn't going to work as a bulk assignment anymore, so |
||
395 | * unroll it to element-by-element assignments and lower each of them. |
||
396 | * |
||
397 | * Note: to unroll into element-by-element assignments, we need to make |
||
398 | * clones of the LHS and RHS. This is safe because expressions and |
||
399 | * l-values are side-effect free. |
||
400 | */ |
||
401 | void *ctx = ralloc_parent(ir); |
||
402 | int array_size = ir->lhs->type->array_size(); |
||
403 | for (int i = 0; i < array_size; ++i) { |
||
404 | ir_dereference_array *new_lhs = new(ctx) ir_dereference_array( |
||
405 | ir->lhs->clone(ctx, NULL), new(ctx) ir_constant(i)); |
||
406 | ir_dereference_array *new_rhs = new(ctx) ir_dereference_array( |
||
407 | ir->rhs->clone(ctx, NULL), new(ctx) ir_constant(i)); |
||
408 | this->handle_rvalue((ir_rvalue **) &new_rhs); |
||
409 | |||
410 | /* Handle the LHS after creating the new assignment. This must |
||
411 | * happen in this order because handle_rvalue may replace the old LHS |
||
412 | * with an ir_expression of ir_binop_vector_extract. Since this is |
||
413 | * not a valide l-value, this will cause an assertion in the |
||
414 | * ir_assignment constructor to fail. |
||
415 | * |
||
416 | * If this occurs, replace the mangled LHS with a dereference of the |
||
417 | * vector, and replace the RHS with an ir_triop_vector_insert. |
||
418 | */ |
||
419 | ir_assignment *const assign = new(ctx) ir_assignment(new_lhs, new_rhs); |
||
420 | this->handle_rvalue((ir_rvalue **) &assign->lhs); |
||
421 | this->fix_lhs(assign); |
||
422 | |||
423 | this->base_ir->insert_before(assign); |
||
424 | } |
||
425 | ir->remove(); |
||
426 | |||
427 | return visit_continue; |
||
428 | } |
||
429 | |||
430 | /* Handle the LHS as if it were an r-value. Normally |
||
431 | * rvalue_visit(ir_assignment *) only visits the RHS, but we need to lower |
||
432 | * expressions in the LHS as well. |
||
433 | * |
||
434 | * This may cause the LHS to get replaced with an ir_expression of |
||
435 | * ir_binop_vector_extract. If this occurs, replace it with a dereference |
||
436 | * of the vector, and replace the RHS with an ir_triop_vector_insert. |
||
437 | */ |
||
438 | handle_rvalue((ir_rvalue **)&ir->lhs); |
||
439 | this->fix_lhs(ir); |
||
440 | |||
441 | return rvalue_visit(ir); |
||
442 | } |
||
443 | |||
444 | |||
445 | /** |
||
446 | * Set up base_ir properly and call visit_leave() on a newly created |
||
447 | * ir_assignment node. This is used in cases where we have to insert an |
||
448 | * ir_assignment in a place where we know the hierarchical visitor won't see |
||
449 | * it. |
||
450 | */ |
||
451 | void |
||
452 | lower_clip_distance_visitor::visit_new_assignment(ir_assignment *ir) |
||
453 | { |
||
454 | ir_instruction *old_base_ir = this->base_ir; |
||
455 | this->base_ir = ir; |
||
456 | ir->accept(this); |
||
457 | this->base_ir = old_base_ir; |
||
458 | } |
||
459 | |||
460 | |||
461 | /** |
||
462 | * If a 1D gl_ClipDistance variable appears as an argument in an ir_call |
||
463 | * expression, replace it with a temporary variable, and make sure the ir_call |
||
464 | * is preceded and/or followed by assignments that copy the contents of the |
||
465 | * temporary variable to and/or from gl_ClipDistance. Each of these |
||
466 | * assignments is then lowered to refer to gl_ClipDistanceMESA. |
||
467 | * |
||
468 | * We need to do a similar replacement for 2D gl_ClipDistance, however since |
||
469 | * it's an input, the only case we need to address is where a 1D slice of it |
||
470 | * is passed as an "in" parameter to an ir_call, e.g.: |
||
471 | * |
||
472 | * foo(gl_in[i].gl_ClipDistance) |
||
473 | */ |
||
474 | ir_visitor_status |
||
475 | lower_clip_distance_visitor::visit_leave(ir_call *ir) |
||
476 | { |
||
477 | void *ctx = ralloc_parent(ir); |
||
478 | |||
479 | const exec_node *formal_param_node = ir->callee->parameters.head; |
||
480 | const exec_node *actual_param_node = ir->actual_parameters.head; |
||
481 | while (!actual_param_node->is_tail_sentinel()) { |
||
482 | ir_variable *formal_param = (ir_variable *) formal_param_node; |
||
483 | ir_rvalue *actual_param = (ir_rvalue *) actual_param_node; |
||
484 | |||
485 | /* Advance formal_param_node and actual_param_node now so that we can |
||
486 | * safely replace actual_param with another node, if necessary, below. |
||
487 | */ |
||
488 | formal_param_node = formal_param_node->next; |
||
489 | actual_param_node = actual_param_node->next; |
||
490 | |||
491 | if (this->is_clip_distance_vec8(actual_param)) { |
||
492 | /* User is trying to pass the whole 1D gl_ClipDistance array (or a 1D |
||
493 | * slice of a 2D gl_ClipDistance array) to a function call. Since we |
||
494 | * are reshaping gl_ClipDistance from an array of floats to an array |
||
495 | * of vec4's, this isn't going to work anymore, so use a temporary |
||
496 | * array instead. |
||
497 | */ |
||
498 | ir_variable *temp_clip_distance = new(ctx) ir_variable( |
||
499 | actual_param->type, "temp_clip_distance", ir_var_temporary); |
||
500 | this->base_ir->insert_before(temp_clip_distance); |
||
501 | actual_param->replace_with( |
||
502 | new(ctx) ir_dereference_variable(temp_clip_distance)); |
||
503 | if (formal_param->data.mode == ir_var_function_in |
||
504 | || formal_param->data.mode == ir_var_function_inout) { |
||
505 | /* Copy from gl_ClipDistance to the temporary before the call. |
||
506 | * Since we are going to insert this copy before the current |
||
507 | * instruction, we need to visit it afterwards to make sure it |
||
508 | * gets lowered. |
||
509 | */ |
||
510 | ir_assignment *new_assignment = new(ctx) ir_assignment( |
||
511 | new(ctx) ir_dereference_variable(temp_clip_distance), |
||
512 | actual_param->clone(ctx, NULL)); |
||
513 | this->base_ir->insert_before(new_assignment); |
||
514 | this->visit_new_assignment(new_assignment); |
||
515 | } |
||
516 | if (formal_param->data.mode == ir_var_function_out |
||
517 | || formal_param->data.mode == ir_var_function_inout) { |
||
518 | /* Copy from the temporary to gl_ClipDistance after the call. |
||
519 | * Since visit_list_elements() has already decided which |
||
520 | * instruction it's going to visit next, we need to visit |
||
521 | * afterwards to make sure it gets lowered. |
||
522 | */ |
||
523 | ir_assignment *new_assignment = new(ctx) ir_assignment( |
||
524 | actual_param->clone(ctx, NULL), |
||
525 | new(ctx) ir_dereference_variable(temp_clip_distance)); |
||
526 | this->base_ir->insert_after(new_assignment); |
||
527 | this->visit_new_assignment(new_assignment); |
||
528 | } |
||
529 | } |
||
530 | } |
||
531 | |||
532 | return rvalue_visit(ir); |
||
533 | } |
||
534 | |||
535 | |||
536 | bool |
||
537 | lower_clip_distance(gl_shader *shader) |
||
538 | { |
||
539 | lower_clip_distance_visitor v(shader->Stage); |
||
540 | |||
541 | visit_list_elements(&v, shader->ir); |
||
542 | |||
543 | if (v.new_clip_distance_1d_var) |
||
544 | shader->symbols->add_variable(v.new_clip_distance_1d_var); |
||
545 | if (v.new_clip_distance_2d_var) |
||
546 | shader->symbols->add_variable(v.new_clip_distance_2d_var); |
||
547 | |||
548 | return v.progress; |
||
549 | }> |