[Mesa-dev] [PATCH 18/18] glsl: Assign locations for uniforms in UBOs using the std140 rules.

Eric Anholt eric at anholt.net
Mon Jul 2 17:38:27 PDT 2012


Fixes piglit layout-std140.
---
 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 8a34b8e..3d78660 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 <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;
+}
diff --git a/src/glsl/glsl_types.h b/src/glsl/glsl_types.h
index 48d41d7..bbc524d 100644
--- a/src/glsl/glsl_types.h
+++ b/src/glsl/glsl_types.h
@@ -243,6 +243,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?
     *
     * \return True if the types are identical or if this type can be converted
diff --git a/src/glsl/link_uniforms.cpp b/src/glsl/link_uniforms.cpp
index 4cc97ce..d7ef5d4 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() &&
@@ -454,6 +462,28 @@ link_update_uniform_buffer_variables(struct gl_shader *shader)
 }
 
 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)
 {
    ralloc_free(prog->UniformStorage);
diff --git a/src/glsl/linker.cpp b/src/glsl/linker.cpp
index 4267602..dd2a036 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 5c54437..7d2e98a 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
  *
-- 
1.7.10



More information about the mesa-dev mailing list