eglcontext.c revision b50703aea55450e04bcd8154335774786e0f253b
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 161static EGLint 162_eglQueryContextRenderBuffer(_EGLContext *ctx) 163{ 164 _EGLSurface *surf = ctx->DrawSurface; 165 EGLint rb; 166 167 if (!surf) 168 return EGL_NONE; 169 if (surf->Type == EGL_WINDOW_BIT && ctx->WindowRenderBuffer != EGL_NONE) 170 rb = ctx->WindowRenderBuffer; 171 else 172 rb = surf->RenderBuffer; 173 return rb; 174} 175 176 177EGLBoolean 178_eglQueryContext(_EGLDriver *drv, _EGLDisplay *dpy, _EGLContext *c, 179 EGLint attribute, EGLint *value) 180{ 181 (void) drv; 182 (void) dpy; 183 184 if (!value) 185 return _eglError(EGL_BAD_PARAMETER, "eglQueryContext"); 186 187 switch (attribute) { 188 case EGL_CONFIG_ID: 189 if (!c->Config) 190 return _eglError(EGL_BAD_ATTRIBUTE, "eglQueryContext"); 191 *value = c->Config->ConfigID; 192 break; 193 case EGL_CONTEXT_CLIENT_VERSION: 194 *value = c->ClientVersion; 195 break; 196 case EGL_CONTEXT_CLIENT_TYPE: 197 *value = c->ClientAPI; 198 break; 199 case EGL_RENDER_BUFFER: 200 *value = _eglQueryContextRenderBuffer(c); 201 break; 202 default: 203 return _eglError(EGL_BAD_ATTRIBUTE, "eglQueryContext"); 204 } 205 206 return EGL_TRUE; 207} 208 209 210/** 211 * Bind the context to the thread and return the previous context. 212 * 213 * Note that the context may be NULL. 214 */ 215static _EGLContext * 216_eglBindContextToThread(_EGLContext *ctx, _EGLThreadInfo *t) 217{ 218 EGLint apiIndex; 219 _EGLContext *oldCtx; 220 221 apiIndex = (ctx) ? 222 _eglConvertApiToIndex(ctx->ClientAPI) : t->CurrentAPIIndex; 223 224 oldCtx = t->CurrentContexts[apiIndex]; 225 if (ctx != oldCtx) { 226 if (oldCtx) 227 oldCtx->Binding = NULL; 228 if (ctx) 229 ctx->Binding = t; 230 231 t->CurrentContexts[apiIndex] = ctx; 232 } 233 234 return oldCtx; 235} 236 237 238/** 239 * Return true if the given context and surfaces can be made current. 240 */ 241static EGLBoolean 242_eglCheckMakeCurrent(_EGLContext *ctx, _EGLSurface *draw, _EGLSurface *read) 243{ 244 _EGLThreadInfo *t = _eglGetCurrentThread(); 245 _EGLDisplay *dpy; 246 EGLint conflict_api; 247 248 if (_eglIsCurrentThreadDummy()) 249 return _eglError(EGL_BAD_ALLOC, "eglMakeCurrent"); 250 251 /* this is easy */ 252 if (!ctx) { 253 if (draw || read) 254 return _eglError(EGL_BAD_MATCH, "eglMakeCurrent"); 255 return EGL_TRUE; 256 } 257 258 dpy = ctx->Resource.Display; 259 if (!dpy->Extensions.KHR_surfaceless_context 260 && (draw == NULL || read == NULL)) 261 return _eglError(EGL_BAD_MATCH, "eglMakeCurrent"); 262 263 /* 264 * The spec says 265 * 266 * "If ctx is current to some other thread, or if either draw or read are 267 * bound to contexts in another thread, an EGL_BAD_ACCESS error is 268 * generated." 269 * 270 * and 271 * 272 * "at most one context may be bound to a particular surface at a given 273 * time" 274 */ 275 if (ctx->Binding && ctx->Binding != t) 276 return _eglError(EGL_BAD_ACCESS, "eglMakeCurrent"); 277 if (draw && draw->CurrentContext && draw->CurrentContext != ctx) { 278 if (draw->CurrentContext->Binding != t || 279 draw->CurrentContext->ClientAPI != ctx->ClientAPI) 280 return _eglError(EGL_BAD_ACCESS, "eglMakeCurrent"); 281 } 282 if (read && read->CurrentContext && read->CurrentContext != ctx) { 283 if (read->CurrentContext->Binding != t || 284 read->CurrentContext->ClientAPI != ctx->ClientAPI) 285 return _eglError(EGL_BAD_ACCESS, "eglMakeCurrent"); 286 } 287 288 /* simply require the configs to be equal */ 289 if ((draw && draw->Config != ctx->Config) || 290 (read && read->Config != ctx->Config)) 291 return _eglError(EGL_BAD_MATCH, "eglMakeCurrent"); 292 293 switch (ctx->ClientAPI) { 294 /* OpenGL and OpenGL ES are conflicting */ 295 case EGL_OPENGL_ES_API: 296 conflict_api = EGL_OPENGL_API; 297 break; 298 case EGL_OPENGL_API: 299 conflict_api = EGL_OPENGL_ES_API; 300 break; 301 default: 302 conflict_api = -1; 303 break; 304 } 305 306 if (conflict_api >= 0 && _eglGetAPIContext(conflict_api)) 307 return _eglError(EGL_BAD_ACCESS, "eglMakeCurrent"); 308 309 return EGL_TRUE; 310} 311 312 313/** 314 * Bind the context to the current thread and given surfaces. Return the 315 * previous bound context and surfaces. The caller should unreference the 316 * returned context and surfaces. 317 * 318 * Making a second call with the resources returned by the first call 319 * unsurprisingly undoes the first call, except for the resouce reference 320 * counts. 321 */ 322EGLBoolean 323_eglBindContext(_EGLContext *ctx, _EGLSurface *draw, _EGLSurface *read, 324 _EGLContext **old_ctx, 325 _EGLSurface **old_draw, _EGLSurface **old_read) 326{ 327 _EGLThreadInfo *t = _eglGetCurrentThread(); 328 _EGLContext *prev_ctx; 329 _EGLSurface *prev_draw, *prev_read; 330 331 if (!_eglCheckMakeCurrent(ctx, draw, read)) 332 return EGL_FALSE; 333 334 /* increment refcounts before binding */ 335 _eglGetContext(ctx); 336 _eglGetSurface(draw); 337 _eglGetSurface(read); 338 339 /* bind the new context */ 340 prev_ctx = _eglBindContextToThread(ctx, t); 341 342 /* break previous bindings */ 343 if (prev_ctx) { 344 prev_draw = prev_ctx->DrawSurface; 345 prev_read = prev_ctx->ReadSurface; 346 347 if (prev_draw) 348 prev_draw->CurrentContext = NULL; 349 if (prev_read) 350 prev_read->CurrentContext = NULL; 351 352 prev_ctx->DrawSurface = NULL; 353 prev_ctx->ReadSurface = NULL; 354 } 355 else { 356 prev_draw = prev_read = NULL; 357 } 358 359 /* establish new bindings */ 360 if (ctx) { 361 if (draw) 362 draw->CurrentContext = ctx; 363 if (read) 364 read->CurrentContext = ctx; 365 366 ctx->DrawSurface = draw; 367 ctx->ReadSurface = read; 368 } 369 370 assert(old_ctx && old_draw && old_read); 371 *old_ctx = prev_ctx; 372 *old_draw = prev_draw; 373 *old_read = prev_read; 374 375 return EGL_TRUE; 376} 377