diff options
-rw-r--r-- | src/egl/main/eglcontext.c | 236 |
1 files changed, 158 insertions, 78 deletions
diff --git a/src/egl/main/eglcontext.c b/src/egl/main/eglcontext.c index ee4b1b59f5..deaa8d3f41 100644 --- a/src/egl/main/eglcontext.c +++ b/src/egl/main/eglcontext.c @@ -140,101 +140,181 @@ _eglQueryContext(_EGLDriver *drv, _EGLDisplay *dpy, _EGLContext *c, /** - * Drivers will typically call this to do the error checking and - * update the various flags. - * Then, the driver will do its device-dependent Make-Current stuff. + * Bind the context to the surface as the draw or read surface. Return the + * previous surface that the context is bound to. + * + * Note that the context may be NULL. */ -EGLBoolean -_eglMakeCurrent(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSurface *draw, - _EGLSurface *read, _EGLContext *ctx) +static _EGLSurface * +_eglBindContextToSurface(_EGLContext *ctx, _EGLSurface *surf, EGLint readdraw) +{ + _EGLSurface **attachment, *oldSurf; + + if (!ctx) { + surf->Binding = NULL; + return NULL; + } + + attachment = (readdraw == EGL_DRAW) ? + &ctx->DrawSurface : &ctx->ReadSurface; + oldSurf = *attachment; + + if (oldSurf == surf) + return NULL; + + if (oldSurf) + oldSurf->Binding = NULL; + surf->Binding = ctx; + *attachment = surf; + + return oldSurf; +} + + +/** + * Bind the context to the thread and return the previous context. + * + * Note that the context may be NULL. + */ +static _EGLContext * +_eglBindContextToThread(_EGLContext *ctx, _EGLThreadInfo *t) { - _EGLThreadInfo *t = _eglGetCurrentThread(); - _EGLContext *oldContext = NULL; - _EGLSurface *oldDrawSurface = NULL; - _EGLSurface *oldReadSurface = NULL; EGLint apiIndex; + _EGLContext *oldCtx; + + apiIndex = (ctx) ? + _eglConvertApiToIndex(ctx->ClientAPI) : t->CurrentAPIIndex; + + oldCtx = t->CurrentContexts[apiIndex]; + if (ctx == oldCtx) + return NULL; + + if (oldCtx) + oldCtx->Binding = NULL; + if (ctx) + ctx->Binding = t; + + t->CurrentContexts[apiIndex] = ctx; + + return oldCtx; +} + + +/** + * Return true if the given context and surfaces can be made current. + */ +static EGLBoolean +_eglCheckMakeCurrent(_EGLContext *ctx, _EGLSurface *draw, _EGLSurface *read) +{ + _EGLThreadInfo *t = _eglGetCurrentThread(); + EGLint conflict_api; if (_eglIsCurrentThreadDummy()) return _eglError(EGL_BAD_ALLOC, "eglMakeCurrent"); - if (ctx) { - /* error checking */ - if (ctx->Binding && ctx->Binding != t) - return _eglError(EGL_BAD_ACCESS, "eglMakeCurrent"); - if (draw == NULL || read == NULL) - return _eglError(EGL_BAD_MATCH, "eglMakeCurrent"); - if (draw->Config != ctx->Config || read->Config != ctx->Config) + /* this is easy */ + if (!ctx) { + if (draw || read) return _eglError(EGL_BAD_MATCH, "eglMakeCurrent"); - if ((draw->Binding && draw->Binding->Binding != t) || - (read->Binding && read->Binding->Binding != t)) - return _eglError(EGL_BAD_ACCESS, "eglMakeCurrent"); + return EGL_TRUE; + } + + /* ctx/draw/read must be all given */ + if (draw == NULL || read == NULL) + return _eglError(EGL_BAD_MATCH, "eglMakeCurrent"); + + /* context stealing from another thread is not allowed */ + if (ctx->Binding && ctx->Binding != t) + return _eglError(EGL_BAD_ACCESS, "eglMakeCurrent"); + + /* + * The spec says + * + * "If ctx is current to some other thread, or if either draw or read are + * bound to contexts in another thread, an EGL_BAD_ACCESS error is + * generated." + * + * But it also says + * + * "at most one context may be bound to a particular surface at a given + * time" + * + * The latter is more restrictive so we can check only the latter case. + */ + if ((draw->Binding && draw->Binding != ctx) || + (read->Binding && read->Binding != ctx)) + return _eglError(EGL_BAD_ACCESS, "eglMakeCurrent"); + + /* simply require the configs to be equal */ + if (draw->Config != ctx->Config || read->Config != ctx->Config) + return _eglError(EGL_BAD_MATCH, "eglMakeCurrent"); + switch (ctx->ClientAPI) { #ifdef EGL_VERSION_1_4 - /* OpenGL and OpenGL ES are conflicting */ - switch (ctx->ClientAPI) { - case EGL_OPENGL_ES_API: - if (t->CurrentContexts[_eglConvertApiToIndex(EGL_OPENGL_API)]) - return _eglError(EGL_BAD_ACCESS, "eglMakeCurrent"); - break; - case EGL_OPENGL_API: - if (t->CurrentContexts[_eglConvertApiToIndex(EGL_OPENGL_ES_API)]) - return _eglError(EGL_BAD_ACCESS, "eglMakeCurrent"); - break; - default: - break; - } + /* OpenGL and OpenGL ES are conflicting */ + case EGL_OPENGL_ES_API: + conflict_api = EGL_OPENGL_API; + break; + case EGL_OPENGL_API: + conflict_api = EGL_OPENGL_ES_API; + break; #endif - apiIndex = _eglConvertApiToIndex(ctx->ClientAPI); - } - else { - if (draw != NULL || read != NULL) - return _eglError(EGL_BAD_MATCH, "eglMakeCurrent"); - apiIndex = t->CurrentAPIIndex; + default: + conflict_api = -1; + break; } - oldContext = t->CurrentContexts[apiIndex]; - if (oldContext) { - oldDrawSurface = oldContext->DrawSurface; - oldReadSurface = oldContext->ReadSurface; - assert(oldDrawSurface); - assert(oldReadSurface); - - /* break old bindings */ - t->CurrentContexts[apiIndex] = NULL; - oldContext->Binding = NULL; - oldContext->DrawSurface = NULL; - oldContext->ReadSurface = NULL; - oldDrawSurface->Binding = NULL; - oldReadSurface->Binding = NULL; - - /* - * check if the old context or surfaces need to be deleted - */ - if (!_eglIsSurfaceLinked(oldDrawSurface)) { - assert(draw != oldDrawSurface && read != oldDrawSurface); - drv->API.DestroySurface(drv, dpy, oldDrawSurface); - } - if (oldReadSurface != oldDrawSurface && - !_eglIsSurfaceLinked(oldReadSurface)) { - assert(draw != oldReadSurface && read != oldReadSurface); - drv->API.DestroySurface(drv, dpy, oldReadSurface); - } - if (!_eglIsContextLinked(oldContext)) { - assert(ctx != oldContext); - drv->API.DestroyContext(drv, dpy, oldContext); - } - } + if (conflict_api >= 0 && _eglGetAPIContext(conflict_api)) + return _eglError(EGL_BAD_ACCESS, "eglMakeCurrent"); + + return EGL_TRUE; +} + + +/** + * Drivers will typically call this to do the error checking and + * update the various flags. + * Then, the driver will do its device-dependent Make-Current stuff. + */ +EGLBoolean +_eglMakeCurrent(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSurface *draw, + _EGLSurface *read, _EGLContext *ctx) +{ + _EGLThreadInfo *t = _eglGetCurrentThread(); + _EGLSurface *oldDraw = NULL, *oldRead = NULL; + _EGLContext *oldCtx; + + if (!_eglCheckMakeCurrent(ctx, draw, read)) + return EGL_FALSE; + + oldCtx = _eglBindContextToThread(ctx, t); - /* build new bindings */ if (ctx) { - t->CurrentContexts[apiIndex] = ctx; - ctx->Binding = t; - ctx->DrawSurface = draw; - ctx->ReadSurface = read; - draw->Binding = ctx; - read->Binding = ctx; + oldDraw = _eglBindContextToSurface(ctx, draw, EGL_DRAW); + oldRead = _eglBindContextToSurface(ctx, read, EGL_READ); + } + else if (oldCtx) { + /* unbind the old context from its binding surfaces */ + oldDraw = oldCtx->DrawSurface; + oldRead = oldCtx->ReadSurface; + + if (oldDraw) + _eglBindContextToSurface(NULL, oldDraw, EGL_DRAW); + if (oldRead && oldRead != oldDraw) + _eglBindContextToSurface(NULL, oldRead, EGL_READ); } + /* avoid double destroy */ + if (oldRead && oldRead == oldDraw) + oldRead = NULL; + + if (oldCtx && !_eglIsContextLinked(oldCtx)) + drv->API.DestroyContext(drv, dpy, oldCtx); + if (oldDraw && !_eglIsSurfaceLinked(oldDraw)) + drv->API.DestroySurface(drv, dpy, oldDraw); + if (oldRead && !_eglIsSurfaceLinked(oldRead)) + drv->API.DestroySurface(drv, dpy, oldRead); + return EGL_TRUE; } |