[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