[Mesa-dev] [PATCH v2 12/23] glsl: Validate vertex emission in geometry shaders.

Ian Romanick idr at freedesktop.org
Wed Jun 18 13:38:43 PDT 2014


On 06/18/2014 02:51 AM, Iago Toral Quiroga wrote:
> Check if non-zero streams are used. Fail to link if emitting to unsupported
> streams or emitting to non-zero streams with output type other than GL_POINTS.
> ---
>  src/glsl/linker.cpp | 148 +++++++++++++++++++++++++++++++++++++++++++++++-----
>  1 file changed, 134 insertions(+), 14 deletions(-)
> 
> diff --git a/src/glsl/linker.cpp b/src/glsl/linker.cpp
> index 0b6a716..f8ff138 100644
> --- a/src/glsl/linker.cpp
> +++ b/src/glsl/linker.cpp
> @@ -250,31 +250,100 @@ public:
>     }
>  };
>  
> -
>  /**
> - * Visitor that determines whether or not a shader uses ir_end_primitive.
> + * Visitor that determines the highest stream id to which a (geometry) shader
> + * emits vertices. It also checks whether End{Stream}Primitive is ever called.
>   */
> -class find_end_primitive_visitor : public ir_hierarchical_visitor {
> +class find_emit_vertex_visitor : public ir_hierarchical_visitor {
>  public:
> -   find_end_primitive_visitor()
> -      : found(false)
> +   find_emit_vertex_visitor(int max_allowed)
> +      : max_stream_allowed(max_allowed),
> +        invalid_stream_id(0),
> +        invalid_stream_id_from_emit_vertex(false),
> +        end_primitive_found(false),
> +        uses_non_zero_stream(false)
>     {
>        /* empty */
>     }
>  
> -   virtual ir_visitor_status visit(ir_end_primitive *)
> +   virtual ir_visitor_status visit_leave(ir_emit_vertex *ir)
>     {
> -      found = true;
> -      return visit_stop;
> +      int stream_id = ir->stream_id();
> +
> +      if (stream_id < 0) {
> +         invalid_stream_id = stream_id;
> +         invalid_stream_id_from_emit_vertex = true;
> +         return visit_stop;
> +      }
> +
> +      if (stream_id > max_stream_allowed) {
> +         invalid_stream_id = stream_id;
> +         invalid_stream_id_from_emit_vertex = true;
> +         return visit_stop;
> +      }
> +
> +      if (stream_id != 0)
> +         uses_non_zero_stream = true;
> +
> +      return visit_continue;
>     }
>  
> -   bool end_primitive_found()
> +   virtual ir_visitor_status visit_leave(ir_end_primitive *ir)
>     {
> -      return found;
> +      end_primitive_found = true;
> +
> +      int stream_id = ir->stream_id();
> +
> +      if (stream_id < 0) {
> +         invalid_stream_id = stream_id;
> +         invalid_stream_id_from_emit_vertex = false;
> +         return visit_stop;
> +      }
> +
> +      if (stream_id > max_stream_allowed) {
> +         invalid_stream_id = stream_id;
> +         invalid_stream_id_from_emit_vertex = false;
> +         return visit_stop;
> +      }
> +
> +      if (stream_id != 0)
> +         uses_non_zero_stream = true;
> +
> +      return visit_continue;
> +   }
> +
> +   bool error()
> +   {
> +      return invalid_stream_id != 0;
> +   }
> +
> +   const char *error_func()
> +   {
> +      return invalid_stream_id_from_emit_vertex ?
> +         "EmitStreamVertex" : "EndStreamPrimitive";
> +   }
> +
> +   int error_stream()
> +   {
> +      return invalid_stream_id;
> +   }
> +
> +   bool uses_streams()
> +   {
> +      return uses_non_zero_stream;
> +   }
> +
> +   bool uses_end_primitive()
> +   {
> +      return end_primitive_found;
>     }
>  
>  private:
> -   bool found;
> +   int max_stream_allowed;
> +   int invalid_stream_id;
> +   bool invalid_stream_id_from_emit_vertex;
> +   bool end_primitive_found;
> +   bool uses_non_zero_stream;
>  };
>  
>  } /* anonymous namespace */
> @@ -551,10 +620,58 @@ validate_geometry_shader_executable(struct gl_shader_program *prog,
>  
>     analyze_clip_usage(prog, shader, &prog->Geom.UsesClipDistance,
>                        &prog->Geom.ClipDistanceArraySize);
> +}
> +
> +/**
> + * Check if geometry shaders emit to non-zero streams and do corresponding
> + * validations.
> + */
> +static void
> +validate_geometry_shader_emissions(struct gl_context *ctx,
> +                                   struct gl_shader_program *prog)
> +{
> +   if (prog->_LinkedShaders[MESA_SHADER_GEOMETRY] != NULL) {
> +      find_emit_vertex_visitor emit_vertex(ctx->Const.MaxVertexStreams - 1);
> +      emit_vertex.run(prog->_LinkedShaders[MESA_SHADER_GEOMETRY]->ir);
> +      if (emit_vertex.error()) {
> +         linker_error(prog, "Invalid call %s(%d). Accepted values for the "
> +                      "stream parameter are in the range [0, %d].",
> +                      emit_vertex.error_func(),
> +                      emit_vertex.error_stream(),
> +                      ctx->Const.MaxVertexStreams - 1);
> +      }
> +      prog->Geom.UsesStreams = emit_vertex.uses_streams();
> +      prog->Geom.UsesEndPrimitive = emit_vertex.uses_end_primitive();
>  
> -   find_end_primitive_visitor end_primitive;
> -   end_primitive.run(shader->ir);
> -   prog->Geom.UsesEndPrimitive = end_primitive.end_primitive_found();
> +      /* From the ARB_gpu_shader5 spec:
> +       *
> +       *   "Multiple vertex streams are supported only if the output primitive
> +       *    type is declared to be "points".  A program will fail to link if it
> +       *    contains a geometry shader calling EmitStreamVertex() or
> +       *    EndStreamPrimitive() if its output primitive type is not "points".
> +       *
> +       * However, in the same spec:
> +       *
> +       *   "The function EmitVertex() is equivalent to calling EmitStreamVertex()
> +       *    with <stream> set to zero."
> +       *
> +       * And:
> +       *
> +       *   "The function EndPrimitive() is equivalent to calling
> +       *    EndStreamPrimitive() with <stream> set to zero."
> +       *
> +       * Since we can call EmitVertex() and EndPrimitive() when we output
> +       * primitives other than points, calling EmitStreamVertex(0) or
> +       * EmitEndPrimitive(0) should not produce errors. This it also what Nvidia
> +       * does. Currently we only set prog->Geom.UsesStreams to TRUE when
> +       * EmitStreamVertex() or EmitEndPrimitive() are called with a non-zero
> +       * stream.

Does AMD also behave this way?  If so, I can submit a spec bug to make
these explicitly legal.  Otherwise, I think we should not allow it.  We
probably ought to check Apple and the Intel Windows driver too...

> +       */
> +      if (prog->Geom.UsesStreams && prog->Geom.OutputType != GL_POINTS) {
> +         linker_error(prog, "EmitStreamVertex(n) and EndStreamPrimitive(n) "
> +                      "with n>0 requires point output");
> +      }
> +   }
>  }
>  
>  
> @@ -2556,6 +2673,9 @@ link_shaders(struct gl_context *ctx, struct gl_shader_program *prog)
>  	 ;
>     }
>  
> +   /* Check and validate stream emissions in geometry shaders */
> +   validate_geometry_shader_emissions(ctx, prog);
> +
>     /* Mark all generic shader inputs and outputs as unpaired. */
>     for (unsigned i = MESA_SHADER_VERTEX; i <= MESA_SHADER_FRAGMENT; i++) {
>        if (prog->_LinkedShaders[i] != NULL) {
> 



More information about the mesa-dev mailing list