eglcurrent.c revision 01e04c3f
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"
3501e04c3fSmrg
364a49301eSmrg#include "egllog.h"
37cdc920a0Smrg#include "eglcurrent.h"
383464ebd5Sriastradh#include "eglglobals.h"
394a49301eSmrg
404a49301eSmrg/* a fallback thread info to guarantee that every thread always has one */
4101e04c3fSmrgstatic _EGLThreadInfo dummy_thread;
4201e04c3fSmrgstatic mtx_t _egl_TSDMutex = _MTX_INITIALIZER_NP;
434a49301eSmrgstatic EGLBoolean _egl_TSDInitialized;
4401e04c3fSmrgstatic tss_t _egl_TSD;
454a49301eSmrgstatic void (*_egl_FreeTSD)(_EGLThreadInfo *);
464a49301eSmrg
473464ebd5Sriastradh#ifdef GLX_USE_TLS
483464ebd5Sriastradhstatic __thread const _EGLThreadInfo *_egl_TLS
493464ebd5Sriastradh   __attribute__ ((tls_model("initial-exec")));
503464ebd5Sriastradh#endif
513464ebd5Sriastradh
5201e04c3fSmrgstatic inline void _eglSetTSD(const _EGLThreadInfo *t)
534a49301eSmrg{
5401e04c3fSmrg   tss_set(_egl_TSD, (void *) t);
553464ebd5Sriastradh#ifdef GLX_USE_TLS
563464ebd5Sriastradh   _egl_TLS = t;
573464ebd5Sriastradh#endif
584a49301eSmrg}
594a49301eSmrg
6001e04c3fSmrgstatic inline _EGLThreadInfo *_eglGetTSD(void)
614a49301eSmrg{
623464ebd5Sriastradh#ifdef GLX_USE_TLS
633464ebd5Sriastradh   return (_EGLThreadInfo *) _egl_TLS;
643464ebd5Sriastradh#else
6501e04c3fSmrg   return (_EGLThreadInfo *) tss_get(_egl_TSD);
663464ebd5Sriastradh#endif
674a49301eSmrg}
684a49301eSmrg
6901e04c3fSmrgstatic inline void _eglFiniTSD(void)
704a49301eSmrg{
7101e04c3fSmrg   mtx_lock(&_egl_TSDMutex);
724a49301eSmrg   if (_egl_TSDInitialized) {
734a49301eSmrg      _EGLThreadInfo *t = _eglGetTSD();
744a49301eSmrg
754a49301eSmrg      _egl_TSDInitialized = EGL_FALSE;
764a49301eSmrg      if (t && _egl_FreeTSD)
774a49301eSmrg         _egl_FreeTSD((void *) t);
7801e04c3fSmrg      tss_delete(_egl_TSD);
794a49301eSmrg   }
8001e04c3fSmrg   mtx_unlock(&_egl_TSDMutex);
814a49301eSmrg}
824a49301eSmrg
8301e04c3fSmrgstatic inline EGLBoolean _eglInitTSD(void (*dtor)(_EGLThreadInfo *))
844a49301eSmrg{
854a49301eSmrg   if (!_egl_TSDInitialized) {
8601e04c3fSmrg      mtx_lock(&_egl_TSDMutex);
874a49301eSmrg
884a49301eSmrg      /* check again after acquiring lock */
894a49301eSmrg      if (!_egl_TSDInitialized) {
9001e04c3fSmrg         if (tss_create(&_egl_TSD, (void (*)(void *)) dtor) != thrd_success) {
9101e04c3fSmrg            mtx_unlock(&_egl_TSDMutex);
924a49301eSmrg            return EGL_FALSE;
934a49301eSmrg         }
944a49301eSmrg         _egl_FreeTSD = dtor;
954a49301eSmrg         _eglAddAtExitCall(_eglFiniTSD);
964a49301eSmrg         _egl_TSDInitialized = EGL_TRUE;
974a49301eSmrg      }
984a49301eSmrg
9901e04c3fSmrg      mtx_unlock(&_egl_TSDMutex);
1004a49301eSmrg   }
1014a49301eSmrg
1024a49301eSmrg   return EGL_TRUE;
1034a49301eSmrg}
1044a49301eSmrg
1054a49301eSmrgstatic void
1064a49301eSmrg_eglInitThreadInfo(_EGLThreadInfo *t)
1074a49301eSmrg{
1084a49301eSmrg   t->LastError = EGL_SUCCESS;
1094a49301eSmrg   /* default, per EGL spec */
11001e04c3fSmrg   t->CurrentAPI = EGL_OPENGL_ES_API;
1114a49301eSmrg}
1124a49301eSmrg
1134a49301eSmrg
1144a49301eSmrg/**
1154a49301eSmrg * Allocate and init a new _EGLThreadInfo object.
1164a49301eSmrg */
1174a49301eSmrgstatic _EGLThreadInfo *
1184a49301eSmrg_eglCreateThreadInfo(void)
1194a49301eSmrg{
120af69d88dSmrg   _EGLThreadInfo *t = calloc(1, sizeof(_EGLThreadInfo));
12101e04c3fSmrg   if (!t)
1224a49301eSmrg      t = &dummy_thread;
12301e04c3fSmrg
12401e04c3fSmrg   _eglInitThreadInfo(t);
1254a49301eSmrg   return t;
1264a49301eSmrg}
1274a49301eSmrg
1284a49301eSmrg
1294a49301eSmrg/**
1304a49301eSmrg * Delete/free a _EGLThreadInfo object.
1314a49301eSmrg */
1324a49301eSmrgstatic void
1334a49301eSmrg_eglDestroyThreadInfo(_EGLThreadInfo *t)
1344a49301eSmrg{
1354a49301eSmrg   if (t != &dummy_thread)
1364a49301eSmrg      free(t);
1374a49301eSmrg}
1384a49301eSmrg
1394a49301eSmrg
1404a49301eSmrg/**
1414a49301eSmrg * Make sure TSD is initialized and return current value.
1424a49301eSmrg */
14301e04c3fSmrgstatic inline _EGLThreadInfo *
1444a49301eSmrg_eglCheckedGetTSD(void)
1454a49301eSmrg{
1464a49301eSmrg   if (_eglInitTSD(&_eglDestroyThreadInfo) != EGL_TRUE) {
1474a49301eSmrg      _eglLog(_EGL_FATAL, "failed to initialize \"current\" system");
1484a49301eSmrg      return NULL;
1494a49301eSmrg   }
1504a49301eSmrg
1514a49301eSmrg   return _eglGetTSD();
1524a49301eSmrg}
1534a49301eSmrg
1544a49301eSmrg
1554a49301eSmrg/**
1564a49301eSmrg * Return the calling thread's thread info.
1574a49301eSmrg * If the calling thread nevers calls this function before, or if its thread
1584a49301eSmrg * info was destroyed, a new one is created.  This function never returns NULL.
1594a49301eSmrg * In the case allocation fails, a dummy one is returned.  See also
1604a49301eSmrg * _eglIsCurrentThreadDummy.
1614a49301eSmrg */
1624a49301eSmrg_EGLThreadInfo *
1634a49301eSmrg_eglGetCurrentThread(void)
1644a49301eSmrg{
1654a49301eSmrg   _EGLThreadInfo *t = _eglCheckedGetTSD();
1664a49301eSmrg   if (!t) {
1674a49301eSmrg      t = _eglCreateThreadInfo();
1684a49301eSmrg      _eglSetTSD(t);
1694a49301eSmrg   }
1704a49301eSmrg
1714a49301eSmrg   return t;
1724a49301eSmrg}
1734a49301eSmrg
1744a49301eSmrg
1754a49301eSmrg/**
1764a49301eSmrg * Destroy the calling thread's thread info.
1774a49301eSmrg */
1784a49301eSmrgvoid
1794a49301eSmrg_eglDestroyCurrentThread(void)
1804a49301eSmrg{
1814a49301eSmrg   _EGLThreadInfo *t = _eglCheckedGetTSD();
1824a49301eSmrg   if (t) {
1834a49301eSmrg      _eglDestroyThreadInfo(t);
1844a49301eSmrg      _eglSetTSD(NULL);
1854a49301eSmrg   }
1864a49301eSmrg}
1874a49301eSmrg
1884a49301eSmrg
1894a49301eSmrg/**
1904a49301eSmrg * Return true if the calling thread's thread info is dummy.
1914a49301eSmrg * A dummy thread info is shared by all threads and should not be modified.
1924a49301eSmrg * Functions like eglBindAPI or eglMakeCurrent should check for dummy-ness
1934a49301eSmrg * before updating the thread info.
1944a49301eSmrg */
1954a49301eSmrgEGLBoolean
1964a49301eSmrg_eglIsCurrentThreadDummy(void)
1974a49301eSmrg{
1984a49301eSmrg   _EGLThreadInfo *t = _eglCheckedGetTSD();
1994a49301eSmrg   return (!t || t == &dummy_thread);
2004a49301eSmrg}
2014a49301eSmrg
2024a49301eSmrg
2034a49301eSmrg/**
204cdc920a0Smrg * Return the currently bound context of the current API, or NULL.
2054a49301eSmrg */
206cdc920a0Smrg_EGLContext *
207cdc920a0Smrg_eglGetCurrentContext(void)
2084a49301eSmrg{
2094a49301eSmrg   _EGLThreadInfo *t = _eglGetCurrentThread();
21001e04c3fSmrg   return t->CurrentContext;
2114a49301eSmrg}
2124a49301eSmrg
2134a49301eSmrg
2144a49301eSmrg/**
215cdc920a0Smrg * Record EGL error code and return EGL_FALSE.
2164a49301eSmrg */
21701e04c3fSmrgstatic EGLBoolean
21801e04c3fSmrg_eglInternalError(EGLint errCode, const char *msg)
2194a49301eSmrg{
2204a49301eSmrg   _EGLThreadInfo *t = _eglGetCurrentThread();
2214a49301eSmrg
2224a49301eSmrg   if (t == &dummy_thread)
2234a49301eSmrg      return EGL_FALSE;
2244a49301eSmrg
225cdc920a0Smrg   t->LastError = errCode;
226cdc920a0Smrg
227cdc920a0Smrg   if (errCode != EGL_SUCCESS) {
228cdc920a0Smrg      const char *s;
2294a49301eSmrg
2304a49301eSmrg      switch (errCode) {
2314a49301eSmrg      case EGL_BAD_ACCESS:
2324a49301eSmrg         s = "EGL_BAD_ACCESS";
2334a49301eSmrg         break;
2344a49301eSmrg      case EGL_BAD_ALLOC:
2354a49301eSmrg         s = "EGL_BAD_ALLOC";
2364a49301eSmrg         break;
2374a49301eSmrg      case EGL_BAD_ATTRIBUTE:
2384a49301eSmrg         s = "EGL_BAD_ATTRIBUTE";
2394a49301eSmrg         break;
2404a49301eSmrg      case EGL_BAD_CONFIG:
2414a49301eSmrg         s = "EGL_BAD_CONFIG";
2424a49301eSmrg         break;
2434a49301eSmrg      case EGL_BAD_CONTEXT:
2444a49301eSmrg         s = "EGL_BAD_CONTEXT";
2454a49301eSmrg         break;
2464a49301eSmrg      case EGL_BAD_CURRENT_SURFACE:
2474a49301eSmrg         s = "EGL_BAD_CURRENT_SURFACE";
2484a49301eSmrg         break;
2494a49301eSmrg      case EGL_BAD_DISPLAY:
2504a49301eSmrg         s = "EGL_BAD_DISPLAY";
2514a49301eSmrg         break;
2524a49301eSmrg      case EGL_BAD_MATCH:
2534a49301eSmrg         s = "EGL_BAD_MATCH";
2544a49301eSmrg         break;
2554a49301eSmrg      case EGL_BAD_NATIVE_PIXMAP:
2564a49301eSmrg         s = "EGL_BAD_NATIVE_PIXMAP";
2574a49301eSmrg         break;
2584a49301eSmrg      case EGL_BAD_NATIVE_WINDOW:
2594a49301eSmrg         s = "EGL_BAD_NATIVE_WINDOW";
2604a49301eSmrg         break;
2614a49301eSmrg      case EGL_BAD_PARAMETER:
2624a49301eSmrg         s = "EGL_BAD_PARAMETER";
2634a49301eSmrg         break;
2644a49301eSmrg      case EGL_BAD_SURFACE:
2654a49301eSmrg         s = "EGL_BAD_SURFACE";
2664a49301eSmrg         break;
2673464ebd5Sriastradh      case EGL_NOT_INITIALIZED:
2683464ebd5Sriastradh         s = "EGL_NOT_INITIALIZED";
2693464ebd5Sriastradh         break;
2704a49301eSmrg      default:
2713464ebd5Sriastradh         s = "other EGL error";
2724a49301eSmrg      }
2734a49301eSmrg      _eglLog(_EGL_DEBUG, "EGL user error 0x%x (%s) in %s\n", errCode, s, msg);
2744a49301eSmrg   }
2754a49301eSmrg
2764a49301eSmrg   return EGL_FALSE;
2774a49301eSmrg}
27801e04c3fSmrg
27901e04c3fSmrgEGLBoolean
28001e04c3fSmrg_eglError(EGLint errCode, const char *msg)
28101e04c3fSmrg{
28201e04c3fSmrg   if (errCode != EGL_SUCCESS) {
28301e04c3fSmrg      EGLint type;
28401e04c3fSmrg      if (errCode == EGL_BAD_ALLOC)
28501e04c3fSmrg         type = EGL_DEBUG_MSG_CRITICAL_KHR;
28601e04c3fSmrg      else
28701e04c3fSmrg         type = EGL_DEBUG_MSG_ERROR_KHR;
28801e04c3fSmrg
28901e04c3fSmrg      _eglDebugReport(errCode, NULL, type, msg);
29001e04c3fSmrg   } else
29101e04c3fSmrg      _eglInternalError(errCode, msg);
29201e04c3fSmrg
29301e04c3fSmrg   return EGL_FALSE;
29401e04c3fSmrg}
29501e04c3fSmrg
29601e04c3fSmrgvoid
29701e04c3fSmrg_eglDebugReport(EGLenum error, const char *funcName,
29801e04c3fSmrg      EGLint type, const char *message, ...)
29901e04c3fSmrg{
30001e04c3fSmrg   _EGLThreadInfo *thr = _eglGetCurrentThread();
30101e04c3fSmrg   EGLDEBUGPROCKHR callback = NULL;
30201e04c3fSmrg   va_list args;
30301e04c3fSmrg
30401e04c3fSmrg   if (funcName == NULL)
30501e04c3fSmrg      funcName = thr->CurrentFuncName;
30601e04c3fSmrg
30701e04c3fSmrg   mtx_lock(_eglGlobal.Mutex);
30801e04c3fSmrg   if (_eglGlobal.debugTypesEnabled & DebugBitFromType(type))
30901e04c3fSmrg      callback = _eglGlobal.debugCallback;
31001e04c3fSmrg
31101e04c3fSmrg   mtx_unlock(_eglGlobal.Mutex);
31201e04c3fSmrg
31301e04c3fSmrg   if (callback != NULL) {
31401e04c3fSmrg      char *buf = NULL;
31501e04c3fSmrg
31601e04c3fSmrg      if (message != NULL) {
31701e04c3fSmrg         va_start(args, message);
31801e04c3fSmrg         if (vasprintf(&buf, message, args) < 0)
31901e04c3fSmrg            buf = NULL;
32001e04c3fSmrg
32101e04c3fSmrg         va_end(args);
32201e04c3fSmrg      }
32301e04c3fSmrg      callback(error, funcName, type, thr->Label, thr->CurrentObjectLabel, buf);
32401e04c3fSmrg      free(buf);
32501e04c3fSmrg   }
32601e04c3fSmrg
32701e04c3fSmrg   if (type == EGL_DEBUG_MSG_CRITICAL_KHR || type == EGL_DEBUG_MSG_ERROR_KHR)
32801e04c3fSmrg      _eglInternalError(error, funcName);
32901e04c3fSmrg}
330