glsl2: Always emit vector constructors inline
authorIan Romanick <ian.d.romanick@intel.com>
Wed, 23 Jun 2010 22:19:40 +0000 (15:19 -0700)
committerIan Romanick <ian.d.romanick@intel.com>
Tue, 29 Jun 2010 18:15:26 +0000 (11:15 -0700)
src/glsl/ast_function.cpp

index f431d1d0156858ccca932dd8ec119ce380d08bdd..f1ab6f0c5a1c3d8597baddc8d9c4b4622c311121 100644 (file)
@@ -444,6 +444,98 @@ generate_constructor_vector(const glsl_type *type, ir_constant *initializer,
 }
 
 
+/**
+ * Determine if a list consists of a single scalar r-value
+ */
+bool
+single_scalar_parameter(exec_list *parameters)
+{
+   const ir_rvalue *const p = (ir_rvalue *) parameters->head;
+   assert(((ir_rvalue *)p)->as_rvalue() != NULL);
+
+   return (p->type->is_scalar() && p->next->is_tail_sentinal());
+}
+
+
+/**
+ * Generate inline code for a vector constructor
+ *
+ * The generated constructor code will consist of a temporary variable
+ * declaration of the same type as the constructor.  A sequence of assignments
+ * from constructor parameters to the temporary will follow.
+ *
+ * \return
+ * An \c ir_dereference_variable of the temprorary generated in the constructor
+ * body.
+ */
+ir_rvalue *
+emit_inline_vector_constructor(const glsl_type *type,
+                              exec_list *instructions,
+                              exec_list *parameters,
+                              void *ctx)
+{
+   assert(!parameters->is_empty());
+
+   ir_variable *var = new(ctx) ir_variable(type, strdup("vec_ctor"));
+   instructions->push_tail(var);
+
+   /* There are two kinds of vector constructors.
+    *
+    *  - Construct a vector from a single scalar by replicating that scalar to
+    *    all components of the vector.
+    *
+    *  - Construct a vector from an arbirary combination of vectors and
+    *    scalars.  The components of the constructor parameters are assigned
+    *    to the vector in order until the vector is full.
+    */
+   const unsigned lhs_components = type->components();
+   if (single_scalar_parameter(parameters)) {
+      ir_rvalue *first_param = (ir_rvalue *)parameters->head;
+      ir_rvalue *rhs = new(ctx) ir_swizzle(first_param, 0, 0, 0, 0,
+                                          lhs_components);
+      ir_dereference_variable *lhs = new(ctx) ir_dereference_variable(var);
+
+      assert(rhs->type == lhs->type);
+
+      ir_instruction *inst = new(ctx) ir_assignment(lhs, rhs, NULL);
+      instructions->push_tail(inst);
+   } else {
+      unsigned base_component = 0;
+      foreach_list(node, parameters) {
+        ir_rvalue *rhs = (ir_rvalue *) node;
+        unsigned rhs_components = rhs->type->components();
+
+        /* Do not try to assign more components to the vector than it has!
+         */
+        if ((rhs_components + base_component) > lhs_components) {
+           rhs_components = lhs_components - base_component;
+        }
+
+        /* Emit an assignment of the constructor parameter to the next set of
+         * components in the temporary variable.
+         */
+        unsigned mask[4] = { 0, 0, 0, 0 };
+        for (unsigned i = 0; i < rhs_components; i++) {
+           mask[i] = i + base_component;
+        }
+
+
+        ir_rvalue *lhs_ref = new(ctx) ir_dereference_variable(var);
+        ir_swizzle *lhs = new(ctx) ir_swizzle(lhs_ref, mask, rhs_components);
+
+        ir_instruction *inst = new(ctx) ir_assignment(lhs, rhs, NULL);
+        instructions->push_tail(inst);
+
+        /* Advance the component index by the number of components that were
+         * just assigned.
+         */
+        base_component += rhs_components;
+      }
+   }
+   return new(ctx) ir_dereference_variable(var);
+}
+
+
 ir_rvalue *
 ast_function_expression::hir(exec_list *instructions,
                             struct _mesa_glsl_parse_state *state)
@@ -708,8 +800,15 @@ ast_function_expression::hir(exec_list *instructions,
                                              &data);
 
               return new(ctx) ir_constant(sig->return_type, &data);
-           } else
+           } else if (constructor_type->is_vector()) {
+              return emit_inline_vector_constructor(constructor_type,
+                                                    instructions,
+                                                    &actual_parameters,
+                                                    ctx);
+           } else {
+              assert(constructor_type->is_matrix());
               return new(ctx) ir_call(sig, & actual_parameters);
+           }
         } else {
            /* FINISHME: Log a better error message here.  G++ will show the
             * FINSIHME: types of the actual parameters and the set of