glsl: Add parsing support for multi-stream output in geometry shaders.
authorSamuel Iglesias Gonsalvez <siglesias@igalia.com>
Tue, 10 Jun 2014 06:45:43 +0000 (08:45 +0200)
committerIago Toral Quiroga <itoral@igalia.com>
Mon, 30 Jun 2014 06:08:49 +0000 (08:08 +0200)
This implements parsing requirements for multi-stream support in
geometry shaders as defined in ARB_gpu_shader5.

Signed-off-by: Samuel Iglesias Gonsalvez <siglesias@igalia.com>
Reviewed-by: Ian Romanick <ian.d.romanick@intel.com>
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.h
src/glsl/glsl_types.h
src/glsl/ir.h

index 56e7bd86f919d5c31c8f1e87e39f1d4fd70c2cc2..15bf086a18ed2bc0a8ab56c1326d0a75030856a1 100644 (file)
@@ -509,6 +509,8 @@ struct ast_type_qualifier {
          /** \name Layout qualifiers for GL_ARB_gpu_shader5 */
          /** \{ */
          unsigned invocations:1;
+         unsigned stream:1; /**< Has stream value assigned  */
+         unsigned explicit_stream:1; /**< stream value assigned explicitly by shader code */
          /** \} */
       }
       /** \brief Set of flags, accessed by name. */
