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/mtypes.h"
54 
55 #define GLSL_CLIP_VAR_NAME "gl_ClipDistanceMESA"
56 
57 namespace {
58 
59 class lower_distance_visitor : public ir_rvalue_visitor {
60 public:
lower_distance_visitor(gl_shader_stage shader_stage,const char * in_name,int total_size,int offset)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 
lower_distance_visitor(gl_shader_stage shader_stage,const char * in_name,const lower_distance_visitor * orig,int offset)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  */
137 ir_visitor_status
visit(ir_variable * ir)138 lower_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  */
224 void
create_indices(ir_rvalue * old_index,ir_rvalue * & array_index,ir_rvalue * & swizzle_index)225 lower_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  */
290 bool
is_distance_vec8(ir_rvalue * ir)291 lower_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  */
330 ir_rvalue *
lower_distance_vec8(ir_rvalue * ir)331 lower_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 
363 void
handle_rvalue(ir_rvalue ** rv)364 lower_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 
398 void
fix_lhs(ir_assignment * ir)399 lower_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  */
436 ir_visitor_status
visit_leave(ir_assignment * ir)437 lower_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  */
506 void
visit_new_assignment(ir_assignment * ir)507 lower_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  */
529 ir_visitor_status
visit_leave(ir_call * ir)530 lower_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 
590 namespace {
591 class lower_distance_visitor_counter : public ir_rvalue_visitor {
592 public:
lower_distance_visitor_counter(void)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  */
612 ir_visitor_status
visit(ir_variable * ir)613 lower_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 
652 void
handle_rvalue(ir_rvalue **)653 lower_distance_visitor_counter::handle_rvalue(ir_rvalue **)
654 {
655    return;
656 }
657 
658 bool
lower_clip_cull_distance(struct gl_shader_program * prog,struct gl_linked_shader * shader)659 lower_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