summaryrefslogtreecommitdiffstats
path: root/src/egl/main/eglcurrent.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/egl/main/eglcurrent.c')
-rw-r--r--src/egl/main/eglcurrent.c342
1 files changed, 342 insertions, 0 deletions
diff --git a/src/egl/main/eglcurrent.c b/src/egl/main/eglcurrent.c
new file mode 100644
index 00000000000..4431f964f69
--- /dev/null
+++ b/src/egl/main/eglcurrent.c
@@ -0,0 +1,342 @@
+#include <stdlib.h>
+#include <string.h>
+#include "eglcurrent.h"
+#include "eglcontext.h"
+#include "egllog.h"
+#include "eglmutex.h"
+#include "eglglobals.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;
+ _eglAddAtExitCall(_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;
+ _eglAddAtExitCall(_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;
+}