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