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