[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