eglcontext.c revision 3464ebd5
1/************************************************************************** 2 * 3 * Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Texas. 4 * Copyright 2009-2010 Chia-I Wu <olvaffe@gmail.com> 5 * Copyright 2010-2011 LunarG, Inc. 6 * All Rights Reserved. 7 * 8 * Permission is hereby granted, free of charge, to any person obtaining a 9 * copy of this software and associated documentation files (the 10 * "Software"), to deal in the Software without restriction, including 11 * without limitation the rights to use, copy, modify, merge, publish, 12 * distribute, sub license, and/or sell copies of the Software, and to 13 * permit persons to whom the Software is furnished to do so, subject to 14 * the following conditions: 15 * 16 * The above copyright notice and this permission notice (including the 17 * next paragraph) shall be included in all copies or substantial portions 18 * of the Software. 19 * 20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 23 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 26 * DEALINGS IN THE SOFTWARE. 27 * 28 **************************************************************************/ 29 30 31#include <assert.h> 32#include <stdlib.h> 33#include <string.h> 34#include "eglconfig.h" 35#include "eglcontext.h" 36#include "egldisplay.h" 37#include "eglcurrent.h" 38#include "eglsurface.h" 39#include "egllog.h" 40 41 42/** 43 * Return the API bit (one of EGL_xxx_BIT) of the context. 44 */ 45static EGLint 46_eglGetContextAPIBit(_EGLContext *ctx) 47{ 48 EGLint bit = 0; 49 50 switch (ctx->ClientAPI) { 51 case EGL_OPENGL_ES_API: 52 switch (ctx->ClientVersion) { 53 case 1: 54 bit = EGL_OPENGL_ES_BIT; 55 break; 56 case 2: 57 bit = EGL_OPENGL_ES2_BIT; 58 break; 59 default: 60 break; 61 } 62 break; 63 case EGL_OPENVG_API: 64 bit = EGL_OPENVG_BIT; 65 break; 66 case EGL_OPENGL_API: 67 bit = EGL_OPENGL_BIT; 68 break; 69 default: 70 break; 71 } 72 73 return bit; 74} 75 76 77/** 78 * Parse the list of context attributes and return the proper error code. 79 */ 80static EGLint 81_eglParseContextAttribList(_EGLContext *ctx, const EGLint *attrib_list) 82{ 83 EGLenum api = ctx->ClientAPI; 84 EGLint i, err = EGL_SUCCESS; 85 86 if (!attrib_list) 87 return EGL_SUCCESS; 88 89 for (i = 0; attrib_list[i] != EGL_NONE; i++) { 90 EGLint attr = attrib_list[i++]; 91 EGLint val = attrib_list[i]; 92 93 switch (attr) { 94 case EGL_CONTEXT_CLIENT_VERSION: 95 if (api != EGL_OPENGL_ES_API) { 96 err = EGL_BAD_ATTRIBUTE; 97 break; 98 } 99 if (val != 1 && val != 2) { 100 err = EGL_BAD_ATTRIBUTE; 101 break; 102 } 103 ctx->ClientVersion = val; 104 break; 105 default: 106 err = EGL_BAD_ATTRIBUTE; 107 break; 108 } 109 110 if (err != EGL_SUCCESS) { 111 _eglLog(_EGL_DEBUG, "bad context attribute 0x%04x", attr); 112 break; 113 } 114 } 115 116 return err; 117} 118 119 120/** 121 * Initialize the given _EGLContext object to defaults and/or the values 122 * in the attrib_list. 123 */ 124EGLBoolean 125_eglInitContext(_EGLContext *ctx, _EGLDisplay *dpy, _EGLConfig *conf, 126 const EGLint *attrib_list) 127{ 128 const EGLenum api = eglQueryAPI(); 129 EGLint err; 130 131 if (api == EGL_NONE) { 132 _eglError(EGL_BAD_MATCH, "eglCreateContext(no client API)"); 133 return EGL_FALSE; 134 } 135 136 _eglInitResource(&ctx->Resource, sizeof(*ctx), dpy); 137 ctx->ClientAPI = api; 138 ctx->Config = conf; 139 ctx->WindowRenderBuffer = EGL_NONE; 140 141 ctx->ClientVersion = 1; /* the default, per EGL spec */ 142 143 err = _eglParseContextAttribList(ctx, attrib_list); 144 if (err == EGL_SUCCESS && ctx->Config) { 145 EGLint api_bit; 146 147 api_bit = _eglGetContextAPIBit(ctx); 148 if (!(ctx->Config->RenderableType & api_bit)) { 149 _eglLog(_EGL_DEBUG, "context api is 0x%x while config supports 0x%x", 150 api_bit, ctx->Config->RenderableType); 151 err = EGL_BAD_CONFIG; 152 } 153 } 154 if (err != EGL_SUCCESS) 155 return _eglError(err, "eglCreateContext"); 156 157 return EGL_TRUE; 158} 159 160 161#ifdef EGL_VERSION_1_2 162static EGLint 163_eglQueryContextRenderBuffer(_EGLContext *ctx) 164{ 165 _EGLSurface *surf = ctx->DrawSurface; 166 EGLint rb; 167 168 if (!surf) 169 return EGL_NONE; 170 if (surf->Type == EGL_WINDOW_BIT && ctx->WindowRenderBuffer != EGL_NONE) 171 rb = ctx->WindowRenderBuffer; 172 else 173 rb = surf->RenderBuffer; 174 return rb; 175} 176#endif /* EGL_VERSION_1_2 */ 177 178 179EGLBoolean 180_eglQueryContext(_EGLDriver *drv, _EGLDisplay *dpy, _EGLContext *c, 181 EGLint attribute, EGLint *value) 182{ 183 (void) drv; 184 (void) dpy; 185 186 if (!value) 187 return _eglError(EGL_BAD_PARAMETER, "eglQueryContext"); 188 189 switch (attribute) { 190 case EGL_CONFIG_ID: 191 if (!c->Config) 192 return _eglError(EGL_BAD_ATTRIBUTE, "eglQueryContext"); 193 *value = c->Config->ConfigID; 194 break; 195 case EGL_CONTEXT_CLIENT_VERSION: 196 *value = c->ClientVersion; 197 break; 198#ifdef EGL_VERSION_1_2 199 case EGL_CONTEXT_CLIENT_TYPE: 200 *value = c->ClientAPI; 201 break; 202 case EGL_RENDER_BUFFER: 203 *value = _eglQueryContextRenderBuffer(c); 204 break; 205#endif /* EGL_VERSION_1_2 */ 206 default: 207 return _eglError(EGL_BAD_ATTRIBUTE, "eglQueryContext"); 208 } 209 210 return EGL_TRUE; 211} 212 213 214/** 215 * Bind the context to the thread and return the previous context. 216 * 217 * Note that the context may be NULL. 218 */ 219static _EGLContext * 220_eglBindContextToThread(_EGLContext *ctx, _EGLThreadInfo *t) 221{ 222 EGLint apiIndex; 223 _EGLContext *oldCtx; 224 225 apiIndex = (ctx) ? 226 _eglConvertApiToIndex(ctx->ClientAPI) : t->CurrentAPIIndex; 227 228 oldCtx = t->CurrentContexts[apiIndex]; 229 if (ctx != oldCtx) { 230 if (oldCtx) 231 oldCtx->Binding = NULL; 232 if (ctx) 233 ctx->Binding = t; 234 235 t->CurrentContexts[apiIndex] = ctx; 236 } 237 238 return oldCtx; 239} 240 241 242/** 243 * Return true if the given context and surfaces can be made current. 244 */ 245static EGLBoolean 246_eglCheckMakeCurrent(_EGLContext *ctx, _EGLSurface *draw, _EGLSurface *read) 247{ 248 _EGLThreadInfo *t = _eglGetCurrentThread(); 249 _EGLDisplay *dpy; 250 EGLint conflict_api; 251 EGLBoolean surfaceless; 252 253 if (_eglIsCurrentThreadDummy()) 254 return _eglError(EGL_BAD_ALLOC, "eglMakeCurrent"); 255 256 /* this is easy */ 257 if (!ctx) { 258 if (draw || read) 259 return _eglError(EGL_BAD_MATCH, "eglMakeCurrent"); 260 return EGL_TRUE; 261 } 262 263 dpy = ctx->Resource.Display; 264 switch (_eglGetContextAPIBit(ctx)) { 265 case EGL_OPENGL_ES_BIT: 266 surfaceless = dpy->Extensions.KHR_surfaceless_gles1; 267 break; 268 case EGL_OPENGL_ES2_BIT: 269 surfaceless = dpy->Extensions.KHR_surfaceless_gles2; 270 break; 271 case EGL_OPENGL_BIT: 272 surfaceless = dpy->Extensions.KHR_surfaceless_opengl; 273 break; 274 default: 275 surfaceless = EGL_FALSE; 276 break; 277 } 278 279 if (!surfaceless && (draw == NULL || read == NULL)) 280 return _eglError(EGL_BAD_MATCH, "eglMakeCurrent"); 281 282 /* 283 * The spec says 284 * 285 * "If ctx is current to some other thread, or if either draw or read are 286 * bound to contexts in another thread, an EGL_BAD_ACCESS error is 287 * generated." 288 * 289 * and 290 * 291 * "at most one context may be bound to a particular surface at a given 292 * time" 293 */ 294 if (ctx->Binding && ctx->Binding != t) 295 return _eglError(EGL_BAD_ACCESS, "eglMakeCurrent"); 296 if (draw && draw->CurrentContext && draw->CurrentContext != ctx) { 297 if (draw->CurrentContext->Binding != t || 298 draw->CurrentContext->ClientAPI != ctx->ClientAPI) 299 return _eglError(EGL_BAD_ACCESS, "eglMakeCurrent"); 300 } 301 if (read && read->CurrentContext && read->CurrentContext != ctx) { 302 if (read->CurrentContext->Binding != t || 303 read->CurrentContext->ClientAPI != ctx->ClientAPI) 304 return _eglError(EGL_BAD_ACCESS, "eglMakeCurrent"); 305 } 306 307 /* simply require the configs to be equal */ 308 if ((draw && draw->Config != ctx->Config) || 309 (read && read->Config != ctx->Config)) 310 return _eglError(EGL_BAD_MATCH, "eglMakeCurrent"); 311 312 switch (ctx->ClientAPI) { 313#ifdef EGL_VERSION_1_4 314 /* OpenGL and OpenGL ES are conflicting */ 315 case EGL_OPENGL_ES_API: 316 conflict_api = EGL_OPENGL_API; 317 break; 318 case EGL_OPENGL_API: 319 conflict_api = EGL_OPENGL_ES_API; 320 break; 321#endif 322 default: 323 conflict_api = -1; 324 break; 325 } 326 327 if (conflict_api >= 0 && _eglGetAPIContext(conflict_api)) 328 return _eglError(EGL_BAD_ACCESS, "eglMakeCurrent"); 329 330 return EGL_TRUE; 331} 332 333 334/** 335 * Bind the context to the current thread and given surfaces. Return the 336 * previous bound context and surfaces. The caller should unreference the 337 * returned context and surfaces. 338 * 339 * Making a second call with the resources returned by the first call 340 * unsurprisingly undoes the first call, except for the resouce reference 341 * counts. 342 */ 343EGLBoolean 344_eglBindContext(_EGLContext *ctx, _EGLSurface *draw, _EGLSurface *read, 345 _EGLContext **old_ctx, 346 _EGLSurface **old_draw, _EGLSurface **old_read) 347{ 348 _EGLThreadInfo *t = _eglGetCurrentThread(); 349 _EGLContext *prev_ctx; 350 _EGLSurface *prev_draw, *prev_read; 351 352 if (!_eglCheckMakeCurrent(ctx, draw, read)) 353 return EGL_FALSE; 354 355 /* increment refcounts before binding */ 356 _eglGetContext(ctx); 357 _eglGetSurface(draw); 358 _eglGetSurface(read); 359 360 /* bind the new context */ 361 prev_ctx = _eglBindContextToThread(ctx, t); 362 363 /* break previous bindings */ 364 if (prev_ctx) { 365 prev_draw = prev_ctx->DrawSurface; 366 prev_read = prev_ctx->ReadSurface; 367 368 if (prev_draw) 369 prev_draw->CurrentContext = NULL; 370 if (prev_read) 371 prev_read->CurrentContext = NULL; 372 373 prev_ctx->DrawSurface = NULL; 374 prev_ctx->ReadSurface = NULL; 375 } 376 else { 377 prev_draw = prev_read = NULL; 378 } 379 380 /* establish new bindings */ 381 if (ctx) { 382 if (draw) 383 draw->CurrentContext = ctx; 384 if (read) 385 read->CurrentContext = ctx; 386 387 ctx->DrawSurface = draw; 388 ctx->ReadSurface = read; 389 } 390 391 assert(old_ctx && old_draw && old_read); 392 *old_ctx = prev_ctx; 393 *old_draw = prev_draw; 394 *old_read = prev_read; 395 396 return EGL_TRUE; 397} 398