X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fegl%2Fmain%2Feglcontext.c;h=b094f49bfc16fcfd332db77cb99cf27dd0ee3675;hb=ea9e5dbbc2e992ead954d3d2ebf3689f7a003f79;hp=1d12a405cfc896f20b7a009015b3ef15556f9502;hpb=7012d01d888d482f2c6ad1180231a482026d213a;p=mesa.git diff --git a/src/egl/main/eglcontext.c b/src/egl/main/eglcontext.c index 1d12a405cfc..bc22913d401 100644 --- a/src/egl/main/eglcontext.c +++ b/src/egl/main/eglcontext.c @@ -3,224 +3,399 @@ #include #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; +}