Tizen 2.0 Release
[profile/ivi/osmesa.git] / src / glsl / lower_mat_op_to_vec.cpp
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 = ralloc_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 }