• 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 /* 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 
_eglSetTSD(const _EGLThreadInfo * t)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 
_eglGetTSD(void)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 
_eglFiniTSD(void)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 
_eglInitTSD(void (* dtor)(_EGLThreadInfo *))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
_eglInitThreadInfo(_EGLThreadInfo * t)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 *
_eglCreateThreadInfo(void)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
_eglDestroyThreadInfo(_EGLThreadInfo * t)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 *
_eglCheckedGetTSD(void)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 *
_eglGetCurrentThread(void)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
_eglDestroyCurrentThread(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
_eglIsCurrentThreadDummy(void)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 *
_eglGetCurrentContext(void)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
_eglInternalError(EGLint errCode,const char * msg)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
_eglError(EGLint errCode,const char * msg)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
_eglGetThreadLabel(void)306 _eglGetThreadLabel(void)
307 {
308    _EGLThreadInfo *t = _eglGetCurrentThread();
309    return t->Label;
310 }
311 
312 static void
_eglDebugReportFullv(EGLenum error,const char * command,const char * funcName,EGLint type,EGLLabelKHR objectLabel,const char * message,va_list args)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
_eglDebugReportFull(EGLenum error,const char * command,const char * funcName,EGLint type,EGLLabelKHR objectLabel,const char * message,...)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
_eglDebugReport(EGLenum error,const char * funcName,EGLint type,const char * message,...)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