glsl: Pack flat "varyings" of mixed types together.
authorPaul Berry <stereotype441@gmail.com>
Wed, 19 Dec 2012 00:37:52 +0000 (16:37 -0800)
committerPaul Berry <stereotype441@gmail.com>
Tue, 8 Jan 2013 17:18:14 +0000 (09:18 -0800)
This patch enhances the varying packing code so that flat varyings of
uint, int, and float types can be packed together.

We accomplish this in lower_packed_varyings.cpp by making the type of
all flat varyings ivec4, and then using information-preserving type
conversions (e.g. ir_unop_bitcast_f2i) to convert all other types to
ints.

The varying_matches::compute_packing_class() function is updated to
reflect the fact that varying packing no longer needs to segregate
varyings of different base types.

Fixes piglit test varying-packing-mixed-types.

Reviewed-by: Kenneth Graunke <kenneth@whitecape.org>
v2: Split lower_packed_varyings_visitor::bitwise_assign into
pack/unpack variants.

src/glsl/link_varyings.cpp
src/glsl/lower_packed_varyings.cpp

index b9c3f5d434d71fd9bff44214b23e211b916296ff..5c27f231e729edb6146c26b83c7bf4719570b36c 100644 (file)
@@ -777,17 +777,25 @@ varying_matches::store_locations(unsigned producer_base,
 unsigned
 varying_matches::compute_packing_class(ir_variable *var)
 {
-   /* In this initial implementation we conservatively assume that variables
-    * can only be packed if their base type (float/int/uint/bool) matches and
-    * their interpolation and centroid qualifiers match.
+   /* Without help from the back-end, there is no way to pack together
+    * variables with different interpolation types, because
+    * lower_packed_varyings must choose exactly one interpolation type for
+    * each packed varying it creates.
     *
-    * TODO: relax these restrictions when the driver back-end permits.
+    * However, we can safely pack together floats, ints, and uints, because:
+    *
+    * - varyings of base type "int" and "uint" must use the "flat"
+    *   interpolation type, which can only occur in GLSL 1.30 and above.
+    *
+    * - On platforms that support GLSL 1.30 and above, lower_packed_varyings
+    *   can store flat floats as ints without losing any information (using
+    *   the ir_unop_bitcast_* opcodes).
+    *
+    * Therefore, the packing class depends only on the interpolation type.
     */
    unsigned packing_class = var->centroid ? 1 : 0;
    packing_class *= 4;
    packing_class += var->interpolation;
-   packing_class *= GLSL_TYPE_ERROR;
-   packing_class += var->type->get_scalar_type()->base_type;
    return packing_class;
 }
 
index 09c551c4e63151c745aa111da08694c3da15a2e6..9e7f274b7d1b43ef7cc0fff51f5bc0b8f7ad933a 100644 (file)
  * performance.  However, hopefully in most cases the performance loss will
  * either be absorbed by a later optimization pass, or it will be offset by
  * memory bandwidth savings (because fewer varyings are used).
+ *
+ * This lowering pass also packs flat floats, ints, and uints together, by
+ * using ivec4 as the base type of flat "varyings", and using appropriate
+ * casts to convert floats and uints into ints.
  */
 
 #include "glsl_symbol_table.h"
@@ -90,6 +94,8 @@ public:
    void run(exec_list *instructions);
 
 private:
+   ir_assignment *bitwise_assign_pack(ir_rvalue *lhs, ir_rvalue *rhs);
+   ir_assignment *bitwise_assign_unpack(ir_rvalue *lhs, ir_rvalue *rhs);
    unsigned lower_rvalue(ir_rvalue *rvalue, unsigned fine_location,
                          ir_variable *unpacked_var, const char *name);
    unsigned lower_arraylike(ir_rvalue *rvalue, unsigned array_size,
@@ -181,6 +187,75 @@ lower_packed_varyings_visitor::run(exec_list *instructions)
    }
 }
 
