eglcurrent.c revision b8e80941
1/************************************************************************** 2 * 3 * Copyright 2009-2010 Chia-I Wu <olvaffe@gmail.com> 4 * All Rights Reserved. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the 8 * "Software"), to deal in the Software without restriction, including 9 * without limitation the rights to use, copy, modify, merge, publish, 10 * distribute, sub license, and/or sell copies of the Software, and to 11 * permit persons to whom the Software is furnished to do so, subject to 12 * the following conditions: 13 * 14 * The above copyright notice and this permission notice (including the 15 * next paragraph) shall be included in all copies or substantial portions 16 * of the Software. 17 * 18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 24 * DEALINGS IN THE SOFTWARE. 25 * 26 **************************************************************************/ 27 28 29#include <stdio.h> 30#include <stdlib.h> 31#include <string.h> 32#include <stdarg.h> 33#include "c99_compat.h" 34#include "c11/threads.h" 35 36#include "egllog.h" 37#include "eglcurrent.h" 38#include "eglglobals.h" 39 40/* a fallback thread info to guarantee that every thread always has one */ 41static _EGLThreadInfo dummy_thread; 42static mtx_t _egl_TSDMutex = _MTX_INITIALIZER_NP; 43static EGLBoolean _egl_TSDInitialized; 44static tss_t _egl_TSD; 45static void _eglDestroyThreadInfo(_EGLThreadInfo *t); 46 47#ifdef GLX_USE_TLS 48static __thread const _EGLThreadInfo *_egl_TLS 49 __attribute__ ((tls_model("initial-exec"))); 50#endif 51 52static inline void _eglSetTSD(const _EGLThreadInfo *t) 53{ 54 tss_set(_egl_TSD, (void *) t); 55#ifdef GLX_USE_TLS 56 _egl_TLS = t; 57#endif 58} 59 60static inline _EGLThreadInfo *_eglGetTSD(void) 61{ 62#ifdef GLX_USE_TLS 63 return (_EGLThreadInfo *) _egl_TLS; 64#else 65 return (_EGLThreadInfo *) tss_get(_egl_TSD); 66#endif 67} 68 69static inline void _eglFiniTSD(void) 70{ 71 mtx_lock(&_egl_TSDMutex); 72 if (_egl_TSDInitialized) { 73 _EGLThreadInfo *t = _eglGetTSD(); 74 75 _egl_TSDInitialized = EGL_FALSE; 76 _eglDestroyThreadInfo(t); 77 tss_delete(_egl_TSD); 78 } 79 mtx_unlock(&_egl_TSDMutex); 80} 81 82static inline EGLBoolean _eglInitTSD() 83{ 84 if (!_egl_TSDInitialized) { 85 mtx_lock(&_egl_TSDMutex); 86 87 /* check again after acquiring lock */ 88 if (!_egl_TSDInitialized) { 89 if (tss_create(&_egl_TSD, (void (*)(void *)) _eglDestroyThreadInfo) != thrd_success) { 90 mtx_unlock(&_egl_TSDMutex); 91 return EGL_FALSE; 92 } 93 _eglAddAtExitCall(_eglFiniTSD); 94 _egl_TSDInitialized = EGL_TRUE; 95 } 96 97 mtx_unlock(&_egl_TSDMutex); 98 } 99 100 return EGL_TRUE; 101} 102 103static void 104_eglInitThreadInfo(_EGLThreadInfo *t) 105{ 106 t->LastError = EGL_SUCCESS; 107 /* default, per EGL spec */ 108 t->CurrentAPI = EGL_OPENGL_ES_API; 109} 110 111 112/** 113 * Allocate and init a new _EGLThreadInfo object. 114 */ 115static _EGLThreadInfo * 116_eglCreateThreadInfo(void) 117{ 118 _EGLThreadInfo *t = calloc(1, sizeof(_EGLThreadInfo)); 119 if (!t) 120 t = &dummy_thread; 121 122 _eglInitThreadInfo(t); 123 return t; 124} 125 126 127/** 128 * Delete/free a _EGLThreadInfo object. 129 */ 130static void 131_eglDestroyThreadInfo(_EGLThreadInfo *t) 132{ 133 if (t != &dummy_thread) 134 free(t); 135} 136 137 138/** 139 * Make sure TSD is initialized and return current value. 140 */ 141static inline _EGLThreadInfo * 142_eglCheckedGetTSD(void) 143{ 144 if (_eglInitTSD() != EGL_TRUE) { 145 _eglLog(_EGL_FATAL, "failed to initialize \"current\" system"); 146 return NULL; 147 } 148 149 return _eglGetTSD(); 150} 151 152 153/** 154 * Return the calling thread's thread info. 155 * If the calling thread nevers calls this function before, or if its thread 156 * info was destroyed, a new one is created. This function never returns NULL. 157 * In the case allocation fails, a dummy one is returned. See also 158 * _eglIsCurrentThreadDummy. 159 */ 160_EGLThreadInfo * 161_eglGetCurrentThread(void) 162{ 163 _EGLThreadInfo *t = _eglCheckedGetTSD(); 164 if (!t) { 165 t = _eglCreateThreadInfo(); 166 _eglSetTSD(t); 167 } 168 169 return t; 170} 171 172 173/** 174 * Destroy the calling thread's thread info. 175 */ 176void 177_eglDestroyCurrentThread(void) 178{ 179 _EGLThreadInfo *t = _eglCheckedGetTSD(); 180 if (t) { 181 _eglDestroyThreadInfo(t); 182 _eglSetTSD(NULL); 183 } 184} 185 186 187/** 188 * Return true if the calling thread's thread info is dummy. 189 * A dummy thread info is shared by all threads and should not be modified. 190 * Functions like eglBindAPI or eglMakeCurrent should check for dummy-ness 191 * before updating the thread info. 192 */ 193EGLBoolean 194_eglIsCurrentThreadDummy(void) 195{ 196 _EGLThreadInfo *t = _eglCheckedGetTSD(); 197 return (!t || t == &dummy_thread); 198} 199 200 201/** 202 * Return the currently bound context of the current API, or NULL. 203 */ 204_EGLContext * 205_eglGetCurrentContext(void) 206{ 207 _EGLThreadInfo *t = _eglGetCurrentThread(); 208 return t->CurrentContext; 209} 210 211 212/** 213 * Record EGL error code and return EGL_FALSE. 214 */ 215static EGLBoolean 216_eglInternalError(EGLint errCode, const char *msg) 217{ 218 _EGLThreadInfo *t = _eglGetCurrentThread(); 219 220 if (t == &dummy_thread) 221 return EGL_FALSE; 222 223 t->LastError = errCode; 224 225 if (errCode != EGL_SUCCESS) { 226 const char *s; 227 228 switch (errCode) { 229 case EGL_BAD_ACCESS: 230 s = "EGL_BAD_ACCESS"; 231 break; 232 case EGL_BAD_ALLOC: 233 s = "EGL_BAD_ALLOC"; 234 break; 235 case EGL_BAD_ATTRIBUTE: 236 s = "EGL_BAD_ATTRIBUTE"; 237 break; 238 case EGL_BAD_CONFIG: 239 s = "EGL_BAD_CONFIG"; 240 break; 241 case EGL_BAD_CONTEXT: 242 s = "EGL_BAD_CONTEXT"; 243 break; 244 case EGL_BAD_CURRENT_SURFACE: 245 s = "EGL_BAD_CURRENT_SURFACE"; 246 break; 247 case EGL_BAD_DISPLAY: 248 s = "EGL_BAD_DISPLAY"; 249 break; 250 case EGL_BAD_MATCH: 251 s = "EGL_BAD_MATCH"; 252 break; 253 case EGL_BAD_NATIVE_PIXMAP: 254 s = "EGL_BAD_NATIVE_PIXMAP"; 255 break; 256 case EGL_BAD_NATIVE_WINDOW: 257 s = "EGL_BAD_NATIVE_WINDOW"; 258 break; 259 case EGL_BAD_PARAMETER: 260 s = "EGL_BAD_PARAMETER"; 261 break; 262 case EGL_BAD_SURFACE: 263 s = "EGL_BAD_SURFACE"; 264 break; 265 case EGL_NOT_INITIALIZED: 266 s = "EGL_NOT_INITIALIZED"; 267 break; 268 default: 269 s = "other EGL error"; 270 } 271 _eglLog(_EGL_DEBUG, "EGL user error 0x%x (%s) in %s\n", errCode, s, msg); 272 } 273 274 return EGL_FALSE; 275} 276 277EGLBoolean 278_eglError(EGLint errCode, const char *msg) 279{ 280 if (errCode != EGL_SUCCESS) { 281 EGLint type; 282 if (errCode == EGL_BAD_ALLOC) 283 type = EGL_DEBUG_MSG_CRITICAL_KHR; 284 else 285 type = EGL_DEBUG_MSG_ERROR_KHR; 286 287 _eglDebugReport(errCode, NULL, type, msg); 288 } else 289 _eglInternalError(errCode, msg); 290 291 return EGL_FALSE; 292} 293 294void 295_eglDebugReport(EGLenum error, const char *funcName, 296 EGLint type, const char *message, ...) 297{ 298 _EGLThreadInfo *thr = _eglGetCurrentThread(); 299 EGLDEBUGPROCKHR callback = NULL; 300 va_list args; 301 302 if (funcName == NULL) 303 funcName = thr->CurrentFuncName; 304 305 mtx_lock(_eglGlobal.Mutex); 306 if (_eglGlobal.debugTypesEnabled & DebugBitFromType(type)) 307 callback = _eglGlobal.debugCallback; 308 309 mtx_unlock(_eglGlobal.Mutex); 310 311 char *message_buf = NULL; 312 if (message != NULL) { 313 va_start(args, message); 314 if (vasprintf(&message_buf, message, args) < 0) 315 message_buf = NULL; 316 va_end(args); 317 } 318 319 if (callback != NULL) { 320 callback(error, funcName, type, thr->Label, thr->CurrentObjectLabel, 321 message_buf); 322 } 323 324 if (type == EGL_DEBUG_MSG_CRITICAL_KHR || type == EGL_DEBUG_MSG_ERROR_KHR) { 325 char *func_message_buf = NULL; 326 /* Note: _eglError() is often called with msg == thr->currentFuncName */ 327 if (message_buf && funcName && strcmp(message_buf, funcName) != 0) { 328 if (asprintf(&func_message_buf, "%s: %s", funcName, message_buf) < 0) 329 func_message_buf = NULL; 330 } 331 _eglInternalError(error, func_message_buf ? func_message_buf : funcName); 332 free(func_message_buf); 333 } 334 free(message_buf); 335} 336