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

Iago Toral itoral at igalia.com
Wed Jun 18 23:22:04 PDT 2014


On Wed, 2014-06-18 at 13:38 -0700, Ian Romanick wrote:
> 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...

I am not sure that I have access to all of these systems, but let me
see.

FWIW, the spec also says this:

"The function EmitVertex() is equivalent to calling EmitStreamVertex()
with <stream> set to zero."

And they can't be equivalent if ones goes through checks that the other
does not...

> > +       */
> > +      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