summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/glsl/link_varyings.cpp194
-rw-r--r--src/glsl/link_varyings.h57
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 <varyings> 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 <varyings> 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;
}
@@ -868,6 +855,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
@@ -42,6 +42,49 @@ 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;
};