13464ebd5Sriastradh/**************************************************************************
23464ebd5Sriastradh *
33464ebd5Sriastradh * Copyright 2009-2010 Chia-I Wu <olvaffe@gmail.com>
43464ebd5Sriastradh * All Rights Reserved.
53464ebd5Sriastradh *
63464ebd5Sriastradh * Permission is hereby granted, free of charge, to any person obtaining a
73464ebd5Sriastradh * copy of this software and associated documentation files (the
83464ebd5Sriastradh * "Software"), to deal in the Software without restriction, including
93464ebd5Sriastradh * without limitation the rights to use, copy, modify, merge, publish,
103464ebd5Sriastradh * distribute, sub license, and/or sell copies of the Software, and to
113464ebd5Sriastradh * permit persons to whom the Software is furnished to do so, subject to
123464ebd5Sriastradh * the following conditions:
133464ebd5Sriastradh *
143464ebd5Sriastradh * The above copyright notice and this permission notice (including the
153464ebd5Sriastradh * next paragraph) shall be included in all copies or substantial portions
163464ebd5Sriastradh * of the Software.
173464ebd5Sriastradh *
183464ebd5Sriastradh * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
193464ebd5Sriastradh * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
203464ebd5Sriastradh * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
213464ebd5Sriastradh * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
223464ebd5Sriastradh * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
233464ebd5Sriastradh * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
243464ebd5Sriastradh * DEALINGS IN THE SOFTWARE.
253464ebd5Sriastradh *
263464ebd5Sriastradh **************************************************************************/
273464ebd5Sriastradh
283464ebd5Sriastradh
2901e04c3fSmrg#include <stdio.h>
304a49301eSmrg#include <stdlib.h>
314a49301eSmrg#include <string.h>
3201e04c3fSmrg#include <stdarg.h>
3301e04c3fSmrg#include "c99_compat.h"
3401e04c3fSmrg#include "c11/threads.h"
357ec681f3Smrg#include "util/u_thread.h"
367ec681f3Smrg#include "util/u_string.h"
3701e04c3fSmrg
384a49301eSmrg#include "egllog.h"
39cdc920a0Smrg#include "eglcurrent.h"
403464ebd5Sriastradh#include "eglglobals.h"
414a49301eSmrg
424a49301eSmrg/* a fallback thread info to guarantee that every thread always has one */
4301e04c3fSmrgstatic _EGLThreadInfo dummy_thread;
4401e04c3fSmrgstatic mtx_t _egl_TSDMutex = _MTX_INITIALIZER_NP;
454a49301eSmrgstatic EGLBoolean _egl_TSDInitialized;
4601e04c3fSmrgstatic tss_t _egl_TSD;
477e102996Smayastatic void _eglDestroyThreadInfo(_EGLThreadInfo *t);
484a49301eSmrg
497ec681f3Smrg#ifdef USE_ELF_TLS
507ec681f3Smrgstatic __THREAD_INITIAL_EXEC const _EGLThreadInfo *_egl_TLS;
513464ebd5Sriastradh#endif
523464ebd5Sriastradh
5301e04c3fSmrgstatic inline void _eglSetTSD(const _EGLThreadInfo *t)
544a49301eSmrg{
5501e04c3fSmrg   tss_set(_egl_TSD, (void *) t);
567ec681f3Smrg#ifdef USE_ELF_TLS
573464ebd5Sriastradh   _egl_TLS = t;
583464ebd5Sriastradh#endif
594a49301eSmrg}
604a49301eSmrg
6101e04c3fSmrgstatic inline _EGLThreadInfo *_eglGetTSD(void)
624a49301eSmrg{
637ec681f3Smrg#ifdef USE_ELF_TLS
643464ebd5Sriastradh   return (_EGLThreadInfo *) _egl_TLS;
653464ebd5Sriastradh#else
6601e04c3fSmrg   return (_EGLThreadInfo *) tss_get(_egl_TSD);
673464ebd5Sriastradh#endif
684a49301eSmrg}
694a49301eSmrg
7001e04c3fSmrgstatic inline void _eglFiniTSD(void)
714a49301eSmrg{
7201e04c3fSmrg   mtx_lock(&_egl_TSDMutex);
734a49301eSmrg   if (_egl_TSDInitialized) {
744a49301eSmrg      _EGLThreadInfo *t = _eglGetTSD();
754a49301eSmrg
764a49301eSmrg      _egl_TSDInitialized = EGL_FALSE;
777e102996Smaya      _eglDestroyThreadInfo(t);
7801e04c3fSmrg      tss_delete(_egl_TSD);
794a49301eSmrg   }
8001e04c3fSmrg   mtx_unlock(&_egl_TSDMutex);
814a49301eSmrg}
824a49301eSmrg
837e102996Smayastatic inline EGLBoolean _eglInitTSD()
844a49301eSmrg{
854a49301eSmrg   if (!_egl_TSDInitialized) {
8601e04c3fSmrg      mtx_lock(&_egl_TSDMutex);
874a49301eSmrg
884a49301eSmrg      /* check again after acquiring lock */
894a49301eSmrg      if (!_egl_TSDInitialized) {
907e102996Smaya         if (tss_create(&_egl_TSD, (void (*)(void *)) _eglDestroyThreadInfo) != thrd_success) {
9101e04c3fSmrg            mtx_unlock(&_egl_TSDMutex);
924a49301eSmrg            return EGL_FALSE;
934a49301eSmrg         }
944a49301eSmrg         _eglAddAtExitCall(_eglFiniTSD);
954a49301eSmrg         _egl_TSDInitialized = EGL_TRUE;
964a49301eSmrg      }
974a49301eSmrg
9801e04c3fSmrg      mtx_unlock(&_egl_TSDMutex);
994a49301eSmrg   }
1004a49301eSmrg
1014a49301eSmrg   return EGL_TRUE;
1024a49301eSmrg}
1034a49301eSmrg
1044a49301eSmrgstatic void
1054a49301eSmrg_eglInitThreadInfo(_EGLThreadInfo *t)
1064a49301eSmrg{
1074a49301eSmrg   t->LastError = EGL_SUCCESS;
1084a49301eSmrg   /* default, per EGL spec */
10901e04c3fSmrg   t->CurrentAPI = EGL_OPENGL_ES_API;
1104a49301eSmrg}
1114a49301eSmrg
1124a49301eSmrg
1134a49301eSmrg/**
1144a49301eSmrg * Allocate and init a new _EGLThreadInfo object.
1154a49301eSmrg */
1164a49301eSmrgstatic _EGLThreadInfo *
1174a49301eSmrg_eglCreateThreadInfo(void)
1184a49301eSmrg{
119af69d88dSmrg   _EGLThreadInfo *t = calloc(1, sizeof(_EGLThreadInfo));
12001e04c3fSmrg   if (!t)
1214a49301eSmrg      t = &dummy_thread;
12201e04c3fSmrg
12301e04c3fSmrg   _eglInitThreadInfo(t);
1244a49301eSmrg   return t;
1254a49301eSmrg}
1264a49301eSmrg
1274a49301eSmrg
1284a49301eSmrg/**
1294a49301eSmrg * Delete/free a _EGLThreadInfo object.
1304a49301eSmrg */
1314a49301eSmrgstatic void
1324a49301eSmrg_eglDestroyThreadInfo(_EGLThreadInfo *t)
1334a49301eSmrg{
1347ec681f3Smrg   if (t != &dummy_thread) {
1354a49301eSmrg      free(t);
1367ec681f3Smrg#ifdef USE_ELF_TLS
1377ec681f3Smrg      /* Reset the TLS also here, otherwise
1387ec681f3Smrg       * it will be having a dangling pointer */
1397ec681f3Smrg      _egl_TLS = NULL;
1407ec681f3Smrg#endif
1417ec681f3Smrg   }
1424a49301eSmrg}
1434a49301eSmrg
1444a49301eSmrg
1454a49301eSmrg/**
1464a49301eSmrg * Make sure TSD is initialized and return current value.
1474a49301eSmrg */
14801e04c3fSmrgstatic inline _EGLThreadInfo *
1494a49301eSmrg_eglCheckedGetTSD(void)
1504a49301eSmrg{
1517e102996Smaya   if (_eglInitTSD() != EGL_TRUE) {
1524a49301eSmrg      _eglLog(_EGL_FATAL, "failed to initialize \"current\" system");
1534a49301eSmrg      return NULL;
1544a49301eSmrg   }
1554a49301eSmrg
1564a49301eSmrg   return _eglGetTSD();
1574a49301eSmrg}
1584a49301eSmrg
1594a49301eSmrg
1604a49301eSmrg/**
1614a49301eSmrg * Return the calling thread's thread info.
1624a49301eSmrg * If the calling thread nevers calls this function before, or if its thread
1634a49301eSmrg * info was destroyed, a new one is created.  This function never returns NULL.
1644a49301eSmrg * In the case allocation fails, a dummy one is returned.  See also
1654a49301eSmrg * _eglIsCurrentThreadDummy.
1664a49301eSmrg */
1674a49301eSmrg_EGLThreadInfo *
1684a49301eSmrg_eglGetCurrentThread(void)
1694a49301eSmrg{
1704a49301eSmrg   _EGLThreadInfo *t = _eglCheckedGetTSD();
1714a49301eSmrg   if (!t) {
1724a49301eSmrg      t = _eglCreateThreadInfo();
1734a49301eSmrg      _eglSetTSD(t);
1744a49301eSmrg   }
1754a49301eSmrg
1764a49301eSmrg   return t;
1774a49301eSmrg}
1784a49301eSmrg
1794a49301eSmrg
1804a49301eSmrg/**
1814a49301eSmrg * Destroy the calling thread's thread info.
1824a49301eSmrg */
1834a49301eSmrgvoid
1844a49301eSmrg_eglDestroyCurrentThread(void)
1854a49301eSmrg{
1864a49301eSmrg   _EGLThreadInfo *t = _eglCheckedGetTSD();
1874a49301eSmrg   if (t) {
1884a49301eSmrg      _eglDestroyThreadInfo(t);
1894a49301eSmrg      _eglSetTSD(NULL);
1904a49301eSmrg   }
1914a49301eSmrg}
1924a49301eSmrg
1934a49301eSmrg
1944a49301eSmrg/**
1954a49301eSmrg * Return true if the calling thread's thread info is dummy.
1964a49301eSmrg * A dummy thread info is shared by all threads and should not be modified.
1974a49301eSmrg * Functions like eglBindAPI or eglMakeCurrent should check for dummy-ness
1984a49301eSmrg * before updating the thread info.
1994a49301eSmrg */
2004a49301eSmrgEGLBoolean
2014a49301eSmrg_eglIsCurrentThreadDummy(void)
2024a49301eSmrg{
2034a49301eSmrg   _EGLThreadInfo *t = _eglCheckedGetTSD();
2044a49301eSmrg   return (!t || t == &dummy_thread);
2054a49301eSmrg}
2064a49301eSmrg
2074a49301eSmrg
2084a49301eSmrg/**
209cdc920a0Smrg * Return the currently bound context of the current API, or NULL.
2104a49301eSmrg */
211cdc920a0Smrg_EGLContext *
212cdc920a0Smrg_eglGetCurrentContext(void)
2134a49301eSmrg{
2144a49301eSmrg   _EGLThreadInfo *t = _eglGetCurrentThread();
21501e04c3fSmrg   return t->CurrentContext;
2164a49301eSmrg}
2174a49301eSmrg
2184a49301eSmrg
2194a49301eSmrg/**
220cdc920a0Smrg * Record EGL error code and return EGL_FALSE.
2214a49301eSmrg */
22201e04c3fSmrgstatic EGLBoolean
22301e04c3fSmrg_eglInternalError(EGLint errCode, const char *msg)
2244a49301eSmrg{
2254a49301eSmrg   _EGLThreadInfo *t = _eglGetCurrentThread();
2264a49301eSmrg
2274a49301eSmrg   if (t == &dummy_thread)
2284a49301eSmrg      return EGL_FALSE;
2294a49301eSmrg
230cdc920a0Smrg   t->LastError = errCode;
231cdc920a0Smrg
232cdc920a0Smrg   if (errCode != EGL_SUCCESS) {
233cdc920a0Smrg      const char *s;
2344a49301eSmrg
2354a49301eSmrg      switch (errCode) {
2364a49301eSmrg      case EGL_BAD_ACCESS:
2374a49301eSmrg         s = "EGL_BAD_ACCESS";
2384a49301eSmrg         break;
2394a49301eSmrg      case EGL_BAD_ALLOC:
2404a49301eSmrg         s = "EGL_BAD_ALLOC";
2414a49301eSmrg         break;
2424a49301eSmrg      case EGL_BAD_ATTRIBUTE:
2434a49301eSmrg         s = "EGL_BAD_ATTRIBUTE";
2444a49301eSmrg         break;
2454a49301eSmrg      case EGL_BAD_CONFIG:
2464a49301eSmrg         s = "EGL_BAD_CONFIG";
2474a49301eSmrg         break;
2484a49301eSmrg      case EGL_BAD_CONTEXT:
2494a49301eSmrg         s = "EGL_BAD_CONTEXT";
2504a49301eSmrg         break;
2514a49301eSmrg      case EGL_BAD_CURRENT_SURFACE:
2524a49301eSmrg         s = "EGL_BAD_CURRENT_SURFACE";
2534a49301eSmrg         break;
2544a49301eSmrg      case EGL_BAD_DISPLAY:
2554a49301eSmrg         s = "EGL_BAD_DISPLAY";
2564a49301eSmrg         break;
2574a49301eSmrg      case EGL_BAD_MATCH:
2584a49301eSmrg         s = "EGL_BAD_MATCH";
2594a49301eSmrg         break;
2604a49301eSmrg      case EGL_BAD_NATIVE_PIXMAP:
2614a49301eSmrg         s = "EGL_BAD_NATIVE_PIXMAP";
2624a49301eSmrg         break;
2634a49301eSmrg      case EGL_BAD_NATIVE_WINDOW:
2644a49301eSmrg         s = "EGL_BAD_NATIVE_WINDOW";
2654a49301eSmrg         break;
2664a49301eSmrg      case EGL_BAD_PARAMETER:
2674a49301eSmrg         s = "EGL_BAD_PARAMETER";
2684a49301eSmrg         break;
2694a49301eSmrg      case EGL_BAD_SURFACE:
2704a49301eSmrg         s = "EGL_BAD_SURFACE";
2714a49301eSmrg         break;
2723464ebd5Sriastradh      case EGL_NOT_INITIALIZED:
2733464ebd5Sriastradh         s = "EGL_NOT_INITIALIZED";
2743464ebd5Sriastradh         break;
2754a49301eSmrg      default:
2763464ebd5Sriastradh         s = "other EGL error";
2774a49301eSmrg      }
2784a49301eSmrg      _eglLog(_EGL_DEBUG, "EGL user error 0x%x (%s) in %s\n", errCode, s, msg);
2794a49301eSmrg   }
2804a49301eSmrg
2814a49301eSmrg   return EGL_FALSE;
2824a49301eSmrg}
28301e04c3fSmrg
28401e04c3fSmrgEGLBoolean
28501e04c3fSmrg_eglError(EGLint errCode, const char *msg)
28601e04c3fSmrg{
28701e04c3fSmrg   if (errCode != EGL_SUCCESS) {
28801e04c3fSmrg      EGLint type;
28901e04c3fSmrg      if (errCode == EGL_BAD_ALLOC)
29001e04c3fSmrg         type = EGL_DEBUG_MSG_CRITICAL_KHR;
29101e04c3fSmrg      else
29201e04c3fSmrg         type = EGL_DEBUG_MSG_ERROR_KHR;
29301e04c3fSmrg
29401e04c3fSmrg      _eglDebugReport(errCode, NULL, type, msg);
29501e04c3fSmrg   } else
29601e04c3fSmrg      _eglInternalError(errCode, msg);
29701e04c3fSmrg
29801e04c3fSmrg   return EGL_FALSE;
29901e04c3fSmrg}
30001e04c3fSmrg
30101e04c3fSmrgvoid
30201e04c3fSmrg_eglDebugReport(EGLenum error, const char *funcName,
30301e04c3fSmrg      EGLint type, const char *message, ...)
30401e04c3fSmrg{
30501e04c3fSmrg   _EGLThreadInfo *thr = _eglGetCurrentThread();
30601e04c3fSmrg   EGLDEBUGPROCKHR callback = NULL;
30701e04c3fSmrg   va_list args;
30801e04c3fSmrg
30901e04c3fSmrg   if (funcName == NULL)
31001e04c3fSmrg      funcName = thr->CurrentFuncName;
31101e04c3fSmrg
31201e04c3fSmrg   mtx_lock(_eglGlobal.Mutex);
31301e04c3fSmrg   if (_eglGlobal.debugTypesEnabled & DebugBitFromType(type))
31401e04c3fSmrg      callback = _eglGlobal.debugCallback;
31501e04c3fSmrg
31601e04c3fSmrg   mtx_unlock(_eglGlobal.Mutex);
31701e04c3fSmrg
3187e102996Smaya   char *message_buf = NULL;
3197e102996Smaya   if (message != NULL) {
3207e102996Smaya      va_start(args, message);
3217e102996Smaya      if (vasprintf(&message_buf, message, args) < 0)
3227e102996Smaya         message_buf = NULL;
3237e102996Smaya      va_end(args);
3247e102996Smaya   }
32501e04c3fSmrg
3267e102996Smaya   if (callback != NULL) {
3277e102996Smaya      callback(error, funcName, type, thr->Label, thr->CurrentObjectLabel,
3287e102996Smaya               message_buf);
3297e102996Smaya   }
33001e04c3fSmrg
3317e102996Smaya   if (type == EGL_DEBUG_MSG_CRITICAL_KHR || type == EGL_DEBUG_MSG_ERROR_KHR) {
3327e102996Smaya      char *func_message_buf = NULL;
3337e102996Smaya      /* Note: _eglError() is often called with msg == thr->currentFuncName */
3347e102996Smaya      if (message_buf && funcName && strcmp(message_buf, funcName) != 0) {
3357e102996Smaya         if (asprintf(&func_message_buf, "%s: %s", funcName, message_buf) < 0)
3367e102996Smaya            func_message_buf = NULL;
33701e04c3fSmrg      }
3387e102996Smaya      _eglInternalError(error, func_message_buf ? func_message_buf : funcName);
3397e102996Smaya      free(func_message_buf);
34001e04c3fSmrg   }
3417e102996Smaya   free(message_buf);
34201e04c3fSmrg}
343