glsl2: Add a new pass at the IR level to break down matrix ops to vector ops.
authorEric Anholt <eric@anholt.net>
Mon, 12 Jul 2010 18:04:07 +0000 (11:04 -0700)
committerEric Anholt <eric@anholt.net>
Mon, 12 Jul 2010 20:26:46 +0000 (13:26 -0700)
This will be used by the Mesa IR and likely most HW backends, as it
allows other optimizations to occur that might not otherwise.

Fixes glsl-vs-mat-sub-1, glsl-vs-mat-div-1.

src/glsl/Makefile
src/glsl/ir.h
src/glsl/ir_mat_op_to_vec.cpp [new file with mode: 0644]
src/glsl/ir_optimization.h
src/mesa/shader/ir_to_mesa.cpp

index ddc9d82d61fb5e8cfea05521e327efde7a99adee..a36ff28a4be3010c82ab3856353b6bd8a75d4bac 100644 (file)
@@ -42,6 +42,7 @@ CXX_SOURCES = \
        ir_hv_accept.cpp \
        ir_if_return.cpp \
        ir_if_simplification.cpp \
+       ir_mat_op_to_vec.cpp \
        ir_mod_to_fract.cpp \
        ir_print_visitor.cpp \
        ir_reader.cpp \
index 500a8c7a0060ac37055c55dc2355410dab402574..0d5bbc20aa82d67d5e5029d5e38db9d278a753b7 100644 (file)
@@ -74,6 +74,7 @@ public:
    virtual class ir_dereference *       as_dereference()      { return NULL; }
    virtual class ir_dereference_array *        as_dereference_array() { return NULL; }
    virtual class ir_dereference_variable *as_dereference_variable() { return NULL; }
+   virtual class ir_expression *        as_expression()       { return NULL; }
    virtual class ir_rvalue *            as_rvalue()           { return NULL; }
    virtual class ir_loop *              as_loop()             { return NULL; }
    virtual class ir_assignment *        as_assignment()       { return NULL; }
@@ -603,6 +604,11 @@ public:
    ir_expression(int op, const struct glsl_type *type,
                 ir_rvalue *, ir_rvalue *);
 
+   virtual ir_expression *as_expression()
+   {
+      return this;
+   }
+
    virtual ir_expression *clone(struct hash_table *ht) const;
 
    static unsigned int get_num_operands(ir_expression_operation);
