glsl: Implement rules for geometry shader input sizes.
authorPaul Berry <stereotype441@gmail.com>
Wed, 31 Jul 2013 04:13:48 +0000 (21:13 -0700)
committerPaul Berry <stereotype441@gmail.com>
Fri, 2 Aug 2013 03:24:39 +0000 (20:24 -0700)
Section 4.3.8.1 (Input Layout Qualifiers) of the GLSL 1.50 spec
contains some tricky rules for how the sizes of geometry shader input
arrays are related to the input layout specification.  In essence,
those rules boil down to the following:

- If an input array declaration does not specify a size, and it
  follows an input layout declaration, it is sized according to the
  input layout.

- If an input layout declaration follows an input array declaration
  that didn't specify a size, the input array declaration is given a
  size at the time the input layout declaration appears.

- All input layout declarations and input array sizes must ultimately
  match.  Inconsistencies are reported as soon as they are detected,
  at compile time if the inconsistency is within one compilation unit,
  otherwise at link time.

- At least one compilation unit must contain an input layout
  declaration.

(Note: the geom_array_resize_visitor class was contributed by Bryan
Cain <bryancain3@gmail.com>.)

Reviewed-by: Ian Romanick <ian.d.romanick@intel.com>
Reviewed-by: Kenneth Graunke <kenneth@whitecape.org>
src/glsl/ast_to_hir.cpp
src/glsl/glsl_parser_extras.h
src/glsl/linker.cpp

index e48e51c6d41b084a9165107b149670fb2d306e51..39d0b76d18d3ceb78c11734044ad8e1bce6c648c 100644 (file)
@@ -2523,6 +2523,73 @@ process_initializer(ir_variable *var, ast_declaration *decl,
    return result;
 }
 
+
+/**
+ * Do additional processing necessary for geometry shader input array
+ * declarations (this covers both interface blocks arrays and input variable
+ * arrays).
+ */
+static void
+handle_geometry_shader_input_decl(struct _mesa_glsl_parse_state *state,
+                                  YYLTYPE loc, ir_variable *var)
+{
+   unsigned num_vertices = 0;
+   if (state->gs_input_prim_type_specified) {
+      num_vertices = vertices_per_prim(state->gs_input_prim_type);
+   }
+
+   assert(var->type->is_array());
+   if (var->type->length == 0) {
+      /* Section 4.3.8.1 (Input Layout Qualifiers) of the GLSL 1.50 spec says:
+       *
+       *   All geometry shader input unsized array declarations will be
+       *   sized by an earlier input layout qualifier, when present, as per
+       *   the following table.
+       *
+       * Followed by a table mapping each allowed input layout qualifier to
+       * the corresponding input length.
+       */
+      if (num_vertices != 0)
+         var->type = glsl_type::get_array_instance(var->type->fields.array,
+                                                   num_vertices);
+   } else {
+      /* Section 4.3.8.1 (Input Layout Qualifiers) of the GLSL 1.50 spec
+       * includes the following examples of compile-time errors:
+       *
+       *   // code sequence within one shader...
+       *   in vec4 Color1[];    // size unknown
+       *   ...Color1.length()...// illegal, length() unknown
+       *   in vec4 Color2[2];   // size is 2
+       *   ...Color1.length()...// illegal, Color1 still has no size
+       *   in vec4 Color3[3];   // illegal, input sizes are inconsistent
+       *   layout(lines) in;    // legal, input size is 2, matching
+       *   in vec4 Color4[3];   // illegal, contradicts layout
+       *   ...
+       *
+       * To detect the case illustrated by Color3, we verify that the size of
+       * an explicitly-sized array matches the size of any previously declared
+       * explicitly-sized array.  To detect the case illustrated by Color4, we
+       * verify that the size of an explicitly-sized array is consistent with
+       * any previously declared input layout.
+       */
+      if (num_vertices != 0 && var->type->length != num_vertices) {
+         _mesa_glsl_error(&loc, state,
+                          "geometry shader input size contradicts previously"
+                          " declared layout (size is %u, but layout requires a"
+                          " size of %u)", var->type->length, num_vertices);
+      } else if (state->gs_input_size != 0 &&
+                 var->type->length != state->gs_input_size) {
+         _mesa_glsl_error(&loc, state,
+                          "geometry shader input sizes are "
+                          "inconsistent (size is %u, but a previous "
+                          "declaration has size %u)",
+                          var->type->length, state->gs_input_size);
+      } else {
+         state->gs_input_size = var->type->length;
+      }
+   }
+}
+
 ir_rvalue *
 ast_declarator_list::hir(exec_list *instructions,
                         struct _mesa_glsl_parse_state *state)
@@ -2833,6 +2900,8 @@ ast_declarator_list::hir(exec_list *instructions,
                _mesa_glsl_error(&loc, state,
                                 "geometry shader inputs must be arrays");
             }
+
+            handle_geometry_shader_input_decl(state, loc, var);
          }
       }
 
@@ -4450,6 +4519,8 @@ ast_interface_block::hir(exec_list *instructions,
       }
 
       var->interface_type = block_type;
