/**************************************************************************
*
* 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 "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->ClientMajorVersion) {
case 1:
bit = EGL_OPENGL_ES_BIT;
break;
case 2:
case 3:
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, _EGLDisplay *dpy,
const EGLint *attrib_list)
{
EGLenum api = ctx->ClientAPI;
EGLint i, err = EGL_SUCCESS;
if (!attrib_list)
return EGL_SUCCESS;
if (api == EGL_OPENVG_API && attrib_list[0] != EGL_NONE) {
_eglLog(_EGL_DEBUG, "bad context attribute 0x%04x", attrib_list[0]);
return EGL_BAD_ATTRIBUTE;
}
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:
ctx->ClientMajorVersion = val;
break;
case EGL_CONTEXT_MINOR_VERSION_KHR:
if (!dpy->Extensions.KHR_create_context) {
err = EGL_BAD_ATTRIBUTE;
break;
}
ctx->ClientMinorVersion = val;
break;
case EGL_CONTEXT_FLAGS_KHR:
if (!dpy->Extensions.KHR_create_context) {
err = EGL_BAD_ATTRIBUTE;
break;
}
/* The EGL_KHR_create_context spec says:
*
* "Flags are only defined for OpenGL context creation, and
* specifying a flags value other than zero for other types of
* contexts, including OpenGL ES contexts, will generate an
* error."
*/
if (api != EGL_OPENGL_API && val != 0) {
err = EGL_BAD_ATTRIBUTE;
break;
}
ctx->Flags = val;
break;
case EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR:
if (!dpy->Extensions.KHR_create_context) {
err = EGL_BAD_ATTRIBUTE;
break;
}
/* The EGL_KHR_create_context spec says:
*
* "[EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR] is only meaningful for
* OpenGL contexts, and specifying it for other types of
* contexts, including OpenGL ES contexts, will generate an
* error."
*/
if (api != EGL_OPENGL_API) {
err = EGL_BAD_ATTRIBUTE;
break;
}
ctx->Profile = val;
break;
case EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR:
/* The EGL_KHR_create_context spec says:
*
* "[EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR] is only
* meaningful for OpenGL contexts, and specifying it for other
* types of contexts, including OpenGL ES contexts, will generate
* an error."
*/
if (!dpy->Extensions.KHR_create_context
|| api != EGL_OPENGL_API) {
err = EGL_BAD_ATTRIBUTE;
break;
}
ctx->ResetNotificationStrategy = val;
break;
case EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT:
/* The EGL_EXT_create_context_robustness spec says:
*
* "[EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT] is only
* meaningful for OpenGL ES contexts, and specifying it for other
* types of contexts will generate an EGL_BAD_ATTRIBUTE error."
*/
if (!dpy->Extensions.EXT_create_context_robustness
|| api != EGL_OPENGL_ES_API) {
err = EGL_BAD_ATTRIBUTE;
break;
}
ctx->ResetNotificationStrategy = val;
break;
case EGL_CONTEXT_OPENGL_ROBUST_ACCESS_EXT:
if (!dpy->Extensions.EXT_create_context_robustness) {
err = EGL_BAD_ATTRIBUTE;
break;
}
ctx->Flags = EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR;
break;
default:
err = EGL_BAD_ATTRIBUTE;
break;
}
if (err != EGL_SUCCESS) {
_eglLog(_EGL_DEBUG, "bad context attribute 0x%04x", attr);
break;
}
}
if (api == EGL_OPENGL_API) {
/* The EGL_KHR_create_context spec says:
*
* "If the requested OpenGL version is less than 3.2,
* EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR is ignored and the
* functionality of the context is determined solely by the
* requested version."
*
* Since the value is ignored, only validate the setting if the version
* is >= 3.2.
*/
if (ctx->ClientMajorVersion >= 4
|| (ctx->ClientMajorVersion == 3 && ctx->ClientMinorVersion >= 2)) {
switch (ctx->Profile) {
case EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR:
case EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR:
break;
default:
/* The EGL_KHR_create_context spec says:
*
* "* If an OpenGL context is requested, the requested version
* is greater than 3.2, and the value for attribute
* EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR has no bits set; has
* any bits set other than EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR
* and EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR; has
* more than one of these bits set; or if the implementation does
* not support the requested profile, then an EGL_BAD_MATCH error
* is generated."
*/
err = EGL_BAD_MATCH;
break;
}
}
/* The EGL_KHR_create_context spec says:
*
* "* If an OpenGL context is requested and the values for
* attributes EGL_CONTEXT_MAJOR_VERSION_KHR and
* EGL_CONTEXT_MINOR_VERSION_KHR, when considered together with
* the value for attribute
* EGL_CONTEXT_FORWARD_COMPATIBLE_BIT_KHR, specify an OpenGL
* version and feature set that are not defined, than an
* EGL_BAD_MATCH error is generated.
*
* ... Thus, examples of invalid combinations of attributes
* include:
*
* - Major version < 1 or > 4
* - Major version == 1 and minor version < 0 or > 5
* - Major version == 2 and minor version < 0 or > 1
* - Major version == 3 and minor version < 0 or > 2
* - Major version == 4 and minor version < 0 or > 2
* - Forward-compatible flag set and major version < 3"
*/
if (ctx->ClientMajorVersion < 1 || ctx->ClientMinorVersion < 0)
err = EGL_BAD_MATCH;
switch (ctx->ClientMajorVersion) {
case 1:
if (ctx->ClientMinorVersion > 5
|| (ctx->Flags & EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR) != 0)
err = EGL_BAD_MATCH;
break;
case 2:
if (ctx->ClientMinorVersion > 1
|| (ctx->Flags & EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR) != 0)
err = EGL_BAD_MATCH;
break;
case 3:
/* Note: The text above is incorrect. There *is* an OpenGL 3.3!
*/
if (ctx->ClientMinorVersion > 3)
err = EGL_BAD_MATCH;
break;
case 4:
default:
/* Don't put additional version checks here. We don't know that
* there won't be versions > 4.2.
*/
break;
}
} else if (api == EGL_OPENGL_ES_API) {
/* The EGL_KHR_create_context spec says:
*
* "* If an OpenGL ES context is requested and the values for
* attributes EGL_CONTEXT_MAJOR_VERSION_KHR and
* EGL_CONTEXT_MINOR_VERSION_KHR specify an OpenGL ES version that
* is not defined, than an EGL_BAD_MATCH error is generated.
*
* ... Examples of invalid combinations of attributes include:
*
* - Major version < 1 or > 2
* - Major version == 1 and minor version < 0 or > 1
* - Major version == 2 and minor version != 0
*/
if (ctx->ClientMajorVersion < 1 || ctx->ClientMinorVersion < 0)
err = EGL_BAD_MATCH;
switch (ctx->ClientMajorVersion) {
case 1:
if (ctx->ClientMinorVersion > 1)
err = EGL_BAD_MATCH;
break;
case 2:
if (ctx->ClientMinorVersion > 0)
err = EGL_BAD_MATCH;
break;
case 3:
default:
/* Don't put additional version checks here. We don't know that
* there won't be versions > 3.0.
*/
break;
}
}
switch (ctx->ResetNotificationStrategy) {
case EGL_NO_RESET_NOTIFICATION_KHR:
case EGL_LOSE_CONTEXT_ON_RESET_KHR:
break;
default:
err = EGL_BAD_ATTRIBUTE;
break;
}
if ((ctx->Flags & ~(EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR
| EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR
| EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR)) != 0) {
err = EGL_BAD_ATTRIBUTE;
}
return err;
}
/**
* Initialize the given _EGLContext object to defaults and/or the values
* in the attrib_list.
*/
EGLBoolean
_eglInitContext(_EGLContext *ctx, _EGLDisplay *dpy, _EGLConfig *conf,
const EGLint *attrib_list)
{
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->Profile = EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR;
ctx->ClientMajorVersion = 1; /* the default, per EGL spec */
ctx->ClientMinorVersion = 0;
ctx->Flags = 0;
ctx->Profile = EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR;
ctx->ResetNotificationStrategy = EGL_NO_RESET_NOTIFICATION_KHR;
err = _eglParseContextAttribList(ctx, dpy, 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;
}
static EGLint
_eglQueryContextRenderBuffer(_EGLContext *ctx)
{
_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;
}
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:
if (!c->Config)
return _eglError(EGL_BAD_ATTRIBUTE, "eglQueryContext");
*value = c->Config->ConfigID;
break;
case EGL_CONTEXT_CLIENT_VERSION:
*value = c->ClientMajorVersion;
break;
case EGL_CONTEXT_CLIENT_TYPE:
*value = c->ClientAPI;
break;
case EGL_RENDER_BUFFER:
*value = _eglQueryContextRenderBuffer(c);
break;
default:
return _eglError(EGL_BAD_ATTRIBUTE, "eglQueryContext");
}
return EGL_TRUE;
}
/**
* 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)
{
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;
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;
}
dpy = ctx->Resource.Display;
if (!dpy->Extensions.KHR_surfaceless_context
&& (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");
}
/* 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) {
/* 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;
default:
conflict_api = -1;
break;
}
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
* 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
_eglBindContext(_EGLContext *ctx, _EGLSurface *draw, _EGLSurface *read,
_EGLContext **old_ctx,
_EGLSurface **old_draw, _EGLSurface **old_read)
{
_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;
}