Merge branch '7.8'
[mesa.git] / src / egl / main / eglcontext.c
index deaa8d3f4121ae00d6470b16ef1404c969ec74d7..d5a1e79a9948926a8a1d3f07f7db90a2983e9dbc 100644 (file)
@@ -4,9 +4,96 @@
 #include "eglconfig.h"
 #include "eglcontext.h"
 #include "egldisplay.h"
-#include "egldriver.h"
-#include "eglglobals.h"
+#include "eglcurrent.h"
 #include "eglsurface.h"
+#include "egllog.h"
+
+
+/**
+ * Return the API bit (one of EGL_xxx_BIT) of the context.
+ */
+static EGLint
+_eglGetContextAPIBit(_EGLContext *ctx)
+{
+   EGLint bit = 0;
+
+   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:
+         break;
+      }
+      break;
+   case EGL_OPENVG_API:
+      bit = EGL_OPENVG_BIT;
+      break;
+   case EGL_OPENGL_API:
+      bit = EGL_OPENGL_BIT;
+      break;
+   default:
+      break;
+   }
+
+   return bit;
+}
+
+
+/**
+ * Parse the list of context attributes and return the proper error code.
+ */
+static EGLint
+_eglParseContextAttribList(_EGLContext *ctx, const EGLint *attrib_list)
+{
+   EGLenum api = ctx->ClientAPI;
+   EGLint i, err = EGL_SUCCESS;
+
+   if (!attrib_list)
+      return EGL_SUCCESS;
+
+   for (i = 0; attrib_list[i] != EGL_NONE; i++) {
+      EGLint attr = attrib_list[i++];
+      EGLint val = attrib_list[i];
+
+      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;
+      }
+   }
+
+   if (err == EGL_SUCCESS) {
+      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;
+}
 
 
 /**
  * in the attrib_list.
  */
 EGLBoolean
-_eglInitContext(_EGLDriver *drv, _EGLContext *ctx,
-                _EGLConfig *conf, const EGLint *attrib_list)
+_eglInitContext(_EGLContext *ctx, _EGLDisplay *dpy, _EGLConfig *conf,
+                const EGLint *attrib_list)
 {
-   EGLint i;
    const EGLenum api = eglQueryAPI();
+   EGLint err;
 
    if (api == EGL_NONE) {
       _eglError(EGL_BAD_MATCH, "eglCreateContext(no client API)");
@@ -26,26 +113,16 @@ _eglInitContext(_EGLDriver *drv, _EGLContext *ctx,
    }
 
    memset(ctx, 0, sizeof(_EGLContext));
+   ctx->Resource.Display = dpy;
+   ctx->ClientAPI = api;
+   ctx->Config = conf;
+   ctx->WindowRenderBuffer = EGL_NONE;
 
    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];
-         break;
-      default:
-         _eglError(EGL_BAD_ATTRIBUTE, "_eglInitContext");
-         return EGL_FALSE;
-      }
-   }
-
-   ctx->Config = conf;
-   ctx->DrawSurface = EGL_NO_SURFACE;
-   ctx->ReadSurface = EGL_NO_SURFACE;
-   ctx->ClientAPI = api;
-   ctx->WindowRenderBuffer = EGL_NONE;
+   err = _eglParseContextAttribList(ctx, attrib_list);
+   if (err != EGL_SUCCESS)
+      return _eglError(err, "eglCreateContext");
 
    return EGL_TRUE;
 }
@@ -58,20 +135,6 @@ _EGLContext *
 _eglCreateContext(_EGLDriver *drv, _EGLDisplay *dpy, _EGLConfig *conf,
                   _EGLContext *share_list, const EGLint *attrib_list)
 {
-#if 0 /* example code */
-   _EGLContext *context;
-
-   context = (_EGLContext *) calloc(1, sizeof(_EGLContext));
-   if (!context)
-      return NULL;
-
-   if (!_eglInitContext(drv, context, conf, attrib_list)) {
-      free(context);
-      return NULL;
-   }
-
-   return context;
-#endif
    return NULL;
 }
 