+      if (state->target == geometry_shader)
+         handle_geometry_shader_input_decl(state, loc, var);
       state->symbols->add_variable(var);
       instructions->push_tail(var);
    } else {
@@ -4498,9 +4569,50 @@ ast_gs_input_layout::hir(exec_list *instructions,
       return NULL;
    }
 
+   /* If any shader inputs occurred before this declaration and specified an
+    * array size, make sure the size they specified is consistent with the
+    * primitive type.
+    */
+   unsigned num_vertices = vertices_per_prim(this->prim_type);
+   if (state->gs_input_size != 0 && state->gs_input_size != num_vertices) {
+      _mesa_glsl_error(&loc, state,
+                       "this geometry shader input layout implies %u vertices"
+                       " per primitive, but a previous input is declared"
+                       " with size %u", num_vertices, state->gs_input_size);
+      return NULL;
+   }
+
    state->gs_input_prim_type_specified = true;
    state->gs_input_prim_type = this->prim_type;
 
+   /* If any shader inputs occurred before this declaration and did not
+    * specify an array size, their size is determined now.
+    */
+   foreach_list (node, instructions) {
+      ir_variable *var = ((ir_instruction *) node)->as_variable();
+      if (var == NULL || var->mode != ir_var_shader_in)
+         continue;
+
+      /* Note: gl_PrimitiveIDIn has mode ir_var_shader_in, but it's not an
+       * array; skip it.
+       */
+      if (!var->type->is_array())
+         continue;
+
+      if (var->type->length == 0) {
+         if (var->max_array_access >= num_vertices) {
+            _mesa_glsl_error(&loc, state,
+                             "this geometry shader input layout implies %u"
+                             " vertices, but an access to element %u of input"
+                             " `%s' already exists", num_vertices,
+                             var->max_array_access, var->name);
+         } else {
+            var->type = glsl_type::get_array_instance(var->type->fields.array,
+                                                      num_vertices);
+         }
+      }
+   }
+
    return NULL;
 }
 
index 7c563ea7f60435c96074f830463c3fb4554909ca..b9ca4e3a4d66d2381165a91c4eedab2a0aa958c8 100644 (file)
@@ -316,6 +316,15 @@ struct _mesa_glsl_parse_state {
    /** Shaders containing built-in functions that are used for linking. */
    struct gl_shader *builtins_to_link[16];
    unsigned num_builtins_to_link;
+
+   /**
+    * For geometry shaders, size of the most recently seen input declaration
+    * that was a sized array, or 0 if no sized input array declarations have
+    * been seen.
+    *
+    * Unused for other shader types.
+    */
+   unsigned gs_input_size;
 };
 
 # define YYLLOC_DEFAULT(Current, Rhs, N)                       \
index 2a17d0652608c28c4f2430dd00faddc1b6f2d431..94ea54ceb6907a727d37b60e5b8ff475275c6115 100644 (file)
@@ -178,6 +178,77 @@ private:
 };
 
 
+class geom_array_resize_visitor : public ir_hierarchical_visitor {
+public:
+   unsigned num_vertices;
+   gl_shader_program *prog;
+
+   geom_array_resize_visitor(unsigned num_vertices, gl_shader_program *prog)
+   {
+      this->num_vertices = num_vertices;
+      this->prog = prog;
+   }
+
+   virtual ~geom_array_resize_visitor()
+   {
+      /* empty */
+   }
+
+   virtual ir_visitor_status visit(ir_variable *var)
+   {
+      if (!var->type->is_array() || var->mode != ir_var_shader_in)
+         return visit_continue;
+
+      unsigned size = var->type->length;
+
+      /* Generate a link error if the shader has declared this array with an
+       * incorrect size.
+       */
+      if (size && size != this->num_vertices) {
+         linker_error(this->prog, "size of array %s declared as %u, "
+                      "but number of input vertices is %u\n",
+                      var->name, size, this->num_vertices);
+         return visit_continue;
+      }
+
+      /* Generate a link error if the shader attempts to access an input
+       * array using an index too large for its actual size assigned at link
+       * time.
+       */
+      if (var->max_array_access >= this->num_vertices) {
+         linker_error(this->prog, "geometry shader accesses element %i of "
+                      "%s, but only %i input vertices\n",
+                      var->max_array_access, var->name, this->num_vertices);
+         return visit_continue;
+      }
+
+      var->type = glsl_type::get_array_instance(var->type->element_type(),
+                                                this->num_vertices);
+      var->max_array_access = this->num_vertices - 1;
+
+      return visit_continue;
+   }
+
+   /* Dereferences of input variables need to be updated so that their type
+    * matches the newly assigned type of the variable they are accessing. */
+   virtual ir_visitor_status visit(ir_dereference_variable *ir)
+   {
+      ir->type = ir->var->type;
+      return visit_continue;
+   }
+
+   /* Dereferences of 2D input arrays need to be updated so that their type
+    * matches the newly assigned type of the array they are accessing. */
+   virtual ir_visitor_status visit_leave(ir_dereference_array *ir)
+   {
+      const glsl_type *const vt = ir->array->type;
+      if (vt->is_array())
+         ir->type = vt->element_type();
+      return visit_continue;
+   }
+};
+
+
 void
 linker_error(gl_shader_program *prog, const char *fmt, ...)
 {
@@ -1173,6 +1244,16 @@ link_intrastage_shaders(void *mem_ctx,
    if (linked)
       validate_ir_tree(linked->ir);
 
+   /* Set the size of geometry shader input arrays */
+   if (linked->Type == GL_GEOMETRY_SHADER) {
+      unsigned num_vertices = vertices_per_prim(prog->Geom.InputType);
+      geom_array_resize_visitor input_resize_visitor(num_vertices, prog);
+      foreach_iter(exec_list_iterator, iter, *linked->ir) {
+         ir_instruction *ir = (ir_instruction *)iter.get();
+         ir->accept(&input_resize_visitor);
+      }
+   }
+
    /* Make a pass over all variable declarations to ensure that arrays with
     * unspecified sizes have a size specified.  The size is inferred from the
     * max_array_access field.