mirror of https://gitlab.freedesktop.org/mesa/mesa
342 lines
7.2 KiB
C
342 lines
7.2 KiB
C
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "eglcurrent.h"
|
|
#include "eglcontext.h"
|
|
#include "egllog.h"
|
|
#include "eglmutex.h"
|
|
|
|
|
|
/* This should be kept in sync with _eglInitThreadInfo() */
|
|
#define _EGL_THREAD_INFO_INITIALIZER \
|
|
{ EGL_SUCCESS, { NULL }, 1 }
|
|
|
|
/* a fallback thread info to guarantee that every thread always has one */
|
|
static _EGLThreadInfo dummy_thread = _EGL_THREAD_INFO_INITIALIZER;
|
|
|
|
|
|
#ifdef GLX_USE_TLS
|
|
static __thread const _EGLThreadInfo *_egl_TSD;
|
|
__attribute__ ((tls_model("initial-exec")));
|
|
|
|
static INLINE void _eglSetTSD(const _EGLThreadInfo *t)
|
|
{
|
|
_egl_TSD = t;
|
|
}
|
|
|
|
static INLINE _EGLThreadInfo *_eglGetTSD(void)
|
|
{
|
|
return (_EGLThreadInfo *) _egl_TSD;
|
|
}
|
|
|
|
static INLINE void _eglFiniTSD(void)
|
|
{
|
|
}
|
|
|
|
static INLINE EGLBoolean _eglInitTSD(void (*dtor)(_EGLThreadInfo *))
|
|
{
|
|
/* TODO destroy TSD */
|
|
(void) dtor;
|
|
(void) _eglFiniTSD;
|
|
return EGL_TRUE;
|
|
}
|
|
|
|
#elif PTHREADS
|
|
#include <pthread.h>
|
|
|
|
static _EGL_DECLARE_MUTEX(_egl_TSDMutex);
|
|
static EGLBoolean _egl_TSDInitialized;
|
|
static pthread_key_t _egl_TSD;
|
|
static void (*_egl_FreeTSD)(_EGLThreadInfo *);
|
|
|
|
static INLINE void _eglSetTSD(const _EGLThreadInfo *t)
|
|
{
|
|
pthread_setspecific(_egl_TSD, (const void *) t);
|
|
}
|
|
|
|
static INLINE _EGLThreadInfo *_eglGetTSD(void)
|
|
{
|
|
return (_EGLThreadInfo *) pthread_getspecific(_egl_TSD);
|
|
}
|
|
|
|
static INLINE void _eglFiniTSD(void)
|
|
{
|
|
_eglLockMutex(&_egl_TSDMutex);
|
|
if (_egl_TSDInitialized) {
|
|
_EGLThreadInfo *t = _eglGetTSD();
|
|
|
|
_egl_TSDInitialized = EGL_FALSE;
|
|
if (t && _egl_FreeTSD)
|
|
_egl_FreeTSD((void *) t);
|
|
pthread_key_delete(_egl_TSD);
|
|
}
|
|
_eglUnlockMutex(&_egl_TSDMutex);
|
|
}
|
|
|
|
static INLINE EGLBoolean _eglInitTSD(void (*dtor)(_EGLThreadInfo *))
|
|
{
|
|
if (!_egl_TSDInitialized) {
|
|
_eglLockMutex(&_egl_TSDMutex);
|
|
|
|
/* check again after acquiring lock */
|
|
if (!_egl_TSDInitialized) {
|
|
if (pthread_key_create(&_egl_TSD, (void (*)(void *)) dtor) != 0) {
|
|
_eglUnlockMutex(&_egl_TSDMutex);
|
|
return EGL_FALSE;
|
|
}
|
|
_egl_FreeTSD = dtor;
|
|
(void) _eglFiniTSD;
|
|
_egl_TSDInitialized = EGL_TRUE;
|
|
}
|
|
|
|
_eglUnlockMutex(&_egl_TSDMutex);
|
|
}
|
|
|
|
return EGL_TRUE;
|
|
}
|
|
|
|
#else /* PTHREADS */
|
|
static const _EGLThreadInfo *_egl_TSD;
|
|
static void (*_egl_FreeTSD)(_EGLThreadInfo *);
|
|
|
|
static INLINE void _eglSetTSD(const _EGLThreadInfo *t)
|
|
{
|
|
_egl_TSD = t;
|
|
}
|
|
|
|
static INLINE _EGLThreadInfo *_eglGetTSD(void)
|
|
{
|
|
return (_EGLThreadInfo *) _egl_TSD;
|
|
}
|
|
|
|
static INLINE void _eglFiniTSD(void)
|
|
{
|
|
if (_egl_FreeTSD && _egl_TSD)
|
|
_egl_FreeTSD((_EGLThreadInfo *) _egl_TSD);
|
|
}
|
|
|
|
static INLINE EGLBoolean _eglInitTSD(void (*dtor)(_EGLThreadInfo *))
|
|
{
|
|
if (!_egl_FreeTSD && dtor) {
|
|
_egl_FreeTSD = dtor;
|
|
(void) _eglFiniTSD;
|
|
}
|
|
return EGL_TRUE;
|
|
}
|
|
|
|
#endif /* !PTHREADS */
|
|
|
|
|
|
static void
|
|
_eglInitThreadInfo(_EGLThreadInfo *t)
|
|
{
|
|
memset(t, 0, sizeof(*t));
|
|
t->LastError = EGL_SUCCESS;
|
|
/* default, per EGL spec */
|
|
t->CurrentAPIIndex = _eglConvertApiToIndex(EGL_OPENGL_ES_API);
|
|
}
|
|
|
|
|
|
/**
|
|
* Allocate and init a new _EGLThreadInfo object.
|
|
*/
|
|
static _EGLThreadInfo *
|
|
_eglCreateThreadInfo(void)
|
|
{
|
|
_EGLThreadInfo *t = (_EGLThreadInfo *) calloc(1, sizeof(_EGLThreadInfo));
|
|
if (t)
|
|
_eglInitThreadInfo(t);
|
|
else
|
|
t = &dummy_thread;
|
|
return t;
|
|
}
|
|
|
|
|
|
/**
|
|
* Delete/free a _EGLThreadInfo object.
|
|
*/
|
|
static void
|
|
_eglDestroyThreadInfo(_EGLThreadInfo *t)
|
|
{
|
|
if (t != &dummy_thread)
|
|
free(t);
|
|
}
|
|
|
|
|
|
/**
|
|
* Make sure TSD is initialized and return current value.
|
|
*/
|
|
static INLINE _EGLThreadInfo *
|
|
_eglCheckedGetTSD(void)
|
|
{
|
|
if (_eglInitTSD(&_eglDestroyThreadInfo) != EGL_TRUE) {
|
|
_eglLog(_EGL_FATAL, "failed to initialize \"current\" system");
|
|
return NULL;
|
|
}
|
|
|
|
return _eglGetTSD();
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the calling thread's thread info.
|
|
* If the calling thread nevers calls this function before, or if its thread
|
|
* info was destroyed, a new one is created. This function never returns NULL.
|
|
* In the case allocation fails, a dummy one is returned. See also
|
|
* _eglIsCurrentThreadDummy.
|
|
*/
|
|
_EGLThreadInfo *
|
|
_eglGetCurrentThread(void)
|
|
{
|
|
_EGLThreadInfo *t = _eglCheckedGetTSD();
|
|
if (!t) {
|
|
t = _eglCreateThreadInfo();
|
|
_eglSetTSD(t);
|
|
}
|
|
|
|
return t;
|
|
}
|
|
|
|
|
|
/**
|
|
* Destroy the calling thread's thread info.
|
|
*/
|
|
void
|
|
_eglDestroyCurrentThread(void)
|
|
{
|
|
_EGLThreadInfo *t = _eglCheckedGetTSD();
|
|
if (t) {
|
|
_eglDestroyThreadInfo(t);
|
|
_eglSetTSD(NULL);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Return true if the calling thread's thread info is dummy.
|
|
* A dummy thread info is shared by all threads and should not be modified.
|
|
* Functions like eglBindAPI or eglMakeCurrent should check for dummy-ness
|
|
* before updating the thread info.
|
|
*/
|
|
EGLBoolean
|
|
_eglIsCurrentThreadDummy(void)
|
|
{
|
|
_EGLThreadInfo *t = _eglCheckedGetTSD();
|
|
return (!t || t == &dummy_thread);
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the currently bound context, or NULL.
|
|
*/
|
|
_EGLContext *
|
|
_eglGetCurrentContext(void)
|
|
{
|
|
_EGLThreadInfo *t = _eglGetCurrentThread();
|
|
return t->CurrentContexts[t->CurrentAPIIndex];
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the display of the currently bound context, or NULL.
|
|
*/
|
|
_EGLDisplay *
|
|
_eglGetCurrentDisplay(void)
|
|
{
|
|
_EGLThreadInfo *t = _eglGetCurrentThread();
|
|
_EGLContext *ctx = t->CurrentContexts[t->CurrentAPIIndex];
|
|
if (ctx)
|
|
return ctx->Display;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the read or write surface of the currently bound context, or NULL.
|
|
*/
|
|
_EGLSurface *
|
|
_eglGetCurrentSurface(EGLint readdraw)
|
|
{
|
|
_EGLThreadInfo *t = _eglGetCurrentThread();
|
|
_EGLContext *ctx = t->CurrentContexts[t->CurrentAPIIndex];
|
|
if (ctx) {
|
|
switch (readdraw) {
|
|
case EGL_DRAW:
|
|
return ctx->DrawSurface;
|
|
case EGL_READ:
|
|
return ctx->ReadSurface;
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/**
|
|
* Record EGL error code.
|
|
*/
|
|
EGLBoolean
|
|
_eglError(EGLint errCode, const char *msg)
|
|
{
|
|
_EGLThreadInfo *t = _eglGetCurrentThread();
|
|
const char *s;
|
|
|
|
if (t == &dummy_thread)
|
|
return EGL_FALSE;
|
|
|
|
if (t->LastError == EGL_SUCCESS) {
|
|
t->LastError = errCode;
|
|
|
|
switch (errCode) {
|
|
case EGL_BAD_ACCESS:
|
|
s = "EGL_BAD_ACCESS";
|
|
break;
|
|
case EGL_BAD_ALLOC:
|
|
s = "EGL_BAD_ALLOC";
|
|
break;
|
|
case EGL_BAD_ATTRIBUTE:
|
|
s = "EGL_BAD_ATTRIBUTE";
|
|
break;
|
|
case EGL_BAD_CONFIG:
|
|
s = "EGL_BAD_CONFIG";
|
|
break;
|
|
case EGL_BAD_CONTEXT:
|
|
s = "EGL_BAD_CONTEXT";
|
|
break;
|
|
case EGL_BAD_CURRENT_SURFACE:
|
|
s = "EGL_BAD_CURRENT_SURFACE";
|
|
break;
|
|
case EGL_BAD_DISPLAY:
|
|
s = "EGL_BAD_DISPLAY";
|
|
break;
|
|
case EGL_BAD_MATCH:
|
|
s = "EGL_BAD_MATCH";
|
|
break;
|
|
case EGL_BAD_NATIVE_PIXMAP:
|
|
s = "EGL_BAD_NATIVE_PIXMAP";
|
|
break;
|
|
case EGL_BAD_NATIVE_WINDOW:
|
|
s = "EGL_BAD_NATIVE_WINDOW";
|
|
break;
|
|
case EGL_BAD_PARAMETER:
|
|
s = "EGL_BAD_PARAMETER";
|
|
break;
|
|
case EGL_BAD_SURFACE:
|
|
s = "EGL_BAD_SURFACE";
|
|
break;
|
|
case EGL_BAD_SCREEN_MESA:
|
|
s = "EGL_BAD_SCREEN_MESA";
|
|
break;
|
|
case EGL_BAD_MODE_MESA:
|
|
s = "EGL_BAD_MODE_MESA";
|
|
break;
|
|
default:
|
|
s = "other";
|
|
}
|
|
_eglLog(_EGL_DEBUG, "EGL user error 0x%x (%s) in %s\n", errCode, s, msg);
|
|
}
|
|
|
|
return EGL_FALSE;
|
|
}
|