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