101e04c3fSmrg
201e04c3fSmrg/*
301e04c3fSmrg * Mesa 3-D graphics library
401e04c3fSmrg *
501e04c3fSmrg * Copyright (C) 1999-2006  Brian Paul   All Rights Reserved.
601e04c3fSmrg *
701e04c3fSmrg * Permission is hereby granted, free of charge, to any person obtaining a
801e04c3fSmrg * copy of this software and associated documentation files (the "Software"),
901e04c3fSmrg * to deal in the Software without restriction, including without limitation
1001e04c3fSmrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
1101e04c3fSmrg * and/or sell copies of the Software, and to permit persons to whom the
1201e04c3fSmrg * Software is furnished to do so, subject to the following conditions:
1301e04c3fSmrg *
1401e04c3fSmrg * The above copyright notice and this permission notice shall be included
1501e04c3fSmrg * in all copies or substantial portions of the Software.
1601e04c3fSmrg *
1701e04c3fSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
1801e04c3fSmrg * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1901e04c3fSmrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
2001e04c3fSmrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
2101e04c3fSmrg * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
2201e04c3fSmrg * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
2301e04c3fSmrg * OTHER DEALINGS IN THE SOFTWARE.
2401e04c3fSmrg *
2501e04c3fSmrg * Authors:
2601e04c3fSmrg *    Keith Whitwell <keithw@vmware.com>
2701e04c3fSmrg */
2801e04c3fSmrg
2901e04c3fSmrg
3001e04c3fSmrg#include "main/mtypes.h"
3101e04c3fSmrg#include "main/macros.h"
3201e04c3fSmrg#include "main/enums.h"
3301e04c3fSmrg#include "vbo/vbo.h"
3401e04c3fSmrg
3501e04c3fSmrg#include "t_split.h"
3601e04c3fSmrg
3701e04c3fSmrg
3801e04c3fSmrg#define MAX_PRIM 32
3901e04c3fSmrg
4001e04c3fSmrg/* Used for splitting without copying. No attempt is made to handle
4101e04c3fSmrg * too large indexed vertex buffers: In general you need to copy to do
4201e04c3fSmrg * that.
4301e04c3fSmrg */
4401e04c3fSmrgstruct split_context {
4501e04c3fSmrg   struct gl_context *ctx;
4601e04c3fSmrg   const struct tnl_vertex_array *array;
4701e04c3fSmrg   const struct _mesa_prim *prim;
4801e04c3fSmrg   GLuint nr_prims;
4901e04c3fSmrg   const struct _mesa_index_buffer *ib;
5001e04c3fSmrg   GLuint min_index;
5101e04c3fSmrg   GLuint max_index;
527ec681f3Smrg   GLuint num_instances;
537ec681f3Smrg   GLuint base_instance;
5401e04c3fSmrg   tnl_draw_func draw;
5501e04c3fSmrg
5601e04c3fSmrg   const struct split_limits *limits;
5701e04c3fSmrg   GLuint limit;
5801e04c3fSmrg
5901e04c3fSmrg   struct _mesa_prim dstprim[MAX_PRIM];
6001e04c3fSmrg   GLuint dstprim_nr;
6101e04c3fSmrg};
6201e04c3fSmrg
6301e04c3fSmrg
6401e04c3fSmrg
6501e04c3fSmrg
6601e04c3fSmrgstatic void
6701e04c3fSmrgflush_vertex( struct split_context *split)
6801e04c3fSmrg{
6901e04c3fSmrg   struct gl_context *ctx = split->ctx;
7001e04c3fSmrg   struct _mesa_index_buffer ib;
7101e04c3fSmrg   GLuint i;
7201e04c3fSmrg
7301e04c3fSmrg   if (!split->dstprim_nr)
7401e04c3fSmrg      return;
7501e04c3fSmrg
7601e04c3fSmrg   if (split->ib) {
7701e04c3fSmrg      ib = *split->ib;
7801e04c3fSmrg
7901e04c3fSmrg      ib.count = split->max_index - split->min_index + 1;
8001e04c3fSmrg      ib.ptr = (const void *)((const char *)ib.ptr +
817ec681f3Smrg                              (split->min_index << ib.index_size_shift));
8201e04c3fSmrg
8301e04c3fSmrg      /* Rebase the primitives to save index buffer entries. */
8401e04c3fSmrg      for (i = 0; i < split->dstprim_nr; i++)
8501e04c3fSmrg         split->dstprim[i].start -= split->min_index;
8601e04c3fSmrg   }
8701e04c3fSmrg
8801e04c3fSmrg   assert(split->max_index >= split->min_index);
8901e04c3fSmrg
9001e04c3fSmrg   split->draw(ctx,
9101e04c3fSmrg               split->array,
9201e04c3fSmrg               split->dstprim,
9301e04c3fSmrg               split->dstprim_nr,
9401e04c3fSmrg               split->ib ? &ib : NULL,
9501e04c3fSmrg               !split->ib,
9601e04c3fSmrg               split->min_index,
9701e04c3fSmrg               split->max_index,
987ec681f3Smrg               split->num_instances,
997ec681f3Smrg               split->base_instance);
10001e04c3fSmrg
10101e04c3fSmrg   split->dstprim_nr = 0;
10201e04c3fSmrg   split->min_index = ~0;
10301e04c3fSmrg   split->max_index = 0;
10401e04c3fSmrg}
10501e04c3fSmrg
10601e04c3fSmrg
10701e04c3fSmrgstatic struct _mesa_prim *
10801e04c3fSmrgnext_outprim(struct split_context *split)
10901e04c3fSmrg{
11001e04c3fSmrg   if (split->dstprim_nr == MAX_PRIM-1) {
11101e04c3fSmrg      flush_vertex(split);
11201e04c3fSmrg   }
11301e04c3fSmrg
11401e04c3fSmrg   {
11501e04c3fSmrg      struct _mesa_prim *prim = &split->dstprim[split->dstprim_nr++];
11601e04c3fSmrg      memset(prim, 0, sizeof(*prim));
11701e04c3fSmrg      return prim;
11801e04c3fSmrg   }
11901e04c3fSmrg}
12001e04c3fSmrg
12101e04c3fSmrg
12201e04c3fSmrgstatic void
12301e04c3fSmrgupdate_index_bounds(struct split_context *split,
12401e04c3fSmrg                    const struct _mesa_prim *prim)
12501e04c3fSmrg{
12601e04c3fSmrg   split->min_index = MIN2(split->min_index, prim->start);
12701e04c3fSmrg   split->max_index = MAX2(split->max_index, prim->start + prim->count - 1);
12801e04c3fSmrg}
12901e04c3fSmrg
13001e04c3fSmrg
13101e04c3fSmrg/* Return the maximum amount of vertices that can be emitted for a
13201e04c3fSmrg * primitive starting at 'prim->start', depending on the previous
13301e04c3fSmrg * index bounds.
13401e04c3fSmrg */
13501e04c3fSmrgstatic GLuint
13601e04c3fSmrgget_max_vertices(struct split_context *split,
13701e04c3fSmrg                 const struct _mesa_prim *prim)
13801e04c3fSmrg{
13901e04c3fSmrg   if ((prim->start > split->min_index &&
14001e04c3fSmrg        prim->start - split->min_index >= split->limit) ||
14101e04c3fSmrg       (prim->start < split->max_index &&
14201e04c3fSmrg        split->max_index - prim->start >= split->limit))
14301e04c3fSmrg      /* "prim" starts too far away from the old range. */
14401e04c3fSmrg      return 0;
14501e04c3fSmrg
14601e04c3fSmrg   return MIN2(split->min_index, prim->start) + split->limit - prim->start;
14701e04c3fSmrg}
14801e04c3fSmrg
14901e04c3fSmrg
15001e04c3fSmrg/* Break large primitives into smaller ones.  If not possible, convert
15101e04c3fSmrg * the primitive to indexed and pass to split_elts().
15201e04c3fSmrg */
15301e04c3fSmrgstatic void
15401e04c3fSmrgsplit_prims(struct split_context *split)
15501e04c3fSmrg{
15601e04c3fSmrg   GLuint i;
15701e04c3fSmrg
15801e04c3fSmrg   for (i = 0; i < split->nr_prims; i++) {
15901e04c3fSmrg      const struct _mesa_prim *prim = &split->prim[i];
16001e04c3fSmrg      GLuint first, incr;
16101e04c3fSmrg      GLboolean split_inplace =
16201e04c3fSmrg         _tnl_split_prim_inplace(prim->mode, &first, &incr);
16301e04c3fSmrg      GLuint available = get_max_vertices(split, prim);
16401e04c3fSmrg      GLuint count = prim->count - (prim->count - first) % incr;
16501e04c3fSmrg
16601e04c3fSmrg      if (prim->count < first)
16701e04c3fSmrg         continue;
16801e04c3fSmrg
16901e04c3fSmrg      if ((available < count && !split_inplace) ||
17001e04c3fSmrg          (available < first && split_inplace)) {
17101e04c3fSmrg         flush_vertex(split);
17201e04c3fSmrg         available = get_max_vertices(split, prim);
17301e04c3fSmrg      }
17401e04c3fSmrg
17501e04c3fSmrg      if (available >= count) {
17601e04c3fSmrg         struct _mesa_prim *outprim = next_outprim(split);
17701e04c3fSmrg
17801e04c3fSmrg         *outprim = *prim;
17901e04c3fSmrg         update_index_bounds(split, outprim);
18001e04c3fSmrg      }
18101e04c3fSmrg      else if (split_inplace) {
18201e04c3fSmrg         GLuint j, nr;
18301e04c3fSmrg
18401e04c3fSmrg         for (j = 0 ; j < count ;) {
18501e04c3fSmrg            GLuint remaining = count - j;
18601e04c3fSmrg            struct _mesa_prim *outprim = next_outprim(split);
18701e04c3fSmrg
18801e04c3fSmrg            nr = MIN2(available, remaining);
18901e04c3fSmrg            nr -= (nr - first) % incr;
19001e04c3fSmrg
19101e04c3fSmrg            outprim->mode = prim->mode;
19201e04c3fSmrg            outprim->begin = (j == 0 && prim->begin);
19301e04c3fSmrg            outprim->end = (nr == remaining && prim->end);
19401e04c3fSmrg            outprim->start = prim->start + j;
19501e04c3fSmrg            outprim->count = nr;
19601e04c3fSmrg
19701e04c3fSmrg            update_index_bounds(split, outprim);
19801e04c3fSmrg
19901e04c3fSmrg            if (nr == remaining) {
20001e04c3fSmrg               /* Finished */
20101e04c3fSmrg               j += nr;
20201e04c3fSmrg            }
20301e04c3fSmrg            else {
20401e04c3fSmrg               /* Wrapped the primitive */
20501e04c3fSmrg               j += nr - (first - incr);
20601e04c3fSmrg               flush_vertex(split);
20701e04c3fSmrg               available = get_max_vertices(split, prim);
20801e04c3fSmrg            }
20901e04c3fSmrg         }
21001e04c3fSmrg      }
21101e04c3fSmrg      else if (split->ib == NULL) {
21201e04c3fSmrg         /* XXX: could at least send the first max_verts off from the
21301e04c3fSmrg          * inplace buffers.
21401e04c3fSmrg          */
21501e04c3fSmrg
21601e04c3fSmrg         /* else convert to indexed primitive and pass to split_elts,
21701e04c3fSmrg          * which will do the necessary copying and turn it back into a
21801e04c3fSmrg          * vertex primitive for rendering...
21901e04c3fSmrg          */
22001e04c3fSmrg         struct _mesa_index_buffer ib;
22101e04c3fSmrg         struct _mesa_prim tmpprim;
22201e04c3fSmrg         GLuint *elts = malloc(count * sizeof(GLuint));
22301e04c3fSmrg         GLuint j;
22401e04c3fSmrg
22501e04c3fSmrg         for (j = 0; j < count; j++)
22601e04c3fSmrg            elts[j] = prim->start + j;
22701e04c3fSmrg
22801e04c3fSmrg         ib.count = count;
2297ec681f3Smrg         ib.index_size_shift = 2;
2307ec681f3Smrg         ib.obj = NULL;
23101e04c3fSmrg         ib.ptr = elts;
23201e04c3fSmrg
23301e04c3fSmrg         tmpprim = *prim;
23401e04c3fSmrg         tmpprim.start = 0;
23501e04c3fSmrg         tmpprim.count = count;
23601e04c3fSmrg
23701e04c3fSmrg         flush_vertex(split);
23801e04c3fSmrg
23901e04c3fSmrg         _tnl_split_copy(split->ctx,
24001e04c3fSmrg                         split->array,
24101e04c3fSmrg                         &tmpprim, 1,
24201e04c3fSmrg                         &ib,
24301e04c3fSmrg                         split->draw,
24401e04c3fSmrg                         split->limits);
24501e04c3fSmrg
24601e04c3fSmrg         free(elts);
24701e04c3fSmrg      }
24801e04c3fSmrg      else {
24901e04c3fSmrg         flush_vertex(split);
25001e04c3fSmrg
25101e04c3fSmrg         _tnl_split_copy(split->ctx,
25201e04c3fSmrg                         split->array,
25301e04c3fSmrg                         prim, 1,
25401e04c3fSmrg                         split->ib,
25501e04c3fSmrg                         split->draw,
25601e04c3fSmrg                         split->limits);
25701e04c3fSmrg      }
25801e04c3fSmrg   }
25901e04c3fSmrg
26001e04c3fSmrg   flush_vertex(split);
26101e04c3fSmrg}
26201e04c3fSmrg
26301e04c3fSmrg
26401e04c3fSmrgvoid
26501e04c3fSmrg_tnl_split_inplace(struct gl_context *ctx,
26601e04c3fSmrg                   const struct tnl_vertex_array *arrays,
26701e04c3fSmrg                   const struct _mesa_prim *prim,
26801e04c3fSmrg                   GLuint nr_prims,
26901e04c3fSmrg                   const struct _mesa_index_buffer *ib,
2707ec681f3Smrg                   GLuint num_instances,
2717ec681f3Smrg                   GLuint base_instance,
27201e04c3fSmrg                   tnl_draw_func draw,
27301e04c3fSmrg                   const struct split_limits *limits)
27401e04c3fSmrg{
27501e04c3fSmrg   struct split_context split;
27601e04c3fSmrg
27701e04c3fSmrg   memset(&split, 0, sizeof(split));
27801e04c3fSmrg
27901e04c3fSmrg   split.ctx = ctx;
28001e04c3fSmrg   split.array = arrays;
28101e04c3fSmrg   split.prim = prim;
28201e04c3fSmrg   split.nr_prims = nr_prims;
28301e04c3fSmrg   split.ib = ib;
28401e04c3fSmrg
28501e04c3fSmrg   /* Empty interval, makes calculations simpler. */
28601e04c3fSmrg   split.min_index = ~0;
28701e04c3fSmrg   split.max_index = 0;
2887ec681f3Smrg   split.num_instances = num_instances;
2897ec681f3Smrg   split.base_instance = base_instance;
29001e04c3fSmrg
29101e04c3fSmrg   split.draw = draw;
29201e04c3fSmrg   split.limits = limits;
29301e04c3fSmrg   split.limit = ib ? limits->max_indices : limits->max_verts;
29401e04c3fSmrg
29501e04c3fSmrg   split_prims(&split);
29601e04c3fSmrg}
297