@@ -140,34 +203,62 @@ _eglQueryContext(_EGLDriver *drv, _EGLDisplay *dpy, _EGLContext *c,
 
 
 /**
- * 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.
+ * 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 _EGLSurface *
-_eglBindContextToSurface(_EGLContext *ctx, _EGLSurface *surf, EGLint readdraw)
+static void
+_eglBindContextToSurfaces(_EGLContext *newCtx,
+                          _EGLSurface **draw, _EGLSurface **read)
 {
-   _EGLSurface **attachment, *oldSurf;
+   _EGLSurface *newDraw = *draw, *newRead = *read;
+   _EGLContext *oldCtx;
 
-   if (!ctx) {
-      surf->Binding = NULL;
-      return NULL;
+   /*
+    * 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.
+    */
+   oldCtx = newDraw->CurrentContext;
+   if (newCtx != oldCtx) {
+      if (oldCtx) {
+         assert(oldCtx->DrawSurface == newDraw);
+         oldCtx->DrawSurface = NULL;
+      }
+
+      if (newCtx) {
+         _EGLSurface *oldDraw = newCtx->DrawSurface;
+         if (oldDraw)
+            oldDraw->CurrentContext = NULL;
+
+         newCtx->DrawSurface = newDraw;
+         *draw = oldDraw;
+      }
+
+      newDraw->CurrentContext = newCtx;
    }
 
-   attachment = (readdraw == EGL_DRAW) ?
-      &ctx->DrawSurface : &ctx->ReadSurface;
-   oldSurf = *attachment;
+   /* likewise */
+   if (newRead != newDraw)
+      oldCtx = newRead->CurrentContext;
+   if (newCtx != oldCtx) {
+      if (oldCtx) {
+         assert(oldCtx->ReadSurface == newRead);
+         oldCtx->ReadSurface = NULL;
+      }
 
-   if (oldSurf == surf)
-      return NULL;
+      if (newCtx) {
+         _EGLSurface *oldRead = newCtx->ReadSurface;
+         if (oldRead)
+            oldRead->CurrentContext = NULL;
 
-   if (oldSurf)
-      oldSurf->Binding = NULL;
-   surf->Binding = ctx;
-   *attachment = surf;
+         newCtx->ReadSurface = newRead;
+         *read = oldRead;
+      }
 
-   return oldSurf;
+      newRead->CurrentContext = newCtx;
+   }
 }
 
 
@@ -186,15 +277,14 @@ _eglBindContextToThread(_EGLContext *ctx, _EGLThreadInfo *t)
       _eglConvertApiToIndex(ctx->ClientAPI) : t->CurrentAPIIndex;
 
    oldCtx = t->CurrentContexts[apiIndex];
-   if (ctx == oldCtx)
-      return NULL;
-
-   if (oldCtx)
-      oldCtx->Binding = NULL;
-   if (ctx)
-      ctx->Binding = t;
+   if (ctx != oldCtx) {
+      if (oldCtx)
+         oldCtx->Binding = NULL;
+      if (ctx)
+         ctx->Binding = t;
 
-   t->CurrentContexts[apiIndex] = ctx;
+      t->CurrentContexts[apiIndex] = ctx;
+   }
 
    return oldCtx;
 }
@@ -241,8 +331,8 @@ _eglCheckMakeCurrent(_EGLContext *ctx, _EGLSurface *draw, _EGLSurface *read)
     *
     * The latter is more restrictive so we can check only the latter case.
     */
-   if ((draw->Binding && draw->Binding != ctx) ||
-       (read->Binding && read->Binding != ctx))
+   if ((draw->CurrentContext && draw->CurrentContext != ctx) ||
+       (read->CurrentContext && read->CurrentContext != ctx))
       return _eglError(EGL_BAD_ACCESS, "eglMakeCurrent");
 
    /* simply require the configs to be equal */
@@ -272,53 +362,54 @@ _eglCheckMakeCurrent(_EGLContext *ctx, _EGLSurface *draw, _EGLSurface *read)
 
 
 /**
- * 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 current thread and given surfaces.  Return the
+ * previously bound context and the surfaces it bound to.  Each argument is
+ * both input and output.
  */
 EGLBoolean
-_eglMakeCurrent(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSurface *draw,
-                _EGLSurface *read, _EGLContext *ctx)
+_eglBindContext(_EGLContext **ctx, _EGLSurface **draw, _EGLSurface **read)
 {
    _EGLThreadInfo *t = _eglGetCurrentThread();
-   _EGLSurface *oldDraw = NULL, *oldRead = NULL;
-   _EGLContext *oldCtx;
+   _EGLContext *newCtx = *ctx, *oldCtx;
 
-   if (!_eglCheckMakeCurrent(ctx, draw, read))
+   if (!_eglCheckMakeCurrent(newCtx, *draw, *read))
       return EGL_FALSE;
 
-   oldCtx = _eglBindContextToThread(ctx, t);
+   /* bind the new context */
+   oldCtx = _eglBindContextToThread(newCtx, t);
 
-   if (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);
-   }
+   if (newCtx)
+      _eglBindContextToSurfaces(newCtx, draw, read);
+
+   /* unbind the old context from its binding surfaces */
+   if (oldCtx && oldCtx != newCtx) {
+      assert(!*draw && !*read);
 
-   /* avoid double destroy */
-   if (oldRead && oldRead == oldDraw)
-      oldRead = NULL;
+      *draw = oldCtx->DrawSurface;
+      *read = oldCtx->ReadSurface;
+      assert(*draw && *read);
 
-   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);
+      _eglBindContextToSurfaces(NULL, draw, read);
+   }
+
+   *ctx = oldCtx;
+   /* draw and read have been updated in _eglBindContextToSurfaces */
 
    return EGL_TRUE;
 }
 
 
+/**
+ * Just a placeholder/demo function.  Drivers should override this.
+ */
+EGLBoolean
+_eglMakeCurrent(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSurface *draw,
+                _EGLSurface *read, _EGLContext *ctx)
+{
+   return EGL_FALSE;
+}
+
+
 /**
  * This is defined by the EGL_MESA_copy_context extension.
  */