vbo_split_copy.c revision 2708ddfb06a36d8568e2aa130bf1f7d551fcd309
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@tungstengraphics.com> 27 */ 28 29/* Split indexed primitives with per-vertex copying. 30 */ 31 32#include "main/glheader.h" 33#include "main/imports.h" 34#include "main/image.h" 35#include "main/macros.h" 36#include "main/enums.h" 37#include "main/mtypes.h" 38 39#include "vbo_split.h" 40#include "vbo.h" 41 42 43#define ELT_TABLE_SIZE 16 44 45/** 46 * Used for vertex-level splitting of indexed buffers. Note that 47 * non-indexed primitives may be converted to indexed in some cases 48 * (eg loops, fans) in order to use this splitting path. 49 */ 50struct copy_context { 51 52 GLcontext *ctx; 53 const struct gl_client_array **array; 54 const struct _mesa_prim *prim; 55 GLuint nr_prims; 56 const struct _mesa_index_buffer *ib; 57 vbo_draw_func draw; 58 59 const struct split_limits *limits; 60 61 struct { 62 GLuint attr; 63 GLuint size; 64 const struct gl_client_array *array; 65 const GLubyte *src_ptr; 66 67 struct gl_client_array dstarray; 68 69 } varying[VERT_ATTRIB_MAX]; 70 GLuint nr_varying; 71 72 const struct gl_client_array *dstarray_ptr[VERT_ATTRIB_MAX]; 73 struct _mesa_index_buffer dstib; 74 75 GLuint *translated_elt_buf; 76 const GLuint *srcelt; 77 78 /** A baby hash table to avoid re-emitting (some) duplicate 79 * vertices when splitting indexed primitives. 80 */ 81 struct { 82 GLuint in; 83 GLuint out; 84 } vert_cache[ELT_TABLE_SIZE]; 85 86 GLuint vertex_size; 87 GLubyte *dstbuf; 88 GLubyte *dstptr; /**< dstptr == dstbuf + dstelt_max * vertsize */ 89 GLuint dstbuf_size; /**< in vertices */ 90 GLuint dstbuf_nr; /**< count of emitted vertices, also the largest value 91 * in dstelt. Our MaxIndex. 92 */ 93 94 GLuint *dstelt; 95 GLuint dstelt_nr; 96 GLuint dstelt_size; 97 98#define MAX_PRIM 32 99 struct _mesa_prim dstprim[MAX_PRIM]; 100 GLuint dstprim_nr; 101 102}; 103 104 105static GLuint attr_size( const struct gl_client_array *array ) 106{ 107 return array->Size * _mesa_sizeof_type(array->Type); 108} 109 110 111/** 112 * Starts returning true slightly before the buffer fills, to ensure 113 * that there is sufficient room for any remaining vertices to finish 114 * off the prim: 115 */ 116static GLboolean 117check_flush( struct copy_context *copy ) 118{ 119 GLenum mode = copy->dstprim[copy->dstprim_nr].mode; 120 121 if (GL_TRIANGLE_STRIP == mode && 122 copy->dstelt_nr & 1) { /* see bug9962 */ 123 return GL_FALSE; 124 } 125 126 if (copy->dstbuf_nr + 4 > copy->dstbuf_size) 127 return GL_TRUE; 128 129 if (copy->dstelt_nr + 4 > copy->dstelt_size) 130 return GL_TRUE; 131 132 return GL_FALSE; 133} 134 135 136/** 137 * Dump the parameters/info for a vbo->draw() call. 138 */ 139static void 140dump_draw_info(GLcontext *ctx, 141 const struct gl_client_array **arrays, 142 const struct _mesa_prim *prims, 143 GLuint nr_prims, 144 const struct _mesa_index_buffer *ib, 145 GLuint min_index, 146 GLuint max_index) 147{ 148 GLuint i, j; 149 150 _mesa_printf("VBO Draw:\n"); 151 for (i = 0; i < nr_prims; i++) { 152 _mesa_printf("Prim %u of %u\n", i, nr_prims); 153 _mesa_printf(" Prim mode 0x%x\n", prims[i].mode); 154 _mesa_printf(" IB: %p\n", (void*) ib); 155 for (j = 0; j < VERT_ATTRIB_MAX; j++) { 156 _mesa_printf(" array %d at %p:\n", j, (void*) arrays[j]); 157 _mesa_printf(" enabled %d, ptr %p, size %d, type 0x%x, stride %d\n", 158 arrays[j]->Enabled, arrays[j]->Ptr, 159 arrays[j]->Size, arrays[j]->Type, arrays[j]->StrideB); 160 if (0) { 161 GLint k = prims[i].start + prims[i].count - 1; 162 GLfloat *last = (GLfloat *) (arrays[j]->Ptr + arrays[j]->Stride * k); 163 _mesa_printf(" last: %f %f %f\n", 164 last[0], last[1], last[2]); 165 } 166 } 167 } 168} 169 170 171static void 172flush( struct copy_context *copy ) 173{ 174 GLuint i; 175 176 /* Set some counters: 177 */ 178 copy->dstib.count = copy->dstelt_nr; 179 180#if 0 181 dump_draw_info(copy->ctx, 182 copy->dstarray_ptr, 183 copy->dstprim, 184 copy->dstprim_nr, 185 ©->dstib, 186 0, 187 copy->dstbuf_nr); 188#else 189 (void) dump_draw_info; 190#endif 191 192 copy->draw( copy->ctx, 193 copy->dstarray_ptr, 194 copy->dstprim, 195 copy->dstprim_nr, 196 ©->dstib, 197 GL_TRUE, 198 0, 199 copy->dstbuf_nr ); 200 201 /* Reset all pointers: 202 */ 203 copy->dstprim_nr = 0; 204 copy->dstelt_nr = 0; 205 copy->dstbuf_nr = 0; 206 copy->dstptr = copy->dstbuf; 207 208 /* Clear the vertex cache: 209 */ 210 for (i = 0; i < ELT_TABLE_SIZE; i++) 211 copy->vert_cache[i].in = ~0; 212} 213 214 215/** 216 * Called at begin of each primitive during replay. 217 */ 218static void 219begin( struct copy_context *copy, GLenum mode, GLboolean begin_flag ) 220{ 221 struct _mesa_prim *prim = ©->dstprim[copy->dstprim_nr]; 222 223/* _mesa_printf("begin %s (%d)\n", _mesa_lookup_prim_by_nr(mode), begin_flag); */ 224 225 prim->mode = mode; 226 prim->begin = begin_flag; 227} 228 229 230/** 231 * Use a hashtable to attempt to identify recently-emitted vertices 232 * and avoid re-emitting them. 233 */ 234static GLuint 235elt(struct copy_context *copy, GLuint elt_idx) 236{ 237 GLuint elt = copy->srcelt[elt_idx]; 238 GLuint slot = elt & (ELT_TABLE_SIZE-1); 239 240/* _mesa_printf("elt %d\n", elt); */ 241 242 /* Look up the incoming element in the vertex cache. Re-emit if 243 * necessary. 244 */ 245 if (copy->vert_cache[slot].in != elt) { 246 GLubyte *csr = copy->dstptr; 247 GLuint i; 248 249/* _mesa_printf(" --> emit to dstelt %d\n", copy->dstbuf_nr); */ 250 251 for (i = 0; i < copy->nr_varying; i++) { 252 const struct gl_client_array *srcarray = copy->varying[i].array; 253 const GLubyte *srcptr = copy->varying[i].src_ptr + elt * srcarray->StrideB; 254 255 memcpy(csr, srcptr, copy->varying[i].size); 256 csr += copy->varying[i].size; 257 258#ifdef NAN_CHECK 259 if (srcarray->Type == GL_FLOAT) { 260 GLuint k; 261 GLfloat *f = (GLfloat *) srcptr; 262 for (k = 0; k < srcarray->Size; k++) { 263 assert(!IS_INF_OR_NAN(f[k])); 264 assert(f[k] <= 1.0e20 && f[k] >= -1.0e20); 265 } 266 } 267#endif 268 269 if (0) 270 { 271 const GLuint *f = (const GLuint *)srcptr; 272 GLuint j; 273 _mesa_printf(" varying %d: ", i); 274 for(j = 0; j < copy->varying[i].size / 4; j++) 275 _mesa_printf("%x ", f[j]); 276 _mesa_printf("\n"); 277 } 278 } 279 280 copy->vert_cache[slot].in = elt; 281 copy->vert_cache[slot].out = copy->dstbuf_nr++; 282 copy->dstptr += copy->vertex_size; 283 284 assert(csr == copy->dstptr); 285 assert(copy->dstptr == (copy->dstbuf + 286 copy->dstbuf_nr * copy->vertex_size)); 287 } 288/* else */ 289/* _mesa_printf(" --> reuse vertex\n"); */ 290 291/* _mesa_printf(" --> emit %d\n", copy->vert_cache[slot].out); */ 292 copy->dstelt[copy->dstelt_nr++] = copy->vert_cache[slot].out; 293 return check_flush(copy); 294} 295 296 297/** 298 * Called at end of each primitive during replay. 299 */ 300static void 301end( struct copy_context *copy, GLboolean end_flag ) 302{ 303 struct _mesa_prim *prim = ©->dstprim[copy->dstprim_nr]; 304 305/* _mesa_printf("end (%d)\n", end_flag); */ 306 307 prim->end = end_flag; 308 prim->count = copy->dstelt_nr - prim->start; 309 310 if (++copy->dstprim_nr == MAX_PRIM || 311 check_flush(copy)) 312 flush(copy); 313} 314 315 316static void 317replay_elts( struct copy_context *copy ) 318{ 319 GLuint i, j, k; 320 GLboolean split; 321 322 for (i = 0; i < copy->nr_prims; i++) { 323 const struct _mesa_prim *prim = ©->prim[i]; 324 const GLuint start = prim->start; 325 GLuint first, incr; 326 327 switch (prim->mode) { 328 329 case GL_LINE_LOOP: 330 /* Convert to linestrip and emit the final vertex explicitly, 331 * but only in the resultant strip that requires it. 332 */ 333 j = 0; 334 while (j != prim->count) { 335 begin(copy, GL_LINE_STRIP, prim->begin && j == 0); 336 337 for (split = GL_FALSE; j != prim->count && !split; j++) 338 split = elt(copy, start + j); 339 340 if (j == prim->count) { 341 /* Done, emit final line. Split doesn't matter as 342 * it is always raised a bit early so we can emit 343 * the last verts if necessary! 344 */ 345 if (prim->end) 346 (void)elt(copy, start + 0); 347 348 end(copy, prim->end); 349 } 350 else { 351 /* Wrap 352 */ 353 assert(split); 354 end(copy, 0); 355 j--; 356 } 357 } 358 break; 359 360 case GL_TRIANGLE_FAN: 361 case GL_POLYGON: 362 j = 2; 363 while (j != prim->count) { 364 begin(copy, prim->mode, prim->begin && j == 0); 365 366 split = elt(copy, start+0); 367 assert(!split); 368 369 split = elt(copy, start+j-1); 370 assert(!split); 371 372 for (; j != prim->count && !split; j++) 373 split = elt(copy, start+j); 374 375 end(copy, prim->end && j == prim->count); 376 377 if (j != prim->count) { 378 /* Wrapped the primitive, need to repeat some vertices: 379 */ 380 j -= 1; 381 } 382 } 383 break; 384 385 default: 386 (void)split_prim_inplace(prim->mode, &first, &incr); 387 388 j = 0; 389 while (j != prim->count) { 390 391 begin(copy, prim->mode, prim->begin && j == 0); 392 393 split = 0; 394 for (k = 0; k < first; k++, j++) 395 split |= elt(copy, start+j); 396 397 assert(!split); 398 399 for (; j != prim->count && !split; ) 400 for (k = 0; k < incr; k++, j++) 401 split |= elt(copy, start+j); 402 403 end(copy, prim->end && j == prim->count); 404 405 if (j != prim->count) { 406 /* Wrapped the primitive, need to repeat some vertices: 407 */ 408 assert(j > first - incr); 409 j -= (first - incr); 410 } 411 } 412 break; 413 } 414 } 415 416 if (copy->dstprim_nr) 417 flush(copy); 418} 419 420 421static void 422replay_init( struct copy_context *copy ) 423{ 424 GLcontext *ctx = copy->ctx; 425 GLuint i; 426 GLuint offset; 427 const GLvoid *srcptr; 428 429 /* Make a list of varying attributes and their vbo's. Also 430 * calculate vertex size. 431 */ 432 copy->vertex_size = 0; 433 for (i = 0; i < VERT_ATTRIB_MAX; i++) { 434 struct gl_buffer_object *vbo = copy->array[i]->BufferObj; 435 436 if (copy->array[i]->StrideB == 0) { 437 copy->dstarray_ptr[i] = copy->array[i]; 438 } 439 else { 440 GLuint j = copy->nr_varying++; 441 442 copy->varying[j].attr = i; 443 copy->varying[j].array = copy->array[i]; 444 copy->varying[j].size = attr_size(copy->array[i]); 445 copy->vertex_size += attr_size(copy->array[i]); 446 447 if (vbo->Name && !vbo->Pointer) 448 ctx->Driver.MapBuffer(ctx, GL_ARRAY_BUFFER, GL_READ_ONLY, vbo); 449 450 copy->varying[j].src_ptr = ADD_POINTERS(vbo->Pointer, 451 copy->array[i]->Ptr); 452 453 copy->dstarray_ptr[i] = ©->varying[j].dstarray; 454 } 455 } 456 457 /* There must always be an index buffer. Currently require the 458 * caller convert non-indexed prims to indexed. Could alternately 459 * do it internally. 460 */ 461 if (copy->ib->obj->Name && !copy->ib->obj->Pointer) 462 ctx->Driver.MapBuffer(ctx, GL_ELEMENT_ARRAY_BUFFER, GL_READ_ONLY, 463 copy->ib->obj); 464 465 srcptr = (const GLubyte *) ADD_POINTERS(copy->ib->obj->Pointer, 466 copy->ib->ptr); 467 468 switch (copy->ib->type) { 469 case GL_UNSIGNED_BYTE: 470 copy->translated_elt_buf = _mesa_malloc(sizeof(GLuint) * copy->ib->count); 471 copy->srcelt = copy->translated_elt_buf; 472 473 for (i = 0; i < copy->ib->count; i++) 474 copy->translated_elt_buf[i] = ((const GLubyte *)srcptr)[i]; 475 break; 476 477 case GL_UNSIGNED_SHORT: 478 copy->translated_elt_buf = _mesa_malloc(sizeof(GLuint) * copy->ib->count); 479 copy->srcelt = copy->translated_elt_buf; 480 481 for (i = 0; i < copy->ib->count; i++) 482 copy->translated_elt_buf[i] = ((const GLushort *)srcptr)[i]; 483 break; 484 485 case GL_UNSIGNED_INT: 486 copy->translated_elt_buf = NULL; 487 copy->srcelt = (const GLuint *)srcptr; 488 break; 489 } 490 491 /* Figure out the maximum allowed vertex buffer size: 492 */ 493 if (copy->vertex_size * copy->limits->max_verts <= copy->limits->max_vb_size) { 494 copy->dstbuf_size = copy->limits->max_verts; 495 } 496 else { 497 copy->dstbuf_size = copy->limits->max_vb_size / copy->vertex_size; 498 } 499 500 /* Allocate an output vertex buffer: 501 * 502 * XXX: This should be a VBO! 503 */ 504 copy->dstbuf = _mesa_malloc(copy->dstbuf_size * copy->vertex_size); 505 copy->dstptr = copy->dstbuf; 506 507 /* Setup new vertex arrays to point into the output buffer: 508 */ 509 for (offset = 0, i = 0; i < copy->nr_varying; i++) { 510 const struct gl_client_array *src = copy->varying[i].array; 511 struct gl_client_array *dst = ©->varying[i].dstarray; 512 513 dst->Size = src->Size; 514 dst->Type = src->Type; 515 dst->Format = GL_RGBA; 516 dst->Stride = copy->vertex_size; 517 dst->StrideB = copy->vertex_size; 518 dst->Ptr = copy->dstbuf + offset; 519 dst->Enabled = GL_TRUE; 520 dst->Normalized = src->Normalized; 521 dst->BufferObj = ctx->Shared->NullBufferObj; 522 dst->_MaxElement = copy->dstbuf_size; /* may be less! */ 523 524 offset += copy->varying[i].size; 525 } 526 527 /* Allocate an output element list: 528 */ 529 copy->dstelt_size = MIN2(65536, 530 copy->ib->count * 2 + 3); 531 copy->dstelt_size = MIN2(copy->dstelt_size, 532 copy->limits->max_indices); 533 copy->dstelt = _mesa_malloc(sizeof(GLuint) * copy->dstelt_size); 534 copy->dstelt_nr = 0; 535 536 /* Setup the new index buffer to point to the allocated element 537 * list: 538 */ 539 copy->dstib.count = 0; /* duplicates dstelt_nr */ 540 copy->dstib.type = GL_UNSIGNED_INT; 541 copy->dstib.obj = ctx->Shared->NullBufferObj; 542 copy->dstib.ptr = copy->dstelt; 543} 544 545 546/** 547 * Free up everything allocated during split/replay. 548 */ 549static void 550replay_finish( struct copy_context *copy ) 551{ 552 GLcontext *ctx = copy->ctx; 553 GLuint i; 554 555 /* Free our vertex and index buffers: 556 */ 557 _mesa_free(copy->translated_elt_buf); 558 _mesa_free(copy->dstbuf); 559 _mesa_free(copy->dstelt); 560 561 /* Unmap VBO's 562 */ 563 for (i = 0; i < copy->nr_varying; i++) { 564 struct gl_buffer_object *vbo = copy->varying[i].array->BufferObj; 565 if (vbo->Name && vbo->Pointer) 566 ctx->Driver.UnmapBuffer(ctx, GL_ARRAY_BUFFER, vbo); 567 } 568 569 /* Unmap index buffer: 570 */ 571 if (copy->ib->obj->Name && copy->ib->obj->Pointer) { 572 ctx->Driver.UnmapBuffer(ctx, GL_ELEMENT_ARRAY_BUFFER, copy->ib->obj); 573 } 574} 575 576 577/** 578 * Split VBO into smaller pieces, draw the pieces. 579 */ 580void vbo_split_copy( GLcontext *ctx, 581 const struct gl_client_array *arrays[], 582 const struct _mesa_prim *prim, 583 GLuint nr_prims, 584 const struct _mesa_index_buffer *ib, 585 vbo_draw_func draw, 586 const struct split_limits *limits ) 587{ 588 struct copy_context copy; 589 GLuint i; 590 591 memset(©, 0, sizeof(copy)); 592 593 /* Require indexed primitives: 594 */ 595 assert(ib); 596 597 copy.ctx = ctx; 598 copy.array = arrays; 599 copy.prim = prim; 600 copy.nr_prims = nr_prims; 601 copy.ib = ib; 602 copy.draw = draw; 603 copy.limits = limits; 604 605 /* Clear the vertex cache: 606 */ 607 for (i = 0; i < ELT_TABLE_SIZE; i++) 608 copy.vert_cache[i].in = ~0; 609 610 replay_init(©); 611 replay_elts(©); 612 replay_finish(©); 613} 614