1 /************************************************************************** 2 * 3 * Copyright 2009-2010 Chia-I Wu <olvaffe (at) 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 /* This should be kept in sync with _eglInitThreadInfo() */ 41 #define _EGL_THREAD_INFO_INITIALIZER \ 42 { EGL_SUCCESS, NULL, EGL_OPENGL_ES_API, NULL, NULL, NULL } 43 44 /* a fallback thread info to guarantee that every thread always has one */ 45 static _EGLThreadInfo dummy_thread = _EGL_THREAD_INFO_INITIALIZER; 46 static mtx_t _egl_TSDMutex = _MTX_INITIALIZER_NP; 47 static EGLBoolean _egl_TSDInitialized; 48 static tss_t _egl_TSD; 49 static void (*_egl_FreeTSD)(_EGLThreadInfo *); 50 51 #ifdef GLX_USE_TLS 52 static __thread const _EGLThreadInfo *_egl_TLS 53 __attribute__ ((tls_model("initial-exec"))); 54 #endif 55 56 static inline void _eglSetTSD(const _EGLThreadInfo *t) 57 { 58 tss_set(_egl_TSD, (void *) t); 59 #ifdef GLX_USE_TLS 60 _egl_TLS = t; 61 #endif 62 } 63 64 static inline _EGLThreadInfo *_eglGetTSD(void) 65 { 66 #ifdef GLX_USE_TLS 67 return (_EGLThreadInfo *) _egl_TLS; 68 #else 69 return (_EGLThreadInfo *) tss_get(_egl_TSD); 70 #endif 71 } 72 73 static inline void _eglFiniTSD(void) 74 { 75 mtx_lock(&_egl_TSDMutex); 76 if (_egl_TSDInitialized) { 77 _EGLThreadInfo *t = _eglGetTSD(); 78 79 _egl_TSDInitialized = EGL_FALSE; 80 if (t && _egl_FreeTSD) 81 _egl_FreeTSD((void *) t); 82 tss_delete(_egl_TSD); 83 } 84 mtx_unlock(&_egl_TSDMutex); 85 } 86 87 static inline EGLBoolean _eglInitTSD(void (*dtor)(_EGLThreadInfo *)) 88 { 89 if (!_egl_TSDInitialized) { 90 mtx_lock(&_egl_TSDMutex); 91 92 /* check again after acquiring lock */ 93 if (!_egl_TSDInitialized) { 94 if (tss_create(&_egl_TSD, (void (*)(void *)) dtor) != thrd_success) { 95 mtx_unlock(&_egl_TSDMutex); 96 return EGL_FALSE; 97 } 98 _egl_FreeTSD = dtor; 99 _eglAddAtExitCall(_eglFiniTSD); 100 _egl_TSDInitialized = EGL_TRUE; 101 } 102 103 mtx_unlock(&_egl_TSDMutex); 104 } 105 106 return EGL_TRUE; 107 } 108 109 static void 110 _eglInitThreadInfo(_EGLThreadInfo *t) 111 { 112 memset(t, 0, sizeof(*t)); 113 t->LastError = EGL_SUCCESS; 114 /* default, per EGL spec */ 115 t->CurrentAPI = EGL_OPENGL_ES_API; 116 } 117 118 119 /** 120 * Allocate and init a new _EGLThreadInfo object. 121 */ 122 static _EGLThreadInfo * 123 _eglCreateThreadInfo(void) 124 { 125 _EGLThreadInfo *t = calloc(1, sizeof(_EGLThreadInfo)); 126 if (t) 127 _eglInitThreadInfo(t); 128 else 129 t = &dummy_thread; 130 return t; 131 } 132 133 134 /** 135 * Delete/free a _EGLThreadInfo object. 136 */ 137 static void 138 _eglDestroyThreadInfo(_EGLThreadInfo *t) 139 { 140 if (t != &dummy_thread) 141 free(t); 142 } 143 144 145 /** 146 * Make sure TSD is initialized and return current value. 147 */ 148 static inline _EGLThreadInfo * 149 _eglCheckedGetTSD(void) 150 { 151 if (_eglInitTSD(&_eglDestroyThreadInfo) != EGL_TRUE) { 152 _eglLog(_EGL_FATAL, "failed to initialize \"current\" system"); 153 return NULL; 154 } 155 156 return _eglGetTSD(); 157 } 158 159 160 /** 161 * Return the calling thread's thread info. 162 * If the calling thread nevers calls this function before, or if its thread 163 * info was destroyed, a new one is created. This function never returns NULL. 164 * In the case allocation fails, a dummy one is returned. See also 165 * _eglIsCurrentThreadDummy. 166 */ 167 _EGLThreadInfo * 168 _eglGetCurrentThread(void) 169 { 170 _EGLThreadInfo *t = _eglCheckedGetTSD(); 171 if (!t) { 172 t = _eglCreateThreadInfo(); 173 _eglSetTSD(t); 174 } 175 176 return t; 177 } 178 179 180 /** 181 * Destroy the calling thread's thread info. 182 */ 183 void 184 _eglDestroyCurrentThread(void) 185 { 186 _EGLThreadInfo *t = _eglCheckedGetTSD(); 187 if (t) { 188 _eglDestroyThreadInfo(t); 189 _eglSetTSD(NULL); 190 } 191 } 192 193 194 /** 195 * Return true if the calling thread's thread info is dummy. 196 * A dummy thread info is shared by all threads and should not be modified. 197 * Functions like eglBindAPI or eglMakeCurrent should check for dummy-ness 198 * before updating the thread info. 199 */ 200 EGLBoolean 201 _eglIsCurrentThreadDummy(void) 202 { 203 _EGLThreadInfo *t = _eglCheckedGetTSD(); 204 return (!t || t == &dummy_thread); 205 } 206 207 208 /** 209 * Return the currently bound context of the current API, or NULL. 210 */ 211 _EGLContext * 212 _eglGetCurrentContext(void) 213 { 214 _EGLThreadInfo *t = _eglGetCurrentThread(); 215 return t->CurrentContext; 216 } 217 218 219 /** 220 * Record EGL error code and return EGL_FALSE. 221 */ 222 static EGLBoolean 223 _eglInternalError(EGLint errCode, const char *msg) 224 { 225 _EGLThreadInfo *t = _eglGetCurrentThread(); 226 227 if (t == &dummy_thread) 228 return EGL_FALSE; 229 230 t->LastError = errCode; 231 232 if (errCode != EGL_SUCCESS) { 233 const char *s; 234 235 switch (errCode) { 236 case EGL_BAD_ACCESS: 237 s = "EGL_BAD_ACCESS"; 238 break; 239 case EGL_BAD_ALLOC: 240 s = "EGL_BAD_ALLOC"; 241 break; 242 case EGL_BAD_ATTRIBUTE: 243 s = "EGL_BAD_ATTRIBUTE"; 244 break; 245 case EGL_BAD_CONFIG: 246 s = "EGL_BAD_CONFIG"; 247 break; 248 case EGL_BAD_CONTEXT: 249 s = "EGL_BAD_CONTEXT"; 250 break; 251 case EGL_BAD_CURRENT_SURFACE: 252 s = "EGL_BAD_CURRENT_SURFACE"; 253 break; 254 case EGL_BAD_DISPLAY: 255 s = "EGL_BAD_DISPLAY"; 256 break; 257 case EGL_BAD_MATCH: 258 s = "EGL_BAD_MATCH"; 259 break; 260 case EGL_BAD_NATIVE_PIXMAP: 261 s = "EGL_BAD_NATIVE_PIXMAP"; 262 break; 263 case EGL_BAD_NATIVE_WINDOW: 264 s = "EGL_BAD_NATIVE_WINDOW"; 265 break; 266 case EGL_BAD_PARAMETER: 267 s = "EGL_BAD_PARAMETER"; 268 break; 269 case EGL_BAD_SURFACE: 270 s = "EGL_BAD_SURFACE"; 271 break; 272 case EGL_NOT_INITIALIZED: 273 s = "EGL_NOT_INITIALIZED"; 274 break; 275 default: 276 s = "other EGL error"; 277 } 278 _eglLog(_EGL_DEBUG, "EGL user error 0x%x (%s) in %s\n", errCode, s, msg); 279 } 280 281 return EGL_FALSE; 282 } 283 284 EGLBoolean 285 _eglError(EGLint errCode, const char *msg) 286 { 287 if (errCode != EGL_SUCCESS) { 288 EGLint type; 289 if (errCode == EGL_BAD_ALLOC) { 290 type = EGL_DEBUG_MSG_CRITICAL_KHR; 291 } else { 292 type = EGL_DEBUG_MSG_ERROR_KHR; 293 } 294 295 _eglDebugReport(errCode, msg, type, NULL); 296 } else 297 _eglInternalError(errCode, msg); 298 299 return EGL_FALSE; 300 } 301 302 /** 303 * Returns the label set for the current thread. 304 */ 305 EGLLabelKHR 306 _eglGetThreadLabel(void) 307 { 308 _EGLThreadInfo *t = _eglGetCurrentThread(); 309 return t->Label; 310 } 311 312 static void 313 _eglDebugReportFullv(EGLenum error, const char *command, const char *funcName, 314 EGLint type, EGLLabelKHR objectLabel, const char *message, va_list args) 315 { 316 EGLDEBUGPROCKHR callback = NULL; 317 318 mtx_lock(_eglGlobal.Mutex); 319 if (_eglGlobal.debugTypesEnabled & DebugBitFromType(type)) { 320 callback = _eglGlobal.debugCallback; 321 } 322 mtx_unlock(_eglGlobal.Mutex); 323 324 if (callback != NULL) { 325 char *buf = NULL; 326 327 if (message != NULL) { 328 if (vasprintf(&buf, message, args) < 0) { 329 buf = NULL; 330 } 331 } 332 callback(error, command, type, _eglGetThreadLabel(), objectLabel, buf); 333 free(buf); 334 } 335 336 if (type == EGL_DEBUG_MSG_CRITICAL_KHR || type == EGL_DEBUG_MSG_ERROR_KHR) { 337 _eglInternalError(error, funcName); 338 } 339 } 340 341 void 342 _eglDebugReportFull(EGLenum error, const char *command, const char *funcName, 343 EGLint type, EGLLabelKHR objectLabel, const char *message, ...) 344 { 345 va_list args; 346 va_start(args, message); 347 _eglDebugReportFullv(error, command, funcName, type, objectLabel, message, args); 348 va_end(args); 349 } 350 351 void 352 _eglDebugReport(EGLenum error, const char *funcName, 353 EGLint type, const char *message, ...) 354 { 355 _EGLThreadInfo *thr = _eglGetCurrentThread(); 356 va_list args; 357 358 if (funcName == NULL) { 359 funcName = thr->CurrentFuncName; 360 } 361 362 va_start(args, message); 363 _eglDebugReportFullv(error, thr->CurrentFuncName, funcName, type, thr->CurrentObjectLabel, message, args); 364 va_end(args); 365 } 366