glsl: Fix write mask in matrix-from-matrix constructors.
[mesa.git] / src / glsl / ir_mat_op_to_vec.cpp
index 828c63c17a7cae13f113b46212ac6a8847c9feec..da6de9492b09c1ea59c9a9a61404a71018819413 100644 (file)
@@ -40,12 +40,26 @@ public:
    ir_mat_op_to_vec_visitor()
    {
       this->made_progress = false;
+      this->mem_ctx = NULL;
    }
 
    ir_visitor_status visit_leave(ir_assignment *);
 
-   ir_rvalue *get_column(ir_variable *var, int i);
+   ir_dereference *get_column(ir_variable *var, int col);
+   ir_rvalue *get_element(ir_variable *var, int col, int row);
 
+   void do_mul_mat_mat(ir_variable *result_var,
+                      ir_variable *a_var, ir_variable *b_var);
+   void do_mul_mat_vec(ir_variable *result_var,
+                      ir_variable *a_var, ir_variable *b_var);
+   void do_mul_vec_mat(ir_variable *result_var,
+                      ir_variable *a_var, ir_variable *b_var);
+   void do_mul_mat_scalar(ir_variable *result_var,
+                         ir_variable *a_var, ir_variable *b_var);
+   void do_equal_mat_mat(ir_variable *result_var, ir_variable *a_var,
+                        ir_variable *b_var, bool test_equal);
+
+   void *mem_ctx;
    bool made_progress;
 };
 
@@ -83,47 +97,282 @@ do_mat_op_to_vec(exec_list *instructions)
 }
 
 ir_rvalue *
-ir_mat_op_to_vec_visitor::get_column(ir_variable *var, int i)
+ir_mat_op_to_vec_visitor::get_element(ir_variable *var, int col, int row)
+{
+   ir_dereference *deref;
+
+   deref = new(mem_ctx) ir_dereference_variable(var);
+
+   if (var->type->is_matrix()) {
+      deref = new(mem_ctx) ir_dereference_array(var,
+                                               new(mem_ctx) ir_constant(col));
+   } else {
+      assert(col == 0);
+   }
+
+   return new(mem_ctx) ir_swizzle(deref, row, 0, 0, 0, 1);
+}
+
+ir_dereference *
+ir_mat_op_to_vec_visitor::get_column(ir_variable *var, int row)
 {
    ir_dereference *deref;
 
    if (!var->type->is_matrix()) {
-      deref = new(base_ir) ir_dereference_variable(var);
+      deref = new(mem_ctx) 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));
+      deref = new(mem_ctx) ir_dereference_variable(var);
+      deref = new(mem_ctx) ir_dereference_array(deref,
+                                               new(mem_ctx) ir_constant(row));
    }
 
    return deref;
 }
 
