glsl/linker: Subdivide the first phase of varying assignment.

This patch further subdivides the loop that assigns varying locations
into two phases: one phase to match up the varyings between shader
stages, and one phase to assign them varying locations.

In between the two phases the matched varyings are stored in a new
data structure called varying_matches.  This will free us to be able
to assign varying locations in any order, which will pave the way for
packing varyings.

Note that the new varying_matches::assign_locations() function returns
the number of varying slots that were used; this return value will be
used in a future patch.

Reviewed-by: Eric Anholt <eric@anholt.net>
This commit is contained in:
Paul Berry 2012-12-04 15:55:59 -08:00
parent 25ed3bef9b
commit eb989e37cb
1 changed files with 164 additions and 45 deletions

View File

@ -1961,58 +1961,167 @@ parse_tfeedback_decls(struct gl_context *ctx, struct gl_shader_program *prog,
/**
* Assign a location for a variable that is produced in one pipeline stage
* (the "producer") and consumed in the next stage (the "consumer").
* Data structure recording the relationship between outputs of one shader
* stage (the "producer") and inputs of another (the "consumer").
*/
class varying_matches
{
public:
varying_matches();
~varying_matches();
void record(ir_variable *producer_var, ir_variable *consumer_var);
unsigned assign_locations();
void store_locations(unsigned producer_base, unsigned consumer_base) const;
private:
/**
* Structure recording the relationship between a single producer output
* and a single consumer input.
*/
struct match {
/**
* The output variable in the producer stage.
*/
ir_variable *producer_var;
/**
* The input variable in the consumer stage.
*/
ir_variable *consumer_var;
/**
* The location which has been assigned for this varying. This is
* expressed in multiples of a float, with the first generic varying
* (i.e. the one referred to by VERT_RESULT_VAR0 or FRAG_ATTRIB_VAR0)
* represented by the value 0.
*/
unsigned generic_location;
} *matches;
/**
* The number of elements in the \c matches array that are currently in
* use.
*/
unsigned num_matches;
/**
* The number of elements that were set aside for the \c matches array when
* it was allocated.
*/
unsigned matches_capacity;
};
varying_matches::varying_matches()
{
/* Note: this initial capacity is rather arbitrarily chosen to be large
* enough for many cases without wasting an unreasonable amount of space.
* varying_matches::record() will resize the array if there are more than
* this number of varyings.
*/
this->matches_capacity = 8;
this->matches = (match *)
malloc(sizeof(*this->matches) * this->matches_capacity);
this->num_matches = 0;
}
varying_matches::~varying_matches()
{
free(this->matches);
}
/**
* Record the given producer/consumer variable pair in the list of variables
* that should later be assigned locations.
*
* \param input_var is the input variable declaration in the consumer.
* It is permissible for \c consumer_var to be NULL (this happens if a
* variable is output by the producer and consumed by transform feedback, but
* not consumed by the consumer).
*
* \param output_var is the output variable declaration in the producer.
*
* \param input_index is the counter that keeps track of assigned input
* locations in the consumer.
*
* \param output_index is the counter that keeps track of assigned output
* locations in the producer.
*
* It is permissible for \c input_var to be NULL (this happens if a variable
* is output by the producer and consumed by transform feedback, but not
* consumed by the consumer).
*
* If the variable has already been assigned a location, this function has no
* effect.
* If \c producer_var has already been paired up with a consumer_var, or
* producer_var is part of fixed pipeline functionality (and hence already has
* a location assigned), this function has no effect.
*/
void
assign_varying_location(ir_variable *input_var, ir_variable *output_var,
unsigned *input_index, unsigned *output_index)
varying_matches::record(ir_variable *producer_var, ir_variable *consumer_var)
{
if (!output_var->is_unmatched_generic_inout) {
/* Location already assigned. */
if (!producer_var->is_unmatched_generic_inout) {
/* Either a location already exists for this variable (since it is part
* of fixed functionality), or it has already been recorded as part of a
* previous match.
*/
return;
}
if (input_var) {
assert(input_var->location == -1);
input_var->location = *input_index;
input_var->is_unmatched_generic_inout = 0;
if (this->num_matches == this->matches_capacity) {
this->matches_capacity *= 2;
this->matches = (match *)
realloc(this->matches,
sizeof(*this->matches) * this->matches_capacity);
}
this->matches[this->num_matches].producer_var = producer_var;
this->matches[this->num_matches].consumer_var = consumer_var;
this->num_matches++;
producer_var->is_unmatched_generic_inout = 0;
if (consumer_var)
consumer_var->is_unmatched_generic_inout = 0;
}
/**
* Choose locations for all of the variable matches that were previously
* passed to varying_matches::record().
*/
unsigned
varying_matches::assign_locations()
{
unsigned generic_location = 0;
for (unsigned i = 0; i < this->num_matches; i++) {
this->matches[i].generic_location = generic_location;
ir_variable *producer_var = this->matches[i].producer_var;
if (producer_var->type->is_array()) {
const unsigned slots = producer_var->type->length
* producer_var->type->fields.array->matrix_columns;
generic_location += 4 * slots;
} else {
const unsigned slots = producer_var->type->matrix_columns;
generic_location += 4 * slots;
}
}
output_var->location = *output_index;
output_var->is_unmatched_generic_inout = 0;
return (generic_location + 3) / 4;
}
/* FINISHME: Support for "varying" records in GLSL 1.50. */
assert(!output_var->type->is_record());
if (output_var->type->is_array()) {
const unsigned slots = output_var->type->length
* output_var->type->fields.array->matrix_columns;
/**
* Update the producer and consumer shaders to reflect the locations
* assignments that were made by varying_matches::assign_locations().
*/
void
varying_matches::store_locations(unsigned producer_base,
unsigned consumer_base) const
{
for (unsigned i = 0; i < this->num_matches; i++) {
ir_variable *producer_var = this->matches[i].producer_var;
ir_variable *consumer_var = this->matches[i].consumer_var;
unsigned generic_location = this->matches[i].generic_location;
unsigned slot = generic_location / 4;
unsigned offset = generic_location % 4;
*output_index += slots;
*input_index += slots;
} else {
const unsigned slots = output_var->type->matrix_columns;
*output_index += slots;
*input_index += slots;
producer_var->location = producer_base + slot;
producer_var->location_frac = offset;
if (consumer_var) {
assert(consumer_var->location == -1);
consumer_var->location = consumer_base + slot;
consumer_var->location_frac = offset;
}
}
}
@ -2070,8 +2179,9 @@ assign_varying_locations(struct gl_context *ctx,
tfeedback_decl *tfeedback_decls)
{
/* FINISHME: Set dynamically when geometry shader support is added. */
unsigned output_index = VERT_RESULT_VAR0;
unsigned input_index = FRAG_ATTRIB_VAR0;
const unsigned producer_base = VERT_RESULT_VAR0;
const unsigned consumer_base = FRAG_ATTRIB_VAR0;
varying_matches matches;
/* Operate in a total of three passes.
*
@ -2097,8 +2207,7 @@ assign_varying_locations(struct gl_context *ctx,
input_var = NULL;
if (input_var) {
assign_varying_location(input_var, output_var, &input_index,
&output_index);
matches.record(output_var, input_var);
}
}
@ -2113,9 +2222,19 @@ assign_varying_locations(struct gl_context *ctx,
return false;
if (output_var->is_unmatched_generic_inout) {
assign_varying_location(NULL, output_var, &input_index,
&output_index);
matches.record(output_var, NULL);
}
}
matches.assign_locations();
matches.store_locations(producer_base, consumer_base);
for (unsigned i = 0; i < num_tfeedback_decls; ++i) {
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))
return false;