Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
4358 Serge 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
   unsigned 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
   unsigned 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
   unsigned 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
   unsigned 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
}