From 99b78337e38308480ee491493b045179f10ed579 Mon Sep 17 00:00:00 2001 From: Paul Berry Date: Tue, 29 Jan 2013 07:34:52 -0800 Subject: [PATCH] glsl: Support transform feedback of varying structs. Since transform feedback needs to be able to access individual fields of varying structs, we can no longer match up the arguments to glTransformFeedbackVaryings() with variables in the vertex shader. Instead, we build up a hashtable which records information about each possible name that is a candidate for transform feedback, and then match up the arguments to glTransformFeedbackVaryings() with the contents of that hashtable. Populating the hashtable uses the program_resource_visitor infrastructure, so the logic is shared with how we handle uniforms. NOTE: This is a candidate for the 9.1 branch. Reviewed-by: Jordan Justen Reviewed-by: Matt Turner --- src/glsl/link_varyings.cpp | 194 +++++++++++++++++++++++++------------ src/glsl/link_varyings.h | 57 ++++++++++- 2 files changed, 184 insertions(+), 67 deletions(-) diff --git a/src/glsl/link_varyings.cpp b/src/glsl/link_varyings.cpp index 980b8d0ac90..e2cb46e8ced 100644 --- a/src/glsl/link_varyings.cpp +++ b/src/glsl/link_varyings.cpp @@ -35,6 +35,7 @@ #include "linker.h" #include "link_varyings.h" #include "main/macros.h" +#include "program/hash_table.h" #include "program.h" @@ -174,6 +175,7 @@ tfeedback_decl::init(struct gl_context *ctx, struct gl_shader_program *prog, this->is_clip_distance_mesa = false; this->skip_components = 0; this->next_buffer_separator = false; + this->matched_candidate = NULL; if (ctx->Extensions.ARB_transform_feedback3) { /* Parse gl_NextBuffer. */ @@ -238,27 +240,32 @@ tfeedback_decl::is_same(const tfeedback_decl &x, const tfeedback_decl &y) /** - * Assign a location for this tfeedback_decl object based on the location - * assignment in output_var. + * Assign a location for this tfeedback_decl object based on the transform + * feedback candidate found by find_candidate. * * If an error occurs, the error is reported through linker_error() and false * is returned. */ bool tfeedback_decl::assign_location(struct gl_context *ctx, - struct gl_shader_program *prog, - ir_variable *output_var) + struct gl_shader_program *prog) { assert(this->is_varying()); - if (output_var->type->is_array()) { + unsigned fine_location + = this->matched_candidate->toplevel_var->location * 4 + + this->matched_candidate->toplevel_var->location_frac + + this->matched_candidate->offset; + + if (this->matched_candidate->type->is_array()) { /* Array variable */ const unsigned matrix_cols = - output_var->type->fields.array->matrix_columns; + this->matched_candidate->type->fields.array->matrix_columns; const unsigned vector_elements = - output_var->type->fields.array->vector_elements; + this->matched_candidate->type->fields.array->vector_elements; unsigned actual_array_size = this->is_clip_distance_mesa ? - prog->Vert.ClipDistanceArraySize : output_var->type->array_size(); + prog->Vert.ClipDistanceArraySize : + this->matched_candidate->type->array_size(); if (this->is_subscripted) { /* Check array bounds. */ @@ -269,22 +276,11 @@ tfeedback_decl::assign_location(struct gl_context *ctx, actual_array_size); return false; } - if (this->is_clip_distance_mesa) { - this->location = - output_var->location + this->array_subscript / 4; - this->location_frac = this->array_subscript % 4; - } else { - unsigned fine_location - = output_var->location * 4 + output_var->location_frac; - unsigned array_elem_size = vector_elements * matrix_cols; - fine_location += array_elem_size * this->array_subscript; - this->location = fine_location / 4; - this->location_frac = fine_location % 4; - } + unsigned array_elem_size = this->is_clip_distance_mesa ? + 1 : vector_elements * matrix_cols; + fine_location += array_elem_size * this->array_subscript; this->size = 1; } else { - this->location = output_var->location; - this->location_frac = output_var->location_frac; this->size = actual_array_size; } this->vector_elements = vector_elements; @@ -292,7 +288,7 @@ tfeedback_decl::assign_location(struct gl_context *ctx, if (this->is_clip_distance_mesa) this->type = GL_FLOAT; else - this->type = output_var->type->fields.array->gl_type; + this->type = this->matched_candidate->type->fields.array->gl_type; } else { /* Regular variable (scalar, vector, or matrix) */ if (this->is_subscripted) { @@ -301,13 +297,13 @@ tfeedback_decl::assign_location(struct gl_context *ctx, this->orig_name, this->var_name); return false; } - this->location = output_var->location; - this->location_frac = output_var->location_frac; this->size = 1; - this->vector_elements = output_var->type->vector_elements; - this->matrix_columns = output_var->type->matrix_columns; - this->type = output_var->type->gl_type; + this->vector_elements = this->matched_candidate->type->vector_elements; + this->matrix_columns = this->matched_candidate->type->matrix_columns; + this->type = this->matched_candidate->type->gl_type; } + this->location = fine_location / 4; + this->location_frac = fine_location % 4; /* From GL_EXT_transform_feedback: * A program will fail to link if: @@ -402,35 +398,26 @@ tfeedback_decl::store(struct gl_context *ctx, struct gl_shader_program *prog, } -ir_variable * -tfeedback_decl::find_output_var(gl_shader_program *prog, - gl_shader *producer) const +const tfeedback_candidate * +tfeedback_decl::find_candidate(gl_shader_program *prog, + hash_table *tfeedback_candidates) { const char *name = this->is_clip_distance_mesa ? "gl_ClipDistanceMESA" : this->var_name; - ir_variable *var = producer->symbols->get_variable(name); - if (var && var->mode == ir_var_shader_out) { - const glsl_type *type = var->type; - while (type->base_type == GLSL_TYPE_ARRAY) - type = type->fields.array; - if (type->base_type == GLSL_TYPE_STRUCT) { - linker_error(prog, "Transform feedback of varying structs not " - "implemented yet."); - return NULL; - } - return var; + this->matched_candidate = (const tfeedback_candidate *) + hash_table_find(tfeedback_candidates, name); + if (!this->matched_candidate) { + /* From GL_EXT_transform_feedback: + * A program will fail to link if: + * + * * any variable name specified in the array is not + * declared as an output in the geometry shader (if present) or + * the vertex shader (if no geometry shader is present); + */ + linker_error(prog, "Transform feedback varying %s undeclared.", + this->orig_name); } - - /* From GL_EXT_transform_feedback: - * A program will fail to link if: - * - * * any variable name specified in the array is not - * declared as an output in the geometry shader (if present) or - * the vertex shader (if no geometry shader is present); - */ - linker_error(prog, "Transform feedback varying %s undeclared.", - this->orig_name); - return NULL; + return this->matched_candidate; } @@ -867,6 +854,80 @@ is_varying_var(GLenum shaderType, const ir_variable *var) } +/** + * Visitor class that generates tfeedback_candidate structs describing all + * possible targets of transform feedback. + * + * tfeedback_candidate structs are stored in the hash table + * tfeedback_candidates, which is passed to the constructor. This hash table + * maps varying names to instances of the tfeedback_candidate struct. + */ +class tfeedback_candidate_generator : public program_resource_visitor +{ +public: + tfeedback_candidate_generator(void *mem_ctx, + hash_table *tfeedback_candidates) + : mem_ctx(mem_ctx), + tfeedback_candidates(tfeedback_candidates) + { + } + + void process(ir_variable *var) + { + this->toplevel_var = var; + this->varying_floats = 0; + if (var->is_interface_instance()) + program_resource_visitor::process(var->interface_type, + var->interface_type->name); + else + program_resource_visitor::process(var); + } + +private: + virtual void visit_field(const glsl_type *type, const char *name, + bool row_major) + { + assert(!type->is_record()); + assert(!(type->is_array() && type->fields.array->is_record())); + assert(!type->is_interface()); + assert(!(type->is_array() && type->fields.array->is_interface())); + + (void) row_major; + + tfeedback_candidate *candidate + = rzalloc(this->mem_ctx, tfeedback_candidate); + candidate->toplevel_var = this->toplevel_var; + candidate->type = type; + candidate->offset = this->varying_floats; + hash_table_insert(this->tfeedback_candidates, candidate, + ralloc_strdup(this->mem_ctx, name)); + this->varying_floats += type->component_slots(); + } + + /** + * Memory context used to allocate hash table keys and values. + */ + void * const mem_ctx; + + /** + * Hash table in which tfeedback_candidate objects should be stored. + */ + hash_table * const tfeedback_candidates; + + /** + * Pointer to the toplevel variable that is being traversed. + */ + ir_variable *toplevel_var; + + /** + * Total number of varying floats that have been visited so far. This is + * used to determine the offset to each varying within the toplevel + * variable. + */ + unsigned varying_floats; +}; + + /** * Assign locations for all variables that are produced in one pipeline stage * (the "producer") and consumed in the next stage (the "consumer"). @@ -899,6 +960,8 @@ assign_varying_locations(struct gl_context *ctx, const unsigned producer_base = VERT_RESULT_VAR0; const unsigned consumer_base = FRAG_ATTRIB_VAR0; varying_matches matches(ctx->Const.DisableVaryingPacking); + hash_table *tfeedback_candidates + = hash_table_ctor(0, hash_table_string_hash, hash_table_string_compare); /* Operate in a total of three passes. * @@ -917,6 +980,9 @@ assign_varying_locations(struct gl_context *ctx, if ((output_var == NULL) || (output_var->mode != ir_var_shader_out)) continue; + tfeedback_candidate_generator g(mem_ctx, tfeedback_candidates); + g.process(output_var); + ir_variable *input_var = consumer ? consumer->symbols->get_variable(output_var->name) : NULL; @@ -932,15 +998,16 @@ assign_varying_locations(struct gl_context *ctx, if (!tfeedback_decls[i].is_varying()) continue; - ir_variable *output_var - = tfeedback_decls[i].find_output_var(prog, producer); + const tfeedback_candidate *matched_candidate + = tfeedback_decls[i].find_candidate(prog, tfeedback_candidates); - if (output_var == NULL) + if (matched_candidate == NULL) { + hash_table_dtor(tfeedback_candidates); return false; - - if (output_var->is_unmatched_generic_inout) { - matches.record(output_var, NULL); } + + if (matched_candidate->toplevel_var->is_unmatched_generic_inout) + matches.record(matched_candidate->toplevel_var, NULL); } const unsigned slots_used = matches.assign_locations(); @@ -950,13 +1017,14 @@ assign_varying_locations(struct gl_context *ctx, 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)) + if (!tfeedback_decls[i].assign_location(ctx, prog)) { + hash_table_dtor(tfeedback_candidates); return false; + } } + hash_table_dtor(tfeedback_candidates); + if (ctx->Const.DisableVaryingPacking) { /* Transform feedback code assumes varyings are packed, so if the driver * has disabled varying packing, make sure it does not support transform diff --git a/src/glsl/link_varyings.h b/src/glsl/link_varyings.h index 806bd1c470f..ee1010a7b7d 100644 --- a/src/glsl/link_varyings.h +++ b/src/glsl/link_varyings.h @@ -41,6 +41,49 @@ struct gl_shader; class ir_variable; +/** + * Data structure describing a varying which is available for use in transform + * feedback. + * + * For example, if the vertex shader contains: + * + * struct S { + * vec4 foo; + * float[3] bar; + * }; + * + * varying S[2] v; + * + * Then there would be tfeedback_candidate objects corresponding to the + * following varyings: + * + * v[0].foo + * v[0].bar + * v[1].foo + * v[1].bar + */ +struct tfeedback_candidate +{ + /** + * Toplevel variable containing this varying. In the above example, this + * would point to the declaration of the varying v. + */ + ir_variable *toplevel_var; + + /** + * Type of this varying. In the above example, this would point to the + * glsl_type for "vec4" or "float[3]". + */ + const glsl_type *type; + + /** + * Offset within the toplevel variable where this varying occurs (counted + * in multiples of the size of a float). + */ + unsigned offset; +}; + + /** * Data structure tracking information about a transform feedback declaration * during linking. @@ -51,14 +94,14 @@ public: void init(struct gl_context *ctx, struct gl_shader_program *prog, const void *mem_ctx, const char *input); static bool is_same(const tfeedback_decl &x, const tfeedback_decl &y); - bool assign_location(struct gl_context *ctx, struct gl_shader_program *prog, - ir_variable *output_var); + bool assign_location(struct gl_context *ctx, + struct gl_shader_program *prog); unsigned get_num_outputs() const; bool store(struct gl_context *ctx, struct gl_shader_program *prog, struct gl_transform_feedback_info *info, unsigned buffer, const unsigned max_outputs) const; - ir_variable *find_output_var(gl_shader_program *prog, - gl_shader *producer) const; + const tfeedback_candidate *find_candidate(gl_shader_program *prog, + hash_table *tfeedback_candidates); bool is_next_buffer_separator() const { @@ -158,6 +201,12 @@ private: * Whether this is gl_NextBuffer from ARB_transform_feedback3. */ bool next_buffer_separator; + + /** + * If find_candidate() has been called, pointer to the tfeedback_candidate + * data structure that was found. Otherwise NULL. + */ + const tfeedback_candidate *matched_candidate; }; -- 2.30.2