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