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