glsl: apply compile-time rules for the offset layout qualifier
authorTimothy Arceri <timothy.arceri@collabora.com>
Wed, 30 Dec 2015 00:09:22 +0000 (11:09 +1100)
committerTimothy Arceri <timothy.arceri@collabora.com>
Sat, 5 Mar 2016 08:38:30 +0000 (19:38 +1100)
This implements the rules for the offset qualifier on block members.

From Section 4.4.5 (Uniform and Shader Storage Block Layout Qualifiers)
of the GLSL 4.50 spec:

   "The offset qualifier can only be used on block members of blocks
   declared with std140 or std430 layouts."

   ...

   "It is a compile-time error to specify an offset that is smaller than
   the offset of the previous member in the block or that lies within the
   previous member of the block."

   ...

   "The specified offset must be a multiple of the base alignment of the
   type of the block member it qualifies, or a compile-time error results."

Reviewed-by: Edward O'Callaghan <eocallaghan@alterapraxis.com>
src/compiler/glsl/ast_to_hir.cpp

index 7e928c48e0e13b8202535ed7cd44bd5e9cba4b58..cd94fa058459fd692e2f6c8f4511d71d200818d9 100644 (file)
@@ -6247,6 +6247,7 @@ ast_process_struct_or_iface_block_members(exec_list *instructions,
                                           unsigned expl_location)
 {
    unsigned decl_count = 0;
+   unsigned next_offset = 0;
 
    /* Make an initial pass over the list of fields to determine how
     * many there are.  Each element in this list is an ast_declarator_list.
@@ -6460,6 +6461,54 @@ ast_process_struct_or_iface_block_members(exec_list *instructions,
             }
          }
 
+         /* Offset can only be used with std430 and std140 layouts an initial
+          * value of 0 is used for error detection.
+          */
+         unsigned align = 0;
+         unsigned size = 0;
+         if (layout) {
+            bool row_major;
+            if (qual->flags.q.row_major ||
+                matrix_layout == GLSL_MATRIX_LAYOUT_ROW_MAJOR) {
+               row_major = true;
+            } else {
+               row_major = false;
+            }
+
+            if(layout->flags.q.std140) {
+               align = field_type->std140_base_alignment(row_major);
+               size = field_type->std140_size(row_major);
+            } else if (layout->flags.q.std430) {
+               align = field_type->std430_base_alignment(row_major);
+               size = field_type->std430_size(row_major);
+            }
+         }
+
+         if (qual->flags.q.explicit_offset) {
+            unsigned qual_offset;
+            if (process_qualifier_constant(state, &loc, "offset",
+                                           qual->offset, &qual_offset)) {
+               if (align != 0 && size != 0) {
+                   if (next_offset > qual_offset)
+                      _mesa_glsl_error(&loc, state, "layout qualifier "
+                                       "offset overlaps previous member");
+
+                  if (qual_offset % align) {
+                     _mesa_glsl_error(&loc, state, "layout qualifier offset "
+                                      "must be a multiple of the base "
+                                      "alignment of %s", field_type->name);
+                  }
+                  next_offset = glsl_align(qual_offset + size, align);
+               } else {
+                  _mesa_glsl_error(&loc, state, "offset can only be used "
+                                   "with std430 and std140 layouts");
+               }
+            }
+         } else {
+            if (align != 0 && size != 0)
+               next_offset = glsl_align(next_offset + size, align);
+         }
+
          /* Propogate row- / column-major information down the fields of the
           * structure or interface block.  Structures need this data because
           * the structure may contain a structure that contains ... a matrix