Merge branch 'master' of git://anongit.freedesktop.org/mesa/mesa
[mesa.git] / src / egl / main / eglcontext.c
index ee4b1b59f5b3f1cbc2e5ac3a68ad7e28711307f9..38e195f0446444175edcfd357fc47284ef1b394b 100644 (file)
+/**************************************************************************
+ *
+ * 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>
 #include "eglconfig.h"
 #include "eglcontext.h"
 #include "egldisplay.h"
-#include "egldriver.h"
-#include "eglglobals.h"
+#include "eglcurrent.h"
 #include "eglsurface.h"
+#include "egllog.h"
 
 
 /**
- * Initialize the given _EGLContext object to defaults and/or the values
- * in the attrib_list.
+ * Return the API bit (one of EGL_xxx_BIT) of the context.
  */
-EGLBoolean
-_eglInitContext(_EGLDriver *drv, _EGLContext *ctx,
-                _EGLConfig *conf, const EGLint *attrib_list)
+static EGLint
+_eglGetContextAPIBit(_EGLContext *ctx)
 {
-   EGLint i;
-   const EGLenum api = eglQueryAPI();
+   EGLint bit = 0;
 
-   if (api == EGL_NONE) {
-      _eglError(EGL_BAD_MATCH, "eglCreateContext(no client API)");
-      return EGL_FALSE;
-   }
-
-   memset(ctx, 0, sizeof(_EGLContext));
-
-   ctx->ClientVersion = 1; /* the default, per EGL spec */
-
-   for (i = 0; attrib_list && attrib_list[i] != EGL_NONE; i++) {
-      switch (attrib_list[i]) {
-      case EGL_CONTEXT_CLIENT_VERSION:
-         i++;
-         ctx->ClientVersion = attrib_list[i];
+   switch (ctx->ClientAPI) {
+   case EGL_OPENGL_ES_API:
+      switch (ctx->ClientVersion) {
+      case 1:
+         bit = EGL_OPENGL_ES_BIT;
+         break;
+      case 2:
+         bit = EGL_OPENGL_ES2_BIT;
          break;
       default:
-         _eglError(EGL_BAD_ATTRIBUTE, "_eglInitContext");
-         return EGL_FALSE;
+         break;
       }
+      break;
+   case EGL_OPENVG_API:
+      bit = EGL_OPENVG_BIT;
+      break;
+   case EGL_OPENGL_API:
+      bit = EGL_OPENGL_BIT;
+      break;
+   default:
+      break;
    }
 
-   ctx->Config = conf;
-   ctx->DrawSurface = EGL_NO_SURFACE;
-   ctx->ReadSurface = EGL_NO_SURFACE;
-   ctx->ClientAPI = api;
-   ctx->WindowRenderBuffer = EGL_NONE;
-
-   return EGL_TRUE;
+   return bit;
 }
 
 
 /**
- * Just a placeholder/demo function.  Real driver will never use this!
+ * Parse the list of context attributes and return the proper error code.
  */
-_EGLContext *
-_eglCreateContext(_EGLDriver *drv, _EGLDisplay *dpy, _EGLConfig *conf,
-                  _EGLContext *share_list, const EGLint *attrib_list)
+static EGLint
+_eglParseContextAttribList(_EGLContext *ctx, const EGLint *attrib_list)
 {
-#if 0 /* example code */
-   _EGLContext *context;
+   EGLenum api = ctx->ClientAPI;
+   EGLint i, err = EGL_SUCCESS;
+
+   if (!attrib_list)
+      return EGL_SUCCESS;
 
-   context = (_EGLContext *) calloc(1, sizeof(_EGLContext));
-   if (!context)
-      return NULL;
+   for (i = 0; attrib_list[i] != EGL_NONE; i++) {
+      EGLint attr = attrib_list[i++];
+      EGLint val = attrib_list[i];
 
-   if (!_eglInitContext(drv, context, conf, attrib_list)) {
-      free(context);
-      return NULL;
+      switch (attr) {
+      case EGL_CONTEXT_CLIENT_VERSION:
+         if (api != EGL_OPENGL_ES_API) {
+            err = EGL_BAD_ATTRIBUTE;
+            break;
+         }
+         if (val != 1 && val != 2) {
+            err = EGL_BAD_ATTRIBUTE;
+            break;
+         }
+         ctx->ClientVersion = val;
+         break;
+      default:
+         err = EGL_BAD_ATTRIBUTE;
+         break;
+      }
+
+      if (err != EGL_SUCCESS) {
+         _eglLog(_EGL_DEBUG, "bad context attribute 0x%04x", attr);
+         break;
+      }
    }
 
-   return context;
-#endif
-   return NULL;
+   return err;
 }
 
 
 /**
- * Default fallback routine - drivers should usually override this.
+ * Initialize the given _EGLContext object to defaults and/or the values
+ * in the attrib_list.
  */
 EGLBoolean
-_eglDestroyContext(_EGLDriver *drv, _EGLDisplay *dpy, _EGLContext *ctx)
+_eglInitContext(_EGLContext *ctx, _EGLDisplay *dpy, _EGLConfig *conf,
+                const EGLint *attrib_list)
 {
-   if (!_eglIsContextBound(ctx))
-      free(ctx);
+   const EGLenum api = eglQueryAPI();
+   EGLint err;
+
+   if (api == EGL_NONE) {
+      _eglError(EGL_BAD_MATCH, "eglCreateContext(no client API)");
+      return EGL_FALSE;
+   }
+
+   _eglInitResource(&ctx->Resource, sizeof(*ctx), dpy);
+   ctx->ClientAPI = api;
+   ctx->Config = conf;
+   ctx->WindowRenderBuffer = EGL_NONE;
+
+   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");
+
    return EGL_TRUE;
 }
 
@@ -118,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;
@@ -140,114 +212,186 @@ _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 thread and return the previous context.
+ *
+ * Note that the context may be NULL.
  */
-EGLBoolean
-_eglMakeCurrent(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSurface *draw,
-                _EGLSurface *read, _EGLContext *ctx)
+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) {
+      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();
+   _EGLDisplay *dpy;
+   EGLint conflict_api;
+   EGLBoolean surfaceless;
 
    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");
-
-#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;
-      }
-#endif
-      apiIndex = _eglConvertApiToIndex(ctx->ClientAPI);
+      return EGL_TRUE;
    }
-   else {
-      if (draw != NULL || read != NULL)
-         return _eglError(EGL_BAD_MATCH, "eglMakeCurrent");
-      apiIndex = t->CurrentAPIIndex;
+
+   dpy = ctx->Resource.Display;
+   switch (_eglGetContextAPIBit(ctx)) {
+   case EGL_OPENGL_ES_BIT:
+      surfaceless = dpy->Extensions.KHR_surfaceless_gles1;
+      break;
+   case EGL_OPENGL_ES2_BIT:
+      surfaceless = dpy->Extensions.KHR_surfaceless_gles2;
+      break;
+   case EGL_OPENGL_BIT:
+      surfaceless = dpy->Extensions.KHR_surfaceless_opengl;
+      break;
+   default:
+      surfaceless = EGL_FALSE;
+      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 (!surfaceless && (draw == NULL || read == NULL))
+      return _eglError(EGL_BAD_MATCH, "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."
+    *
+    * and
+    *
+    * "at most one context may be bound to a particular surface at a given
+    * time"
+    */
+   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");
    }
 
-   /* build new bindings */
-   if (ctx) {
-      t->CurrentContexts[apiIndex] = ctx;
-      ctx->Binding = t;
-      ctx->DrawSurface = draw;
-      ctx->ReadSurface = read;
-      draw->Binding = ctx;
-      read->Binding = ctx;
+   /* simply require the configs to be equal */
+   if ((draw && draw->Config != ctx->Config) ||
+       (read && read->Config != ctx->Config))
+      return _eglError(EGL_BAD_MATCH, "eglMakeCurrent");
+
+   switch (ctx->ClientAPI) {
+#ifdef EGL_VERSION_1_4
+   /* 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
+   default:
+      conflict_api = -1;
+      break;
    }
 
+   if (conflict_api >= 0 && _eglGetAPIContext(conflict_api))
+      return _eglError(EGL_BAD_ACCESS, "eglMakeCurrent");
+
    return EGL_TRUE;
 }
 
 
 /**
- * This is defined by the EGL_MESA_copy_context extension.
+ * Bind the context to the current thread and given surfaces.  Return the
+ * 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
-_eglCopyContextMESA(_EGLDriver *drv, EGLDisplay dpy, EGLContext source,
-                    EGLContext dest, EGLint mask)
+_eglBindContext(_EGLContext *ctx, _EGLSurface *draw, _EGLSurface *read,
+                _EGLContext **old_ctx,
+                _EGLSurface **old_draw, _EGLSurface **old_read)
 {
-   /* 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;
+   _EGLThreadInfo *t = _eglGetCurrentThread();
+   _EGLContext *prev_ctx;
+   _EGLSurface *prev_draw, *prev_read;
+
+   if (!_eglCheckMakeCurrent(ctx, draw, read))
+      return EGL_FALSE;
+
+   /* increment refcounts before binding */
+   _eglGetContext(ctx);
+   _eglGetSurface(draw);
+   _eglGetSurface(read);
+
+   /* bind the new context */
+   prev_ctx = _eglBindContextToThread(ctx, t);
+
+   /* break previous bindings */
+   if (prev_ctx) {
+      prev_draw = prev_ctx->DrawSurface;
+      prev_read = prev_ctx->ReadSurface;
+
+      if (prev_draw)
+         prev_draw->CurrentContext = NULL;
+      if (prev_read)
+         prev_read->CurrentContext = NULL;
+
+      prev_ctx->DrawSurface = NULL;
+      prev_ctx->ReadSurface = NULL;
+   }
+   else {
+      prev_draw = prev_read = NULL;
+   }
+
+   /* establish new bindings */
+   if (ctx) {
+      if (draw)
+         draw->CurrentContext = ctx;
+      if (read)
+         read->CurrentContext = ctx;
+
+      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;
+
+   return EGL_TRUE;
 }