<div class="gmail_quote">On 7 November 2011 22:28, Paul Berry <span dir="ltr">&lt;<a href="mailto:stereotype441@gmail.com">stereotype441@gmail.com</a>&gt;</span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;">
<div class="im">This patch modifies the GLSL linker to assign additional slots for<br>
varying variables used by transform feedback, and record the varying<br>
slots used by transform feedback for use by the driver back-end.<br>
<br>
This required modifying assign_varying_locations() so that it assigns<br>
a varying location if either (a) the varying is used by the next stage<br>
of the GL pipeline, or (b) the varying is required by transform<br>
feedback.  In order to avoid duplicating the code to assign a single<br>
varying location, I moved it into its own function,<br>
assign_varying_location().<br>
<br>
</div>In addition, to support transform feedback in the case where there is<br>
no fragment shader, it is now possible to call<br>
assign_varying_locations() with a consumer of NULL.<br>
---<br>
Changes from v1:<br>
<br>
- Fixed loop bound in tfeedback_decl::store() (was this-&gt;vector_elements,<br>
  should have been this-&gt;matrix_columns).<br>
<br>
- Fixed the case where transform feedback is in use but there is no fragment<br>
  shader.<br>
<br>
 src/glsl/linker.cpp    |  552 ++++++++++++++++++++++++++++++++++++++++++------<br>
 src/mesa/main/mtypes.h |   13 ++<br>
 2 files changed, 502 insertions(+), 63 deletions(-)<br>
