[Mesa-dev] [PATCH 3/3] glsl: finish up the linker for transform feedback

Marek Olšák maraeo at gmail.com
Tue Nov 1 12:48:02 PDT 2011


---
 src/glsl/linker.cpp |  192 ++++++++++++++++++++++++++++++++++++++++++++++-----
 1 files changed, 174 insertions(+), 18 deletions(-)

diff --git a/src/glsl/linker.cpp b/src/glsl/linker.cpp
index e6012bf..d5d50b5 100644
--- a/src/glsl/linker.cpp
+++ b/src/glsl/linker.cpp
@@ -1615,49 +1615,170 @@ assign_varying_locations(struct gl_context *ctx,
 }
 
 
-void
+static bool
 assign_transform_feedback_varying_locations(struct gl_context *ctx,
-					    struct gl_shader_program *prog)
+					    struct gl_shader_program *prog,
+					    void *mem_ctx)
 {
-   struct gl_shader *vs = prog->_LinkedShaders[MESA_SHADER_VERTEX];
-
-   if (vs == NULL)
-      return;
+   /* No geometry shader support (yet). */
+   assert(!prog->_LinkedShaders[MESA_SHADER_GEOMETRY]);
 
    char **names = prog->TransformFeedback.VaryingNames;
    int num_names = prog->TransformFeedback.NumVarying;
 
-   if (num_names <= 0)
-      return;
+   if (num_names <= 0) {
+      return true;
+   }
+
+   /* From GL_EXT_transform_feedback:
+    *   A program will fail to link if:
+    *
+    *   * the <count> specified by TransformFeedbackVaryingsEXT is
+    *     non-zero, but the program object has no vertex or geometry
+    *     shader;
+    */
+   struct gl_shader *vs = prog->_LinkedShaders[MESA_SHADER_VERTEX];
+   if (!vs) {
+      linker_error(prog, "Transform feedback varyings specified, but "
+                   "no vertex shader is present.");
+      return false;
+   }
+
+   /* From GL_EXT_transform_feedback:
+    *   A program will fail to link if:
+    *
+    *   * any two entries in the <varyings> array specify the same varying
+    *    variable;
+    *
+    * (Do we have the redblack tree in Mesa?)
+    */
+   for (int i = 0; i < num_names; i++) {
+      for (int j = 0; j < num_names; j++) {
+         if (i != j) {
+            if (strcmp(names[i], names[j]) == 0) {
+               linker_error(prog, "Transform feedback varying %s specified "
+                            "more than once.", names[i]);
+               return false;
+            }
+         }
+      }
+   }
+
+   struct tfeedback_decl *decl =
+         (struct tfeedback_decl*)
+         ralloc_size(mem_ctx, sizeof(*decl) * num_names);
+   memset(decl, 0, sizeof(*decl) * num_names);
+
+   bool *status = (bool*)ralloc_size(mem_ctx, sizeof(bool) * num_names);
+   memset(status, 0, sizeof(bool) * num_names);
+
+   /* Parse the varyings. */
+   for (int i = 0; i < num_names; i++) {
+      if (!parse_tfeedback_decl(mem_ctx, names[i], decl+i)) {
+         linker_error(prog, "Cannot parse transform feedback varying %s",
+                      names[i]);
+         return false;
+      }
+   }
 
    int num_varying = max_varying(vs, ir_var_out) + 1;
    unsigned output_index =
      (num_varying > VERT_RESULT_VAR0)
         ? num_varying
         : VERT_RESULT_VAR0;
+   unsigned num_components = 0;
 
    foreach_list(node, vs->ir) {
       ir_variable *const output_var = ((ir_instruction *) node)->as_variable();
 
-      if (output_var == NULL
-      ||  output_var->mode != ir_var_out
-      ||  output_var->location != -1)
+      if (output_var == NULL ||  output_var->mode != ir_var_out)
 	 continue;
 
       /* Find a transform feedback varying variable that has
        * the same name as the shader variable.
        */
-      int varying_index = -1;
-      for (int num_name = 0; num_name < num_names; num_name++) {
-         char *name = names[num_name];
-         if (strcmp(output_var->name, name) == 0) {
-            varying_index = num_name;
+      int i;
+      for (i = 0; i < num_names; i++) {
+         if (strcmp(output_var->name, decl[i].name) == 0) {
             break;
          }
       }
 
-      if (varying_index == -1)
+      if (i == num_names) {
 	 continue;
+      }
+
+      /* From GL_EXT_transform_feedback:
+       *   A program will fail to link if:
+       *
+       *   * the total number of components to capture in any varying
+       *     variable in <varyings> is greater than the constant
+       *     MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS_EXT and the buffer
+       *     mode is SEPARATE_ATTRIBS_EXT;
+       */
+      if (prog->TransformFeedback.BufferMode == GL_SEPARATE_ATTRIBS &&
+          output_var->type->vector_elements *
+          output_var->type->matrix_columns >
+          (int)ctx->Const.MaxTransformFeedbackSeparateComponents) {
+         linker_error(prog, "Transform feedback varying %s exceeds "
+                      "MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS.",
+                      names[i]);
+         return false;
+      }
+
+      /* Validate all occurences of output_var->name in the array of TFB
+       * varyings. (We process each variable only once, but the array
+       * may reference one variable multiple times, each time with
+       * a different array index. */
+      do {
+         /* Check the type.
+          *
+          * From GL_EXT_transform_feedback:
+          *   26. If a varying is declared as an array, is the whole array
+          *       streamed out?
+          *
+          *   RESOLVED: No, the application has to specify which elements of
+          *   an array it wants to stream out. ...
+          */
+         if (output_var->type->is_array() != decl[i].is_array) {
+            if (decl[i].is_array) {
+               linker_error(prog, "Transform feedback varying %s found, but "
+                            "it's not an array ([] not expected).", names[i]);
+            } else {
+               linker_error(prog, "Transform feedback varying %s found, but "
+                            "it's an array. ([] expected)", names[i]);
+            }
+            return false;
+         }
+
+         /* Check array bounds. */
+         if (output_var->type->is_array() &&
+             output_var->type->array_size() <= (int)decl[i].array_index) {
+            linker_error(prog, "Transform feedback varying %s has index %i, "
+                         "but the array size is %i.",
+                         names[i], decl[i].array_index,
+                         output_var->type->array_size());
+            return false;
+         }
+
+         /* Everything's alright. */
+         status[i] = true;
+         num_components += output_var->type->vector_elements *
+                           output_var->type->matrix_columns;
+
+         /* Proceed to the next occurence. */
+         i++;
+         for (; i < num_names; i++) {
+            if (strcmp(output_var->name, decl[i].name) == 0) {
+               break;
+            }
+         }
+      } while (i < num_names);
+
+      /* Now assign the location if needed. */
+      if (output_var->location != -1) {
+         continue;
+      }
 
       output_var->location = output_index;
 
@@ -1675,6 +1796,39 @@ assign_transform_feedback_varying_locations(struct gl_context *ctx,
 	 output_index += slots;
       }
    }
+
+   /* From GL_EXT_transform_feedback:
+    *   A program will fail to link if:
+    *
+    *     * the total number of components to capture is greater than
+    *       the constant MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS_EXT
+    *       and the buffer mode is INTERLEAVED_ATTRIBS_EXT.
+    */
+   if (prog->TransformFeedback.BufferMode == GL_INTERLEAVED_ATTRIBS &&
+       num_components > GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS) {
+      linker_error(prog, "The MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS "
+                   "limit has been exceeded.");
+      return false;
+   }
+
+   /* Check that all transform feedback varyings have been found in the IR.
+    *
+    * From GL_EXT_transform_feedback:
+    *   A program will fail to link if:
+    *
+    *   * any variable name specified in the <varyings> array is not
+    *     declared as an output in the geometry shader (if present) or
+    *     the vertex shader (if no geometry shader is present);
+    */
+   for (int i = 0; i < num_names; i++) {
+      if (!status[i]) {
+         linker_error(prog, "Transform feedback varying %s undeclared.\n",
+                      names[i]);
+         return false;
+      }
+   }
+
+   return true;
 }
 
 
@@ -1860,7 +2014,9 @@ link_shaders(struct gl_context *ctx, struct gl_shader_program *prog)
       prev = i;
    }
 
-   assign_transform_feedback_varying_locations(ctx, prog);
+   if (!assign_transform_feedback_varying_locations(ctx, prog, mem_ctx)) {
+      goto done;
+   }
 
    if (prog->_LinkedShaders[MESA_SHADER_VERTEX] != NULL) {
       demote_shader_inputs_and_outputs(prog->_LinkedShaders[MESA_SHADER_VERTEX],
-- 
1.7.4.1



More information about the mesa-dev mailing list