From dafebc5bb70303f0b5baf0b087cf4d9a64b5c7f0 Mon Sep 17 00:00:00 2001 From: marha Date: Mon, 12 Sep 2011 11:27:51 +0200 Subject: Synchronised line endinge with release branch --- mesalib/src/mesa/vbo/vbo_save_api.c | 2896 +++++++++++++++++------------------ 1 file changed, 1448 insertions(+), 1448 deletions(-) (limited to 'mesalib/src/mesa/vbo/vbo_save_api.c') diff --git a/mesalib/src/mesa/vbo/vbo_save_api.c b/mesalib/src/mesa/vbo/vbo_save_api.c index e4743edd3..87a52e81f 100644 --- a/mesalib/src/mesa/vbo/vbo_save_api.c +++ b/mesalib/src/mesa/vbo/vbo_save_api.c @@ -1,1448 +1,1448 @@ -/************************************************************************** - -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 - */ - - - -/* 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/eval.h" -#include "main/macros.h" -#include "main/mfeatures.h" -#include "main/api_noop.h" -#include "main/api_validate.h" -#include "main/api_arrayelt.h" -#include "main/vtxfmt.h" -#include "main/dispatch.h" - -#include "vbo_context.h" - - -#if FEATURE_dlist - - -#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(struct gl_context *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++) - 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++) - 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++) - memcpy(dst + i * sz, src + (nr - ovf + i) * sz, - sz * sizeof(GLfloat)); - return i; - case GL_LINE_STRIP: - if (nr == 0) - return 0; - else { - 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) { - memcpy(dst, src + 0, sz * sizeof(GLfloat)); - return 1; - } - else { - memcpy(dst, src + 0, sz * sizeof(GLfloat)); - 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++) - 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(struct gl_context *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(struct gl_context *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(struct gl_context *ctx, - struct vbo_save_vertex_store *vertex_store) -{ - assert(vertex_store->bufferobj); - assert(!vertex_store->buffer); - vertex_store->buffer = - (GLfloat *) ctx->Driver.MapBufferRange(ctx, 0, - vertex_store->bufferobj->Size, - GL_MAP_WRITE_BIT, /* not used */ - vertex_store->bufferobj); - - assert(vertex_store->buffer); - return vertex_store->buffer + vertex_store->used; -} - - -static void -unmap_vertex_store(struct gl_context *ctx, - struct vbo_save_vertex_store *vertex_store) -{ - ctx->Driver.UnmapBuffer(ctx, vertex_store->bufferobj); - vertex_store->buffer = NULL; -} - - -static struct vbo_save_primitive_store * -alloc_prim_store(struct gl_context *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(struct gl_context *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->buffer_ptr); - - 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(struct gl_context *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_dlist_alloc(ctx, save->opcode_vertex_list, sizeof(*node)); - - if (!node) - return; - - /* Duplicate our template, increment refcounts to the storage structs: - */ - 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++; - - if (node->prim[0].no_current_update) { - node->current_size = 0; - node->current_data = NULL; - } - else { - node->current_size = node->vertex_size - node->attrsz[0]; - node->current_data = NULL; - - if (node->current_size) { - /* If the malloc fails, we just pull the data out of the VBO - * later instead. - */ - node->current_data = MALLOC(node->current_size * sizeof(GLfloat)); - if (node->current_data) { - const char *buffer = (const char *) save->vertex_store->buffer; - unsigned attr_offset = node->attrsz[0] * sizeof(GLfloat); - unsigned vertex_offset = 0; - - if (node->count) - vertex_offset = - (node->count - 1) * node->vertex_size * sizeof(GLfloat); - - memcpy(node->current_data, - buffer + node->buffer_offset + vertex_offset + attr_offset, - node->current_size * sizeof(GLfloat)); - } - } - } - - assert(node->attrsz[VBO_ATTRIB_POS] != 0 || node->count == 0); - - if (save->dangling_attr_ref) - ctx->ListState.CurrentList->Flags |= 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->buffer_ptr = 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(struct gl_context *ctx) -{ - struct vbo_save_context *save = &vbo_context(ctx)->save; - GLint i = save->prim_count - 1; - GLenum mode; - GLboolean weak; - GLboolean no_current_update; - - 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; - no_current_update = save->prim[i].no_current_update; - - /* 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].no_current_update = no_current_update; - 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[0].num_instances = 1; - 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(struct gl_context *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++) { - memcpy(save->buffer_ptr, data, save->vertex_size * sizeof(GLfloat)); - data += save->vertex_size; - save->buffer_ptr += save->vertex_size; - save->vert_count++; - } -} - - -static void -_save_copy_to_current(struct gl_context *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(struct gl_context *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(struct gl_context *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->buffer_ptr = dest; - save->vert_count += save->copied.nr; - } -} - - -static void -save_fixup_vertex(struct gl_context *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(struct gl_context *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(err) _mesa_compile_error(ctx, err, __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->buffer_ptr[i] = save->vertex[i]; \ - \ - save->buffer_ptr += 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. - */ -static void -dlist_fallback(struct gl_context *ctx) -{ - struct vbo_save_context *save = &vbo_context(ctx)->save; - - if (save->vert_count || save->prim_count) { - if (save->prim_count > 0) { - /* Close off in-progress primitive. */ - GLint i = save->prim_count - 1; - save->prim[i].count = save->vert_count - save->prim[i].start; - } - - /* Need to replay this display list with loopback, - * unfortunately, otherwise this primitive won't be handled - * properly: - */ - save->dangling_attr_ref = 1; - - _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; -} - - -static void GLAPIENTRY -_save_EvalCoord1f(GLfloat u) -{ - GET_CURRENT_CONTEXT(ctx); - dlist_fallback(ctx); - CALL_EvalCoord1f(ctx->Save, (u)); -} - -static void GLAPIENTRY -_save_EvalCoord1fv(const GLfloat * v) -{ - GET_CURRENT_CONTEXT(ctx); - dlist_fallback(ctx); - CALL_EvalCoord1fv(ctx->Save, (v)); -} - -static void GLAPIENTRY -_save_EvalCoord2f(GLfloat u, GLfloat v) -{ - GET_CURRENT_CONTEXT(ctx); - dlist_fallback(ctx); - CALL_EvalCoord2f(ctx->Save, (u, v)); -} - -static void GLAPIENTRY -_save_EvalCoord2fv(const GLfloat * v) -{ - GET_CURRENT_CONTEXT(ctx); - dlist_fallback(ctx); - CALL_EvalCoord2fv(ctx->Save, (v)); -} - -static void GLAPIENTRY -_save_EvalPoint1(GLint i) -{ - GET_CURRENT_CONTEXT(ctx); - dlist_fallback(ctx); - CALL_EvalPoint1(ctx->Save, (i)); -} - -static void GLAPIENTRY -_save_EvalPoint2(GLint i, GLint j) -{ - GET_CURRENT_CONTEXT(ctx); - dlist_fallback(ctx); - CALL_EvalPoint2(ctx->Save, (i, j)); -} - -static void GLAPIENTRY -_save_CallList(GLuint l) -{ - GET_CURRENT_CONTEXT(ctx); - dlist_fallback(ctx); - CALL_CallList(ctx->Save, (l)); -} - -static void GLAPIENTRY -_save_CallLists(GLsizei n, GLenum type, const GLvoid * v) -{ - GET_CURRENT_CONTEXT(ctx); - dlist_fallback(ctx); - CALL_CallLists(ctx->Save, (n, type, v)); -} - - - -/* This begin is hooked into ... Updating of - * ctx->Driver.CurrentSavePrimitive is already taken care of. - */ -GLboolean -vbo_save_NotifyBegin(struct gl_context *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_MODE_MASK; - save->prim[i].begin = 1; - save->prim[i].end = 0; - save->prim[i].weak = (mode & VBO_SAVE_PRIM_WEAK) ? 1 : 0; - save->prim[i].no_current_update = - (mode & VBO_SAVE_PRIM_NO_CURRENT_UPDATE) ? 1 : 0; - save->prim[i].pad = 0; - save->prim[i].start = save->vert_count; - save->prim[i].count = 0; - save->prim[i].num_instances = 1; - - _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_DrawElementsBaseVertex(GLenum mode, GLsizei count, GLenum type, - const GLvoid * indices, GLint basevertex) -{ - GET_CURRENT_CONTEXT(ctx); - (void) mode; - (void) count; - (void) type; - (void) indices; - (void) basevertex; - _mesa_compile_error(ctx, GL_INVALID_OPERATION, "glDrawElements"); -} - - -static void GLAPIENTRY -_save_DrawRangeElementsBaseVertex(GLenum mode, - GLuint start, - GLuint end, - GLsizei count, - GLenum type, - const GLvoid * indices, GLint basevertex) -{ - GET_CURRENT_CONTEXT(ctx); - (void) mode; - (void) start; - (void) end; - (void) count; - (void) type; - (void) indices; - (void) basevertex; - _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"); -} - - -static void GLAPIENTRY -_save_PrimitiveRestartNV(void) -{ - GLenum curPrim; - GET_CURRENT_CONTEXT(ctx); - - curPrim = ctx->Driver.CurrentSavePrimitive; - - _save_End(); - _save_Begin(curPrim); -} - - -/* 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 - | VBO_SAVE_PRIM_NO_CURRENT_UPDATE)); - - 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, 0)) - return; - - _ae_map_vbos(ctx); - - if (_mesa_is_bufferobj(ctx->Array.ElementArrayBufferObj)) - indices = - ADD_POINTERS(ctx->Array.ElementArrayBufferObj->Pointer, indices); - - vbo_save_NotifyBegin(ctx, (mode | VBO_SAVE_PRIM_WEAK | - VBO_SAVE_PRIM_NO_CURRENT_UPDATE)); - - 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, 0)) { - _save_OBE_DrawElements(mode, count, type, indices); - } -} - - -static void -_save_vtxfmt_init(struct gl_context *ctx) -{ - struct vbo_save_context *save = &vbo_context(ctx)->save; - GLvertexformat *vfmt = &save->vtxfmt; - - _MESA_INIT_ARRAYELT_VTXFMT(vfmt, _ae_); - - 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->PrimitiveRestartNV = _save_PrimitiveRestartNV; - 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; - - /* integer-valued */ - vfmt->VertexAttribI1i = _save_VertexAttribI1i; - vfmt->VertexAttribI2i = _save_VertexAttribI2i; - vfmt->VertexAttribI3i = _save_VertexAttribI3i; - vfmt->VertexAttribI4i = _save_VertexAttribI4i; - vfmt->VertexAttribI2iv = _save_VertexAttribI2iv; - vfmt->VertexAttribI3iv = _save_VertexAttribI3iv; - vfmt->VertexAttribI4iv = _save_VertexAttribI4iv; - - /* unsigned integer-valued */ - vfmt->VertexAttribI1ui = _save_VertexAttribI1ui; - vfmt->VertexAttribI2ui = _save_VertexAttribI2ui; - vfmt->VertexAttribI3ui = _save_VertexAttribI3ui; - vfmt->VertexAttribI4ui = _save_VertexAttribI4ui; - vfmt->VertexAttribI2uiv = _save_VertexAttribI2uiv; - vfmt->VertexAttribI3uiv = _save_VertexAttribI3uiv; - vfmt->VertexAttribI4uiv = _save_VertexAttribI4uiv; - - vfmt->VertexP2ui = _save_VertexP2ui; - vfmt->VertexP3ui = _save_VertexP3ui; - vfmt->VertexP4ui = _save_VertexP4ui; - vfmt->VertexP2uiv = _save_VertexP2uiv; - vfmt->VertexP3uiv = _save_VertexP3uiv; - vfmt->VertexP4uiv = _save_VertexP4uiv; - - vfmt->TexCoordP1ui = _save_TexCoordP1ui; - vfmt->TexCoordP2ui = _save_TexCoordP2ui; - vfmt->TexCoordP3ui = _save_TexCoordP3ui; - vfmt->TexCoordP4ui = _save_TexCoordP4ui; - vfmt->TexCoordP1uiv = _save_TexCoordP1uiv; - vfmt->TexCoordP2uiv = _save_TexCoordP2uiv; - vfmt->TexCoordP3uiv = _save_TexCoordP3uiv; - vfmt->TexCoordP4uiv = _save_TexCoordP4uiv; - - vfmt->MultiTexCoordP1ui = _save_MultiTexCoordP1ui; - vfmt->MultiTexCoordP2ui = _save_MultiTexCoordP2ui; - vfmt->MultiTexCoordP3ui = _save_MultiTexCoordP3ui; - vfmt->MultiTexCoordP4ui = _save_MultiTexCoordP4ui; - vfmt->MultiTexCoordP1uiv = _save_MultiTexCoordP1uiv; - vfmt->MultiTexCoordP2uiv = _save_MultiTexCoordP2uiv; - vfmt->MultiTexCoordP3uiv = _save_MultiTexCoordP3uiv; - vfmt->MultiTexCoordP4uiv = _save_MultiTexCoordP4uiv; - - vfmt->NormalP3ui = _save_NormalP3ui; - vfmt->NormalP3uiv = _save_NormalP3uiv; - - vfmt->ColorP3ui = _save_ColorP3ui; - vfmt->ColorP4ui = _save_ColorP4ui; - vfmt->ColorP3uiv = _save_ColorP3uiv; - vfmt->ColorP4uiv = _save_ColorP4uiv; - - vfmt->SecondaryColorP3ui = _save_SecondaryColorP3ui; - vfmt->SecondaryColorP3uiv = _save_SecondaryColorP3uiv; - - vfmt->VertexAttribP1ui = _save_VertexAttribP1ui; - vfmt->VertexAttribP2ui = _save_VertexAttribP2ui; - vfmt->VertexAttribP3ui = _save_VertexAttribP3ui; - vfmt->VertexAttribP4ui = _save_VertexAttribP4ui; - - vfmt->VertexAttribP1uiv = _save_VertexAttribP1uiv; - vfmt->VertexAttribP2uiv = _save_VertexAttribP2uiv; - vfmt->VertexAttribP3uiv = _save_VertexAttribP3uiv; - vfmt->VertexAttribP4uiv = _save_VertexAttribP4uiv; - - /* This will all require us to fallback to saving the list as opcodes: - */ - _MESA_INIT_DLIST_VTXFMT(vfmt, _save_); /* inside begin/end */ - - _MESA_INIT_EVAL_VTXFMT(vfmt, _save_); - - /* These are all errors as we at least know we are in some sort of - * begin/end pair: - */ - vfmt->Begin = _save_Begin; - vfmt->Rectf = _save_Rectf; - vfmt->DrawArrays = _save_DrawArrays; - vfmt->DrawElements = _save_DrawElements; - vfmt->DrawRangeElements = _save_DrawRangeElements; - vfmt->DrawElementsBaseVertex = _save_DrawElementsBaseVertex; - vfmt->DrawRangeElementsBaseVertex = _save_DrawRangeElementsBaseVertex; - /* Loops back into vfmt->DrawElements */ - vfmt->MultiDrawElementsEXT = _mesa_noop_MultiDrawElements; - vfmt->MultiDrawElementsBaseVertex = _mesa_noop_MultiDrawElementsBaseVertex; -} - - -void -vbo_save_SaveFlushVertices(struct gl_context *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(struct gl_context *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->buffer_ptr = map_vertex_store(ctx, save->vertex_store); - - _save_reset_vertex(ctx); - _save_reset_counters(ctx); - ctx->Driver.SaveNeedFlush = 0; -} - - -void -vbo_save_EndList(struct gl_context *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(struct gl_context *ctx, struct gl_display_list *dlist) -{ - struct vbo_save_context *save = &vbo_context(ctx)->save; - save->replay_flags |= dlist->Flags; -} - - -void -vbo_save_EndCallList(struct gl_context *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(struct gl_context *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); - - if (node->current_data) { - FREE(node->current_data); - node->current_data = NULL; - } -} - - -static void -vbo_print_vertex_list(struct gl_context *ctx, void *data) -{ - struct vbo_save_vertex_list *node = (struct vbo_save_vertex_list *) data; - GLuint i; - (void) ctx; - - printf("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_prim_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(struct gl_context *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) -{ - struct gl_context *ctx = save->ctx; - GLuint i; - - save->opcode_vertex_list = - _mesa_dlist_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; - /* loops back into _save_OBE_DrawElements */ - ctx->ListState.ListVtxfmt.MultiDrawElementsEXT = - _mesa_noop_MultiDrawElements; - ctx->ListState.ListVtxfmt.MultiDrawElementsBaseVertex = - _mesa_noop_MultiDrawElementsBaseVertex; - _mesa_install_save_vtxfmt(ctx, &ctx->ListState.ListVtxfmt); -} - - -#endif /* FEATURE_dlist */ +/************************************************************************** + +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 + */ + + + +/* 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/eval.h" +#include "main/macros.h" +#include "main/mfeatures.h" +#include "main/api_noop.h" +#include "main/api_validate.h" +#include "main/api_arrayelt.h" +#include "main/vtxfmt.h" +#include "main/dispatch.h" + +#include "vbo_context.h" + + +#if FEATURE_dlist + + +#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(struct gl_context *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++) + 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++) + 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++) + memcpy(dst + i * sz, src + (nr - ovf + i) * sz, + sz * sizeof(GLfloat)); + return i; + case GL_LINE_STRIP: + if (nr == 0) + return 0; + else { + 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) { + memcpy(dst, src + 0, sz * sizeof(GLfloat)); + return 1; + } + else { + memcpy(dst, src + 0, sz * sizeof(GLfloat)); + 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++) + 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(struct gl_context *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(struct gl_context *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(struct gl_context *ctx, + struct vbo_save_vertex_store *vertex_store) +{ + assert(vertex_store->bufferobj); + assert(!vertex_store->buffer); + vertex_store->buffer = + (GLfloat *) ctx->Driver.MapBufferRange(ctx, 0, + vertex_store->bufferobj->Size, + GL_MAP_WRITE_BIT, /* not used */ + vertex_store->bufferobj); + + assert(vertex_store->buffer); + return vertex_store->buffer + vertex_store->used; +} + + +static void +unmap_vertex_store(struct gl_context *ctx, + struct vbo_save_vertex_store *vertex_store) +{ + ctx->Driver.UnmapBuffer(ctx, vertex_store->bufferobj); + vertex_store->buffer = NULL; +} + + +static struct vbo_save_primitive_store * +alloc_prim_store(struct gl_context *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(struct gl_context *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->buffer_ptr); + + 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(struct gl_context *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_dlist_alloc(ctx, save->opcode_vertex_list, sizeof(*node)); + + if (!node) + return; + + /* Duplicate our template, increment refcounts to the storage structs: + */ + 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++; + + if (node->prim[0].no_current_update) { + node->current_size = 0; + node->current_data = NULL; + } + else { + node->current_size = node->vertex_size - node->attrsz[0]; + node->current_data = NULL; + + if (node->current_size) { + /* If the malloc fails, we just pull the data out of the VBO + * later instead. + */ + node->current_data = MALLOC(node->current_size * sizeof(GLfloat)); + if (node->current_data) { + const char *buffer = (const char *) save->vertex_store->buffer; + unsigned attr_offset = node->attrsz[0] * sizeof(GLfloat); + unsigned vertex_offset = 0; + + if (node->count) + vertex_offset = + (node->count - 1) * node->vertex_size * sizeof(GLfloat); + + memcpy(node->current_data, + buffer + node->buffer_offset + vertex_offset + attr_offset, + node->current_size * sizeof(GLfloat)); + } + } + } + + assert(node->attrsz[VBO_ATTRIB_POS] != 0 || node->count == 0); + + if (save->dangling_attr_ref) + ctx->ListState.CurrentList->Flags |= 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->buffer_ptr = 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(struct gl_context *ctx) +{ + struct vbo_save_context *save = &vbo_context(ctx)->save; + GLint i = save->prim_count - 1; + GLenum mode; + GLboolean weak; + GLboolean no_current_update; + + 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; + no_current_update = save->prim[i].no_current_update; + + /* 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].no_current_update = no_current_update; + 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[0].num_instances = 1; + 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(struct gl_context *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++) { + memcpy(save->buffer_ptr, data, save->vertex_size * sizeof(GLfloat)); + data += save->vertex_size; + save->buffer_ptr += save->vertex_size; + save->vert_count++; + } +} + + +static void +_save_copy_to_current(struct gl_context *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(struct gl_context *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(struct gl_context *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->buffer_ptr = dest; + save->vert_count += save->copied.nr; + } +} + + +static void +save_fixup_vertex(struct gl_context *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(struct gl_context *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(err) _mesa_compile_error(ctx, err, __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->buffer_ptr[i] = save->vertex[i]; \ + \ + save->buffer_ptr += 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. + */ +static void +dlist_fallback(struct gl_context *ctx) +{ + struct vbo_save_context *save = &vbo_context(ctx)->save; + + if (save->vert_count || save->prim_count) { + if (save->prim_count > 0) { + /* Close off in-progress primitive. */ + GLint i = save->prim_count - 1; + save->prim[i].count = save->vert_count - save->prim[i].start; + } + + /* Need to replay this display list with loopback, + * unfortunately, otherwise this primitive won't be handled + * properly: + */ + save->dangling_attr_ref = 1; + + _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; +} + + +static void GLAPIENTRY +_save_EvalCoord1f(GLfloat u) +{ + GET_CURRENT_CONTEXT(ctx); + dlist_fallback(ctx); + CALL_EvalCoord1f(ctx->Save, (u)); +} + +static void GLAPIENTRY +_save_EvalCoord1fv(const GLfloat * v) +{ + GET_CURRENT_CONTEXT(ctx); + dlist_fallback(ctx); + CALL_EvalCoord1fv(ctx->Save, (v)); +} + +static void GLAPIENTRY +_save_EvalCoord2f(GLfloat u, GLfloat v) +{ + GET_CURRENT_CONTEXT(ctx); + dlist_fallback(ctx); + CALL_EvalCoord2f(ctx->Save, (u, v)); +} + +static void GLAPIENTRY +_save_EvalCoord2fv(const GLfloat * v) +{ + GET_CURRENT_CONTEXT(ctx); + dlist_fallback(ctx); + CALL_EvalCoord2fv(ctx->Save, (v)); +} + +static void GLAPIENTRY +_save_EvalPoint1(GLint i) +{ + GET_CURRENT_CONTEXT(ctx); + dlist_fallback(ctx); + CALL_EvalPoint1(ctx->Save, (i)); +} + +static void GLAPIENTRY +_save_EvalPoint2(GLint i, GLint j) +{ + GET_CURRENT_CONTEXT(ctx); + dlist_fallback(ctx); + CALL_EvalPoint2(ctx->Save, (i, j)); +} + +static void GLAPIENTRY +_save_CallList(GLuint l) +{ + GET_CURRENT_CONTEXT(ctx); + dlist_fallback(ctx); + CALL_CallList(ctx->Save, (l)); +} + +static void GLAPIENTRY +_save_CallLists(GLsizei n, GLenum type, const GLvoid * v) +{ + GET_CURRENT_CONTEXT(ctx); + dlist_fallback(ctx); + CALL_CallLists(ctx->Save, (n, type, v)); +} + + + +/* This begin is hooked into ... Updating of + * ctx->Driver.CurrentSavePrimitive is already taken care of. + */ +GLboolean +vbo_save_NotifyBegin(struct gl_context *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_MODE_MASK; + save->prim[i].begin = 1; + save->prim[i].end = 0; + save->prim[i].weak = (mode & VBO_SAVE_PRIM_WEAK) ? 1 : 0; + save->prim[i].no_current_update = + (mode & VBO_SAVE_PRIM_NO_CURRENT_UPDATE) ? 1 : 0; + save->prim[i].pad = 0; + save->prim[i].start = save->vert_count; + save->prim[i].count = 0; + save->prim[i].num_instances = 1; + + _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_DrawElementsBaseVertex(GLenum mode, GLsizei count, GLenum type, + const GLvoid * indices, GLint basevertex) +{ + GET_CURRENT_CONTEXT(ctx); + (void) mode; + (void) count; + (void) type; + (void) indices; + (void) basevertex; + _mesa_compile_error(ctx, GL_INVALID_OPERATION, "glDrawElements"); +} + + +static void GLAPIENTRY +_save_DrawRangeElementsBaseVertex(GLenum mode, + GLuint start, + GLuint end, + GLsizei count, + GLenum type, + const GLvoid * indices, GLint basevertex) +{ + GET_CURRENT_CONTEXT(ctx); + (void) mode; + (void) start; + (void) end; + (void) count; + (void) type; + (void) indices; + (void) basevertex; + _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"); +} + + +static void GLAPIENTRY +_save_PrimitiveRestartNV(void) +{ + GLenum curPrim; + GET_CURRENT_CONTEXT(ctx); + + curPrim = ctx->Driver.CurrentSavePrimitive; + + _save_End(); + _save_Begin(curPrim); +} + + +/* 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 + | VBO_SAVE_PRIM_NO_CURRENT_UPDATE)); + + 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, 0)) + return; + + _ae_map_vbos(ctx); + + if (_mesa_is_bufferobj(ctx->Array.ElementArrayBufferObj)) + indices = + ADD_POINTERS(ctx->Array.ElementArrayBufferObj->Pointer, indices); + + vbo_save_NotifyBegin(ctx, (mode | VBO_SAVE_PRIM_WEAK | + VBO_SAVE_PRIM_NO_CURRENT_UPDATE)); + + 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, 0)) { + _save_OBE_DrawElements(mode, count, type, indices); + } +} + + +static void +_save_vtxfmt_init(struct gl_context *ctx) +{ + struct vbo_save_context *save = &vbo_context(ctx)->save; + GLvertexformat *vfmt = &save->vtxfmt; + + _MESA_INIT_ARRAYELT_VTXFMT(vfmt, _ae_); + + 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->PrimitiveRestartNV = _save_PrimitiveRestartNV; + 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; + + /* integer-valued */ + vfmt->VertexAttribI1i = _save_VertexAttribI1i; + vfmt->VertexAttribI2i = _save_VertexAttribI2i; + vfmt->VertexAttribI3i = _save_VertexAttribI3i; + vfmt->VertexAttribI4i = _save_VertexAttribI4i; + vfmt->VertexAttribI2iv = _save_VertexAttribI2iv; + vfmt->VertexAttribI3iv = _save_VertexAttribI3iv; + vfmt->VertexAttribI4iv = _save_VertexAttribI4iv; + + /* unsigned integer-valued */ + vfmt->VertexAttribI1ui = _save_VertexAttribI1ui; + vfmt->VertexAttribI2ui = _save_VertexAttribI2ui; + vfmt->VertexAttribI3ui = _save_VertexAttribI3ui; + vfmt->VertexAttribI4ui = _save_VertexAttribI4ui; + vfmt->VertexAttribI2uiv = _save_VertexAttribI2uiv; + vfmt->VertexAttribI3uiv = _save_VertexAttribI3uiv; + vfmt->VertexAttribI4uiv = _save_VertexAttribI4uiv; + + vfmt->VertexP2ui = _save_VertexP2ui; + vfmt->VertexP3ui = _save_VertexP3ui; + vfmt->VertexP4ui = _save_VertexP4ui; + vfmt->VertexP2uiv = _save_VertexP2uiv; + vfmt->VertexP3uiv = _save_VertexP3uiv; + vfmt->VertexP4uiv = _save_VertexP4uiv; + + vfmt->TexCoordP1ui = _save_TexCoordP1ui; + vfmt->TexCoordP2ui = _save_TexCoordP2ui; + vfmt->TexCoordP3ui = _save_TexCoordP3ui; + vfmt->TexCoordP4ui = _save_TexCoordP4ui; + vfmt->TexCoordP1uiv = _save_TexCoordP1uiv; + vfmt->TexCoordP2uiv = _save_TexCoordP2uiv; + vfmt->TexCoordP3uiv = _save_TexCoordP3uiv; + vfmt->TexCoordP4uiv = _save_TexCoordP4uiv; + + vfmt->MultiTexCoordP1ui = _save_MultiTexCoordP1ui; + vfmt->MultiTexCoordP2ui = _save_MultiTexCoordP2ui; + vfmt->MultiTexCoordP3ui = _save_MultiTexCoordP3ui; + vfmt->MultiTexCoordP4ui = _save_MultiTexCoordP4ui; + vfmt->MultiTexCoordP1uiv = _save_MultiTexCoordP1uiv; + vfmt->MultiTexCoordP2uiv = _save_MultiTexCoordP2uiv; + vfmt->MultiTexCoordP3uiv = _save_MultiTexCoordP3uiv; + vfmt->MultiTexCoordP4uiv = _save_MultiTexCoordP4uiv; + + vfmt->NormalP3ui = _save_NormalP3ui; + vfmt->NormalP3uiv = _save_NormalP3uiv; + + vfmt->ColorP3ui = _save_ColorP3ui; + vfmt->ColorP4ui = _save_ColorP4ui; + vfmt->ColorP3uiv = _save_ColorP3uiv; + vfmt->ColorP4uiv = _save_ColorP4uiv; + + vfmt->SecondaryColorP3ui = _save_SecondaryColorP3ui; + vfmt->SecondaryColorP3uiv = _save_SecondaryColorP3uiv; + + vfmt->VertexAttribP1ui = _save_VertexAttribP1ui; + vfmt->VertexAttribP2ui = _save_VertexAttribP2ui; + vfmt->VertexAttribP3ui = _save_VertexAttribP3ui; + vfmt->VertexAttribP4ui = _save_VertexAttribP4ui; + + vfmt->VertexAttribP1uiv = _save_VertexAttribP1uiv; + vfmt->VertexAttribP2uiv = _save_VertexAttribP2uiv; + vfmt->VertexAttribP3uiv = _save_VertexAttribP3uiv; + vfmt->VertexAttribP4uiv = _save_VertexAttribP4uiv; + + /* This will all require us to fallback to saving the list as opcodes: + */ + _MESA_INIT_DLIST_VTXFMT(vfmt, _save_); /* inside begin/end */ + + _MESA_INIT_EVAL_VTXFMT(vfmt, _save_); + + /* These are all errors as we at least know we are in some sort of + * begin/end pair: + */ + vfmt->Begin = _save_Begin; + vfmt->Rectf = _save_Rectf; + vfmt->DrawArrays = _save_DrawArrays; + vfmt->DrawElements = _save_DrawElements; + vfmt->DrawRangeElements = _save_DrawRangeElements; + vfmt->DrawElementsBaseVertex = _save_DrawElementsBaseVertex; + vfmt->DrawRangeElementsBaseVertex = _save_DrawRangeElementsBaseVertex; + /* Loops back into vfmt->DrawElements */ + vfmt->MultiDrawElementsEXT = _mesa_noop_MultiDrawElements; + vfmt->MultiDrawElementsBaseVertex = _mesa_noop_MultiDrawElementsBaseVertex; +} + + +void +vbo_save_SaveFlushVertices(struct gl_context *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(struct gl_context *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->buffer_ptr = map_vertex_store(ctx, save->vertex_store); + + _save_reset_vertex(ctx); + _save_reset_counters(ctx); + ctx->Driver.SaveNeedFlush = 0; +} + + +void +vbo_save_EndList(struct gl_context *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(struct gl_context *ctx, struct gl_display_list *dlist) +{ + struct vbo_save_context *save = &vbo_context(ctx)->save; + save->replay_flags |= dlist->Flags; +} + + +void +vbo_save_EndCallList(struct gl_context *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(struct gl_context *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); + + if (node->current_data) { + FREE(node->current_data); + node->current_data = NULL; + } +} + + +static void +vbo_print_vertex_list(struct gl_context *ctx, void *data) +{ + struct vbo_save_vertex_list *node = (struct vbo_save_vertex_list *) data; + GLuint i; + (void) ctx; + + printf("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_prim_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(struct gl_context *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) +{ + struct gl_context *ctx = save->ctx; + GLuint i; + + save->opcode_vertex_list = + _mesa_dlist_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; + /* loops back into _save_OBE_DrawElements */ + ctx->ListState.ListVtxfmt.MultiDrawElementsEXT = + _mesa_noop_MultiDrawElements; + ctx->ListState.ListVtxfmt.MultiDrawElementsBaseVertex = + _mesa_noop_MultiDrawElementsBaseVertex; + _mesa_install_save_vtxfmt(ctx, &ctx->ListState.ListVtxfmt); +} + + +#endif /* FEATURE_dlist */ -- cgit v1.2.3