[Mesa-dev] [PATCH 11/18] glsl: Add support for EmitStreamVertex() and EndStreamPrimitive().

Chris Forbes chrisf at ijw.co.nz
Fri Jun 13 13:34:28 PDT 2014


Right, this happens because ir_emit_vertex doesn't take a proper
operand, so it can't keep it alive.

What I think you want to do is change the stream in ir_emit_vertex and
ir_end_primitive to be a pointer to ir_rvalue (and apply the various
tweaks required to consider it alive; have rvalue visitors descend
into it; etc) then emit:

(function EmitStreamVertex
  (signature void
    (parameters
      (declare (const_in ) int stream)
    )
    (
      (emit-vertex (var_ref stream))
    ))
)

which would inline in your case to


(declare () int stream)
(assign  (x) (var_ref stream)  (constant int (0)) )
(emit-vertex (var_ref stream))


and then after constant propagation,

(emit-vertex (constant int (0)) )

which you can then pick out in your later visitors just as easily as
you can with the integer you're currently storing.


On Fri, Jun 13, 2014 at 11:52 PM, Iago Toral <itoral at igalia.com> wrote:
> On Fri, 2014-06-13 at 10:28 +0200, Iago Toral wrote:
>> I forgot to add an important piece of info. I also had to add this in
>> the opt_dead_code.cpp, do_dead_code():
>>
>> if (strcmp(entry->var->name, "stream") == 0)
>>     continue;
>>
>> without that, the variable referenced by ir_emit_vertex() gets trashed
>> anyway, whether the ralloc context in link_shaders is detroyed or not.
>
> The variable is killed because it is not used, as I was anticipating in
> my patch, but I don't think the optimization passes are broken, I think
> this is expected to happen:
>
> This is the code generated for EmitStreamVertex(0) after function
> inlining:
>
> (declare () int stream)
> (assign  (x) (var_ref stream)  (constant int (0)) )
> (emit-vertex)
>
> (...)
>
> (function EmitStreamVertex
>   (signature void
>     (parameters
>       (declare (const_in ) int stream)
>     )
>     (
>       (emit-vertex)
>     ))
> )
>
> And this is after the dead code elimination passes (dead_code and
> dead_code_local), first the assignment is removed:
>
> (declare () int stream)
> (emit-vertex)
>
> And finally, in a second pass, it removes the declaration too, leaving:
>
> (emit-vertex)
>
> Seems to make sense: it is killing a variable that is, at this stage,
> not used for anything. So, as I was saying in the original patch, I
> think we can't do EmitStreamVertex(n) like any other built-in function
> because we won't be using its input parameter in the body of the
> function for anything, the variable's value is to be used in the visitor
> when it is time to generate the native code and that happens after the
> optimization passes, so we need to grab its constant value before the
> optimization passes (as my original patch did) or we have to find a way
> to tell the optimization passes that it should not touch this variable
> specifically (and then we would still have to figure out how to access
> that temporary variable holding the stream value from the visitor).
>
> Iago
>
>> Iago
>>
>> On Fri, 2014-06-13 at 10:09 +0200, Iago Toral wrote:
>> > After debugging I have more information about what is going on. There
>> > are two problems, one is that the stream variable in ir_emit_vertex gets
>> > trashed and the other one is that even if we manage to avoid that it
>> > won't get its value assigned. I explain how these two come to happen
>> > below and maybe someone can point me to what I am doing wrong:
>> >
>> > first, this is how I am defining EmitStreamVertex():
>> >
>> > ir_function_signature *
>> > builtin_builder::_EmitStreamVertex(builtin_available_predicate avail,
>> >                                    const glsl_type *stream_type)
>> > {
>> >    ir_variable *stream =
>> >       new(mem_ctx) ir_variable(stream_type, "stream", ir_var_const_in);
>> >    MAKE_SIG(glsl_type::void_type, avail, 1, stream);
>> >    ir_emit_vertex *ir = new(mem_ctx) ir_emit_vertex();
>> >    ir->stream = var_ref(stream);
>> >    body.emit(ir);
>> >    return sig;
>> > }
>> >
>> > The pattern is similar to other built-in functions. Notice how
>> > ir_stream_vertex will take a reference to the input stream variable.
>> >
>> > And this is how I am defining ir_emit_vertex:
>> >
>> > class ir_emit_vertex : public ir_instruction {
>> > public:
>> >  ir_emit_vertex() : ir_instruction(ir_type_emit_vertex), stream(NULL) {}
>> >
>> >  virtual void accept(ir_visitor *v) { v->visit(this); }
>> >
>> >  virtual ir_emit_vertex *clone(void *mem_ctx, struct hash_table *ht)
>> > const
>> >  {
>> >     ir_emit_vertex *ir = new(mem_ctx) ir_emit_vertex();
>> >     if (this->stream)
>> >        ir->stream = this->stream->clone(mem_ctx, ht);
>> >     return ir;
>> >  }
>> >
>> >  virtual ir_visitor_status accept(ir_hierarchical_visitor *);
>> >  ir_dereference_variable *stream;
>> > };
>> >
>> > Again, I don't see anything special as it follows the same pattern as
>> > other IR definitions in ir.h.
>> >
>> > If I only do this, then, by the time I reach
>> > vec4_gs_visitor::visit(ir_emit_vertex *ir), ir->stream has been trashed.
>> >
>> > ¿Is this expected? ¿Am I missing something in EmitStreamVertex(),
>> > ir_emit_vertex or somewhere else that is causing this?
>> >
>> > Valgrind says the variable gets killed with the destruction of the
>> > ralloc context created in link_shaders. And indeed, removing the
>> > ralloc_free lets the variable reach the visitor.  I suppose this is not
>> > expected (otherwise we would have this problem in any built-in function
>> > that accepts input parameters). Just in case, I noticed this code in
>> > link_shaders:
>> >
>> >   clone_ir_list(mem_ctx, linked->ir, main->ir);
>> >
>> > It seems that it clones code using that ralloc context created in
>> > link_shaders, so I changed it to be:
>> >
>> >    clone_ir_list(linked, linked->ir, main->ir);
>> >
>> > And it fixes the problem, but I suppose this is only a workaround for
>> > the real problem.
>> >
>> > As for the second problem, if I bypass the variable trashing by removing
>> > the call to ralloc_free in link_shaders() or by doing the change above,
>> > then when we reach  vec4_gs_visitor::visit(ir_emit_vertex *ir), if I do
>> > ((ir_rvalue*)ir->stream)->as_constant() it will still return NULL, so it
>> > is useless. I want to read the value of the variable here, which I
>> > should be able to do since this is a constant expression.
>> >
>> > However, as far as I can see by looking into ir_call::generate_inline()
>> > this seems to be expected: inputs to functions get a *new* variable for
>> > them, where the actual parameter value is set via an ir_assignment:
>> >
>> > parameters[i] = sig_param->clone(ctx, ht);
>> > parameters[i]->data.mode = ir_var_auto;
>> > (...)
>> > assign =
>> >    new(ctx) ir_assignment(new(ctx)
>> >                           ir_dereference_variable(parameters[i]),
>> >                                                   param, NULL);
>> > next_ir->insert_before(assign);
>> > (...)
>> >
>> > And then it clones the body of the function, like so:
>> >
>> > foreach_list(n, &callee->body) {
>> >    ir_instruction *ir = (ir_instruction *) n;
>> >    ir_instruction *new_ir = ir->clone(ctx, ht);
>> >
>> >    new_instructions.push_tail(new_ir);
>> >    visit_tree(new_ir, replace_return_with_assignment,
>> >               this->return_deref);
>> > }
>> >
>> > In our case, there is only one instruction here: ir_emit_vertex, and
>> > when cloning it we are also cloning its reference to the stream
>> > variable, but this is *different* from the parameter variable where we
>> > copied the actual parameter value, so there is no way we will be able to
>> > access the value of the variable from this reference.
>> >
>> > I can work around this problem in the visitor by doing this:
>> >
>> > dst_reg *reg = variable_storage(ir->stream->var);
>> >
>> > This seems to return the register associated with the real stream
>> > parameter that was the target of the ir_assignment, and then work with
>> > reg directly rather than creating a register in the visitor and moving
>> > the stream_id value to it. If I do it like this, however, I can't do
>> > some things that I was doing before, like not generating any code to set
>> > control data bits when we are calling EmitStreamVertex(0), because I
>> > can't access the value of the variable in the visitor to assess its
>> > value.
>> >
>> > So that's it, hopefully that's enough information for someone to tell
>> > what is missing in the implementation or if there is some other problem
>> > that is causing all this.
>> >
>> > Iago
>> >
>> > On Wed, 2014-06-11 at 21:25 +1200, Chris Forbes wrote:
>> > > This is pretty weird.
>> > >
>> > > We should be able to generate a normal builtin function body here
>> > > which consists of just the ir_emit_vertex, passing the stream
>> > > parameter to it. This would then get inlined like any other function
>> > > leaving a bare ir_emit_vertex / ir_end_primitive with a constant
>> > > operand. If one of the optimization passes eats that, then it's
>> > > broken.
>> > >
>> > >
>> > > On Wed, Jun 11, 2014 at 7:49 PM, Iago Toral Quiroga <itoral at igalia.com> wrote:
>> > > > ---
>> > > >  src/glsl/ast_function.cpp      | 37 +++++++++++++++++++++-----
>> > > >  src/glsl/builtin_functions.cpp | 60 ++++++++++++++++++++++++++++++++++++++++++
>> > > >  src/glsl/ir.h                  | 18 ++++++++-----
>> > > >  3 files changed, 103 insertions(+), 12 deletions(-)
>> > > >
>> > > > diff --git a/src/glsl/ast_function.cpp b/src/glsl/ast_function.cpp
>> > > > index 8e91a1e..1c4d7e7 100644
>> > > > --- a/src/glsl/ast_function.cpp
>> > > > +++ b/src/glsl/ast_function.cpp
>> > > > @@ -1722,15 +1722,40 @@ ast_function_expression::hir(exec_list *instructions,
>> > > >        ir_function_signature *sig =
>> > > >          match_function_by_name(func_name, &actual_parameters, state);
>> > > >
>> > > > +      bool is_emit_stream_vertex = !strcmp(func_name, "EmitStreamVertex");
>> > > > +      bool is_end_stream_primitive = !strcmp(func_name, "EndStreamPrimitive");
>> > > > +
>> > > >        ir_rvalue *value = NULL;
>> > > >        if (sig == NULL) {
>> > > > -        no_matching_function_error(func_name, &loc, &actual_parameters, state);
>> > > > -        value = ir_rvalue::error_value(ctx);
>> > > > +         no_matching_function_error(func_name, &loc, &actual_parameters, state);
>> > > > +         value = ir_rvalue::error_value(ctx);
>> > > >        } else if (!verify_parameter_modes(state, sig, actual_parameters, this->expressions)) {
>> > > > -        /* an error has already been emitted */
>> > > > -        value = ir_rvalue::error_value(ctx);
>> > > > -      } else {
>> > > > -        value = generate_call(instructions, sig, &actual_parameters, state);
>> > > > +         /* an error has already been emitted */
>> > > > +         value = ir_rvalue::error_value(ctx);
>> > > > +      } else if (is_emit_stream_vertex || is_end_stream_primitive) {
>> > > > +         /* See builtin_builder::_EmitStreamVertex() to undertand why we need
>> > > > +          * to handle these as a special case.
>> > > > +          */
>> > > > +         ir_rvalue *stream_param = (ir_rvalue *) actual_parameters.head;
>> > > > +         ir_constant *stream_const = stream_param->as_constant();
>> > > > +         /* stream_const should not be NULL if we got here */
>> > > > +         assert(stream_const);
>> > > > +         int stream_id = stream_const->value.i[0];
>> > > > +         if (stream_id < 0 || stream_id >= MAX_VERTEX_STREAMS) {
>> > > > +            _mesa_glsl_error(&loc, state,
>> > > > +                             "Invalid call %s(%d). Accepted "
>> > > > +                             "values for the stream parameter are in the "
>> > > > +                             "range [0, %d].",
>> > > > +                             func_name, stream_id, MAX_VERTEX_STREAMS - 1);
>> > > > +         }
>> > > > +         /* Only enable multi-stream if we emit vertices to non-zero streams */
>> > > > +         state->gs_uses_streams = state->gs_uses_streams || stream_id > 0;
>> > > > +         if (is_emit_stream_vertex)
>> > > > +            instructions->push_tail(new(ctx) ir_emit_vertex(stream_id));
>> > > > +         else
>> > > > +            instructions->push_tail(new(ctx) ir_end_primitive(stream_id));
>> > > > +       } else {
>> > > > +         value = generate_call(instructions, sig, &actual_parameters, state);
>> > > >        }
>> > > >
>> > > >        return value;
>> > > > diff --git a/src/glsl/builtin_functions.cpp b/src/glsl/builtin_functions.cpp
>> > > > index f9f0686..412e8f3 100644
>> > > > --- a/src/glsl/builtin_functions.cpp
>> > > > +++ b/src/glsl/builtin_functions.cpp
>> > > > @@ -359,6 +359,12 @@ shader_image_load_store(const _mesa_glsl_parse_state *state)
>> > > >             state->ARB_shader_image_load_store_enable);
>> > > >  }
>> > > >
>> > > > +static bool
>> > > > +gs_streams(const _mesa_glsl_parse_state *state)
>> > > > +{
>> > > > +   return gpu_shader5(state) && gs_only(state);
>> > > > +}
>> > > > +
>> > > >  /** @} */
>> > > >
>> > > >  /******************************************************************************/
>> > > > @@ -594,6 +600,10 @@ private:
>> > > >
>> > > >     B0(EmitVertex)
>> > > >     B0(EndPrimitive)
>> > > > +   ir_function_signature *_EmitStreamVertex(builtin_available_predicate avail,
>> > > > +                                            const glsl_type *stream_type);
>> > > > +   ir_function_signature *_EndStreamPrimitive(builtin_available_predicate avail,
>> > > > +                                              const glsl_type *stream_type);
>> > > >
>> > > >     B2(textureQueryLod);
>> > > >     B1(textureQueryLevels);
>> > > > @@ -1708,6 +1718,14 @@ builtin_builder::create_builtins()
>> > > >
>> > > >     add_function("EmitVertex",   _EmitVertex(),   NULL);
>> > > >     add_function("EndPrimitive", _EndPrimitive(), NULL);
>> > > > +   add_function("EmitStreamVertex",
>> > > > +                _EmitStreamVertex(gs_streams, glsl_type::uint_type),
>> > > > +                _EmitStreamVertex(gs_streams, glsl_type::int_type),
>> > > > +                NULL);
>> > > > +   add_function("EndStreamPrimitive",
>> > > > +                _EndStreamPrimitive(gs_streams, glsl_type::uint_type),
>> > > > +                _EndStreamPrimitive(gs_streams, glsl_type::int_type),
>> > > > +                NULL);
>> > > >
>> > > >     add_function("textureQueryLOD",
>> > > >                  _textureQueryLod(glsl_type::sampler1D_type,  glsl_type::float_type),
>> > > > @@ -3878,6 +3896,35 @@ builtin_builder::_EmitVertex()
>> > > >  }
>> > > >
>> > > >  ir_function_signature *
>> > > > +builtin_builder::_EmitStreamVertex(builtin_available_predicate avail,
>> > > > +                                   const glsl_type *stream_type)
>> > > > +{
>> > > > +   ir_variable *stream =
>> > > > +      new(mem_ctx) ir_variable(stream_type, "stream", ir_var_const_in);
>> > > > +
>> > > > +   /* EmitStreamVertex is a special kind of built-in function. It does
>> > > > +    * not really generate any IR when processed, instead, it only adds a
>> > > > +    * marker in the IR to know when we need to generate code for vertex
>> > > > +    * emission, just like EmitVertex(). However, in this case we have
>> > > > +    * an input parameter that we need to preserve and that the dead code
>> > > > +    * optimization passes will kill because it is apparently unused.
>> > > > +    * We will actually use it, but not until code generation time, after
>> > > > +    * the dead code elimination passes have run and kill the input variable.
>> > > > +    *
>> > > > +    * To deal with this situation, since the input parameter for
>> > > > +    * EmitStreamVertex() must be a constant expression, we don't generate a
>> > > > +    * function body here. Then, when we detect EmitVertexStream() calls the
>> > > > +    * AST, instead of producing an ir_call, we get the constant value of
>> > > > +    * the input parameter in that moment and produce a ir_emit_vertex directly.
>> > > > +    * See ast_function_expression::hir().
>> > > > +    */
>> > > > +
>> > > > +   MAKE_INTRINSIC(glsl_type::void_type, avail, 1, stream);
>> > > > +
>> > > > +   return sig;
>> > > > +}
>> > > > +
>> > > > +ir_function_signature *
>> > > >  builtin_builder::_EndPrimitive()
>> > > >  {
>> > > >     MAKE_SIG(glsl_type::void_type, gs_only, 0);
>> > > > @@ -3888,6 +3935,19 @@ builtin_builder::_EndPrimitive()
>> > > >  }
>> > > >
>> > > >  ir_function_signature *
>> > > > +builtin_builder::_EndStreamPrimitive(builtin_available_predicate avail,
>> > > > +                                     const glsl_type *stream_type)
>> > > > +{
>> > > > +   ir_variable *stream =
>> > > > +      new(mem_ctx) ir_variable(stream_type, "stream", ir_var_const_in);
>> > > > +
>> > > > +   /* See comment in builtin_builder::_EmitStreamVertex, same applies here */
>> > > > +   MAKE_INTRINSIC(glsl_type::void_type, avail, 1, stream);
>> > > > +
>> > > > +   return sig;
>> > > > +}
>> > > > +
>> > > > +ir_function_signature *
>> > > >  builtin_builder::_textureQueryLod(const glsl_type *sampler_type,
>> > > >                                    const glsl_type *coord_type)
>> > > >  {
>> > > > diff --git a/src/glsl/ir.h b/src/glsl/ir.h
>> > > > index 8cc58af..7ae91ab 100644
>> > > > --- a/src/glsl/ir.h
>> > > > +++ b/src/glsl/ir.h
>> > > > @@ -2159,8 +2159,9 @@ private:
>> > > >   */
>> > > >  class ir_emit_vertex : public ir_instruction {
>> > > >  public:
>> > > > -   ir_emit_vertex()
>> > > > -      : ir_instruction(ir_type_emit_vertex)
>> > > > +   ir_emit_vertex(unsigned stream = 0)
>> > > > +      : ir_instruction(ir_type_emit_vertex),
>> > > > +        stream(stream)
>> > > >     {
>> > > >     }
>> > > >
>> > > > @@ -2171,10 +2172,12 @@ public:
>> > > >
>> > > >     virtual ir_emit_vertex *clone(void *mem_ctx, struct hash_table *) const
>> > > >     {
>> > > > -      return new(mem_ctx) ir_emit_vertex();
>> > > > +      return new(mem_ctx) ir_emit_vertex(stream);
>> > > >     }
>> > > >
>> > > >     virtual ir_visitor_status accept(ir_hierarchical_visitor *);
>> > > > +
>> > > > +   unsigned stream;
>> > > >  };
>> > > >
>> > > >  /**
>> > > > @@ -2183,8 +2186,9 @@ public:
>> > > >   */
>> > > >  class ir_end_primitive : public ir_instruction {
>> > > >  public:
>> > > > -   ir_end_primitive()
>> > > > -      : ir_instruction(ir_type_end_primitive)
>> > > > +   ir_end_primitive(unsigned stream = 0)
>> > > > +      : ir_instruction(ir_type_end_primitive),
>> > > > +        stream(stream)
>> > > >     {
>> > > >     }
>> > > >
>> > > > @@ -2195,10 +2199,12 @@ public:
>> > > >
>> > > >     virtual ir_end_primitive *clone(void *mem_ctx, struct hash_table *) const
>> > > >     {
>> > > > -      return new(mem_ctx) ir_end_primitive();
>> > > > +      return new(mem_ctx) ir_end_primitive(stream);
>> > > >     }
>> > > >
>> > > >     virtual ir_visitor_status accept(ir_hierarchical_visitor *);
>> > > > +
>> > > > +   unsigned stream;
>> > > >  };
>> > > >
>> > > >  /*@}*/
>> > > > --
>> > > > 1.9.1
>> > > >
>> > > > _______________________________________________
>> > > > mesa-dev mailing list
>> > > > mesa-dev at lists.freedesktop.org
>> > > > http://lists.freedesktop.org/mailman/listinfo/mesa-dev
>> > >
>> >
>>
>>
>> _______________________________________________
>> mesa-dev mailing list
>> mesa-dev at lists.freedesktop.org
>> http://lists.freedesktop.org/mailman/listinfo/mesa-dev
>
>


More information about the mesa-dev mailing list