/**
- * 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;
}