rtengine/src/renderer/vk/pipelines.c
2024-02-05 01:23:31 +01:00

185 lines
6.0 KiB
C

#include "runtime/config.h"
#include "runtime/handles.h"
#include "runtime/mem_arena.h"
#include "runtime/renderer_api.h"
#include "runtime/resources.h"
#include "runtime/threading.h"
#include "gpu.h"
#include "pipelines.h"
#include <stdlib.h>
#include <volk/volk.h>
RT_CVAR_I(r_VkMaxPipelineCount, "Maximum number of pipeline objects. Default: 1024", 1024);
typedef struct rt_pipeline_s {
uint32_t version;
rt_pipeline pipeline;
struct rt_pipeline_s *next_free;
} rt_pipeline_slot;
static rt_pipeline_slot *_pipelines;
static rt_pipeline_slot *_first_free;
static rt_rwlock _lock;
static void DestroyPipeline(rt_pipeline_slot *slot) {
if (slot->pipeline.pipeline) {
vkDestroyPipeline(g_gpu.device, slot->pipeline.pipeline, g_gpu.alloc_cb);
}
slot->next_free = _first_free;
_first_free = slot;
}
static VkShaderModule CreateShaderModuleFromResource(rt_resource_id rid) {
if (rid == RT_INVALID_RESOURCE_ID)
return VK_NULL_HANDLE;
rt_resource *resource = NULL;
size_t size = rtGetResourceSize(rid);
if (size == 0)
return VK_NULL_HANDLE;
rt_temp_arena temp = rtGetTemporaryArena(NULL, 0);
if (!temp.arena)
return VK_NULL_HANDLE;
VkShaderModule module = VK_NULL_HANDLE;
resource = rtArenaPush(temp.arena, size);
if (!resource) {
rtLog("VK", "Failed to allocate temporary memory for retrieving a shader resource");
goto out;
}
if (rtGetResource(rid, resource) != RT_SUCCESS) {
goto out;
}
if (resource->type != RT_RESOURCE_SHADER) {
rtLog("VK", "Attempted to create a shader module from a non-shader resource %llx", rid);
goto out;
}
rt_shader_info *info = resource->data;
if (!info) {
rtLog("VK", "Shader resource %llx has no attached shader_info", rid);
goto out;
}
VkShaderModuleCreateInfo module_info = {.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
.pCode = rtResolveRelptr(&info->bytecode),
.codeSize = info->bytecode_length};
if (vkCreateShaderModule(g_gpu.device, &module_info, g_gpu.alloc_cb, &module) != VK_SUCCESS) {
rtLog("VK", "Failed to create the shader module from resource %llx", rid);
goto out;
}
out:
rtReturnTemporaryArena(temp);
return module;
}
static bool CreateComputePipeline(VkShaderModule compute_shader,
const rt_pipeline_info *info,
rt_pipeline_slot *slot) {
return false;
}
static bool CreateGraphicsPipeline(VkShaderModule vertex_shader,
VkShaderModule fragment_shader,
const rt_pipeline_info *info,
rt_pipeline_slot *slot) {
return false;
}
rt_result InitPipelineManagement(void) {
rt_create_rwlock_result lock_res = rtCreateRWLock();
if (!lock_res.ok)
return RT_UNKNOWN_ERROR;
_lock = lock_res.lock;
_pipelines = calloc(r_VkMaxPipelineCount.i, sizeof(rt_pipeline_slot));
if (!_pipelines) {
rtDestroyRWLock(&_lock);
return RT_OUT_OF_MEMORY;
}
/* Keep [0] unused to preserve 0 as the invalid handle */
_first_free = &_pipelines[1];
for (int i = 1; i < r_VkMaxPipelineCount.i - 1; ++i) {
_pipelines[i].next_free = &_pipelines[i + 1];
}
return RT_SUCCESS;
}
void ShutdownPipelineManagement(void) {
for (int i = 1; i < r_VkMaxPipelineCount.i; ++i) {
DestroyPipeline(&_pipelines[i]);
}
free(_pipelines);
rtDestroyRWLock(&_lock);
_first_free = NULL;
}
rt_pipeline_handle RT_RENDERER_API_FN(CompilePipeline)(const rt_pipeline_info *info) {
rt_pipeline_handle handle = RT_INVALID_HANDLE;
rtLockWrite(&_lock);
if (!_first_free) {
rtLog("VK", "No free pipeline slots!");
rtUnlockWrite(&_lock);
return handle;
}
rt_pipeline_slot *slot = _first_free;
_first_free = slot->next_free;
slot->version = (slot->version + 1) & RT_GFX_HANDLE_MAX_VERSION;
/* No other thread that calls compile gets the same slot.
* Another thread accessing the slot via GetPipeline would get a version mismatch.
* The same holds for DestroyPipeline
*/
rtUnlockWrite(&_lock);
VkShaderModule vertex_shader = CreateShaderModuleFromResource(info->vertex_shader);
VkShaderModule fragment_shader = CreateShaderModuleFromResource(info->fragment_shader);
VkShaderModule compute_shader = CreateShaderModuleFromResource(info->compute_shader);
RT_UNUSED(vertex_shader);
RT_UNUSED(fragment_shader);
RT_UNUSED(compute_shader);
bool create_success = false;
if (compute_shader) {
create_success = CreateComputePipeline(compute_shader, info, slot);
} else if (vertex_shader && fragment_shader) {
create_success = CreateGraphicsPipeline(vertex_shader, fragment_shader, info, slot);
} else {
rtLog("VK", "Invalid combination of shaders in pipeline info.");
}
if (create_success) {
handle.version = slot->version;
handle.index = (uint32_t)(slot - _pipelines);
}
return handle;
}
void RT_RENDERER_API_FN(DestroyPipeline)(rt_pipeline_handle handle) {
if (handle.index >= (uint32_t)r_VkMaxPipelineCount.i)
return;
rtLockWrite(&_lock);
if (_pipelines[handle.index].version == handle.version)
DestroyPipeline(&_pipelines[handle.index]);
else
rtLog("VK", "Tried to destroy a pipeline using an outdated handle.");
rtUnlockWrite(&_lock);
}
const rt_pipeline *rtGetPipeline(rt_pipeline_handle handle) {
if (handle.index >= (uint32_t)r_VkMaxPipelineCount.i)
return NULL;
rtLockRead(&_lock);
rt_pipeline *res = NULL;
if (_pipelines[handle.index].version == handle.version)
res = &_pipelines[handle.index].pipeline;
else
rtLog("VK", "Tried to access a pipeline using an outdated handle.");
rtUnlockRead(&_lock);
return res;
}