Merge branch 'master' of git://anongit.freedesktop.org/mesa/mesa
[mesa.git] / src / egl / main / eglcontext.c
index 74a5a632eba1ea34c904ef07e6c5172f7e6181ec..38e195f0446444175edcfd357fc47284ef1b394b 100644 (file)
@@ -1,3 +1,33 @@
+/**************************************************************************
+ *
+ * Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Texas.
+ * Copyright 2009-2010 Chia-I Wu <olvaffe@gmail.com>
+ * Copyright 2010-2011 LunarG, Inc.
+ * 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 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 NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS 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.
+ *
+ **************************************************************************/
+
+
 #include <assert.h>
 #include <stdlib.h>
 #include <string.h>
@@ -83,15 +113,6 @@ _eglParseContextAttribList(_EGLContext *ctx, const EGLint *attrib_list)
       }
    }
 
-   if (err == EGL_SUCCESS && ctx->Config) {
-      EGLint renderable_type, api_bit;
-
-      renderable_type = GET_CONFIG_ATTRIB(ctx->Config, EGL_RENDERABLE_TYPE);
-      api_bit = _eglGetContextAPIBit(ctx);
-      if (!(renderable_type & api_bit))
-         err = EGL_BAD_CONFIG;
-   }
-
    return err;
 }
 
@@ -112,8 +133,7 @@ _eglInitContext(_EGLContext *ctx, _EGLDisplay *dpy, _EGLConfig *conf,
       return EGL_FALSE;
    }
 
-   memset(ctx, 0, sizeof(_EGLContext));
-   ctx->Resource.Display = dpy;
+   _eglInitResource(&ctx->Resource, sizeof(*ctx), dpy);
    ctx->ClientAPI = api;
    ctx->Config = conf;
    ctx->WindowRenderBuffer = EGL_NONE;