@@ -542,6 +544,9 @@ struct ast_type_qualifier {
    /** Maximum output vertices in GLSL 1.50 geometry shaders. */
    int max_vertices;
 
+   /** Stream in GLSL 1.50 geometry shaders. */
+   unsigned stream;
+
    /** Input or output primitive type in GLSL 1.50 geometry shaders */
    GLenum prim_type;
 
index 7ba04a808c299f5d44656cd965d7b457a21f582b..042ef243b646fb3ffbe7b9e3252bb6abfa86c042 100644 (file)
@@ -2461,6 +2461,11 @@ apply_type_qualifier_to_variable(const struct ast_type_qualifier *qual,
    if (qual->flags.q.sample)
       var->data.sample = 1;
 
+   if (state->stage == MESA_SHADER_GEOMETRY &&
+       qual->flags.q.out && qual->flags.q.stream) {
+      var->data.stream = qual->stream;
+   }
+
    if (qual->flags.q.attribute && state->stage != MESA_SHADER_VERTEX) {
       var->type = glsl_type::error_type;
       _mesa_glsl_error(loc, state,
@@ -5100,6 +5105,9 @@ ast_process_structure_or_interface_block(exec_list *instructions,
          fields[i].centroid = qual->flags.q.centroid ? 1 : 0;
          fields[i].sample = qual->flags.q.sample ? 1 : 0;
 
+         /* Only save explicitly defined streams in block's field */
+         fields[i].stream = qual->flags.q.explicit_stream ? qual->stream : -1;
+
          if (qual->flags.q.row_major || qual->flags.q.column_major) {
             if (!qual->flags.q.uniform) {
                _mesa_glsl_error(&loc, state,
@@ -5548,6 +5556,17 @@ ast_interface_block::hir(exec_list *instructions,
          var->data.sample = fields[i].sample;
          var->init_interface_type(block_type);
 
+         if (fields[i].stream != -1 &&
+             ((unsigned)fields[i].stream) != this->layout.stream) {
+            _mesa_glsl_error(&loc, state,
+                             "stream layout qualifier on "
+                             "interface block member `%s' does not match "
+                             "the interface block (%d vs %d)",
+                             var->name, fields[i].stream, this->layout.stream);
+         }
+
+         var->data.stream = this->layout.stream;
+
          if (redeclaring_per_vertex) {
             ir_variable *earlier =
                get_variable_being_redeclared(var, loc, state,
index 77053d5b12eea965be3d93f2593480cf24bcfd04..017f23d0e3860311b2cdd674e1db582eab5c6346 100644 (file)
@@ -125,9 +125,13 @@ ast_type_qualifier::merge_qualifier(YYLTYPE *loc,
    /* Uniform block layout qualifiers get to overwrite each
     * other (rightmost having priority), while all other
     * qualifiers currently don't allow duplicates.
+    *
+    * Geometry shaders can have several layout qualifiers
+    * assigning different stream values.
     */
 
-   if ((this->flags.i & q.flags.i & ~(ubo_mat_mask.flags.i |
+   if ((state->stage != MESA_SHADER_GEOMETRY) &&
+       (this->flags.i & q.flags.i & ~(ubo_mat_mask.flags.i |
                                      ubo_layout_mask.flags.i |
                                       ubo_binding_mask.flags.i)) != 0) {
       _mesa_glsl_error(loc, state,
@@ -154,6 +158,39 @@ ast_type_qualifier::merge_qualifier(YYLTYPE *loc,
       this->max_vertices = q.max_vertices;
    }
 
+   if (state->stage == MESA_SHADER_GEOMETRY &&
+       state->has_explicit_attrib_stream()) {
+      if (q.flags.q.stream && q.stream >= state->ctx->Const.MaxVertexStreams) {
+         _mesa_glsl_error(loc, state,
+                          "`stream' value is larger than MAX_VERTEX_STREAMS - 1 "
+                          "(%d > %d)",
+                          q.stream, state->ctx->Const.MaxVertexStreams - 1);
+      }
+      if (this->flags.q.explicit_stream &&
+          this->stream >= state->ctx->Const.MaxVertexStreams) {
+         _mesa_glsl_error(loc, state,
+                          "`stream' value is larger than MAX_VERTEX_STREAMS - 1 "
+                          "(%d > %d)",
+                          this->stream, state->ctx->Const.MaxVertexStreams - 1);
+      }
+
+      if (!this->flags.q.explicit_stream) {
+         if (q.flags.q.stream) {
+            this->flags.q.stream = 1;
+            this->stream = q.stream;
+         } else if (!this->flags.q.stream && this->flags.q.out) {
+            /* Assign default global stream value */
+            this->flags.q.stream = 1;
+            this->stream = state->out_qualifier->stream;
+         }
+      } else {
+         if (q.flags.q.explicit_stream) {
+            _mesa_glsl_error(loc, state,
+                             "duplicate layout `stream' qualifier");
+         }
+      }
+   }
+
    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 2409959015d3e9c5d11102ebaa784271ee44fc2c..b9897498ff7fc8071e51b109dff158f385fcccce 100644 (file)
@@ -1395,6 +1395,22 @@ layout_qualifier_id:
          }
       }
 
+      if (state->stage == MESA_SHADER_GEOMETRY) {
+         if (match_layout_qualifier("stream", $1, state) == 0 &&
+             state->check_explicit_attrib_stream_allowed(& @3)) {
+            $$.flags.q.stream = 1;
+
+            if ($3 < 0) {
+               _mesa_glsl_error(& @3, state,
+                                "invalid stream %d specified", $3);
+               YYERROR;
+            } else {
+               $$.flags.q.explicit_stream = 1;
+               $$.stream = $3;
+            }
+         }
+      }
+
       static const char * const local_size_qualifiers[3] = {
          "local_size_x",
          "local_size_y",
@@ -1693,6 +1709,20 @@ storage_qualifier:
    {
       memset(& $$, 0, sizeof($$));
       $$.flags.q.out = 1;
+
+      if (state->stage == MESA_SHADER_GEOMETRY &&
+          state->has_explicit_attrib_stream()) {
+         /* Section 4.3.8.2 (Output Layout Qualifiers) of the GLSL 4.00
+          * spec says:
+          *
+          *     "If the block or variable is declared with the stream
+          *     identifier, it is associated with the specified stream;
+          *     otherwise, it is associated with the current default stream."
+          */
+          $$.flags.q.stream = 1;
+          $$.flags.q.explicit_stream = 0;
+          $$.stream = state->out_qualifier->stream;
+      }
    }
    | UNIFORM
    {
@@ -2361,6 +2391,18 @@ interface_block:
       if (!block->layout.merge_qualifier(& @1, state, $1)) {
          YYERROR;
       }
+
+      foreach_list_typed (ast_declarator_list, member, link, &block->declarations) {
+         ast_type_qualifier& qualifier = member->type->qualifier;
+         if (qualifier.flags.q.stream && qualifier.stream != block->layout.stream) {
+               _mesa_glsl_error(& @1, state,
+                             "stream layout qualifier on "
+                             "interface block member does not match "
+                             "the interface block (%d vs %d)",
+                             qualifier.stream, block->layout.stream);
+               YYERROR;
+         }
+      }
       $$ = block;
    }
    ;
@@ -2434,6 +2476,14 @@ basic_interface_block:
 
       block->layout.flags.i |= block_interface_qualifier;
 
+      if (state->stage == MESA_SHADER_GEOMETRY &&
+          state->has_explicit_attrib_stream()) {
+         /* Assign global layout's stream value. */
+         block->layout.flags.q.stream = 1;
+         block->layout.flags.q.explicit_stream = 0;
+         block->layout.stream = state->out_qualifier->stream;
+      }
+
       foreach_list_typed (ast_declarator_list, member, link, &block->declarations) {
          ast_type_qualifier& qualifier = member->type->qualifier;
          if ((qualifier.flags.i & interface_type_mask) == 0) {
@@ -2576,6 +2626,9 @@ layout_defaults:
          }
          if (!state->out_qualifier->merge_qualifier(& @1, state, $1))
             YYERROR;
+
+         /* Allow future assigments of global out's stream id value */
+         state->out_qualifier->flags.q.explicit_stream = 0;
       }
       $$ = NULL;
    }
index aa4a114e4142e52b114782434335ded9650fd1fc..a0af965fd233c561654d4c47f22da1373039e620 100644 (file)
@@ -119,6 +119,19 @@ struct _mesa_glsl_parse_state {
       return check_version(130, 300, locp, "bit-wise operations are forbidden");
    }
 
+   bool check_explicit_attrib_stream_allowed(YYLTYPE *locp)
+   {
+      if (!this->has_explicit_attrib_stream()) {
+         const char *const requirement = "GL_ARB_gpu_shader5 extension or GLSL 400";
+
+         _mesa_glsl_error(locp, this, "explicit stream requires %s",
+                          requirement);
+         return false;
+      }
+
+      return true;
+   }
+
    bool check_explicit_attrib_location_allowed(YYLTYPE *locp,
                                                const ir_variable *var)
    {
@@ -166,6 +179,11 @@ struct _mesa_glsl_parse_state {
       return true;
    }
 
+   bool has_explicit_attrib_stream() const
+   {
+      return ARB_gpu_shader5_enable || is_version(400, 0);
+   }
+
    bool has_explicit_attrib_location() const
    {
       return ARB_explicit_attrib_location_enable || is_version(330, 300);
index f6d4a02ab68b4f9881ec04d62bc11fc7b53020f2..0b63d4850e1e8a04e1106e119a6f9d747769b488 100644 (file)
@@ -671,6 +671,12 @@ struct glsl_struct_field {
     * in ir_variable::sample). 0 otherwise.
     */
    unsigned sample:1;
+
+   /**
+    * For interface blocks, it has a value if this variable uses multiple vertex
+    * streams (as in ir_variable::stream). -1 otherwise.
+    */
+   int stream;
 };
 
 static inline unsigned int
index b4e52d3d0a51386bef39ea1a329ae26b5a273b89..2e422ddac4adde0b2bbd39c6e43f0bccb04ed35d 100644 (file)
@@ -705,6 +705,11 @@ public:
        */
       int location;
 
+      /**
+       * Vertex stream output identifier.
+       */
+      unsigned stream;
+
       /**
        * output index for dual source blending.
        */