-ir_visitor_status
-ir_mat_op_to_vec_visitor::visit_leave(ir_assignment *assign)
+void
+ir_mat_op_to_vec_visitor::do_mul_mat_mat(ir_variable *result_var,
+                                        ir_variable *a_var,
+                                        ir_variable *b_var)
 {
-   ir_expression *expr = assign->rhs->as_expression();
-   bool found_matrix = false;
-   unsigned int i, matrix_columns = 1;
-   ir_variable *op_var[2];
+   int b_col, i;
+   ir_assignment *assign;
+   ir_expression *expr;
 
-   if (!expr)
-      return visit_continue;
+   for (b_col = 0; b_col < b_var->type->matrix_columns; b_col++) {
+      ir_rvalue *a = get_column(a_var, 0);
+      ir_rvalue *b = get_element(b_var, b_col, 0);
 
-   for (i = 0; i < expr->get_num_operands(); i++) {
+      /* first column */
+      expr = new(mem_ctx) ir_expression(ir_binop_mul,
+                                       a->type,
+                                       a,
+                                       b);
+
+      /* following columns */
+      for (i = 1; i < a_var->type->matrix_columns; i++) {
+        ir_expression *mul_expr;
+
+        a = get_column(a_var, i);
+        b = get_element(b_var, b_col, i);
+
+        mul_expr = new(mem_ctx) ir_expression(ir_binop_mul,
+                                              a->type,
+                                              a,
+                                              b);
+        expr = new(mem_ctx) ir_expression(ir_binop_add,
+                                          a->type,
+                                          expr,
+                                          mul_expr);
+      }
+
+      ir_rvalue *result = get_column(result_var, b_col);
+      assign = new(mem_ctx) ir_assignment(result,
+                                         expr,
+                                         NULL);
+      base_ir->insert_before(assign);
+   }
+}
+
+void
+ir_mat_op_to_vec_visitor::do_mul_mat_vec(ir_variable *result_var,
+                                        ir_variable *a_var,
+                                        ir_variable *b_var)
+{
+   int i;
+   ir_rvalue *a = get_column(a_var, 0);
+   ir_rvalue *b = get_element(b_var, 0, 0);
+   ir_assignment *assign;
+   ir_expression *expr;
+
+   /* first column */
+   expr = new(mem_ctx) ir_expression(ir_binop_mul,
+                                    result_var->type,
+                                    a,
+                                    b);
+
+   /* following columns */
+   for (i = 1; i < a_var->type->matrix_columns; i++) {
+      ir_expression *mul_expr;
+
+      a = get_column(a_var, i);
+      b = get_element(b_var, 0, i);
+
+      mul_expr = new(mem_ctx) ir_expression(ir_binop_mul,
+                                           result_var->type,
+                                           a,
+                                           b);
+      expr = new(mem_ctx) ir_expression(ir_binop_add,
+                                       result_var->type,
+                                       expr,
+                                       mul_expr);
+   }
+
+   ir_rvalue *result = new(mem_ctx) ir_dereference_variable(result_var);
+   assign = new(mem_ctx) ir_assignment(result,
+                                      expr,
+                                      NULL);
+   base_ir->insert_before(assign);
+}
+
+void
+ir_mat_op_to_vec_visitor::do_mul_vec_mat(ir_variable *result_var,
+                                        ir_variable *a_var,
+                                        ir_variable *b_var)
+{
+   int i;
+
+   for (i = 0; i < b_var->type->matrix_columns; i++) {
+      ir_rvalue *a = new(mem_ctx) ir_dereference_variable(a_var);
+      ir_rvalue *b = get_column(b_var, i);
+      ir_rvalue *result;
+      ir_expression *column_expr;
+      ir_assignment *column_assign;
+
+      result = new(mem_ctx) ir_dereference_variable(result_var);
+      result = new(mem_ctx) ir_swizzle(result, i, 0, 0, 0, 1);
+
+      column_expr = new(mem_ctx) ir_expression(ir_binop_dot,
+                                              result->type,
+                                              a,
+                                              b);
+
+      column_assign = new(mem_ctx) ir_assignment(result,
+                                                column_expr,
+                                                NULL);
+      base_ir->insert_before(column_assign);
+   }
+}
+
+void
+ir_mat_op_to_vec_visitor::do_mul_mat_scalar(ir_variable *result_var,
+                                           ir_variable *a_var,
+                                           ir_variable *b_var)
+{
+   int i;
+
+   for (i = 0; i < a_var->type->matrix_columns; i++) {
+      ir_rvalue *a = get_column(a_var, i);
+      ir_rvalue *b = new(mem_ctx) ir_dereference_variable(b_var);
+      ir_rvalue *result = get_column(result_var, i);
+      ir_expression *column_expr;
+      ir_assignment *column_assign;
+
+      column_expr = new(mem_ctx) ir_expression(ir_binop_mul,
+                                              result->type,
+                                              a,
+                                              b);
+
+      column_assign = new(mem_ctx) ir_assignment(result,
+                                                column_expr,
+                                                NULL);
+      base_ir->insert_before(column_assign);
+   }
+}
+
+void
+ir_mat_op_to_vec_visitor::do_equal_mat_mat(ir_variable *result_var,
+                                          ir_variable *a_var,
+                                          ir_variable *b_var,
+                                          bool test_equal)
+{
+   /* This essentially implements the following GLSL:
+    *
+    * bool equal(mat4 a, mat4 b)
+    * {
+    *   return !any(bvec4(a[0] != b[0],
+    *                     a[1] != b[1],
+    *                     a[2] != b[2],
+    *                     a[3] != b[3]);
+    * }
+    *
+    * bool nequal(mat4 a, mat4 b)
+    * {
+    *   return any(bvec4(a[0] != b[0],
+    *                    a[1] != b[1],
+    *                    a[2] != b[2],
+    *                    a[3] != b[3]);
+    * }
+    */
+   const unsigned columns = a_var->type->matrix_columns;
+   const glsl_type *const bvec_type =
+      glsl_type::get_instance(GLSL_TYPE_BOOL, columns, 1);
+
+   ir_variable *const tmp_bvec =
+      new(this->mem_ctx) ir_variable(bvec_type, "mat_cmp_bvec",
+                                    ir_var_temporary);
+   this->base_ir->insert_before(tmp_bvec);
+
+   for (unsigned i = 0; i < columns; i++) {
+      ir_dereference *const op0 = get_column(a_var, i);
+      ir_dereference *const op1 = get_column(b_var, i);
+
+      ir_expression *const cmp =
+        new(this->mem_ctx) ir_expression(ir_binop_nequal,
+                                         glsl_type::bool_type, op0, op1);
+
+      ir_rvalue *const swiz =
+        new(this->mem_ctx) ir_swizzle(cmp, i, i, i, i, columns);
+
+      ir_dereference *const lhs =
+        new(this->mem_ctx) ir_dereference_variable(tmp_bvec);
+
+      ir_assignment *const assign =
+        new(this->mem_ctx) ir_assignment(lhs, swiz, NULL, (1U << i));
+
+      this->base_ir->insert_before(assign);
+   }
+
+   ir_rvalue *const val =
+      new(this->mem_ctx) ir_dereference_variable(tmp_bvec);
+
+   ir_expression *any =
+      new(this->mem_ctx) ir_expression(ir_unop_any, glsl_type::bool_type,
+                                      val, NULL);
+
+   if (test_equal)
+      any = new(this->mem_ctx) ir_expression(ir_unop_logic_not,
+                                            glsl_type::bool_type,
+                                            any, NULL);
+
+   ir_rvalue *const result =
+      new(this->mem_ctx) ir_dereference_variable(result_var);
+
+   ir_assignment *const assign =
+        new(mem_ctx) ir_assignment(result, any, NULL);
+   base_ir->insert_before(assign);
+}
+
+static bool
+has_matrix_operand(const ir_expression *expr, unsigned &columns)
+{
+   for (unsigned 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;
+        columns = expr->operands[i]->type->matrix_columns;
+        return true;
       }
    }
-   if (!found_matrix)
+
+   return false;
+}
+
+
+ir_visitor_status
+ir_mat_op_to_vec_visitor::visit_leave(ir_assignment *orig_assign)
+{
+   ir_expression *orig_expr = orig_assign->rhs->as_expression();
+   unsigned int i, matrix_columns = 1;
+   ir_variable *op_var[2];
+
+   if (!orig_expr)
       return visit_continue;
 
-   /* FINISHME: see below */
-   if (expr->operation == ir_binop_mul)
+   if (!has_matrix_operand(orig_expr, matrix_columns))
       return visit_continue;
 
-   ir_dereference_variable *lhs_deref = assign->lhs->as_dereference_variable();
+   mem_ctx = talloc_parent(orig_assign);
+
+   ir_dereference_variable *lhs_deref =
+      orig_assign->lhs->as_dereference_variable();
    assert(lhs_deref);
 
    ir_variable *result_var = lhs_deref->var;
@@ -131,26 +380,53 @@ ir_mat_op_to_vec_visitor::visit_leave(ir_assignment *assign)
    /* Store the expression operands in temps so we can use them
     * multiple times.
     */
-   for (i = 0; i < expr->get_num_operands(); i++) {
+   for (i = 0; i < orig_expr->get_num_operands(); i++) {
       ir_assignment *assign;
 
-      op_var[i] = new(base_ir) ir_variable(expr->operands[i]->type,
-                                          "mat_op_to_vec");
+      op_var[i] = new(mem_ctx) ir_variable(orig_expr->operands[i]->type,
+                                          "mat_op_to_vec",
+                                          ir_var_temporary);
       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],
+      lhs_deref = new(mem_ctx) ir_dereference_variable(op_var[i]);
+      assign = new(mem_ctx) ir_assignment(lhs_deref,
+                                         orig_expr->operands[i],
                                          NULL);
       base_ir->insert_before(assign);
    }
 
    /* OK, time to break down this matrix operation. */
-   switch (expr->operation) {
+   switch (orig_expr->operation) {
+   case ir_unop_neg: {
+      const unsigned mask = (1U << result_var->type->vector_elements) - 1;
+
+      /* Apply the operation to each column.*/
+      for (i = 0; i < matrix_columns; i++) {
+        ir_rvalue *op0 = get_column(op_var[0], i);
+        ir_dereference *result = get_column(result_var, i);
+        ir_expression *column_expr;
+        ir_assignment *column_assign;
+
+        column_expr = new(mem_ctx) ir_expression(orig_expr->operation,
+                                                 result->type,
+                                                 op0,
+                                                 NULL);
+
+        column_assign = new(mem_ctx) ir_assignment(result,
+                                                   column_expr,
+                                                   NULL,
+                                                   mask);
+        assert(column_assign->write_mask != 0);
+        base_ir->insert_before(column_assign);
+      }
+      break;
+   }
    case ir_binop_add:
    case ir_binop_sub:
    case ir_binop_div:
-   case ir_binop_mod:
+   case ir_binop_mod: {
+      const unsigned mask = (1U << result_var->type->vector_elements) - 1;
+
       /* For most operations, the matrix version is just going
        * column-wise through and applying the operation to each column
        * if available.
@@ -158,30 +434,57 @@ ir_mat_op_to_vec_visitor::visit_leave(ir_assignment *assign)
       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_dereference *result = get_column(result_var, i);
         ir_expression *column_expr;
         ir_assignment *column_assign;
 
-        column_expr = new(base_ir) ir_expression(expr->operation,
+        column_expr = new(mem_ctx) ir_expression(orig_expr->operation,
                                                  result->type,
                                                  op0,
                                                  op1);
 
-        column_assign = new(base_ir) ir_assignment(result,
+        column_assign = new(mem_ctx) ir_assignment(result,
                                                    column_expr,
-                                                   NULL);
+                                                   NULL,
+                                                   mask);
+        assert(column_assign->write_mask != 0);
         base_ir->insert_before(column_assign);
       }
       break;
+   }
    case ir_binop_mul:
-      /* FINISHME */
-      return visit_continue;
+      if (op_var[0]->type->is_matrix()) {
+        if (op_var[1]->type->is_matrix()) {
+           do_mul_mat_mat(result_var, op_var[0], op_var[1]);
+        } else if (op_var[1]->type->is_vector()) {
+           do_mul_mat_vec(result_var, op_var[0], op_var[1]);
+        } else {
+           assert(op_var[1]->type->is_scalar());
+           do_mul_mat_scalar(result_var, op_var[0], op_var[1]);
+        }
+      } else {
+        assert(op_var[1]->type->is_matrix());
+        if (op_var[0]->type->is_vector()) {
+           do_mul_vec_mat(result_var, op_var[0], op_var[1]);
+        } else {
+           assert(op_var[0]->type->is_scalar());
+           do_mul_mat_scalar(result_var, op_var[1], op_var[0]);
+        }
+      }
       break;
+
+   case ir_binop_equal:
+   case ir_binop_nequal:
+      do_equal_mat_mat(result_var, op_var[1], op_var[0],
+                      (orig_expr->operation == ir_binop_equal));
+      break;
+
    default:
-      printf("FINISHME: Handle matrix operation for %s\n", expr->operator_string());
+      printf("FINISHME: Handle matrix operation for %s\n",
+            orig_expr->operator_string());
       abort();
    }
-   assign->remove();
+   orig_assign->remove();
    this->made_progress = true;
 
    return visit_continue;