glsl: Parse the GLSL 1.50 GS layout qualifiers.
authorEric Anholt <eric@anholt.net>
Wed, 12 Jun 2013 21:03:49 +0000 (14:03 -0700)
committerPaul Berry <stereotype441@gmail.com>
Fri, 2 Aug 2013 03:23:33 +0000 (20:23 -0700)
Limited semantic checking (compatibility between declarations, checking
that they're in the right shader target, etc.) is done.

v2: Remove stray debug printfs.

v3 (Paul Berry <stereotype441@gmail.com>): Process input layout
qualifiers at ast_to_hir time rather than at parse time, since certain
error conditions depend on the relative ordering between input layout
qualifiers, declarations, and calls to .length().

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

index d98f1a39b4720303d0e25880e9553c6b08bc02d6..3ef9913a78eede675fa9dd2f4db4dcb980fa4ce2 100644 (file)
@@ -435,6 +435,12 @@ struct ast_type_qualifier {
          unsigned column_major:1;
          unsigned row_major:1;
         /** \} */
+
+        /** \name Layout qualifiers for GLSL 1.50 geometry shaders */
+        /** \{ */
+        unsigned prim_type:1;
+        unsigned max_vertices:1;
+        /** \} */
       }
       /** \brief Set of flags, accessed by name. */
       q;
@@ -461,6 +467,12 @@ struct ast_type_qualifier {
     */
    int index;
 
+   /** Maximum output vertices in GLSL 1.50 geometry shaders. */
+   int max_vertices;
+
+   /** Input or output primitive type in GLSL 1.50 geometry shaders */
+   GLenum prim_type;
+
    /**
     * Binding specified via GL_ARB_shading_language_420pack's "binding" keyword.
     *
@@ -931,6 +943,28 @@ public:
     */
    ast_expression *array_size;
 };
+
+
+/**
+ * AST node representing a declaration of the input layout for geometry
+ * shaders.
+ */
+class ast_gs_input_layout : public ast_node
+{
+public:
+   ast_gs_input_layout(const struct YYLTYPE &locp, GLenum prim_type)
+      : prim_type(prim_type)
+   {
+      set_location(locp);
+   }
+
+   virtual ir_rvalue *hir(exec_list *instructions,
+                          struct _mesa_glsl_parse_state *state);
+
+private:
+   const GLenum prim_type;
+};
+
 /*@}*/
 
 extern void
index f3c49d2271dfa395a90f5b1a6ed121938c501e98..d2170e5e5665977d7856b68340b66553cbc15772 100644 (file)
@@ -72,6 +72,8 @@ _mesa_ast_to_hir(exec_list *instructions, struct _mesa_glsl_parse_state *state)
 
    state->toplevel_ir = instructions;
 
+   state->gs_input_prim_type_specified = false;
+
    /* Section 4.2 of the GLSL 1.20 specification states:
     * "The built-in functions are scoped in a scope outside the global scope
     *  users declare global variables in.  That is, a shader's global scope,
@@ -4451,6 +4453,31 @@ ast_interface_block::hir(exec_list *instructions,
    return NULL;
 }
 
+
+ir_rvalue *
+ast_gs_input_layout::hir(exec_list *instructions,
+                         struct _mesa_glsl_parse_state *state)
+{
+   YYLTYPE loc = this->get_location();
+
+   /* If any geometry input layout declaration preceded this one, make sure it
+    * was consistent with this one.
+    */
+   if (state->gs_input_prim_type_specified &&
+       state->gs_input_prim_type != this->prim_type) {
+      _mesa_glsl_error(&loc, state,
+                       "geometry shader input layout does not match"
+                       " previous declaration");
+      return NULL;
+   }
+
+   state->gs_input_prim_type_specified = true;
+   state->gs_input_prim_type = this->prim_type;
+
+   return NULL;
+}
+
+
 static void
 detect_conflicting_assignments(struct _mesa_glsl_parse_state *state,
                               exec_list *instructions)
index 38c3f8eb02c94148a13279efd32dbecdeee98d37..ce6b6a77145bbe7d411fb2decdf06b82fbce95b2 100644 (file)
@@ -133,6 +133,25 @@ ast_type_qualifier::merge_qualifier(YYLTYPE *loc,
       return false;
    }
 
+   if (q.flags.q.prim_type) {
+      if (this->flags.q.prim_type && this->prim_type != q.prim_type) {
+        _mesa_glsl_error(loc, state,
+                         "conflicting primitive type qualifiers used");
+        return false;
+      }
+      this->prim_type = q.prim_type;
+   }
+
+   if (q.flags.q.max_vertices) {
+      if (this->flags.q.max_vertices && this->max_vertices != q.max_vertices) {
+        _mesa_glsl_error(loc, state,
+                         "geometry shader set conflicting max_vertices "
+                         "(%d and %d)", this->max_vertices, q.max_vertices);
+        return false;
+      }
+      this->max_vertices = q.max_vertices;
+   }
+
    if ((q.flags.i & ubo_mat_mask.flags.i) != 0)
       this->flags.i &= ~ubo_mat_mask.flags.i;
    if ((q.flags.i & ubo_layout_mask.flags.i) != 0)
index fcc5620cd6d56a9905b46f3e86750d1a744d81b1..2c339add62af43e75252020e9f44a5721f49f15c 100644 (file)
@@ -254,6 +254,7 @@ _mesa_glsl_lex(YYSTYPE *val, YYLTYPE *loc, _mesa_glsl_parse_state *state)
 %type <node> for_init_statement
 %type <for_rest_statement> for_rest_statement
 %type <n> integer_constant
