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 /* 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