/** * If a 1D gl_ClipDistance variable appears as an argument in an ir_call * expression, replace it with a temporary variable, and make sure the ir_call * is preceded and/or followed by assignments that copy the contents of the * temporary variable to and/or from gl_ClipDistance. Each of these * assignments is then lowered to refer to gl_ClipDistanceMESA. * * We need to do a similar replacement for 2D gl_ClipDistance, however since * it's an input, the only case we need to address is where a 1D slice of it * is passed as an "in" parameter to an ir_call, e.g.: * * foo(gl_in[i].gl_ClipDistance) */ ir_visitor_status lower_clip_distance_visitor::visit_leave(ir_call *ir) { void *ctx = ralloc_parent(ir); const exec_node *formal_param_node = ir->callee->parameters.head; const exec_node *actual_param_node = ir->actual_parameters.head; while (!actual_param_node->is_tail_sentinel()) { ir_variable *formal_param = (ir_variable *) formal_param_node; ir_rvalue *actual_param = (ir_rvalue *) actual_param_node; /* Advance formal_param_node and actual_param_node now so that we can * safely replace actual_param with another node, if necessary, below. */ formal_param_node = formal_param_node->next; actual_param_node = actual_param_node->next; if (this->is_clip_distance_vec8(actual_param)) { /* User is trying to pass the whole 1D gl_ClipDistance array (or a 1D * slice of a 2D gl_ClipDistance array) to a function call. Since we * are reshaping gl_ClipDistance from an array of floats to an array * of vec4's, this isn't going to work anymore, so use a temporary * array instead. */ ir_variable *temp_clip_distance = new(ctx) ir_variable( actual_param->type, "temp_clip_distance", ir_var_temporary, actual_param->get_precision()); this->base_ir->insert_before(temp_clip_distance); actual_param->replace_with( new(ctx) ir_dereference_variable(temp_clip_distance)); if (formal_param->data.mode == ir_var_function_in || formal_param->data.mode == ir_var_function_inout) { /* Copy from gl_ClipDistance to the temporary before the call. * Since we are going to insert this copy before the current * instruction, we need to visit it afterwards to make sure it * gets lowered. */ ir_assignment *new_assignment = new(ctx) ir_assignment( new(ctx) ir_dereference_variable(temp_clip_distance), actual_param->clone(ctx, NULL)); this->base_ir->insert_before(new_assignment); this->visit_new_assignment(new_assignment); } if (formal_param->data.mode == ir_var_function_out || formal_param->data.mode == ir_var_function_inout) { /* Copy from the temporary to gl_ClipDistance after the call. * Since visit_list_elements() has already decided which * instruction it's going to visit next, we need to visit * afterwards to make sure it gets lowered. */ ir_assignment *new_assignment = new(ctx) ir_assignment( actual_param->clone(ctx, NULL), new(ctx) ir_dereference_variable(temp_clip_distance)); this->base_ir->insert_after(new_assignment); this->visit_new_assignment(new_assignment); } } } return rvalue_visit(ir); }
/** * Replace any assignment having gl_ClipDistance (undereferenced) as its LHS * or RHS with a sequence of assignments, one for each component of the array. * Each of these assignments is lowered to refer to gl_ClipDistanceMESA as * appropriate. */ ir_visitor_status lower_clip_distance_visitor::visit_leave(ir_assignment *ir) { ir_dereference_variable *lhs_var = ir->lhs->as_dereference_variable(); ir_dereference_variable *rhs_var = ir->rhs->as_dereference_variable(); if ((lhs_var && lhs_var->var == this->old_clip_distance_var) || (rhs_var && rhs_var->var == this->old_clip_distance_var)) { /* LHS or RHS of the assignment is the entire gl_ClipDistance array. * Since we are reshaping gl_ClipDistance from an array of floats to an * array of vec4's, this isn't going to work as a bulk assignment * anymore, so unroll it to element-by-element assignments and lower * each of them. * * Note: to unroll into element-by-element assignments, we need to make * clones of the LHS and RHS. This is safe because expressions and * l-values are side-effect free. */ void *ctx = ralloc_parent(ir); int array_size = this->old_clip_distance_var->type->array_size(); for (int i = 0; i < array_size; ++i) { ir_dereference_array *new_lhs = new(ctx) ir_dereference_array( ir->lhs->clone(ctx, NULL), new(ctx) ir_constant(i)); ir_dereference_array *new_rhs = new(ctx) ir_dereference_array( ir->rhs->clone(ctx, NULL), new(ctx) ir_constant(i)); this->handle_rvalue((ir_rvalue **) &new_rhs); /* Handle the LHS after creating the new assignment. This must * happen in this order because handle_rvalue may replace the old LHS * with an ir_expression of ir_binop_vector_extract. Since this is * not a valide l-value, this will cause an assertion in the * ir_assignment constructor to fail. * * If this occurs, replace the mangled LHS with a dereference of the * vector, and replace the RHS with an ir_triop_vector_insert. */ ir_assignment *const assign = new(ctx) ir_assignment(new_lhs, new_rhs); this->handle_rvalue((ir_rvalue **) &assign->lhs); this->fix_lhs(assign); this->base_ir->insert_before(assign); } ir->remove(); return visit_continue; } /* Handle the LHS as if it were an r-value. Normally * rvalue_visit(ir_assignment *) only visits the RHS, but we need to lower * expressions in the LHS as well. * * This may cause the LHS to get replaced with an ir_expression of * ir_binop_vector_extract. If this occurs, replace it with a dereference * of the vector, and replace the RHS with an ir_triop_vector_insert. */ handle_rvalue((ir_rvalue **)&ir->lhs); this->fix_lhs(ir); return rvalue_visit(ir); }