linker: Support matrix and array vertex inputs
This commit is contained in:
198
linker.cpp
198
linker.cpp
@@ -126,6 +126,41 @@ invalidate_variable_locations(glsl_shader *sh, enum ir_variable_mode mode,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine the number of attribute slots required for a particular type
|
||||||
|
*
|
||||||
|
* This code is here because it implements the language rules of a specific
|
||||||
|
* GLSL version. Since it's a property of the language and not a property of
|
||||||
|
* types in general, it doesn't really belong in glsl_type.
|
||||||
|
*/
|
||||||
|
unsigned
|
||||||
|
count_attribute_slots(const glsl_type *t)
|
||||||
|
{
|
||||||
|
/* From page 31 (page 37 of the PDF) of the GLSL 1.50 spec:
|
||||||
|
*
|
||||||
|
* "A scalar input counts the same amount against this limit as a vec4,
|
||||||
|
* so applications may want to consider packing groups of four
|
||||||
|
* unrelated float inputs together into a vector to better utilize the
|
||||||
|
* capabilities of the underlying hardware. A matrix input will use up
|
||||||
|
* multiple locations. The number of locations used will equal the
|
||||||
|
* number of columns in the matrix."
|
||||||
|
*
|
||||||
|
* The spec does not explicitly say how arrays are counted. However, it
|
||||||
|
* should be safe to assume the total number of slots consumed by an array
|
||||||
|
* is the number of entries in the array multiplied by the number of slots
|
||||||
|
* consumed by a single element of the array.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (t->is_array())
|
||||||
|
return t->array_size() * count_attribute_slots(t->element_type());
|
||||||
|
|
||||||
|
if (t->is_matrix())
|
||||||
|
return t->matrix_columns;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verify that a vertex shader executable meets all semantic requirements
|
* Verify that a vertex shader executable meets all semantic requirements
|
||||||
*
|
*
|
||||||
@@ -434,7 +469,39 @@ assign_uniform_locations(struct glsl_program *prog)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
/**
|
||||||
|
* Find a contiguous set of available bits in a bitmask
|
||||||
|
*
|
||||||
|
* \param used_mask Bits representing used (1) and unused (0) locations
|
||||||
|
* \param needed_count Number of contiguous bits needed.
|
||||||
|
*
|
||||||
|
* \return
|
||||||
|
* Base location of the available bits on success or -1 on failure.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
find_available_slots(unsigned used_mask, unsigned needed_count)
|
||||||
|
{
|
||||||
|
unsigned needed_mask = (1 << needed_count) - 1;
|
||||||
|
const int max_bit_to_test = (8 * sizeof(used_mask)) - needed_count;
|
||||||
|
|
||||||
|
/* The comparison to 32 is redundant, but without it GCC emits "warning:
|
||||||
|
* cannot optimize possibly infinite loops" for the loop below.
|
||||||
|
*/
|
||||||
|
if ((needed_count == 0) || (max_bit_to_test < 0) || (max_bit_to_test > 32))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
for (int i = 0; i <= max_bit_to_test; i++) {
|
||||||
|
if ((needed_mask & ~used_mask) == needed_mask)
|
||||||
|
return i;
|
||||||
|
|
||||||
|
needed_mask <<= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
assign_attribute_locations(glsl_shader *sh,
|
assign_attribute_locations(glsl_shader *sh,
|
||||||
struct gl_program_parameter_list *attrib)
|
struct gl_program_parameter_list *attrib)
|
||||||
{
|
{
|
||||||
@@ -442,14 +509,19 @@ assign_attribute_locations(glsl_shader *sh,
|
|||||||
|
|
||||||
assert(sh->Type == GL_VERTEX_SHADER);
|
assert(sh->Type == GL_VERTEX_SHADER);
|
||||||
|
|
||||||
/* Operate in a total of three passes.
|
/* Operate in a total of four passes.
|
||||||
*
|
*
|
||||||
* 1. Invalidate the location assignments for all vertex shader inputs.
|
* 1. Invalidate the location assignments for all vertex shader inputs.
|
||||||
*
|
*
|
||||||
* 2. Assign locations for inputs that have user-defined (via
|
* 2. Assign locations for inputs that have user-defined (via
|
||||||
* glBindVertexAttribLocation) locatoins.
|
* glBindVertexAttribLocation) locatoins.
|
||||||
*
|
*
|
||||||
* 3. Assign locations to any inputs without assigned locations.
|
* 3. Sort the attributes without assigned locations by number of slots
|
||||||
|
* required in decreasing order. Fragmentation caused by attribute
|
||||||
|
* locations assigned by the application may prevent large attributes
|
||||||
|
* from having enough contiguous space.
|
||||||
|
*
|
||||||
|
* 4. Assign locations to any inputs without assigned locations.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
invalidate_variable_locations(sh, ir_var_in, VERT_ATTRIB_GENERIC0);
|
invalidate_variable_locations(sh, ir_var_in, VERT_ATTRIB_GENERIC0);
|
||||||
@@ -459,16 +531,84 @@ assign_attribute_locations(glsl_shader *sh,
|
|||||||
ir_variable *const var =
|
ir_variable *const var =
|
||||||
sh->symbols->get_variable(attrib->Parameters[i].Name);
|
sh->symbols->get_variable(attrib->Parameters[i].Name);
|
||||||
|
|
||||||
if (var == NULL)
|
/* Note: attributes that occupy multiple slots, such as arrays or
|
||||||
|
* matrices, may appear in the attrib array multiple times.
|
||||||
|
*/
|
||||||
|
if ((var == NULL) || (var->location != -1))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
/* From page 61 of the OpenGL 4.0 spec:
|
||||||
|
*
|
||||||
|
* "LinkProgram will fail if the attribute bindings assigned by
|
||||||
|
* BindAttribLocation do not leave not enough space to assign a
|
||||||
|
* location for an active matrix attribute or an active attribute
|
||||||
|
* array, both of which require multiple contiguous generic
|
||||||
|
* attributes."
|
||||||
|
*
|
||||||
|
* Previous versions of the spec contain similar language but omit the
|
||||||
|
* bit about attribute arrays.
|
||||||
|
*
|
||||||
|
* Page 61 of the OpenGL 4.0 spec also says:
|
||||||
|
*
|
||||||
|
* "It is possible for an application to bind more than one
|
||||||
|
* attribute name to the same location. This is referred to as
|
||||||
|
* aliasing. This will only work if only one of the aliased
|
||||||
|
* attributes is active in the executable program, or if no path
|
||||||
|
* through the shader consumes more than one attribute of a set
|
||||||
|
* of attributes aliased to the same location. A link error can
|
||||||
|
* occur if the linker determines that every path through the
|
||||||
|
* shader consumes multiple aliased attributes, but
|
||||||
|
* implementations are not required to generate an error in this
|
||||||
|
* case."
|
||||||
|
*
|
||||||
|
* These two paragraphs are either somewhat contradictory, or I don't
|
||||||
|
* fully understand one or both of them.
|
||||||
|
*/
|
||||||
|
/* FINISHME: The code as currently written does not support attribute
|
||||||
|
* FINISHME: location aliasing (see comment above).
|
||||||
|
*/
|
||||||
const int attr = attrib->Parameters[i].StateIndexes[0];
|
const int attr = attrib->Parameters[i].StateIndexes[0];
|
||||||
|
const unsigned slots = count_attribute_slots(var->type);
|
||||||
|
|
||||||
|
/* Mask representing the contiguous slots that will be used by this
|
||||||
|
* attribute.
|
||||||
|
*/
|
||||||
|
const unsigned use_mask = (1 << slots) - 1;
|
||||||
|
|
||||||
|
/* Generate a link error if the set of bits requested for this
|
||||||
|
* attribute overlaps any previously allocated bits.
|
||||||
|
*/
|
||||||
|
if ((~(use_mask << attr) & used_locations) != used_locations) {
|
||||||
|
printf("error: insufficient contiguous attribute locations "
|
||||||
|
"available for vertex shader input `%s'",
|
||||||
|
var->name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
var->location = VERT_ATTRIB_GENERIC0 + attr;
|
var->location = VERT_ATTRIB_GENERIC0 + attr;
|
||||||
used_locations |= (1 << attr);
|
used_locations |= (use_mask << attr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Temporary storage for the set of attributes that need locations assigned.
|
||||||
|
*/
|
||||||
|
struct temp_attr {
|
||||||
|
unsigned slots;
|
||||||
|
ir_variable *var;
|
||||||
|
|
||||||
|
/* Used below in the call to qsort. */
|
||||||
|
static int compare(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
const temp_attr *const l = (const temp_attr *) a;
|
||||||
|
const temp_attr *const r = (const temp_attr *) b;
|
||||||
|
|
||||||
|
/* Reversed because we want a descending order sort below. */
|
||||||
|
return r->slots - l->slots;
|
||||||
|
}
|
||||||
|
} to_assign[16];
|
||||||
|
|
||||||
|
unsigned num_attr = 0;
|
||||||
|
|
||||||
foreach_list(node, &sh->ir) {
|
foreach_list(node, &sh->ir) {
|
||||||
ir_variable *const var = ((ir_instruction *) node)->as_variable();
|
ir_variable *const var = ((ir_instruction *) node)->as_variable();
|
||||||
|
|
||||||
@@ -480,17 +620,40 @@ assign_attribute_locations(glsl_shader *sh,
|
|||||||
if (var->location != -1)
|
if (var->location != -1)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* Find an unused bit in used_locations and assign that as the
|
to_assign[num_attr].slots = count_attribute_slots(var->type);
|
||||||
* attribute location.
|
to_assign[num_attr].var = var;
|
||||||
*/
|
num_attr++;
|
||||||
for (unsigned i = 0; i < (8 * sizeof(used_locations)); i++) {
|
|
||||||
if ((used_locations & (1 << i)) == 0) {
|
|
||||||
var->location = VERT_ATTRIB_GENERIC0 + i;
|
|
||||||
used_locations |= (1 << i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If all of the attributes were assigned locations by the application (or
|
||||||
|
* are built-in attributes with fixed locations), return early. This should
|
||||||
|
* be the common case.
|
||||||
|
*/
|
||||||
|
if (num_attr == 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
qsort(to_assign, num_attr, sizeof(to_assign[0]), temp_attr::compare);
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < num_attr; i++) {
|
||||||
|
/* Mask representing the contiguous slots that will be used by this
|
||||||
|
* attribute.
|
||||||
|
*/
|
||||||
|
const unsigned use_mask = (1 << to_assign[i].slots) - 1;
|
||||||
|
|
||||||
|
int location = find_available_slots(used_locations, to_assign[i].slots);
|
||||||
|
|
||||||
|
if (location < 0) {
|
||||||
|
printf("error: insufficient contiguous attribute locations "
|
||||||
|
"available for vertex shader input `%s'",
|
||||||
|
to_assign[i].var->name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
to_assign[i].var->location = VERT_ATTRIB_GENERIC0 + location;
|
||||||
|
used_locations |= (use_mask << location);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -573,8 +736,9 @@ link_shaders(struct glsl_program *prog)
|
|||||||
assign_uniform_locations(prog);
|
assign_uniform_locations(prog);
|
||||||
|
|
||||||
if (prog->_LinkedShaders[0]->Type == GL_VERTEX_SHADER)
|
if (prog->_LinkedShaders[0]->Type == GL_VERTEX_SHADER)
|
||||||
assign_attribute_locations(prog->_LinkedShaders[0],
|
if (!assign_attribute_locations(prog->_LinkedShaders[0],
|
||||||
prog->Attributes);
|
prog->Attributes))
|
||||||
|
goto done;
|
||||||
|
|
||||||
/* FINISHME: Assign vertex shader output / fragment shader input
|
/* FINISHME: Assign vertex shader output / fragment shader input
|
||||||
* FINISHME: locations.
|
* FINISHME: locations.
|
||||||
|
Reference in New Issue
Block a user