glsl/linker: Subdivide the first phase of varying assignment.
authorPaul Berry <stereotype441@gmail.com>
Tue, 4 Dec 2012 23:55:59 +0000 (15:55 -0800)
committerPaul Berry <stereotype441@gmail.com>
Fri, 14 Dec 2012 18:49:08 +0000 (10:49 -0800)
This patch further subdivides the loop that assigns varying locations
into two phases: one phase to match up the varyings between shader
stages, and one phase to assign them varying locations.

In between the two phases the matched varyings are stored in a new
data structure called varying_matches.  This will free us to be able
to assign varying locations in any order, which will pave the way for
packing varyings.

Note that the new varying_matches::assign_locations() function returns
the number of varying slots that were used; this return value will be
used in a future patch.

Reviewed-by: Eric Anholt <eric@anholt.net>
src/glsl/linker.cpp

index e4d31d79d69223bf3a1dd15528caafa2cb77dac1..439548ee8d6de785ba2d9002ff1b659ba52715c2 100644 (file)
@@ -1961,58 +1961,167 @@ parse_tfeedback_decls(struct gl_context *ctx, struct gl_shader_program *prog,
 
 
 /**
- * Assign a location for a variable that is produced in one pipeline stage
- * (the "producer") and consumed in the next stage (the "consumer").
- *
- * \param input_var is the input variable declaration in the consumer.
- *
- * \param output_var is the output variable declaration in the producer.
- *
- * \param input_index is the counter that keeps track of assigned input
- *        locations in the consumer.
- *
- * \param output_index is the counter that keeps track of assigned output
- *        locations in the producer.
+ * Data structure recording the relationship between outputs of one shader
+ * stage (the "producer") and inputs of another (the "consumer").
+ */
+class varying_matches
+{
+public:
+   varying_matches();
+   ~varying_matches();
+   void record(ir_variable *producer_var, ir_variable *consumer_var);
+   unsigned assign_locations();
+   void store_locations(unsigned producer_base, unsigned consumer_base) const;
+
+private:
+   /**
+    * Structure recording the relationship between a single producer output
+    * and a single consumer input.
+    */
+   struct match {
+      /**
+       * The output variable in the producer stage.
+       */
+      ir_variable *producer_var;
+
+      /**
+       * The input variable in the consumer stage.
+       */
+      ir_variable *consumer_var;
+
+      /**
+       * The location which has been assigned for this varying.  This is
+       * expressed in multiples of a float, with the first generic varying
+       * (i.e. the one referred to by VERT_RESULT_VAR0 or FRAG_ATTRIB_VAR0)
+       * represented by the value 0.
+       */
+      unsigned generic_location;
+   } *matches;
+
+   /**
+    * The number of elements in the \c matches array that are currently in
+    * use.
+    */
+   unsigned num_matches;
+
+   /**
+    * The number of elements that were set aside for the \c matches array when
+    * it was allocated.
+    */
+   unsigned matches_capacity;
+};
+
+
+varying_matches::varying_matches()
+{
+   /* Note: this initial capacity is rather arbitrarily chosen to be large
+    * enough for many cases without wasting an unreasonable amount of space.
+    * varying_matches::record() will resize the array if there are more than
+    * this number of varyings.
+    */
+   this->matches_capacity = 8;
+   this->matches = (match *)
+      malloc(sizeof(*this->matches) * this->matches_capacity);
+   this->num_matches = 0;
+}
+
+
+varying_matches::~varying_matches()
+{
+   free(this->matches);
+}
+
+
+/**
+ * Record the given producer/consumer variable pair in the list of variables
+ * that should later be assigned locations.
  *
- * It is permissible for \c input_var to be NULL (this happens if a variable
- * is output by the producer and consumed by transform feedback, but not
- * consumed by the consumer).
+ * It is permissible for \c consumer_var to be NULL (this happens if a
+ * variable is output by the producer and consumed by transform feedback, but
+ * not consumed by the consumer).
  *
- * If the variable has already been assigned a location, this function has no
- * effect.
+ * If \c producer_var has already been paired up with a consumer_var, or
+ * producer_var is part of fixed pipeline functionality (and hence already has
+ * a location assigned), this function has no effect.
  */
 void
-assign_varying_location(ir_variable *input_var, ir_variable *output_var,
-                        unsigned *input_index, unsigned *output_index)
+varying_matches::record(ir_variable *producer_var, ir_variable *consumer_var)
 {
-   if (!output_var->is_unmatched_generic_inout) {
-      /* Location already assigned. */
+   if (!producer_var->is_unmatched_generic_inout) {
+      /* Either a location already exists for this variable (since it is part
+       * of fixed functionality), or it has already been recorded as part of a
+       * previous match.
+       */
       return;
    }
 
-   if (input_var) {
-      assert(input_var->location == -1);
-      input_var->location = *input_index;
-      input_var->is_unmatched_generic_inout = 0;
+   if (this->num_matches == this->matches_capacity) {
+      this->matches_capacity *= 2;
+      this->matches = (match *)
+         realloc(this->matches,
+                 sizeof(*this->matches) * this->matches_capacity);
    }
+   this->matches[this->num_matches].producer_var = producer_var;
+   this->matches[this->num_matches].consumer_var = consumer_var;
+   this->num_matches++;
+   producer_var->is_unmatched_generic_inout = 0;
+   if (consumer_var)
+      consumer_var->is_unmatched_generic_inout = 0;
+}
 
-   output_var->location = *output_index;
-   output_var->is_unmatched_generic_inout = 0;
 
-   /* FINISHME: Support for "varying" records in GLSL 1.50. */
-   assert(!output_var->type->is_record());
+/**
+ * Choose locations for all of the variable matches that were previously
+ * passed to varying_matches::record().
+ */
+unsigned
+varying_matches::assign_locations()
+{
+   unsigned generic_location = 0;
 
-   if (output_var->type->is_array()) {
-      const unsigned slots = output_var->type->length
-         * output_var->type->fields.array->matrix_columns;
+   for (unsigned i = 0; i < this->num_matches; i++) {
+      this->matches[i].generic_location = generic_location;
 
-      *output_index += slots;
-      *input_index += slots;
-   } else {
-      const unsigned slots = output_var->type->matrix_columns;
+      ir_variable *producer_var = this->matches[i].producer_var;
+
+      if (producer_var->type->is_array()) {
+         const unsigned slots = producer_var->type->length
+            * producer_var->type->fields.array->matrix_columns;
+
+         generic_location += 4 * slots;
+      } else {
+         const unsigned slots = producer_var->type->matrix_columns;
 
-      *output_index += slots;
-      *input_index += slots;
+         generic_location += 4 * slots;
+      }
+   }
+
+   return (generic_location + 3) / 4;
+}
+
+
+/**
+ * Update the producer and consumer shaders to reflect the locations
+ * assignments that were made by varying_matches::assign_locations().
+ */
+void
+varying_matches::store_locations(unsigned producer_base,
+                                 unsigned consumer_base) const
+{
+   for (unsigned i = 0; i < this->num_matches; i++) {
+      ir_variable *producer_var = this->matches[i].producer_var;
+      ir_variable *consumer_var = this->matches[i].consumer_var;
+      unsigned generic_location = this->matches[i].generic_location;
+      unsigned slot = generic_location / 4;
+      unsigned offset = generic_location % 4;
+
+      producer_var->location = producer_base + slot;
+      producer_var->location_frac = offset;
+      if (consumer_var) {
+         assert(consumer_var->location == -1);
+         consumer_var->location = consumer_base + slot;
+         consumer_var->location_frac = offset;
+      }
    }
 }
 
@@ -2070,8 +2179,9 @@ assign_varying_locations(struct gl_context *ctx,
                          tfeedback_decl *tfeedback_decls)
 {
    /* FINISHME: Set dynamically when geometry shader support is added. */
-   unsigned output_index = VERT_RESULT_VAR0;
-   unsigned input_index = FRAG_ATTRIB_VAR0;
+   const unsigned producer_base = VERT_RESULT_VAR0;
+   const unsigned consumer_base = FRAG_ATTRIB_VAR0;
+   varying_matches matches;
 
    /* Operate in a total of three passes.
     *
@@ -2097,8 +2207,7 @@ assign_varying_locations(struct gl_context *ctx,
          input_var = NULL;
 
       if (input_var) {
-         assign_varying_location(input_var, output_var, &input_index,
-                                 &output_index);
+         matches.record(output_var, input_var);
       }
    }
 
@@ -2113,9 +2222,19 @@ assign_varying_locations(struct gl_context *ctx,
          return false;
 
       if (output_var->is_unmatched_generic_inout) {
-         assign_varying_location(NULL, output_var, &input_index,
-                                 &output_index);
+         matches.record(output_var, NULL);
       }
+   }
+
+   matches.assign_locations();
+   matches.store_locations(producer_base, consumer_base);
+
+   for (unsigned i = 0; i < num_tfeedback_decls; ++i) {
+      if (!tfeedback_decls[i].is_varying())
+         continue;
+
+      ir_variable *output_var
+         = tfeedback_decls[i].find_output_var(prog, producer);
 
       if (!tfeedback_decls[i].assign_location(ctx, prog, output_var))
          return false;