| /************************************************************************** |
| |
| Copyright 2002-2008 Tungsten Graphics Inc., Cedar Park, Texas. |
| |
| All Rights Reserved. |
| |
| Permission is hereby granted, free of charge, to any person obtaining a |
| copy of this software and associated documentation files (the "Software"), |
| to deal in the Software without restriction, including without limitation |
| on the rights to use, copy, modify, merge, publish, distribute, sub |
| license, and/or sell copies of the Software, and to permit persons to whom |
| the Software is furnished to do so, subject to the following conditions: |
| |
| The above copyright notice and this permission notice (including the next |
| paragraph) shall be included in all copies or substantial portions of the |
| Software. |
| |
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL |
| TUNGSTEN GRAPHICS AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, |
| DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR |
| OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE |
| USE OR OTHER DEALINGS IN THE SOFTWARE. |
| |
| **************************************************************************/ |
| |
| /* |
| * Authors: |
| * Keith Whitwell <keith@tungstengraphics.com> |
| */ |
| |
| |
| |
| /* Display list compiler attempts to store lists of vertices with the |
| * same vertex layout. Additionally it attempts to minimize the need |
| * for execute-time fixup of these vertex lists, allowing them to be |
| * cached on hardware. |
| * |
| * There are still some circumstances where this can be thwarted, for |
| * example by building a list that consists of one very long primitive |
| * (eg Begin(Triangles), 1000 vertices, End), and calling that list |
| * from inside a different begin/end object (Begin(Lines), CallList, |
| * End). |
| * |
| * In that case the code will have to replay the list as individual |
| * commands through the Exec dispatch table, or fix up the copied |
| * vertices at execute-time. |
| * |
| * The other case where fixup is required is when a vertex attribute |
| * is introduced in the middle of a primitive. Eg: |
| * Begin(Lines) |
| * TexCoord1f() Vertex2f() |
| * TexCoord1f() Color3f() Vertex2f() |
| * End() |
| * |
| * If the current value of Color isn't known at compile-time, this |
| * primitive will require fixup. |
| * |
| * |
| * The list compiler currently doesn't attempt to compile lists |
| * containing EvalCoord or EvalPoint commands. On encountering one of |
| * these, compilation falls back to opcodes. |
| * |
| * This could be improved to fallback only when a mix of EvalCoord and |
| * Vertex commands are issued within a single primitive. |
| */ |
| |
| |
| #include "main/glheader.h" |
| #include "main/bufferobj.h" |
| #include "main/context.h" |
| #include "main/dlist.h" |
| #include "main/enums.h" |
| #include "main/macros.h" |
| #include "main/api_validate.h" |
| #include "main/api_arrayelt.h" |
| #include "main/vtxfmt.h" |
| #include "glapi/dispatch.h" |
| |
| #include "vbo_context.h" |
| |
| |
| #ifdef ERROR |
| #undef ERROR |
| #endif |
| |
| |
| /* An interesting VBO number/name to help with debugging */ |
| #define VBO_BUF_ID 12345 |
| |
| |
| /* |
| * NOTE: Old 'parity' issue is gone, but copying can still be |
| * wrong-footed on replay. |
| */ |
| static GLuint _save_copy_vertices( GLcontext *ctx, |
| const struct vbo_save_vertex_list *node, |
| const GLfloat *src_buffer) |
| { |
| struct vbo_save_context *save = &vbo_context( ctx )->save; |
| const struct _mesa_prim *prim = &node->prim[node->prim_count-1]; |
| GLuint nr = prim->count; |
| GLuint sz = save->vertex_size; |
| const GLfloat *src = src_buffer + prim->start * sz; |
| GLfloat *dst = save->copied.buffer; |
| GLuint ovf, i; |
| |
| if (prim->end) |
| return 0; |
| |
| switch( prim->mode ) |
| { |
| case GL_POINTS: |
| return 0; |
| case GL_LINES: |
| ovf = nr&1; |
| for (i = 0 ; i < ovf ; i++) |
| _mesa_memcpy( dst+i*sz, src+(nr-ovf+i)*sz, sz*sizeof(GLfloat) ); |
| return i; |
| case GL_TRIANGLES: |
| ovf = nr%3; |
| for (i = 0 ; i < ovf ; i++) |
| _mesa_memcpy( dst+i*sz, src+(nr-ovf+i)*sz, sz*sizeof(GLfloat) ); |
| return i; |
| case GL_QUADS: |
| ovf = nr&3; |
| for (i = 0 ; i < ovf ; i++) |
| _mesa_memcpy( dst+i*sz, src+(nr-ovf+i)*sz, sz*sizeof(GLfloat) ); |
| return i; |
| case GL_LINE_STRIP: |
| if (nr == 0) |
| return 0; |
| else { |
| _mesa_memcpy( dst, src+(nr-1)*sz, sz*sizeof(GLfloat) ); |
| return 1; |
| } |
| case GL_LINE_LOOP: |
| case GL_TRIANGLE_FAN: |
| case GL_POLYGON: |
| if (nr == 0) |
| return 0; |
| else if (nr == 1) { |
| _mesa_memcpy( dst, src+0, sz*sizeof(GLfloat) ); |
| return 1; |
| } else { |
| _mesa_memcpy( dst, src+0, sz*sizeof(GLfloat) ); |
| _mesa_memcpy( dst+sz, src+(nr-1)*sz, sz*sizeof(GLfloat) ); |
| return 2; |
| } |
| case GL_TRIANGLE_STRIP: |
| case GL_QUAD_STRIP: |
| switch (nr) { |
| case 0: ovf = 0; break; |
| case 1: ovf = 1; break; |
| default: ovf = 2 + (nr&1); break; |
| } |
| for (i = 0 ; i < ovf ; i++) |
| _mesa_memcpy( dst+i*sz, src+(nr-ovf+i)*sz, sz*sizeof(GLfloat) ); |
| return i; |
| default: |
| assert(0); |
| return 0; |
| } |
| } |
| |
| |
| static struct vbo_save_vertex_store *alloc_vertex_store( GLcontext *ctx ) |
| { |
| struct vbo_save_vertex_store *vertex_store = CALLOC_STRUCT(vbo_save_vertex_store); |
| |
| /* obj->Name needs to be non-zero, but won't ever be examined more |
| * closely than that. In particular these buffers won't be entered |
| * into the hash and can never be confused with ones visible to the |
| * user. Perhaps there could be a special number for internal |
| * buffers: |
| */ |
| vertex_store->bufferobj = ctx->Driver.NewBufferObject(ctx, |
| VBO_BUF_ID, |
| GL_ARRAY_BUFFER_ARB); |
| |
| ctx->Driver.BufferData( ctx, |
| GL_ARRAY_BUFFER_ARB, |
| VBO_SAVE_BUFFER_SIZE * sizeof(GLfloat), |
| NULL, |
| GL_STATIC_DRAW_ARB, |
| vertex_store->bufferobj); |
| |
| vertex_store->buffer = NULL; |
| vertex_store->used = 0; |
| vertex_store->refcount = 1; |
| |
| return vertex_store; |
| } |
| |
| static void free_vertex_store( GLcontext *ctx, struct vbo_save_vertex_store *vertex_store ) |
| { |
| assert(!vertex_store->buffer); |
| |
| if (vertex_store->bufferobj) { |
| _mesa_reference_buffer_object(ctx, &vertex_store->bufferobj, NULL); |
| } |
| |
| FREE( vertex_store ); |
| } |
| |
| static GLfloat *map_vertex_store( GLcontext *ctx, struct vbo_save_vertex_store *vertex_store ) |
| { |
| assert(vertex_store->bufferobj); |
| assert(!vertex_store->buffer); |
| vertex_store->buffer = (GLfloat *)ctx->Driver.MapBuffer(ctx, |
| GL_ARRAY_BUFFER_ARB, /* not used */ |
| GL_WRITE_ONLY, /* not used */ |
| vertex_store->bufferobj); |
| |
| assert(vertex_store->buffer); |
| return vertex_store->buffer + vertex_store->used; |
| } |
| |
| static void unmap_vertex_store( GLcontext *ctx, struct vbo_save_vertex_store *vertex_store ) |
| { |
| ctx->Driver.UnmapBuffer( ctx, GL_ARRAY_BUFFER_ARB, vertex_store->bufferobj ); |
| vertex_store->buffer = NULL; |
| } |
| |
| |
| static struct vbo_save_primitive_store *alloc_prim_store( GLcontext *ctx ) |
| { |
| struct vbo_save_primitive_store *store = CALLOC_STRUCT(vbo_save_primitive_store); |
| (void) ctx; |
| store->used = 0; |
| store->refcount = 1; |
| return store; |
| } |
| |
| static void _save_reset_counters( GLcontext *ctx ) |
| { |
| struct vbo_save_context *save = &vbo_context(ctx)->save; |
| |
| save->prim = save->prim_store->buffer + save->prim_store->used; |
| save->buffer = (save->vertex_store->buffer + |
| save->vertex_store->used); |
| |
| assert(save->buffer == save->vbptr); |
| |
| if (save->vertex_size) |
| save->max_vert = ((VBO_SAVE_BUFFER_SIZE - save->vertex_store->used) / |
| save->vertex_size); |
| else |
| save->max_vert = 0; |
| |
| save->vert_count = 0; |
| save->prim_count = 0; |
| save->prim_max = VBO_SAVE_PRIM_SIZE - save->prim_store->used; |
| save->dangling_attr_ref = 0; |
| } |
| |
| |
| /* Insert the active immediate struct onto the display list currently |
| * being built. |
| */ |
| static void _save_compile_vertex_list( GLcontext *ctx ) |
| { |
| struct vbo_save_context *save = &vbo_context(ctx)->save; |
| struct vbo_save_vertex_list *node; |
| |
| /* Allocate space for this structure in the display list currently |
| * being compiled. |
| */ |
| node = (struct vbo_save_vertex_list *) |
| _mesa_alloc_instruction(ctx, save->opcode_vertex_list, sizeof(*node)); |
| |
| if (!node) |
| return; |
| |
| /* Duplicate our template, increment refcounts to the storage structs: |
| */ |
| _mesa_memcpy(node->attrsz, save->attrsz, sizeof(node->attrsz)); |
| node->vertex_size = save->vertex_size; |
| node->buffer_offset = (save->buffer - save->vertex_store->buffer) * sizeof(GLfloat); |
| node->count = save->vert_count; |
| node->wrap_count = save->copied.nr; |
| node->dangling_attr_ref = save->dangling_attr_ref; |
| node->prim = save->prim; |
| node->prim_count = save->prim_count; |
| node->vertex_store = save->vertex_store; |
| node->prim_store = save->prim_store; |
| |
| node->vertex_store->refcount++; |
| node->prim_store->refcount++; |
| |
| assert(node->attrsz[VBO_ATTRIB_POS] != 0 || |
| node->count == 0); |
| |
| if (save->dangling_attr_ref) |
| ctx->ListState.CurrentList->flags |= MESA_DLIST_DANGLING_REFS; |
| |
| save->vertex_store->used += save->vertex_size * node->count; |
| save->prim_store->used += node->prim_count; |
| |
| |
| /* Copy duplicated vertices |
| */ |
| save->copied.nr = _save_copy_vertices( ctx, node, save->buffer ); |
| |
| |
| /* Deal with GL_COMPILE_AND_EXECUTE: |
| */ |
| if (ctx->ExecuteFlag) { |
| struct _glapi_table *dispatch = GET_DISPATCH(); |
| |
| _glapi_set_dispatch(ctx->Exec); |
| |
| vbo_loopback_vertex_list( ctx, |
| (const GLfloat *)((const char *)save->vertex_store->buffer + |
| node->buffer_offset), |
| node->attrsz, |
| node->prim, |
| node->prim_count, |
| node->wrap_count, |
| node->vertex_size); |
| |
| _glapi_set_dispatch(dispatch); |
| } |
| |
| |
| /* Decide whether the storage structs are full, or can be used for |
| * the next vertex lists as well. |
| */ |
| if (save->vertex_store->used > |
| VBO_SAVE_BUFFER_SIZE - 16 * (save->vertex_size + 4)) { |
| |
| /* Unmap old store: |
| */ |
| unmap_vertex_store( ctx, save->vertex_store ); |
| |
| /* Release old reference: |
| */ |
| save->vertex_store->refcount--; |
| assert(save->vertex_store->refcount != 0); |
| save->vertex_store = NULL; |
| |
| /* Allocate and map new store: |
| */ |
| save->vertex_store = alloc_vertex_store( ctx ); |
| save->vbptr = map_vertex_store( ctx, save->vertex_store ); |
| } |
| |
| if (save->prim_store->used > VBO_SAVE_PRIM_SIZE - 6) { |
| save->prim_store->refcount--; |
| assert(save->prim_store->refcount != 0); |
| save->prim_store = alloc_prim_store( ctx ); |
| } |
| |
| /* Reset our structures for the next run of vertices: |
| */ |
| _save_reset_counters( ctx ); |
| } |
| |
| |
| /* TODO -- If no new vertices have been stored, don't bother saving |
| * it. |
| */ |
| static void _save_wrap_buffers( GLcontext *ctx ) |
| { |
| struct vbo_save_context *save = &vbo_context(ctx)->save; |
| GLint i = save->prim_count - 1; |
| GLenum mode; |
| GLboolean weak; |
| |
| assert(i < (GLint) save->prim_max); |
| assert(i >= 0); |
| |
| /* Close off in-progress primitive. |
| */ |
| save->prim[i].count = (save->vert_count - |
| save->prim[i].start); |
| mode = save->prim[i].mode; |
| weak = save->prim[i].weak; |
| |
| /* store the copied vertices, and allocate a new list. |
| */ |
| _save_compile_vertex_list( ctx ); |
| |
| /* Restart interrupted primitive |
| */ |
| save->prim[0].mode = mode; |
| save->prim[0].weak = weak; |
| save->prim[0].begin = 0; |
| save->prim[0].end = 0; |
| save->prim[0].pad = 0; |
| save->prim[0].start = 0; |
| save->prim[0].count = 0; |
| save->prim_count = 1; |
| } |
| |
| |
| |
| /* Called only when buffers are wrapped as the result of filling the |
| * vertex_store struct. |
| */ |
| static void _save_wrap_filled_vertex( GLcontext *ctx ) |
| { |
| struct vbo_save_context *save = &vbo_context(ctx)->save; |
| GLfloat *data = save->copied.buffer; |
| GLuint i; |
| |
| /* Emit a glEnd to close off the last vertex list. |
| */ |
| _save_wrap_buffers( ctx ); |
| |
| /* Copy stored stored vertices to start of new list. |
| */ |
| assert(save->max_vert - save->vert_count > save->copied.nr); |
| |
| for (i = 0 ; i < save->copied.nr ; i++) { |
| _mesa_memcpy( save->vbptr, data, save->vertex_size * sizeof(GLfloat)); |
| data += save->vertex_size; |
| save->vbptr += save->vertex_size; |
| save->vert_count++; |
| } |
| } |
| |
| |
| static void _save_copy_to_current( GLcontext *ctx ) |
| { |
| struct vbo_save_context *save = &vbo_context(ctx)->save; |
| GLuint i; |
| |
| for (i = VBO_ATTRIB_POS+1 ; i < VBO_ATTRIB_MAX ; i++) { |
| if (save->attrsz[i]) { |
| save->currentsz[i][0] = save->attrsz[i]; |
| COPY_CLEAN_4V(save->current[i], |
| save->attrsz[i], |
| save->attrptr[i]); |
| } |
| } |
| } |
| |
| |
| static void _save_copy_from_current( GLcontext *ctx ) |
| { |
| struct vbo_save_context *save = &vbo_context(ctx)->save; |
| GLint i; |
| |
| for (i = VBO_ATTRIB_POS+1 ; i < VBO_ATTRIB_MAX ; i++) { |
| switch (save->attrsz[i]) { |
| case 4: save->attrptr[i][3] = save->current[i][3]; |
| case 3: save->attrptr[i][2] = save->current[i][2]; |
| case 2: save->attrptr[i][1] = save->current[i][1]; |
| case 1: save->attrptr[i][0] = save->current[i][0]; |
| case 0: break; |
| } |
| } |
| } |
| |
| |
| |
| |
| /* Flush existing data, set new attrib size, replay copied vertices. |
| */ |
| static void _save_upgrade_vertex( GLcontext *ctx, |
| GLuint attr, |
| GLuint newsz ) |
| { |
| struct vbo_save_context *save = &vbo_context(ctx)->save; |
| GLuint oldsz; |
| GLuint i; |
| GLfloat *tmp; |
| |
| /* Store the current run of vertices, and emit a GL_END. Emit a |
| * BEGIN in the new buffer. |
| */ |
| if (save->vert_count) |
| _save_wrap_buffers( ctx ); |
| else |
| assert( save->copied.nr == 0 ); |
| |
| /* Do a COPY_TO_CURRENT to ensure back-copying works for the case |
| * when the attribute already exists in the vertex and is having |
| * its size increased. |
| */ |
| _save_copy_to_current( ctx ); |
| |
| /* Fix up sizes: |
| */ |
| oldsz = save->attrsz[attr]; |
| save->attrsz[attr] = newsz; |
| |
| save->vertex_size += newsz - oldsz; |
| save->max_vert = ((VBO_SAVE_BUFFER_SIZE - save->vertex_store->used) / |
| save->vertex_size); |
| save->vert_count = 0; |
| |
| /* Recalculate all the attrptr[] values: |
| */ |
| for (i = 0, tmp = save->vertex ; i < VBO_ATTRIB_MAX ; i++) { |
| if (save->attrsz[i]) { |
| save->attrptr[i] = tmp; |
| tmp += save->attrsz[i]; |
| } |
| else |
| save->attrptr[i] = NULL; /* will not be dereferenced. */ |
| } |
| |
| /* Copy from current to repopulate the vertex with correct values. |
| */ |
| _save_copy_from_current( ctx ); |
| |
| /* Replay stored vertices to translate them to new format here. |
| * |
| * If there are copied vertices and the new (upgraded) attribute |
| * has not been defined before, this list is somewhat degenerate, |
| * and will need fixup at runtime. |
| */ |
| if (save->copied.nr) |
| { |
| GLfloat *data = save->copied.buffer; |
| GLfloat *dest = save->buffer; |
| GLuint j; |
| |
| /* Need to note this and fix up at runtime (or loopback): |
| */ |
| if (attr != VBO_ATTRIB_POS && save->currentsz[attr][0] == 0) { |
| assert(oldsz == 0); |
| save->dangling_attr_ref = GL_TRUE; |
| } |
| |
| for (i = 0 ; i < save->copied.nr ; i++) { |
| for (j = 0 ; j < VBO_ATTRIB_MAX ; j++) { |
| if (save->attrsz[j]) { |
| if (j == attr) { |
| if (oldsz) { |
| COPY_CLEAN_4V( dest, oldsz, data ); |
| data += oldsz; |
| dest += newsz; |
| } |
| else { |
| COPY_SZ_4V( dest, newsz, save->current[attr] ); |
| dest += newsz; |
| } |
| } |
| else { |
| GLint sz = save->attrsz[j]; |
| COPY_SZ_4V( dest, sz, data ); |
| data += sz; |
| dest += sz; |
| } |
| } |
| } |
| } |
| |
| save->vbptr = dest; |
| save->vert_count += save->copied.nr; |
| } |
| } |
| |
| static void save_fixup_vertex( GLcontext *ctx, GLuint attr, GLuint sz ) |
| { |
| struct vbo_save_context *save = &vbo_context(ctx)->save; |
| |
| if (sz > save->attrsz[attr]) { |
| /* New size is larger. Need to flush existing vertices and get |
| * an enlarged vertex format. |
| */ |
| _save_upgrade_vertex( ctx, attr, sz ); |
| } |
| else if (sz < save->active_sz[attr]) { |
| static GLfloat id[4] = { 0, 0, 0, 1 }; |
| GLuint i; |
| |
| /* New size is equal or smaller - just need to fill in some |
| * zeros. |
| */ |
| for (i = sz ; i <= save->attrsz[attr] ; i++) |
| save->attrptr[attr][i-1] = id[i-1]; |
| } |
| |
| save->active_sz[attr] = sz; |
| } |
| |
| static void _save_reset_vertex( GLcontext *ctx ) |
| { |
| struct vbo_save_context *save = &vbo_context(ctx)->save; |
| GLuint i; |
| |
| for (i = 0 ; i < VBO_ATTRIB_MAX ; i++) { |
| save->attrsz[i] = 0; |
| save->active_sz[i] = 0; |
| } |
| |
| save->vertex_size = 0; |
| } |
| |
| |
| |
| #define ERROR() _mesa_compile_error( ctx, GL_INVALID_ENUM, __FUNCTION__ ); |
| |
| |
| /* Only one size for each attribute may be active at once. Eg. if |
| * Color3f is installed/active, then Color4f may not be, even if the |
| * vertex actually contains 4 color coordinates. This is because the |
| * 3f version won't otherwise set color[3] to 1.0 -- this is the job |
| * of the chooser function when switching between Color4f and Color3f. |
| */ |
| #define ATTR( A, N, V0, V1, V2, V3 ) \ |
| do { \ |
| struct vbo_save_context *save = &vbo_context(ctx)->save; \ |
| \ |
| if (save->active_sz[A] != N) \ |
| save_fixup_vertex(ctx, A, N); \ |
| \ |
| { \ |
| GLfloat *dest = save->attrptr[A]; \ |
| if (N>0) dest[0] = V0; \ |
| if (N>1) dest[1] = V1; \ |
| if (N>2) dest[2] = V2; \ |
| if (N>3) dest[3] = V3; \ |
| } \ |
| \ |
| if ((A) == 0) { \ |
| GLuint i; \ |
| \ |
| for (i = 0; i < save->vertex_size; i++) \ |
| save->vbptr[i] = save->vertex[i]; \ |
| \ |
| save->vbptr += save->vertex_size; \ |
| \ |
| if (++save->vert_count >= save->max_vert) \ |
| _save_wrap_filled_vertex( ctx ); \ |
| } \ |
| } while (0) |
| |
| #define TAG(x) _save_##x |
| |
| #include "vbo_attrib_tmp.h" |
| |
| |
| |
| |
| /* Cope with EvalCoord/CallList called within a begin/end object: |
| * -- Flush current buffer |
| * -- Fallback to opcodes for the rest of the begin/end object. |
| */ |
| #define DO_FALLBACK(ctx) \ |
| do { \ |
| struct vbo_save_context *save = &vbo_context(ctx)->save; \ |
| \ |
| if (save->vert_count || save->prim_count) \ |
| _save_compile_vertex_list( ctx ); \ |
| \ |
| _save_copy_to_current( ctx ); \ |
| _save_reset_vertex( ctx ); \ |
| _save_reset_counters( ctx ); \ |
| _mesa_install_save_vtxfmt( ctx, &ctx->ListState.ListVtxfmt ); \ |
| ctx->Driver.SaveNeedFlush = 0; \ |
| } while (0) |
| |
| static void GLAPIENTRY _save_EvalCoord1f( GLfloat u ) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| DO_FALLBACK(ctx); |
| ctx->Save->EvalCoord1f( u ); |
| } |
| |
| static void GLAPIENTRY _save_EvalCoord1fv( const GLfloat *v ) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| DO_FALLBACK(ctx); |
| ctx->Save->EvalCoord1fv( v ); |
| } |
| |
| static void GLAPIENTRY _save_EvalCoord2f( GLfloat u, GLfloat v ) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| DO_FALLBACK(ctx); |
| ctx->Save->EvalCoord2f( u, v ); |
| } |
| |
| static void GLAPIENTRY _save_EvalCoord2fv( const GLfloat *v ) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| DO_FALLBACK(ctx); |
| ctx->Save->EvalCoord2fv( v ); |
| } |
| |
| static void GLAPIENTRY _save_EvalPoint1( GLint i ) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| DO_FALLBACK(ctx); |
| ctx->Save->EvalPoint1( i ); |
| } |
| |
| static void GLAPIENTRY _save_EvalPoint2( GLint i, GLint j ) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| DO_FALLBACK(ctx); |
| ctx->Save->EvalPoint2( i, j ); |
| } |
| |
| static void GLAPIENTRY _save_CallList( GLuint l ) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| DO_FALLBACK(ctx); |
| ctx->Save->CallList( l ); |
| } |
| |
| static void GLAPIENTRY _save_CallLists( GLsizei n, GLenum type, const GLvoid *v ) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| DO_FALLBACK(ctx); |
| ctx->Save->CallLists( n, type, v ); |
| } |
| |
| |
| |
| |
| /* This begin is hooked into ... Updating of |
| * ctx->Driver.CurrentSavePrimitive is already taken care of. |
| */ |
| GLboolean vbo_save_NotifyBegin( GLcontext *ctx, GLenum mode ) |
| { |
| struct vbo_save_context *save = &vbo_context(ctx)->save; |
| |
| GLuint i = save->prim_count++; |
| |
| assert(i < save->prim_max); |
| save->prim[i].mode = mode & ~VBO_SAVE_PRIM_WEAK; |
| save->prim[i].begin = 1; |
| save->prim[i].end = 0; |
| save->prim[i].weak = (mode & VBO_SAVE_PRIM_WEAK) ? 1 : 0; |
| save->prim[i].pad = 0; |
| save->prim[i].start = save->vert_count; |
| save->prim[i].count = 0; |
| |
| _mesa_install_save_vtxfmt( ctx, &save->vtxfmt ); |
| ctx->Driver.SaveNeedFlush = 1; |
| return GL_TRUE; |
| } |
| |
| |
| |
| static void GLAPIENTRY _save_End( void ) |
| { |
| GET_CURRENT_CONTEXT( ctx ); |
| struct vbo_save_context *save = &vbo_context(ctx)->save; |
| GLint i = save->prim_count - 1; |
| |
| ctx->Driver.CurrentSavePrimitive = PRIM_OUTSIDE_BEGIN_END; |
| save->prim[i].end = 1; |
| save->prim[i].count = (save->vert_count - |
| save->prim[i].start); |
| |
| if (i == (GLint) save->prim_max - 1) { |
| _save_compile_vertex_list( ctx ); |
| assert(save->copied.nr == 0); |
| } |
| |
| /* Swap out this vertex format while outside begin/end. Any color, |
| * etc. received between here and the next begin will be compiled |
| * as opcodes. |
| */ |
| _mesa_install_save_vtxfmt( ctx, &ctx->ListState.ListVtxfmt ); |
| } |
| |
| |
| /* These are all errors as this vtxfmt is only installed inside |
| * begin/end pairs. |
| */ |
| static void GLAPIENTRY _save_DrawElements(GLenum mode, GLsizei count, GLenum type, |
| const GLvoid *indices) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| (void) mode; (void) count; (void) type; (void) indices; |
| _mesa_compile_error( ctx, GL_INVALID_OPERATION, "glDrawElements" ); |
| } |
| |
| |
| static void GLAPIENTRY _save_DrawRangeElements(GLenum mode, |
| GLuint start, GLuint end, |
| GLsizei count, GLenum type, |
| const GLvoid *indices) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| (void) mode; (void) start; (void) end; (void) count; (void) type; (void) indices; |
| _mesa_compile_error( ctx, GL_INVALID_OPERATION, "glDrawRangeElements" ); |
| } |
| |
| static void GLAPIENTRY _save_DrawArrays(GLenum mode, GLint start, GLsizei count) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| (void) mode; (void) start; (void) count; |
| _mesa_compile_error( ctx, GL_INVALID_OPERATION, "glDrawArrays" ); |
| } |
| |
| static void GLAPIENTRY _save_Rectf( GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2 ) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| (void) x1; (void) y1; (void) x2; (void) y2; |
| _mesa_compile_error( ctx, GL_INVALID_OPERATION, "glRectf" ); |
| } |
| |
| static void GLAPIENTRY _save_EvalMesh1( GLenum mode, GLint i1, GLint i2 ) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| (void) mode; (void) i1; (void) i2; |
| _mesa_compile_error( ctx, GL_INVALID_OPERATION, "glEvalMesh1" ); |
| } |
| |
| static void GLAPIENTRY _save_EvalMesh2( GLenum mode, GLint i1, GLint i2, |
| GLint j1, GLint j2 ) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| (void) mode; (void) i1; (void) i2; (void) j1; (void) j2; |
| _mesa_compile_error( ctx, GL_INVALID_OPERATION, "glEvalMesh2" ); |
| } |
| |
| static void GLAPIENTRY _save_Begin( GLenum mode ) |
| { |
| GET_CURRENT_CONTEXT( ctx ); |
| (void) mode; |
| _mesa_compile_error( ctx, GL_INVALID_OPERATION, "Recursive glBegin" ); |
| } |
| |
| |
| /* Unlike the functions above, these are to be hooked into the vtxfmt |
| * maintained in ctx->ListState, active when the list is known or |
| * suspected to be outside any begin/end primitive. |
| */ |
| static void GLAPIENTRY _save_OBE_Rectf( GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2 ) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| vbo_save_NotifyBegin( ctx, GL_QUADS | VBO_SAVE_PRIM_WEAK ); |
| CALL_Vertex2f(GET_DISPATCH(), ( x1, y1 )); |
| CALL_Vertex2f(GET_DISPATCH(), ( x2, y1 )); |
| CALL_Vertex2f(GET_DISPATCH(), ( x2, y2 )); |
| CALL_Vertex2f(GET_DISPATCH(), ( x1, y2 )); |
| CALL_End(GET_DISPATCH(), ()); |
| } |
| |
| |
| static void GLAPIENTRY _save_OBE_DrawArrays(GLenum mode, GLint start, GLsizei count) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| GLint i; |
| |
| if (!_mesa_validate_DrawArrays( ctx, mode, start, count )) |
| return; |
| |
| _ae_map_vbos( ctx ); |
| |
| vbo_save_NotifyBegin( ctx, mode | VBO_SAVE_PRIM_WEAK ); |
| |
| for (i = 0; i < count; i++) |
| CALL_ArrayElement(GET_DISPATCH(), (start + i)); |
| CALL_End(GET_DISPATCH(), ()); |
| |
| _ae_unmap_vbos( ctx ); |
| } |
| |
| /* Could do better by copying the arrays and element list intact and |
| * then emitting an indexed prim at runtime. |
| */ |
| static void GLAPIENTRY _save_OBE_DrawElements(GLenum mode, GLsizei count, GLenum type, |
| const GLvoid *indices) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| GLint i; |
| |
| if (!_mesa_validate_DrawElements( ctx, mode, count, type, indices )) |
| return; |
| |
| _ae_map_vbos( ctx ); |
| |
| if (ctx->Array.ElementArrayBufferObj->Name) |
| indices = ADD_POINTERS(ctx->Array.ElementArrayBufferObj->Pointer, indices); |
| |
| vbo_save_NotifyBegin( ctx, mode | VBO_SAVE_PRIM_WEAK ); |
| |
| switch (type) { |
| case GL_UNSIGNED_BYTE: |
| for (i = 0 ; i < count ; i++) |
| CALL_ArrayElement(GET_DISPATCH(), ( ((GLubyte *)indices)[i] )); |
| break; |
| case GL_UNSIGNED_SHORT: |
| for (i = 0 ; i < count ; i++) |
| CALL_ArrayElement(GET_DISPATCH(), ( ((GLushort *)indices)[i] )); |
| break; |
| case GL_UNSIGNED_INT: |
| for (i = 0 ; i < count ; i++) |
| CALL_ArrayElement(GET_DISPATCH(), ( ((GLuint *)indices)[i] )); |
| break; |
| default: |
| _mesa_error( ctx, GL_INVALID_ENUM, "glDrawElements(type)" ); |
| break; |
| } |
| |
| CALL_End(GET_DISPATCH(), ()); |
| |
| _ae_unmap_vbos( ctx ); |
| } |
| |
| static void GLAPIENTRY _save_OBE_DrawRangeElements(GLenum mode, |
| GLuint start, GLuint end, |
| GLsizei count, GLenum type, |
| const GLvoid *indices) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| if (_mesa_validate_DrawRangeElements( ctx, mode, |
| start, end, |
| count, type, indices )) |
| _save_OBE_DrawElements( mode, count, type, indices ); |
| } |
| |
| |
| |
| |
| |
| static void _save_vtxfmt_init( GLcontext *ctx ) |
| { |
| struct vbo_save_context *save = &vbo_context(ctx)->save; |
| GLvertexformat *vfmt = &save->vtxfmt; |
| |
| vfmt->ArrayElement = _ae_loopback_array_elt; /* generic helper */ |
| vfmt->Begin = _save_Begin; |
| vfmt->Color3f = _save_Color3f; |
| vfmt->Color3fv = _save_Color3fv; |
| vfmt->Color4f = _save_Color4f; |
| vfmt->Color4fv = _save_Color4fv; |
| vfmt->EdgeFlag = _save_EdgeFlag; |
| vfmt->End = _save_End; |
| vfmt->FogCoordfEXT = _save_FogCoordfEXT; |
| vfmt->FogCoordfvEXT = _save_FogCoordfvEXT; |
| vfmt->Indexf = _save_Indexf; |
| vfmt->Indexfv = _save_Indexfv; |
| vfmt->Materialfv = _save_Materialfv; |
| vfmt->MultiTexCoord1fARB = _save_MultiTexCoord1f; |
| vfmt->MultiTexCoord1fvARB = _save_MultiTexCoord1fv; |
| vfmt->MultiTexCoord2fARB = _save_MultiTexCoord2f; |
| vfmt->MultiTexCoord2fvARB = _save_MultiTexCoord2fv; |
| vfmt->MultiTexCoord3fARB = _save_MultiTexCoord3f; |
| vfmt->MultiTexCoord3fvARB = _save_MultiTexCoord3fv; |
| vfmt->MultiTexCoord4fARB = _save_MultiTexCoord4f; |
| vfmt->MultiTexCoord4fvARB = _save_MultiTexCoord4fv; |
| vfmt->Normal3f = _save_Normal3f; |
| vfmt->Normal3fv = _save_Normal3fv; |
| vfmt->SecondaryColor3fEXT = _save_SecondaryColor3fEXT; |
| vfmt->SecondaryColor3fvEXT = _save_SecondaryColor3fvEXT; |
| vfmt->TexCoord1f = _save_TexCoord1f; |
| vfmt->TexCoord1fv = _save_TexCoord1fv; |
| vfmt->TexCoord2f = _save_TexCoord2f; |
| vfmt->TexCoord2fv = _save_TexCoord2fv; |
| vfmt->TexCoord3f = _save_TexCoord3f; |
| vfmt->TexCoord3fv = _save_TexCoord3fv; |
| vfmt->TexCoord4f = _save_TexCoord4f; |
| vfmt->TexCoord4fv = _save_TexCoord4fv; |
| vfmt->Vertex2f = _save_Vertex2f; |
| vfmt->Vertex2fv = _save_Vertex2fv; |
| vfmt->Vertex3f = _save_Vertex3f; |
| vfmt->Vertex3fv = _save_Vertex3fv; |
| vfmt->Vertex4f = _save_Vertex4f; |
| vfmt->Vertex4fv = _save_Vertex4fv; |
| vfmt->VertexAttrib1fARB = _save_VertexAttrib1fARB; |
| vfmt->VertexAttrib1fvARB = _save_VertexAttrib1fvARB; |
| vfmt->VertexAttrib2fARB = _save_VertexAttrib2fARB; |
| vfmt->VertexAttrib2fvARB = _save_VertexAttrib2fvARB; |
| vfmt->VertexAttrib3fARB = _save_VertexAttrib3fARB; |
| vfmt->VertexAttrib3fvARB = _save_VertexAttrib3fvARB; |
| vfmt->VertexAttrib4fARB = _save_VertexAttrib4fARB; |
| vfmt->VertexAttrib4fvARB = _save_VertexAttrib4fvARB; |
| |
| vfmt->VertexAttrib1fNV = _save_VertexAttrib1fNV; |
| vfmt->VertexAttrib1fvNV = _save_VertexAttrib1fvNV; |
| vfmt->VertexAttrib2fNV = _save_VertexAttrib2fNV; |
| vfmt->VertexAttrib2fvNV = _save_VertexAttrib2fvNV; |
| vfmt->VertexAttrib3fNV = _save_VertexAttrib3fNV; |
| vfmt->VertexAttrib3fvNV = _save_VertexAttrib3fvNV; |
| vfmt->VertexAttrib4fNV = _save_VertexAttrib4fNV; |
| vfmt->VertexAttrib4fvNV = _save_VertexAttrib4fvNV; |
| |
| /* This will all require us to fallback to saving the list as opcodes: |
| */ |
| vfmt->CallList = _save_CallList; /* inside begin/end */ |
| vfmt->CallLists = _save_CallLists; /* inside begin/end */ |
| vfmt->EvalCoord1f = _save_EvalCoord1f; |
| vfmt->EvalCoord1fv = _save_EvalCoord1fv; |
| vfmt->EvalCoord2f = _save_EvalCoord2f; |
| vfmt->EvalCoord2fv = _save_EvalCoord2fv; |
| vfmt->EvalPoint1 = _save_EvalPoint1; |
| vfmt->EvalPoint2 = _save_EvalPoint2; |
| |
| /* These are all errors as we at least know we are in some sort of |
| * begin/end pair: |
| */ |
| vfmt->EvalMesh1 = _save_EvalMesh1; |
| vfmt->EvalMesh2 = _save_EvalMesh2; |
| vfmt->Begin = _save_Begin; |
| vfmt->Rectf = _save_Rectf; |
| vfmt->DrawArrays = _save_DrawArrays; |
| vfmt->DrawElements = _save_DrawElements; |
| vfmt->DrawRangeElements = _save_DrawRangeElements; |
| |
| } |
| |
| |
| void vbo_save_SaveFlushVertices( GLcontext *ctx ) |
| { |
| struct vbo_save_context *save = &vbo_context(ctx)->save; |
| |
| /* Noop when we are actually active: |
| */ |
| if (ctx->Driver.CurrentSavePrimitive == PRIM_INSIDE_UNKNOWN_PRIM || |
| ctx->Driver.CurrentSavePrimitive <= GL_POLYGON) |
| return; |
| |
| if (save->vert_count || |
| save->prim_count) |
| _save_compile_vertex_list( ctx ); |
| |
| _save_copy_to_current( ctx ); |
| _save_reset_vertex( ctx ); |
| _save_reset_counters( ctx ); |
| ctx->Driver.SaveNeedFlush = 0; |
| } |
| |
| void vbo_save_NewList( GLcontext *ctx, GLuint list, GLenum mode ) |
| { |
| struct vbo_save_context *save = &vbo_context(ctx)->save; |
| |
| (void) list; (void) mode; |
| |
| if (!save->prim_store) |
| save->prim_store = alloc_prim_store( ctx ); |
| |
| if (!save->vertex_store) |
| save->vertex_store = alloc_vertex_store( ctx ); |
| |
| save->vbptr = map_vertex_store( ctx, save->vertex_store ); |
| |
| _save_reset_vertex( ctx ); |
| _save_reset_counters( ctx ); |
| ctx->Driver.SaveNeedFlush = 0; |
| } |
| |
| void vbo_save_EndList( GLcontext *ctx ) |
| { |
| struct vbo_save_context *save = &vbo_context(ctx)->save; |
| |
| /* EndList called inside a (saved) Begin/End pair? |
| */ |
| if (ctx->Driver.CurrentSavePrimitive != PRIM_OUTSIDE_BEGIN_END) { |
| |
| if (save->prim_count > 0) { |
| GLint i = save->prim_count - 1; |
| ctx->Driver.CurrentSavePrimitive = PRIM_OUTSIDE_BEGIN_END; |
| save->prim[i].end = 0; |
| save->prim[i].count = (save->vert_count - |
| save->prim[i].start); |
| } |
| |
| /* Make sure this vertex list gets replayed by the "loopback" |
| * mechanism: |
| */ |
| save->dangling_attr_ref = 1; |
| vbo_save_SaveFlushVertices( ctx ); |
| |
| /* Swap out this vertex format while outside begin/end. Any color, |
| * etc. received between here and the next begin will be compiled |
| * as opcodes. |
| */ |
| _mesa_install_save_vtxfmt( ctx, &ctx->ListState.ListVtxfmt ); |
| } |
| |
| unmap_vertex_store( ctx, save->vertex_store ); |
| |
| assert(save->vertex_size == 0); |
| } |
| |
| void vbo_save_BeginCallList( GLcontext *ctx, struct mesa_display_list *dlist ) |
| { |
| struct vbo_save_context *save = &vbo_context(ctx)->save; |
| save->replay_flags |= dlist->flags; |
| } |
| |
| void vbo_save_EndCallList( GLcontext *ctx ) |
| { |
| struct vbo_save_context *save = &vbo_context(ctx)->save; |
| |
| if (ctx->ListState.CallDepth == 1) { |
| /* This is correct: want to keep only the VBO_SAVE_FALLBACK |
| * flag, if it is set: |
| */ |
| save->replay_flags &= VBO_SAVE_FALLBACK; |
| } |
| } |
| |
| |
| static void vbo_destroy_vertex_list( GLcontext *ctx, void *data ) |
| { |
| struct vbo_save_vertex_list *node = (struct vbo_save_vertex_list *)data; |
| (void) ctx; |
| |
| if ( --node->vertex_store->refcount == 0 ) |
| free_vertex_store( ctx, node->vertex_store ); |
| |
| if ( --node->prim_store->refcount == 0 ) |
| FREE( node->prim_store ); |
| } |
| |
| |
| static void vbo_print_vertex_list( GLcontext *ctx, void *data ) |
| { |
| struct vbo_save_vertex_list *node = (struct vbo_save_vertex_list *)data; |
| GLuint i; |
| (void) ctx; |
| |
| _mesa_debug(NULL, "VBO-VERTEX-LIST, %u vertices %d primitives, %d vertsize\n", |
| node->count, |
| node->prim_count, |
| node->vertex_size); |
| |
| for (i = 0 ; i < node->prim_count ; i++) { |
| struct _mesa_prim *prim = &node->prim[i]; |
| _mesa_debug(NULL, " prim %d: %s%s %d..%d %s %s\n", |
| i, |
| _mesa_lookup_enum_by_nr(prim->mode), |
| prim->weak ? " (weak)" : "", |
| prim->start, |
| prim->start + prim->count, |
| (prim->begin) ? "BEGIN" : "(wrap)", |
| (prim->end) ? "END" : "(wrap)"); |
| } |
| } |
| |
| |
| static void _save_current_init( GLcontext *ctx ) |
| { |
| struct vbo_save_context *save = &vbo_context(ctx)->save; |
| GLint i; |
| |
| for (i = VBO_ATTRIB_POS; i <= VBO_ATTRIB_GENERIC15; i++) { |
| const GLuint j = i - VBO_ATTRIB_POS; |
| ASSERT(j < VERT_ATTRIB_MAX); |
| save->currentsz[i] = &ctx->ListState.ActiveAttribSize[j]; |
| save->current[i] = ctx->ListState.CurrentAttrib[j]; |
| } |
| |
| for (i = VBO_ATTRIB_FIRST_MATERIAL; i <= VBO_ATTRIB_LAST_MATERIAL; i++) { |
| const GLuint j = i - VBO_ATTRIB_FIRST_MATERIAL; |
| ASSERT(j < MAT_ATTRIB_MAX); |
| save->currentsz[i] = &ctx->ListState.ActiveMaterialSize[j]; |
| save->current[i] = ctx->ListState.CurrentMaterial[j]; |
| } |
| } |
| |
| /** |
| * Initialize the display list compiler |
| */ |
| void vbo_save_api_init( struct vbo_save_context *save ) |
| { |
| GLcontext *ctx = save->ctx; |
| GLuint i; |
| |
| save->opcode_vertex_list = |
| _mesa_alloc_opcode( ctx, |
| sizeof(struct vbo_save_vertex_list), |
| vbo_save_playback_vertex_list, |
| vbo_destroy_vertex_list, |
| vbo_print_vertex_list ); |
| |
| ctx->Driver.NotifySaveBegin = vbo_save_NotifyBegin; |
| |
| _save_vtxfmt_init( ctx ); |
| _save_current_init( ctx ); |
| |
| /* These will actually get set again when binding/drawing */ |
| for (i = 0; i < VBO_ATTRIB_MAX; i++) |
| save->inputs[i] = &save->arrays[i]; |
| |
| /* Hook our array functions into the outside-begin-end vtxfmt in |
| * ctx->ListState. |
| */ |
| ctx->ListState.ListVtxfmt.Rectf = _save_OBE_Rectf; |
| ctx->ListState.ListVtxfmt.DrawArrays = _save_OBE_DrawArrays; |
| ctx->ListState.ListVtxfmt.DrawElements = _save_OBE_DrawElements; |
| ctx->ListState.ListVtxfmt.DrawRangeElements = _save_OBE_DrawRangeElements; |
| _mesa_install_save_vtxfmt( ctx, &ctx->ListState.ListVtxfmt ); |
| } |
| |