glsl: Assign locations for uniforms in UBOs using the std140 rules.
authorEric Anholt <eric@anholt.net>
Tue, 1 May 2012 22:10:14 +0000 (15:10 -0700)
committerEric Anholt <eric@anholt.net>
Fri, 20 Jul 2012 17:44:04 +0000 (10:44 -0700)
Fixes piglit layout-std140.

Reviewed-by: Ian Romanick <ian.d.romanick@intel.com>
src/glsl/glsl_types.cpp
src/glsl/glsl_types.h
src/glsl/link_uniforms.cpp
src/glsl/linker.cpp
src/glsl/linker.h

index 8a34b8eb09c9e16384142e4e548d9b61d73a560f..3d7866058240cfc44c385b191d6af6e1d5778c7f 100644 (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;
+}
index 48d41d7f8e68f3ed07c37026317e2815abee7768..bbc524d30eb22f26a7e879c25e031b5e62a7cf6f 100644 (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?
     *
index 4cc97cead80156becb737cd57fe57365760ae304..d7ef5d4d2811b84293954ddcb4109b6e41625ee7 100644 (file)
 #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)
 {
index dd2278545d83329a5048732f16aea5f1866ba672..bfdde4023e96a8411110841df3e700fd0e573b03 100644 (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,
index 5c54437a9eae5a94c39935e5790d33ab9a35679d..7d2e98a1536f551ff6836747cc1b923570880642 100644 (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
  *