]> O.S.I.I.S - jp/vkvg.git/commitdiff
test with per thread gradient buff
authorJean-Philippe Bruyère <jp_bruyere@hotmail.com>
Sat, 15 Jan 2022 12:07:14 +0000 (13:07 +0100)
committerJean-Philippe Bruyère <jp_bruyere@hotmail.com>
Tue, 18 Jan 2022 12:31:08 +0000 (13:31 +0100)
CMakeLists.txt
src/cross_os.h
src/deps/tinycthread.c [new file with mode: 0644]
src/deps/tinycthread.h [new file with mode: 0644]
src/vkvg_context.c
src/vkvg_context_internal.c
src/vkvg_context_internal.h
src/vkvg_device_internal.c
src/vkvg_device_internal.h

index a398a96ba9d4ed108e1d7abce1e0900b5043df44..e426e11e1b8d0d0d838065533799252d0762ec13 100644 (file)
@@ -200,7 +200,7 @@ IF (VKVG_USE_GLUTESS)
        ADD_SUBDIRECTORY (external/glutess)
 ENDIF ()
 
-FILE(GLOB VKVG_OBJ_SRC src/*.c)
+FILE(GLOB VKVG_OBJ_SRC src/*.c src/deps/*.c)
 SET (VKVG_PUBLIC_HEADERS "include/vkvg.h")
 
 IF (VKVG_SVG)
index 1a4386c91b84410e71de8c46c2eaf4efa1a71384..c1290412fcc998776f5491d6276b7a3e8e0e3db0 100644 (file)
@@ -49,6 +49,8 @@
        #define reset_warning (warn) #pragma GCC diagnostic warning "-W"#warn
 #endif
 
+#include "deps/tinycthread.h"
+
 const char* getUserDir ();
 
 #endif // CROSS_OS_H
diff --git a/src/deps/tinycthread.c b/src/deps/tinycthread.c
new file mode 100644 (file)
index 0000000..f9cea2e
--- /dev/null
@@ -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 <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_
+
diff --git a/src/deps/tinycthread.h b/src/deps/tinycthread.h
new file mode 100644 (file)
index 0000000..42958c3
--- /dev/null
@@ -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 <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_ */
+
index f497a5442e8fb3f517fc6ca56d6b9363216e4fc1..a64345d5f87d675f7a17ec2f710c9e87ba215738 100644 (file)
@@ -129,13 +129,14 @@ VkvgContext vkvg_create(VkvgSurface surf)
        ctx->cmdPool = vkh_cmd_pool_create ((VkhDevice)dev, dev->gQueue->familyIndex, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT);
 
        _create_vertices_buff   (ctx);
-       _create_gradient_buff   (ctx);
+
+       ctx->th_objs = _get_or_create_threaded_objects(ctx->pSurf->dev, thrd_current());
+
        _create_cmd_buff                (ctx);
        _createDescriptorPool   (ctx);
        _init_descriptor_sets   (ctx);
        _update_descriptor_set  (ctx, ctx->pSurf->dev->fontCache->texture, ctx->dsFont);
        _update_descriptor_set  (ctx, surf->dev->emptyImg, ctx->dsSrc);
-       _update_gradient_desc_set(ctx);
 
        _clear_path                             (ctx);
 
@@ -235,12 +236,11 @@ void vkvg_destroy (VkvgContext ctx)
        vkFreeCommandBuffers(dev, ctx->cmdPool, 2, ctx->cmdBuffers);
        vkDestroyCommandPool(dev, ctx->cmdPool, NULL);
 
-       VkDescriptorSet dss[] = {ctx->dsFont,ctx->dsSrc, ctx->dsGrad};
-       vkFreeDescriptorSets    (dev, ctx->descriptorPool, 3, dss);
+       VkDescriptorSet dss[] = {ctx->dsFont,ctx->dsSrc};
+       vkFreeDescriptorSets    (dev, ctx->descriptorPool, 2, dss);
 
        vkDestroyDescriptorPool (dev, ctx->descriptorPool,NULL);
 
-       vkvg_buffer_destroy (&ctx->uboGrad);
        vkvg_buffer_destroy (&ctx->indices);
        vkvg_buffer_destroy (&ctx->vertices);
 
index d40d2b7f0a0e8f712e125f97c299a48f43d7f697..d96be1ac8eff4ef050b6d043369d4c315f030b9d 100644 (file)
@@ -31,7 +31,6 @@
 
 #include "vkvg_surface_internal.h"
 #include "vkvg_context_internal.h"
-#include "vkvg_device_internal.h"
 #include "vkvg_pattern.h"
 #include "vkh_queue.h"
 #include "vkh_image.h"
@@ -246,12 +245,7 @@ float _get_arc_step (VkvgContext ctx, float radius) {
                return fminf(M_PI / 3.f, M_PI / r);
        return fminf(M_PI / 3.f,M_PI / (r * 0.4f));
 }
-void _create_gradient_buff (VkvgContext ctx){
-       vkvg_buffer_create (ctx->pSurf->dev,
-               VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
-               VMA_MEMORY_USAGE_CPU_TO_GPU,
-               sizeof(vkvg_gradient_t), &ctx->uboGrad);
-}
+
 void _create_vertices_buff (VkvgContext ctx){
        vkvg_buffer_create (ctx->pSurf->dev,
                VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
@@ -607,7 +601,7 @@ void _start_cmd_for_render_pass (VkvgContext ctx) {
 
        CmdSetScissor(ctx->cmd, 0, 1, &ctx->bounds);
 
-       VkDescriptorSet dss[] = {ctx->dsFont, ctx->dsSrc,ctx->dsGrad};
+       VkDescriptorSet dss[] = {ctx->dsFont, ctx->dsSrc,ctx->th_objs->dsGrad};
        CmdBindDescriptorSets(ctx->cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, ctx->pSurf->dev->pipelineLayout,
                                                        0, 3, dss, 0, NULL);
 
@@ -767,8 +761,8 @@ void _update_cur_pattern (VkvgContext ctx, VkvgPattern pat) {
                        vkvg_matrix_transform_distance (&ctx->pushConsts.mat, &grad.cp[1].z, &grad.cp[0].w);
                }
 
-               memcpy (ctx->uboGrad.allocInfo.pMappedData , &grad, sizeof(vkvg_gradient_t));
-               vkvg_buffer_flush(&ctx->uboGrad);
+               memcpy (ctx->th_objs->uboGrad.allocInfo.pMappedData , &grad, sizeof(vkvg_gradient_t));
+               vkvg_buffer_flush(&ctx->th_objs->uboGrad);
                break;
        }
        ctx->pushConsts.fsq_patternType = (ctx->pushConsts.fsq_patternType & FULLSCREEN_BIT) + newPatternType;
@@ -790,18 +784,6 @@ void _update_descriptor_set (VkvgContext ctx, VkhImage img, VkDescriptorSet ds){
        };
        vkUpdateDescriptorSets(ctx->pSurf->dev->vkDev, 1, &writeDescriptorSet, 0, NULL);
 }
-void _update_gradient_desc_set (VkvgContext ctx){
-       VkDescriptorBufferInfo dbi = {ctx->uboGrad.buffer, 0, VK_WHOLE_SIZE};
-       VkWriteDescriptorSet writeDescriptorSet = {
-                       .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
-                       .dstSet = ctx->dsGrad,
-                       .dstBinding = 0,
-                       .descriptorCount = 1,
-                       .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
-                       .pBufferInfo = &dbi
-       };
-       vkUpdateDescriptorSets(ctx->pSurf->dev->vkDev, 1, &writeDescriptorSet, 0, NULL);
-}
 /*
  * Reset currently bound descriptor which image could be destroyed
  */
@@ -840,8 +822,6 @@ void _init_descriptor_sets (VkvgContext ctx){
        VK_CHECK_RESULT(vkAllocateDescriptorSets(dev->vkDev, &descriptorSetAllocateInfo, &ctx->dsFont));
        descriptorSetAllocateInfo.pSetLayouts = &dev->dslSrc;
        VK_CHECK_RESULT(vkAllocateDescriptorSets(dev->vkDev, &descriptorSetAllocateInfo, &ctx->dsSrc));
-       descriptorSetAllocateInfo.pSetLayouts = &dev->dslGrad;
-       VK_CHECK_RESULT(vkAllocateDescriptorSets(dev->vkDev, &descriptorSetAllocateInfo, &ctx->dsGrad));
 }
 //populate vertice buff for stroke
 bool _build_vb_step (vkvg_context* ctx, float hw, stroke_context_t* str, bool isCurve){
index 537fc8f081cb30e08cc53b843a993c55ea4875d3..c9e799b4b9606f9efd0a654a60188a54d68cb755 100644 (file)
@@ -27,6 +27,7 @@
 #include "vkvg_buff.h"
 #include "vkh.h"
 #include "vkvg_fonts.h"
+#include "vkvg_device_internal.h"
 
 #if VKVG_RECORDING
        #include "recording/vkvg_record_internal.h"
@@ -141,7 +142,6 @@ typedef struct _vkvg_context_t {
        VkDescriptorPool        descriptorPool;//one pool per thread
        VkDescriptorSet         dsFont;         //fonts glyphs texture atlas descriptor (local for thread safety)
        VkDescriptorSet         dsSrc;          //source ds
-       VkDescriptorSet         dsGrad;         //gradient uniform buffer
 
        VkRect2D                        bounds;
 
@@ -161,7 +161,7 @@ typedef struct _vkvg_context_t {
        vkvg_recording_t*       recording;
 #endif
 
-       vkvg_buff       uboGrad;                //uniform buff obj holdings gradient infos
+       vkvg_device_thread_items_t* th_objs;
 
        //vk buffers, holds data until flush
        vkvg_buff       indices;                //index buffer with persistent map memory
@@ -279,7 +279,6 @@ void _poly_fill                             (VkvgContext ctx);
 void _fill_non_zero                    (VkvgContext ctx);
 void _draw_full_screen_quad (VkvgContext ctx, bool useScissor);
 
-void _create_gradient_buff     (VkvgContext ctx);
 void _create_vertices_buff     (VkvgContext ctx);
 void _add_vertex                       (VkvgContext ctx, Vertex v);
 void _add_vertexf                      (VkvgContext ctx, float x, float y);
@@ -308,7 +307,6 @@ void _start_cmd_for_render_pass (VkvgContext ctx);
 void _createDescriptorPool     (VkvgContext ctx);
 void _init_descriptor_sets     (VkvgContext ctx);
 void _update_descriptor_set (VkvgContext ctx, VkhImage img, VkDescriptorSet ds);
-void _update_gradient_desc_set(VkvgContext ctx);
 void _free_ctx_save                    (vkvg_context_save_t* sav);
 
 static inline float vec2_zcross (vec2 v1, vec2 v2){
index 90556a283858282b9eb01dd8ea819123ec6a4e00..e635ea59636f3b7e8407e1c32223a02337c70f7d 100644 (file)
@@ -27,6 +27,7 @@
 #include "vkvg_device_internal.h"
 #include "vkvg_context_internal.h"
 #include "shaders.h"
+#include "vkvg_pattern.h"
 
 uint32_t vkvg_log_level = VKVG_LOG_DEBUG;
 #ifdef VKVG_WIRED_DEBUG
@@ -55,6 +56,71 @@ PFN_vkWaitForFences                          WaitForFences;
 PFN_vkResetFences                              ResetFences;
 PFN_vkResetCommandBuffer               ResetCommandBuffer;
 
+void _delete_threaded_object (VkvgDevice dev, vkvg_device_thread_items_t* throbjs) {
+       vkvg_buffer_destroy (&throbjs->uboGrad);
+
+       vkDestroyDescriptorPool (dev->vkDev, throbjs->descriptorPool,NULL);
+
+       free (throbjs);
+}
+void _add_threaded_objects (VkvgDevice dev, vkvg_device_thread_items_t* throbjs) {
+       vkvg_device_thread_items_t* tmp = dev->threaded_objects;
+       if (!tmp) {
+               dev->threaded_objects = throbjs;
+               return;
+       }
+       while (tmp->next)
+               tmp = tmp->next;
+       tmp->next = throbjs;
+}
+
+vkvg_device_thread_items_t* _get_or_create_threaded_objects (VkvgDevice dev, thrd_t id) {
+       vkvg_device_thread_items_t* tmp = dev->threaded_objects;
+       while (tmp) {
+               if (thrd_equal(tmp->id, id))
+                       return tmp;
+               tmp = tmp->next;
+       }
+       tmp = (vkvg_device_thread_items_t*)calloc(1, sizeof(vkvg_device_thread_items_t));
+       tmp->id = thrd_current();
+
+       const VkDescriptorPoolSize descriptorPoolSize[] = {
+               {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 2 },
+               {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1 }
+       };
+       VkDescriptorPoolCreateInfo descriptorPoolCreateInfo = { .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
+                                                                                                                       .maxSets = 3,
+                                                                                                                       .flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT,
+                                                                                                                       .poolSizeCount = 2,
+                                                                                                                       .pPoolSizes = descriptorPoolSize };
+       VK_CHECK_RESULT(vkCreateDescriptorPool (dev->vkDev, &descriptorPoolCreateInfo, NULL, &tmp->descriptorPool));
+
+       VkDescriptorSetAllocateInfo descriptorSetAllocateInfo = { .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
+                                                                                                                         .descriptorPool = tmp->descriptorPool,
+                                                                                                                         .descriptorSetCount = 1,
+                                                                                                                         .pSetLayouts = &dev->dslGrad };
+       VK_CHECK_RESULT(vkAllocateDescriptorSets(dev->vkDev, &descriptorSetAllocateInfo, &tmp->dsGrad));
+
+       vkvg_buffer_create (dev,
+               VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
+               VMA_MEMORY_USAGE_CPU_TO_GPU,
+               sizeof(vkvg_gradient_t), &tmp->uboGrad);
+
+       VkDescriptorBufferInfo dbi = {tmp->uboGrad.buffer, 0, VK_WHOLE_SIZE};
+       VkWriteDescriptorSet writeDescriptorSet = {
+                       .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
+                       .dstSet = tmp->dsGrad,
+                       .dstBinding = 0,
+                       .descriptorCount = 1,
+                       .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+                       .pBufferInfo = &dbi
+       };
+       vkUpdateDescriptorSets(dev->vkDev, 1, &writeDescriptorSet, 0, NULL);
+
+       _add_threaded_objects(dev, tmp);
+
+       return tmp;
+}
 bool _try_get_phyinfo (VkhPhyInfo* phys, uint32_t phyCount, VkPhysicalDeviceType gpuType, VkhPhyInfo* phy) {
        for (uint32_t i=0; i<phyCount; i++){
                if (vkh_phyinfo_get_properties(phys[i]).deviceType == gpuType) {
index d706416fa82f05dade9ea1e410fc143b2f2adbc2..cffe2dd8e09a4d713468d62cb3993c1af9d7aa6e 100644 (file)
@@ -51,6 +51,17 @@ extern PFN_vkWaitForFences                           WaitForFences;
 extern PFN_vkResetFences                               ResetFences;
 extern PFN_vkResetCommandBuffer                        ResetCommandBuffer;
 
+//per thread vulkan object used in contexts
+typedef struct _vkvg_device_thread_items_t {
+       thrd_t                          id;
+       VkDescriptorPool        descriptorPool;         //one pool per thread
+       VkDescriptorSet         dsGrad;         //gradient uniform buffer
+
+       vkvg_buff                       uboGrad;                        //uniform buff obj holdings gradient infos
+
+       struct _vkvg_device_thread_items_t* next;
+}vkvg_device_thread_items_t;
+
 typedef struct _vkvg_device_t{
        VkDevice                                vkDev;                                  /**< Vulkan Logical Device */
        VkPhysicalDeviceMemoryProperties phyMemProps;   /**< Vulkan Physical device memory properties */
@@ -104,6 +115,8 @@ typedef struct _vkvg_device_t{
 
        _font_cache_t*  fontCache;                                              /**< Store everything relative to common font caching system */
        VkvgContext             lastCtx;                                                /**< last element of double linked list of context, used to trigger font caching system update on all contexts*/
+
+       vkvg_device_thread_items_t* threaded_objects;
 }vkvg_device;
 
 bool _try_get_phyinfo                  (VkhPhyInfo* phys, uint32_t phyCount, VkPhysicalDeviceType gpuType, VkhPhyInfo* phy);
@@ -128,4 +141,6 @@ void _instance_extensions_check_release ();
 bool _layer_is_present (const char* layerName);
 void _layers_check_init ();
 void _layers_check_release ();
+
+vkvg_device_thread_items_t* _get_or_create_threaded_objects (VkvgDevice dev, thrd_t id);
 #endif