<div class="gmail_quote">On 8 November 2011 05:15, Marek Olšák <span dir="ltr">&lt;<a href="mailto:maraeo@gmail.com">maraeo@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="HOEnZb"><div class="h5">On Tue, Nov 8, 2011 at 7:28 AM, Paul Berry &lt;<a href="mailto:stereotype441@gmail.com">stereotype441@gmail.com</a>&gt; wrote:<br>
&gt; This patch modifies the GLSL linker to assign additional slots for<br>
&gt; varying variables used by transform feedback, and record the varying<br>
&gt; slots used by transform feedback for use by the driver back-end.<br>
&gt;<br>
&gt; This required modifying assign_varying_locations() so that it assigns<br>
&gt; a varying location if either (a) the varying is used by the next stage<br>
&gt; of the GL pipeline, or (b) the varying is required by transform<br>
&gt; feedback.  In order to avoid duplicating the code to assign a single<br>
&gt; varying location, I moved it into its own function,<br>
&gt; assign_varying_location().<br>
&gt;<br>
&gt; In addition, to support transform feedback in the case where there is<br>
&gt; no fragment shader, it is now possible to call<br>
&gt; assign_varying_locations() with a consumer of NULL.<br>
&gt; ---<br>
&gt; Changes from v1:<br>
&gt;<br>
&gt; - Fixed loop bound in tfeedback_decl::store() (was this-&gt;vector_elements,<br>
&gt;  should have been this-&gt;matrix_columns).<br>
&gt;<br>
&gt; - Fixed the case where transform feedback is in use but there is no fragment<br>
&gt;  shader.<br>
&gt;<br>
&gt;  src/glsl/linker.cpp    |  552 ++++++++++++++++++++++++++++++++++++++++++------<br>
&gt;  src/mesa/main/mtypes.h |   13 ++<br>
&gt;  2 files changed, 502 insertions(+), 63 deletions(-)<br>
&gt;<br>
&gt; diff --git a/src/glsl/linker.cpp b/src/glsl/linker.cpp<br>
&gt; index 915d5bb..5cccd7f 100644<br>
&gt; --- a/src/glsl/linker.cpp<br>
&gt; +++ b/src/glsl/linker.cpp<br>
&gt; @@ -1519,10 +1519,358 @@ demote_shader_inputs_and_outputs(gl_shader *sh, enum ir_variable_mode mode)<br>
&gt;  }<br>
&gt;<br>
&gt;<br>
&gt; +/**<br>
&gt; + * Data structure tracking information about a transform feedback declaration<br>
&gt; + * during linking.<br>
&gt; + */<br>
&gt; +class tfeedback_decl<br>
&gt; +{<br>
&gt; +public:<br>
&gt; +   bool init(struct gl_shader_program *prog, const void *mem_ctx,<br>
&gt; +             const char *input);<br>
&gt; +   static bool is_same(const tfeedback_decl &amp;x, const tfeedback_decl &amp;y);<br>
&gt; +   bool assign_location(struct gl_context *ctx, struct gl_shader_program *prog,<br>
&gt; +                        ir_variable *output_var);<br>
&gt; +   bool store(struct gl_shader_program *prog,<br>
&gt; +              struct gl_transform_feedback_info *info, unsigned buffer) const;<br>
&gt; +<br>
&gt; +<br>
&gt; +   /**<br>
&gt; +    * True if assign_location() has been called for this object.<br>
&gt; +    */<br>
&gt; +   bool is_assigned() const<br>
&gt; +   {<br>
&gt; +      return this-&gt;location != -1;<br>
&gt; +   }<br>
&gt; +<br>
&gt; +   /**<br>
&gt; +    * Determine whether this object refers to the variable var.<br>
&gt; +    */<br>
&gt; +   bool matches_var(ir_variable *var) const<br>
&gt; +   {<br>
&gt; +      return strcmp(var-&gt;name, this-&gt;var_name) == 0;<br>
&gt; +   }<br>
&gt; +<br>
&gt; +   /**<br>
&gt; +    * The total number of varying components taken up by this variable.  Only<br>
&gt; +    * valid if is_assigned() is true.<br>
&gt; +    */<br>
&gt; +   unsigned num_components() const<br>
&gt; +   {<br>
&gt; +      return this-&gt;vector_elements * this-&gt;matrix_columns;<br>
&gt; +   }<br>
&gt; +<br>
&gt; +private:<br>
&gt; +   /**<br>
&gt; +    * The name that was supplied to glTransformFeedbackVaryings.  Used for<br>
&gt; +    * error reporting.<br>
&gt; +    */<br>
&gt; +   const char *orig_name;<br>
&gt; +<br>
&gt; +   /**<br>
&gt; +    * The name of the variable, parsed from orig_name.<br>
&gt; +    */<br>
&gt; +   char *var_name;<br>
&gt; +<br>
&gt; +   /**<br>
&gt; +    * True if the declaration in orig_name represents an array.<br>
&gt; +    */<br>
&gt; +   bool is_array;<br>
&gt; +<br>
&gt; +   /**<br>
&gt; +    * If is_array is true, the array index that was specified in orig_name.<br>
&gt; +    */<br>
&gt; +   unsigned array_index;<br>
&gt; +<br>
&gt; +   /**<br>
&gt; +    * The vertex shader output location that the linker assigned for this<br>
&gt; +    * variable.  -1 if a location hasn&#39;t been assigned yet.<br>
&gt; +    */<br>
&gt; +   int location;<br>
&gt; +<br>
&gt; +   /**<br>
&gt; +    * If location != -1, the number of vector elements in this variable, or 1<br>
&gt; +    * if this variable is a scalar.<br>
&gt; +    */<br>
&gt; +   unsigned vector_elements;<br>
&gt; +<br>
&gt; +   /**<br>
&gt; +    * If location != -1, the number of matrix columns in this variable, or 1<br>
&gt; +    * if this variable is not a matrix.<br>
&gt; +    */<br>
&gt; +   unsigned matrix_columns;<br>
&gt; +};<br>
&gt; +<br>
&gt; +<br>
&gt; +/**<br>
&gt; + * Initialize this object based on a string that was passed to<br>
&gt; + * glTransformFeedbackVaryings.  If there is a parse error, the error is<br>
&gt; + * reported using linker_error(), and false is returned.<br>
&gt; + */<br>
&gt; +bool<br>
&gt; +tfeedback_decl::init(struct gl_shader_program *prog, const void *mem_ctx,<br>
&gt; +                     const char *input)<br>
&gt; +{<br>
&gt; +   /* We don&#39;t have to be pedantic about what is a valid GLSL variable name,<br>
&gt; +    * because any variable with an invalid name can&#39;t exist in the IR anyway.<br>
&gt; +    */<br>
&gt; +<br>
&gt; +   this-&gt;location = -1;<br>
&gt; +   this-&gt;orig_name = input;<br>
&gt; +<br>
&gt; +   const char *bracket = strrchr(input, &#39;[&#39;);<br>
&gt; +<br>
&gt; +   if (bracket) {<br>
&gt; +      this-&gt;var_name = ralloc_strndup(mem_ctx, input, bracket - input);<br>
&gt; +      if (sscanf(bracket, &quot;[%u]&quot;, &amp;this-&gt;array_index) == 1) {<br>
&gt; +         this-&gt;is_array = true;<br>
&gt; +         return true;<br>
&gt; +      }<br>
&gt; +   } else {<br>
&gt; +      this-&gt;var_name = ralloc_strdup(mem_ctx, input);<br>
&gt; +      this-&gt;is_array = false;<br>
&gt; +      return true;<br>
&gt; +   }<br>
&gt; +<br>
&gt; +   linker_error(prog, &quot;Cannot parse transform feedback varying %s&quot;, input);<br>
&gt; +   return false;<br>
&gt; +}<br>
&gt; +<br>
&gt; +<br>
&gt; +/**<br>
&gt; + * Determine whether two tfeedback_decl objects refer to the same variable and<br>
&gt; + * array index (if applicable).<br>
&gt; + */<br>
&gt; +bool<br>
&gt; +tfeedback_decl::is_same(const tfeedback_decl &amp;x, const tfeedback_decl &amp;y)<br>
&gt; +{<br>
&gt; +   if (strcmp(x.var_name, y.var_name) != 0)<br>
&gt; +      return false;<br>
&gt; +   if (x.is_array != y.is_array)<br>
&gt; +      return false;<br>
&gt; +   if (x.is_array &amp;&amp; x.array_index != y.array_index)<br>
&gt; +      return false;<br>
&gt; +   return true;<br>
&gt; +}<br>
&gt; +<br>
&gt; +<br>
&gt; +/**<br>
&gt; + * Assign a location for this tfeedback_decl object based on the location<br>
&gt; + * assignment in output_var.<br>
&gt; + *<br>
&gt; + * If an error occurs, the error is reported through linker_error() and false<br>
&gt; + * is returned.<br>
&gt; + */<br>
&gt; +bool<br>
&gt; +tfeedback_decl::assign_location(struct gl_context *ctx,<br>
&gt; +                                struct gl_shader_program *prog,<br>
&gt; +                                ir_variable *output_var)<br>
&gt; +{<br>
&gt; +   if (output_var-&gt;type-&gt;is_array()) {<br>
&gt; +      /* Array variable */<br>
&gt; +      if (!this-&gt;is_array) {<br>
&gt; +         linker_error(prog, &quot;Transform feedback varying %s found, &quot;<br>
&gt; +                      &quot;but it&#39;s not an array ([] not expected).&quot;,<br>
&gt; +                      this-&gt;orig_name);<br>
&gt; +         return false;<br>
&gt; +      }<br>
&gt; +      /* Check array bounds. */<br>
&gt; +      if (this-&gt;array_index &gt;=<br>
&gt; +          (unsigned) output_var-&gt;type-&gt;array_size()) {<br>
&gt; +         linker_error(prog, &quot;Transform feedback varying %s has index &quot;<br>
&gt; +                      &quot;%i, but the array size is %i.&quot;,<br>
&gt; +                      this-&gt;orig_name, this-&gt;array_index,<br>
&gt; +                      output_var-&gt;type-&gt;array_size());<br>
&gt; +      }<br>
&gt; +      const unsigned matrix_cols =<br>
&gt; +         output_var-&gt;type-&gt;fields.array-&gt;matrix_columns;<br>
&gt; +      this-&gt;location = output_var-&gt;location + this-&gt;array_index * matrix_cols;<br>
&gt; +      this-&gt;vector_elements = output_var-&gt;type-&gt;fields.array-&gt;vector_elements;<br>
&gt; +      this-&gt;matrix_columns = matrix_cols;<br>
&gt; +   } else {<br>
&gt; +      /* Regular variable (scalar, vector, or matrix) */<br>
&gt; +      if (this-&gt;is_array) {<br>
&gt; +         linker_error(prog, &quot;Transform feedback varying %s found, &quot;<br>
&gt; +                      &quot;but it&#39;s an array ([] expected).&quot;,<br>
&gt; +                      this-&gt;orig_name);<br>
&gt; +         return false;<br>
&gt; +      }<br>
&gt; +      this-&gt;location = output_var-&gt;location;<br>
&gt; +      this-&gt;vector_elements = output_var-&gt;type-&gt;vector_elements;<br>
&gt; +      this-&gt;matrix_columns = output_var-&gt;type-&gt;matrix_columns;<br>
&gt; +   }<br>
&gt; +   /* From GL_EXT_transform_feedback:<br>
&gt; +    *   A program will fail to link if:<br>
&gt; +    *<br>
&gt; +    *   * the total number of components to capture in any varying<br>
&gt; +    *     variable in &lt;varyings&gt; is greater than the constant<br>
&gt; +    *     MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS_EXT and the<br>
&gt; +    *     buffer mode is SEPARATE_ATTRIBS_EXT;<br>
&gt; +    */<br>
&gt; +   if (prog-&gt;TransformFeedback.BufferMode == GL_SEPARATE_ATTRIBS &amp;&amp;<br>
&gt; +       this-&gt;num_components() &gt;<br>
&gt; +       ctx-&gt;Const.MaxTransformFeedbackSeparateComponents) {<br>
&gt; +      linker_error(prog, &quot;Transform feedback varying %s exceeds &quot;<br>
&gt; +                   &quot;MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS.&quot;,<br>
&gt; +                   this-&gt;orig_name);<br>
&gt; +      return false;<br>
&gt; +   }<br>
&gt; +<br>
&gt; +   return true;<br>
&gt; +}<br>
&gt; +<br>
&gt; +<br>
&gt; +/**<br>
&gt; + * Update gl_transform_feedback_info to reflect this tfeedback_decl.<br>
&gt; + *<br>
&gt; + * If an error occurs, the error is reported through linker_error() and false<br>
&gt; + * is returned.<br>
&gt; + */<br>
&gt; +bool<br>
&gt; +tfeedback_decl::store(struct gl_shader_program *prog,<br>
&gt; +                      struct gl_transform_feedback_info *info,<br>
&gt; +                      unsigned buffer) const<br>
&gt; +{<br>
&gt; +   if (!this-&gt;is_assigned()) {<br>
&gt; +      /* From GL_EXT_transform_feedback:<br>
&gt; +       *   A program will fail to link if:<br>
&gt; +       *<br>
&gt; +       *   * any variable name specified in the &lt;varyings&gt; array is not<br>
&gt; +       *     declared as an output in the geometry shader (if present) or<br>
&gt; +       *     the vertex shader (if no geometry shader is present);<br>
&gt; +       */<br>
&gt; +      linker_error(prog, &quot;Transform feedback varying %s undeclared.&quot;,<br>
&gt; +                   this-&gt;orig_name);<br>
&gt; +      return false;<br>
&gt; +   }<br>
&gt; +   for (unsigned v = 0; v &lt; this-&gt;matrix_columns; ++v) {<br>
&gt; +      info-&gt;Outputs[info-&gt;NumOutputs].OutputRegister = this-&gt;location + v;<br>
&gt; +      info-&gt;Outputs[info-&gt;NumOutputs].NumComponents = this-&gt;vector_elements;<br>
&gt; +      info-&gt;Outputs[info-&gt;NumOutputs].OutputBuffer = buffer;<br>
&gt; +      ++info-&gt;NumOutputs;<br>
&gt; +   }<br>
&gt; +   return true;<br>
&gt; +}<br>
&gt; +<br>
&gt; +<br>
&gt; +/**<br>
&gt; + * Parse all the transform feedback declarations that were passed to<br>
&gt; + * glTransformFeedbackVaryings() and store them in tfeedback_decl objects.<br>
&gt; + *<br>
&gt; + * If an error occurs, the error is reported through linker_error() and false<br>
&gt; + * is returned.<br>
&gt; + */<br>
&gt; +static bool<br>
&gt; +parse_tfeedback_decls(struct gl_shader_program *prog, const void *mem_ctx,<br>
&gt; +                      unsigned num_names, char **varying_names,<br>
&gt; +                      tfeedback_decl *decls)<br>
&gt; +{<br>
&gt; +   for (unsigned i = 0; i &lt; num_names; ++i) {<br>
&gt; +      if (!decls[i].init(prog, mem_ctx, varying_names[i]))<br>
&gt; +         return false;<br>
&gt; +      /* From GL_EXT_transform_feedback:<br>
&gt; +       *   A program will fail to link if:<br>
&gt; +       *<br>
&gt; +       *   * any two entries in the &lt;varyings&gt; array specify the same varying<br>
&gt; +       *     variable;<br>
&gt; +       *<br>
&gt; +       * We interpret this to mean &quot;any two entries in the &lt;varyings&gt; array<br>
&gt; +       * specify the same varying variable and array index&quot;, since transform<br>
&gt; +       * feedback of arrays would be useless otherwise.<br>
&gt; +       */<br>
&gt; +      for (unsigned j = 0; j &lt; i; ++j) {<br>
&gt; +         if (tfeedback_decl::is_same(decls[i], decls[j])) {<br>
&gt; +            linker_error(prog, &quot;Transform feedback varying %s specified &quot;<br>
&gt; +                         &quot;more than once.&quot;, varying_names[i]);<br>
&gt; +         }<br>
&gt; +         return false;<br>
<br>
</div></div>The &quot;return false&quot; statement should be put next to linker_error.<br>
Otherwise it always fails when the number of TFB varyings is at least<br>
2.<br></blockquote><div><br></div><div>Whoops, you&#39;re right of course.</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;">
<br>
With that fixed, this patch is:<br>
<br>
Reviewed-by: Marek Olšák &lt;<a href="mailto:maraeo@gmail.com">maraeo@gmail.com</a>&gt;<br>
Tested-by: Marek Olšák &lt;<a href="mailto:maraeo@gmail.com">maraeo@gmail.com</a>&gt;<br></blockquote><div><br></div><div>Thanks for the quick turnaround, Marek.  I&#39;ll wait until the end of the day to push this upstream, in case anyone else has any review comments.</div>
<div><br></div><div>Paul</div></div>