<br>
diff --git a/src/glsl/linker.cpp b/src/glsl/linker.cpp<br>
index 915d5bb..5cccd7f 100644<br>
--- a/src/glsl/linker.cpp<br>
+++ b/src/glsl/linker.cpp<br>
@@ -1519,10 +1519,358 @@ demote_shader_inputs_and_outputs(gl_shader *sh, enum ir_variable_mode mode)<br>
<div class="im"> }<br>
<br>
<br>
+/**<br>
+ * Data structure tracking information about a transform feedback declaration<br>
+ * during linking.<br>
+ */<br>
+class tfeedback_decl<br>
+{<br>
+public:<br>
+   bool init(struct gl_shader_program *prog, const void *mem_ctx,<br>
+             const char *input);<br>
+   static bool is_same(const tfeedback_decl &amp;x, const tfeedback_decl &amp;y);<br>
+   bool assign_location(struct gl_context *ctx, struct gl_shader_program *prog,<br>
+                        ir_variable *output_var);<br>
</div>+   bool store(struct gl_shader_program *prog,<br>
+              struct gl_transform_feedback_info *info, unsigned buffer) const;<br>
+<br>
+<br>
<div><div class="h5">+   /**<br>
+    * True if assign_location() has been called for this object.<br>
+    */<br>
+   bool is_assigned() const<br>
+   {<br>
+      return this-&gt;location != -1;<br>
+   }<br>
+<br>
+   /**<br>
+    * Determine whether this object refers to the variable var.<br>
+    */<br>
+   bool matches_var(ir_variable *var) const<br>
+   {<br>
+      return strcmp(var-&gt;name, this-&gt;var_name) == 0;<br>
+   }<br>
+<br>
+   /**<br>
+    * The total number of varying components taken up by this variable.  Only<br>
+    * valid if is_assigned() is true.<br>
+    */<br>
+   unsigned num_components() const<br>
+   {<br>
+      return this-&gt;vector_elements * this-&gt;matrix_columns;<br>
+   }<br>
+<br>
+private:<br>
+   /**<br>
+    * The name that was supplied to glTransformFeedbackVaryings.  Used for<br>
+    * error reporting.<br>
+    */<br>
+   const char *orig_name;<br>
+<br>
+   /**<br>
+    * The name of the variable, parsed from orig_name.<br>
+    */<br>
+   char *var_name;<br>
+<br>
+   /**<br>
+    * True if the declaration in orig_name represents an array.<br>
+    */<br>
+   bool is_array;<br>
+<br>
+   /**<br>
+    * If is_array is true, the array index that was specified in orig_name.<br>
+    */<br>
+   unsigned array_index;<br>
+<br>
+   /**<br>
+    * The vertex shader output location that the linker assigned for this<br>
+    * variable.  -1 if a location hasn&#39;t been assigned yet.<br>
+    */<br>
+   int location;<br>
+<br>
+   /**<br>
+    * If location != -1, the number of vector elements in this variable, or 1<br>
+    * if this variable is a scalar.<br>
+    */<br>
+   unsigned vector_elements;<br>
+<br>
+   /**<br>
+    * If location != -1, the number of matrix columns in this variable, or 1<br>
+    * if this variable is not a matrix.<br>
+    */<br>
+   unsigned matrix_columns;<br>
+};<br>
+<br>
+<br>
+/**<br>
+ * Initialize this object based on a string that was passed to<br>
+ * glTransformFeedbackVaryings.  If there is a parse error, the error is<br>
</div></div>+ * reported using linker_error(), and false is returned.<br>
+ */<br>
+bool<br>
<div><div class="h5">+tfeedback_decl::init(struct gl_shader_program *prog, const void *mem_ctx,<br>
+                     const char *input)<br>
+{<br>
+   /* We don&#39;t have to be pedantic about what is a valid GLSL variable name,<br>
+    * because any variable with an invalid name can&#39;t exist in the IR anyway.<br>
+    */<br>
+<br>
+   this-&gt;location = -1;<br>
+   this-&gt;orig_name = input;<br>
+<br>
+   const char *bracket = strrchr(input, &#39;[&#39;);<br>
+<br>
+   if (bracket) {<br>
+      this-&gt;var_name = ralloc_strndup(mem_ctx, input, bracket - input);<br>
+      if (sscanf(bracket, &quot;[%u]&quot;, &amp;this-&gt;array_index) == 1) {<br>
+         this-&gt;is_array = true;<br>
+         return true;<br>
+      }<br>
+   } else {<br>
+      this-&gt;var_name = ralloc_strdup(mem_ctx, input);<br>
+      this-&gt;is_array = false;<br>
+      return true;<br>
+   }<br>
+<br>
+   linker_error(prog, &quot;Cannot parse transform feedback varying %s&quot;, input);<br>
</div></div><div class="im">+   return false;<br>
+}<br>
+<br>
</div><div class="im">+<br>
+/**<br>
+ * Determine whether two tfeedback_decl objects refer to the same variable and<br>
+ * array index (if applicable).<br>
+ */<br>
+bool<br>
+tfeedback_decl::is_same(const tfeedback_decl &amp;x, const tfeedback_decl &amp;y)<br>
+{<br>
+   if (strcmp(x.var_name, y.var_name) != 0)<br>
+      return false;<br>
+   if (x.is_array != y.is_array)<br>
+      return false;<br>
+   if (x.is_array &amp;&amp; x.array_index != y.array_index)<br>
+      return false;<br>
+   return true;<br>
+}<br>
+<br>
+<br>
+/**<br>
+ * Assign a location for this tfeedback_decl object based on the location<br>
+ * assignment in output_var.<br>
</div><div class="im">+ *<br>
+ * If an error occurs, the error is reported through linker_error() and false<br>
+ * is returned.<br>
+ */<br>
+bool<br>
</div><div class="im">+tfeedback_decl::assign_location(struct gl_context *ctx,<br>
+                                struct gl_shader_program *prog,<br>
+                                ir_variable *output_var)<br>
+{<br>
+   if (output_var-&gt;type-&gt;is_array()) {<br>
+      /* Array variable */<br>
+      if (!this-&gt;is_array) {<br>
+         linker_error(prog, &quot;Transform feedback varying %s found, &quot;<br>
</div>+                      &quot;but it&#39;s not an array ([] not expected).&quot;,<br>
<div class="im">+                      this-&gt;orig_name);<br>
+         return false;<br>
+      }<br>
</div><div class="im">+      /* Check array bounds. */<br>
+      if (this-&gt;array_index &gt;=<br>
+          (unsigned) output_var-&gt;type-&gt;array_size()) {<br>
+         linker_error(prog, &quot;Transform feedback varying %s has index &quot;<br>
+                      &quot;%i, but the array size is %i.&quot;,<br>
+                      this-&gt;orig_name, this-&gt;array_index,<br>
+                      output_var-&gt;type-&gt;array_size());<br></div></blockquote><div><br></div><div>I just noticed that a &quot;return false;&quot; is missing right here.  I&#39;ll fix this before pushing the patch tomorrow morning.</div>
<div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;"><div class="im">
+      }<br>
+      const unsigned matrix_cols =<br>
+         output_var-&gt;type-&gt;fields.array-&gt;matrix_columns;<br>
+      this-&gt;location = output_var-&gt;location + this-&gt;array_index * matrix_cols;<br>
+      this-&gt;vector_elements = output_var-&gt;type-&gt;fields.array-&gt;vector_elements;<br>
+      this-&gt;matrix_columns = matrix_cols;<br>
+   } else {<br>
+      /* Regular variable (scalar, vector, or matrix) */<br>
+      if (this-&gt;is_array) {<br>
+         linker_error(prog, &quot;Transform feedback varying %s found, &quot;<br>
</div>+                      &quot;but it&#39;s an array ([] expected).&quot;,<br>
<div class="im">+                      this-&gt;orig_name);<br>
+         return false;<br>
+      }<br>
</div><div class="im">+      this-&gt;location = output_var-&gt;location;<br>
+      this-&gt;vector_elements = output_var-&gt;type-&gt;vector_elements;<br>
+      this-&gt;matrix_columns = output_var-&gt;type-&gt;matrix_columns;<br>
+   }<br>
</div><div class="im">+   /* From GL_EXT_transform_feedback:<br>
+    *   A program will fail to link if:<br>
+    *<br>
</div><div class="im">+    *   * the total number of components to capture in any varying<br>
+    *     variable in &lt;varyings&gt; is greater than the constant<br>
+    *     MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS_EXT and the<br>
+    *     buffer mode is SEPARATE_ATTRIBS_EXT;<br>
</div>+    */<br>
<div class="im">+   if (prog-&gt;TransformFeedback.BufferMode == GL_SEPARATE_ATTRIBS &amp;&amp;<br>
+       this-&gt;num_components() &gt;<br>
+       ctx-&gt;Const.MaxTransformFeedbackSeparateComponents) {<br>
</div>+      linker_error(prog, &quot;Transform feedback varying %s exceeds &quot;<br>
+                   &quot;MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS.&quot;,<br>
<div class="im">+                   this-&gt;orig_name);<br>
+      return false;<br>
+   }<br>
+<br>
</div><div class="im">+   return true;<br>
+}<br>
+<br>
+<br>
</div><div class="im">+/**<br>
+ * Update gl_transform_feedback_info to reflect this tfeedback_decl.<br>
+ *<br>
+ * If an error occurs, the error is reported through linker_error() and false<br>
+ * is returned.<br>
+ */<br>
+bool<br>
+tfeedback_decl::store(struct gl_shader_program *prog,<br>
+                      struct gl_transform_feedback_info *info,<br>
+                      unsigned buffer) const<br>
+{<br>
+   if (!this-&gt;is_assigned()) {<br>
</div><div class="im">+      /* From GL_EXT_transform_feedback:<br>
+       *   A program will fail to link if:<br>
+       *<br>
</div><div class="im">+       *   * any variable name specified in the &lt;varyings&gt; array is not<br>
+       *     declared as an output in the geometry shader (if present) or<br>
+       *     the vertex shader (if no geometry shader is present);<br>
+       */<br>
+      linker_error(prog, &quot;Transform feedback varying %s undeclared.&quot;,<br>
+                   this-&gt;orig_name);<br>
+      return false;<br>
+   }<br>
</div>+   for (unsigned v = 0; v &lt; this-&gt;matrix_columns; ++v) {<br>
<div class="im">+      info-&gt;Outputs[info-&gt;NumOutputs].OutputRegister = this-&gt;location + v;<br>
+      info-&gt;Outputs[info-&gt;NumOutputs].NumComponents = this-&gt;vector_elements;<br>
+      info-&gt;Outputs[info-&gt;NumOutputs].OutputBuffer = buffer;<br>
+      ++info-&gt;NumOutputs;<br>
+   }<br>
+   return true;<br>
+}<br>
</div><div class="im">+<br>
+<br>
+/**<br>
+ * Parse all the transform feedback declarations that were passed to<br>
+ * glTransformFeedbackVaryings() and store them in tfeedback_decl objects.<br>
</div><div class="im">+ *<br>
+ * If an error occurs, the error is reported through linker_error() and false<br>
+ * is returned.<br>
+ */<br>
</div><div class="im">+static bool<br>
+parse_tfeedback_decls(struct gl_shader_program *prog, const void *mem_ctx,<br>
+                      unsigned num_names, char **varying_names,<br>
+                      tfeedback_decl *decls)<br>
</div>+{<br>
<div class="im">+   for (unsigned i = 0; i &lt; num_names; ++i) {<br>
+      if (!decls[i].init(prog, mem_ctx, varying_names[i]))<br>
+         return false;<br>
</div><div class="im">+      /* From GL_EXT_transform_feedback:<br>
+       *   A program will fail to link if:<br>
+       *<br>
</div><div class="im">+       *   * any two entries in the &lt;varyings&gt; array specify the same varying<br>
+       *     variable;<br>
+       *<br>
+       * We interpret this to mean &quot;any two entries in the &lt;varyings&gt; array<br>
+       * specify the same varying variable and array index&quot;, since transform<br>
+       * feedback of arrays would be useless otherwise.<br>
+       */<br>
+      for (unsigned j = 0; j &lt; i; ++j) {<br>
+         if (tfeedback_decl::is_same(decls[i], decls[j])) {<br>
+            linker_error(prog, &quot;Transform feedback varying %s specified &quot;<br>
+                         &quot;more than once.&quot;, varying_names[i]);<br>
+         }<br>
</div><div class="im">+         return false;<br>
+      }<br>
+   }<br>
</div><div class="im">+   return true;<br>
+}<br>
+<br>
+<br>
+/**<br>
+ * Assign a location for a variable that is produced in one pipeline stage<br>
+ * (the &quot;producer&quot;) and consumed in the next stage (the &quot;consumer&quot;).<br>
+ *<br>
+ * \param input_var is the input variable declaration in the consumer.<br>
+ *<br>
+ * \param output_var is the output variable declaration in the producer.<br>
+ *<br>
+ * \param input_index is the counter that keeps track of assigned input<br>
+ *        locations in the consumer.<br>
+ *<br>
+ * \param output_index is the counter that keeps track of assigned output<br>
+ *        locations in the producer.<br>
+ *<br>
+ * It is permissible for \c input_var to be NULL (this happens if a variable<br>
+ * is output by the producer and consumed by transform feedback, but not<br>
+ * consumed by the consumer).<br>
</div>+ *<br>
<div><div class="h5">+ * If the variable has already been assigned a location, this function has no<br>
+ * effect.<br>
+ */<br>
+void<br>
+assign_varying_location(ir_variable *input_var, ir_variable *output_var,<br>
+                        unsigned *input_index, unsigned *output_index)<br>
+{<br>
+   if (output_var-&gt;location != -1) {<br>
+      /* Location already assigned. */<br>
+      return;<br>
+   }<br>
+<br>
+   if (input_var) {<br>
+      assert(input_var-&gt;location == -1);<br>
+      input_var-&gt;location = *input_index;<br>
+   }<br>
+<br>
+   output_var-&gt;location = *output_index;<br>
+<br>
+   /* FINISHME: Support for &quot;varying&quot; records in GLSL 1.50. */<br>
+   assert(!output_var-&gt;type-&gt;is_record());<br>
+<br>
+   if (output_var-&gt;type-&gt;is_array()) {<br>
+      const unsigned slots = output_var-&gt;type-&gt;length<br>
+         * output_var-&gt;type-&gt;fields.array-&gt;matrix_columns;<br>
+<br>
+      *output_index += slots;<br>
+      *input_index += slots;<br>
+   } else {<br>
+      const unsigned slots = output_var-&gt;type-&gt;matrix_columns;<br>
+<br>
+      *output_index += slots;<br>
+      *input_index += slots;<br>
+   }<br>
+}<br>
+<br>
+<br>
+/**<br>
+ * Assign locations for all variables that are produced in one pipeline stage<br>
+ * (the &quot;producer&quot;) and consumed in the next stage (the &quot;consumer&quot;).<br>
+ *<br>
+ * Variables produced by the producer may also be consumed by transform<br>
+ * feedback.<br>
+ *<br>
+ * \param num_tfeedback_decls is the number of declarations indicating<br>
+ *        variables that may be consumed by transform feedback.<br>
+ *<br>
+ * \param tfeedback_decls is a pointer to an array of tfeedback_decl objects<br>
+ *        representing the result of parsing the strings passed to<br>
+ *        glTransformFeedbackVaryings().  assign_location() will be called for<br>
+ *        each of these objects that matches one of the outputs of the<br>
+ *        producer.<br>
+ *<br>
</div></div>+ * When num_tfeedback_decls is nonzero, it is permissible for the consumer to<br>
+ * be NULL.  In this case, varying locations are assigned solely based on the<br>
+ * requirements of transform feedback.<br>
<div class="im">+ */<br>
 bool<br>
 assign_varying_locations(struct gl_context *ctx,<br>
                         struct gl_shader_program *prog,<br>
-                        gl_shader *producer, gl_shader *consumer)<br>
+                        gl_shader *producer, gl_shader *consumer,<br>
+                         unsigned num_tfeedback_decls,<br>
+                         tfeedback_decl *tfeedback_decls)<br>
 {<br>
    /* FINISHME: Set dynamically when geometry shader support is added. */<br>
    unsigned output_index = VERT_RESULT_VAR0;<br>
</div>@@ -1540,79 +1888,77 @@ assign_varying_locations(struct gl_context *ctx,<br>
     */<br>
<br>
    invalidate_variable_locations(producer, ir_var_out, VERT_RESULT_VAR0);<br>
-   invalidate_variable_locations(consumer, ir_var_in, FRAG_ATTRIB_VAR0);<br>
+   if (consumer)<br>
+      invalidate_variable_locations(consumer, ir_var_in, FRAG_ATTRIB_VAR0);<br>
<div class="im"><br>
    foreach_list(node, producer-&gt;ir) {<br>
       ir_variable *const output_var = ((ir_instruction *) node)-&gt;as_variable();<br>
<br>
-      if ((output_var == NULL) || (output_var-&gt;mode != ir_var_out)<br>
-         || (output_var-&gt;location != -1))<br>
+      if ((output_var == NULL) || (output_var-&gt;mode != ir_var_out))<br>
         continue;<br>
<br>
-      ir_variable *const input_var =<br>
</div>-        consumer-&gt;symbols-&gt;get_variable(output_var-&gt;name);<br>
<div class="im">-<br>
-      if ((input_var == NULL) || (input_var-&gt;mode != ir_var_in))<br>
-        continue;<br>
-<br>
-      assert(input_var-&gt;location == -1);<br>
-<br>
-      output_var-&gt;location = output_index;<br>
-      input_var-&gt;location = input_index;<br>
-<br>
-      /* FINISHME: Support for &quot;varying&quot; records in GLSL 1.50. */<br>
-      assert(!output_var-&gt;type-&gt;is_record());<br>
</div>+      ir_variable *input_var =<br>
+        consumer ? consumer-&gt;symbols-&gt;get_variable(output_var-&gt;name) : NULL;<br>
<div class="im"><br>
-      if (output_var-&gt;type-&gt;is_array()) {<br>
-        const unsigned slots = output_var-&gt;type-&gt;length<br>
-           * output_var-&gt;type-&gt;fields.array-&gt;matrix_columns;<br>
+      if (input_var &amp;&amp; input_var-&gt;mode != ir_var_in)<br>
+         input_var = NULL;<br>
<br>
</div><div class="im">-        output_index += slots;<br>
-        input_index += slots;<br>
-      } else {<br>
-        const unsigned slots = output_var-&gt;type-&gt;matrix_columns;<br>
+      if (input_var) {<br>
+         assign_varying_location(input_var, output_var, &amp;input_index,<br>
+                                 &amp;output_index);<br>
+      }<br>
<br>
-        output_index += slots;<br>
-        input_index += slots;<br>
+      for (unsigned i = 0; i &lt; num_tfeedback_decls; ++i) {<br>
+         if (!tfeedback_decls[i].is_assigned() &amp;&amp;<br>
+             tfeedback_decls[i].matches_var(output_var)) {<br>
+            if (output_var-&gt;location == -1) {<br>
+               assign_varying_location(input_var, output_var, &amp;input_index,<br>
+                                       &amp;output_index);<br>
+            }<br>
+            if (!tfeedback_decls[i].assign_location(ctx, prog, output_var))<br>
+               return false;<br>
+         }<br>
       }<br>
    }<br>
<br>
</div>    unsigned varying_vectors = 0;<br>
<br>
-   foreach_list(node, consumer-&gt;ir) {<br>
-      ir_variable *const var = ((ir_instruction *) node)-&gt;as_variable();<br>
-<br>
-      if ((var == NULL) || (var-&gt;mode != ir_var_in))<br>
-        continue;<br>
-<br>
-      if (var-&gt;location == -1) {<br>
-        if (prog-&gt;Version &lt;= 120) {<br>
-           /* On page 25 (page 31 of the PDF) of the GLSL 1.20 spec:<br>
-            *<br>
-            *     Only those varying variables used (i.e. read) in<br>
-            *     the fragment shader executable must be written to<br>
-            *     by the vertex shader executable; declaring<br>
-            *     superfluous varying variables in a vertex shader is<br>
-            *     permissible.<br>
-            *<br>
-            * We interpret this text as meaning that the VS must<br>
-            * write the variable for the FS to read it.  See<br>
-            * &quot;glsl1-varying read but not written&quot; in piglit.<br>
-            */<br>
-<br>
-           linker_error(prog, &quot;fragment shader varying %s not written &quot;<br>
-                        &quot;by vertex shader\n.&quot;, var-&gt;name);<br>
-        }<br>
+   if (consumer) {<br>
+      foreach_list(node, consumer-&gt;ir) {<br>
+         ir_variable *const var = ((ir_instruction *) node)-&gt;as_variable();<br>
+<br>
+         if ((var == NULL) || (var-&gt;mode != ir_var_in))<br>
+            continue;<br>
+<br>
+         if (var-&gt;location == -1) {<br>
+            if (prog-&gt;Version &lt;= 120) {<br>
+               /* On page 25 (page 31 of the PDF) of the GLSL 1.20 spec:<br>
+                *<br>
+                *     Only those varying variables used (i.e. read) in<br>
+                *     the fragment shader executable must be written to<br>
+                *     by the vertex shader executable; declaring<br>
+                *     superfluous varying variables in a vertex shader is<br>
+                *     permissible.<br>
+                *<br>
+                * We interpret this text as meaning that the VS must<br>
+                * write the variable for the FS to read it.  See<br>
+                * &quot;glsl1-varying read but not written&quot; in piglit.<br>
+                */<br>
+<br>
+               linker_error(prog, &quot;fragment shader varying %s not written &quot;<br>
+                            &quot;by vertex shader\n.&quot;, var-&gt;name);<br>
+            }<br>
<br>
-        /* An &#39;in&#39; variable is only really a shader input if its<br>
-         * value is written by the previous stage.<br>
-         */<br>
-        var-&gt;mode = ir_var_auto;<br>
-      } else {<br>
-        /* The packing rules are used for vertex shader inputs are also used<br>
-         * for fragment shader inputs.<br>
-         */<br>
-        varying_vectors += count_attribute_slots(var-&gt;type);<br>
+            /* An &#39;in&#39; variable is only really a shader input if its<br>
+             * value is written by the previous stage.<br>
+             */<br>
+            var-&gt;mode = ir_var_auto;<br>
+         } else {<br>
+            /* The packing rules are used for vertex shader inputs are also<br>
+             * used for fragment shader inputs.<br>
+             */<br>
+            varying_vectors += count_attribute_slots(var-&gt;type);<br>
+         }<br>
       }<br>
    }<br>
<br>
@@ -1637,9 +1983,54 @@ assign_varying_locations(struct gl_context *ctx,<br>
<div class="im"> }<br>
<br>
<br>
+/**<br>
+ * Store transform feedback location assignments into<br>
+ * prog-&gt;LinkedTransformFeedback based on the data stored in tfeedback_decls.<br>
</div><div class="im">+ *<br>
+ * If an error occurs, the error is reported through linker_error() and false<br>
+ * is returned.<br>
+ */<br>
</div><div class="im">+static bool<br>
+store_tfeedback_info(struct gl_context *ctx, struct gl_shader_program *prog,<br>
+                     unsigned num_tfeedback_decls,<br>
+                     tfeedback_decl *tfeedback_decls)<br>
+{<br>
+   unsigned total_tfeedback_components = 0;<br>
+   prog-&gt;LinkedTransformFeedback.NumOutputs = 0;<br>
+   for (unsigned i = 0; i &lt; num_tfeedback_decls; ++i) {<br>
+      unsigned buffer =<br>
+         prog-&gt;TransformFeedback.BufferMode == GL_SEPARATE_ATTRIBS ? i : 0;<br>
+      if (!tfeedback_decls[i].store(prog, &amp;prog-&gt;LinkedTransformFeedback,<br>
+                                    buffer))<br>
+         return false;<br>
+      total_tfeedback_components += tfeedback_decls[i].num_components();<br>
+   }<br>
+<br>
</div><div class="im">+   /* From GL_EXT_transform_feedback:<br>
+    *   A program will fail to link if:<br>
+    *<br>
</div><div class="im">+    *     * the total number of components to capture is greater than<br>
+    *       the constant MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS_EXT<br>
+    *       and the buffer mode is INTERLEAVED_ATTRIBS_EXT.<br>
</div>+    */<br>
<div class="im">+   if (prog-&gt;TransformFeedback.BufferMode == GL_INTERLEAVED_ATTRIBS &amp;&amp;<br>
+       total_tfeedback_components &gt;<br>
+       ctx-&gt;Const.MaxTransformFeedbackInterleavedComponents) {<br>
+      linker_error(prog, &quot;The MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS &quot;<br>
+                   &quot;limit has been exceeded.&quot;);<br>
</div><div class="im">+      return false;<br>
+   }<br>
+<br>
</div><div class="im">+   return true;<br>
+}<br>
+<br>
+<br>
 void<br>
</div><div class="im"> link_shaders(struct gl_context *ctx, struct gl_shader_program *prog)<br>
</div><div class="im"> {<br>
+   tfeedback_decl *tfeedback_decls = NULL;<br>
</div>+   unsigned num_tfeedback_decls = prog-&gt;TransformFeedback.NumVarying;<br>
+<br>
    void *mem_ctx = ralloc_context(NULL); // temporary linker context<br>
<br>
    prog-&gt;LinkStatus = false;<br>
@@ -1806,19 +2197,54 @@ link_shaders(struct gl_context *ctx, struct gl_shader_program *prog)<br>
         break;<br>
    }<br>
<br>
+   if (num_tfeedback_decls != 0) {<br>
<div class="im">+      /* From GL_EXT_transform_feedback:<br>
+       *   A program will fail to link if:<br>
+       *<br>
+       *   * the &lt;count&gt; specified by TransformFeedbackVaryingsEXT is<br>
+       *     non-zero, but the program object has no vertex or geometry<br>
+       *     shader;<br>
+       */<br>
</div>+      if (prev &gt;= MESA_SHADER_FRAGMENT) {<br>
<div class="im">+         linker_error(prog, &quot;Transform feedback varyings specified, but &quot;<br>
+                      &quot;no vertex or geometry shader is present.&quot;);<br>
+         goto done;<br>
+      }<br>
+<br>
</div><div class="im">+      tfeedback_decls = ralloc_array(mem_ctx, tfeedback_decl,<br>
+                                     prog-&gt;TransformFeedback.NumVarying);<br>
+      if (!parse_tfeedback_decls(prog, mem_ctx, num_tfeedback_decls,<br>
+                                 prog-&gt;TransformFeedback.VaryingNames,<br>
+                                 tfeedback_decls))<br>
+         goto done;<br>
+   }<br>
+<br>
</div><div class="im">    for (unsigned i = prev + 1; i &lt; MESA_SHADER_TYPES; i++) {<br>
       if (prog-&gt;_LinkedShaders[i] == NULL)<br>
         continue;<br>
<br>
</div>-      if (!assign_varying_locations(ctx, prog,<br>
-                                   prog-&gt;_LinkedShaders[prev],<br>
<div class="im">-                                   prog-&gt;_LinkedShaders[i])) {<br>
</div>+      if (!assign_varying_locations(<br>
+             ctx, prog, prog-&gt;_LinkedShaders[prev], prog-&gt;_LinkedShaders[i],<br>
+             i == MESA_SHADER_FRAGMENT ? num_tfeedback_decls : 0,<br>
+             tfeedback_decls))<br>
         goto done;<br>
-      }<br>
<br>
       prev = i;<br>
    }<br>
<br>
+   if (prev != MESA_SHADER_FRAGMENT &amp;&amp; num_tfeedback_decls != 0) {<br>
+      /* There was no fragment shader, but we still have to assign varying<br>
+       * locations for use by transform feedback.<br>
+       */<br>
+      if (!assign_varying_locations(<br>
+             ctx, prog, prog-&gt;_LinkedShaders[prev], NULL, num_tfeedback_decls,<br>
<div class="im">+             tfeedback_decls))<br>
+         goto done;<br>
+   }<br>
+<br>
</div>+   if (!store_tfeedback_info(ctx, prog, num_tfeedback_decls, tfeedback_decls))<br>
+      goto done;<br>
+<br>
    if (prog-&gt;_LinkedShaders[MESA_SHADER_VERTEX] != NULL) {<br>
       demote_shader_inputs_and_outputs(prog-&gt;_LinkedShaders[MESA_SHADER_VERTEX],<br>
                                       ir_var_out);<br>
<div class="HOEnZb"><div class="h5">diff --git a/src/mesa/main/mtypes.h b/src/mesa/main/mtypes.h<br>
index 6190972..5df898c 100644<br>
--- a/src/mesa/main/mtypes.h<br>
+++ b/src/mesa/main/mtypes.h<br>
@@ -1818,6 +1818,16 @@ struct prog_instruction;<br>
 struct gl_program_parameter_list;<br>
 struct gl_uniform_list;<br>
<br>
+/** Post-link transform feedback info. */<br>
+struct gl_transform_feedback_info {<br>
+   unsigned NumOutputs;<br>
+<br>
+   struct {<br>
+      unsigned OutputRegister;<br>
+      unsigned OutputBuffer;<br>
+      unsigned NumComponents;<br>
+   } Outputs[MAX_PROGRAM_OUTPUTS];<br>
+};<br>
<br>
 /**<br>
  * Base class for any kind of program object<br>
@@ -2191,6 +2201,9 @@ struct gl_shader_program<br>
       GLchar **VaryingNames;  /**&lt; Array [NumVarying] of char * */<br>
    } TransformFeedback;<br>
<br>
+   /** Post-link transform feedback info. */<br>
+   struct gl_transform_feedback_info LinkedTransformFeedback;<br>
+<br>
    /** Geometry shader state - copied into gl_geometry_program at link time */<br>
    struct {<br>
       GLint VerticesOut;<br>
--<br>
1.7.6.4<br>
<br>
</div></div></blockquote></div><br>