From 8ab5842a6d992956ee365c0e0232c6e6b907863e Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Tue, 1 May 2012 15:10:14 -0700 Subject: [PATCH] glsl: Assign locations for uniforms in UBOs using the std140 rules. Fixes piglit layout-std140. Reviewed-by: Ian Romanick --- src/glsl/glsl_types.cpp | 220 +++++++++++++++++++++++++++++++++++++ src/glsl/glsl_types.h | 13 +++ src/glsl/link_uniforms.cpp | 34 +++++- src/glsl/linker.cpp | 2 + src/glsl/linker.h | 3 + 5 files changed, 270 insertions(+), 2 deletions(-) diff --git a/src/glsl/glsl_types.cpp b/src/glsl/glsl_types.cpp index 8a34b8eb09c..3d786605824 100644 --- a/src/glsl/glsl_types.cpp +++ b/src/glsl/glsl_types.cpp @@ -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 basic machine units, the + * base alignment is . + * + * (2) If the member is a two- or four-component vector with components + * consuming basic machine units, the base alignment is 2 or + * 4, respectively. + * + * (3) If the member is a three-component vector with components consuming + * basic machine units, the base alignment is 4. + */ + 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 column-major matrices with + * columns and rows, the matrix is stored identically to a row of + * * column vectors with components each, according to rule + * (4). + * + * (8) If the member is an array of row-major matrices with columns + * and rows, the matrix is stored identically to a row of * + * row vectors with components each, according to rule (4). + * + * (10) If the member is an array of structures, the 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 columns and + * rows, the matrix is stored identically to an array of + * column vectors with components each, according to + * rule (4). + * + * (7) If the member is a row-major matrix with columns and + * rows, the matrix is stored identically to an array of + * row vectors with 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 , where 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 basic machine units, the + * base alignment is . + * + * (2) If the member is a two- or four-component vector with components + * consuming basic machine units, the base alignment is 2 or + * 4, respectively. + * + * (3) If the member is a three-component vector with components consuming + * basic machine units, the base alignment is 4. + */ + if (this->is_scalar() || this->is_vector()) { + return this->vector_elements * 4; + } + + /* (5) If the member is a column-major matrix with columns and + * rows, the matrix is stored identically to an array of + * column vectors with components each, according to + * rule (4). + * + * (6) If the member is an array of column-major matrices with + * columns and rows, the matrix is stored identically to a row of + * * column vectors with components each, according to rule + * (4). + * + * (7) If the member is a row-major matrix with columns and + * rows, the matrix is stored identically to an array of + * row vectors with components each, according to rule (4). + * + * (8) If the member is an array of row-major matrices with columns + * and rows, the matrix is stored identically to a row of * + * row vectors with 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 structures, the 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 , where 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; +} diff --git a/src/glsl/glsl_types.h b/src/glsl/glsl_types.h index 48d41d7f8e6..bbc524d30eb 100644 --- a/src/glsl/glsl_types.h +++ b/src/glsl/glsl_types.h @@ -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? * diff --git a/src/glsl/link_uniforms.cpp b/src/glsl/link_uniforms.cpp index 4cc97cead80..d7ef5d4d281 100644 --- a/src/glsl/link_uniforms.cpp +++ b/src/glsl/link_uniforms.cpp @@ -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) { diff --git a/src/glsl/linker.cpp b/src/glsl/linker.cpp index dd2278545d8..bfdde4023e9 100644 --- a/src/glsl/linker.cpp +++ b/src/glsl/linker.cpp @@ -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, diff --git a/src/glsl/linker.h b/src/glsl/linker.h index 5c54437a9ea..7d2e98a1536 100644 --- a/src/glsl/linker.h +++ b/src/glsl/linker.h @@ -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 * -- 2.30.2