Home | History | Annotate | Download | only in glsl
      1 /*
      2  * Copyright  2010 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_mat_op_to_vec.cpp
     26  *
     27  * Breaks matrix operation expressions down to a series of vector operations.
     28  *
     29  * Generally this is how we have to codegen matrix operations for a
     30  * GPU, so this gives us the chance to constant fold operations on a
     31  * column or row.
     32  */
     33 
     34 #include "ir.h"
     35 #include "ir_expression_flattening.h"
     36 #include "glsl_types.h"
     37 
     38 class ir_mat_op_to_vec_visitor : public ir_hierarchical_visitor {
     39 public:
     40    ir_mat_op_to_vec_visitor()
     41    {
     42       this->made_progress = false;
     43       this->mem_ctx = NULL;
     44    }
     45 
     46    ir_visitor_status visit_leave(ir_assignment *);
     47 
     48    ir_dereference *get_column(ir_variable *var, int col);
     49    ir_rvalue *get_element(ir_variable *var, int col, int row);
     50 
     51    void do_mul_mat_mat(ir_variable *result_var,
     52 		       ir_variable *a_var, ir_variable *b_var);
     53    void do_mul_mat_vec(ir_variable *result_var,
     54 		       ir_variable *a_var, ir_variable *b_var);
     55    void do_mul_vec_mat(ir_variable *result_var,
     56 		       ir_variable *a_var, ir_variable *b_var);
     57    void do_mul_mat_scalar(ir_variable *result_var,
     58 			  ir_variable *a_var, ir_variable *b_var);
     59    void do_equal_mat_mat(ir_variable *result_var, ir_variable *a_var,
     60 			 ir_variable *b_var, bool test_equal);
     61 
     62    void *mem_ctx;
     63    bool made_progress;
     64 };
     65 
     66 static bool
     67 mat_op_to_vec_predicate(ir_instruction *ir)
     68 {
     69    ir_expression *expr = ir->as_expression();
     70    unsigned int i;
     71 
     72    if (!expr)
     73       return false;
     74 
     75    for (i = 0; i < expr->get_num_operands(); i++) {
     76       if (expr->operands[i]->type->is_matrix())
     77 	 return true;
     78    }
     79 
     80    return false;
     81 }
     82 
     83 bool
     84 do_mat_op_to_vec(exec_list *instructions)
     85 {
     86    ir_mat_op_to_vec_visitor v;
     87 
     88    /* Pull out any matrix expression to a separate assignment to a
     89     * temp.  This will make our handling of the breakdown to
     90     * operations on the matrix's vector components much easier.
     91     */
     92    do_expression_flattening(instructions, mat_op_to_vec_predicate);
     93 
     94    visit_list_elements(&v, instructions);
     95 
     96    return v.made_progress;
     97 }
     98 
     99 ir_rvalue *
    100 ir_mat_op_to_vec_visitor::get_element(ir_variable *var, int col, int row)
    101 {
    102    ir_dereference *deref;
    103 
    104    deref = new(mem_ctx) ir_dereference_variable(var);
    105 
    106    if (var->type->is_matrix()) {
    107       deref = new(mem_ctx) ir_dereference_array(var,
    108 						new(mem_ctx) ir_constant(col));
    109    } else {
    110       assert(col == 0);
    111    }
    112 
    113    return new(mem_ctx) ir_swizzle(deref, row, 0, 0, 0, 1);
    114 }
    115 
    116 ir_dereference *
    117 ir_mat_op_to_vec_visitor::get_column(ir_variable *var, int row)
    118 {
    119    ir_dereference *deref;
    120 
    121    if (!var->type->is_matrix()) {
    122       deref = new(mem_ctx) ir_dereference_variable(var);
    123    } else {
    124       deref = new(mem_ctx) ir_dereference_variable(var);
    125       deref = new(mem_ctx) ir_dereference_array(deref,
    126 						new(mem_ctx) ir_constant(row));
    127    }
    128 
    129    return deref;
    130 }
    131 
    132 void
    133 ir_mat_op_to_vec_visitor::do_mul_mat_mat(ir_variable *result_var,
    134 					 ir_variable *a_var,
    135 					 ir_variable *b_var)
    136 {
    137    int b_col, i;
    138    ir_assignment *assign;
    139    ir_expression *expr;
    140 
    141    for (b_col = 0; b_col < b_var->type->matrix_columns; b_col++) {
    142       ir_rvalue *a = get_column(a_var, 0);
    143       ir_rvalue *b = get_element(b_var, b_col, 0);
    144 
    145       /* first column */
    146       expr = new(mem_ctx) ir_expression(ir_binop_mul,
    147 					a->type,
    148 					a,
    149 					b);
    150 
    151       /* following columns */
    152       for (i = 1; i < a_var->type->matrix_columns; i++) {
    153 	 ir_expression *mul_expr;
    154 
    155 	 a = get_column(a_var, i);
    156 	 b = get_element(b_var, b_col, i);
    157 
    158 	 mul_expr = new(mem_ctx) ir_expression(ir_binop_mul,
    159 					       a->type,
    160 					       a,
    161 					       b);
    162 	 expr = new(mem_ctx) ir_expression(ir_binop_add,
    163 					   a->type,
    164 					   expr,
    165 					   mul_expr);
    166       }
    167 
    168       ir_rvalue *result = get_column(result_var, b_col);
    169       assign = new(mem_ctx) ir_assignment(result,
    170 					  expr,
    171 					  NULL);
    172       base_ir->insert_before(assign);
    173    }
    174 }
    175 
    176 void
    177 ir_mat_op_to_vec_visitor::do_mul_mat_vec(ir_variable *result_var,
    178 					 ir_variable *a_var,
    179 					 ir_variable *b_var)
    180 {
    181    int i;
    182    ir_rvalue *a = get_column(a_var, 0);
    183    ir_rvalue *b = get_element(b_var, 0, 0);
    184    ir_assignment *assign;
    185    ir_expression *expr;
    186 
    187    /* first column */
    188    expr = new(mem_ctx) ir_expression(ir_binop_mul,
    189 				     result_var->type,
    190 				     a,
    191 				     b);
    192 
    193    /* following columns */
    194    for (i = 1; i < a_var->type->matrix_columns; i++) {
    195       ir_expression *mul_expr;
    196 
    197       a = get_column(a_var, i);
    198       b = get_element(b_var, 0, i);
    199 
    200       mul_expr = new(mem_ctx) ir_expression(ir_binop_mul,
    201 					    result_var->type,
    202 					    a,
    203 					    b);
    204       expr = new(mem_ctx) ir_expression(ir_binop_add,
    205 					result_var->type,
    206 					expr,
    207 					mul_expr);
    208    }
    209 
    210    ir_rvalue *result = new(mem_ctx) ir_dereference_variable(result_var);
    211    assign = new(mem_ctx) ir_assignment(result,
    212 				       expr,
    213 				       NULL);
    214    base_ir->insert_before(assign);
    215 }
    216 
    217 void
    218 ir_mat_op_to_vec_visitor::do_mul_vec_mat(ir_variable *result_var,
    219 					 ir_variable *a_var,
    220 					 ir_variable *b_var)
    221 {
    222    int i;
    223 
    224    for (i = 0; i < b_var->type->matrix_columns; i++) {
    225       ir_rvalue *a = new(mem_ctx) ir_dereference_variable(a_var);
    226       ir_rvalue *b = get_column(b_var, i);
    227       ir_rvalue *result;
    228       ir_expression *column_expr;
    229       ir_assignment *column_assign;
    230 
    231       result = new(mem_ctx) ir_dereference_variable(result_var);
    232       result = new(mem_ctx) ir_swizzle(result, i, 0, 0, 0, 1);
    233 
    234       column_expr = new(mem_ctx) ir_expression(ir_binop_dot,
    235 					       result->type,
    236 					       a,
    237 					       b);
    238 
    239       column_assign = new(mem_ctx) ir_assignment(result,
    240 						 column_expr,
    241 						 NULL);
    242       base_ir->insert_before(column_assign);
    243    }
    244 }
    245 
    246 void
    247 ir_mat_op_to_vec_visitor::do_mul_mat_scalar(ir_variable *result_var,
    248 					    ir_variable *a_var,
    249 					    ir_variable *b_var)
    250 {
    251    int i;
    252 
    253    for (i = 0; i < a_var->type->matrix_columns; i++) {
    254       ir_rvalue *a = get_column(a_var, i);
    255       ir_rvalue *b = new(mem_ctx) ir_dereference_variable(b_var);
    256       ir_rvalue *result = get_column(result_var, i);
    257       ir_expression *column_expr;
    258       ir_assignment *column_assign;
    259 
    260       column_expr = new(mem_ctx) ir_expression(ir_binop_mul,
    261 					       result->type,
    262 					       a,
    263 					       b);
    264 
    265       column_assign = new(mem_ctx) ir_assignment(result,
    266 						 column_expr,
    267 						 NULL);
    268       base_ir->insert_before(column_assign);
    269    }
    270 }
    271 
    272 void
    273 ir_mat_op_to_vec_visitor::do_equal_mat_mat(ir_variable *result_var,
    274 					   ir_variable *a_var,
    275 					   ir_variable *b_var,
    276 					   bool test_equal)
    277 {
    278    /* This essentially implements the following GLSL:
    279     *
    280     * bool equal(mat4 a, mat4 b)
    281     * {
    282     *   return !any(bvec4(a[0] != b[0],
    283     *                     a[1] != b[1],
    284     *                     a[2] != b[2],
    285     *                     a[3] != b[3]);
    286     * }
    287     *
    288     * bool nequal(mat4 a, mat4 b)
    289     * {
    290     *   return any(bvec4(a[0] != b[0],
    291     *                    a[1] != b[1],
    292     *                    a[2] != b[2],
    293     *                    a[3] != b[3]);
    294     * }
    295     */
    296    const unsigned columns = a_var->type->matrix_columns;
    297    const glsl_type *const bvec_type =
    298       glsl_type::get_instance(GLSL_TYPE_BOOL, columns, 1);
    299 
    300    ir_variable *const tmp_bvec =
    301       new(this->mem_ctx) ir_variable(bvec_type, "mat_cmp_bvec",
    302 				     ir_var_temporary);
    303    this->base_ir->insert_before(tmp_bvec);
    304 
    305    for (unsigned i = 0; i < columns; i++) {
    306       ir_dereference *const op0 = get_column(a_var, i);
    307       ir_dereference *const op1 = get_column(b_var, i);
    308 
    309       ir_expression *const cmp =
    310 	 new(this->mem_ctx) ir_expression(ir_binop_any_nequal,
    311 					  glsl_type::bool_type, op0, op1);
    312 
    313       ir_dereference *const lhs =
    314 	 new(this->mem_ctx) ir_dereference_variable(tmp_bvec);
    315 
    316       ir_assignment *const assign =
    317 	 new(this->mem_ctx) ir_assignment(lhs, cmp, NULL, (1U << i));
    318 
    319       this->base_ir->insert_before(assign);
    320    }
    321 
    322    ir_rvalue *const val =
    323       new(this->mem_ctx) ir_dereference_variable(tmp_bvec);
    324 
    325    ir_expression *any =
    326       new(this->mem_ctx) ir_expression(ir_unop_any, glsl_type::bool_type,
    327 				       val, NULL);
    328 
    329    if (test_equal)
    330       any = new(this->mem_ctx) ir_expression(ir_unop_logic_not,
    331 					     glsl_type::bool_type,
    332 					     any, NULL);
    333 
    334    ir_rvalue *const result =
    335       new(this->mem_ctx) ir_dereference_variable(result_var);
    336 
    337    ir_assignment *const assign =
    338 	 new(mem_ctx) ir_assignment(result, any, NULL);
    339    base_ir->insert_before(assign);
    340 }
    341 
    342 static bool
    343 has_matrix_operand(const ir_expression *expr, unsigned &columns)
    344 {
    345    for (unsigned i = 0; i < expr->get_num_operands(); i++) {
    346       if (expr->operands[i]->type->is_matrix()) {
    347 	 columns = expr->operands[i]->type->matrix_columns;
    348 	 return true;
    349       }
    350    }
    351 
    352    return false;
    353 }
    354 
    355 
    356 ir_visitor_status
    357 ir_mat_op_to_vec_visitor::visit_leave(ir_assignment *orig_assign)
    358 {
    359    ir_expression *orig_expr = orig_assign->rhs->as_expression();
    360    unsigned int i, matrix_columns = 1;
    361    ir_variable *op_var[2];
    362 
    363    if (!orig_expr)
    364       return visit_continue;
    365 
    366    if (!has_matrix_operand(orig_expr, matrix_columns))
    367       return visit_continue;
    368 
    369    assert(orig_expr->get_num_operands() <= 2);
    370 
    371    mem_ctx = hieralloc_parent(orig_assign);
    372 
    373    ir_dereference_variable *lhs_deref =
    374       orig_assign->lhs->as_dereference_variable();
    375    assert(lhs_deref);
    376 
    377    ir_variable *result_var = lhs_deref->var;
    378 
    379    /* Store the expression operands in temps so we can use them
    380     * multiple times.
    381     */
    382    for (i = 0; i < orig_expr->get_num_operands(); i++) {
    383       ir_assignment *assign;
    384 
    385       op_var[i] = new(mem_ctx) ir_variable(orig_expr->operands[i]->type,
    386 					   "mat_op_to_vec",
    387 					   ir_var_temporary);
    388       base_ir->insert_before(op_var[i]);
    389 
    390       lhs_deref = new(mem_ctx) ir_dereference_variable(op_var[i]);
    391       assign = new(mem_ctx) ir_assignment(lhs_deref,
    392 					  orig_expr->operands[i],
    393 					  NULL);
    394       base_ir->insert_before(assign);
    395    }
    396 
    397    /* OK, time to break down this matrix operation. */
    398    switch (orig_expr->operation) {
    399    case ir_unop_neg: {
    400       const unsigned mask = (1U << result_var->type->vector_elements) - 1;
    401 
    402       /* Apply the operation to each column.*/
    403       for (i = 0; i < matrix_columns; i++) {
    404 	 ir_rvalue *op0 = get_column(op_var[0], i);
    405 	 ir_dereference *result = get_column(result_var, i);
    406 	 ir_expression *column_expr;
    407 	 ir_assignment *column_assign;
    408 
    409 	 column_expr = new(mem_ctx) ir_expression(orig_expr->operation,
    410 						  result->type,
    411 						  op0,
    412 						  NULL);
    413 
    414 	 column_assign = new(mem_ctx) ir_assignment(result,
    415 						    column_expr,
    416 						    NULL,
    417 						    mask);
    418 	 assert(column_assign->write_mask != 0);
    419 	 base_ir->insert_before(column_assign);
    420       }
    421       break;
    422    }
    423    case ir_binop_add:
    424    case ir_binop_sub:
    425    case ir_binop_div:
    426    case ir_binop_mod: {
    427       const unsigned mask = (1U << result_var->type->vector_elements) - 1;
    428 
    429       /* For most operations, the matrix version is just going
    430        * column-wise through and applying the operation to each column
    431        * if available.
    432        */
    433       for (i = 0; i < matrix_columns; i++) {
    434 	 ir_rvalue *op0 = get_column(op_var[0], i);
    435 	 ir_rvalue *op1 = get_column(op_var[1], i);
    436 	 ir_dereference *result = get_column(result_var, i);
    437 	 ir_expression *column_expr;
    438 	 ir_assignment *column_assign;
    439 
    440 	 column_expr = new(mem_ctx) ir_expression(orig_expr->operation,
    441 						  result->type,
    442 						  op0,
    443 						  op1);
    444 
    445 	 column_assign = new(mem_ctx) ir_assignment(result,
    446 						    column_expr,
    447 						    NULL,
    448 						    mask);
    449 	 assert(column_assign->write_mask != 0);
    450 	 base_ir->insert_before(column_assign);
    451       }
    452       break;
    453    }
    454    case ir_binop_mul:
    455       if (op_var[0]->type->is_matrix()) {
    456 	 if (op_var[1]->type->is_matrix()) {
    457 	    do_mul_mat_mat(result_var, op_var[0], op_var[1]);
    458 	 } else if (op_var[1]->type->is_vector()) {
    459 	    do_mul_mat_vec(result_var, op_var[0], op_var[1]);
    460 	 } else {
    461 	    assert(op_var[1]->type->is_scalar());
    462 	    do_mul_mat_scalar(result_var, op_var[0], op_var[1]);
    463 	 }
    464       } else {
    465 	 assert(op_var[1]->type->is_matrix());
    466 	 if (op_var[0]->type->is_vector()) {
    467 	    do_mul_vec_mat(result_var, op_var[0], op_var[1]);
    468 	 } else {
    469 	    assert(op_var[0]->type->is_scalar());
    470 	    do_mul_mat_scalar(result_var, op_var[1], op_var[0]);
    471 	 }
    472       }
    473       break;
    474 
    475    case ir_binop_all_equal:
    476    case ir_binop_any_nequal:
    477       do_equal_mat_mat(result_var, op_var[1], op_var[0],
    478 		       (orig_expr->operation == ir_binop_all_equal));
    479       break;
    480 
    481    default:
    482       printf("FINISHME: Handle matrix operation for %s\n",
    483 	     orig_expr->operator_string());
    484       abort();
    485    }
    486    orig_assign->remove();
    487    this->made_progress = true;
    488 
    489    return visit_continue;
    490 }
    491