@@ -121,6 +141,16 @@ _eglInitContext(_EGLContext *ctx, _EGLDisplay *dpy, _EGLConfig *conf,
    ctx->ClientVersion = 1; /* the default, per EGL spec */
 
    err = _eglParseContextAttribList(ctx, attrib_list);
+   if (err == EGL_SUCCESS && ctx->Config) {
+      EGLint api_bit;
+
+      api_bit = _eglGetContextAPIBit(ctx);
+      if (!(ctx->Config->RenderableType & api_bit)) {
+         _eglLog(_EGL_DEBUG, "context api is 0x%x while config supports 0x%x",
+               api_bit, ctx->Config->RenderableType);
+         err = EGL_BAD_CONFIG;
+      }
+   }
    if (err != EGL_SUCCESS)
       return _eglError(err, "eglCreateContext");
 
@@ -128,29 +158,6 @@ _eglInitContext(_EGLContext *ctx, _EGLDisplay *dpy, _EGLConfig *conf,
 }
 
 
-/**
- * Just a placeholder/demo function.  Real driver will never use this!
- */
-_EGLContext *
-_eglCreateContext(_EGLDriver *drv, _EGLDisplay *dpy, _EGLConfig *conf,
-                  _EGLContext *share_list, const EGLint *attrib_list)
-{
-   return NULL;
-}
-
-
-/**
- * Default fallback routine - drivers should usually override this.
- */
-EGLBoolean
-_eglDestroyContext(_EGLDriver *drv, _EGLDisplay *dpy, _EGLContext *ctx)
-{
-   if (!_eglIsContextBound(ctx))
-      free(ctx);
-   return EGL_TRUE;
-}
-
-
 #ifdef EGL_VERSION_1_2
 static EGLint
 _eglQueryContextRenderBuffer(_EGLContext *ctx)
@@ -181,7 +188,9 @@ _eglQueryContext(_EGLDriver *drv, _EGLDisplay *dpy, _EGLContext *c,
 
    switch (attribute) {
    case EGL_CONFIG_ID:
-      *value = GET_CONFIG_ATTRIB(c->Config, EGL_CONFIG_ID);
+      if (!c->Config)
+         return _eglError(EGL_BAD_ATTRIBUTE, "eglQueryContext");
+      *value = c->Config->ConfigID;
       break;
    case EGL_CONTEXT_CLIENT_VERSION:
       *value = c->ClientVersion;
@@ -202,70 +211,6 @@ _eglQueryContext(_EGLDriver *drv, _EGLDisplay *dpy, _EGLContext *c,
 }
 
 
-/**
- * Bind the context to the surfaces.  Return the surfaces that are "orphaned".
- * That is, when the context is not NULL, return the surfaces it previously
- * bound to;  when the context is NULL, the same surfaces are returned.
- */
-static void
-_eglBindContextToSurfaces(_EGLContext *newCtx,
-                          _EGLSurface **draw, _EGLSurface **read)
-{
-   _EGLSurface *newDraw = *draw, *newRead = *read;
-   _EGLContext *oldCtx;
-
-   /*
-    * The goal is to bind a newCtx to newDraw.  Since newDraw may already have
-    * a binding context (oldCtx), and newCtx may already be bound to another
-    * surface (oldDraw), the old bindings are broken first and the new one is
-    * created.
-    */
-   if (newDraw) {
-      oldCtx = newDraw->CurrentContext;
-      if (newCtx != oldCtx) {
-        if (oldCtx) {
-           assert(oldCtx->DrawSurface == newDraw);
-           oldCtx->DrawSurface = NULL;
-        }
-      
-        newDraw->CurrentContext = newCtx;
-      }
-   }
-
-   if (newCtx) {
-      _EGLSurface *oldDraw = newCtx->DrawSurface;
-      if (oldDraw)
-        oldDraw->CurrentContext = NULL;
-
-      newCtx->DrawSurface = newDraw;
-      *draw = oldDraw;
-   }
-
-   /* likewise */
-   if (newRead && newRead != newDraw) {
-      oldCtx = newRead->CurrentContext;
-      if (newCtx != oldCtx) {
-        if (oldCtx) {
-           assert(oldCtx->ReadSurface == newRead);
-           oldCtx->ReadSurface = NULL;
-        }
-
-        newRead->CurrentContext = newCtx;
-      }
-   }
-
-   if (newCtx) {
-      _EGLSurface *oldRead = newCtx->ReadSurface;
-      if (oldRead)
-        oldRead->CurrentContext = NULL;
-
-      newCtx->ReadSurface = newRead;
-      *read = oldRead;
-   }
-
-}
-
-
 /**
  * Bind the context to the thread and return the previous context.
  *
@@ -301,7 +246,7 @@ static EGLBoolean
 _eglCheckMakeCurrent(_EGLContext *ctx, _EGLSurface *draw, _EGLSurface *read)
 {
    _EGLThreadInfo *t = _eglGetCurrentThread();
-   _EGLDisplay *dpy = ctx->Resource.Display;
+   _EGLDisplay *dpy;
    EGLint conflict_api;
    EGLBoolean surfaceless;
 
@@ -315,6 +260,7 @@ _eglCheckMakeCurrent(_EGLContext *ctx, _EGLSurface *draw, _EGLSurface *read)
       return EGL_TRUE;
    }
 
+   dpy = ctx->Resource.Display;
    switch (_eglGetContextAPIBit(ctx)) {
    case EGL_OPENGL_ES_BIT:
       surfaceless = dpy->Extensions.KHR_surfaceless_gles1;
@@ -333,10 +279,6 @@ _eglCheckMakeCurrent(_EGLContext *ctx, _EGLSurface *draw, _EGLSurface *read)
    if (!surfaceless && (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
     *
@@ -344,16 +286,23 @@ _eglCheckMakeCurrent(_EGLContext *ctx, _EGLSurface *draw, _EGLSurface *read)
     * bound to contexts in another thread, an EGL_BAD_ACCESS error is
     * generated."
     *
-    * But it also says
+    * and
     *
     * "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 && draw->CurrentContext && draw->CurrentContext != ctx) ||
-       (read && read->CurrentContext && read->CurrentContext != ctx))
+   if (ctx->Binding && ctx->Binding != t)
       return _eglError(EGL_BAD_ACCESS, "eglMakeCurrent");
+   if (draw && draw->CurrentContext && draw->CurrentContext != ctx) {
+      if (draw->CurrentContext->Binding != t ||
+          draw->CurrentContext->ClientAPI != ctx->ClientAPI)
+         return _eglError(EGL_BAD_ACCESS, "eglMakeCurrent");
+   }
+   if (read && read->CurrentContext && read->CurrentContext != ctx) {
+      if (read->CurrentContext->Binding != t ||
+          read->CurrentContext->ClientAPI != ctx->ClientAPI)
+         return _eglError(EGL_BAD_ACCESS, "eglMakeCurrent");
+   }
 
    /* simply require the configs to be equal */
    if ((draw && draw->Config != ctx->Config) ||
@@ -384,61 +333,65 @@ _eglCheckMakeCurrent(_EGLContext *ctx, _EGLSurface *draw, _EGLSurface *read)
 
 /**
  * Bind the context to the current thread and given surfaces.  Return the
- * previously bound context and the surfaces it bound to.  Each argument is
- * both input and output.
+ * previous bound context and surfaces.  The caller should unreference the
+ * returned context and surfaces.
+ *
+ * Making a second call with the resources returned by the first call
+ * unsurprisingly undoes the first call, except for the resouce reference
+ * counts.
  */
 EGLBoolean
-_eglBindContext(_EGLContext **ctx, _EGLSurface **draw, _EGLSurface **read)
+_eglBindContext(_EGLContext *ctx, _EGLSurface *draw, _EGLSurface *read,
+                _EGLContext **old_ctx,
+                _EGLSurface **old_draw, _EGLSurface **old_read)
 {
    _EGLThreadInfo *t = _eglGetCurrentThread();
-   _EGLContext *newCtx = *ctx, *oldCtx;
+   _EGLContext *prev_ctx;
+   _EGLSurface *prev_draw, *prev_read;
 
-   if (!_eglCheckMakeCurrent(newCtx, *draw, *read))
+   if (!_eglCheckMakeCurrent(ctx, draw, read))
       return EGL_FALSE;
 
-   /* bind the new context */
-   oldCtx = _eglBindContextToThread(newCtx, t);
+   /* increment refcounts before binding */
+   _eglGetContext(ctx);
+   _eglGetSurface(draw);
+   _eglGetSurface(read);
 
-   if (newCtx)
-      _eglBindContextToSurfaces(newCtx, draw, read);
+   /* bind the new context */
+   prev_ctx = _eglBindContextToThread(ctx, t);
 
-   /* unbind the old context from its binding surfaces */
-   if (oldCtx && oldCtx != newCtx) {
-      assert(!*draw && !*read);
+   /* break previous bindings */
+   if (prev_ctx) {
+      prev_draw = prev_ctx->DrawSurface;
+      prev_read = prev_ctx->ReadSurface;
 
-      *draw = oldCtx->DrawSurface;
-      *read = oldCtx->ReadSurface;
+      if (prev_draw)
+         prev_draw->CurrentContext = NULL;
+      if (prev_read)
+         prev_read->CurrentContext = NULL;
 
-      _eglBindContextToSurfaces(NULL, draw, read);
+      prev_ctx->DrawSurface = NULL;
+      prev_ctx->ReadSurface = NULL;
+   }
+   else {
+      prev_draw = prev_read = NULL;
    }
 
-   *ctx = oldCtx;
-   /* draw and read have been updated in _eglBindContextToSurfaces */
-
-   return EGL_TRUE;
-}
-
+   /* establish new bindings */
+   if (ctx) {
+      if (draw)
+         draw->CurrentContext = ctx;
+      if (read)
+         read->CurrentContext = ctx;
 
-/**
- * Just a placeholder/demo function.  Drivers should override this.
- */
-EGLBoolean
-_eglMakeCurrent(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSurface *draw,
-                _EGLSurface *read, _EGLContext *ctx)
-{
-   return EGL_FALSE;
-}
+      ctx->DrawSurface = draw;
+      ctx->ReadSurface = read;
+   }
 
+   assert(old_ctx && old_draw && old_read);
+   *old_ctx = prev_ctx;
+   *old_draw = prev_draw;
+   *old_read = prev_read;
 
-/**
- * This is defined by the EGL_MESA_copy_context extension.
- */
-EGLBoolean
-_eglCopyContextMESA(_EGLDriver *drv, EGLDisplay dpy, EGLContext source,
-                    EGLContext dest, EGLint mask)
-{
-   /* This function will always have to be overridden/implemented in the
-    * device driver.  If the driver is based on Mesa, use _mesa_copy_context().
-    */
-   return EGL_FALSE;
+   return EGL_TRUE;
 }