OPTION(ENABLE_DBG_UTILS "enable VK_EXT_debug_utils extension" ON)
OPTION(ENABLE_WIRED_FILL "enable wired polygon draw to check vertices and primitives" OFF)
IF (UNIX)
- SET(CMAKE_${LANG}_FLAGS "-Wall -Wno-extra -Wno-unknown-pragmas -Wno-missing-braces -Wno-unused-variable -Wno-switch")
+ SET(CMAKE_${LANG}_FLAGS "-rdynamic -Wall -Wno-extra -Wno-unknown-pragmas -Wno-missing-braces -Wno-unused-variable -Wno-switch")
ELSEIF(MSVC)
SET(CMAKE_${LANG}_FLAGS "/TC /W4 /wd4201 /wd4204 /wd4221 /wd4100") # c11 compliant
ENDIF()
#define VKVG_LOG_INFO_IBO 0x00000040
#define VKVG_LOG_INFO_VAO (VKVG_LOG_INFO_VBO|VKVG_LOG_INFO_IBO)
#define VKVG_LOG_DBG_ARRAYS 0x00001000
-//#define VKVG_LOG_THREADING 0x00000080
#define VKVG_LOG_FULL 0xffffffff
#define VKVG_LOG_INFO 0x00008000//(VKVG_LOG_INFO_PTS|VKVG_LOG_INFO_PATH|VKVG_LOG_INFO_CMD|VKVG_LOG_INFO_VAO)
#ifdef DEBUG
-extern uint32_t vkvg_log_level;
+ extern uint32_t vkvg_log_level;
#ifdef VKVG_WIRED_DEBUG
typedef enum {
vkvg_wired_debug_mode_normal = 0x01,
* Device holds the font cache so that each time a context draws text, the same cache is used.
*
* @{ */
-typedef void (*vkvg_device_guard)(void* user_data);
+
vkvg_public
-void vkvg_device_set_guards (VkvgDevice dev, vkvg_device_guard lock_callback, vkvg_device_guard unlock_callback, void* user_data);
+void vkvg_device_set_thread_aware (VkvgDevice dev, uint32_t thread_awayre);
/**
* @brief Create a new vkvg device.
return pw->pw_dir;
#endif
}
+
+#if __linux__
+#include <stdio.h>
+#include <execinfo.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+void handler(int sig) {
+ void *array[100];
+ size_t size;
+
+ // get void*'s for all entries on the stack
+ size = backtrace(array, 100);
+
+ // print out all the frames to stderr
+ fprintf(stderr, "Error: signal %d:\n", sig);
+ backtrace_symbols_fd(array, size, STDERR_FILENO);
+ exit(1);
+}
+
+void _linux_register_error_handler () {
+ signal(SIGSEGV, handler); // install our handler
+ signal(SIGABRT, handler); // install our handler
+}
+#endif
#define vkvg_inline static inline __attribute((always_inline))
#define disable_warning (warn) #pragma GCC diagnostic ignored "-W"#warn
#define reset_warning (warn) #pragma GCC diagnostic warning "-W"#warn
+ #if __linux__
+ void _linux_register_error_handler ();
+ #endif
#endif
const char* getUserDir ();
--- /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_ */
+
VkvgDevice dev = surf->dev;
VkvgContext ctx = NULL;
- if (_vkvg_device_try_get_cached_context (dev, &ctx) ) {
+ if (_device_try_get_cached_context (dev, &ctx) ) {
ctx->pSurf = surf;
if (!surf || surf->status) {
#endif
if (!ctx->status && ctx->dev->cachedContextCount < VKVG_MAX_CACHED_CONTEXT_COUNT) {
- _vkvg_device_store_context (ctx);
+ _device_store_context (ctx);
return;
}
if (!_wait_flush_fence (ctx))
return false;
- _vkvg_device_reset_fence(ctx->pSurf->dev, ctx->flushFence);
- _submit_cmd (ctx->pSurf->dev, &ctx->cmd, ctx->flushFence);
+ _device_reset_fence(ctx->pSurf->dev, ctx->flushFence);
+ _device_submit_cmd (ctx->pSurf->dev, &ctx->cmd, ctx->flushFence);
if (ctx->cmd == ctx->cmdBuffers[0])
ctx->cmd = ctx->cmdBuffers[1];
}
void _release_context_ressources (VkvgContext ctx) {
VkDevice dev = ctx->dev->vkDev;
- _vkvg_device_destroy_fence (ctx->dev, ctx->flushFence);
+
+ _device_destroy_fence (ctx->dev, ctx->flushFence);
vkFreeCommandBuffers(dev, ctx->cmdPool, 2, ctx->cmdBuffers);
vkDestroyCommandPool(dev, ctx->cmdPool, NULL);
_create_empty_texture (dev, format, dev->supportedTiling);
-#if defined(DEBUG) && defined (VKVG_DBG_UTILS)
- vkh_device_set_object_name((VkhDevice)dev, VK_OBJECT_TYPE_COMMAND_POOL, (uint64_t)dev->cmdPool, "Device Cmd Pool");
- vkh_device_set_object_name((VkhDevice)dev, VK_OBJECT_TYPE_COMMAND_BUFFER, (uint64_t)dev->cmd, "Device Cmd Buff");
- vkh_device_set_object_name((VkhDevice)dev, VK_OBJECT_TYPE_FENCE, (uint64_t)dev->fence, "Device Fence");
- vkh_device_set_object_name((VkhDevice)dev, VK_OBJECT_TYPE_RENDER_PASS, (uint64_t)dev->renderPass, "RP load img/stencil");
- vkh_device_set_object_name((VkhDevice)dev, VK_OBJECT_TYPE_RENDER_PASS, (uint64_t)dev->renderPass_ClearStencil, "RP clear stencil");
- vkh_device_set_object_name((VkhDevice)dev, VK_OBJECT_TYPE_RENDER_PASS, (uint64_t)dev->renderPass_ClearAll, "RP clear all");
-
- vkh_device_set_object_name((VkhDevice)dev, VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT, (uint64_t)dev->dslSrc, "DSLayout SOURCE");
- vkh_device_set_object_name((VkhDevice)dev, VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT, (uint64_t)dev->dslFont, "DSLayout FONT");
- vkh_device_set_object_name((VkhDevice)dev, VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT, (uint64_t)dev->dslGrad, "DSLayout GRADIENT");
- vkh_device_set_object_name((VkhDevice)dev, VK_OBJECT_TYPE_PIPELINE_LAYOUT, (uint64_t)dev->pipelineLayout, "PLLayout dev");
-
-#ifndef __APPLE__
- vkh_device_set_object_name((VkhDevice)dev, VK_OBJECT_TYPE_PIPELINE, (uint64_t)dev->pipelinePolyFill, "PL Poly fill");
-#endif
- vkh_device_set_object_name((VkhDevice)dev, VK_OBJECT_TYPE_PIPELINE, (uint64_t)dev->pipelineClipping, "PL Clipping");
- vkh_device_set_object_name((VkhDevice)dev, VK_OBJECT_TYPE_PIPELINE, (uint64_t)dev->pipe_OVER, "PL draw Over");
- vkh_device_set_object_name((VkhDevice)dev, VK_OBJECT_TYPE_PIPELINE, (uint64_t)dev->pipe_SUB, "PL draw Substract");
- vkh_device_set_object_name((VkhDevice)dev, VK_OBJECT_TYPE_PIPELINE, (uint64_t)dev->pipe_CLEAR, "PL draw Clear");
-
- vkh_image_set_name(dev->emptyImg, "empty IMG");
- vkh_device_set_object_name((VkhDevice)dev, VK_OBJECT_TYPE_IMAGE_VIEW, (uint64_t)vkh_image_get_view(dev->emptyImg), "empty IMG VIEW");
- vkh_device_set_object_name((VkhDevice)dev, VK_OBJECT_TYPE_SAMPLER, (uint64_t)vkh_image_get_sampler(dev->emptyImg), "empty IMG SAMPLER");
+#ifdef DEBUG
+ #if __linux__
+ _linux_register_error_handler ();
+ #endif
+ #ifdef VKVG_DBG_UTILS
+ vkh_device_set_object_name((VkhDevice)dev, VK_OBJECT_TYPE_COMMAND_POOL, (uint64_t)dev->cmdPool, "Device Cmd Pool");
+ vkh_device_set_object_name((VkhDevice)dev, VK_OBJECT_TYPE_COMMAND_BUFFER, (uint64_t)dev->cmd, "Device Cmd Buff");
+ vkh_device_set_object_name((VkhDevice)dev, VK_OBJECT_TYPE_FENCE, (uint64_t)dev->fence, "Device Fence");
+ vkh_device_set_object_name((VkhDevice)dev, VK_OBJECT_TYPE_RENDER_PASS, (uint64_t)dev->renderPass, "RP load img/stencil");
+ vkh_device_set_object_name((VkhDevice)dev, VK_OBJECT_TYPE_RENDER_PASS, (uint64_t)dev->renderPass_ClearStencil, "RP clear stencil");
+ vkh_device_set_object_name((VkhDevice)dev, VK_OBJECT_TYPE_RENDER_PASS, (uint64_t)dev->renderPass_ClearAll, "RP clear all");
+
+ vkh_device_set_object_name((VkhDevice)dev, VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT, (uint64_t)dev->dslSrc, "DSLayout SOURCE");
+ vkh_device_set_object_name((VkhDevice)dev, VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT, (uint64_t)dev->dslFont, "DSLayout FONT");
+ vkh_device_set_object_name((VkhDevice)dev, VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT, (uint64_t)dev->dslGrad, "DSLayout GRADIENT");
+ vkh_device_set_object_name((VkhDevice)dev, VK_OBJECT_TYPE_PIPELINE_LAYOUT, (uint64_t)dev->pipelineLayout, "PLLayout dev");
+
+ #ifndef __APPLE__
+ vkh_device_set_object_name((VkhDevice)dev, VK_OBJECT_TYPE_PIPELINE, (uint64_t)dev->pipelinePolyFill, "PL Poly fill");
+ #endif
+ vkh_device_set_object_name((VkhDevice)dev, VK_OBJECT_TYPE_PIPELINE, (uint64_t)dev->pipelineClipping, "PL Clipping");
+ vkh_device_set_object_name((VkhDevice)dev, VK_OBJECT_TYPE_PIPELINE, (uint64_t)dev->pipe_OVER, "PL draw Over");
+ vkh_device_set_object_name((VkhDevice)dev, VK_OBJECT_TYPE_PIPELINE, (uint64_t)dev->pipe_SUB, "PL draw Substract");
+ vkh_device_set_object_name((VkhDevice)dev, VK_OBJECT_TYPE_PIPELINE, (uint64_t)dev->pipe_CLEAR, "PL draw Clear");
+
+ vkh_image_set_name(dev->emptyImg, "empty IMG");
+ vkh_device_set_object_name((VkhDevice)dev, VK_OBJECT_TYPE_IMAGE_VIEW, (uint64_t)vkh_image_get_view(dev->emptyImg), "empty IMG VIEW");
+ vkh_device_set_object_name((VkhDevice)dev, VK_OBJECT_TYPE_SAMPLER, (uint64_t)vkh_image_get_sampler(dev->emptyImg), "empty IMG SAMPLER");
+ #endif
#endif
dev->status = VKVG_STATUS_SUCCESS;
}
void vkvg_device_destroy (VkvgDevice dev)
{
+ LOCK_DEVICE
dev->references--;
- if (dev->references > 0)
+ if (dev->references > 0) {
+ UNLOCK_DEVICE
return;
+ }
+ UNLOCK_DEVICE
while (dev->cachedContextCount > 0)
_release_context_ressources (dev->cachedContext[--dev->cachedContextCount]);
vmaDestroyAllocator (dev->allocator);
+ if (dev->threadAware)
+ mtx_destroy (&dev->mutex);
+
if (dev->vkhDev) {
VkhApp app = vkh_device_get_app (dev->vkhDev);
vkh_device_destroy (dev->vkhDev);
return dev->status;
}
VkvgDevice vkvg_device_reference (VkvgDevice dev) {
+ LOCK_DEVICE
dev->references++;
+ UNLOCK_DEVICE
return dev;
}
uint32_t vkvg_device_get_reference_count (VkvgDevice dev) {
*hdpy = dev->hdpi;
*vdpy = dev->vdpi;
}
-void vkvg_device_set_guards (VkvgDevice dev, vkvg_device_guard lock_callback, vkvg_device_guard unlock_callback, void* user_data) {
- dev->deviceLockGuard = lock_callback;
- dev->deviceUnlockGuard = unlock_callback;
- dev->deviceGuardUserData = user_data;
+void vkvg_device_set_thread_aware (VkvgDevice dev, uint32_t thread_aware) {
+ if (thread_aware) {
+ if (dev->threadAware)
+ return;
+ mtx_init (&dev->mutex, mtx_plain);
+ dev->threadAware = true;
+ } else if (dev->threadAware) {
+ mtx_destroy (&dev->mutex);
+ dev->threadAware = false;
+ }
}
#if VKVG_DBG_STATS
vkvg_debug_stats_t vkvg_device_get_stats (VkvgDevice dev) {
}
return false;
}
-void _flush_all_contexes (VkvgDevice dev){
+void _device_flush_all_contexes (VkvgDevice dev){
/*VkvgContext ctx = dev->lastCtx;
while (ctx != NULL){
if (ctx->cmdStarted)
VK_CHECK_RESULT(vkCreatePipelineLayout(dev->vkDev, &pipelineLayoutCreateInfo, NULL, &dev->pipelineLayout));
}
-void _wait_idle (VkvgDevice dev) {
+void _device_wait_idle (VkvgDevice dev) {
vkDeviceWaitIdle (dev->vkDev);
}
-void _wait_and_reset_device_fence (VkvgDevice dev) {
+void _device_wait_and_reset_device_fence (VkvgDevice dev) {
vkWaitForFences (dev->vkDev, 1, &dev->fence, VK_TRUE, UINT64_MAX);
- _vkvg_device_reset_fence(dev, dev->fence);
+ _device_reset_fence(dev, dev->fence);
}
-void _vkvg_device_destroy_fence (VkvgDevice dev, VkFence fence) {
- if (dev->deviceLockGuard)
- dev->deviceLockGuard (dev->deviceGuardUserData);
+void _device_destroy_fence (VkvgDevice dev, VkFence fence) {
+ LOCK_DEVICE
if (dev->gQLastFence == fence)
dev->gQLastFence = VK_NULL_HANDLE;
vkDestroyFence (dev->vkDev, fence, NULL);
- if (dev->deviceUnlockGuard)
- dev->deviceUnlockGuard (dev->deviceGuardUserData);
+ UNLOCK_DEVICE
}
-void _vkvg_device_reset_fence (VkvgDevice dev, VkFence fence){
- if (dev->deviceLockGuard)
- dev->deviceLockGuard (dev->deviceGuardUserData);
+void _device_reset_fence (VkvgDevice dev, VkFence fence){
+ LOCK_DEVICE
if (dev->gQLastFence == fence)
dev->gQLastFence = VK_NULL_HANDLE;
ResetFences (dev->vkDev, 1, &fence);
- if (dev->deviceUnlockGuard)
- dev->deviceUnlockGuard (dev->deviceGuardUserData);
+ UNLOCK_DEVICE
}
-void _vkvg_device_wait_fence (VkvgDevice dev, VkFence fence){
+void _device_wait_fence (VkvgDevice dev, VkFence fence){
}
-void _vkvg_device_wait_and_reset_fence (VkvgDevice dev, VkFence fence){
+void _device_wait_and_reset_fence (VkvgDevice dev, VkFence fence){
}
-bool _vkvg_device_try_get_cached_context (VkvgDevice dev, VkvgContext* pCtx) {
- if (dev->deviceLockGuard)
- dev->deviceLockGuard (dev->deviceGuardUserData);
+bool _device_try_get_cached_context (VkvgDevice dev, VkvgContext* pCtx) {
+ LOCK_DEVICE
if (dev->cachedContextCount)
*pCtx = dev->cachedContext[--dev->cachedContextCount];
else
*pCtx = NULL;
- if (dev->deviceUnlockGuard)
- dev->deviceUnlockGuard (dev->deviceGuardUserData);
+ UNLOCK_DEVICE
return *pCtx != NULL;
}
-void _vkvg_device_store_context (VkvgContext ctx) {
+void _device_store_context (VkvgContext ctx) {
VkvgDevice dev = ctx->dev;
- if (dev->deviceLockGuard)
- dev->deviceLockGuard (dev->deviceGuardUserData);
+ LOCK_DEVICE
if (dev->gQLastFence == ctx->flushFence)
dev->gQLastFence = VK_NULL_HANDLE;
dev->cachedContext[dev->cachedContextCount++] = ctx;
ctx->references++;
- if (dev->deviceUnlockGuard)
- dev->deviceUnlockGuard (dev->deviceGuardUserData);
+ UNLOCK_DEVICE
}
-void _submit_cmd (VkvgDevice dev, VkCommandBuffer* cmd, VkFence fence) {
- if (dev->deviceLockGuard)
- dev->deviceLockGuard (dev->deviceGuardUserData);
+void _device_submit_cmd (VkvgDevice dev, VkCommandBuffer* cmd, VkFence fence) {
+ LOCK_DEVICE
if (dev->gQLastFence != VK_NULL_HANDLE)
WaitForFences (dev->vkDev, 1, &dev->gQLastFence, VK_TRUE, VKVG_FENCE_TIMEOUT);
vkh_cmd_submit (dev->gQueue, cmd, fence);
dev->gQLastFence = fence;
- if (dev->deviceUnlockGuard)
- dev->deviceUnlockGuard (dev->deviceGuardUserData);
+ UNLOCK_DEVICE
}
bool _init_function_pointers (VkvgDevice dev) {
VK_IMAGE_USAGE_SAMPLED_BIT|VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT);
vkh_image_create_descriptor(dev->emptyImg, VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_ASPECT_COLOR_BIT, VK_FILTER_NEAREST, VK_FILTER_NEAREST, VK_SAMPLER_MIPMAP_MODE_NEAREST,VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE);
- _wait_and_reset_device_fence (dev);
+ _device_wait_and_reset_device_fence (dev);
vkh_cmd_begin (dev->cmd, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT);
vkh_image_set_layout (dev->cmd, dev->emptyImg, VK_IMAGE_ASPECT_COLOR_BIT,
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
vkh_cmd_end (dev->cmd);
- _submit_cmd (dev, &dev->cmd, dev->fence);
+ _device_submit_cmd (dev, &dev->cmd, dev->fence);
}
void _check_best_image_tiling (VkvgDevice dev, VkFormat format) {
VkFormat pngStagFormat; /**< Supported vulkan image format png write staging img */
VkImageTiling pngStagTiling; /**< tiling for the blit operation */
- vkvg_device_guard deviceLockGuard;
- vkvg_device_guard deviceUnlockGuard;
- void* deviceGuardUserData;
+ mtx_t mutex; /**< protect device access (queue, cahes, ...)from ctxs in separate threads */
+ bool threadAware; /**< if true, mutex is created and guard device queue and caches access */
VkhQueue gQueue; /**< Vulkan Queue with Graphic flag */
VkFence gQLastFence;
VkPipeline pipelinePolyFill; /**< even-odd polygon filling first step */
VkPipeline pipelineClipping; /**< draw on stencil to update clipping regions */
-#ifdef VKVG_WIRED_DEBUG
- VkPipeline pipelineWired;
- VkPipeline pipelineLineList;
-#endif
-#if VKVG_DBG_STATS
- vkvg_debug_stats_t debug_stats; /**< debug statistics on memory usage and vulkan ressources */
-#endif
VkPipelineCache pipelineCache; /**< speed up startup by caching configured pipelines on disk */
VkPipelineLayout pipelineLayout; /**< layout common to all pipelines */
VkDescriptorSetLayout dslFont; /**< font cache descriptors layout */
VkDescriptorSetLayout dslSrc; /**< context source surface descriptors layout */
VkDescriptorSetLayout dslGrad; /**< context gradient descriptors layout */
- int hdpi, /**< only used for FreeType fonts and svg loading */
- vdpi;
+ int hdpi, /**< only used for FreeType fonts and svg loading */
+ vdpi;
VkhDevice vkhDev; /**< old VkhDev created during vulkan context creation by @ref vkvg_device_create. */
bool deferredResolve; /**< if true, resolve only on context destruction and set as source */
vkvg_status_t status; /**< Current status of device, affected by last operation */
- _font_cache_t* fontCache; /**< Store everything relative to common font caching system */
+ _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*/
- VkvgContext lastCtx; /**< last element of double linked list of context, used to trigger font caching system update on all contexts*/
+ int32_t cachedContextCount;
+ VkvgContext cachedContext[VKVG_MAX_CACHED_CONTEXT_COUNT];
- int32_t cachedContextCount;
- VkvgContext cachedContext[VKVG_MAX_CACHED_CONTEXT_COUNT];
+#ifdef VKVG_WIRED_DEBUG
+ VkPipeline pipelineWired;
+ VkPipeline pipelineLineList;
+#endif
+#if VKVG_DBG_STATS
+ vkvg_debug_stats_t debug_stats; /**< debug statistics on memory usage and vulkan ressources */
+#endif
}vkvg_device;
+#define LOCK_DEVICE \
+ if (dev->threadAware)\
+ mtx_lock (&dev->mutex);
+#define UNLOCK_DEVICE \
+ if (dev->threadAware)\
+ mtx_unlock (&dev->mutex);
+
bool _try_get_phyinfo (VkhPhyInfo* phys, uint32_t phyCount, VkPhysicalDeviceType gpuType, VkhPhyInfo* phy);
bool _init_function_pointers (VkvgDevice dev);
void _create_empty_texture (VkvgDevice dev, VkFormat format, VkImageTiling tiling);
VkRenderPass _createRenderPassMS(VkvgDevice dev, VkAttachmentLoadOp loadOp, VkAttachmentLoadOp stencilLoadOp);
VkRenderPass _createRenderPassNoResolve(VkvgDevice dev, VkAttachmentLoadOp loadOp, VkAttachmentLoadOp stencilLoadOp);
void _setupPipelines (VkvgDevice dev);
-void _createDescriptorSetLayout (VkvgDevice dev);
-void _flush_all_contexes (VkvgDevice dev);
-void _wait_idle (VkvgDevice dev);
-void _wait_and_reset_device_fence (VkvgDevice dev);
-void _submit_cmd (VkvgDevice dev, VkCommandBuffer* cmd, VkFence fence);
-
-void _vkvg_device_destroy_fence (VkvgDevice dev, VkFence fence);
-void _vkvg_device_reset_fence (VkvgDevice dev, VkFence fence);
-void _vkvg_device_wait_fence (VkvgDevice dev, VkFence fence);
-void _vkvg_device_wait_and_reset_fence (VkvgDevice dev, VkFence fence);
-bool _vkvg_device_try_get_cached_context(VkvgDevice dev, VkvgContext* pCtx);
-void _vkvg_device_store_context (VkvgContext ctx);
+void _device_createDescriptorSetLayout (VkvgDevice dev);
+void _device_flush_all_contexes (VkvgDevice dev);
+void _device_wait_idle (VkvgDevice dev);
+void _device_wait_and_reset_device_fence (VkvgDevice dev);
+void _device_submit_cmd (VkvgDevice dev, VkCommandBuffer* cmd, VkFence fence);
+
+void _device_destroy_fence (VkvgDevice dev, VkFence fence);
+void _device_reset_fence (VkvgDevice dev, VkFence fence);
+void _device_wait_fence (VkvgDevice dev, VkFence fence);
+void _device_wait_and_reset_fence (VkvgDevice dev, VkFence fence);
+bool _device_try_get_cached_context(VkvgDevice dev, VkvgContext* pCtx);
+void _device_store_context (VkvgContext ctx);
#endif
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
VK_CHECK_RESULT(vkEndCommandBuffer(cache->cmd));
- _submit_cmd (dev, &cache->cmd, cache->uploadFence);
+ _device_submit_cmd (dev, &cache->cmd, cache->uploadFence);
cache->hostBuff = (uint8_t*)malloc(buffLength);
cache->pensY = (int*)calloc(cache->texLength, sizeof(int));
void _increase_font_tex_array (VkvgDevice dev){
LOG(VKVG_LOG_INFO, "_increase_font_tex_array\n");
- _flush_all_contexes (dev);
+ _device_flush_all_contexes (dev);
_font_cache_t* cache = dev->fontCache;
vkWaitForFences (dev->vkDev, 1, &cache->uploadFence, VK_TRUE, UINT64_MAX);
- _vkvg_device_reset_fence (dev, cache->uploadFence);
+ _device_reset_fence (dev, cache->uploadFence);
vkResetCommandBuffer(cache->cmd, 0);
VK_CHECK_RESULT(vkEndCommandBuffer(cache->cmd));
- _submit_cmd (dev, &cache->cmd, cache->uploadFence);
+ _device_submit_cmd (dev, &cache->cmd, cache->uploadFence);
vkWaitForFences (dev->vkDev, 1, &cache->uploadFence, VK_TRUE, UINT64_MAX);
cache->pensY = (int*)realloc(cache->pensY, newSize * sizeof(int));
_update_descriptor_set (next, cache->texture, next->dsFont);
next = next->pPrev;
}*/
- _wait_idle(dev);
+ _device_wait_idle(dev);
}
///Start a new line in font cache, increase texture layer count if needed.
void _init_next_line_in_tex_cache (VkvgDevice dev, _vkvg_font_t* f){
LOG(VKVG_LOG_INFO, "_flush_chars_to_tex pen(%d, %d)\n",f->curLine.penX, f->curLine.penY);
vkWaitForFences (dev->vkDev,1,&cache->uploadFence,VK_TRUE,UINT64_MAX);
- _vkvg_device_reset_fence (dev, cache->uploadFence);
+ _device_reset_fence (dev, cache->uploadFence);
vkResetCommandBuffer(cache->cmd,0);
memcpy(cache->buff.allocInfo.pMappedData, cache->hostBuff, (uint64_t)f->curLine.height * FONT_PAGE_SIZE * cache->texPixelSize);
VK_CHECK_RESULT(vkEndCommandBuffer(cache->cmd));
- _submit_cmd (dev, &cache->cmd, cache->uploadFence);
+ _device_submit_cmd (dev, &cache->cmd, cache->uploadFence);
f->curLine.penX += cache->stagingX;
cache->stagingX = 0;
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#endif
+#include "deps/tinycthread.h"
#include "cross_os.h"
//width of the stencil buffer will determine the number of context saving/restore layers
//the two first bits of the stencil are the FILL and the CLIP bits, all other bits are
_create_surface_images (surf);
+ surf->status = VKVG_STATUS_SUCCESS;
vkvg_device_reference (surf->dev);
- dev->status = VKVG_STATUS_SUCCESS;
return surf;
}
VkvgSurface vkvg_surface_create_for_VkhImage (VkvgDevice dev, void* vkhImg) {
_create_framebuffer (surf);
_clear_surface (surf, VK_IMAGE_ASPECT_STENCIL_BIT);
+ surf->status = VKVG_STATUS_SUCCESS;
vkvg_device_reference (surf->dev);
- dev->status = VKVG_STATUS_SUCCESS;
return surf;
}
//TODO: it would be better to blit in original size and create ms final image with dest surf dims
VkCommandBuffer cmd = dev->cmd;
- _wait_and_reset_device_fence (dev);
+ _device_wait_and_reset_device_fence (dev);
vkh_cmd_begin (cmd, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT);
vkh_image_set_layout (cmd, stagImg, VK_IMAGE_ASPECT_COLOR_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
vkh_cmd_end (cmd);
- _submit_cmd (dev, &cmd, dev->fence);
+ _device_submit_cmd (dev, &cmd, dev->fence);
//don't reset fence after completion as this is the last cmd. (signaled idle fence)
vkWaitForFences (dev->vkDev, 1, &dev->fence, VK_TRUE, UINT64_MAX);
vkh_image_destroy (tmpImg);
+ surf->status = VKVG_STATUS_SUCCESS;
vkvg_device_reference (surf->dev);
- dev->status = VKVG_STATUS_SUCCESS;
return surf;
}
VkvgSurface vkvg_surface_create_from_image (VkvgDevice dev, const char* filePath) {
void vkvg_surface_destroy(VkvgSurface surf)
{
+ LOCK_SURFACE(surf)
surf->references--;
- if (surf->references > 0)
+ if (surf->references > 0) {
+ UNLOCK_SURFACE(surf)
return;
+ }
+ UNLOCK_SURFACE(surf)
vkDestroyFramebuffer(surf->dev->vkDev, surf->fb, NULL);
vkh_image_destroy(surf->imgMS);
vkh_image_destroy(surf->stencil);
+ if (surf->dev->threadAware)
+ mtx_destroy (&surf->mutex);
+
vkvg_device_destroy (surf->dev);
free(surf);
}
VkvgSurface vkvg_surface_reference (VkvgSurface surf) {
+ LOCK_SURFACE(surf)
surf->references++;
+ UNLOCK_SURFACE(surf)
return surf;
}
uint32_t vkvg_surface_get_reference_count (VkvgSurface surf) {
LOG(VKVG_LOG_ERR, "vkvg_surface_write_to_png failed, null path\n");
return VKVG_STATUS_WRITE_ERROR;
}
-
+ LOCK_SURFACE(surf)
VkImageSubresourceLayers imgSubResLayers = {VK_IMAGE_ASPECT_COLOR_BIT,0,0,1};
VkvgDevice dev = surf->dev;
VK_IMAGE_USAGE_TRANSFER_SRC_BIT|VK_IMAGE_USAGE_TRANSFER_DST_BIT);
VkCommandBuffer cmd = dev->cmd;
- _wait_and_reset_device_fence (dev);
+ _device_wait_and_reset_device_fence (dev);
vkh_cmd_begin (cmd, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT);
vkh_image_set_layout (cmd, stagImg, VK_IMAGE_ASPECT_COLOR_BIT,
vkh_image_get_vkimage (stagImg), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit, VK_FILTER_NEAREST);
vkh_cmd_end (cmd);
- _submit_cmd (dev, &cmd, dev->fence);
+ _device_submit_cmd (dev, &cmd, dev->fence);
VkhImage stagImgLinear = stagImg;
.dstOffset = {0},
.extent = {(int32_t)surf->width, (int32_t)surf->height, 1}
};
- _wait_and_reset_device_fence (dev);
+ _device_wait_and_reset_device_fence (dev);
vkh_cmd_begin (cmd, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT);
vkh_image_set_layout (cmd, stagImgLinear, VK_IMAGE_ASPECT_COLOR_BIT,
vkh_image_get_vkimage (stagImgLinear), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &cpy);
vkh_cmd_end (cmd);
- _submit_cmd (dev, &cmd, dev->fence);
+ _device_submit_cmd (dev, &cmd, dev->fence);
vkWaitForFences (dev->vkDev, 1, &dev->fence, VK_TRUE, UINT64_MAX);
vkh_image_destroy (stagImg);
vkh_image_unmap (stagImgLinear);
vkh_image_destroy (stagImgLinear);
+ UNLOCK_SURFACE(surf)
return VKVG_STATUS_SUCCESS;
}
return VKVG_STATUS_INVALID_IMAGE;
}
+ LOCK_SURFACE(surf)
+
VkImageSubresourceLayers imgSubResLayers = {VK_IMAGE_ASPECT_COLOR_BIT,0,0,1};
VkvgDevice dev = surf->dev;
VK_IMAGE_USAGE_TRANSFER_SRC_BIT|VK_IMAGE_USAGE_TRANSFER_DST_BIT);
VkCommandBuffer cmd = dev->cmd;
- _wait_and_reset_device_fence (dev);
+ _device_wait_and_reset_device_fence (dev);
vkh_cmd_begin (cmd, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT);
vkh_image_set_layout (cmd, stagImg, VK_IMAGE_ASPECT_COLOR_BIT,
vkh_image_get_vkimage (stagImg), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit, VK_FILTER_NEAREST);
vkh_cmd_end (cmd);
- _submit_cmd (dev, &cmd, dev->fence);
+ _device_submit_cmd (dev, &cmd, dev->fence);
vkWaitForFences (dev->vkDev, 1, &dev->fence, VK_TRUE, UINT64_MAX);
uint64_t stride = vkh_image_get_stride(stagImg);
vkh_image_unmap (stagImg);
vkh_image_destroy (stagImg);
+ UNLOCK_SURFACE(surf)
+
return VKVG_STATUS_SUCCESS;
}
VkvgDevice dev = surf->dev;
VkCommandBuffer cmd = dev->cmd;
- _wait_and_reset_device_fence (dev);
+ _device_wait_and_reset_device_fence (dev);
vkh_cmd_begin (cmd, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT);
vkh_image_set_layout (cmd, surf->imgMS, VK_IMAGE_ASPECT_COLOR_BIT,
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
vkh_cmd_end (cmd);
- _submit_cmd (dev, &cmd, dev->fence);
+ _device_submit_cmd (dev, &cmd, dev->fence);
}
void _clear_surface (VkvgSurface surf, VkImageAspectFlags aspect)
VkvgDevice dev = surf->dev;
VkCommandBuffer cmd = dev->cmd;
- _wait_and_reset_device_fence (dev);
+ _device_wait_and_reset_device_fence (dev);
vkh_cmd_begin (cmd, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT);
}
vkh_cmd_end (cmd);
- _submit_cmd (dev, &cmd, dev->fence);
+ _device_submit_cmd (dev, &cmd, dev->fence);
}
void _create_surface_main_image (VkvgSurface surf){
}
surf->dev = dev;
surf->format = format;
+ if (dev->threadAware)
+ mtx_init (&surf->mutex, mtx_plain);
return surf;
}
uint32_t references;
vkvg_status_t status; /**< Current status of surface, affected by last operation */
bool new;
+ mtx_t mutex;
}vkvg_surface;
+#define LOCK_SURFACE(surf) \
+ if (surf->dev->threadAware)\
+ mtx_lock (&surf->mutex);
+#define UNLOCK_SURFACE(surf) \
+ if (surf->dev->threadAware)\
+ mtx_unlock (&surf->mutex);
+
void _explicit_ms_resolve (VkvgSurface surf);
void _clear_surface (VkvgSurface surf, VkImageAspectFlags aspect);
void _create_surface_main_image (VkvgSurface surf);
#include "test.h"
#include "tinycthread.h"
-#define THREAD_COUNT 8
+#define THREAD_COUNT 16
static int finishedThreadCount = 0;
vkvg_rectangle(ctx, x, y, s, s);
}
-void _before_submit (void* data) {
- mtx_lock((mtx_t*)data);
-}
-void _after_submit (void* data) {
- mtx_unlock((mtx_t*)data);
-}
int drawRectsThread () {
return 0;
}
void fixedSizeRects(){
- mtx_t gQMutex, mutex;
- mtx_t* pgQMutex = &gQMutex;
+ mtx_t mutex;
pmutex = &mutex;
- mtx_init (pgQMutex, mtx_plain);
- vkvg_device_set_guards (device, _before_submit, _after_submit, pgQMutex);
+ vkvg_device_set_thread_aware (device, 1);
thrd_t threads[THREAD_COUNT];
finishedThreadCount = 0;
mtx_init (pmutex, mtx_plain);
- for (uint32_t i=0; i<THREAD_COUNT; i++) {
+ for (uint32_t i=0; i<THREAD_COUNT; i++)
thrd_create (&threads[i], drawRectsThread, NULL);
- }
const struct timespec ts = {1,0};
while (finishedThreadCount < THREAD_COUNT)
mtx_destroy (pmutex);
pmutex = NULL;
- vkvg_device_set_guards (device, NULL, NULL, NULL);
- mtx_destroy (pgQMutex);
- pgQMutex = NULL;
+ vkvg_device_set_thread_aware (device, 0);
}
int main(int argc, char *argv[]) {
#include "test.h"
#include "tinycthread.h"
-#define THREAD_COUNT 8
+#define THREAD_COUNT 32
static int finishedThreadCount = 0;
return 0;
}
void fixedSizeRects(){
- mtx_t gQMutex, mutex;
- mtx_t* pgQMutex = &gQMutex;
+ mtx_t mutex;
pmutex = &mutex;
- mtx_init (pgQMutex, mtx_plain);
- vkvg_device_set_guards (device, _before_submit, _after_submit, pgQMutex);
+ vkvg_device_set_thread_aware (device, 1);
thrd_t threads[THREAD_COUNT];
finishedThreadCount = 0;
mtx_init (pmutex, mtx_plain);
- for (uint32_t i=0; i<THREAD_COUNT; i++) {
+ for (uint32_t i=0; i<THREAD_COUNT; i++)
thrd_create (&threads[i], drawRectsThread, NULL);
- }
const struct timespec ts = {1,0};
while (finishedThreadCount < THREAD_COUNT)
mtx_destroy (pmutex);
pmutex = NULL;
- vkvg_device_set_guards (device, NULL, NULL, NULL);
- mtx_destroy (pgQMutex);
- pgQMutex = NULL;
+ vkvg_device_set_thread_aware (device, 0);
}
int main(int argc, char *argv[]) {