eglcurrent.c revision 01e04c3f
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 (*_egl_FreeTSD)(_EGLThreadInfo *);
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      if (t && _egl_FreeTSD)
77         _egl_FreeTSD((void *) t);
78      tss_delete(_egl_TSD);
79   }
80   mtx_unlock(&_egl_TSDMutex);
81}
82
83static 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
105static void
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 */
117static _EGLThreadInfo *
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 */
132static void
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 */
143static inline _EGLThreadInfo *
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 *
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 */
178void
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 */
195EGLBoolean
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 *
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 */
217static EGLBoolean
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
279EGLBoolean
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
296void
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