ADD_SUBDIRECTORY (external/glutess)
ENDIF ()
-FILE(GLOB VKVG_OBJ_SRC src/*.c)
+FILE(GLOB VKVG_OBJ_SRC src/*.c src/deps/*.c)
SET (VKVG_PUBLIC_HEADERS "include/vkvg.h")
IF (VKVG_SVG)
#define reset_warning (warn) #pragma GCC diagnostic warning "-W"#warn
#endif
+#include "deps/tinycthread.h"
+
const char* getUserDir ();
#endif // CROSS_OS_H
--- /dev/null
+/* -*- mode: c; tab-width: 2; indent-tabs-mode: nil; -*-
+Copyright (c) 2012 Marcus Geelnard
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+/* 2013-01-06 Camilla Löwy <elmindreda@glfw.org>
+ *
+ * Added casts from time_t to DWORD to avoid warnings on VC++.
+ * Fixed time retrieval on POSIX systems.
+ */
+
+#include "tinycthread.h"
+#include <stdlib.h>
+
+/* Platform specific includes */
+#if defined(_TTHREAD_POSIX_)
+ #include <signal.h>
+ #include <sched.h>
+ #include <unistd.h>
+ #include <sys/time.h>
+ #include <errno.h>
+#elif defined(_TTHREAD_WIN32_)
+ #include <process.h>
+ #include <sys/timeb.h>
+#endif
+
+/* Standard, good-to-have defines */
+#ifndef NULL
+ #define NULL (void*)0
+#endif
+#ifndef TRUE
+ #define TRUE 1
+#endif
+#ifndef FALSE
+ #define FALSE 0
+#endif
+
+int mtx_init(mtx_t *mtx, int type)
+{
+#if defined(_TTHREAD_WIN32_)
+ mtx->mAlreadyLocked = FALSE;
+ mtx->mRecursive = type & mtx_recursive;
+ InitializeCriticalSection(&mtx->mHandle);
+ return thrd_success;
+#else
+ int ret;
+ pthread_mutexattr_t attr;
+ pthread_mutexattr_init(&attr);
+ if (type & mtx_recursive)
+ {
+ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
+ }
+ ret = pthread_mutex_init(mtx, &attr);
+ pthread_mutexattr_destroy(&attr);
+ return ret == 0 ? thrd_success : thrd_error;
+#endif
+}
+
+void mtx_destroy(mtx_t *mtx)
+{
+#if defined(_TTHREAD_WIN32_)
+ DeleteCriticalSection(&mtx->mHandle);
+#else
+ pthread_mutex_destroy(mtx);
+#endif
+}
+
+int mtx_lock(mtx_t *mtx)
+{
+#if defined(_TTHREAD_WIN32_)
+ EnterCriticalSection(&mtx->mHandle);
+ if (!mtx->mRecursive)
+ {
+ while(mtx->mAlreadyLocked) Sleep(1000); /* Simulate deadlock... */
+ mtx->mAlreadyLocked = TRUE;
+ }
+ return thrd_success;
+#else
+ return pthread_mutex_lock(mtx) == 0 ? thrd_success : thrd_error;
+#endif
+}
+
+int mtx_timedlock(mtx_t *mtx, const struct timespec *ts)
+{
+ /* FIXME! */
+ (void)mtx;
+ (void)ts;
+ return thrd_error;
+}
+
+int mtx_trylock(mtx_t *mtx)
+{
+#if defined(_TTHREAD_WIN32_)
+ int ret = TryEnterCriticalSection(&mtx->mHandle) ? thrd_success : thrd_busy;
+ if ((!mtx->mRecursive) && (ret == thrd_success) && mtx->mAlreadyLocked)
+ {
+ LeaveCriticalSection(&mtx->mHandle);
+ ret = thrd_busy;
+ }
+ return ret;
+#else
+ return (pthread_mutex_trylock(mtx) == 0) ? thrd_success : thrd_busy;
+#endif
+}
+
+int mtx_unlock(mtx_t *mtx)
+{
+#if defined(_TTHREAD_WIN32_)
+ mtx->mAlreadyLocked = FALSE;
+ LeaveCriticalSection(&mtx->mHandle);
+ return thrd_success;
+#else
+ return pthread_mutex_unlock(mtx) == 0 ? thrd_success : thrd_error;;
+#endif
+}
+
+#if defined(_TTHREAD_WIN32_)
+#define _CONDITION_EVENT_ONE 0
+#define _CONDITION_EVENT_ALL 1
+#endif
+
+int cnd_init(cnd_t *cond)
+{
+#if defined(_TTHREAD_WIN32_)
+ cond->mWaitersCount = 0;
+
+ /* Init critical section */
+ InitializeCriticalSection(&cond->mWaitersCountLock);
+
+ /* Init events */
+ cond->mEvents[_CONDITION_EVENT_ONE] = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if (cond->mEvents[_CONDITION_EVENT_ONE] == NULL)
+ {
+ cond->mEvents[_CONDITION_EVENT_ALL] = NULL;
+ return thrd_error;
+ }
+ cond->mEvents[_CONDITION_EVENT_ALL] = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (cond->mEvents[_CONDITION_EVENT_ALL] == NULL)
+ {
+ CloseHandle(cond->mEvents[_CONDITION_EVENT_ONE]);
+ cond->mEvents[_CONDITION_EVENT_ONE] = NULL;
+ return thrd_error;
+ }
+
+ return thrd_success;
+#else
+ return pthread_cond_init(cond, NULL) == 0 ? thrd_success : thrd_error;
+#endif
+}
+
+void cnd_destroy(cnd_t *cond)
+{
+#if defined(_TTHREAD_WIN32_)
+ if (cond->mEvents[_CONDITION_EVENT_ONE] != NULL)
+ {
+ CloseHandle(cond->mEvents[_CONDITION_EVENT_ONE]);
+ }
+ if (cond->mEvents[_CONDITION_EVENT_ALL] != NULL)
+ {
+ CloseHandle(cond->mEvents[_CONDITION_EVENT_ALL]);
+ }
+ DeleteCriticalSection(&cond->mWaitersCountLock);
+#else
+ pthread_cond_destroy(cond);
+#endif
+}
+
+int cnd_signal(cnd_t *cond)
+{
+#if defined(_TTHREAD_WIN32_)
+ int haveWaiters;
+
+ /* Are there any waiters? */
+ EnterCriticalSection(&cond->mWaitersCountLock);
+ haveWaiters = (cond->mWaitersCount > 0);
+ LeaveCriticalSection(&cond->mWaitersCountLock);
+
+ /* If we have any waiting threads, send them a signal */
+ if(haveWaiters)
+ {
+ if (SetEvent(cond->mEvents[_CONDITION_EVENT_ONE]) == 0)
+ {
+ return thrd_error;
+ }
+ }
+
+ return thrd_success;
+#else
+ return pthread_cond_signal(cond) == 0 ? thrd_success : thrd_error;
+#endif
+}
+
+int cnd_broadcast(cnd_t *cond)
+{
+#if defined(_TTHREAD_WIN32_)
+ int haveWaiters;
+
+ /* Are there any waiters? */
+ EnterCriticalSection(&cond->mWaitersCountLock);
+ haveWaiters = (cond->mWaitersCount > 0);
+ LeaveCriticalSection(&cond->mWaitersCountLock);
+
+ /* If we have any waiting threads, send them a signal */
+ if(haveWaiters)
+ {
+ if (SetEvent(cond->mEvents[_CONDITION_EVENT_ALL]) == 0)
+ {
+ return thrd_error;
+ }
+ }
+
+ return thrd_success;
+#else
+ return pthread_cond_signal(cond) == 0 ? thrd_success : thrd_error;
+#endif
+}
+
+#if defined(_TTHREAD_WIN32_)
+static int _cnd_timedwait_win32(cnd_t *cond, mtx_t *mtx, DWORD timeout)
+{
+ int result, lastWaiter;
+
+ /* Increment number of waiters */
+ EnterCriticalSection(&cond->mWaitersCountLock);
+ ++ cond->mWaitersCount;
+ LeaveCriticalSection(&cond->mWaitersCountLock);
+
+ /* Release the mutex while waiting for the condition (will decrease
+ the number of waiters when done)... */
+ mtx_unlock(mtx);
+
+ /* Wait for either event to become signaled due to cnd_signal() or
+ cnd_broadcast() being called */
+ result = WaitForMultipleObjects(2, cond->mEvents, FALSE, timeout);
+ if (result == WAIT_TIMEOUT)
+ {
+ return thrd_timeout;
+ }
+ else if (result == (int)WAIT_FAILED)
+ {
+ return thrd_error;
+ }
+
+ /* Check if we are the last waiter */
+ EnterCriticalSection(&cond->mWaitersCountLock);
+ -- cond->mWaitersCount;
+ lastWaiter = (result == (WAIT_OBJECT_0 + _CONDITION_EVENT_ALL)) &&
+ (cond->mWaitersCount == 0);
+ LeaveCriticalSection(&cond->mWaitersCountLock);
+
+ /* If we are the last waiter to be notified to stop waiting, reset the event */
+ if (lastWaiter)
+ {
+ if (ResetEvent(cond->mEvents[_CONDITION_EVENT_ALL]) == 0)
+ {
+ return thrd_error;
+ }
+ }
+
+ /* Re-acquire the mutex */
+ mtx_lock(mtx);
+
+ return thrd_success;
+}
+#endif
+
+int cnd_wait(cnd_t *cond, mtx_t *mtx)
+{
+#if defined(_TTHREAD_WIN32_)
+ return _cnd_timedwait_win32(cond, mtx, INFINITE);
+#else
+ return pthread_cond_wait(cond, mtx) == 0 ? thrd_success : thrd_error;
+#endif
+}
+
+int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts)
+{
+#if defined(_TTHREAD_WIN32_)
+ struct timespec now;
+ if (clock_gettime(CLOCK_REALTIME, &now) == 0)
+ {
+ DWORD delta = (DWORD) ((ts->tv_sec - now.tv_sec) * 1000 +
+ (ts->tv_nsec - now.tv_nsec + 500000) / 1000000);
+ return _cnd_timedwait_win32(cond, mtx, delta);
+ }
+ else
+ return thrd_error;
+#else
+ int ret;
+ ret = pthread_cond_timedwait(cond, mtx, ts);
+ if (ret == ETIMEDOUT)
+ {
+ return thrd_timeout;
+ }
+ return ret == 0 ? thrd_success : thrd_error;
+#endif
+}
+
+
+/** Information to pass to the new thread (what to run). */
+typedef struct {
+ thrd_start_t mFunction; /**< Pointer to the function to be executed. */
+ void * mArg; /**< Function argument for the thread function. */
+} _thread_start_info;
+
+/* Thread wrapper function. */
+#if defined(_TTHREAD_WIN32_)
+static unsigned WINAPI _thrd_wrapper_function(void * aArg)
+#elif defined(_TTHREAD_POSIX_)
+static void * _thrd_wrapper_function(void * aArg)
+#endif
+{
+ thrd_start_t fun;
+ void *arg;
+ int res;
+#if defined(_TTHREAD_POSIX_)
+ void *pres;
+#endif
+
+ /* Get thread startup information */
+ _thread_start_info *ti = (_thread_start_info *) aArg;
+ fun = ti->mFunction;
+ arg = ti->mArg;
+
+ /* The thread is responsible for freeing the startup information */
+ free((void *)ti);
+
+ /* Call the actual client thread function */
+ res = fun(arg);
+
+#if defined(_TTHREAD_WIN32_)
+ return res;
+#else
+ pres = malloc(sizeof(int));
+ if (pres != NULL)
+ {
+ *(int*)pres = res;
+ }
+ return pres;
+#endif
+}
+
+int thrd_create(thrd_t *thr, thrd_start_t func, void *arg)
+{
+ /* Fill out the thread startup information (passed to the thread wrapper,
+ which will eventually free it) */
+ _thread_start_info* ti = (_thread_start_info*)malloc(sizeof(_thread_start_info));
+ if (ti == NULL)
+ {
+ return thrd_nomem;
+ }
+ ti->mFunction = func;
+ ti->mArg = arg;
+
+ /* Create the thread */
+#if defined(_TTHREAD_WIN32_)
+ *thr = (HANDLE)_beginthreadex(NULL, 0, _thrd_wrapper_function, (void *)ti, 0, NULL);
+#elif defined(_TTHREAD_POSIX_)
+ if(pthread_create(thr, NULL, _thrd_wrapper_function, (void *)ti) != 0)
+ {
+ *thr = 0;
+ }
+#endif
+
+ /* Did we fail to create the thread? */
+ if(!*thr)
+ {
+ free(ti);
+ return thrd_error;
+ }
+
+ return thrd_success;
+}
+
+thrd_t thrd_current(void)
+{
+#if defined(_TTHREAD_WIN32_)
+ return GetCurrentThread();
+#else
+ return pthread_self();
+#endif
+}
+
+int thrd_detach(thrd_t thr)
+{
+ /* FIXME! */
+ (void)thr;
+ return thrd_error;
+}
+
+int thrd_equal(thrd_t thr0, thrd_t thr1)
+{
+#if defined(_TTHREAD_WIN32_)
+ return thr0 == thr1;
+#else
+ return pthread_equal(thr0, thr1);
+#endif
+}
+
+void thrd_exit(int res)
+{
+#if defined(_TTHREAD_WIN32_)
+ ExitThread(res);
+#else
+ void *pres = malloc(sizeof(int));
+ if (pres != NULL)
+ {
+ *(int*)pres = res;
+ }
+ pthread_exit(pres);
+#endif
+}
+
+int thrd_join(thrd_t thr, int *res)
+{
+#if defined(_TTHREAD_WIN32_)
+ if (WaitForSingleObject(thr, INFINITE) == WAIT_FAILED)
+ {
+ return thrd_error;
+ }
+ if (res != NULL)
+ {
+ DWORD dwRes;
+ GetExitCodeThread(thr, &dwRes);
+ *res = dwRes;
+ }
+#elif defined(_TTHREAD_POSIX_)
+ void *pres;
+ int ires = 0;
+ if (pthread_join(thr, &pres) != 0)
+ {
+ return thrd_error;
+ }
+ if (pres != NULL)
+ {
+ ires = *(int*)pres;
+ free(pres);
+ }
+ if (res != NULL)
+ {
+ *res = ires;
+ }
+#endif
+ return thrd_success;
+}
+
+int thrd_sleep(const struct timespec *time_point, struct timespec *remaining)
+{
+ struct timespec now;
+#if defined(_TTHREAD_WIN32_)
+ DWORD delta;
+#else
+ long delta;
+#endif
+
+ /* Get the current time */
+ if (clock_gettime(CLOCK_REALTIME, &now) != 0)
+ return -2; // FIXME: Some specific error code?
+
+#if defined(_TTHREAD_WIN32_)
+ /* Delta in milliseconds */
+ delta = (DWORD) ((time_point->tv_sec - now.tv_sec) * 1000 +
+ (time_point->tv_nsec - now.tv_nsec + 500000) / 1000000);
+ if (delta > 0)
+ {
+ Sleep(delta);
+ }
+#else
+ /* Delta in microseconds */
+ delta = (time_point->tv_sec - now.tv_sec) * 1000000L +
+ (time_point->tv_nsec - now.tv_nsec + 500L) / 1000L;
+
+ /* On some systems, the usleep argument must be < 1000000 */
+ while (delta > 999999L)
+ {
+ usleep(999999);
+ delta -= 999999L;
+ }
+ if (delta > 0L)
+ {
+ usleep((useconds_t)delta);
+ }
+#endif
+
+ /* We don't support waking up prematurely (yet) */
+ if (remaining)
+ {
+ remaining->tv_sec = 0;
+ remaining->tv_nsec = 0;
+ }
+ return 0;
+}
+
+void thrd_yield(void)
+{
+#if defined(_TTHREAD_WIN32_)
+ Sleep(0);
+#else
+ sched_yield();
+#endif
+}
+
+int tss_create(tss_t *key, tss_dtor_t dtor)
+{
+#if defined(_TTHREAD_WIN32_)
+ /* FIXME: The destructor function is not supported yet... */
+ if (dtor != NULL)
+ {
+ return thrd_error;
+ }
+ *key = TlsAlloc();
+ if (*key == TLS_OUT_OF_INDEXES)
+ {
+ return thrd_error;
+ }
+#else
+ if (pthread_key_create(key, dtor) != 0)
+ {
+ return thrd_error;
+ }
+#endif
+ return thrd_success;
+}
+
+void tss_delete(tss_t key)
+{
+#if defined(_TTHREAD_WIN32_)
+ TlsFree(key);
+#else
+ pthread_key_delete(key);
+#endif
+}
+
+void *tss_get(tss_t key)
+{
+#if defined(_TTHREAD_WIN32_)
+ return TlsGetValue(key);
+#else
+ return pthread_getspecific(key);
+#endif
+}
+
+int tss_set(tss_t key, void *val)
+{
+#if defined(_TTHREAD_WIN32_)
+ if (TlsSetValue(key, val) == 0)
+ {
+ return thrd_error;
+ }
+#else
+ if (pthread_setspecific(key, val) != 0)
+ {
+ return thrd_error;
+ }
+#endif
+ return thrd_success;
+}
+
+#if defined(_TTHREAD_EMULATE_CLOCK_GETTIME_)
+int _tthread_clock_gettime(clockid_t clk_id, struct timespec *ts)
+{
+#if defined(_TTHREAD_WIN32_)
+ struct _timeb tb;
+ _ftime(&tb);
+ ts->tv_sec = (time_t)tb.time;
+ ts->tv_nsec = 1000000L * (long)tb.millitm;
+#else
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ ts->tv_sec = (time_t)tv.tv_sec;
+ ts->tv_nsec = 1000L * (long)tv.tv_usec;
+#endif
+ return 0;
+}
+#endif // _TTHREAD_EMULATE_CLOCK_GETTIME_
+
--- /dev/null
+/* -*- mode: c; tab-width: 2; indent-tabs-mode: nil; -*-
+Copyright (c) 2012 Marcus Geelnard
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#ifndef _TINYCTHREAD_H_
+#define _TINYCTHREAD_H_
+
+/**
+* @file
+* @mainpage TinyCThread API Reference
+*
+* @section intro_sec Introduction
+* TinyCThread is a minimal, portable implementation of basic threading
+* classes for C.
+*
+* They closely mimic the functionality and naming of the C11 standard, and
+* should be easily replaceable with the corresponding standard variants.
+*
+* @section port_sec Portability
+* The Win32 variant uses the native Win32 API for implementing the thread
+* classes, while for other systems, the POSIX threads API (pthread) is used.
+*
+* @section misc_sec Miscellaneous
+* The following special keywords are available: #_Thread_local.
+*
+* For more detailed information, browse the different sections of this
+* documentation. A good place to start is:
+* tinycthread.h.
+*/
+
+/* Which platform are we on? */
+#if !defined(_TTHREAD_PLATFORM_DEFINED_)
+ #if defined(_WIN32) || defined(__WIN32__) || defined(__WINDOWS__)
+ #define _TTHREAD_WIN32_
+ #else
+ #define _TTHREAD_POSIX_
+ #endif
+ #define _TTHREAD_PLATFORM_DEFINED_
+#endif
+
+/* Activate some POSIX functionality (e.g. clock_gettime and recursive mutexes) */
+#if defined(_TTHREAD_POSIX_)
+ #undef _FEATURES_H
+ #if !defined(_GNU_SOURCE)
+ #define _GNU_SOURCE
+ #endif
+ #if !defined(_POSIX_C_SOURCE) || ((_POSIX_C_SOURCE - 0) < 199309L)
+ #undef _POSIX_C_SOURCE
+ #define _POSIX_C_SOURCE 199309L
+ #endif
+ #if !defined(_XOPEN_SOURCE) || ((_XOPEN_SOURCE - 0) < 500)
+ #undef _XOPEN_SOURCE
+ #define _XOPEN_SOURCE 500
+ #endif
+#endif
+
+/* Generic includes */
+#include <time.h>
+
+/* Platform specific includes */
+#if defined(_TTHREAD_POSIX_)
+ #include <sys/time.h>
+ #include <pthread.h>
+#elif defined(_TTHREAD_WIN32_)
+ #ifndef WIN32_LEAN_AND_MEAN
+ #define WIN32_LEAN_AND_MEAN
+ #define __UNDEF_LEAN_AND_MEAN
+ #endif
+ #include <windows.h>
+ #ifdef __UNDEF_LEAN_AND_MEAN
+ #undef WIN32_LEAN_AND_MEAN
+ #undef __UNDEF_LEAN_AND_MEAN
+ #endif
+#endif
+
+/* Workaround for missing TIME_UTC: If time.h doesn't provide TIME_UTC,
+ it's quite likely that libc does not support it either. Hence, fall back to
+ the only other supported time specifier: CLOCK_REALTIME (and if that fails,
+ we're probably emulating clock_gettime anyway, so anything goes). */
+#ifndef TIME_UTC
+ #ifdef CLOCK_REALTIME
+ #define TIME_UTC CLOCK_REALTIME
+ #else
+ #define TIME_UTC 0
+ #endif
+#endif
+
+/* Workaround for missing clock_gettime (most Windows compilers, afaik) */
+#if defined(_TTHREAD_WIN32_) || defined(__APPLE_CC__)
+#define _TTHREAD_EMULATE_CLOCK_GETTIME_
+/* Emulate struct timespec */
+#if defined(_TTHREAD_WIN32_)
+struct _ttherad_timespec {
+ time_t tv_sec;
+ long tv_nsec;
+};
+#define timespec _ttherad_timespec
+#endif
+
+/* Emulate clockid_t */
+typedef int _tthread_clockid_t;
+#define clockid_t _tthread_clockid_t
+
+/* Emulate clock_gettime */
+int _tthread_clock_gettime(clockid_t clk_id, struct timespec *ts);
+#define clock_gettime _tthread_clock_gettime
+#ifndef CLOCK_REALTIME
+ #define CLOCK_REALTIME 0
+#endif
+#endif
+
+
+/** TinyCThread version (major number). */
+#define TINYCTHREAD_VERSION_MAJOR 1
+/** TinyCThread version (minor number). */
+#define TINYCTHREAD_VERSION_MINOR 1
+/** TinyCThread version (full version). */
+#define TINYCTHREAD_VERSION (TINYCTHREAD_VERSION_MAJOR * 100 + TINYCTHREAD_VERSION_MINOR)
+
+/**
+* @def _Thread_local
+* Thread local storage keyword.
+* A variable that is declared with the @c _Thread_local keyword makes the
+* value of the variable local to each thread (known as thread-local storage,
+* or TLS). Example usage:
+* @code
+* // This variable is local to each thread.
+* _Thread_local int variable;
+* @endcode
+* @note The @c _Thread_local keyword is a macro that maps to the corresponding
+* compiler directive (e.g. @c __declspec(thread)).
+* @note This directive is currently not supported on Mac OS X (it will give
+* a compiler error), since compile-time TLS is not supported in the Mac OS X
+* executable format. Also, some older versions of MinGW (before GCC 4.x) do
+* not support this directive.
+* @hideinitializer
+*/
+
+/* FIXME: Check for a PROPER value of __STDC_VERSION__ to know if we have C11 */
+#if !(defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201102L)) && !defined(_Thread_local)
+ #if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__SUNPRO_CC) || defined(__IBMCPP__)
+ #define _Thread_local __thread
+ #else
+ #define _Thread_local __declspec(thread)
+ #endif
+#endif
+
+/* Macros */
+#define TSS_DTOR_ITERATIONS 0
+
+/* Function return values */
+#define thrd_error 0 /**< The requested operation failed */
+#define thrd_success 1 /**< The requested operation succeeded */
+#define thrd_timeout 2 /**< The time specified in the call was reached without acquiring the requested resource */
+#define thrd_busy 3 /**< The requested operation failed because a tesource requested by a test and return function is already in use */
+#define thrd_nomem 4 /**< The requested operation failed because it was unable to allocate memory */
+
+/* Mutex types */
+#define mtx_plain 1
+#define mtx_timed 2
+#define mtx_try 4
+#define mtx_recursive 8
+
+/* Mutex */
+#if defined(_TTHREAD_WIN32_)
+typedef struct {
+ CRITICAL_SECTION mHandle; /* Critical section handle */
+ int mAlreadyLocked; /* TRUE if the mutex is already locked */
+ int mRecursive; /* TRUE if the mutex is recursive */
+} mtx_t;
+#else
+typedef pthread_mutex_t mtx_t;
+#endif
+
+/** Create a mutex object.
+* @param mtx A mutex object.
+* @param type Bit-mask that must have one of the following six values:
+* @li @c mtx_plain for a simple non-recursive mutex
+* @li @c mtx_timed for a non-recursive mutex that supports timeout
+* @li @c mtx_try for a non-recursive mutex that supports test and return
+* @li @c mtx_plain | @c mtx_recursive (same as @c mtx_plain, but recursive)
+* @li @c mtx_timed | @c mtx_recursive (same as @c mtx_timed, but recursive)
+* @li @c mtx_try | @c mtx_recursive (same as @c mtx_try, but recursive)
+* @return @ref thrd_success on success, or @ref thrd_error if the request could
+* not be honored.
+*/
+int mtx_init(mtx_t *mtx, int type);
+
+/** Release any resources used by the given mutex.
+* @param mtx A mutex object.
+*/
+void mtx_destroy(mtx_t *mtx);
+
+/** Lock the given mutex.
+* Blocks until the given mutex can be locked. If the mutex is non-recursive, and
+* the calling thread already has a lock on the mutex, this call will block
+* forever.
+* @param mtx A mutex object.
+* @return @ref thrd_success on success, or @ref thrd_error if the request could
+* not be honored.
+*/
+int mtx_lock(mtx_t *mtx);
+
+/** NOT YET IMPLEMENTED.
+*/
+int mtx_timedlock(mtx_t *mtx, const struct timespec *ts);
+
+/** Try to lock the given mutex.
+* The specified mutex shall support either test and return or timeout. If the
+* mutex is already locked, the function returns without blocking.
+* @param mtx A mutex object.
+* @return @ref thrd_success on success, or @ref thrd_busy if the resource
+* requested is already in use, or @ref thrd_error if the request could not be
+* honored.
+*/
+int mtx_trylock(mtx_t *mtx);
+
+/** Unlock the given mutex.
+* @param mtx A mutex object.
+* @return @ref thrd_success on success, or @ref thrd_error if the request could
+* not be honored.
+*/
+int mtx_unlock(mtx_t *mtx);
+
+/* Condition variable */
+#if defined(_TTHREAD_WIN32_)
+typedef struct {
+ HANDLE mEvents[2]; /* Signal and broadcast event HANDLEs. */
+ unsigned int mWaitersCount; /* Count of the number of waiters. */
+ CRITICAL_SECTION mWaitersCountLock; /* Serialize access to mWaitersCount. */
+} cnd_t;
+#else
+typedef pthread_cond_t cnd_t;
+#endif
+
+/** Create a condition variable object.
+* @param cond A condition variable object.
+* @return @ref thrd_success on success, or @ref thrd_error if the request could
+* not be honored.
+*/
+int cnd_init(cnd_t *cond);
+
+/** Release any resources used by the given condition variable.
+* @param cond A condition variable object.
+*/
+void cnd_destroy(cnd_t *cond);
+
+/** Signal a condition variable.
+* Unblocks one of the threads that are blocked on the given condition variable
+* at the time of the call. If no threads are blocked on the condition variable
+* at the time of the call, the function does nothing and return success.
+* @param cond A condition variable object.
+* @return @ref thrd_success on success, or @ref thrd_error if the request could
+* not be honored.
+*/
+int cnd_signal(cnd_t *cond);
+
+/** Broadcast a condition variable.
+* Unblocks all of the threads that are blocked on the given condition variable
+* at the time of the call. If no threads are blocked on the condition variable
+* at the time of the call, the function does nothing and return success.
+* @param cond A condition variable object.
+* @return @ref thrd_success on success, or @ref thrd_error if the request could
+* not be honored.
+*/
+int cnd_broadcast(cnd_t *cond);
+
+/** Wait for a condition variable to become signaled.
+* The function atomically unlocks the given mutex and endeavors to block until
+* the given condition variable is signaled by a call to cnd_signal or to
+* cnd_broadcast. When the calling thread becomes unblocked it locks the mutex
+* before it returns.
+* @param cond A condition variable object.
+* @param mtx A mutex object.
+* @return @ref thrd_success on success, or @ref thrd_error if the request could
+* not be honored.
+*/
+int cnd_wait(cnd_t *cond, mtx_t *mtx);
+
+/** Wait for a condition variable to become signaled.
+* The function atomically unlocks the given mutex and endeavors to block until
+* the given condition variable is signaled by a call to cnd_signal or to
+* cnd_broadcast, or until after the specified time. When the calling thread
+* becomes unblocked it locks the mutex before it returns.
+* @param cond A condition variable object.
+* @param mtx A mutex object.
+* @param xt A point in time at which the request will time out (absolute time).
+* @return @ref thrd_success upon success, or @ref thrd_timeout if the time
+* specified in the call was reached without acquiring the requested resource, or
+* @ref thrd_error if the request could not be honored.
+*/
+int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts);
+
+/* Thread */
+#if defined(_TTHREAD_WIN32_)
+typedef HANDLE thrd_t;
+#else
+typedef pthread_t thrd_t;
+#endif
+
+/** Thread start function.
+* Any thread that is started with the @ref thrd_create() function must be
+* started through a function of this type.
+* @param arg The thread argument (the @c arg argument of the corresponding
+* @ref thrd_create() call).
+* @return The thread return value, which can be obtained by another thread
+* by using the @ref thrd_join() function.
+*/
+typedef int (*thrd_start_t)(void *arg);
+
+/** Create a new thread.
+* @param thr Identifier of the newly created thread.
+* @param func A function pointer to the function that will be executed in
+* the new thread.
+* @param arg An argument to the thread function.
+* @return @ref thrd_success on success, or @ref thrd_nomem if no memory could
+* be allocated for the thread requested, or @ref thrd_error if the request
+* could not be honored.
+* @note A thread’s identifier may be reused for a different thread once the
+* original thread has exited and either been detached or joined to another
+* thread.
+*/
+int thrd_create(thrd_t *thr, thrd_start_t func, void *arg);
+
+/** Identify the calling thread.
+* @return The identifier of the calling thread.
+*/
+thrd_t thrd_current(void);
+
+/** NOT YET IMPLEMENTED.
+*/
+int thrd_detach(thrd_t thr);
+
+/** Compare two thread identifiers.
+* The function determines if two thread identifiers refer to the same thread.
+* @return Zero if the two thread identifiers refer to different threads.
+* Otherwise a nonzero value is returned.
+*/
+int thrd_equal(thrd_t thr0, thrd_t thr1);
+
+/** Terminate execution of the calling thread.
+* @param res Result code of the calling thread.
+*/
+void thrd_exit(int res);
+
+/** Wait for a thread to terminate.
+* The function joins the given thread with the current thread by blocking
+* until the other thread has terminated.
+* @param thr The thread to join with.
+* @param res If this pointer is not NULL, the function will store the result
+* code of the given thread in the integer pointed to by @c res.
+* @return @ref thrd_success on success, or @ref thrd_error if the request could
+* not be honored.
+*/
+int thrd_join(thrd_t thr, int *res);
+
+/** Put the calling thread to sleep.
+* Suspend execution of the calling thread.
+* @param time_point A point in time at which the thread will resume (absolute time).
+* @param remaining If non-NULL, this parameter will hold the remaining time until
+* time_point upon return. This will typically be zero, but if
+* the thread was woken up by a signal that is not ignored before
+* time_point was reached @c remaining will hold a positive
+* time.
+* @return 0 (zero) on successful sleep, or -1 if an interrupt occurred.
+*/
+int thrd_sleep(const struct timespec *time_point, struct timespec *remaining);
+
+/** Yield execution to another thread.
+* Permit other threads to run, even if the current thread would ordinarily
+* continue to run.
+*/
+void thrd_yield(void);
+
+/* Thread local storage */
+#if defined(_TTHREAD_WIN32_)
+typedef DWORD tss_t;
+#else
+typedef pthread_key_t tss_t;
+#endif
+
+/** Destructor function for a thread-specific storage.
+* @param val The value of the destructed thread-specific storage.
+*/
+typedef void (*tss_dtor_t)(void *val);
+
+/** Create a thread-specific storage.
+* @param key The unique key identifier that will be set if the function is
+* successful.
+* @param dtor Destructor function. This can be NULL.
+* @return @ref thrd_success on success, or @ref thrd_error if the request could
+* not be honored.
+* @note The destructor function is not supported under Windows. If @c dtor is
+* not NULL when calling this function under Windows, the function will fail
+* and return @ref thrd_error.
+*/
+int tss_create(tss_t *key, tss_dtor_t dtor);
+
+/** Delete a thread-specific storage.
+* The function releases any resources used by the given thread-specific
+* storage.
+* @param key The key that shall be deleted.
+*/
+void tss_delete(tss_t key);
+
+/** Get the value for a thread-specific storage.
+* @param key The thread-specific storage identifier.
+* @return The value for the current thread held in the given thread-specific
+* storage.
+*/
+void *tss_get(tss_t key);
+
+/** Set the value for a thread-specific storage.
+* @param key The thread-specific storage identifier.
+* @param val The value of the thread-specific storage to set for the current
+* thread.
+* @return @ref thrd_success on success, or @ref thrd_error if the request could
+* not be honored.
+*/
+int tss_set(tss_t key, void *val);
+
+
+#endif /* _TINYTHREAD_H_ */
+
ctx->cmdPool = vkh_cmd_pool_create ((VkhDevice)dev, dev->gQueue->familyIndex, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT);
_create_vertices_buff (ctx);
- _create_gradient_buff (ctx);
+
+ ctx->th_objs = _get_or_create_threaded_objects(ctx->pSurf->dev, thrd_current());
+
_create_cmd_buff (ctx);
_createDescriptorPool (ctx);
_init_descriptor_sets (ctx);
_update_descriptor_set (ctx, ctx->pSurf->dev->fontCache->texture, ctx->dsFont);
_update_descriptor_set (ctx, surf->dev->emptyImg, ctx->dsSrc);
- _update_gradient_desc_set(ctx);
_clear_path (ctx);
vkFreeCommandBuffers(dev, ctx->cmdPool, 2, ctx->cmdBuffers);
vkDestroyCommandPool(dev, ctx->cmdPool, NULL);
- VkDescriptorSet dss[] = {ctx->dsFont,ctx->dsSrc, ctx->dsGrad};
- vkFreeDescriptorSets (dev, ctx->descriptorPool, 3, dss);
+ VkDescriptorSet dss[] = {ctx->dsFont,ctx->dsSrc};
+ vkFreeDescriptorSets (dev, ctx->descriptorPool, 2, dss);
vkDestroyDescriptorPool (dev, ctx->descriptorPool,NULL);
- vkvg_buffer_destroy (&ctx->uboGrad);
vkvg_buffer_destroy (&ctx->indices);
vkvg_buffer_destroy (&ctx->vertices);
#include "vkvg_surface_internal.h"
#include "vkvg_context_internal.h"
-#include "vkvg_device_internal.h"
#include "vkvg_pattern.h"
#include "vkh_queue.h"
#include "vkh_image.h"
return fminf(M_PI / 3.f, M_PI / r);
return fminf(M_PI / 3.f,M_PI / (r * 0.4f));
}
-void _create_gradient_buff (VkvgContext ctx){
- vkvg_buffer_create (ctx->pSurf->dev,
- VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
- VMA_MEMORY_USAGE_CPU_TO_GPU,
- sizeof(vkvg_gradient_t), &ctx->uboGrad);
-}
+
void _create_vertices_buff (VkvgContext ctx){
vkvg_buffer_create (ctx->pSurf->dev,
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
CmdSetScissor(ctx->cmd, 0, 1, &ctx->bounds);
- VkDescriptorSet dss[] = {ctx->dsFont, ctx->dsSrc,ctx->dsGrad};
+ VkDescriptorSet dss[] = {ctx->dsFont, ctx->dsSrc,ctx->th_objs->dsGrad};
CmdBindDescriptorSets(ctx->cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, ctx->pSurf->dev->pipelineLayout,
0, 3, dss, 0, NULL);
vkvg_matrix_transform_distance (&ctx->pushConsts.mat, &grad.cp[1].z, &grad.cp[0].w);
}
- memcpy (ctx->uboGrad.allocInfo.pMappedData , &grad, sizeof(vkvg_gradient_t));
- vkvg_buffer_flush(&ctx->uboGrad);
+ memcpy (ctx->th_objs->uboGrad.allocInfo.pMappedData , &grad, sizeof(vkvg_gradient_t));
+ vkvg_buffer_flush(&ctx->th_objs->uboGrad);
break;
}
ctx->pushConsts.fsq_patternType = (ctx->pushConsts.fsq_patternType & FULLSCREEN_BIT) + newPatternType;
};
vkUpdateDescriptorSets(ctx->pSurf->dev->vkDev, 1, &writeDescriptorSet, 0, NULL);
}
-void _update_gradient_desc_set (VkvgContext ctx){
- VkDescriptorBufferInfo dbi = {ctx->uboGrad.buffer, 0, VK_WHOLE_SIZE};
- VkWriteDescriptorSet writeDescriptorSet = {
- .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
- .dstSet = ctx->dsGrad,
- .dstBinding = 0,
- .descriptorCount = 1,
- .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
- .pBufferInfo = &dbi
- };
- vkUpdateDescriptorSets(ctx->pSurf->dev->vkDev, 1, &writeDescriptorSet, 0, NULL);
-}
/*
* Reset currently bound descriptor which image could be destroyed
*/
VK_CHECK_RESULT(vkAllocateDescriptorSets(dev->vkDev, &descriptorSetAllocateInfo, &ctx->dsFont));
descriptorSetAllocateInfo.pSetLayouts = &dev->dslSrc;
VK_CHECK_RESULT(vkAllocateDescriptorSets(dev->vkDev, &descriptorSetAllocateInfo, &ctx->dsSrc));
- descriptorSetAllocateInfo.pSetLayouts = &dev->dslGrad;
- VK_CHECK_RESULT(vkAllocateDescriptorSets(dev->vkDev, &descriptorSetAllocateInfo, &ctx->dsGrad));
}
//populate vertice buff for stroke
bool _build_vb_step (vkvg_context* ctx, float hw, stroke_context_t* str, bool isCurve){
#include "vkvg_buff.h"
#include "vkh.h"
#include "vkvg_fonts.h"
+#include "vkvg_device_internal.h"
#if VKVG_RECORDING
#include "recording/vkvg_record_internal.h"
VkDescriptorPool descriptorPool;//one pool per thread
VkDescriptorSet dsFont; //fonts glyphs texture atlas descriptor (local for thread safety)
VkDescriptorSet dsSrc; //source ds
- VkDescriptorSet dsGrad; //gradient uniform buffer
VkRect2D bounds;
vkvg_recording_t* recording;
#endif
- vkvg_buff uboGrad; //uniform buff obj holdings gradient infos
+ vkvg_device_thread_items_t* th_objs;
//vk buffers, holds data until flush
vkvg_buff indices; //index buffer with persistent map memory
void _fill_non_zero (VkvgContext ctx);
void _draw_full_screen_quad (VkvgContext ctx, bool useScissor);
-void _create_gradient_buff (VkvgContext ctx);
void _create_vertices_buff (VkvgContext ctx);
void _add_vertex (VkvgContext ctx, Vertex v);
void _add_vertexf (VkvgContext ctx, float x, float y);
void _createDescriptorPool (VkvgContext ctx);
void _init_descriptor_sets (VkvgContext ctx);
void _update_descriptor_set (VkvgContext ctx, VkhImage img, VkDescriptorSet ds);
-void _update_gradient_desc_set(VkvgContext ctx);
void _free_ctx_save (vkvg_context_save_t* sav);
static inline float vec2_zcross (vec2 v1, vec2 v2){
#include "vkvg_device_internal.h"
#include "vkvg_context_internal.h"
#include "shaders.h"
+#include "vkvg_pattern.h"
uint32_t vkvg_log_level = VKVG_LOG_DEBUG;
#ifdef VKVG_WIRED_DEBUG
PFN_vkResetFences ResetFences;
PFN_vkResetCommandBuffer ResetCommandBuffer;
+void _delete_threaded_object (VkvgDevice dev, vkvg_device_thread_items_t* throbjs) {
+ vkvg_buffer_destroy (&throbjs->uboGrad);
+
+ vkDestroyDescriptorPool (dev->vkDev, throbjs->descriptorPool,NULL);
+
+ free (throbjs);
+}
+void _add_threaded_objects (VkvgDevice dev, vkvg_device_thread_items_t* throbjs) {
+ vkvg_device_thread_items_t* tmp = dev->threaded_objects;
+ if (!tmp) {
+ dev->threaded_objects = throbjs;
+ return;
+ }
+ while (tmp->next)
+ tmp = tmp->next;
+ tmp->next = throbjs;
+}
+
+vkvg_device_thread_items_t* _get_or_create_threaded_objects (VkvgDevice dev, thrd_t id) {
+ vkvg_device_thread_items_t* tmp = dev->threaded_objects;
+ while (tmp) {
+ if (thrd_equal(tmp->id, id))
+ return tmp;
+ tmp = tmp->next;
+ }
+ tmp = (vkvg_device_thread_items_t*)calloc(1, sizeof(vkvg_device_thread_items_t));
+ tmp->id = thrd_current();
+
+ const VkDescriptorPoolSize descriptorPoolSize[] = {
+ {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 2 },
+ {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1 }
+ };
+ VkDescriptorPoolCreateInfo descriptorPoolCreateInfo = { .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
+ .maxSets = 3,
+ .flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT,
+ .poolSizeCount = 2,
+ .pPoolSizes = descriptorPoolSize };
+ VK_CHECK_RESULT(vkCreateDescriptorPool (dev->vkDev, &descriptorPoolCreateInfo, NULL, &tmp->descriptorPool));
+
+ VkDescriptorSetAllocateInfo descriptorSetAllocateInfo = { .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
+ .descriptorPool = tmp->descriptorPool,
+ .descriptorSetCount = 1,
+ .pSetLayouts = &dev->dslGrad };
+ VK_CHECK_RESULT(vkAllocateDescriptorSets(dev->vkDev, &descriptorSetAllocateInfo, &tmp->dsGrad));
+
+ vkvg_buffer_create (dev,
+ VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
+ VMA_MEMORY_USAGE_CPU_TO_GPU,
+ sizeof(vkvg_gradient_t), &tmp->uboGrad);
+
+ VkDescriptorBufferInfo dbi = {tmp->uboGrad.buffer, 0, VK_WHOLE_SIZE};
+ VkWriteDescriptorSet writeDescriptorSet = {
+ .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
+ .dstSet = tmp->dsGrad,
+ .dstBinding = 0,
+ .descriptorCount = 1,
+ .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+ .pBufferInfo = &dbi
+ };
+ vkUpdateDescriptorSets(dev->vkDev, 1, &writeDescriptorSet, 0, NULL);
+
+ _add_threaded_objects(dev, tmp);
+
+ return tmp;
+}
bool _try_get_phyinfo (VkhPhyInfo* phys, uint32_t phyCount, VkPhysicalDeviceType gpuType, VkhPhyInfo* phy) {
for (uint32_t i=0; i<phyCount; i++){
if (vkh_phyinfo_get_properties(phys[i]).deviceType == gpuType) {
extern PFN_vkResetFences ResetFences;
extern PFN_vkResetCommandBuffer ResetCommandBuffer;
+//per thread vulkan object used in contexts
+typedef struct _vkvg_device_thread_items_t {
+ thrd_t id;
+ VkDescriptorPool descriptorPool; //one pool per thread
+ VkDescriptorSet dsGrad; //gradient uniform buffer
+
+ vkvg_buff uboGrad; //uniform buff obj holdings gradient infos
+
+ struct _vkvg_device_thread_items_t* next;
+}vkvg_device_thread_items_t;
+
typedef struct _vkvg_device_t{
VkDevice vkDev; /**< Vulkan Logical Device */
VkPhysicalDeviceMemoryProperties phyMemProps; /**< Vulkan Physical device memory properties */
_font_cache_t* fontCache; /**< Store everything relative to common font caching system */
VkvgContext lastCtx; /**< last element of double linked list of context, used to trigger font caching system update on all contexts*/
+
+ vkvg_device_thread_items_t* threaded_objects;
}vkvg_device;
bool _try_get_phyinfo (VkhPhyInfo* phys, uint32_t phyCount, VkPhysicalDeviceType gpuType, VkhPhyInfo* phy);
bool _layer_is_present (const char* layerName);
void _layers_check_init ();
void _layers_check_release ();
+
+vkvg_device_thread_items_t* _get_or_create_threaded_objects (VkvgDevice dev, thrd_t id);
#endif