diff options
Diffstat (limited to 'src/egl/main/eglcontext.c')
-rw-r--r-- | src/egl/main/eglcontext.c | 368 |
1 files changed, 256 insertions, 112 deletions
diff --git a/src/egl/main/eglcontext.c b/src/egl/main/eglcontext.c index ee4b1b59f5b..012d8dfe1f4 100644 --- a/src/egl/main/eglcontext.c +++ b/src/egl/main/eglcontext.c @@ -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; +} /** @@ -14,11 +101,11 @@ * 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,99 +203,169 @@ _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 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. */ -EGLBoolean -_eglMakeCurrent(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSurface *draw, - _EGLSurface *read, _EGLContext *ctx) +static void +_eglBindContextToSurfaces(_EGLContext *ctx, + _EGLSurface **draw, _EGLSurface **read) +{ + _EGLSurface *newDraw = *draw, *newRead = *read; + + if (newDraw->CurrentContext) + newDraw->CurrentContext->DrawSurface = NULL; + newDraw->CurrentContext = ctx; + + if (newRead->CurrentContext) + newRead->CurrentContext->ReadSurface = NULL; + newRead->CurrentContext = ctx; + + if (ctx) { + *draw = ctx->DrawSurface; + ctx->DrawSurface = newDraw; + + *read = ctx->ReadSurface; + ctx->ReadSurface = newRead; + } +} + + +/** + * 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) + /* this is easy */ + if (!ctx) { + if (draw || read) return _eglError(EGL_BAD_MATCH, "eglMakeCurrent"); - if (draw->Config != ctx->Config || read->Config != ctx->Config) - 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->CurrentContext && draw->CurrentContext != ctx) || + (read->CurrentContext && read->CurrentContext != 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; + if (conflict_api >= 0 && _eglGetAPIContext(conflict_api)) + return _eglError(EGL_BAD_ACCESS, "eglMakeCurrent"); + + return EGL_TRUE; +} + + +/** + * 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 +_eglBindContext(_EGLContext **ctx, _EGLSurface **draw, _EGLSurface **read) +{ + _EGLThreadInfo *t = _eglGetCurrentThread(); + _EGLContext *newCtx = *ctx, *oldCtx; + + if (!_eglCheckMakeCurrent(newCtx, *draw, *read)) + return EGL_FALSE; + + /* bind the new context */ + oldCtx = _eglBindContextToThread(newCtx, t); + *ctx = oldCtx; + if (newCtx) + _eglBindContextToSurfaces(newCtx, draw, read); + /* unbind the old context from its binding surfaces */ + if (oldCtx) { /* - * check if the old context or surfaces need to be deleted + * If the new context replaces some old context, the new one should not + * be current before the replacement and it should not be bound to any + * surface. */ - 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 (newCtx) + assert(!*draw && !*read); - /* build new bindings */ - if (ctx) { - t->CurrentContexts[apiIndex] = ctx; - ctx->Binding = t; - ctx->DrawSurface = draw; - ctx->ReadSurface = read; - draw->Binding = ctx; - read->Binding = ctx; + *draw = oldCtx->DrawSurface; + *read = oldCtx->ReadSurface; + assert(*draw && *read); + + _eglBindContextToSurfaces(NULL, draw, read); } return EGL_TRUE; @@ -240,6 +373,17 @@ _eglMakeCurrent(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSurface *draw, /** + * 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 |