#include <string.h>
#include "eglconfig.h"
#include "eglcontext.h"
-#include "egldriver.h"
-#include "eglglobals.h"
-#include "eglhash.h"
+#include "egldisplay.h"
+#include "eglcurrent.h"
#include "eglsurface.h"
+#include "egllog.h"
/**
- * Initialize the given _EGLContext object to defaults.
+ * Return the API bit (one of EGL_xxx_BIT) of the context.
*/
-void
-_eglInitContext(_EGLContext *ctx)
+static EGLint
+_eglGetContextAPIBit(_EGLContext *ctx)
{
- /* just init to zero for now */
- memset(ctx, 0, sizeof(_EGLContext));
+ 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;
}
-/*
- * Assign an EGLContext handle to the _EGLContext object then put it into
- * the hash table.
+/**
+ * Parse the list of context attributes and return the proper error code.
*/
-void
-_eglSaveContext(_EGLContext *ctx)
+static EGLint
+_eglParseContextAttribList(_EGLContext *ctx, const EGLint *attrib_list)
{
- assert(ctx);
- ctx->Handle = _eglHashGenKey(_eglGlobal.Contexts);
- _eglHashInsert(_eglGlobal.Contexts, ctx->Handle, ctx);
+ 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;
+ }
+ }
+
+ return err;
}
/**
- * Remove the given _EGLContext object from the hash table.
+ * Initialize the given _EGLContext object to defaults and/or the values
+ * in the attrib_list.
*/
-void
-_eglRemoveContext(_EGLContext *ctx)
+EGLBoolean
+_eglInitContext(_EGLContext *ctx, _EGLDisplay *dpy, _EGLConfig *conf,
+ const EGLint *attrib_list)
{
- _eglHashRemove(_eglGlobal.Contexts, ctx->Handle);
+ const EGLenum api = eglQueryAPI();
+ EGLint err;
+
+ if (api == EGL_NONE) {
+ _eglError(EGL_BAD_MATCH, "eglCreateContext(no client API)");
+ return EGL_FALSE;
+ }
+
+ 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 */
+
+ err = _eglParseContextAttribList(ctx, 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)) {
+ _eglLog(_EGL_DEBUG, "context api is 0x%x while config supports 0x%x",
+ api_bit, renderable_type);
+ err = EGL_BAD_CONFIG;
+ }
+ }
+ if (err != EGL_SUCCESS)
+ return _eglError(err, "eglCreateContext");
+
+ return EGL_TRUE;
}
/**
- * Return the _EGLContext object that corresponds to the given
- * EGLContext handle.
+ * Just a placeholder/demo function. Real driver will never use this!
*/
_EGLContext *
-_eglLookupContext(EGLContext ctx)
+_eglCreateContext(_EGLDriver *drv, _EGLDisplay *dpy, _EGLConfig *conf,
+ _EGLContext *share_list, const EGLint *attrib_list)
{
- _EGLContext *c = (_EGLContext *) _eglHashLookup(_eglGlobal.Contexts, ctx);
- return c;
+ return NULL;
}
/**
- * Return the currently bound _EGLContext object, or NULL.
+ * Default fallback routine - drivers should usually override this.
*/
-_EGLContext *
-_eglGetCurrentContext(void)
+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)
{
- /* XXX this should be per-thread someday */
- return _eglGlobal.CurrentContext;
+ _EGLSurface *surf = ctx->DrawSurface;
+ EGLint rb;
+
+ if (!surf)
+ return EGL_NONE;
+ if (surf->Type == EGL_WINDOW_BIT && ctx->WindowRenderBuffer != EGL_NONE)
+ rb = ctx->WindowRenderBuffer;
+ else
+ rb = surf->RenderBuffer;
+ return rb;
+}
+#endif /* EGL_VERSION_1_2 */
+
+
+EGLBoolean
+_eglQueryContext(_EGLDriver *drv, _EGLDisplay *dpy, _EGLContext *c,
+ EGLint attribute, EGLint *value)
+{
+ (void) drv;
+ (void) dpy;
+
+ if (!value)
+ return _eglError(EGL_BAD_PARAMETER, "eglQueryContext");
+
+ switch (attribute) {
+ case EGL_CONFIG_ID:
+ *value = GET_CONFIG_ATTRIB(c->Config, EGL_CONFIG_ID);
+ break;
+ case EGL_CONTEXT_CLIENT_VERSION:
+ *value = c->ClientVersion;
+ break;
+#ifdef EGL_VERSION_1_2
+ case EGL_CONTEXT_CLIENT_TYPE:
+ *value = c->ClientAPI;
+ break;
+ case EGL_RENDER_BUFFER:
+ *value = _eglQueryContextRenderBuffer(c);
+ break;
+#endif /* EGL_VERSION_1_2 */
+ default:
+ return _eglError(EGL_BAD_ATTRIBUTE, "eglQueryContext");
+ }
+
+ return EGL_TRUE;
}
/**
- * Just a placeholder/demo function. Real driver will never use this!
+ * Bind the context to the thread and return the previous context.
+ *
+ * Note that the context may be NULL.
*/
-EGLContext
-_eglCreateContext(_EGLDriver *drv, EGLDisplay dpy, EGLConfig config, EGLContext share_list, const EGLint *attrib_list)
+static _EGLContext *
+_eglBindContextToThread(_EGLContext *ctx, _EGLThreadInfo *t)
{
- _EGLConfig *conf = _eglLookupConfig(drv, dpy, config);
- if (!conf) {
- _eglError(EGL_BAD_CONFIG, "eglCreateContext");
- return EGL_NO_CONTEXT;
- }
+ EGLint apiIndex;
+ _EGLContext *oldCtx;
- if (share_list != EGL_NO_CONTEXT) {
- _EGLContext *shareCtx = _eglLookupContext(share_list);
- if (!shareCtx) {
- _eglError(EGL_BAD_CONTEXT, "eglCreateContext(share_list)");
- return EGL_NO_CONTEXT;
- }
+ 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 EGL_NO_CONTEXT;
+ return oldCtx;
}
/**
- * Default fallback routine - drivers should usually override this.
+ * Return true if the given context and surfaces can be made current.
*/
-EGLBoolean
-_eglDestroyContext(_EGLDriver *drv, EGLDisplay dpy, EGLContext ctx)
+static EGLBoolean
+_eglCheckMakeCurrent(_EGLContext *ctx, _EGLSurface *draw, _EGLSurface *read)
{
- _EGLContext *context = _eglLookupContext(ctx);
- if (context) {
- _eglHashRemove(_eglGlobal.Contexts, ctx);
- if (context->IsBound) {
- context->DeletePending = EGL_TRUE;
- }
- else {
- free(context);
- }
+ _EGLThreadInfo *t = _eglGetCurrentThread();
+ _EGLDisplay *dpy;
+ EGLint conflict_api;
+ EGLBoolean surfaceless;
+
+ if (_eglIsCurrentThreadDummy())
+ return _eglError(EGL_BAD_ALLOC, "eglMakeCurrent");
+
+ /* this is easy */
+ if (!ctx) {
+ if (draw || read)
+ return _eglError(EGL_BAD_MATCH, "eglMakeCurrent");
return EGL_TRUE;
}
- else {
- _eglError(EGL_BAD_CONTEXT, "eglDestroyContext");
- return EGL_TRUE;
+
+ 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;
}
-}
+ if (!surfaceless && (draw == NULL || read == NULL))
+ return _eglError(EGL_BAD_MATCH, "eglMakeCurrent");
-EGLBoolean
-_eglQueryContext(_EGLDriver *drv, EGLDisplay dpy, EGLContext ctx, EGLint attribute, EGLint *value)
-{
- _EGLContext *c = _eglLookupContext(ctx);
+ /* context stealing from another thread is not allowed */
+ if (ctx->Binding && ctx->Binding != t)
+ return _eglError(EGL_BAD_ACCESS, "eglMakeCurrent");
- (void) drv;
- (void) dpy;
+ /*
+ * 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 && draw->CurrentContext && draw->CurrentContext != ctx) ||
+ (read && read->CurrentContext && read->CurrentContext != ctx))
+ return _eglError(EGL_BAD_ACCESS, "eglMakeCurrent");
- if (!c) {
- _eglError(EGL_BAD_CONTEXT, "eglQueryContext");
- return EGL_FALSE;
- }
+ /* 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 (attribute) {
- case EGL_CONFIG_ID:
- *value = GET_CONFIG_ATTRIB(c->Config, EGL_CONFIG_ID);
- return EGL_TRUE;
+ 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:
- _eglError(EGL_BAD_ATTRIBUTE, "eglQueryContext");
- return EGL_FALSE;
+ conflict_api = -1;
+ break;
}
+
+ 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 IsBound and DeletePending flags.
- * Then, the driver will do its device-dependent Make-Current stuff.
+ * Bind the context to the current thread and given surfaces. Return the
+ * "orphaned" context and surfaces. Each argument is both input and output.
*/
EGLBoolean
-_eglMakeCurrent(_EGLDriver *drv, EGLDisplay dpy, EGLSurface d, EGLSurface r, EGLContext context)
+_eglBindContext(_EGLContext **ctx, _EGLSurface **draw, _EGLSurface **read)
{
- _EGLContext *ctx = _eglLookupContext(context);
- _EGLSurface *draw = _eglLookupSurface(d);
- _EGLSurface *read = _eglLookupSurface(r);
-
- _EGLContext *oldContext = _eglGetCurrentContext();
- _EGLSurface *oldDrawSurface = _eglGetCurrentSurface(EGL_DRAW);
- _EGLSurface *oldReadSurface = _eglGetCurrentSurface(EGL_READ);
-
- /* error checking */
- if (ctx) {
- if (draw == NULL || read == NULL) {
- _eglError(EGL_BAD_MATCH, "eglMakeCurrent");
- return EGL_FALSE;
- }
- if (draw->Config != ctx->Config) {
- _eglError(EGL_BAD_MATCH, "eglMakeCurrent");
- return EGL_FALSE;
- }
- if (read->Config != ctx->Config) {
- _eglError(EGL_BAD_MATCH, "eglMakeCurrent");
- return EGL_FALSE;
- }
- }
+ _EGLThreadInfo *t = _eglGetCurrentThread();
+ _EGLContext *newCtx = *ctx, *oldCtx;
+ _EGLSurface *newDraw = *draw, *newRead = *read;
- /*
- * check if the old context or surfaces need to be deleted
- */
- if (oldDrawSurface != NULL) {
- oldDrawSurface->IsBound = EGL_FALSE;
- if (oldDrawSurface->DeletePending) {
- /* make sure we don't try to rebind a deleted surface */
- if (draw == oldDrawSurface || draw == oldReadSurface) {
- draw = NULL;
- }
- /* really delete surface now */
- drv->DestroySurface(drv, dpy, oldDrawSurface->Handle);
- }
- }
- if (oldReadSurface != NULL && oldReadSurface != oldDrawSurface) {
- oldReadSurface->IsBound = EGL_FALSE;
- if (oldReadSurface->DeletePending) {
- /* make sure we don't try to rebind a deleted surface */
- if (read == oldDrawSurface || read == oldReadSurface) {
- read = NULL;
- }
- /* really delete surface now */
- drv->DestroySurface(drv, dpy, oldReadSurface->Handle);
- }
- }
- if (oldContext != NULL) {
- oldContext->IsBound = EGL_FALSE;
- if (oldContext->DeletePending) {
- /* make sure we don't try to rebind a deleted context */
- if (ctx == oldContext) {
- ctx = NULL;
- }
- /* really delete context now */
- drv->DestroyContext(drv, dpy, oldContext->Handle);
- }
+ if (!_eglCheckMakeCurrent(newCtx, newDraw, newRead))
+ return EGL_FALSE;
+
+ /* bind the new context */
+ oldCtx = _eglBindContextToThread(newCtx, t);
+
+ /* break old bindings */
+ if (oldCtx) {
+ *ctx = oldCtx;
+ *draw = oldCtx->DrawSurface;
+ *read = oldCtx->ReadSurface;
+
+ if (*draw)
+ (*draw)->CurrentContext = NULL;
+ if (*read)
+ (*read)->CurrentContext = NULL;
+
+ oldCtx->DrawSurface = NULL;
+ oldCtx->ReadSurface = NULL;
}
- if (ctx) {
- /* check read/draw again, in case we deleted them above */
- if (draw == NULL || read == NULL) {
- _eglError(EGL_BAD_MATCH, "eglMakeCurrent");
- return EGL_FALSE;
- }
- ctx->DrawSurface = draw;
- ctx->ReadSurface = read;
- ctx->IsBound = EGL_TRUE;
- draw->IsBound = EGL_TRUE;
- read->IsBound = EGL_TRUE;
+ /* establish new bindings */
+ if (newCtx) {
+ if (newDraw)
+ newDraw->CurrentContext = newCtx;
+ if (newRead)
+ newRead->CurrentContext = newCtx;
+
+ newCtx->DrawSurface = newDraw;
+ newCtx->ReadSurface = newRead;
}
- _eglGlobal.CurrentContext = ctx;
+ /* an old context or surface is not orphaned if it is still bound */
+ if (*ctx == newCtx)
+ *ctx = NULL;
+ if (*draw == newDraw || *draw == newRead)
+ *draw = NULL;
+ if (*read == newDraw || *read == newRead)
+ *read = NULL;
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.
+ */
+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;
+}