egl: Refactor _eglMakeCurrent.
authorChia-I Wu <olvaffe@gmail.com>
Tue, 26 Jan 2010 08:53:40 +0000 (16:53 +0800)
committerChia-I Wu <olvaffe@gmail.com>
Tue, 26 Jan 2010 10:46:05 +0000 (18:46 +0800)
Refactor _eglMakeCurrent into _eglCheckMakeCurrent,
_eglBindContextToSurface, and _eglBindContextToThread.

src/egl/main/eglcontext.c

index ee4b1b59f5b3f1cbc2e5ac3a68ad7e28711307f9..deaa8d3f4121ae00d6470b16ef1404c969ec74d7 100644 (file)
@@ -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;
 }