glsl: Assign locations for uniforms in UBOs using the std140 rules.

Fixes piglit layout-std140.

Reviewed-by: Ian Romanick <ian.d.romanick@intel.com>
This commit is contained in:
Eric Anholt 2012-05-01 15:10:14 -07:00
parent 9feb403b0e
commit 8ab5842a6d
5 changed files with 270 additions and 2 deletions

View file

@ -628,3 +628,223 @@ glsl_type::can_implicitly_convert_to(const glsl_type *desired) const
&& this->is_integer()
&& this->vector_elements == desired->vector_elements;
}
unsigned
glsl_type::std140_base_alignment(bool row_major) const
{
/* (1) If the member is a scalar consuming <N> basic machine units, the
* base alignment is <N>.
*
* (2) If the member is a two- or four-component vector with components
* consuming <N> basic machine units, the base alignment is 2<N> or
* 4<N>, respectively.
*
* (3) If the member is a three-component vector with components consuming
* <N> basic machine units, the base alignment is 4<N>.
*/
if (this->is_scalar() || this->is_vector()) {
switch (this->vector_elements) {
case 1:
return 4;
case 2:
return 8;
case 3:
case 4:
return 16;
}
}
/* (4) If the member is an array of scalars or vectors, the base alignment
* and array stride are set to match the base alignment of a single
* array element, according to rules (1), (2), and (3), and rounded up
* to the base alignment of a vec4. The array may have padding at the
* end; the base offset of the member following the array is rounded up
* to the next multiple of the base alignment.
*
* (6) If the member is an array of <S> column-major matrices with <C>
* columns and <R> rows, the matrix is stored identically to a row of
* <S>*<C> column vectors with <R> components each, according to rule
* (4).
*
* (8) If the member is an array of <S> row-major matrices with <C> columns
* and <R> rows, the matrix is stored identically to a row of <S>*<R>
* row vectors with <C> components each, according to rule (4).
*
* (10) If the member is an array of <S> structures, the <S> elements of
* the array are laid out in order, according to rule (9).
*/
if (this->is_array()) {
if (this->fields.array->is_scalar() ||
this->fields.array->is_vector() ||
this->fields.array->is_matrix()) {
return MAX2(this->fields.array->std140_base_alignment(row_major), 16);
} else {
assert(this->fields.array->is_record());
return this->fields.array->std140_base_alignment(row_major);
}
}
/* (5) If the member is a column-major matrix with <C> columns and
* <R> rows, the matrix is stored identically to an array of
* <C> column vectors with <R> components each, according to
* rule (4).
*
* (7) If the member is a row-major matrix with <C> columns and <R>
* rows, the matrix is stored identically to an array of <R>
* row vectors with <C> components each, according to rule (4).
*/
if (this->is_matrix()) {
const struct glsl_type *vec_type;
if (row_major) {
vec_type = get_instance(GLSL_TYPE_FLOAT, this->vector_elements, 1);
} else {
vec_type = get_instance(GLSL_TYPE_FLOAT, this->matrix_columns, 1);
}
return vec_type->std140_base_alignment(false);
}
/* (9) If the member is a structure, the base alignment of the
* structure is <N>, where <N> is the largest base alignment
* value of any of its members, and rounded up to the base
* alignment of a vec4. The individual members of this
* sub-structure are then assigned offsets by applying this set
* of rules recursively, where the base offset of the first
* member of the sub-structure is equal to the aligned offset
* of the structure. The structure may have padding at the end;
* the base offset of the member following the sub-structure is
* rounded up to the next multiple of the base alignment of the
* structure.
*/
if (this->is_record()) {
unsigned base_alignment = 16;
for (unsigned i = 0; i < this->length; i++) {
const struct glsl_type *field_type = this->fields.structure[i].type;
base_alignment = MAX2(base_alignment,
field_type->std140_base_alignment(row_major));
}
return base_alignment;
}
assert(!"not reached");
return -1;
}
static unsigned
align(unsigned val, unsigned align)
{
return (val + align - 1) / align * align;
}
unsigned
glsl_type::std140_size(bool row_major) const
{
/* (1) If the member is a scalar consuming <N> basic machine units, the
* base alignment is <N>.
*
* (2) If the member is a two- or four-component vector with components
* consuming <N> basic machine units, the base alignment is 2<N> or
* 4<N>, respectively.
*
* (3) If the member is a three-component vector with components consuming
* <N> basic machine units, the base alignment is 4<N>.
*/
if (this->is_scalar() || this->is_vector()) {
return this->vector_elements * 4;
}
/* (5) If the member is a column-major matrix with <C> columns and
* <R> rows, the matrix is stored identically to an array of
* <C> column vectors with <R> components each, according to
* rule (4).
*
* (6) If the member is an array of <S> column-major matrices with <C>
* columns and <R> rows, the matrix is stored identically to a row of
* <S>*<C> column vectors with <R> components each, according to rule
* (4).
*
* (7) If the member is a row-major matrix with <C> columns and <R>
* rows, the matrix is stored identically to an array of <R>
* row vectors with <C> components each, according to rule (4).
*
* (8) If the member is an array of <S> row-major matrices with <C> columns
* and <R> rows, the matrix is stored identically to a row of <S>*<R>
* row vectors with <C> components each, according to rule (4).
*/
if (this->is_matrix() || (this->is_array() &&
this->fields.array->is_matrix())) {
const struct glsl_type *element_type;
const struct glsl_type *vec_type;
unsigned int array_len;
if (this->is_array()) {
element_type = this->fields.array;
array_len = this->length;
} else {
element_type = this;
array_len = 1;
}
if (row_major) {
vec_type = get_instance(GLSL_TYPE_FLOAT,
element_type->matrix_columns, 1);
array_len *= element_type->vector_elements;
} else {
vec_type = get_instance(GLSL_TYPE_FLOAT,
element_type->vector_elements, 1);
array_len *= element_type->matrix_columns;
}
const glsl_type *array_type = glsl_type::get_array_instance(vec_type,
array_len);
return array_type->std140_size(false);
}
/* (4) If the member is an array of scalars or vectors, the base alignment
* and array stride are set to match the base alignment of a single
* array element, according to rules (1), (2), and (3), and rounded up
* to the base alignment of a vec4. The array may have padding at the
* end; the base offset of the member following the array is rounded up
* to the next multiple of the base alignment.
*
* (10) If the member is an array of <S> structures, the <S> elements of
* the array are laid out in order, according to rule (9).
*/
if (this->is_array()) {
if (this->fields.array->is_record()) {
return this->length * this->fields.array->std140_size(row_major);
} else {
unsigned element_base_align =
this->fields.array->std140_base_alignment(row_major);
return this->length * MAX2(element_base_align, 16);
}
}
/* (9) If the member is a structure, the base alignment of the
* structure is <N>, where <N> is the largest base alignment
* value of any of its members, and rounded up to the base
* alignment of a vec4. The individual members of this
* sub-structure are then assigned offsets by applying this set
* of rules recursively, where the base offset of the first
* member of the sub-structure is equal to the aligned offset
* of the structure. The structure may have padding at the end;
* the base offset of the member following the sub-structure is
* rounded up to the next multiple of the base alignment of the
* structure.
*/
if (this->is_record()) {
unsigned size = 0;
for (unsigned i = 0; i < this->length; i++) {
const struct glsl_type *field_type = this->fields.structure[i].type;
unsigned align = field_type->std140_base_alignment(row_major);
size = (size + align - 1) / align * align;
size += field_type->std140_size(row_major);
}
size = align(size,
this->fields.structure[0].type->std140_base_alignment(row_major));
return size;
}
assert(!"not reached");
return -1;
}

