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_dereference *val, int col);
     49    ir_rvalue *get_element(ir_dereference *val, int col, int row);
     50 
     51    void do_mul_mat_mat(ir_dereference *result,
     52 		       ir_dereference *a, ir_dereference *b);
     53    void do_mul_mat_vec(ir_dereference *result,
     54 		       ir_dereference *a, ir_dereference *b);
     55    void do_mul_vec_mat(ir_dereference *result,
     56 		       ir_dereference *a, ir_dereference *b);
     57    void do_mul_mat_scalar(ir_dereference *result,
     58 			  ir_dereference *a, ir_dereference *b);
     59    void do_equal_mat_mat(ir_dereference *result, ir_dereference *a,
     60 			 ir_dereference *b, 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_dereference *val, int col, int row)
    101 {
    102    val = get_column(val, col);
    103 
    104    return new(mem_ctx) ir_swizzle(val, row, 0, 0, 0, 1);
    105 }
    106 
    107 ir_dereference *
    108 ir_mat_op_to_vec_visitor::get_column(ir_dereference *val, int row)
    109 {
    110    val = val->clone(mem_ctx, NULL);
    111 
    112    if (val->type->is_matrix()) {
    113       val = new(mem_ctx) ir_dereference_array(val,
    114 					      new(mem_ctx) ir_constant(row));
    115    }
    116 
    117    return val;
    118 }
    119 
    120 void
    121 ir_mat_op_to_vec_visitor::do_mul_mat_mat(ir_dereference *result,
    122 					 ir_dereference *a,
    123 					 ir_dereference *b)
    124 {
    125    int b_col, i;
    126    ir_assignment *assign;
    127    ir_expression *expr;
    128 
    129    for (b_col = 0; b_col < b->type->matrix_columns; b_col++) {
    130       /* first column */
    131       expr = new(mem_ctx) ir_expression(ir_binop_mul,
    132 					get_column(a, 0),
    133 					get_element(b, b_col, 0));
    134 
    135       /* following columns */
    136       for (i = 1; i < a->type->matrix_columns; i++) {
    137 	 ir_expression *mul_expr;
    138 
    139 	 mul_expr = new(mem_ctx) ir_expression(ir_binop_mul,
    140 					       get_column(a, i),
    141 					       get_element(b, b_col, i));
    142 	 expr = new(mem_ctx) ir_expression(ir_binop_add,
    143 					   expr,
    144 					   mul_expr);
    145       }
    146 
    147       assign = new(mem_ctx) ir_assignment(get_column(result, b_col), expr);
    148       base_ir->insert_before(assign);
    149    }
    150 }
    151 
    152 void
    153 ir_mat_op_to_vec_visitor::do_mul_mat_vec(ir_dereference *result,
    154 					 ir_dereference *a,
    155 					 ir_dereference *b)
    156 {
    157    int i;
    158    ir_assignment *assign;
    159    ir_expression *expr;
    160 
    161    /* first column */
    162    expr = new(mem_ctx) ir_expression(ir_binop_mul,
    163 				     get_column(a, 0),
    164 				     get_element(b, 0, 0));
    165 
    166    /* following columns */
    167    for (i = 1; i < a->type->matrix_columns; i++) {
    168       ir_expression *mul_expr;
    169 
    170       mul_expr = new(mem_ctx) ir_expression(ir_binop_mul,
    171 					    get_column(a, i),
    172 					    get_element(b, 0, i));
    173       expr = new(mem_ctx) ir_expression(ir_binop_add, expr, mul_expr);
    174    }
    175 
    176    result = result->clone(mem_ctx, NULL);
    177    assign = new(mem_ctx) ir_assignment(result, expr);
    178    base_ir->insert_before(assign);
    179 }
    180 
    181 void
    182 ir_mat_op_to_vec_visitor::do_mul_vec_mat(ir_dereference *result,
    183 					 ir_dereference *a,
    184 					 ir_dereference *b)
    185 {
    186    int i;
    187 
    188    for (i = 0; i < b->type->matrix_columns; i++) {
    189       ir_rvalue *column_result;
    190       ir_expression *column_expr;
    191       ir_assignment *column_assign;
    192 
    193       column_result = result->clone(mem_ctx, NULL);
    194       column_result = new(mem_ctx) ir_swizzle(column_result, i, 0, 0, 0, 1);
    195 
    196       column_expr = new(mem_ctx) ir_expression(ir_binop_dot,
    197 					       a->clone(mem_ctx, NULL),
    198 					       get_column(b, i));
    199 
    200       column_assign = new(mem_ctx) ir_assignment(column_result,
    201 						 column_expr);
    202       base_ir->insert_before(column_assign);
    203    }
    204 }
    205 
    206 void
    207 ir_mat_op_to_vec_visitor::do_mul_mat_scalar(ir_dereference *result,
    208 					    ir_dereference *a,
    209 					    ir_dereference *b)
    210 {
    211    int i;
    212 
    213    for (i = 0; i < a->type->matrix_columns; i++) {
    214       ir_expression *column_expr;
    215       ir_assignment *column_assign;
    216 
    217       column_expr = new(mem_ctx) ir_expression(ir_binop_mul,
    218 					       get_column(a, i),
    219 					       b->clone(mem_ctx, NULL));
    220 
    221       column_assign = new(mem_ctx) ir_assignment(get_column(result, i),
    222 						 column_expr);
    223       base_ir->insert_before(column_assign);
    224    }
    225 }
    226 
    227 void
    228 ir_mat_op_to_vec_visitor::do_equal_mat_mat(ir_dereference *result,
    229 					   ir_dereference *a,
    230 					   ir_dereference *b,
    231 					   bool test_equal)
    232 {
    233    /* This essentially implements the following GLSL:
    234     *
    235     * bool equal(mat4 a, mat4 b)
    236     * {
    237     *   return !any(bvec4(a[0] != b[0],
    238     *                     a[1] != b[1],
    239     *                     a[2] != b[2],
    240     *                     a[3] != b[3]);
    241     * }
    242     *
    243     * bool nequal(mat4 a, mat4 b)
    244     * {
    245     *   return any(bvec4(a[0] != b[0],
    246     *                    a[1] != b[1],
    247     *                    a[2] != b[2],
    248     *                    a[3] != b[3]);
    249     * }
    250     */
    251    const unsigned columns = a->type->matrix_columns;
    252    const glsl_type *const bvec_type =
    253       glsl_type::get_instance(GLSL_TYPE_BOOL, columns, 1);
    254 
    255    ir_variable *const tmp_bvec =
    256       new(this->mem_ctx) ir_variable(bvec_type, "mat_cmp_bvec",
    257 				     ir_var_temporary);
    258    this->base_ir->insert_before(tmp_bvec);
    259 
    260    for (unsigned i = 0; i < columns; i++) {
    261       ir_expression *const cmp =
    262 	 new(this->mem_ctx) ir_expression(ir_binop_any_nequal,
    263 					  get_column(a, i),
    264 					  get_column(b, i));
    265 
    266       ir_dereference *const lhs =
    267 	 new(this->mem_ctx) ir_dereference_variable(tmp_bvec);
    268 
    269       ir_assignment *const assign =
    270 	 new(this->mem_ctx) ir_assignment(lhs, cmp, NULL, (1U << i));
    271 
    272       this->base_ir->insert_before(assign);
    273    }
    274 
    275    ir_rvalue *const val = new(this->mem_ctx) ir_dereference_variable(tmp_bvec);
    276    ir_expression *any = new(this->mem_ctx) ir_expression(ir_unop_any, val);
    277 
    278    if (test_equal)
    279       any = new(this->mem_ctx) ir_expression(ir_unop_logic_not, any);
    280 
    281    ir_assignment *const assign =
    282       new(mem_ctx) ir_assignment(result->clone(mem_ctx, NULL), any);
    283    base_ir->insert_before(assign);
    284 }
    285 
    286 static bool
    287 has_matrix_operand(const ir_expression *expr, unsigned &columns)
    288 {
    289    for (unsigned i = 0; i < expr->get_num_operands(); i++) {
    290       if (expr->operands[i]->type->is_matrix()) {
    291 	 columns = expr->operands[i]->type->matrix_columns;
    292 	 return true;
    293       }
    294    }
    295 
    296    return false;
    297 }
    298 
    299 
    300 ir_visitor_status
    301 ir_mat_op_to_vec_visitor::visit_leave(ir_assignment *orig_assign)
    302 {
    303    ir_expression *orig_expr = orig_assign->rhs->as_expression();
    304    unsigned int i, matrix_columns = 1;
    305    ir_dereference *op[2];
    306 
    307    if (!orig_expr)
    308       return visit_continue;
    309 
    310    if (!has_matrix_operand(orig_expr, matrix_columns))
    311       return visit_continue;
    312 
    313    assert(orig_expr->get_num_operands() <= 2);
    314 
    315    mem_ctx = ralloc_parent(orig_assign);
    316 
    317    ir_dereference_variable *result =
    318       orig_assign->lhs->as_dereference_variable();
    319    assert(result);
    320 
    321    /* Store the expression operands in temps so we can use them
    322     * multiple times.
    323     */
    324    for (i = 0; i < orig_expr->get_num_operands(); i++) {
    325       ir_assignment *assign;
    326       ir_dereference *deref = orig_expr->operands[i]->as_dereference();
    327 
    328       /* Avoid making a temporary if we don't need to to avoid aliasing. */
    329       if (deref &&
    330 	  deref->variable_referenced() != result->variable_referenced()) {
    331 	 op[i] = deref;
    332 	 continue;
    333       }
    334 
    335       /* Otherwise, store the operand in a temporary generally if it's
    336        * not a dereference.
    337        */
    338       ir_variable *var = new(mem_ctx) ir_variable(orig_expr->operands[i]->type,
    339 						  "mat_op_to_vec",
    340 						  ir_var_temporary);
    341       base_ir->insert_before(var);
    342 
    343       /* Note that we use this dereference for the assignment.  That means
    344        * that others that want to use op[i] have to clone the deref.
    345        */
    346       op[i] = new(mem_ctx) ir_dereference_variable(var);
    347       assign = new(mem_ctx) ir_assignment(op[i], orig_expr->operands[i]);
    348       base_ir->insert_before(assign);
    349    }
    350 
    351    /* OK, time to break down this matrix operation. */
    352    switch (orig_expr->operation) {
    353    case ir_unop_neg: {
    354       /* Apply the operation to each column.*/
    355       for (i = 0; i < matrix_columns; i++) {
    356 	 ir_expression *column_expr;
    357 	 ir_assignment *column_assign;
    358 
    359 	 column_expr = new(mem_ctx) ir_expression(orig_expr->operation,
    360 						  get_column(op[0], i));
    361 
    362 	 column_assign = new(mem_ctx) ir_assignment(get_column(result, i),
    363 						    column_expr);
    364 	 assert(column_assign->write_mask != 0);
    365 	 base_ir->insert_before(column_assign);
    366       }
    367       break;
    368    }
    369    case ir_binop_add:
    370    case ir_binop_sub:
    371    case ir_binop_div:
    372    case ir_binop_mod: {
    373       /* For most operations, the matrix version is just going
    374        * column-wise through and applying the operation to each column
    375        * if available.
    376        */
    377       for (i = 0; i < matrix_columns; i++) {
    378 	 ir_expression *column_expr;
    379 	 ir_assignment *column_assign;
    380 
    381 	 column_expr = new(mem_ctx) ir_expression(orig_expr->operation,
    382 						  get_column(op[0], i),
    383 						  get_column(op[1], i));
    384 
    385 	 column_assign = new(mem_ctx) ir_assignment(get_column(result, i),
    386 						    column_expr);
    387 	 assert(column_assign->write_mask != 0);
    388 	 base_ir->insert_before(column_assign);
    389       }
    390       break;
    391    }
    392    case ir_binop_mul:
    393       if (op[0]->type->is_matrix()) {
    394 	 if (op[1]->type->is_matrix()) {
    395 	    do_mul_mat_mat(result, op[0], op[1]);
    396 	 } else if (op[1]->type->is_vector()) {
    397 	    do_mul_mat_vec(result, op[0], op[1]);
    398 	 } else {
    399 	    assert(op[1]->type->is_scalar());
    400 	    do_mul_mat_scalar(result, op[0], op[1]);
    401 	 }
    402       } else {
    403 	 assert(op[1]->type->is_matrix());
    404 	 if (op[0]->type->is_vector()) {
    405 	    do_mul_vec_mat(result, op[0], op[1]);
    406 	 } else {
    407 	    assert(op[0]->type->is_scalar());
    408 	    do_mul_mat_scalar(result, op[1], op[0]);
    409 	 }
    410       }
    411       break;
    412 
    413    case ir_binop_all_equal:
    414    case ir_binop_any_nequal:
    415       do_equal_mat_mat(result, op[1], op[0],
    416 		       (orig_expr->operation == ir_binop_all_equal));
    417       break;
    418 
    419    default:
    420       printf("FINISHME: Handle matrix operation for %s\n",
    421 	     orig_expr->operator_string());
    422       abort();
    423    }
    424    orig_assign->remove();
    425    this->made_progress = true;
    426 
    427    return visit_continue;
    428 }
    429