[Mesa-dev] [PATCH 2/2] draw: implement primitive assembler

Roland Scheidegger sroland at vmware.com
Fri Apr 19 10:45:03 PDT 2013


Am 18.04.2013 12:16, schrieb Zack Rusin:
> Input assembler needs to be able to decompose adjacency primitives
> into something that can be understood by the rest of the pipeline.
> The specs say that the adjacency primitives are *only* visible
> in the geometry shader, for everything else they need to be
> decomposed. Which in most of the cases is not an issue, but the
> geometry shader always decomposes for us, but without geometry
> shader we were passing unchanged adjacency primitives to the
> rest of the pipeline and causing crashes everywhere. This
> commit introduces a primitive assembler which, if geometry
> shader is missing and the input primitive is one of the
> adjacency primitives, decomposes them into something
> that the rest of the pipeline can understand.
> 
> Signed-off-by: Zack Rusin <zackr at vmware.com>
> ---
>  src/gallium/auxiliary/Makefile.sources             |    1 +
>  src/gallium/auxiliary/draw/draw_prim_assembler.c   |  202 ++++++++++++++++++++
>  src/gallium/auxiliary/draw/draw_prim_assembler.h   |   46 +++++
>  .../auxiliary/draw/draw_prim_assembler_tmp.h       |   33 ++++
>  .../auxiliary/draw/draw_pt_fetch_shade_pipeline.c  |   25 ++-
>  .../draw/draw_pt_fetch_shade_pipeline_llvm.c       |   25 ++-
>  src/gallium/auxiliary/util/u_prim.h                |   21 ++
>  7 files changed, 349 insertions(+), 4 deletions(-)
>  create mode 100644 src/gallium/auxiliary/draw/draw_prim_assembler.c
>  create mode 100644 src/gallium/auxiliary/draw/draw_prim_assembler.h
>  create mode 100644 src/gallium/auxiliary/draw/draw_prim_assembler_tmp.h
> 
> diff --git a/src/gallium/auxiliary/Makefile.sources b/src/gallium/auxiliary/Makefile.sources
> index 79def21..20ff5ba 100644
> --- a/src/gallium/auxiliary/Makefile.sources
> +++ b/src/gallium/auxiliary/Makefile.sources
> @@ -23,6 +23,7 @@ C_SOURCES := \
>  	draw/draw_pipe_vbuf.c \
>  	draw/draw_pipe_wide_line.c \
>  	draw/draw_pipe_wide_point.c \
> +	draw/draw_prim_assembler.c \
>  	draw/draw_pt.c \
>  	draw/draw_pt_emit.c \
>  	draw/draw_pt_fetch.c \
> diff --git a/src/gallium/auxiliary/draw/draw_prim_assembler.c b/src/gallium/auxiliary/draw/draw_prim_assembler.c
> new file mode 100644
> index 0000000..9717c32
> --- /dev/null
> +++ b/src/gallium/auxiliary/draw/draw_prim_assembler.c
> @@ -0,0 +1,202 @@
> +/**************************************************************************
> + *
> + * Copyright 2013 VMware, Inc.
> + * All Rights Reserved.
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the
> + * "Software"), to deal in the Software without restriction, including
> + * without limitation the rights to use, copy, modify, merge, publish,
> + * distribute, sub license, and/or sell copies of the Software, and to
> + * permit persons to whom the Software is furnished to do so, subject to
> + * the following conditions:
> + *
> + * The above copyright notice and this permission notice (including the
> + * next paragraph) shall be included in all copies or substantial portions
> + * of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
> + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
> + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
> + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
> + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
> + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
> + *
> + **************************************************************************/
> +
> +#include "draw_prim_assembler.h"
> +
> +#include "util/u_debug.h"
> +#include "util/u_memory.h"
> +#include "util/u_prim.h"
> +
> +#include "pipe/p_defines.h"
> +
> +struct draw_assembler
> +{
> +   struct draw_context *draw;
> +
> +   struct draw_prim_info *output_prims;
> +   struct draw_vertex_info *output_verts;
> +
> +   const struct draw_prim_info *input_prims;
> +   const struct draw_vertex_info *input_verts;
> +};
> +
> +boolean
> +draw_prim_assembler_is_required(struct draw_context *draw,
> +                                const struct draw_prim_info *prim_info,
> +                                const struct draw_vertex_info *vert_info)
> +{
> +   switch (prim_info->prim) {
> +   case PIPE_PRIM_LINES_ADJACENCY:
> +   case PIPE_PRIM_LINE_STRIP_ADJACENCY:
> +   case PIPE_PRIM_TRIANGLES_ADJACENCY:
> +   case PIPE_PRIM_TRIANGLE_STRIP_ADJACENCY:
> +      return TRUE;
> +   default:
> +      return FALSE;
> +   }
> +}
> +
> +static void
> +copy_verts(struct draw_assembler *asmblr,
> +           unsigned *indices, unsigned num_indices)
> +{
> +   unsigned i;
> +
> +   char *output = (char*)asmblr->output_verts->verts;
> +   const char *input = (const char*)asmblr->input_verts->verts;
> +   
> +   for (i = 0; i < num_indices; ++i) {
> +      unsigned idx = indices[i];
> +      unsigned output_offset =
> +         asmblr->output_verts->count * asmblr->output_verts->stride;
> +      unsigned input_offset = asmblr->input_verts->stride * idx;
> +      memcpy(output + output_offset, input + input_offset,
> +             asmblr->input_verts->vertex_size);
> +      asmblr->output_verts->count += 1;
> +   }
> +}
> +
> +static void prim_point(struct draw_assembler *asmblr,
> +                       int idx)
> +{
> +   unsigned indices[1];
> +
> +   indices[0] = idx;
> +
> +   copy_verts(asmblr, indices, 1);
> +}
> +
> +static void prim_line(struct draw_assembler *asmblr,
> +                      int i0, int i1)
> +{
> +   unsigned indices[2];
> +
> +   indices[0] = i0;
> +   indices[1] = i1;
> +
> +   copy_verts(asmblr, indices, 2);
> +}
> +
> +static void prim_line_adj(struct draw_assembler *asmblr,
> +                          int i0, int i1, int i2, int i3)
> +{
> +   unsigned indices[4];
> +
> +   indices[0] = i1;
> +   indices[1] = i2;
> +
> +   copy_verts(asmblr, indices, 2);
> +}
> +
> +static void prim_tri(struct draw_assembler *asmblr,
> +                   int i0, int i1, int i2)
> +{
> +   unsigned indices[3];
> +
> +   indices[0] = i0;
> +   indices[1] = i1;
> +   indices[2] = i2;
> +
> +   copy_verts(asmblr, indices, 3);
> +}
> +
> +static void prim_tri_adj(struct draw_assembler *asmblr,
> +                         int i0, int i1, int i2,
> +                         int i3, int i4, int i5)
> +{
> +   unsigned indices[6];
> +
> +   indices[0] = i0;
> +   indices[1] = i2;
> +   indices[2] = i4;
> +
> +   copy_verts(asmblr, indices, 3);
> +}
> +
> +
> +
> +#define FUNC assembler_run_linear
> +#define GET_ELT(idx) (start + (idx))
> +#include "draw_prim_assembler_tmp.h"
> +
> +#define FUNC assembler_run_elts
> +#define LOCAL_VARS   const ushort *elts = input_prims->elts;
> +#define GET_ELT(idx) (elts[start + (idx)])
> +#include "draw_prim_assembler_tmp.h"
> +
> +void
> +draw_prim_assembler_run(struct draw_context *draw,
> +                        const struct draw_prim_info *input_prims,
> +                        const struct draw_vertex_info *input_verts,
> +                        struct draw_prim_info *output_prims,
> +                        struct draw_vertex_info *output_verts)
> +{
> +   struct draw_assembler asmblr;
> +   unsigned start, i;
> +   unsigned assembled_prim = u_assembled_primitive(input_prims->prim);
> +   unsigned max_primitives = u_decomposed_prims_for_vertices(
> +      input_prims->prim, input_prims->count);
> +   unsigned max_verts = u_vertices_per_prim(assembled_prim) * max_primitives;
> +
> +   asmblr.draw = draw;
> +   asmblr.output_prims = output_prims;
> +   asmblr.output_verts = output_verts;
> +   asmblr.input_prims = input_prims;
> +   asmblr.input_verts = input_verts;
> +   
> +   output_prims->linear = TRUE;
> +   output_prims->elts = NULL;
> +   output_prims->start = 0;
> +   output_prims->prim = u_assembled_primitive(input_prims->prim);
> +   output_prims->flags = 0x0;
> +   output_prims->primitive_lengths = MALLOC(sizeof(unsigned));
> +   output_prims->primitive_lengths[0] = 0;
> +   output_prims->primitive_count = 1;
> +
> +   output_verts->vertex_size = input_verts->vertex_size;
> +   output_verts->stride = input_verts->stride;
> +   output_verts->verts = (struct vertex_header*)MALLOC(
> +      input_verts->vertex_size * max_verts);
> +   output_verts->count = 0;
> +
> +   
> +   for (start = i = 0; i < input_prims->primitive_count;
> +        start += input_prims->primitive_lengths[i], i++)
> +   {
> +      unsigned count = input_prims->primitive_lengths[i];
> +      if (input_prims->linear) {
> +         assembler_run_linear(&asmblr, input_prims, input_verts,
> +                              start, count);
> +      } else {
> +         assembler_run_elts(&asmblr, input_prims, input_verts,
> +                            start, count);
> +      }
> +   }
> +   
> +   output_prims->primitive_lengths[0] = output_verts->count;
> +   output_prims->count = output_verts->count;
> +}
> diff --git a/src/gallium/auxiliary/draw/draw_prim_assembler.h b/src/gallium/auxiliary/draw/draw_prim_assembler.h
> new file mode 100644
> index 0000000..b6b6c7c
> --- /dev/null
> +++ b/src/gallium/auxiliary/draw/draw_prim_assembler.h
> @@ -0,0 +1,46 @@
> +/**************************************************************************
> + *
> + * Copyright 2013 VMware, Inc.
> + * All Rights Reserved.
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the
> + * "Software"), to deal in the Software without restriction, including
> + * without limitation the rights to use, copy, modify, merge, publish,
> + * distribute, sub license, and/or sell copies of the Software, and to
> + * permit persons to whom the Software is furnished to do so, subject to
> + * the following conditions:
> + *
> + * The above copyright notice and this permission notice (including the
> + * next paragraph) shall be included in all copies or substantial portions
> + * of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
> + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
> + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
> + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
> + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
> + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
> + *
> + **************************************************************************/
> +
> +#ifndef DRAW_PRIM_ASSEMBLER_H
> +#define DRAW_PRIM_ASSEMBLER_H
> +
> +#include "draw/draw_private.h"
> +
> +boolean
> +draw_prim_assembler_is_required(struct draw_context *draw,
> +                                const struct draw_prim_info *prim_info,
> +                                const struct draw_vertex_info *vert_info);
> +
> +void
> +draw_prim_assembler_run(struct draw_context *draw,
> +                        const struct draw_prim_info *in_prim_info,
> +                        const struct draw_vertex_info *in_vert_info,
> +                        struct draw_prim_info *out_prim_info,
> +                        struct draw_vertex_info *out_vert_info);
> +
> +
> +#endif
> diff --git a/src/gallium/auxiliary/draw/draw_prim_assembler_tmp.h b/src/gallium/auxiliary/draw/draw_prim_assembler_tmp.h
> new file mode 100644
> index 0000000..fbfa7c6
> --- /dev/null
> +++ b/src/gallium/auxiliary/draw/draw_prim_assembler_tmp.h
> @@ -0,0 +1,33 @@
> +#define FUNC_VARS                               \
> +   struct draw_assembler *asmblr,               \
> +   const struct draw_prim_info *input_prims,    \
> +   const struct draw_vertex_info *input_verts,  \
> +   unsigned start,                              \
> +   unsigned count
> +
> +#define FUNC_ENTER                                                \
> +   /* declare more local vars */                                  \
> +   const unsigned prim = input_prims->prim;                       \
> +   const unsigned prim_flags = input_prims->flags;                \
> +   const boolean quads_flatshade_last = FALSE;                    \
> +   const boolean last_vertex_last = !asmblr->draw->rasterizer->flatshade_first;  \
> +   do {                                                                \
> +      switch (prim) {                                                  \
> +      case PIPE_PRIM_QUADS:                                            \
> +      case PIPE_PRIM_QUAD_STRIP:                                       \
> +      case PIPE_PRIM_POLYGON:                                          \
> +         debug_assert(!"unexpected primitive type in prim assembler"); \
> +         return;                                                       \
> +      default:                                                         \
> +         break;                                                        \
> +      }                                                                \
> +   } while (0)                                                         \
> +
> +
> +#define POINT(i0)                             prim_point(asmblr, i0)
> +#define LINE(flags, i0, i1)                   prim_line(asmblr, i0, i1)
> +#define TRIANGLE(flags, i0, i1, i2)           prim_tri(asmblr, i0, i1, i2)
> +#define LINE_ADJ(flags, i0, i1, i2, i3)       prim_line_adj(asmblr, i0, i1, i2, i3)
> +#define TRIANGLE_ADJ(flags,i0,i1,i2,i3,i4,i5) prim_tri_adj(asmblr,i0,i1,i2,i3,i4,i5)
> +
> +#include "draw_decompose_tmp.h"
> diff --git a/src/gallium/auxiliary/draw/draw_pt_fetch_shade_pipeline.c b/src/gallium/auxiliary/draw/draw_pt_fetch_shade_pipeline.c
> index e17f161..072174d 100644
> --- a/src/gallium/auxiliary/draw/draw_pt_fetch_shade_pipeline.c
> +++ b/src/gallium/auxiliary/draw/draw_pt_fetch_shade_pipeline.c
> @@ -31,6 +31,7 @@
>  #include "draw/draw_context.h"
>  #include "draw/draw_vbuf.h"
>  #include "draw/draw_vertex.h"
> +#include "draw/draw_prim_assembler.h"
>  #include "draw/draw_pt.h"
>  #include "draw/draw_vs.h"
>  #include "draw/draw_gs.h"
> @@ -69,7 +70,8 @@ static void fetch_pipeline_prepare( struct draw_pt_middle_end *middle,
>     unsigned i;
>     unsigned instance_id_index = ~0;
>  
> -   unsigned gs_out_prim = (gs ? gs->output_primitive : prim);
> +   const unsigned gs_out_prim = (gs ? gs->output_primitive :
> +                                 u_assembled_primitive(prim);
>  
>     /* Add one to num_outputs because the pipeline occasionally tags on
>      * an additional texcoord, eg for AA lines.
> @@ -217,7 +219,7 @@ static void draw_vertex_shader_run(struct draw_vertex_shader *vshader,
>  
>  static void fetch_pipeline_generic( struct draw_pt_middle_end *middle,
>                                      const struct draw_fetch_info *fetch_info,
> -                                    const struct draw_prim_info *prim_info )
> +                                    const struct draw_prim_info *in_prim_info )
>  {
>     struct fetch_pipeline_middle_end *fpme = (struct fetch_pipeline_middle_end *)middle;
>     struct draw_context *draw = fpme->draw;
> @@ -228,6 +230,10 @@ static void fetch_pipeline_generic( struct draw_pt_middle_end *middle,
>     struct draw_vertex_info vs_vert_info;
>     struct draw_vertex_info gs_vert_info;
>     struct draw_vertex_info *vert_info;
> +   struct draw_prim_info ia_prim_info;
> +   struct draw_vertex_info ia_vert_info;
> +   const struct draw_prim_info *prim_info = in_prim_info;
> +   boolean free_prim_info = FALSE;
>     unsigned opt = fpme->opt;
>  
>     fetched_vert_info.count = fetch_info->count;
> @@ -283,6 +289,18 @@ static void fetch_pipeline_generic( struct draw_pt_middle_end *middle,
>        FREE(vert_info->verts);
>        vert_info = &gs_vert_info;
>        prim_info = &gs_prim_info;
> +   } else {
> +      if (draw_prim_assembler_is_required(draw, prim_info, vert_info)) {
> +         draw_prim_assembler_run(draw, prim_info, vert_info,
> +                                 &ia_prim_info, &ia_vert_info);
> +
> +         if (ia_vert_info.count) {
> +            FREE(vert_info->verts);
> +            vert_info = &ia_vert_info;
> +            prim_info = &ia_prim_info;
> +            free_prim_info = TRUE;
> +         }
> +      }
>     }
>  
>  
> @@ -314,6 +332,9 @@ static void fetch_pipeline_generic( struct draw_pt_middle_end *middle,
>        }
>     }
>     FREE(vert_info->verts);
> +   if (free_prim_info) {
> +      FREE(prim_info->primitive_lengths);
> +   }
>  }
>  
>  static void fetch_pipeline_run( struct draw_pt_middle_end *middle,
> diff --git a/src/gallium/auxiliary/draw/draw_pt_fetch_shade_pipeline_llvm.c b/src/gallium/auxiliary/draw/draw_pt_fetch_shade_pipeline_llvm.c
> index d312dc4..31bd7ce 100644
> --- a/src/gallium/auxiliary/draw/draw_pt_fetch_shade_pipeline_llvm.c
> +++ b/src/gallium/auxiliary/draw/draw_pt_fetch_shade_pipeline_llvm.c
> @@ -33,6 +33,7 @@
>  #include "draw/draw_vbuf.h"
>  #include "draw/draw_vertex.h"
>  #include "draw/draw_pt.h"
> +#include "draw/draw_prim_assembler.h"
>  #include "draw/draw_vs.h"
>  #include "draw/draw_llvm.h"
>  #include "gallivm/lp_bld_init.h"
> @@ -138,7 +139,8 @@ llvm_middle_end_prepare( struct draw_pt_middle_end *middle,
>     struct draw_context *draw = fpme->draw;
>     struct draw_vertex_shader *vs = draw->vs.vertex_shader;
>     struct draw_geometry_shader *gs = draw->gs.geometry_shader;
> -   const unsigned out_prim = gs ? gs->output_primitive : in_prim;
> +   const unsigned out_prim = gs ? gs->output_primitive :
> +      u_assembled_primitive(in_prim);
>  
>     /* Add one to num_outputs because the pipeline occasionally tags on
>      * an additional texcoord, eg for AA lines.
> @@ -312,7 +314,7 @@ static void emit(struct pt_emit *emit,
>  static void
>  llvm_pipeline_generic( struct draw_pt_middle_end *middle,
>                         const struct draw_fetch_info *fetch_info,
> -                       const struct draw_prim_info *prim_info )
> +                       const struct draw_prim_info *in_prim_info )
>  {
>     struct llvm_middle_end *fpme = (struct llvm_middle_end *)middle;
>     struct draw_context *draw = fpme->draw;
> @@ -321,6 +323,10 @@ llvm_pipeline_generic( struct draw_pt_middle_end *middle,
>     struct draw_vertex_info llvm_vert_info;
>     struct draw_vertex_info gs_vert_info;
>     struct draw_vertex_info *vert_info;
> +   struct draw_prim_info ia_prim_info;
> +   struct draw_vertex_info ia_vert_info;
> +   const struct draw_prim_info *prim_info = in_prim_info;
> +   boolean free_prim_info = FALSE;
>     unsigned opt = fpme->opt;
>     unsigned clipped = 0;
>  
> @@ -380,6 +386,18 @@ llvm_pipeline_generic( struct draw_pt_middle_end *middle,
>        FREE(vert_info->verts);
>        vert_info = &gs_vert_info;
>        prim_info = &gs_prim_info;
> +   } else {
> +      if (draw_prim_assembler_is_required(draw, prim_info, vert_info)) {
> +         draw_prim_assembler_run(draw, prim_info, vert_info,
> +                                 &ia_prim_info, &ia_vert_info);
> +
> +         if (ia_vert_info.count) {
> +            FREE(vert_info->verts);
> +            vert_info = &ia_vert_info;
> +            prim_info = &ia_prim_info;
> +            free_prim_info = TRUE;
> +         }
> +      }
>     }
>  
>     /* stream output needs to be done before clipping */
> @@ -407,6 +425,9 @@ llvm_pipeline_generic( struct draw_pt_middle_end *middle,
>        }
>     }
>     FREE(vert_info->verts);
> +   if (free_prim_info) {
> +      FREE(prim_info->primitive_lengths);
> +   }
>  }
>  
>  
> diff --git a/src/gallium/auxiliary/util/u_prim.h b/src/gallium/auxiliary/util/u_prim.h
> index 507d12e..e477444 100644
> --- a/src/gallium/auxiliary/util/u_prim.h
> +++ b/src/gallium/auxiliary/util/u_prim.h
> @@ -213,6 +213,27 @@ u_decomposed_prims_for_vertices(int primitive, int vertices)
>     }
>  }
>  
> +static INLINE unsigned
> +u_assembled_primitive(unsigned prim)
> +{
> +   switch (prim) {
> +   case PIPE_PRIM_LINES_ADJACENCY:
> +      return PIPE_PRIM_LINES;
> +   case PIPE_PRIM_LINE_STRIP_ADJACENCY:
> +      return PIPE_PRIM_LINES;
> +   case PIPE_PRIM_TRIANGLES_ADJACENCY:
> +      return PIPE_PRIM_TRIANGLES;
> +   case PIPE_PRIM_TRIANGLE_STRIP_ADJACENCY:
> +      return PIPE_PRIM_TRIANGLES;
> +   default:
> +      return prim;
> +   }
> +
> +   return prim;
> +}
> +      
> +      
> +
>  const char *u_prim_name( unsigned pipe_prim );
>  
>  #endif

Looks good to me though I wonder if it would be an option to do this at
vertex fetch time instead after vs? I guess though that way it would
have to be done separate for the llvm and non-llvm code as the fetch is
integrated into the llvm code. I think that the idea for d3d10 was to do
it in the input assembler, so the unneeded vertices don't have to go
through the vs. I doubt though it makes much of a difference.

Roland


More information about the mesa-dev mailing list