Home | History | Annotate | Download | only in main
      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 /* 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 
     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 
     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 
     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 
     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
    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 *
    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
    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 *
    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  */
    178 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
    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  */
    217 static 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 
    279 EGLBoolean
    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
    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