vbo_save_api.c revision c4d1f4607abf3dfbcfcffc5c67bb89d420d3381a
1/************************************************************************** 2 3Copyright 2002-2008 Tungsten Graphics Inc., Cedar Park, Texas. 4 5All Rights Reserved. 6 7Permission is hereby granted, free of charge, to any person obtaining a 8copy of this software and associated documentation files (the "Software"), 9to deal in the Software without restriction, including without limitation 10on the rights to use, copy, modify, merge, publish, distribute, sub 11license, and/or sell copies of the Software, and to permit persons to whom 12the Software is furnished to do so, subject to the following conditions: 13 14The above copyright notice and this permission notice (including the next 15paragraph) shall be included in all copies or substantial portions of the 16Software. 17 18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 21TUNGSTEN GRAPHICS AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, 22DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 23OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 24USE OR OTHER DEALINGS IN THE SOFTWARE. 25 26**************************************************************************/ 27 28/* 29 * Authors: 30 * Keith Whitwell <keith@tungstengraphics.com> 31 */ 32 33 34 35/* Display list compiler attempts to store lists of vertices with the 36 * same vertex layout. Additionally it attempts to minimize the need 37 * for execute-time fixup of these vertex lists, allowing them to be 38 * cached on hardware. 39 * 40 * There are still some circumstances where this can be thwarted, for 41 * example by building a list that consists of one very long primitive 42 * (eg Begin(Triangles), 1000 vertices, End), and calling that list 43 * from inside a different begin/end object (Begin(Lines), CallList, 44 * End). 45 * 46 * In that case the code will have to replay the list as individual 47 * commands through the Exec dispatch table, or fix up the copied 48 * vertices at execute-time. 49 * 50 * The other case where fixup is required is when a vertex attribute 51 * is introduced in the middle of a primitive. Eg: 52 * Begin(Lines) 53 * TexCoord1f() Vertex2f() 54 * TexCoord1f() Color3f() Vertex2f() 55 * End() 56 * 57 * If the current value of Color isn't known at compile-time, this 58 * primitive will require fixup. 59 * 60 * 61 * The list compiler currently doesn't attempt to compile lists 62 * containing EvalCoord or EvalPoint commands. On encountering one of 63 * these, compilation falls back to opcodes. 64 * 65 * This could be improved to fallback only when a mix of EvalCoord and 66 * Vertex commands are issued within a single primitive. 67 */ 68 69 70#include "main/glheader.h" 71#include "main/bufferobj.h" 72#include "main/context.h" 73#include "main/dlist.h" 74#include "main/enums.h" 75#include "main/macros.h" 76#include "main/api_validate.h" 77#include "main/api_arrayelt.h" 78#include "main/vtxfmt.h" 79#include "glapi/dispatch.h" 80 81#include "vbo_context.h" 82 83 84#ifdef ERROR 85#undef ERROR 86#endif 87 88 89/* An interesting VBO number/name to help with debugging */ 90#define VBO_BUF_ID 12345 91 92 93/* 94 * NOTE: Old 'parity' issue is gone, but copying can still be 95 * wrong-footed on replay. 96 */ 97static GLuint _save_copy_vertices( GLcontext *ctx, 98 const struct vbo_save_vertex_list *node, 99 const GLfloat *src_buffer) 100{ 101 struct vbo_save_context *save = &vbo_context( ctx )->save; 102 const struct _mesa_prim *prim = &node->prim[node->prim_count-1]; 103 GLuint nr = prim->count; 104 GLuint sz = save->vertex_size; 105 const GLfloat *src = src_buffer + prim->start * sz; 106 GLfloat *dst = save->copied.buffer; 107 GLuint ovf, i; 108 109 if (prim->end) 110 return 0; 111 112 switch( prim->mode ) 113 { 114 case GL_POINTS: 115 return 0; 116 case GL_LINES: 117 ovf = nr&1; 118 for (i = 0 ; i < ovf ; i++) 119 _mesa_memcpy( dst+i*sz, src+(nr-ovf+i)*sz, sz*sizeof(GLfloat) ); 120 return i; 121 case GL_TRIANGLES: 122 ovf = nr%3; 123 for (i = 0 ; i < ovf ; i++) 124 _mesa_memcpy( dst+i*sz, src+(nr-ovf+i)*sz, sz*sizeof(GLfloat) ); 125 return i; 126 case GL_QUADS: 127 ovf = nr&3; 128 for (i = 0 ; i < ovf ; i++) 129 _mesa_memcpy( dst+i*sz, src+(nr-ovf+i)*sz, sz*sizeof(GLfloat) ); 130 return i; 131 case GL_LINE_STRIP: 132 if (nr == 0) 133 return 0; 134 else { 135 _mesa_memcpy( dst, src+(nr-1)*sz, sz*sizeof(GLfloat) ); 136 return 1; 137 } 138 case GL_LINE_LOOP: 139 case GL_TRIANGLE_FAN: 140 case GL_POLYGON: 141 if (nr == 0) 142 return 0; 143 else if (nr == 1) { 144 _mesa_memcpy( dst, src+0, sz*sizeof(GLfloat) ); 145 return 1; 146 } else { 147 _mesa_memcpy( dst, src+0, sz*sizeof(GLfloat) ); 148 _mesa_memcpy( dst+sz, src+(nr-1)*sz, sz*sizeof(GLfloat) ); 149 return 2; 150 } 151 case GL_TRIANGLE_STRIP: 152 case GL_QUAD_STRIP: 153 switch (nr) { 154 case 0: ovf = 0; break; 155 case 1: ovf = 1; break; 156 default: ovf = 2 + (nr&1); break; 157 } 158 for (i = 0 ; i < ovf ; i++) 159 _mesa_memcpy( dst+i*sz, src+(nr-ovf+i)*sz, sz*sizeof(GLfloat) ); 160 return i; 161 default: 162 assert(0); 163 return 0; 164 } 165} 166 167 168static struct vbo_save_vertex_store *alloc_vertex_store( GLcontext *ctx ) 169{ 170 struct vbo_save_vertex_store *vertex_store = CALLOC_STRUCT(vbo_save_vertex_store); 171 172 /* obj->Name needs to be non-zero, but won't ever be examined more 173 * closely than that. In particular these buffers won't be entered 174 * into the hash and can never be confused with ones visible to the 175 * user. Perhaps there could be a special number for internal 176 * buffers: 177 */ 178 vertex_store->bufferobj = ctx->Driver.NewBufferObject(ctx, 179 VBO_BUF_ID, 180 GL_ARRAY_BUFFER_ARB); 181 182 ctx->Driver.BufferData( ctx, 183 GL_ARRAY_BUFFER_ARB, 184 VBO_SAVE_BUFFER_SIZE * sizeof(GLfloat), 185 NULL, 186 GL_STATIC_DRAW_ARB, 187 vertex_store->bufferobj); 188 189 vertex_store->buffer = NULL; 190 vertex_store->used = 0; 191 vertex_store->refcount = 1; 192 193 return vertex_store; 194} 195 196static void free_vertex_store( GLcontext *ctx, struct vbo_save_vertex_store *vertex_store ) 197{ 198 assert(!vertex_store->buffer); 199 200 if (vertex_store->bufferobj) { 201 _mesa_reference_buffer_object(ctx, &vertex_store->bufferobj, NULL); 202 } 203 204 FREE( vertex_store ); 205} 206 207static GLfloat *map_vertex_store( GLcontext *ctx, struct vbo_save_vertex_store *vertex_store ) 208{ 209 assert(vertex_store->bufferobj); 210 assert(!vertex_store->buffer); 211 vertex_store->buffer = (GLfloat *)ctx->Driver.MapBuffer(ctx, 212 GL_ARRAY_BUFFER_ARB, /* not used */ 213 GL_WRITE_ONLY, /* not used */ 214 vertex_store->bufferobj); 215 216 assert(vertex_store->buffer); 217 return vertex_store->buffer + vertex_store->used; 218} 219 220static void unmap_vertex_store( GLcontext *ctx, struct vbo_save_vertex_store *vertex_store ) 221{ 222 ctx->Driver.UnmapBuffer( ctx, GL_ARRAY_BUFFER_ARB, vertex_store->bufferobj ); 223 vertex_store->buffer = NULL; 224} 225 226 227static struct vbo_save_primitive_store *alloc_prim_store( GLcontext *ctx ) 228{ 229 struct vbo_save_primitive_store *store = CALLOC_STRUCT(vbo_save_primitive_store); 230 (void) ctx; 231 store->used = 0; 232 store->refcount = 1; 233 return store; 234} 235 236static void _save_reset_counters( GLcontext *ctx ) 237{ 238 struct vbo_save_context *save = &vbo_context(ctx)->save; 239 240 save->prim = save->prim_store->buffer + save->prim_store->used; 241 save->buffer = (save->vertex_store->buffer + 242 save->vertex_store->used); 243 244 assert(save->buffer == save->buffer_ptr); 245 246 if (save->vertex_size) 247 save->max_vert = ((VBO_SAVE_BUFFER_SIZE - save->vertex_store->used) / 248 save->vertex_size); 249 else 250 save->max_vert = 0; 251 252 save->vert_count = 0; 253 save->prim_count = 0; 254 save->prim_max = VBO_SAVE_PRIM_SIZE - save->prim_store->used; 255 save->dangling_attr_ref = 0; 256} 257 258 259/* Insert the active immediate struct onto the display list currently 260 * being built. 261 */ 262static void _save_compile_vertex_list( GLcontext *ctx ) 263{ 264 struct vbo_save_context *save = &vbo_context(ctx)->save; 265 struct vbo_save_vertex_list *node; 266 267 /* Allocate space for this structure in the display list currently 268 * being compiled. 269 */ 270 node = (struct vbo_save_vertex_list *) 271 _mesa_alloc_instruction(ctx, save->opcode_vertex_list, sizeof(*node)); 272 273 if (!node) 274 return; 275 276 /* Duplicate our template, increment refcounts to the storage structs: 277 */ 278 _mesa_memcpy(node->attrsz, save->attrsz, sizeof(node->attrsz)); 279 node->vertex_size = save->vertex_size; 280 node->buffer_offset = (save->buffer - save->vertex_store->buffer) * sizeof(GLfloat); 281 node->count = save->vert_count; 282 node->wrap_count = save->copied.nr; 283 node->dangling_attr_ref = save->dangling_attr_ref; 284 node->prim = save->prim; 285 node->prim_count = save->prim_count; 286 node->vertex_store = save->vertex_store; 287 node->prim_store = save->prim_store; 288 289 node->vertex_store->refcount++; 290 node->prim_store->refcount++; 291 292 assert(node->attrsz[VBO_ATTRIB_POS] != 0 || 293 node->count == 0); 294 295 if (save->dangling_attr_ref) 296 ctx->ListState.CurrentList->flags |= MESA_DLIST_DANGLING_REFS; 297 298 save->vertex_store->used += save->vertex_size * node->count; 299 save->prim_store->used += node->prim_count; 300 301 302 /* Copy duplicated vertices 303 */ 304 save->copied.nr = _save_copy_vertices( ctx, node, save->buffer ); 305 306 307 /* Deal with GL_COMPILE_AND_EXECUTE: 308 */ 309 if (ctx->ExecuteFlag) { 310 struct _glapi_table *dispatch = GET_DISPATCH(); 311 312 _glapi_set_dispatch(ctx->Exec); 313 314 vbo_loopback_vertex_list( ctx, 315 (const GLfloat *)((const char *)save->vertex_store->buffer + 316 node->buffer_offset), 317 node->attrsz, 318 node->prim, 319 node->prim_count, 320 node->wrap_count, 321 node->vertex_size); 322 323 _glapi_set_dispatch(dispatch); 324 } 325 326 327 /* Decide whether the storage structs are full, or can be used for 328 * the next vertex lists as well. 329 */ 330 if (save->vertex_store->used > 331 VBO_SAVE_BUFFER_SIZE - 16 * (save->vertex_size + 4)) { 332 333 /* Unmap old store: 334 */ 335 unmap_vertex_store( ctx, save->vertex_store ); 336 337 /* Release old reference: 338 */ 339 save->vertex_store->refcount--; 340 assert(save->vertex_store->refcount != 0); 341 save->vertex_store = NULL; 342 343 /* Allocate and map new store: 344 */ 345 save->vertex_store = alloc_vertex_store( ctx ); 346 save->buffer_ptr = map_vertex_store( ctx, save->vertex_store ); 347 } 348 349 if (save->prim_store->used > VBO_SAVE_PRIM_SIZE - 6) { 350 save->prim_store->refcount--; 351 assert(save->prim_store->refcount != 0); 352 save->prim_store = alloc_prim_store( ctx ); 353 } 354 355 /* Reset our structures for the next run of vertices: 356 */ 357 _save_reset_counters( ctx ); 358} 359 360 361/* TODO -- If no new vertices have been stored, don't bother saving 362 * it. 363 */ 364static void _save_wrap_buffers( GLcontext *ctx ) 365{ 366 struct vbo_save_context *save = &vbo_context(ctx)->save; 367 GLint i = save->prim_count - 1; 368 GLenum mode; 369 GLboolean weak; 370 371 assert(i < (GLint) save->prim_max); 372 assert(i >= 0); 373 374 /* Close off in-progress primitive. 375 */ 376 save->prim[i].count = (save->vert_count - 377 save->prim[i].start); 378 mode = save->prim[i].mode; 379 weak = save->prim[i].weak; 380 381 /* store the copied vertices, and allocate a new list. 382 */ 383 _save_compile_vertex_list( ctx ); 384 385 /* Restart interrupted primitive 386 */ 387 save->prim[0].mode = mode; 388 save->prim[0].weak = weak; 389 save->prim[0].begin = 0; 390 save->prim[0].end = 0; 391 save->prim[0].pad = 0; 392 save->prim[0].start = 0; 393 save->prim[0].count = 0; 394 save->prim_count = 1; 395} 396 397 398 399/* Called only when buffers are wrapped as the result of filling the 400 * vertex_store struct. 401 */ 402static void _save_wrap_filled_vertex( GLcontext *ctx ) 403{ 404 struct vbo_save_context *save = &vbo_context(ctx)->save; 405 GLfloat *data = save->copied.buffer; 406 GLuint i; 407 408 /* Emit a glEnd to close off the last vertex list. 409 */ 410 _save_wrap_buffers( ctx ); 411 412 /* Copy stored stored vertices to start of new list. 413 */ 414 assert(save->max_vert - save->vert_count > save->copied.nr); 415 416 for (i = 0 ; i < save->copied.nr ; i++) { 417 _mesa_memcpy( save->buffer_ptr, data, save->vertex_size * sizeof(GLfloat)); 418 data += save->vertex_size; 419 save->buffer_ptr += save->vertex_size; 420 save->vert_count++; 421 } 422} 423 424 425static void _save_copy_to_current( GLcontext *ctx ) 426{ 427 struct vbo_save_context *save = &vbo_context(ctx)->save; 428 GLuint i; 429 430 for (i = VBO_ATTRIB_POS+1 ; i < VBO_ATTRIB_MAX ; i++) { 431 if (save->attrsz[i]) { 432 save->currentsz[i][0] = save->attrsz[i]; 433 COPY_CLEAN_4V(save->current[i], 434 save->attrsz[i], 435 save->attrptr[i]); 436 } 437 } 438} 439 440 441static void _save_copy_from_current( GLcontext *ctx ) 442{ 443 struct vbo_save_context *save = &vbo_context(ctx)->save; 444 GLint i; 445 446 for (i = VBO_ATTRIB_POS+1 ; i < VBO_ATTRIB_MAX ; i++) { 447 switch (save->attrsz[i]) { 448 case 4: save->attrptr[i][3] = save->current[i][3]; 449 case 3: save->attrptr[i][2] = save->current[i][2]; 450 case 2: save->attrptr[i][1] = save->current[i][1]; 451 case 1: save->attrptr[i][0] = save->current[i][0]; 452 case 0: break; 453 } 454 } 455} 456 457 458 459 460/* Flush existing data, set new attrib size, replay copied vertices. 461 */ 462static void _save_upgrade_vertex( GLcontext *ctx, 463 GLuint attr, 464 GLuint newsz ) 465{ 466 struct vbo_save_context *save = &vbo_context(ctx)->save; 467 GLuint oldsz; 468 GLuint i; 469 GLfloat *tmp; 470 471 /* Store the current run of vertices, and emit a GL_END. Emit a 472 * BEGIN in the new buffer. 473 */ 474 if (save->vert_count) 475 _save_wrap_buffers( ctx ); 476 else 477 assert( save->copied.nr == 0 ); 478 479 /* Do a COPY_TO_CURRENT to ensure back-copying works for the case 480 * when the attribute already exists in the vertex and is having 481 * its size increased. 482 */ 483 _save_copy_to_current( ctx ); 484 485 /* Fix up sizes: 486 */ 487 oldsz = save->attrsz[attr]; 488 save->attrsz[attr] = newsz; 489 490 save->vertex_size += newsz - oldsz; 491 save->max_vert = ((VBO_SAVE_BUFFER_SIZE - save->vertex_store->used) / 492 save->vertex_size); 493 save->vert_count = 0; 494 495 /* Recalculate all the attrptr[] values: 496 */ 497 for (i = 0, tmp = save->vertex ; i < VBO_ATTRIB_MAX ; i++) { 498 if (save->attrsz[i]) { 499 save->attrptr[i] = tmp; 500 tmp += save->attrsz[i]; 501 } 502 else 503 save->attrptr[i] = NULL; /* will not be dereferenced. */ 504 } 505 506 /* Copy from current to repopulate the vertex with correct values. 507 */ 508 _save_copy_from_current( ctx ); 509 510 /* Replay stored vertices to translate them to new format here. 511 * 512 * If there are copied vertices and the new (upgraded) attribute 513 * has not been defined before, this list is somewhat degenerate, 514 * and will need fixup at runtime. 515 */ 516 if (save->copied.nr) 517 { 518 GLfloat *data = save->copied.buffer; 519 GLfloat *dest = save->buffer; 520 GLuint j; 521 522 /* Need to note this and fix up at runtime (or loopback): 523 */ 524 if (attr != VBO_ATTRIB_POS && save->currentsz[attr][0] == 0) { 525 assert(oldsz == 0); 526 save->dangling_attr_ref = GL_TRUE; 527 } 528 529 for (i = 0 ; i < save->copied.nr ; i++) { 530 for (j = 0 ; j < VBO_ATTRIB_MAX ; j++) { 531 if (save->attrsz[j]) { 532 if (j == attr) { 533 if (oldsz) { 534 COPY_CLEAN_4V( dest, oldsz, data ); 535 data += oldsz; 536 dest += newsz; 537 } 538 else { 539 COPY_SZ_4V( dest, newsz, save->current[attr] ); 540 dest += newsz; 541 } 542 } 543 else { 544 GLint sz = save->attrsz[j]; 545 COPY_SZ_4V( dest, sz, data ); 546 data += sz; 547 dest += sz; 548 } 549 } 550 } 551 } 552 553 save->buffer_ptr = dest; 554 save->vert_count += save->copied.nr; 555 } 556} 557 558static void save_fixup_vertex( GLcontext *ctx, GLuint attr, GLuint sz ) 559{ 560 struct vbo_save_context *save = &vbo_context(ctx)->save; 561 562 if (sz > save->attrsz[attr]) { 563 /* New size is larger. Need to flush existing vertices and get 564 * an enlarged vertex format. 565 */ 566 _save_upgrade_vertex( ctx, attr, sz ); 567 } 568 else if (sz < save->active_sz[attr]) { 569 static GLfloat id[4] = { 0, 0, 0, 1 }; 570 GLuint i; 571 572 /* New size is equal or smaller - just need to fill in some 573 * zeros. 574 */ 575 for (i = sz ; i <= save->attrsz[attr] ; i++) 576 save->attrptr[attr][i-1] = id[i-1]; 577 } 578 579 save->active_sz[attr] = sz; 580} 581 582static void _save_reset_vertex( GLcontext *ctx ) 583{ 584 struct vbo_save_context *save = &vbo_context(ctx)->save; 585 GLuint i; 586 587 for (i = 0 ; i < VBO_ATTRIB_MAX ; i++) { 588 save->attrsz[i] = 0; 589 save->active_sz[i] = 0; 590 } 591 592 save->vertex_size = 0; 593} 594 595 596 597#define ERROR() _mesa_compile_error( ctx, GL_INVALID_ENUM, __FUNCTION__ ); 598 599 600/* Only one size for each attribute may be active at once. Eg. if 601 * Color3f is installed/active, then Color4f may not be, even if the 602 * vertex actually contains 4 color coordinates. This is because the 603 * 3f version won't otherwise set color[3] to 1.0 -- this is the job 604 * of the chooser function when switching between Color4f and Color3f. 605 */ 606#define ATTR( A, N, V0, V1, V2, V3 ) \ 607do { \ 608 struct vbo_save_context *save = &vbo_context(ctx)->save; \ 609 \ 610 if (save->active_sz[A] != N) \ 611 save_fixup_vertex(ctx, A, N); \ 612 \ 613 { \ 614 GLfloat *dest = save->attrptr[A]; \ 615 if (N>0) dest[0] = V0; \ 616 if (N>1) dest[1] = V1; \ 617 if (N>2) dest[2] = V2; \ 618 if (N>3) dest[3] = V3; \ 619 } \ 620 \ 621 if ((A) == 0) { \ 622 GLuint i; \ 623 \ 624 for (i = 0; i < save->vertex_size; i++) \ 625 save->buffer_ptr[i] = save->vertex[i]; \ 626 \ 627 save->buffer_ptr += save->vertex_size; \ 628 \ 629 if (++save->vert_count >= save->max_vert) \ 630 _save_wrap_filled_vertex( ctx ); \ 631 } \ 632} while (0) 633 634#define TAG(x) _save_##x 635 636#include "vbo_attrib_tmp.h" 637 638 639 640 641/* Cope with EvalCoord/CallList called within a begin/end object: 642 * -- Flush current buffer 643 * -- Fallback to opcodes for the rest of the begin/end object. 644 */ 645#define DO_FALLBACK(ctx) \ 646do { \ 647 struct vbo_save_context *save = &vbo_context(ctx)->save; \ 648 \ 649 if (save->vert_count || save->prim_count) \ 650 _save_compile_vertex_list( ctx ); \ 651 \ 652 _save_copy_to_current( ctx ); \ 653 _save_reset_vertex( ctx ); \ 654 _save_reset_counters( ctx ); \ 655 _mesa_install_save_vtxfmt( ctx, &ctx->ListState.ListVtxfmt ); \ 656 ctx->Driver.SaveNeedFlush = 0; \ 657} while (0) 658 659static void GLAPIENTRY _save_EvalCoord1f( GLfloat u ) 660{ 661 GET_CURRENT_CONTEXT(ctx); 662 DO_FALLBACK(ctx); 663 ctx->Save->EvalCoord1f( u ); 664} 665 666static void GLAPIENTRY _save_EvalCoord1fv( const GLfloat *v ) 667{ 668 GET_CURRENT_CONTEXT(ctx); 669 DO_FALLBACK(ctx); 670 ctx->Save->EvalCoord1fv( v ); 671} 672 673static void GLAPIENTRY _save_EvalCoord2f( GLfloat u, GLfloat v ) 674{ 675 GET_CURRENT_CONTEXT(ctx); 676 DO_FALLBACK(ctx); 677 ctx->Save->EvalCoord2f( u, v ); 678} 679 680static void GLAPIENTRY _save_EvalCoord2fv( const GLfloat *v ) 681{ 682 GET_CURRENT_CONTEXT(ctx); 683 DO_FALLBACK(ctx); 684 ctx->Save->EvalCoord2fv( v ); 685} 686 687static void GLAPIENTRY _save_EvalPoint1( GLint i ) 688{ 689 GET_CURRENT_CONTEXT(ctx); 690 DO_FALLBACK(ctx); 691 ctx->Save->EvalPoint1( i ); 692} 693 694static void GLAPIENTRY _save_EvalPoint2( GLint i, GLint j ) 695{ 696 GET_CURRENT_CONTEXT(ctx); 697 DO_FALLBACK(ctx); 698 ctx->Save->EvalPoint2( i, j ); 699} 700 701static void GLAPIENTRY _save_CallList( GLuint l ) 702{ 703 GET_CURRENT_CONTEXT(ctx); 704 DO_FALLBACK(ctx); 705 ctx->Save->CallList( l ); 706} 707 708static void GLAPIENTRY _save_CallLists( GLsizei n, GLenum type, const GLvoid *v ) 709{ 710 GET_CURRENT_CONTEXT(ctx); 711 DO_FALLBACK(ctx); 712 ctx->Save->CallLists( n, type, v ); 713} 714 715 716 717 718/* This begin is hooked into ... Updating of 719 * ctx->Driver.CurrentSavePrimitive is already taken care of. 720 */ 721GLboolean vbo_save_NotifyBegin( GLcontext *ctx, GLenum mode ) 722{ 723 struct vbo_save_context *save = &vbo_context(ctx)->save; 724 725 GLuint i = save->prim_count++; 726 727 assert(i < save->prim_max); 728 save->prim[i].mode = mode & ~VBO_SAVE_PRIM_WEAK; 729 save->prim[i].begin = 1; 730 save->prim[i].end = 0; 731 save->prim[i].weak = (mode & VBO_SAVE_PRIM_WEAK) ? 1 : 0; 732 save->prim[i].pad = 0; 733 save->prim[i].start = save->vert_count; 734 save->prim[i].count = 0; 735 736 _mesa_install_save_vtxfmt( ctx, &save->vtxfmt ); 737 ctx->Driver.SaveNeedFlush = 1; 738 return GL_TRUE; 739} 740 741 742 743static void GLAPIENTRY _save_End( void ) 744{ 745 GET_CURRENT_CONTEXT( ctx ); 746 struct vbo_save_context *save = &vbo_context(ctx)->save; 747 GLint i = save->prim_count - 1; 748 749 ctx->Driver.CurrentSavePrimitive = PRIM_OUTSIDE_BEGIN_END; 750 save->prim[i].end = 1; 751 save->prim[i].count = (save->vert_count - 752 save->prim[i].start); 753 754 if (i == (GLint) save->prim_max - 1) { 755 _save_compile_vertex_list( ctx ); 756 assert(save->copied.nr == 0); 757 } 758 759 /* Swap out this vertex format while outside begin/end. Any color, 760 * etc. received between here and the next begin will be compiled 761 * as opcodes. 762 */ 763 _mesa_install_save_vtxfmt( ctx, &ctx->ListState.ListVtxfmt ); 764} 765 766 767/* These are all errors as this vtxfmt is only installed inside 768 * begin/end pairs. 769 */ 770static void GLAPIENTRY _save_DrawElements(GLenum mode, GLsizei count, GLenum type, 771 const GLvoid *indices) 772{ 773 GET_CURRENT_CONTEXT(ctx); 774 (void) mode; (void) count; (void) type; (void) indices; 775 _mesa_compile_error( ctx, GL_INVALID_OPERATION, "glDrawElements" ); 776} 777 778 779static void GLAPIENTRY _save_DrawRangeElements(GLenum mode, 780 GLuint start, GLuint end, 781 GLsizei count, GLenum type, 782 const GLvoid *indices) 783{ 784 GET_CURRENT_CONTEXT(ctx); 785 (void) mode; (void) start; (void) end; (void) count; (void) type; (void) indices; 786 _mesa_compile_error( ctx, GL_INVALID_OPERATION, "glDrawRangeElements" ); 787} 788 789static void GLAPIENTRY _save_DrawArrays(GLenum mode, GLint start, GLsizei count) 790{ 791 GET_CURRENT_CONTEXT(ctx); 792 (void) mode; (void) start; (void) count; 793 _mesa_compile_error( ctx, GL_INVALID_OPERATION, "glDrawArrays" ); 794} 795 796static void GLAPIENTRY _save_Rectf( GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2 ) 797{ 798 GET_CURRENT_CONTEXT(ctx); 799 (void) x1; (void) y1; (void) x2; (void) y2; 800 _mesa_compile_error( ctx, GL_INVALID_OPERATION, "glRectf" ); 801} 802 803static void GLAPIENTRY _save_EvalMesh1( GLenum mode, GLint i1, GLint i2 ) 804{ 805 GET_CURRENT_CONTEXT(ctx); 806 (void) mode; (void) i1; (void) i2; 807 _mesa_compile_error( ctx, GL_INVALID_OPERATION, "glEvalMesh1" ); 808} 809 810static void GLAPIENTRY _save_EvalMesh2( GLenum mode, GLint i1, GLint i2, 811 GLint j1, GLint j2 ) 812{ 813 GET_CURRENT_CONTEXT(ctx); 814 (void) mode; (void) i1; (void) i2; (void) j1; (void) j2; 815 _mesa_compile_error( ctx, GL_INVALID_OPERATION, "glEvalMesh2" ); 816} 817 818static void GLAPIENTRY _save_Begin( GLenum mode ) 819{ 820 GET_CURRENT_CONTEXT( ctx ); 821 (void) mode; 822 _mesa_compile_error( ctx, GL_INVALID_OPERATION, "Recursive glBegin" ); 823} 824 825 826/* Unlike the functions above, these are to be hooked into the vtxfmt 827 * maintained in ctx->ListState, active when the list is known or 828 * suspected to be outside any begin/end primitive. 829 */ 830static void GLAPIENTRY _save_OBE_Rectf( GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2 ) 831{ 832 GET_CURRENT_CONTEXT(ctx); 833 vbo_save_NotifyBegin( ctx, GL_QUADS | VBO_SAVE_PRIM_WEAK ); 834 CALL_Vertex2f(GET_DISPATCH(), ( x1, y1 )); 835 CALL_Vertex2f(GET_DISPATCH(), ( x2, y1 )); 836 CALL_Vertex2f(GET_DISPATCH(), ( x2, y2 )); 837 CALL_Vertex2f(GET_DISPATCH(), ( x1, y2 )); 838 CALL_End(GET_DISPATCH(), ()); 839} 840 841 842static void GLAPIENTRY _save_OBE_DrawArrays(GLenum mode, GLint start, GLsizei count) 843{ 844 GET_CURRENT_CONTEXT(ctx); 845 GLint i; 846 847 if (!_mesa_validate_DrawArrays( ctx, mode, start, count )) 848 return; 849 850 _ae_map_vbos( ctx ); 851 852 vbo_save_NotifyBegin( ctx, mode | VBO_SAVE_PRIM_WEAK ); 853 854 for (i = 0; i < count; i++) 855 CALL_ArrayElement(GET_DISPATCH(), (start + i)); 856 CALL_End(GET_DISPATCH(), ()); 857 858 _ae_unmap_vbos( ctx ); 859} 860 861/* Could do better by copying the arrays and element list intact and 862 * then emitting an indexed prim at runtime. 863 */ 864static void GLAPIENTRY _save_OBE_DrawElements(GLenum mode, GLsizei count, GLenum type, 865 const GLvoid *indices) 866{ 867 GET_CURRENT_CONTEXT(ctx); 868 GLint i; 869 870 if (!_mesa_validate_DrawElements( ctx, mode, count, type, indices )) 871 return; 872 873 _ae_map_vbos( ctx ); 874 875 if (ctx->Array.ElementArrayBufferObj->Name) 876 indices = ADD_POINTERS(ctx->Array.ElementArrayBufferObj->Pointer, indices); 877 878 vbo_save_NotifyBegin( ctx, mode | VBO_SAVE_PRIM_WEAK ); 879 880 switch (type) { 881 case GL_UNSIGNED_BYTE: 882 for (i = 0 ; i < count ; i++) 883 CALL_ArrayElement(GET_DISPATCH(), ( ((GLubyte *)indices)[i] )); 884 break; 885 case GL_UNSIGNED_SHORT: 886 for (i = 0 ; i < count ; i++) 887 CALL_ArrayElement(GET_DISPATCH(), ( ((GLushort *)indices)[i] )); 888 break; 889 case GL_UNSIGNED_INT: 890 for (i = 0 ; i < count ; i++) 891 CALL_ArrayElement(GET_DISPATCH(), ( ((GLuint *)indices)[i] )); 892 break; 893 default: 894 _mesa_error( ctx, GL_INVALID_ENUM, "glDrawElements(type)" ); 895 break; 896 } 897 898 CALL_End(GET_DISPATCH(), ()); 899 900 _ae_unmap_vbos( ctx ); 901} 902 903static void GLAPIENTRY _save_OBE_DrawRangeElements(GLenum mode, 904 GLuint start, GLuint end, 905 GLsizei count, GLenum type, 906 const GLvoid *indices) 907{ 908 GET_CURRENT_CONTEXT(ctx); 909 if (_mesa_validate_DrawRangeElements( ctx, mode, 910 start, end, 911 count, type, indices )) 912 _save_OBE_DrawElements( mode, count, type, indices ); 913} 914 915 916 917 918 919static void _save_vtxfmt_init( GLcontext *ctx ) 920{ 921 struct vbo_save_context *save = &vbo_context(ctx)->save; 922 GLvertexformat *vfmt = &save->vtxfmt; 923 924 vfmt->ArrayElement = _ae_loopback_array_elt; /* generic helper */ 925 vfmt->Begin = _save_Begin; 926 vfmt->Color3f = _save_Color3f; 927 vfmt->Color3fv = _save_Color3fv; 928 vfmt->Color4f = _save_Color4f; 929 vfmt->Color4fv = _save_Color4fv; 930 vfmt->EdgeFlag = _save_EdgeFlag; 931 vfmt->End = _save_End; 932 vfmt->FogCoordfEXT = _save_FogCoordfEXT; 933 vfmt->FogCoordfvEXT = _save_FogCoordfvEXT; 934 vfmt->Indexf = _save_Indexf; 935 vfmt->Indexfv = _save_Indexfv; 936 vfmt->Materialfv = _save_Materialfv; 937 vfmt->MultiTexCoord1fARB = _save_MultiTexCoord1f; 938 vfmt->MultiTexCoord1fvARB = _save_MultiTexCoord1fv; 939 vfmt->MultiTexCoord2fARB = _save_MultiTexCoord2f; 940 vfmt->MultiTexCoord2fvARB = _save_MultiTexCoord2fv; 941 vfmt->MultiTexCoord3fARB = _save_MultiTexCoord3f; 942 vfmt->MultiTexCoord3fvARB = _save_MultiTexCoord3fv; 943 vfmt->MultiTexCoord4fARB = _save_MultiTexCoord4f; 944 vfmt->MultiTexCoord4fvARB = _save_MultiTexCoord4fv; 945 vfmt->Normal3f = _save_Normal3f; 946 vfmt->Normal3fv = _save_Normal3fv; 947 vfmt->SecondaryColor3fEXT = _save_SecondaryColor3fEXT; 948 vfmt->SecondaryColor3fvEXT = _save_SecondaryColor3fvEXT; 949 vfmt->TexCoord1f = _save_TexCoord1f; 950 vfmt->TexCoord1fv = _save_TexCoord1fv; 951 vfmt->TexCoord2f = _save_TexCoord2f; 952 vfmt->TexCoord2fv = _save_TexCoord2fv; 953 vfmt->TexCoord3f = _save_TexCoord3f; 954 vfmt->TexCoord3fv = _save_TexCoord3fv; 955 vfmt->TexCoord4f = _save_TexCoord4f; 956 vfmt->TexCoord4fv = _save_TexCoord4fv; 957 vfmt->Vertex2f = _save_Vertex2f; 958 vfmt->Vertex2fv = _save_Vertex2fv; 959 vfmt->Vertex3f = _save_Vertex3f; 960 vfmt->Vertex3fv = _save_Vertex3fv; 961 vfmt->Vertex4f = _save_Vertex4f; 962 vfmt->Vertex4fv = _save_Vertex4fv; 963 vfmt->VertexAttrib1fARB = _save_VertexAttrib1fARB; 964 vfmt->VertexAttrib1fvARB = _save_VertexAttrib1fvARB; 965 vfmt->VertexAttrib2fARB = _save_VertexAttrib2fARB; 966 vfmt->VertexAttrib2fvARB = _save_VertexAttrib2fvARB; 967 vfmt->VertexAttrib3fARB = _save_VertexAttrib3fARB; 968 vfmt->VertexAttrib3fvARB = _save_VertexAttrib3fvARB; 969 vfmt->VertexAttrib4fARB = _save_VertexAttrib4fARB; 970 vfmt->VertexAttrib4fvARB = _save_VertexAttrib4fvARB; 971 972 vfmt->VertexAttrib1fNV = _save_VertexAttrib1fNV; 973 vfmt->VertexAttrib1fvNV = _save_VertexAttrib1fvNV; 974 vfmt->VertexAttrib2fNV = _save_VertexAttrib2fNV; 975 vfmt->VertexAttrib2fvNV = _save_VertexAttrib2fvNV; 976 vfmt->VertexAttrib3fNV = _save_VertexAttrib3fNV; 977 vfmt->VertexAttrib3fvNV = _save_VertexAttrib3fvNV; 978 vfmt->VertexAttrib4fNV = _save_VertexAttrib4fNV; 979 vfmt->VertexAttrib4fvNV = _save_VertexAttrib4fvNV; 980 981 /* This will all require us to fallback to saving the list as opcodes: 982 */ 983 vfmt->CallList = _save_CallList; /* inside begin/end */ 984 vfmt->CallLists = _save_CallLists; /* inside begin/end */ 985 vfmt->EvalCoord1f = _save_EvalCoord1f; 986 vfmt->EvalCoord1fv = _save_EvalCoord1fv; 987 vfmt->EvalCoord2f = _save_EvalCoord2f; 988 vfmt->EvalCoord2fv = _save_EvalCoord2fv; 989 vfmt->EvalPoint1 = _save_EvalPoint1; 990 vfmt->EvalPoint2 = _save_EvalPoint2; 991 992 /* These are all errors as we at least know we are in some sort of 993 * begin/end pair: 994 */ 995 vfmt->EvalMesh1 = _save_EvalMesh1; 996 vfmt->EvalMesh2 = _save_EvalMesh2; 997 vfmt->Begin = _save_Begin; 998 vfmt->Rectf = _save_Rectf; 999 vfmt->DrawArrays = _save_DrawArrays; 1000 vfmt->DrawElements = _save_DrawElements; 1001 vfmt->DrawRangeElements = _save_DrawRangeElements; 1002 1003} 1004 1005 1006void vbo_save_SaveFlushVertices( GLcontext *ctx ) 1007{ 1008 struct vbo_save_context *save = &vbo_context(ctx)->save; 1009 1010 /* Noop when we are actually active: 1011 */ 1012 if (ctx->Driver.CurrentSavePrimitive == PRIM_INSIDE_UNKNOWN_PRIM || 1013 ctx->Driver.CurrentSavePrimitive <= GL_POLYGON) 1014 return; 1015 1016 if (save->vert_count || 1017 save->prim_count) 1018 _save_compile_vertex_list( ctx ); 1019 1020 _save_copy_to_current( ctx ); 1021 _save_reset_vertex( ctx ); 1022 _save_reset_counters( ctx ); 1023 ctx->Driver.SaveNeedFlush = 0; 1024} 1025 1026void vbo_save_NewList( GLcontext *ctx, GLuint list, GLenum mode ) 1027{ 1028 struct vbo_save_context *save = &vbo_context(ctx)->save; 1029 1030 (void) list; (void) mode; 1031 1032 if (!save->prim_store) 1033 save->prim_store = alloc_prim_store( ctx ); 1034 1035 if (!save->vertex_store) 1036 save->vertex_store = alloc_vertex_store( ctx ); 1037 1038 save->buffer_ptr = map_vertex_store( ctx, save->vertex_store ); 1039 1040 _save_reset_vertex( ctx ); 1041 _save_reset_counters( ctx ); 1042 ctx->Driver.SaveNeedFlush = 0; 1043} 1044 1045void vbo_save_EndList( GLcontext *ctx ) 1046{ 1047 struct vbo_save_context *save = &vbo_context(ctx)->save; 1048 1049 /* EndList called inside a (saved) Begin/End pair? 1050 */ 1051 if (ctx->Driver.CurrentSavePrimitive != PRIM_OUTSIDE_BEGIN_END) { 1052 1053 if (save->prim_count > 0) { 1054 GLint i = save->prim_count - 1; 1055 ctx->Driver.CurrentSavePrimitive = PRIM_OUTSIDE_BEGIN_END; 1056 save->prim[i].end = 0; 1057 save->prim[i].count = (save->vert_count - 1058 save->prim[i].start); 1059 } 1060 1061 /* Make sure this vertex list gets replayed by the "loopback" 1062 * mechanism: 1063 */ 1064 save->dangling_attr_ref = 1; 1065 vbo_save_SaveFlushVertices( ctx ); 1066 1067 /* Swap out this vertex format while outside begin/end. Any color, 1068 * etc. received between here and the next begin will be compiled 1069 * as opcodes. 1070 */ 1071 _mesa_install_save_vtxfmt( ctx, &ctx->ListState.ListVtxfmt ); 1072 } 1073 1074 unmap_vertex_store( ctx, save->vertex_store ); 1075 1076 assert(save->vertex_size == 0); 1077} 1078 1079void vbo_save_BeginCallList( GLcontext *ctx, struct mesa_display_list *dlist ) 1080{ 1081 struct vbo_save_context *save = &vbo_context(ctx)->save; 1082 save->replay_flags |= dlist->flags; 1083} 1084 1085void vbo_save_EndCallList( GLcontext *ctx ) 1086{ 1087 struct vbo_save_context *save = &vbo_context(ctx)->save; 1088 1089 if (ctx->ListState.CallDepth == 1) { 1090 /* This is correct: want to keep only the VBO_SAVE_FALLBACK 1091 * flag, if it is set: 1092 */ 1093 save->replay_flags &= VBO_SAVE_FALLBACK; 1094 } 1095} 1096 1097 1098static void vbo_destroy_vertex_list( GLcontext *ctx, void *data ) 1099{ 1100 struct vbo_save_vertex_list *node = (struct vbo_save_vertex_list *)data; 1101 (void) ctx; 1102 1103 if ( --node->vertex_store->refcount == 0 ) 1104 free_vertex_store( ctx, node->vertex_store ); 1105 1106 if ( --node->prim_store->refcount == 0 ) 1107 FREE( node->prim_store ); 1108} 1109 1110 1111static void vbo_print_vertex_list( GLcontext *ctx, void *data ) 1112{ 1113 struct vbo_save_vertex_list *node = (struct vbo_save_vertex_list *)data; 1114 GLuint i; 1115 (void) ctx; 1116 1117 _mesa_debug(NULL, "VBO-VERTEX-LIST, %u vertices %d primitives, %d vertsize\n", 1118 node->count, 1119 node->prim_count, 1120 node->vertex_size); 1121 1122 for (i = 0 ; i < node->prim_count ; i++) { 1123 struct _mesa_prim *prim = &node->prim[i]; 1124 _mesa_debug(NULL, " prim %d: %s%s %d..%d %s %s\n", 1125 i, 1126 _mesa_lookup_enum_by_nr(prim->mode), 1127 prim->weak ? " (weak)" : "", 1128 prim->start, 1129 prim->start + prim->count, 1130 (prim->begin) ? "BEGIN" : "(wrap)", 1131 (prim->end) ? "END" : "(wrap)"); 1132 } 1133} 1134 1135 1136static void _save_current_init( GLcontext *ctx ) 1137{ 1138 struct vbo_save_context *save = &vbo_context(ctx)->save; 1139 GLint i; 1140 1141 for (i = VBO_ATTRIB_POS; i <= VBO_ATTRIB_GENERIC15; i++) { 1142 const GLuint j = i - VBO_ATTRIB_POS; 1143 ASSERT(j < VERT_ATTRIB_MAX); 1144 save->currentsz[i] = &ctx->ListState.ActiveAttribSize[j]; 1145 save->current[i] = ctx->ListState.CurrentAttrib[j]; 1146 } 1147 1148 for (i = VBO_ATTRIB_FIRST_MATERIAL; i <= VBO_ATTRIB_LAST_MATERIAL; i++) { 1149 const GLuint j = i - VBO_ATTRIB_FIRST_MATERIAL; 1150 ASSERT(j < MAT_ATTRIB_MAX); 1151 save->currentsz[i] = &ctx->ListState.ActiveMaterialSize[j]; 1152 save->current[i] = ctx->ListState.CurrentMaterial[j]; 1153 } 1154} 1155 1156/** 1157 * Initialize the display list compiler 1158 */ 1159void vbo_save_api_init( struct vbo_save_context *save ) 1160{ 1161 GLcontext *ctx = save->ctx; 1162 GLuint i; 1163 1164 save->opcode_vertex_list = 1165 _mesa_alloc_opcode( ctx, 1166 sizeof(struct vbo_save_vertex_list), 1167 vbo_save_playback_vertex_list, 1168 vbo_destroy_vertex_list, 1169 vbo_print_vertex_list ); 1170 1171 ctx->Driver.NotifySaveBegin = vbo_save_NotifyBegin; 1172 1173 _save_vtxfmt_init( ctx ); 1174 _save_current_init( ctx ); 1175 1176 /* These will actually get set again when binding/drawing */ 1177 for (i = 0; i < VBO_ATTRIB_MAX; i++) 1178 save->inputs[i] = &save->arrays[i]; 1179 1180 /* Hook our array functions into the outside-begin-end vtxfmt in 1181 * ctx->ListState. 1182 */ 1183 ctx->ListState.ListVtxfmt.Rectf = _save_OBE_Rectf; 1184 ctx->ListState.ListVtxfmt.DrawArrays = _save_OBE_DrawArrays; 1185 ctx->ListState.ListVtxfmt.DrawElements = _save_OBE_DrawElements; 1186 ctx->ListState.ListVtxfmt.DrawRangeElements = _save_OBE_DrawRangeElements; 1187 _mesa_install_save_vtxfmt( ctx, &ctx->ListState.ListVtxfmt ); 1188} 1189 1190