diff --git a/src/renderer/backend_api.h b/src/renderer/backend_api.h index 445185c..9838514 100644 --- a/src/renderer/backend_api.h +++ b/src/renderer/backend_api.h @@ -2,7 +2,10 @@ #define RT_RENCOM_RENDERER_API_H #include +#include + #include "renderer.h" +#include "render_resource.h" typedef struct rt_physical_resource_manager_i rt_physical_resource_manager_i; @@ -15,11 +18,20 @@ typedef struct { rt_render_device_get_physical_resource_manager_fn *GetPhysicalResourceManager; } rt_render_device_i; +typedef bool rt_physical_resource_manager_is_present_fn(void *o, rt_render_resource_handle h); +typedef void rt_physical_resource_manager_destroy_fn(void *o, rt_render_resource_handle h); +typedef rt_result rt_physical_resource_manager_create_buffer_fn(void *o, rt_render_resource_handle h, const rt_render_buffer_desc *desc); +typedef rt_result rt_physical_resource_manager_create_texture2d_fn(void *o, rt_render_resource_handle h, const rt_render_texture2d_desc *desc); + /* Interface for the physical resource manager. * The physical resource manager maps render resources to actual gpu memory (=api objects like VkImage) */ struct rt_physical_resource_manager_i { void *o; + rt_physical_resource_manager_is_present_fn *IsPresent; + rt_physical_resource_manager_destroy_fn *Destroy; + rt_physical_resource_manager_create_buffer_fn *CreateBuffer; + rt_physical_resource_manager_create_texture2d_fn *CreateTexture2D; }; typedef struct { diff --git a/src/renderer/init.c b/src/renderer/init.c index 04e47ed..339076f 100644 --- a/src/renderer/init.c +++ b/src/renderer/init.c @@ -1,7 +1,7 @@ #include "renderer.h" -#include "backend_api.h" -#include "runtime/runtime.h" +#include "backend_api.h" +#include #include extern rt_cvar r_MaxRenderResources; diff --git a/src/renderer/vk/device.c b/src/renderer/vk/device.c index 5c29369..8c2f15c 100644 --- a/src/renderer/vk/device.c +++ b/src/renderer/vk/device.c @@ -649,6 +649,10 @@ rt_physical_resource_manager_i rtVkDevGetPhysicalResourceManager(void *o) { rt_vk_device *dev = o; rt_physical_resource_manager_i iface = { .o = &dev->phys_res_mgr, + .IsPresent = rtVkPhysicalResourceManagerIsPresent, + .Destroy = rtVkPhysicalResourceManagerDestroy, + .CreateBuffer = rtVkPhysicalResourceManagerCreateBuffer, + .CreateTexture2D = rtVkPhysicalResourceManagerCreateTexture2D, }; return iface; } \ No newline at end of file diff --git a/src/renderer/vk/init.c b/src/renderer/vk/init.c index 2ce00c2..e942571 100644 --- a/src/renderer/vk/init.c +++ b/src/renderer/vk/init.c @@ -8,15 +8,18 @@ extern rt_cvar r_VkEnableAPIAllocTracking; extern rt_cvar r_VkPhysDeviceName; extern rt_cvar r_VkMaxFramesInFlight; extern rt_cvar r_VkPreferMailboxMode; +extern rt_cvar r_VkPreferRelaxedMode; +extern rt_cvar r_VkEnableVSync; +extern rt_cvar r_VkMaxResources; void VkRegisterCVARs(void) { rtRegisterCVAR(&r_VkEnableAPIAllocTracking); rtRegisterCVAR(&r_VkPhysDeviceName); rtRegisterCVAR(&r_VkMaxFramesInFlight); -#if 0 - rtRegisterCVAR(&r_VkPreferredSwapchainImages); rtRegisterCVAR(&r_VkPreferMailboxMode); -#endif + rtRegisterCVAR(&r_VkPreferRelaxedMode); + rtRegisterCVAR(&r_VkEnableVSync); + rtRegisterCVAR(&r_VkMaxResources); } static rt_vk_device _device; diff --git a/src/renderer/vk/physical_resource_manager.c b/src/renderer/vk/physical_resource_manager.c index 3d0a898..ffaf8f0 100644 --- a/src/renderer/vk/physical_resource_manager.c +++ b/src/renderer/vk/physical_resource_manager.c @@ -1,6 +1,14 @@ -#include "physical_resource_manager.h" -#include "device.h" +#include +#include +#include "device.h" +#include "physical_resource_manager.h" + +#include + +RT_CVAR_SZ(r_VkMaxResources, + "Maximum number of simulaneously existing resources. (Default: 4096)", + 4096); static VmaAllocator CreateAllocator(rt_vk_device *dev) { #define SET_FNC(name) fncs.name = name @@ -58,9 +66,152 @@ rt_create_vk_physical_resource_manager_result rtCreateVkPhysicalResourceManager( return (rt_create_vk_physical_resource_manager_result){.result = RT_UNKNOWN_ERROR}; } - return (rt_create_vk_physical_resource_manager_result){.result = RT_SUCCESS, .phys_res_mgr = phys_res_mgr}; + rt_create_rwlock_result lock_res = rtCreateRWLock(); + if (!lock_res.ok) { + vmaDestroyAllocator(phys_res_mgr.allocator); + return (rt_create_vk_physical_resource_manager_result){.result = RT_UNKNOWN_ERROR}; + } + phys_res_mgr.lock = lock_res.lock; + + phys_res_mgr.resources = calloc(r_VkMaxResources.sz, sizeof(rt_vk_physical_resource)); + if (!phys_res_mgr.resources) { + rtDestroyRWLock(&phys_res_mgr.lock); + vmaDestroyAllocator(phys_res_mgr.allocator); + return (rt_create_vk_physical_resource_manager_result){.result = RT_OUT_OF_MEMORY}; + } + void *lut_mem = malloc(RT_HASH_TABLE_MEMORY_REQUIRED(2 * r_VkMaxResources.sz)); + if (!lut_mem) { + free(phys_res_mgr.resources); + rtDestroyRWLock(&phys_res_mgr.lock); + vmaDestroyAllocator(phys_res_mgr.allocator); + return (rt_create_vk_physical_resource_manager_result){.result = RT_OUT_OF_MEMORY}; + } + phys_res_mgr.resource_lut = rtCreateHashtable(2 * r_VkMaxResources.sz, lut_mem, NULL, NULL); + phys_res_mgr.lut_mem = lut_mem; + + phys_res_mgr.free_slots = calloc(r_VkMaxResources.sz, sizeof(uint32_t)); + if (!phys_res_mgr.free_slots) { + free(phys_res_mgr.lut_mem); + free(phys_res_mgr.resources); + rtDestroyRWLock(&phys_res_mgr.lock); + vmaDestroyAllocator(phys_res_mgr.allocator); + return (rt_create_vk_physical_resource_manager_result){.result = RT_OUT_OF_MEMORY}; + } + for (uint32_t i = 0; i < r_VkMaxResources.sz; ++i) { + phys_res_mgr.free_slots[i] = i; + } + phys_res_mgr.free_slot_count = r_VkMaxResources.sz; + + /* TODO: Create pools for transient resources. */ + + return (rt_create_vk_physical_resource_manager_result){.result = RT_SUCCESS, + .phys_res_mgr = phys_res_mgr}; } void rtDestroyVkPhysicalResourceManager(rt_vk_physical_resource_manager *phys_res_mgr) { + if (phys_res_mgr->free_slot_count < r_VkMaxResources.sz) { + rtReportError("VK", "Destroyed the physical resource manager, but there are still resources left."); + } + free(phys_res_mgr->free_slots); + free(phys_res_mgr->lut_mem); + free(phys_res_mgr->resources); vmaDestroyAllocator(phys_res_mgr->allocator); +} + +bool rtVkPhysicalResourceManagerIsPresent(void *o, rt_render_resource_handle handle) { + rt_vk_physical_resource_manager *phys_res_mgr = o; + rtLockRead(&phys_res_mgr->lock); + bool is_present = rtHashtableLookup(&phys_res_mgr->resource_lut, (uint64_t)handle.value, UINT64_MAX) != UINT64_MAX; + rtUnlockRead(&phys_res_mgr->lock); + return is_present; +} + +void rtVkPhysicalResourceManagerDestroy(void *o, rt_render_resource_handle handle) { + rt_vk_physical_resource_manager *phys_res_mgr = o; + rtLockWrite(&phys_res_mgr->lock); + uint32_t slot = rtHashtableLookup(&phys_res_mgr->resource_lut, handle.value, UINT32_MAX); + if (slot != UINT32_MAX) { + switch (phys_res_mgr->resources[slot].type) { + case RT_RENDER_RESOURCE_TYPE_BUFFER: { + vmaDestroyBuffer(phys_res_mgr->allocator, phys_res_mgr->resources[slot].buffer, phys_res_mgr->resources[slot].allocation); + } break; + case RT_RENDER_RESOURCE_TYPE_TEXTURE2D: { + RT_NOT_IMPLEMENTED; + } break; + default: { + rtReportError("VK", "Tried to destroy a resource, but the type is not valid."); + } + } + phys_res_mgr->free_slots[phys_res_mgr->free_slot_count++] = slot; + } + rtUnlockWrite(&phys_res_mgr->lock); +} + +rt_result rtVkPhysicalResourceManagerCreateBuffer(void *o, rt_render_resource_handle h, const rt_render_buffer_desc *desc) { + rt_vk_physical_resource_manager *phys_res_mgr = o; + + VkBufferUsageFlags usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT; + if ((desc->usage & RT_RENDER_BUFFER_USAGE_INDEX_BUFFER) != 0) + usage |= VK_BUFFER_USAGE_INDEX_BUFFER_BIT; + if ((desc->usage & RT_RENDER_BUFFER_USAGE_VERTEX_BUFFER) != 0) + usage |= VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; + if ((desc->usage & RT_RENDER_BUFFER_USAGE_STORAGE_BUFFER) != 0) + usage |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; + if ((desc->usage & RT_RENDER_BUFFER_USAGE_UPLOAD_BUFFER) != 0) { + usage &= ~VK_BUFFER_USAGE_TRANSFER_DST_BIT; + usage |= VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + } + + VkBufferCreateInfo buffer_info = { + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .size = desc->size, + .usage = usage, + .sharingMode = VK_SHARING_MODE_CONCURRENT, + .pQueueFamilyIndices = phys_res_mgr->dev->unique_families, + .queueFamilyIndexCount = phys_res_mgr->dev->unique_family_count, + }; + + VmaAllocationCreateFlags alloc_flags = 0; + if (desc->access == RT_RENDER_BUFFER_ACCESS_CPU_AND_GPU) + alloc_flags |= VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT; + + VmaAllocationCreateInfo alloc_info = { + .usage = VMA_MEMORY_USAGE_AUTO, + .flags = alloc_flags, + }; + + VkBuffer buffer; + VmaAllocation allocation; + if (vmaCreateBuffer(phys_res_mgr->allocator, &buffer_info, &alloc_info, &buffer, &allocation, NULL) != VK_SUCCESS) { + rtLog("VK", "Failed to create buffer."); + return RT_UNKNOWN_ERROR; + } + + /* Store */ + rt_result res = RT_SUCCESS; + rtLockWrite(&phys_res_mgr->lock); + if (phys_res_mgr->free_slot_count > 0u) { + uint32_t slot = phys_res_mgr->free_slots[--phys_res_mgr->free_slot_count]; + phys_res_mgr->resources[slot].type = RT_RENDER_RESOURCE_TYPE_BUFFER; + phys_res_mgr->resources[slot].buffer = buffer; + phys_res_mgr->resources[slot].allocation = allocation; + + /* The hashtable is large enough that this should never fail */ + rt_result insert_res = rtHashtableInsert(&phys_res_mgr->resource_lut, (uint64_t)h.value, (uint64_t)slot); + RT_UNUSED(insert_res); + RT_VERIFY(insert_res == RT_SUCCESS); + } + else { + rtLog("VK", "Could not create buffer, because no free slots for storing it are available."); + vmaDestroyBuffer(phys_res_mgr->allocator, buffer, allocation); + res = RT_OUT_OF_MEMORY; + } + rtUnlockWrite(&phys_res_mgr->lock); + + return res; +} + +rt_result rtVkPhysicalResourceManagerCreateTexture2D(void *o, rt_render_resource_handle h, const rt_render_texture2d_desc *desc) { + RT_NOT_IMPLEMENTED; + return RT_UNKNOWN_ERROR; } \ No newline at end of file diff --git a/src/renderer/vk/physical_resource_manager.h b/src/renderer/vk/physical_resource_manager.h index df0d803..11f1545 100644 --- a/src/renderer/vk/physical_resource_manager.h +++ b/src/renderer/vk/physical_resource_manager.h @@ -1,7 +1,12 @@ #ifndef RT_VK_PHYSICAL_RESOURCE_MANAGER_H #define RT_VK_PHYSICAL_RESOURCE_MANAGER_H +#include #include +#include +#include +#include + #include #define VMA_STATIC_VULKAN_FUNCTIONS 0 @@ -10,9 +15,29 @@ struct rt_vk_device; +typedef struct { + rt_render_resource_type type; + VmaAllocation allocation; + + union { + VkBuffer buffer; + VkImage image; + }; +} rt_vk_physical_resource; + typedef struct { struct rt_vk_device *dev; VmaAllocator allocator; + + /* Maps handles to slots inside resources */ + rt_hashtable resource_lut; + rt_vk_physical_resource *resources; + void *lut_mem; + + uint32_t *free_slots; + uint32_t free_slot_count; + + rt_rwlock lock; } rt_vk_physical_resource_manager; typedef struct { @@ -24,4 +49,10 @@ rt_create_vk_physical_resource_manager_result rtCreateVkPhysicalResourceManager( void rtDestroyVkPhysicalResourceManager(rt_vk_physical_resource_manager *phys_res_mgr); +/* rt_physical_resource_manager_i functions */ +bool rtVkPhysicalResourceManagerIsPresent(void *o, rt_render_resource_handle handle); +void rtVkPhysicalResourceManagerDestroy(void *o, rt_render_resource_handle handle); +rt_result rtVkPhysicalResourceManagerCreateBuffer(void *o, rt_render_resource_handle h, const rt_render_buffer_desc *desc); +rt_result rtVkPhysicalResourceManagerCreateTexture2D(void *o, rt_render_resource_handle h, const rt_render_texture2d_desc *desc); + #endif