diff --git a/src/mesa/main/context.c b/src/mesa/main/context.c index 561eb4685e3..e30b63a443e 100644 --- a/src/mesa/main/context.c +++ b/src/mesa/main/context.c @@ -81,6 +81,7 @@ #include "imports.h" #include "accum.h" #include "api_exec.h" +#include "api_loopback.h" #include "arrayobj.h" #include "attrib.h" #include "blend.h" @@ -859,6 +860,81 @@ _mesa_alloc_dispatch_table(int size) return table; } +/** + * Creates a minimal dispatch table for use within glBegin()/glEnd(). + * + * This ensures that we generate GL_INVALID_OPERATION errors from most + * functions, since the set of functions that are valid within Begin/End is + * very small. + * + * From the GL 1.0 specification section 2.6.3, "GL Commands within + * Begin/End" + * + * "The only GL commands that are allowed within any Begin/End pairs are + * the commands for specifying vertex coordinates, vertex color, normal + * coordinates, and texture coordinates (Vertex, Color, Index, Normal, + * TexCoord), EvalCoord and EvalPoint commands (see section 5.1), + * commands for specifying lighting material parameters (Material + * commands see section 2.12.2), display list invocation commands + * (CallList and CallLists see section 5.4), and the EdgeFlag + * command. Executing Begin after Begin has already been executed but + * before an End is issued generates the INVALID OPERATION error, as does + * executing End without a previous corresponding Begin. Executing any + * other GL command within Begin/End results in the error INVALID + * OPERATION." + * + * The table entries for specifying vertex attributes are set up by + * install_vtxfmt() and _mesa_loopback_init_api_table(), and End() and dlists + * are set by install_vtxfmt() as well. + */ +static struct _glapi_table * +create_beginend_table(const struct gl_context *ctx) +{ + struct _glapi_table *table; + + table = _mesa_alloc_dispatch_table(_gloffset_COUNT); + if (!table) + return NULL; + + /* Fill in functions which return a value, since they should return some + * specific value even if they emit a GL_INVALID_OPERATION error from them + * being called within glBegin()/glEnd(). + */ +#define COPY_DISPATCH(func) SET_##func(table, GET_##func(ctx->Exec)) + + COPY_DISPATCH(GenLists); + COPY_DISPATCH(IsProgram); + COPY_DISPATCH(IsVertexArray); + COPY_DISPATCH(IsBuffer); + COPY_DISPATCH(IsEnabled); + COPY_DISPATCH(IsEnabledi); + COPY_DISPATCH(IsRenderbuffer); + COPY_DISPATCH(IsFramebuffer); + COPY_DISPATCH(CheckFramebufferStatus); + COPY_DISPATCH(RenderMode); + COPY_DISPATCH(GetString); + COPY_DISPATCH(GetStringi); + COPY_DISPATCH(GetPointerv); + COPY_DISPATCH(IsQuery); + COPY_DISPATCH(IsSampler); + COPY_DISPATCH(IsSync); + COPY_DISPATCH(IsTexture); + COPY_DISPATCH(IsTransformFeedback); + COPY_DISPATCH(DeleteQueries); + COPY_DISPATCH(AreTexturesResident); + COPY_DISPATCH(FenceSync); + COPY_DISPATCH(ClientWaitSync); + COPY_DISPATCH(MapBuffer); + COPY_DISPATCH(UnmapBuffer); + COPY_DISPATCH(MapBufferRange); + COPY_DISPATCH(MapBufferRange); + COPY_DISPATCH(ObjectPurgeableAPPLE); + COPY_DISPATCH(ObjectUnpurgeableAPPLE); + + _mesa_loopback_init_api_table(ctx, table); + + return table; +} /** * Initialize a struct gl_context struct (rendering context). @@ -933,19 +1009,15 @@ _mesa_initialize_context(struct gl_context *ctx, _mesa_reference_shared_state(ctx, &ctx->Shared, shared); - if (!init_attrib_groups( ctx )) { - _mesa_reference_shared_state(ctx, &ctx->Shared, NULL); - return GL_FALSE; - } + if (!init_attrib_groups( ctx )) + goto fail; /* setup the API dispatch tables with all nop functions */ - ctx->Exec = _mesa_alloc_dispatch_table(_gloffset_COUNT); - - if (!ctx->Exec) { - _mesa_reference_shared_state(ctx, &ctx->Shared, NULL); - return GL_FALSE; - } - ctx->CurrentDispatch = ctx->Exec; + ctx->OutsideBeginEnd = _mesa_alloc_dispatch_table(_gloffset_COUNT); + if (!ctx->OutsideBeginEnd) + goto fail; + ctx->Exec = ctx->OutsideBeginEnd; + ctx->CurrentDispatch = ctx->OutsideBeginEnd; ctx->FragmentProgram._MaintainTexEnvProgram = (_mesa_getenv("MESA_TEX_PROG") != NULL); @@ -967,12 +1039,10 @@ _mesa_initialize_context(struct gl_context *ctx, switch (ctx->API) { case API_OPENGL_COMPAT: + ctx->BeginEnd = create_beginend_table(ctx); ctx->Save = _mesa_create_save_table(ctx); - if (!ctx->Save) { - _mesa_reference_shared_state(ctx, &ctx->Shared, NULL); - free(ctx->Exec); - return GL_FALSE; - } + if (!ctx->BeginEnd || !ctx->Save) + goto fail; /* fall-through */ case API_OPENGL_CORE: @@ -1002,6 +1072,12 @@ _mesa_initialize_context(struct gl_context *ctx, ctx->FirstTimeCurrent = GL_TRUE; return GL_TRUE; + +fail: + free(ctx->BeginEnd); + free(ctx->Exec); + free(ctx->Save); + return GL_FALSE; } diff --git a/src/mesa/main/mtypes.h b/src/mesa/main/mtypes.h index cba1e1681a5..ead75d50eb5 100644 --- a/src/mesa/main/mtypes.h +++ b/src/mesa/main/mtypes.h @@ -3440,9 +3440,28 @@ struct gl_context /** \name API function pointer tables */ /*@{*/ gl_api API; - struct _glapi_table *Save; /**< Display list save functions */ - struct _glapi_table *Exec; /**< Execute functions */ - struct _glapi_table *CurrentDispatch; /**< == Save or Exec !! */ + /** + * The current dispatch table for non-displaylist-saving execution, either + * BeginEnd or OutsideBeginEnd + */ + struct _glapi_table *Exec; + /** + * The normal dispatch table for non-displaylist-saving, non-begin/end + */ + struct _glapi_table *OutsideBeginEnd; + /** The dispatch table used between glNewList() and glEndList() */ + struct _glapi_table *Save; + /** + * The dispatch table used between glBegin() and glEnd() (outside of a + * display list). Only valid functions between those two are set, which is + * mostly just the set in a GLvertexformat struct. + */ + struct _glapi_table *BeginEnd; + /** + * Tracks the current dispatch table out of the 3 above, so that it can be + * re-set on glXMakeCurrent(). + */ + struct _glapi_table *CurrentDispatch; /*@}*/ struct gl_config Visual; diff --git a/src/mesa/main/vtxfmt.c b/src/mesa/main/vtxfmt.c index 6d687de606b..347d07d5727 100644 --- a/src/mesa/main/vtxfmt.c +++ b/src/mesa/main/vtxfmt.c @@ -252,6 +252,8 @@ void _mesa_install_exec_vtxfmt(struct gl_context *ctx, const GLvertexformat *vfmt) { install_vtxfmt( ctx, ctx->Exec, vfmt ); + if (ctx->BeginEnd) + install_vtxfmt( ctx, ctx->BeginEnd, vfmt ); } diff --git a/src/mesa/vbo/vbo_exec_api.c b/src/mesa/vbo/vbo_exec_api.c index 84bcdd63bf3..f9874396658 100644 --- a/src/mesa/vbo/vbo_exec_api.c +++ b/src/mesa/vbo/vbo_exec_api.c @@ -833,6 +833,17 @@ static void GLAPIENTRY vbo_exec_Begin( GLenum mode ) exec->vtx.prim[i].base_instance = 0; ctx->Driver.CurrentExecPrimitive = mode; + + ctx->Exec = ctx->BeginEnd; + /* We may have been called from a display list, in which case we should + * leave dlist.c's dispatch table in place. + */ + if (ctx->CurrentDispatch == ctx->OutsideBeginEnd) { + ctx->CurrentDispatch = ctx->BeginEnd; + _glapi_set_dispatch(ctx->CurrentDispatch); + } else { + assert(ctx->CurrentDispatch == ctx->Save); + } } @@ -849,6 +860,12 @@ static void GLAPIENTRY vbo_exec_End( void ) return; } + ctx->Exec = ctx->OutsideBeginEnd; + if (ctx->CurrentDispatch == ctx->BeginEnd) { + ctx->CurrentDispatch = ctx->OutsideBeginEnd; + _glapi_set_dispatch(ctx->CurrentDispatch); + } + if (exec->vtx.prim_count > 0) { /* close off current primitive */ int idx = exec->vtx.vert_count;