diff --git a/src/glsl/ir_mat_op_to_vec.cpp b/src/glsl/ir_mat_op_to_vec.cpp
new file mode 100644 (file)
index 0000000..828c63c
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * Copyright © 2010 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+/**
+ * \file ir_mat_op_to_vec.cpp
+ *
+ * Breaks matrix operation expressions down to a series of vector operations.
+ *
+ * Generally this is how we have to codegen matrix operations for a
+ * GPU, so this gives us the chance to constant fold operations on a
+ * column or row.
+ */
+
+#include "ir.h"
+#include "ir_expression_flattening.h"
+#include "glsl_types.h"
+
+class ir_mat_op_to_vec_visitor : public ir_hierarchical_visitor {
+public:
+   ir_mat_op_to_vec_visitor()
+   {
+      this->made_progress = false;
+   }
+
+   ir_visitor_status visit_leave(ir_assignment *);
+
+   ir_rvalue *get_column(ir_variable *var, int i);
+
+   bool made_progress;
+};
+
+static bool
+mat_op_to_vec_predicate(ir_instruction *ir)
+{
+   ir_expression *expr = ir->as_expression();
+   unsigned int i;
+
+   if (!expr)
+      return false;
+
+   for (i = 0; i < expr->get_num_operands(); i++) {
+      if (expr->operands[i]->type->is_matrix())
+        return true;
+   }
+
+   return false;
+}
+
+bool
+do_mat_op_to_vec(exec_list *instructions)
+{
+   ir_mat_op_to_vec_visitor v;
+
+   /* Pull out any matrix expression to a separate assignment to a
+    * temp.  This will make our handling of the breakdown to
+    * operations on the matrix's vector components much easier.
+    */
+   do_expression_flattening(instructions, mat_op_to_vec_predicate);
+
+   visit_list_elements(&v, instructions);
+
+   return v.made_progress;
+}
+
+ir_rvalue *
+ir_mat_op_to_vec_visitor::get_column(ir_variable *var, int i)
+{
+   ir_dereference *deref;
+
+   if (!var->type->is_matrix()) {
+      deref = new(base_ir) ir_dereference_variable(var);
+   } else {
+      deref = new(base_ir) ir_dereference_variable(var);
+      deref = new(base_ir) ir_dereference_array(deref,
+                                               new(base_ir) ir_constant(i));
+   }
+
+   return deref;
+}
+
+ir_visitor_status
+ir_mat_op_to_vec_visitor::visit_leave(ir_assignment *assign)
+{
+   ir_expression *expr = assign->rhs->as_expression();
+   bool found_matrix = false;
+   unsigned int i, matrix_columns = 1;
+   ir_variable *op_var[2];
+
+   if (!expr)
+      return visit_continue;
+
+   for (i = 0; i < expr->get_num_operands(); i++) {
+      if (expr->operands[i]->type->is_matrix()) {
+        found_matrix = true;
+        matrix_columns = expr->operands[i]->type->matrix_columns;
+        break;
+      }
+   }
+   if (!found_matrix)
+      return visit_continue;
+
+   /* FINISHME: see below */
+   if (expr->operation == ir_binop_mul)
+      return visit_continue;
+
+   ir_dereference_variable *lhs_deref = assign->lhs->as_dereference_variable();
+   assert(lhs_deref);
+
+   ir_variable *result_var = lhs_deref->var;
+
+   /* Store the expression operands in temps so we can use them
+    * multiple times.
+    */
+   for (i = 0; i < expr->get_num_operands(); i++) {
+      ir_assignment *assign;
+
+      op_var[i] = new(base_ir) ir_variable(expr->operands[i]->type,
+                                          "mat_op_to_vec");
+      base_ir->insert_before(op_var[i]);
+
+      lhs_deref = new(base_ir) ir_dereference_variable(op_var[i]);
+      assign = new(base_ir) ir_assignment(lhs_deref,
+                                         expr->operands[i],
+                                         NULL);
+      base_ir->insert_before(assign);
+   }
+
+   /* OK, time to break down this matrix operation. */
+   switch (expr->operation) {
+   case ir_binop_add:
+   case ir_binop_sub:
+   case ir_binop_div:
+   case ir_binop_mod:
+      /* For most operations, the matrix version is just going
+       * column-wise through and applying the operation to each column
+       * if available.
+       */
+      for (i = 0; i < matrix_columns; i++) {
+        ir_rvalue *op0 = get_column(op_var[0], i);
+        ir_rvalue *op1 = get_column(op_var[1], i);
+        ir_rvalue *result = get_column(result_var, i);
+        ir_expression *column_expr;
+        ir_assignment *column_assign;
+
+        column_expr = new(base_ir) ir_expression(expr->operation,
+                                                 result->type,
+                                                 op0,
+                                                 op1);
+
+        column_assign = new(base_ir) ir_assignment(result,
+                                                   column_expr,
+                                                   NULL);
+        base_ir->insert_before(column_assign);
+      }
+      break;
+   case ir_binop_mul:
+      /* FINISHME */
+      return visit_continue;
+      break;
+   default:
+      printf("FINISHME: Handle matrix operation for %s\n", expr->operator_string());
+      abort();
+   }
+   assign->remove();
+   this->made_progress = true;
+
+   return visit_continue;
+}
index b03c0644cf0ac371457b092569671141e1559e87..fae583df75698527a12c9c047e5d85048a3d7147 100644 (file)
@@ -41,6 +41,7 @@ bool do_div_to_mul_rcp(exec_list *instructions);
 bool do_function_inlining(exec_list *instructions);
 bool do_if_return(exec_list *instructions);
 bool do_if_simplification(exec_list *instructions);
+bool do_mat_op_to_vec(exec_list *instructions);
 bool do_mod_to_fract(exec_list *instructions);
 bool do_swizzle_swizzle(exec_list *instructions);
 bool do_vec_index_to_cond_assign(exec_list *instructions);
index 708c6fece1c1fe9e4ad72a2da7176c5657418132..81b91918cb08a1e74567bb0bf3a035d69d25047e 100644 (file)
@@ -1960,6 +1960,7 @@ _mesa_glsl_compile_shader(GLcontext *ctx, struct gl_shader *shader)
       _mesa_ast_to_hir(shader->ir, state);
 
    /* Lowering */
+   do_mat_op_to_vec(shader->ir);
    do_mod_to_fract(shader->ir);
    do_div_to_mul_rcp(shader->ir);