View file

@ -242,6 +242,19 @@ struct glsl_type {
*/
unsigned component_slots() const;
/**
* Alignment in bytes of the start of this type in a std140 uniform
* block.
*/
unsigned std140_base_alignment(bool row_major) const;
/** Size in bytes of this type in a std140 uniform block.
*
* Note that this is not GL_UNIFORM_SIZE (which is the number of
* elements in the array)
*/
unsigned std140_size(bool row_major) const;
/**
* \brief Can this type be implicitly converted to another?
*

View file

@ -29,6 +29,12 @@
#include "program/hash_table.h"
#include "program.h"
static inline unsigned int
align(unsigned int a, unsigned int align)
{
return (a + align - 1) / align * align;
}
/**
* \file link_uniforms.cpp
* Assign locations for GLSL uniforms.
@ -317,9 +323,11 @@ private:
if (this->ubo_var) {
this->uniforms[id].block_index = this->ubo_block_index;
/* FINISHME: Actual std140 offset assignment. */
unsigned alignment = type->std140_base_alignment(ubo_var->RowMajor);
this->ubo_byte_offset = align(this->ubo_byte_offset, alignment);
this->uniforms[id].offset = this->ubo_byte_offset;
this->ubo_byte_offset += 4 * type->components();
this->ubo_byte_offset += type->std140_size(ubo_var->RowMajor);
this->uniforms[id].array_stride = 0;
this->uniforms[id].matrix_stride = 0;
this->uniforms[id].row_major = base_type->is_matrix() &&
@ -453,6 +461,28 @@ link_update_uniform_buffer_variables(struct gl_shader *shader)
return true;
}
void
link_assign_uniform_block_offsets(struct gl_shader *shader)
{
for (unsigned b = 0; b < shader->NumUniformBlocks; b++) {
struct gl_uniform_block *block = &shader->UniformBlocks[b];
unsigned offset = 0;
for (unsigned int i = 0; i < block->NumUniforms; i++) {
struct gl_uniform_buffer_variable *ubo_var = &block->Uniforms[i];
const struct glsl_type *type = ubo_var->Type;
unsigned alignment = type->std140_base_alignment(ubo_var->RowMajor);
unsigned size = type->std140_size(ubo_var->RowMajor);
offset = align(offset, alignment);
ubo_var->Offset = offset;
offset += size;
}
block->UniformBufferSize = offset;
}
}
void
link_assign_uniform_locations(struct gl_shader_program *prog)
{

View file

@ -979,6 +979,8 @@ link_intrastage_shaders(void *mem_ctx,
struct gl_shader *sh = shader_list[i];
for (unsigned j = 0; j < shader_list[i]->NumUniformBlocks; j++) {
link_assign_uniform_block_offsets(shader_list[i]);
int index = link_cross_validate_uniform_block(mem_ctx,
&uniform_blocks,
&num_uniform_blocks,

View file

@ -46,6 +46,9 @@ link_cross_validate_uniform_block(void *mem_ctx,
unsigned int *num_linked_blocks,
struct gl_uniform_block *new_block);
void
link_assign_uniform_block_offsets(struct gl_shader *shader);
/**
* Class for processing all of the leaf fields of an uniform
*