From: Jean-Philippe Bruyère Date: Thu, 27 Jan 2022 21:12:10 +0000 (+0100) Subject: thread aware option in device, simple mutex device lock X-Git-Url: https://git.osiis.dedyn.io/?a=commitdiff_plain;h=38e5aabd57bfab88732ef3b18e288a60b1f9ce7a;p=jp%2Fvkvg.git thread aware option in device, simple mutex device lock --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 9724a07..44b8d18 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,7 +37,7 @@ if (CMAKE_BUILD_TYPE STREQUAL "Debug") 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() diff --git a/include/vkvg.h b/include/vkvg.h index 9508823..8c1b36a 100644 --- a/include/vkvg.h +++ b/include/vkvg.h @@ -90,12 +90,11 @@ extern "C" { #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, @@ -535,9 +534,9 @@ void vkvg_matrix_get_scale (const vkvg_matrix_t *matrix, float *sx, float *sy); * 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. diff --git a/src/cross_os.c b/src/cross_os.c index 2aa8b50..3640b11 100644 --- a/src/cross_os.c +++ b/src/cross_os.c @@ -45,3 +45,29 @@ const char* getUserDir () { return pw->pw_dir; #endif } + +#if __linux__ +#include +#include +#include +#include +#include + +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 diff --git a/src/cross_os.h b/src/cross_os.h index cc0cae0..32f05f0 100644 --- a/src/cross_os.h +++ b/src/cross_os.h @@ -53,6 +53,9 @@ #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 (); diff --git a/src/deps/tinycthread.c b/src/deps/tinycthread.c new file mode 100644 index 0000000..f9cea2e --- /dev/null +++ b/src/deps/tinycthread.c @@ -0,0 +1,594 @@ +/* -*- 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 + * + * Added casts from time_t to DWORD to avoid warnings on VC++. + * Fixed time retrieval on POSIX systems. + */ + +#include "tinycthread.h" +#include + +/* Platform specific includes */ +#if defined(_TTHREAD_POSIX_) + #include + #include + #include + #include + #include +#elif defined(_TTHREAD_WIN32_) + #include + #include +#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_ + diff --git a/src/deps/tinycthread.h b/src/deps/tinycthread.h new file mode 100644 index 0000000..42958c3 --- /dev/null +++ b/src/deps/tinycthread.h @@ -0,0 +1,443 @@ +/* -*- 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 + +/* Platform specific includes */ +#if defined(_TTHREAD_POSIX_) + #include + #include +#elif defined(_TTHREAD_WIN32_) + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #define __UNDEF_LEAN_AND_MEAN + #endif + #include + #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_ */ + diff --git a/src/vkvg_context.c b/src/vkvg_context.c index c7f5faf..8feab5c 100644 --- a/src/vkvg_context.c +++ b/src/vkvg_context.c @@ -91,7 +91,7 @@ VkvgContext vkvg_create(VkvgSurface surf) 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) { @@ -294,7 +294,7 @@ void vkvg_destroy (VkvgContext ctx) #endif if (!ctx->status && ctx->dev->cachedContextCount < VKVG_MAX_CACHED_CONTEXT_COUNT) { - _vkvg_device_store_context (ctx); + _device_store_context (ctx); return; } diff --git a/src/vkvg_context_internal.c b/src/vkvg_context_internal.c index 4e1cf77..0a0d2b6 100644 --- a/src/vkvg_context_internal.c +++ b/src/vkvg_context_internal.c @@ -424,8 +424,8 @@ bool _wait_and_submit_cmd (VkvgContext ctx){ 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]; @@ -854,7 +854,8 @@ void _init_descriptor_sets (VkvgContext ctx){ } 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); diff --git a/src/vkvg_device.c b/src/vkvg_device.c index 2691d26..bb2433a 100644 --- a/src/vkvg_device.c +++ b/src/vkvg_device.c @@ -89,30 +89,35 @@ void _device_init (VkvgDevice dev, VkInstance inst, VkPhysicalDevice phy, VkDevi _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; } @@ -257,9 +262,13 @@ VkvgDevice vkvg_device_create_from_vk_multisample(VkInstance inst, VkPhysicalDev 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]); @@ -303,6 +312,9 @@ void vkvg_device_destroy (VkvgDevice dev) 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); @@ -316,7 +328,9 @@ vkvg_status_t vkvg_device_status (VkvgDevice dev) { 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) { @@ -332,10 +346,16 @@ void vkvg_device_get_dpy (VkvgDevice dev, int* hdpy, int* vdpy) { *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) { diff --git a/src/vkvg_device_internal.c b/src/vkvg_device_internal.c index 5d08bf1..b7303e8 100644 --- a/src/vkvg_device_internal.c +++ b/src/vkvg_device_internal.c @@ -64,7 +64,7 @@ bool _try_get_phyinfo (VkhPhyInfo* phys, uint32_t phyCount, VkPhysicalDeviceType } 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) @@ -419,81 +419,71 @@ void _createDescriptorSetLayout (VkvgDevice dev) { 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) { @@ -530,14 +520,14 @@ void _create_empty_texture (VkvgDevice dev, VkFormat format, VkImageTiling tilin 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) { diff --git a/src/vkvg_device_internal.h b/src/vkvg_device_internal.h index db2c3d3..33c275b 100644 --- a/src/vkvg_device_internal.h +++ b/src/vkvg_device_internal.h @@ -65,9 +65,8 @@ typedef struct _vkvg_device_t{ 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; @@ -87,21 +86,14 @@ typedef struct _vkvg_device_t{ 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. */ @@ -110,14 +102,29 @@ typedef struct _vkvg_device_t{ 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); @@ -127,16 +134,16 @@ void _create_pipeline_cache (VkvgDevice dev); 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 diff --git a/src/vkvg_fonts.c b/src/vkvg_fonts.c index 7d34bee..90f7766 100644 --- a/src/vkvg_fonts.c +++ b/src/vkvg_fonts.c @@ -88,7 +88,7 @@ void _init_fonts_cache (VkvgDevice dev){ 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)); @@ -99,12 +99,12 @@ void _init_fonts_cache (VkvgDevice dev){ 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); @@ -140,7 +140,7 @@ void _increase_font_tex_array (VkvgDevice dev){ 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)); @@ -155,7 +155,7 @@ void _increase_font_tex_array (VkvgDevice dev){ _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){ @@ -242,7 +242,7 @@ void _flush_chars_to_tex (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); @@ -269,7 +269,7 @@ void _flush_chars_to_tex (VkvgDevice dev, _vkvg_font_t* f) { 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; diff --git a/src/vkvg_internal.h b/src/vkvg_internal.h index 2b8b5c9..120d5d4 100644 --- a/src/vkvg_internal.h +++ b/src/vkvg_internal.h @@ -73,6 +73,7 @@ #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 diff --git a/src/vkvg_surface.c b/src/vkvg_surface.c index 89ff376..21c08de 100644 --- a/src/vkvg_surface.c +++ b/src/vkvg_surface.c @@ -46,8 +46,8 @@ VkvgSurface vkvg_surface_create (VkvgDevice dev, uint32_t width, uint32_t height _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) { @@ -73,8 +73,8 @@ 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 @@ -112,7 +112,7 @@ VkvgSurface vkvg_surface_create_from_bitmap (VkvgDevice dev, unsigned char* img, 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, @@ -148,7 +148,7 @@ VkvgSurface vkvg_surface_create_from_bitmap (VkvgDevice dev, unsigned char* img, 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); @@ -178,8 +178,8 @@ VkvgSurface vkvg_surface_create_from_bitmap (VkvgDevice dev, unsigned char* img, 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) { @@ -201,9 +201,13 @@ 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); @@ -213,12 +217,17 @@ void vkvg_surface_destroy(VkvgSurface surf) 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) { @@ -260,7 +269,7 @@ vkvg_status_t vkvg_surface_write_to_png (VkvgSurface surf, const char* path){ 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; @@ -277,7 +286,7 @@ vkvg_status_t vkvg_surface_write_to_png (VkvgSurface surf, const char* path){ 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, @@ -298,7 +307,7 @@ vkvg_status_t vkvg_surface_write_to_png (VkvgSurface surf, const char* path){ 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; @@ -313,7 +322,7 @@ vkvg_status_t vkvg_surface_write_to_png (VkvgSurface surf, const char* path){ .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, @@ -327,7 +336,7 @@ vkvg_status_t vkvg_surface_write_to_png (VkvgSurface surf, const char* path){ 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); @@ -343,6 +352,7 @@ vkvg_status_t vkvg_surface_write_to_png (VkvgSurface surf, const char* path){ vkh_image_unmap (stagImgLinear); vkh_image_destroy (stagImgLinear); + UNLOCK_SURFACE(surf) return VKVG_STATUS_SUCCESS; } @@ -356,6 +366,8 @@ vkvg_status_t vkvg_surface_write_to_memory (VkvgSurface surf, unsigned char* con return VKVG_STATUS_INVALID_IMAGE; } + LOCK_SURFACE(surf) + VkImageSubresourceLayers imgSubResLayers = {VK_IMAGE_ASPECT_COLOR_BIT,0,0,1}; VkvgDevice dev = surf->dev; @@ -365,7 +377,7 @@ vkvg_status_t vkvg_surface_write_to_memory (VkvgSurface surf, unsigned char* con 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, @@ -386,7 +398,7 @@ vkvg_status_t vkvg_surface_write_to_memory (VkvgSurface surf, unsigned char* con 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); @@ -403,5 +415,7 @@ vkvg_status_t vkvg_surface_write_to_memory (VkvgSurface surf, unsigned char* con vkh_image_unmap (stagImg); vkh_image_destroy (stagImg); + UNLOCK_SURFACE(surf) + return VKVG_STATUS_SUCCESS; } diff --git a/src/vkvg_surface_internal.c b/src/vkvg_surface_internal.c index a8cc8ff..4bcb0c1 100644 --- a/src/vkvg_surface_internal.c +++ b/src/vkvg_surface_internal.c @@ -28,7 +28,7 @@ void _explicit_ms_resolve (VkvgSurface surf){ 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, @@ -53,7 +53,7 @@ void _explicit_ms_resolve (VkvgSurface surf){ 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) @@ -61,7 +61,7 @@ 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); @@ -101,7 +101,7 @@ void _clear_surface (VkvgSurface surf, VkImageAspectFlags aspect) } vkh_cmd_end (cmd); - _submit_cmd (dev, &cmd, dev->fence); + _device_submit_cmd (dev, &cmd, dev->fence); } void _create_surface_main_image (VkvgSurface surf){ @@ -186,5 +186,7 @@ VkvgSurface _create_surface (VkvgDevice dev, VkFormat format) { } surf->dev = dev; surf->format = format; + if (dev->threadAware) + mtx_init (&surf->mutex, mtx_plain); return surf; } diff --git a/src/vkvg_surface_internal.h b/src/vkvg_surface_internal.h index 506cd44..e01092e 100644 --- a/src/vkvg_surface_internal.h +++ b/src/vkvg_surface_internal.h @@ -38,8 +38,16 @@ typedef struct _vkvg_surface_t { 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); diff --git a/tests/multithreading/multithreaded.c b/tests/multithreading/multithreaded.c index 00e1444..bd14e84 100644 --- a/tests/multithreading/multithreaded.c +++ b/tests/multithreading/multithreaded.c @@ -1,7 +1,7 @@ #include "test.h" #include "tinycthread.h" -#define THREAD_COUNT 8 +#define THREAD_COUNT 16 static int finishedThreadCount = 0; @@ -17,12 +17,6 @@ void drawRandomRect (VkvgContext ctx, float s) { 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 () { @@ -48,20 +42,17 @@ 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