+%type <node> layout_defaults
 
 %right THEN ELSE
 %%
@@ -1222,6 +1223,34 @@ layout_qualifier_id:
          }
       }
 
+      /* Layout qualifiers for GLSL 1.50 geometry shaders. */
+      if (!$$.flags.i) {
+         struct {
+            const char *s;
+            GLenum e;
+         } map[] = {
+                 { "points", GL_POINTS },
+                 { "lines", GL_LINES },
+                 { "lines_adjacency", GL_LINES_ADJACENCY },
+                 { "line_strip", GL_LINE_STRIP },
+                 { "triangles", GL_TRIANGLES },
+                 { "triangles_adjacency", GL_TRIANGLES_ADJACENCY },
+                 { "triangle_strip", GL_TRIANGLE_STRIP },
+         };
+         for (unsigned i = 0; i < Elements(map); i++) {
+            if (strcmp($1, map[i].s) == 0) {
+               $$.flags.q.prim_type = 1;
+               $$.prim_type = map[i].e;
+               break;
+            }
+         }
+
+         if ($$.flags.i && !state->is_version(150, 0)) {
+            _mesa_glsl_error(& @1, state, "#version 150 layout "
+                             "qualifier `%s' used", $1);
+         }
+      }
+
       if (!$$.flags.i) {
          _mesa_glsl_error(& @1, state, "unrecognized layout identifier "
                           "`%s'", $1);
@@ -1264,6 +1293,23 @@ layout_qualifier_id:
          $$.binding = $3;
       }
 
+      if (strcmp("max_vertices", $1) == 0) {
+         $$.flags.q.max_vertices = 1;
+
+         if ($3 < 0) {
+            _mesa_glsl_error(& @3, state,
+                             "invalid max_vertices %d specified", $3);
+            YYERROR;
+         } else {
+            $$.max_vertices = $3;
+            if (!state->is_version(150, 0)) {
+               _mesa_glsl_error(& @3, state,
+                                "#version 150 max_vertices qualifier "
+                                "specified", $3);
+            }
+         }
+      }
+
       /* If the identifier didn't match any known layout identifiers,
        * emit an error.
        */
@@ -2046,7 +2092,7 @@ external_declaration:
    function_definition      { $$ = $1; }
    | declaration            { $$ = $1; }
    | pragma_statement       { $$ = NULL; }
-   | layout_defaults        { $$ = NULL; }
+   | layout_defaults        { $$ = $1; }
    ;
 
 function_definition:
@@ -2263,4 +2309,32 @@ layout_defaults:
       if (!state->default_uniform_qualifier->merge_qualifier(& @1, state, $1)) {
          YYERROR;
       }
+      $$ = NULL;
+   }
+
+   | layout_qualifier IN_TOK ';'
+   {
+      void *ctx = state;
+      if (state->target != geometry_shader) {
+         _mesa_glsl_error(& @1, state,
+                          "input layout qualifiers only valid in "
+                          "geometry shaders");
+      } else if (!$1.flags.q.prim_type) {
+         _mesa_glsl_error(& @1, state,
+                          "input layout qualifiers must specify a primitive"
+                          " type");
+      }
+      $$ = new(ctx) ast_gs_input_layout(@1, $1.prim_type);
+   }
+
+   | layout_qualifier OUT_TOK ';'
+   {
+      if (state->target != geometry_shader) {
+         _mesa_glsl_error(& @1, state,
+                          "out layout qualifiers only valid in "
+                          "geometry shaders");
+      } else if (!state->out_qualifier->merge_qualifier(& @1, state, $1)) {
+         YYERROR;
+      }
+      $$ = NULL;
    }
index a5bc20c2384c81dec38de4235e9f9e9bc7b26451..a962742e439d03cc0fe2aaf913925e2553a66309 100644 (file)
@@ -159,6 +159,10 @@ _mesa_glsl_parse_state::_mesa_glsl_parse_state(struct gl_context *_ctx,
    this->default_uniform_qualifier = new(this) ast_type_qualifier();
    this->default_uniform_qualifier->flags.q.shared = 1;
    this->default_uniform_qualifier->flags.q.column_major = 1;
+
+   this->gs_input_prim_type_specified = false;
+   this->gs_input_prim_type = GL_POINTS;
+   this->out_qualifier = new(this) ast_type_qualifier();
 }
 
 /**
index 1e386dd3132a60fa8f55e3b1754093e958e5f928..7c563ea7f60435c96074f830463c3fb4554909ca 100644 (file)
@@ -165,6 +165,24 @@ struct _mesa_glsl_parse_state {
     */
    struct ast_type_qualifier *default_uniform_qualifier;
 
+   /**
+    * True if a geometry shader input primitive type was specified using a
+    * layout directive.
+    *
+    * Note: this value is computed at ast_to_hir time rather than at parse
+    * time.
+    */
+   bool gs_input_prim_type_specified;
+
+   /**
+    * If gs_input_prim_type_specified is true, the primitive type that was
+    * specified.  Otherwise ignored.
+    */
+   GLenum gs_input_prim_type;
+
+   /** Output layout qualifiers from GLSL 1.50. (geometry shader controls)*/
+   struct ast_type_qualifier *out_qualifier;
+
    /**
     * Printable list of GLSL versions supported by the current context
     *