+
+/**
+ * Make an ir_assignment from \c rhs to \c lhs, performing appropriate
+ * bitcasts if necessary to match up types.
+ *
+ * This function is called when packing varyings.
+ */
+ir_assignment *
+lower_packed_varyings_visitor::bitwise_assign_pack(ir_rvalue *lhs,
+                                                   ir_rvalue *rhs)
+{
+   if (lhs->type->base_type != rhs->type->base_type) {
+      /* Since we only mix types in flat varyings, and we always store flat
+       * varyings as type ivec4, we need only produce conversions from (uint
+       * or float) to int.
+       */
+      assert(lhs->type->base_type == GLSL_TYPE_INT);
+      switch (rhs->type->base_type) {
+      case GLSL_TYPE_UINT:
+         rhs = new(this->mem_ctx)
+            ir_expression(ir_unop_u2i, lhs->type, rhs);
+         break;
+      case GLSL_TYPE_FLOAT:
+         rhs = new(this->mem_ctx)
+            ir_expression(ir_unop_bitcast_f2i, lhs->type, rhs);
+         break;
+      default:
+         assert(!"Unexpected type conversion while lowering varyings");
+         break;
+      }
+   }
+   return new(this->mem_ctx) ir_assignment(lhs, rhs);
+}
+
+
+/**
+ * Make an ir_assignment from \c rhs to \c lhs, performing appropriate
+ * bitcasts if necessary to match up types.
+ *
+ * This function is called when unpacking varyings.
+ */
+ir_assignment *
+lower_packed_varyings_visitor::bitwise_assign_unpack(ir_rvalue *lhs,
+                                                     ir_rvalue *rhs)
+{
+   if (lhs->type->base_type != rhs->type->base_type) {
+      /* Since we only mix types in flat varyings, and we always store flat
+       * varyings as type ivec4, we need only produce conversions from int to
+       * (uint or float).
+       */
+      assert(rhs->type->base_type == GLSL_TYPE_INT);
+      switch (lhs->type->base_type) {
+      case GLSL_TYPE_UINT:
+         rhs = new(this->mem_ctx)
+            ir_expression(ir_unop_i2u, lhs->type, rhs);
+         break;
+      case GLSL_TYPE_FLOAT:
+         rhs = new(this->mem_ctx)
+            ir_expression(ir_unop_bitcast_i2f, lhs->type, rhs);
+         break;
+      default:
+         assert(!"Unexpected type conversion while lowering varyings");
+         break;
+      }
+   }
+   return new(this->mem_ctx) ir_assignment(lhs, rhs);
+}
+
+
 /**
  * Recursively pack or unpack the given varying (or portion of a varying) by
  * traversing all of its constituent vectors.
@@ -262,12 +337,12 @@ lower_packed_varyings_visitor::lower_rvalue(ir_rvalue *rvalue,
       ir_swizzle *swizzle = new(this->mem_ctx)
          ir_swizzle(packed_deref, swizzle_values, components);
       if (this->mode == ir_var_out) {
-         ir_assignment *assignment = new(this->mem_ctx)
-            ir_assignment(swizzle, rvalue);
+         ir_assignment *assignment
+            = this->bitwise_assign_pack(swizzle, rvalue);
          this->main_instructions->push_tail(assignment);
       } else {
-         ir_assignment *assignment = new(this->mem_ctx)
-            ir_assignment(rvalue, swizzle);
+         ir_assignment *assignment
+            = this->bitwise_assign_unpack(rvalue, swizzle);
          this->main_instructions->push_head(assignment);
       }
       return fine_location + components;
@@ -306,8 +381,9 @@ lower_packed_varyings_visitor::lower_arraylike(ir_rvalue *rvalue,
  * If no packed varying has been created for the given varying location yet,
  * create it and add it to the shader before returning it.
  *
- * The newly created varying inherits its base type (float, uint, or int) and
- * interpolation parameters from \c unpacked_var.
+ * The newly created varying inherits its interpolation parameters from \c
+ * unpacked_var.  Its base type is ivec4 if we are lowering a flat varying,
+ * vec4 otherwise.
  */
 ir_variable *
 lower_packed_varyings_visitor::get_packed_varying(unsigned location,
@@ -318,8 +394,11 @@ lower_packed_varyings_visitor::get_packed_varying(unsigned location,
    assert(slot < locations_used);
    if (this->packed_varyings[slot] == NULL) {
       char *packed_name = ralloc_asprintf(this->mem_ctx, "packed:%s", name);
-      const glsl_type *packed_type = glsl_type::get_instance(
-            unpacked_var->type->get_scalar_type()->base_type, 4, 1);
+      const glsl_type *packed_type;
+      if (unpacked_var->interpolation == INTERP_QUALIFIER_FLAT)
+         packed_type = glsl_type::ivec4_type;
+      else
+         packed_type = glsl_type::vec4_type;
       ir_variable *packed_var = new(this->mem_ctx)
          ir_variable(packed_type, packed_name, this->mode);
       packed_var->centroid = unpacked_var->centroid;