feat(vk): Semaphore pool
- Also adds atomic functions for linux (CAS & exchange) - Also adds rt_bool32 type
This commit is contained in:
parent
33e596c9d6
commit
a6f3a04993
@ -629,6 +629,10 @@ rt_create_vk_device_result rtCreateVkDevice(const rt_renderer_window_info *info)
|
||||
if ((res = bindless_registry_result.result) != RT_SUCCESS)
|
||||
goto out;
|
||||
dev.bindless_registry = bindless_registry_result.bindless_registry;
|
||||
rt_create_vk_semaphore_pool_result semaphore_pool_result = rtCreateVkSemaphorePool(&dev, 128);
|
||||
if ((res = semaphore_pool_result.result) != RT_SUCCESS)
|
||||
goto out;
|
||||
dev.semaphore_pool = semaphore_pool_result.semaphore_pool;
|
||||
|
||||
dev.created_pools = calloc(128 * 9, sizeof(VkCommandPool));
|
||||
dev.created_pool_count = 0;
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "physical_resource_manager.h"
|
||||
#include "bindless_registry.h"
|
||||
#include "command_buffers.h"
|
||||
#include "semaphores.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
struct HINSTANCE__;
|
||||
@ -78,6 +79,7 @@ typedef struct rt_vk_device {
|
||||
/* *** Subsystems *** */
|
||||
rt_vk_physical_resource_manager phys_res_mgr;
|
||||
rt_vk_bindless_registry bindless_registry;
|
||||
rt_vk_semaphore_pool semaphore_pool;
|
||||
|
||||
/* *** Save created command pools in a list to clean them up at exit */
|
||||
VkCommandPool *created_pools;
|
||||
|
@ -17,13 +17,17 @@ if get_option('build_vk')
|
||||
'command_buffers.h',
|
||||
'device.h',
|
||||
'physical_resource_manager.h',
|
||||
'semaphores.h',
|
||||
'utils.h',
|
||||
|
||||
'bindless_registry.c',
|
||||
'command_buffers.c',
|
||||
'device.c',
|
||||
'init.c',
|
||||
'physical_resource_manager.c',
|
||||
'semaphores.c',
|
||||
'swapchain.c',
|
||||
'utils.c',
|
||||
'vma_impl.cpp',
|
||||
|
||||
'../../../contrib/volk/volk.c',
|
||||
|
@ -0,0 +1,3 @@
|
||||
#include <volk/volk.h>
|
||||
|
||||
#include <runtime/runtime.h>
|
115
src/renderer/vk/semaphores.c
Normal file
115
src/renderer/vk/semaphores.c
Normal file
@ -0,0 +1,115 @@
|
||||
#include "semaphores.h"
|
||||
#include "device.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <runtime/atomics.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
rt_create_vk_semaphore_pool_result rtCreateVkSemaphorePool(rt_vk_device *dev, uint32_t initial_size) {
|
||||
rt_vk_semaphore_pool sem_pool;
|
||||
rt_create_rwlock_result lock_res = rtCreateRWLock();
|
||||
if (!lock_res.ok) {
|
||||
return (rt_create_vk_semaphore_pool_result){.result = RT_UNKNOWN_ERROR};
|
||||
}
|
||||
sem_pool.resize_lock = lock_res.lock;
|
||||
sem_pool.dev = dev;
|
||||
sem_pool.acquire_index = 0;
|
||||
sem_pool.size = initial_size;
|
||||
sem_pool.semaphores = calloc(initial_size, sizeof(rt_vk_semaphore));
|
||||
if (!sem_pool.semaphores)
|
||||
return (rt_create_vk_semaphore_pool_result){.result = RT_OUT_OF_MEMORY};
|
||||
|
||||
for (uint32_t i = 0; i < initial_size; ++i) {
|
||||
VkSemaphoreTypeCreateInfo type_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO,
|
||||
.semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE,
|
||||
.initialValue = 0,
|
||||
};
|
||||
VkSemaphoreCreateInfo semaphore_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
|
||||
.flags = 0,
|
||||
.pNext = &type_info
|
||||
};
|
||||
|
||||
VkResult res = vkCreateSemaphore(dev->device, &semaphore_info, dev->alloc_cb, &sem_pool.semaphores[i].semaphore);
|
||||
if (res != VK_SUCCESS) {
|
||||
for (uint32_t j = 0; j < i; ++j) {
|
||||
vkDestroySemaphore(dev->device, sem_pool.semaphores[i].semaphore, dev->alloc_cb);
|
||||
}
|
||||
free(sem_pool.semaphores);
|
||||
return (rt_create_vk_semaphore_pool_result){.result = rtVkResultToRTResult(res)};
|
||||
}
|
||||
sem_pool.semaphores[i].value = 0;
|
||||
sem_pool.semaphores[i].in_use = RT_FALSE;
|
||||
}
|
||||
|
||||
return (rt_create_vk_semaphore_pool_result){
|
||||
.result = RT_SUCCESS,
|
||||
.semaphore_pool = sem_pool,
|
||||
};
|
||||
}
|
||||
|
||||
void rtDestroyVkSemaphorePool(rt_vk_semaphore_pool *pool) {
|
||||
for (uint32_t i = 0; i < pool->size; ++i) {
|
||||
vkDestroySemaphore(pool->dev->device, pool->semaphores[i].semaphore, pool->dev->alloc_cb);
|
||||
}
|
||||
free(pool->semaphores);
|
||||
}
|
||||
|
||||
rt_vk_semaphore *rtAcquireSemaphore(rt_vk_semaphore_pool *pool) {
|
||||
rtLockRead(&pool->resize_lock);
|
||||
uint32_t index = rtAtomic32Inc(&pool->acquire_index) % pool->size;
|
||||
if (rtAtomic32CAS(&pool->semaphores[index].in_use, RT_TRUE, RT_FALSE) == RT_FALSE) {
|
||||
/* Successfully acquired the semaphore */
|
||||
rtUnlockRead(&pool->resize_lock);
|
||||
return &pool->semaphores[index];
|
||||
}
|
||||
|
||||
/* We need to resize the pool */
|
||||
uint32_t pre_resize_size = pool->size;
|
||||
rtUnlockRead(&pool->resize_lock);
|
||||
rtLockWrite(&pool->resize_lock);
|
||||
if (pool->size > pre_resize_size) {
|
||||
/* Someone else was faster. Just try again */
|
||||
rtUnlockWrite(&pool->resize_lock);
|
||||
return rtAcquireSemaphore(pool);
|
||||
}
|
||||
uint32_t new_size = pre_resize_size * 2;
|
||||
rt_vk_semaphore *tmp = realloc(pool->semaphores, sizeof(rt_vk_semaphore) * new_size);
|
||||
if (!tmp) {
|
||||
rtUnlockWrite(&pool->resize_lock);
|
||||
rtReportError("VK", "Tried to grow the semaphore pool, but ran out of memory.");
|
||||
return NULL;
|
||||
}
|
||||
pool->semaphores = tmp;
|
||||
/* Create new semaphores */
|
||||
for (uint32_t i = pre_resize_size; i < new_size; ++i) {
|
||||
VkSemaphoreTypeCreateInfo type_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO,
|
||||
.semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE,
|
||||
.initialValue = 0,
|
||||
};
|
||||
VkSemaphoreCreateInfo semaphore_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
|
||||
.flags = 0,
|
||||
.pNext = &type_info
|
||||
};
|
||||
|
||||
VkResult res = vkCreateSemaphore(pool->dev->device, &semaphore_info, pool->dev->alloc_cb, &pool->semaphores[i].semaphore);
|
||||
if (res != VK_SUCCESS) {
|
||||
for (uint32_t j = pre_resize_size; j < i; ++j) {
|
||||
vkDestroySemaphore(pool->dev->device, pool->semaphores[i].semaphore, pool->dev->alloc_cb);
|
||||
}
|
||||
rtReportError("VK", "Tried to grow the semaphore pool, but failed to create new semaphores");
|
||||
}
|
||||
pool->semaphores[i].value = 0;
|
||||
pool->semaphores[i].in_use = RT_FALSE;
|
||||
}
|
||||
pool->size = new_size;
|
||||
rtUnlockWrite(&pool->resize_lock);
|
||||
return rtAcquireSemaphore(pool);
|
||||
}
|
||||
|
||||
void rtReleaseSemaphore(rt_vk_semaphore *semaphore) {
|
||||
rtAtomic32Exchange(&semaphore->in_use, RT_FALSE);
|
||||
}
|
47
src/renderer/vk/semaphores.h
Normal file
47
src/renderer/vk/semaphores.h
Normal file
@ -0,0 +1,47 @@
|
||||
#ifndef RT_VK_SEMAPHORES_H
|
||||
#define RT_VK_SEMAPHORES_H
|
||||
|
||||
#include <volk/volk.h>
|
||||
#include <runtime/threading.h>
|
||||
|
||||
struct rt_vk_device;
|
||||
|
||||
typedef struct {
|
||||
/* A timeline semaphore */
|
||||
VkSemaphore semaphore;
|
||||
|
||||
/* It's current value, or the next value it will signal */
|
||||
uint64_t value;
|
||||
|
||||
rt_bool32 in_use;
|
||||
} rt_vk_semaphore;
|
||||
|
||||
typedef struct {
|
||||
struct rt_vk_device *dev;
|
||||
|
||||
rt_vk_semaphore *semaphores;
|
||||
|
||||
/* Number of semaphores inside the pool */
|
||||
uint32_t size;
|
||||
|
||||
/* Running index of acquire operations. Atomically incremented to fetch a "new" semaphore */
|
||||
uint32_t acquire_index;
|
||||
|
||||
/* Locked as writing when resizing the array. During normal usage, this gets a read lock */
|
||||
rt_rwlock resize_lock;
|
||||
} rt_vk_semaphore_pool;
|
||||
|
||||
typedef struct {
|
||||
rt_result result;
|
||||
rt_vk_semaphore_pool semaphore_pool;
|
||||
} rt_create_vk_semaphore_pool_result;
|
||||
|
||||
rt_create_vk_semaphore_pool_result rtCreateVkSemaphorePool(struct rt_vk_device *device, uint32_t initial_size);
|
||||
|
||||
void rtDestroyVkSemaphorePool(rt_vk_semaphore_pool *pool);
|
||||
|
||||
rt_vk_semaphore *rtAcquireSemaphore(rt_vk_semaphore_pool *pool);
|
||||
|
||||
void rtReleaseSemaphore(rt_vk_semaphore *semaphore);
|
||||
|
||||
#endif
|
12
src/renderer/vk/utils.c
Normal file
12
src/renderer/vk/utils.c
Normal file
@ -0,0 +1,12 @@
|
||||
#include "utils.h"
|
||||
|
||||
rt_result rtVkResultToRTResult(VkResult result) {
|
||||
switch (result) {
|
||||
case VK_SUCCESS:
|
||||
return RT_SUCCESS;
|
||||
case VK_ERROR_OUT_OF_HOST_MEMORY:
|
||||
return RT_OUT_OF_MEMORY;
|
||||
default:
|
||||
return RT_UNKNOWN_ERROR;
|
||||
}
|
||||
}
|
9
src/renderer/vk/utils.h
Normal file
9
src/renderer/vk/utils.h
Normal file
@ -0,0 +1,9 @@
|
||||
#ifndef RT_VK_UTILS_H
|
||||
#define RT_VK_UTILS_H
|
||||
|
||||
#include <runtime/runtime.h>
|
||||
#include <volk/volk.h>
|
||||
|
||||
rt_result rtVkResultToRTResult(VkResult result);
|
||||
|
||||
#endif
|
@ -23,7 +23,8 @@
|
||||
_InterlockedExchange_rel((volatile long *)(_pDest), (_NewVal))
|
||||
#define rtAtomic32CASAcq(_pDest, _NewVal, _Compare) \
|
||||
_InterlockedCompareExchange_acq((volatile long *)(_pDest), (_NewVal), (_Compare))
|
||||
#define rtAtomic32CASRel(_pDest, _NewVal, _Compare) _InterlockedCompareExchange_rel((volatile long *)(_pDest), (_NewVal), (_Compare
|
||||
#define rtAtomic32CASRel(_pDest, _NewVal, _Compare) \
|
||||
_InterlockedCompareExchange_rel((volatile long *)(_pDest), (_NewVal), (_Compare))
|
||||
#else
|
||||
/* x64/86 does not have acquire/release versions of these */
|
||||
#define rtAtomic32ExchangeAcq(_pDest, _NewVal) \
|
||||
@ -32,7 +33,8 @@
|
||||
_InterlockedExchange((volatile long *)(_pDest), (_NewVal))
|
||||
#define rtAtomic32CASAcq(_pDest, _NewVal, _Compare) \
|
||||
_InterlockedCompareExchange((volatile long *)(_pDest), (_NewVal), (_Compare))
|
||||
#define rtAtomic32CASRel(_pDest, _NewVal, _Compare) _InterlockedCompareExchange((volatile long *)(_pDest), (_NewVal), (_Compare
|
||||
#define rtAtomic32CASRel(_pDest, _NewVal, _Compare) \
|
||||
_InterlockedCompareExchange((volatile long *)(_pDest), (_NewVal), (_Compare))
|
||||
#endif
|
||||
#define rtAtomic32Exchange(_pDest, _NewVal) \
|
||||
_InterlockedExchange((volatile long *)(_pDest), (_NewVal))
|
||||
@ -49,9 +51,37 @@
|
||||
#define rtAtomic32FetchAdd(pa, value) __atomic_fetch_add((pa), (value), __ATOMIC_SEQ_CST)
|
||||
#define rtAtomic64FetchAdd(pa, value) __atomic_fetch_add((pa), (value), __ATOMIC_SEQ_CST)
|
||||
|
||||
/* TODO Linux versions of compare exchange
|
||||
https://gcc.gnu.org/onlinedocs/gcc/_005f_005fatomic-Builtins.html
|
||||
*/
|
||||
/* CAS "implementations" to make it conform to our expected api, i.e. return the original value of
|
||||
* *dest */
|
||||
#define __RT_POSIX_CAS_N_IMPL(_T, _Name) \
|
||||
static RT_INLINE _T _Name(volatile _T *dest, _T new_val, _T compare, int memorder) { \
|
||||
_T original = compare; \
|
||||
if (!__atomic_compare_exchange_n(dest, \
|
||||
&compare, \
|
||||
new_val, \
|
||||
false, \
|
||||
memorder, \
|
||||
__ATOMIC_ACQUIRE)) \
|
||||
original = compare; /* Overwritten on failure with the original value */ \
|
||||
return original; \
|
||||
}
|
||||
|
||||
__RT_POSIX_CAS_N_IMPL(int, __rtPOSIXCASInt)
|
||||
__RT_POSIX_CAS_N_IMPL(long, __rtPOSIXCASLong)
|
||||
__RT_POSIX_CAS_N_IMPL(uint32_t, __rtPOSIXCASUint32)
|
||||
|
||||
#define rtAtomic32ExchangeAcq(_pDest, _NewVal) \
|
||||
__atomic_exchange_n((_pDest), (_NewVal), __ATOMIC_ACQUIRE)
|
||||
#define rtAtomic32ExchangeRel(_pDest, _NewVal) \
|
||||
__atomic_exchange_n((_pDest), (_NewVal), __ATOMIC_RELEASE)
|
||||
#define rtAtomic32CASAcq(_pDest, _NewVal, _Compare) \
|
||||
_Generic((_NewVal), int: __rtPOSIXCASInt, long: __rtPOSIXCASLong, uint32_t: __rtPOSIXCASUint32)((_pDest), (_NewVal), (_Compare), __ATOMIC_ACQUIRE)
|
||||
#define rtAtomic32CASRel(_pDest, _NewVal, _Compare) \
|
||||
_Generic((_NewVal), int: __rtPOSIXCASInt, long: __rtPOSIXCASLong, uint32_t: __rtPOSIXCASUint32)((_pDest), (_NewVal), (_Compare), __ATOMIC_RELEASE)
|
||||
#define rtAtomic32Exchange(_pDest, _NewVal) \
|
||||
__atomic_exchange_n((_pDest), (_NewVal), __ATOMIC_SEQ_CST)
|
||||
#define rtAtomic32CAS(_pDest, _NewVal, _Compare) \
|
||||
_Generic((_NewVal), int: __rtPOSIXCASInt, long: __rtPOSIXCASLong, uint32_t: __rtPOSIXCASUint32)((_pDest), (_NewVal), (_Compare), __ATOMIC_SEQ_CST)
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -87,6 +87,10 @@ typedef struct {
|
||||
unsigned int length;
|
||||
} rt_text_span;
|
||||
|
||||
typedef uint32_t rt_bool32;
|
||||
#define RT_TRUE 1u
|
||||
#define RT_FALSE 0u
|
||||
|
||||
/* snprintf replacement.
|
||||
* Always returns a zero terminated string.
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user