Home | History | Annotate | Download | only in vbo
      1 
      2 /*
      3  * Mesa 3-D graphics library
      4  * Version:  6.5
      5  *
      6  * Copyright (C) 1999-2006  Brian Paul   All Rights Reserved.
      7  *
      8  * Permission is hereby granted, free of charge, to any person obtaining a
      9  * copy of this software and associated documentation files (the "Software"),
     10  * to deal in the Software without restriction, including without limitation
     11  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     12  * and/or sell copies of the Software, and to permit persons to whom the
     13  * Software is furnished to do so, subject to the following conditions:
     14  *
     15  * The above copyright notice and this permission notice shall be included
     16  * in all copies or substantial portions of the Software.
     17  *
     18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
     19  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     20  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     21  * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
     22  * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
     23  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     24  *
     25  * Authors:
     26  *    Keith Whitwell <keith (at) tungstengraphics.com>
     27  */
     28 
     29 
     30 #include "main/mtypes.h"
     31 #include "main/macros.h"
     32 #include "main/enums.h"
     33 #include "main/glformats.h"
     34 #include "vbo_split.h"
     35 
     36 
     37 #define MAX_PRIM 32
     38 
     39 /* Used for splitting without copying. No attempt is made to handle
     40  * too large indexed vertex buffers: In general you need to copy to do
     41  * that.
     42  */
     43 struct split_context {
     44    struct gl_context *ctx;
     45    const struct gl_client_array **array;
     46    const struct _mesa_prim *prim;
     47    GLuint nr_prims;
     48    const struct _mesa_index_buffer *ib;
     49    GLuint min_index;
     50    GLuint max_index;
     51    vbo_draw_func draw;
     52 
     53    const struct split_limits *limits;
     54    GLuint limit;
     55 
     56    struct _mesa_prim dstprim[MAX_PRIM];
     57    GLuint dstprim_nr;
     58 };
     59 
     60 
     61 
     62 
     63 static void flush_vertex( struct split_context *split )
     64 {
     65    struct gl_context *ctx = split->ctx;
     66    const struct gl_client_array **saved_arrays = ctx->Array._DrawArrays;
     67    struct _mesa_index_buffer ib;
     68    GLuint i;
     69 
     70    if (!split->dstprim_nr)
     71       return;
     72 
     73    if (split->ib) {
     74       ib = *split->ib;
     75 
     76       ib.count = split->max_index - split->min_index + 1;
     77       ib.ptr = (const void *)((const char *)ib.ptr +
     78                               split->min_index * _mesa_sizeof_type(ib.type));
     79 
     80       /* Rebase the primitives to save index buffer entries. */
     81       for (i = 0; i < split->dstprim_nr; i++)
     82 	 split->dstprim[i].start -= split->min_index;
     83    }
     84 
     85    assert(split->max_index >= split->min_index);
     86 
     87    ctx->Array._DrawArrays = split->array;
     88    ctx->NewDriverState |= ctx->DriverFlags.NewArray;
     89 
     90    split->draw(ctx,
     91 	       split->dstprim,
     92 	       split->dstprim_nr,
     93 	       split->ib ? &ib : NULL,
     94 	       !split->ib,
     95 	       split->min_index,
     96 	       split->max_index,
     97 	       NULL);
     98 
     99    ctx->Array._DrawArrays = saved_arrays;
    100    ctx->NewDriverState |= ctx->DriverFlags.NewArray;
    101 
    102    split->dstprim_nr = 0;
    103    split->min_index = ~0;
    104    split->max_index = 0;
    105 }
    106 
    107 
    108 static struct _mesa_prim *next_outprim( struct split_context *split )
    109 {
    110    if (split->dstprim_nr == MAX_PRIM-1) {
    111       flush_vertex(split);
    112    }
    113 
    114    {
    115       struct _mesa_prim *prim = &split->dstprim[split->dstprim_nr++];
    116       memset(prim, 0, sizeof(*prim));
    117       return prim;
    118    }
    119 }
    120 
    121 static void update_index_bounds(struct split_context *split,
    122 				const struct _mesa_prim *prim)
    123 {
    124    split->min_index = MIN2(split->min_index, prim->start);
    125    split->max_index = MAX2(split->max_index, prim->start + prim->count - 1);
    126 }
    127 
    128 /* Return the maximum amount of vertices that can be emitted for a
    129  * primitive starting at 'prim->start', depending on the previous
    130  * index bounds.
    131  */
    132 static GLuint get_max_vertices(struct split_context *split,
    133 			       const struct _mesa_prim *prim)
    134 {
    135    if ((prim->start > split->min_index &&
    136 	prim->start - split->min_index >= split->limit) ||
    137        (prim->start < split->max_index &&
    138         split->max_index - prim->start >= split->limit))
    139       /* "prim" starts too far away from the old range. */
    140       return 0;
    141 
    142    return MIN2(split->min_index, prim->start) + split->limit - prim->start;
    143 }
    144 
    145 /* Break large primitives into smaller ones.  If not possible, convert
    146  * the primitive to indexed and pass to split_elts().
    147  */
    148 static void split_prims( struct split_context *split)
    149 {
    150    GLuint i;
    151 
    152    for (i = 0; i < split->nr_prims; i++) {
    153       const struct _mesa_prim *prim = &split->prim[i];
    154       GLuint first, incr;
    155       GLboolean split_inplace = split_prim_inplace(prim->mode, &first, &incr);
    156       GLuint available = get_max_vertices(split, prim);
    157       GLuint count = prim->count - (prim->count - first) % incr;
    158 
    159       if (prim->count < first)
    160 	 continue;
    161 
    162       if ((available < count && !split_inplace) ||
    163 	  (available < first && split_inplace)) {
    164 	 flush_vertex(split);
    165 	 available = get_max_vertices(split, prim);
    166       }
    167 
    168       if (available >= count) {
    169 	 struct _mesa_prim *outprim = next_outprim(split);
    170 
    171 	 *outprim = *prim;
    172 	 update_index_bounds(split, outprim);
    173       }
    174       else if (split_inplace) {
    175 	 GLuint j, nr;
    176 
    177 	 for (j = 0 ; j < count ; ) {
    178 	    GLuint remaining = count - j;
    179 	    struct _mesa_prim *outprim = next_outprim(split);
    180 
    181 	    nr = MIN2( available, remaining );
    182 	    nr -= (nr - first) % incr;
    183 
    184 	    outprim->mode = prim->mode;
    185 	    outprim->begin = (j == 0 && prim->begin);
    186 	    outprim->end = (nr == remaining && prim->end);
    187 	    outprim->start = prim->start + j;
    188 	    outprim->count = nr;
    189             outprim->num_instances = prim->num_instances;
    190             outprim->base_instance = prim->base_instance;
    191 
    192 	    update_index_bounds(split, outprim);
    193 
    194 	    if (nr == remaining) {
    195 	       /* Finished.
    196 		*/
    197 	       j += nr;
    198 	    }
    199 	    else {
    200 	       /* Wrapped the primitive:
    201 		*/
    202 	       j += nr - (first - incr);
    203 	       flush_vertex(split);
    204 	       available = get_max_vertices(split, prim);
    205 	    }
    206 	 }
    207       }
    208       else if (split->ib == NULL) {
    209 	 /* XXX: could at least send the first max_verts off from the
    210 	  * inplace buffers.
    211 	  */
    212 
    213 	 /* else convert to indexed primitive and pass to split_elts,
    214 	  * which will do the necessary copying and turn it back into a
    215 	  * vertex primitive for rendering...
    216 	  */
    217 	 struct _mesa_index_buffer ib;
    218 	 struct _mesa_prim tmpprim;
    219 	 GLuint *elts = malloc(count * sizeof(GLuint));
    220 	 GLuint j;
    221 
    222 	 for (j = 0; j < count; j++)
    223 	    elts[j] = prim->start + j;
    224 
    225 	 ib.count = count;
    226 	 ib.type = GL_UNSIGNED_INT;
    227 	 ib.obj = split->ctx->Shared->NullBufferObj;
    228 	 ib.ptr = elts;
    229 
    230 	 tmpprim = *prim;
    231 	 tmpprim.indexed = 1;
    232 	 tmpprim.start = 0;
    233 	 tmpprim.count = count;
    234          tmpprim.num_instances = 1;
    235          tmpprim.base_instance = 0;
    236 
    237 	 flush_vertex(split);
    238 
    239 	 vbo_split_copy(split->ctx,
    240 			split->array,
    241 			&tmpprim, 1,
    242 			&ib,
    243 			split->draw,
    244 			split->limits);
    245 
    246 	 free(elts);
    247       }
    248       else {
    249 	 flush_vertex(split);
    250 
    251 	 vbo_split_copy(split->ctx,
    252 			split->array,
    253 			prim, 1,
    254 			split->ib,
    255 			split->draw,
    256 			split->limits);
    257       }
    258    }
    259 
    260    flush_vertex(split);
    261 }
    262 
    263 
    264 void vbo_split_inplace( struct gl_context *ctx,
    265 			const struct gl_client_array *arrays[],
    266 			const struct _mesa_prim *prim,
    267 			GLuint nr_prims,
    268 			const struct _mesa_index_buffer *ib,
    269 			GLuint min_index,
    270 			GLuint max_index,
    271 			vbo_draw_func draw,
    272 			const struct split_limits *limits )
    273 {
    274    struct split_context split;
    275 
    276    memset(&split, 0, sizeof(split));
    277 
    278    split.ctx = ctx;
    279    split.array = arrays;
    280    split.prim = prim;
    281    split.nr_prims = nr_prims;
    282    split.ib = ib;
    283 
    284    /* Empty interval, makes calculations simpler. */
    285    split.min_index = ~0;
    286    split.max_index = 0;
    287 
    288    split.draw = draw;
    289    split.limits = limits;
    290    split.limit = ib ? limits->max_indices : limits->max_verts;
    291 
    292    split_prims( &split );
    293 }
    294 
    295 
    296