From 71befd8eaf7244badb0188b751066afacb8d8ee3 Mon Sep 17 00:00:00 2001 From: Danny Holman Date: Fri, 25 Oct 2024 22:57:43 -0500 Subject: render: create the rendering subsystem Create the rendering subsystem and populate it with a basic Vulkan renderer. This renderer is not yet functional and should not be used yet. Signed-off-by: Danny Holman --- CMakeLists.txt | 44 ++++++++- render/vulkan/context.c | 176 ++++++++++++++++++++++++++++++++++++ render/vulkan/context.h | 43 +++++++++ render/vulkan/device.c | 213 ++++++++++++++++++++++++++++++++++++++++++++ render/vulkan/device.h | 60 +++++++++++++ render/vulkan/fence.c | 63 +++++++++++++ render/vulkan/fence.h | 19 ++++ render/vulkan/framebuffer.c | 34 +++++++ render/vulkan/framebuffer.h | 17 ++++ render/vulkan/image.c | 71 +++++++++++++++ render/vulkan/image.h | 19 ++++ render/vulkan/renderer.c | 167 ++++++++++++++++++++++++++++++++++ render/vulkan/renderpass.c | 172 +++++++++++++++++++++++++++++++++++ render/vulkan/renderpass.h | 42 +++++++++ render/vulkan/surface.h | 13 +++ render/vulkan/swapchain.c | 112 +++++++++++++++++++++++ render/vulkan/swapchain.h | 25 ++++++ render/vulkan/vkassert.h | 15 ++++ 18 files changed, 1304 insertions(+), 1 deletion(-) create mode 100644 render/vulkan/context.c create mode 100644 render/vulkan/context.h create mode 100644 render/vulkan/device.c create mode 100644 render/vulkan/device.h create mode 100644 render/vulkan/fence.c create mode 100644 render/vulkan/fence.h create mode 100644 render/vulkan/framebuffer.c create mode 100644 render/vulkan/framebuffer.h create mode 100644 render/vulkan/image.c create mode 100644 render/vulkan/image.h create mode 100644 render/vulkan/renderer.c create mode 100644 render/vulkan/renderpass.c create mode 100644 render/vulkan/renderpass.h create mode 100644 render/vulkan/surface.h create mode 100644 render/vulkan/swapchain.c create mode 100644 render/vulkan/swapchain.h create mode 100644 render/vulkan/vkassert.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ed4e53c..35a543e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,19 +20,61 @@ list(APPEND SOURCE_FILES ) list(APPEND SOURCE_FILES + render/directx/renderer.c + render/vulkan/context.c + render/vulkan/device.c + render/vulkan/fence.c + render/vulkan/framebuffer.c + render/vulkan/image.c + render/vulkan/renderer.c + render/vulkan/renderpass.c + render/vulkan/swapchain.c ui/window.c ui/input.c ui/renderer.c ) +find_package(Vulkan REQUIRED) +find_package(glfw3 REQUIRED) +find_package(OpenAL REQUIRED) +find_package(cglm REQUIRED) +find_package(Threads REQUIRED) + set(HEADER_DIR include) +list(APPEND INCLUDE_DIRS + ${HEADER_DIR} + ${Vulkan_INCLUDE_DIRS} + ${GLFW_INCLUDE_DIR} + ${OPENAL_INCLUDE_DIR} +) + +list(APPEND LINK_LIBS + ${Vulkan_LIBRARIES} + ${GLFW_LIBRARIES} + ${OPENAL_LIBRARY} + ${CMAKE_THREAD_LIBS_INIT} +) + +if (WIN32) + find_package(dlfcn-win32 REQUIRED) + set(GLFW_LIBRARIES glfw3dll) + set(DX12_LIBRARIES d3d12.lib dxgi.lib dxguid.lib) + set(DL_LIBRARIES dlfcn-win32::dl) + list(APPEND LINK_LIBS ${DX12_LIBRARIES} ${DL_LIBRARIES}) +else() + set(GLFW_LIBRARIES glfw) +endif() + +list(APPEND LINK_LIBS ${GLFW_LIBRARIES}) + add_compile_definitions(VERSION="${PROJECT_VERSION}") add_compile_definitions(RAPI_EXPORT) add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES}) set_property(TARGET ${PROJECT_NAME} PROPERTY ENABLE_EXPORTS ON) -target_include_directories(${PROJECT_NAME} PUBLIC ${HEADER_DIR}) +target_include_directories(${PROJECT_NAME} PUBLIC ${INCLUDE_DIRS}) +target_link_libraries(${PROJECT_NAME} ${LINK_LIBS}) set_target_properties(${PROJECT_NAME} PROPERTIES VERSION ${PROJECT_VERSION}) set_target_properties(${PROJECT_NAME} PROPERTIES SOVERSION ${PROJECT_VERSION_MAJOR}) diff --git a/render/vulkan/context.c b/render/vulkan/context.c new file mode 100644 index 0000000..c0fa650 --- /dev/null +++ b/render/vulkan/context.c @@ -0,0 +1,176 @@ +#include "context.h" +#include +#include +#include +#include + +char* _get_vkerr_str(VkResult res) { + char *ret; + switch (res) { + case VK_SUCCESS: + ret = "SUCCESS"; + break; + case VK_ERROR_OUT_OF_HOST_MEMORY: + ret = "OUT OF HOST MEMORY"; + break; + case VK_ERROR_OUT_OF_DEVICE_MEMORY: + ret = "OUT OF DEVICE MEMORY"; + break; + case VK_ERROR_INITIALIZATION_FAILED: + ret = "INITIALIZATION FAILED"; + break; + case VK_ERROR_LAYER_NOT_PRESENT: + ret = "VALIDATION LAYER NOT PRESENT"; + break; + case VK_ERROR_EXTENSION_NOT_PRESENT: + ret = "EXTENSION NOT PRESENT"; + break; + case VK_ERROR_INCOMPATIBLE_DRIVER: + ret = "INCOMPATIBLE DRIVER"; + break; + default: + ret = "UNKNOWN RESULT"; + break; + } + return strdup(ret); +} + +VKAPI_ATTR VkBool32 VKAPI_CALL _vulkan_db_callback( + VkDebugUtilsMessageSeverityFlagBitsEXT message_severity, + VkDebugUtilsMessageTypeFlagsEXT message_types, + const VkDebugUtilsMessengerCallbackDataEXT *callback_data, + void *user_data) { + switch (message_severity) { + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: + log_output(LOG_ERROR, callback_data->pMessage); + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: + log_output(LOG_WARN, callback_data->pMessage); + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: + log_output(LOG_INFO, callback_data->pMessage); + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: + log_output(LOG_DEBUG, callback_data->pMessage); + break; + default: + break; + } + return VK_FALSE; +} + +int _init_vkdebugger(struct vkcontext *context) { + log_output(LOG_INFO, "Initializing Vulkan debugger"); + uint32_t log_severity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT; + VkDebugUtilsMessengerCreateInfoEXT dbinfo = {VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT}; + dbinfo.messageSeverity = log_severity; + dbinfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT; + dbinfo.pfnUserCallback = _vulkan_db_callback; + dbinfo.pUserData = NULL; + PFN_vkCreateDebugUtilsMessengerEXT func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(context->instance, "vkCreateDebugUtilsMessengerEXT"); + if (func == NULL) { + log_output(LOG_ERROR, "Failed to create Vulkan debugger"); + return -1; + } + + VkResult res; + res = func(context->instance, &dbinfo, NULL, &context->db_messenger); + if (res != VK_SUCCESS) { + char *err_str = _get_vkerr_str(res); + log_output(LOG_ERROR, "Cannot create a Vulkan debug session: %s", err_str); + free(err_str); + return -1; + } + return 0; +} + +struct vkcontext* create_vkcontext(struct vklayer_container *vklayers, struct ext_container *ext) { + log_output(LOG_DEBUG, "Initializing Vulkan"); + VkApplicationInfo app_info = {VK_STRUCTURE_TYPE_APPLICATION_INFO}; + app_info.apiVersion = VK_API_VERSION_1_2; + app_info.pApplicationName = "RuneClient"; + app_info.applicationVersion = VK_MAKE_VERSION(1, 0, 0); + app_info.pEngineName = "RuneEngine"; + app_info.engineVersion = VK_MAKE_VERSION(1, 0, 0); + + VkInstanceCreateInfo cinfo = {VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO}; + cinfo.pApplicationInfo = &app_info; + cinfo.enabledExtensionCount = ext->ext_count; + cinfo.ppEnabledExtensionNames = ext->extensions; + cinfo.pNext = NULL; + + struct vkcontext *ret = rune_calloc(0, sizeof(struct vkcontext)); + ret->surface = rune_alloc(sizeof(struct vksurface)); + VkResult res; + res = vkCreateInstance(&cinfo, NULL, &ret->instance); + if (res != VK_SUCCESS) { + char *err_str = _get_vkerr_str(res); + log_output(LOG_FATAL, "Cannot create a Vulkan instance: %s", err_str); + free(err_str); + rune_free(ret); + ret = NULL; + } + + if (vklayers != NULL) { + log_output(LOG_INFO, "Validation layers enabled"); + _init_vkdebugger(ret); + cinfo.enabledLayerCount = vklayers->vklayer_count; + cinfo.ppEnabledLayerNames = vklayers->vklayer_names; + } else { + log_output(LOG_INFO, "Validation layers disabled"); + } + + ret->frame = 0; + return ret; +} + +void destroy_vkcontext(struct vkcontext *context) { + log_output(LOG_DEBUG, "Closing Vulkan instance"); + PFN_vkDestroyDebugUtilsMessengerEXT func = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(context->instance, "vkDestroyDebugUtilsMessengerEXT"); + func(context->instance, context->db_messenger, NULL); + vkDestroySurfaceKHR(context->instance, context->surface->handle, NULL); + vkDestroyInstance(context->instance, NULL); + rune_free(context->surface); + rune_free(context); +} + +struct vklayer_container* init_vklayers(struct ext_container *ext) { + const char** new_extensions = rune_alloc(sizeof(char*) * ext->ext_count++); + for (uint32_t i = 0; i < ext->ext_count-1; i++) + new_extensions[i] = ext->extensions[i]; + new_extensions[ext->ext_count-1] = VK_EXT_DEBUG_UTILS_EXTENSION_NAME; + ext->extensions = new_extensions; + + uint32_t layer_count; + vkEnumerateInstanceLayerProperties(&layer_count, NULL); + VkLayerProperties layer_props[layer_count]; + vkEnumerateInstanceLayerProperties(&layer_count, layer_props); + + struct vklayer_container *ret = rune_alloc(sizeof(struct vklayer_container)); + ret->vklayer_count = 1; + ret->vklayer_names = rune_alloc(sizeof(char*) * ret->vklayer_count); + ret->vklayer_names[0] = "VK_LAYER_KHRONOS_validation"; + + for (uint32_t i = 0; i < ret->vklayer_count; i++) { + log_output(LOG_DEBUG, "Searching for layer: %s", ret->vklayer_names[i]); + int found = 0; + for (uint32_t j = 0; j < layer_count; j++) { + if (strcmp(ret->vklayer_names[i], layer_props[j].layerName) == 0) { + found = 1; + break; + } + } + if (found == 0) { + log_output(LOG_WARN, "Required validation layer is missing: %s", ret->vklayer_names[i]); + rune_free(ret); + rune_free(ext->extensions); + return NULL; + } + } + log_output(LOG_INFO, "All prerequisite validation layers found"); + rune_free(ext->extensions); + return ret; +} diff --git a/render/vulkan/context.h b/render/vulkan/context.h new file mode 100644 index 0000000..14a86f1 --- /dev/null +++ b/render/vulkan/context.h @@ -0,0 +1,43 @@ +#ifndef VKCONTEXT_H +#define VKCONTEXT_H + +#include "surface.h" +#include "device.h" +#include "swapchain.h" +#include "renderpass.h" +#include "framebuffer.h" +#include "fence.h" + +struct ext_container { + const char** extensions; + uint32_t ext_count; +}; + +struct vklayer_container { + const char** vklayer_names; + uint32_t vklayer_count; +}; + +struct vkcontext { + VkInstance instance; + VkDebugUtilsMessengerEXT db_messenger; + VkSemaphore *queue_semaphores; + VkSemaphore *image_semaphores; + struct vksurface *surface; + struct vkswapchain *swapchain; + struct vkrendpass *rendpass; + struct vkdev *dev; + struct vkcmdbuffer** cmdbuffers; + struct vkframebuffer** framebuffers; + struct vkfence** fences_in_flight; + struct vkfence** images_in_flight; + uint32_t num_fences_in_flight; + uint32_t img_index; +}; + +struct vkcontext* create_vkcontext(struct vklayer_container *vklayers, struct ext_container *ext); +void destroy_vkcontext(struct vkcontext *context); + +struct vklayer_container* init_vklayers(struct ext_container *ext); + +#endif diff --git a/render/vulkan/device.c b/render/vulkan/device.c new file mode 100644 index 0000000..02cf246 --- /dev/null +++ b/render/vulkan/device.c @@ -0,0 +1,213 @@ +#include "device.h" +#include "vkassert.h" +#include +#include + +struct vkqfam_data { + uint32_t gfx; + uint32_t present; + uint32_t compute; + uint32_t transfer; +}; + +struct vkdev_data { + VkPhysicalDeviceProperties pdev_props; + VkPhysicalDeviceFeatures pdev_feats; + VkPhysicalDeviceMemoryProperties pdev_mprops; + uint32_t pdev_ext_count; + const char** pdev_extensions; +}; + +int _query_qfam_data(VkSurfaceKHR surface, VkPhysicalDevice pdev, VkQueueFamilyProperties **qfam_props) { + uint32_t qfam_count = 0; + vkGetPhysicalDeviceQueueFamilyProperties(pdev, &qfam_count, NULL); + *qfam_props = rune_alloc(sizeof(VkQueueFamilyProperties) * qfam_count); + vkGetPhysicalDeviceQueueFamilyProperties(pdev, &qfam_count, *qfam_props); + return qfam_count; +} + +void _query_pdev_data(VkPhysicalDevice pdev, struct vkdev_data *pdata) { + vkGetPhysicalDeviceProperties(pdev, &pdata->pdev_props); + vkGetPhysicalDeviceFeatures(pdev, &pdata->pdev_feats); + vkGetPhysicalDeviceMemoryProperties(pdev, &pdata->pdev_mprops); +} + +int _get_qfam_index(int num_props, uint32_t queue_type, VkQueueFamilyProperties *qfam_props) { + for (int i = 0; i < num_props; i++) { + if (qfam_props[i].queueFlags & queue_type) + return i; + } + return -1; +} + +int _check_pdev(VkSurfaceKHR surface, VkPhysicalDevice pdev) { + int score = 0; + + struct vkdev_data pdata; + _query_pdev_data(pdev, &pdata); + if (pdata.pdev_props.deviceType != VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) + return score; + + VkQueueFamilyProperties *qfam_props; + int num_qfams = _query_qfam_data(surface, pdev, &qfam_props); + if (_get_qfam_index(num_qfams, VK_QUEUE_GRAPHICS_BIT, qfam_props) != -1) + score += 20; + if (_get_qfam_index(num_qfams, VK_QUEUE_COMPUTE_BIT, qfam_props) != -1) + score += 5; + if (_get_qfam_index(num_qfams, VK_QUEUE_TRANSFER_BIT, qfam_props) != -1) + score += 20; + + VkBool32 present_bit; + vkGetPhysicalDeviceSurfaceSupportKHR(pdev, 0, surface, &present_bit); + if (present_bit != VK_FALSE) + score += 20; + + rune_free(qfam_props); + return score; +} + +struct vkdev* create_vkdev(VkInstance instance, VkSurfaceKHR surface) { + uint32_t pdev_count; + vkEnumeratePhysicalDevices(instance, &pdev_count, NULL); + if (pdev_count == 0) + return NULL; + VkPhysicalDevice pdevs[pdev_count]; + vkEnumeratePhysicalDevices(instance, &pdev_count, pdevs); + + uint32_t selected_pdev = -1; + for (uint32_t i = 0; i < pdev_count; i++) { + if (_check_pdev(surface, pdevs[i]) >= 60) { + selected_pdev = i; + break; + } + } + + if (selected_pdev == -1) { + log_output(LOG_FATAL, "No device meets minimum requirements for rendering"); + return NULL; + } + + struct vkdev *dev = rune_alloc(sizeof(struct vkdev)); + dev->pdev = pdevs[selected_pdev]; + + VkQueueFamilyProperties *qfam_props; + int num_qfams = _query_qfam_data(surface, pdevs[selected_pdev], &qfam_props); + VkBool32 present_support; + for (int i = 0; i < num_qfams; i++) { + vkassert(vkGetPhysicalDeviceSurfaceSupportKHR(dev->pdev, i, surface, &present_support), "Error retrieving present queue support"); + if (present_support == VK_TRUE) + dev->pres_index = i; + } + dev->gfx_index = _get_qfam_index(num_qfams, VK_QUEUE_GRAPHICS_BIT, qfam_props); + dev->tsfr_index = _get_qfam_index(num_qfams, VK_QUEUE_TRANSFER_BIT, qfam_props); + dev->comp_index = _get_qfam_index(num_qfams, VK_QUEUE_COMPUTE_BIT, qfam_props); + + float queue_priority = 1.0f; + VkDeviceQueueCreateInfo qcinfos[3]; + qcinfos[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + qcinfos[0].queueFamilyIndex = dev->gfx_index; + qcinfos[0].queueCount = 1; + qcinfos[0].flags = 0; + qcinfos[0].pNext = NULL; + qcinfos[0].pQueuePriorities = &queue_priority; + + qcinfos[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + qcinfos[1].queueFamilyIndex = dev->tsfr_index; + qcinfos[1].queueCount = 1; + qcinfos[1].flags = 0; + qcinfos[1].pNext = NULL; + qcinfos[1].pQueuePriorities = &queue_priority; + + qcinfos[2].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + qcinfos[2].queueFamilyIndex = dev->comp_index; + qcinfos[2].queueCount = 1; + qcinfos[2].flags = 0; + qcinfos[2].pNext = NULL; + qcinfos[2].pQueuePriorities = &queue_priority; + + struct vkdev_data pdata; + _query_pdev_data(pdevs[selected_pdev], &pdata); + + VkDeviceCreateInfo dcinfo; + dcinfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + dcinfo.pNext = NULL; + dcinfo.flags = 0; + dcinfo.queueCreateInfoCount = 2; + dcinfo.pQueueCreateInfos = qcinfos; + dcinfo.enabledLayerCount = 0; + dcinfo.ppEnabledLayerNames = NULL; + dcinfo.enabledExtensionCount = 1; + const char *ext_names = VK_KHR_SWAPCHAIN_EXTENSION_NAME; + dcinfo.ppEnabledExtensionNames = &ext_names; + dcinfo.pEnabledFeatures = &pdata.pdev_feats; + vkassert(vkCreateDevice(dev->pdev, &dcinfo, NULL, &dev->ldev), "Error creating Vulkan logical device"); + + vkGetDeviceQueue(dev->ldev, dev->gfx_index, 0, &dev->gfx_queue); + vkGetDeviceQueue(dev->ldev, dev->tsfr_index, 0, &dev->tsfr_queue); + vkGetDeviceQueue(dev->ldev, dev->pres_index, 0, &dev->pres_queue); + vkGetDeviceQueue(dev->ldev, dev->comp_index, 0, &dev->comp_queue); + + VkCommandPoolCreateInfo pcinfo; + pcinfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + pcinfo.pNext = NULL; + pcinfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + pcinfo.queueFamilyIndex = dev->gfx_index; + vkassert(vkCreateCommandPool(dev->ldev, &pcinfo, NULL, &dev->cmd_pool), "Error creating Vulkan command pool"); + + log_output(LOG_DEBUG, "Initialized new logical device"); + return dev; +} + +void destroy_vkdev(struct vkdev *dev) { + vkDestroyDevice(dev->ldev, NULL); + rune_free(dev); +} + +void get_swapchain_data(struct vkdev *dev, VkSurfaceKHR *surface) { + vkGetPhysicalDeviceSurfaceCapabilitiesKHR(dev->pdev, *surface, &dev->scdata.capabilities); + + vkGetPhysicalDeviceSurfaceFormatsKHR(dev->pdev, *surface, &dev->scdata.format_count, NULL); + dev->scdata.formats = rune_alloc(sizeof(VkSurfaceFormatKHR) * dev->scdata.format_count); + vkGetPhysicalDeviceSurfaceFormatsKHR(dev->pdev, *surface, &dev->scdata.format_count, dev->scdata.formats); + + vkGetPhysicalDeviceSurfacePresentModesKHR(dev->pdev, *surface, &dev->scdata.present_count, NULL); + dev->scdata.present_modes = rune_alloc(sizeof(VkPresentModeKHR) * dev->scdata.present_count); + vkGetPhysicalDeviceSurfacePresentModesKHR(dev->pdev, *surface, &dev->scdata.present_count, dev->scdata.present_modes); +} + +int get_depth_format(struct vkdev *dev) { + const uint64_t count = 3; + VkFormat formats[3] = { + VK_FORMAT_D32_SFLOAT_S8_UINT, + VK_FORMAT_D24_UNORM_S8_UINT, + VK_FORMAT_D32_SFLOAT}; + + uint32_t flags = VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT; + VkFormatProperties props; + for (uint64_t i = 0; i < count; i++) { + vkGetPhysicalDeviceFormatProperties(dev->pdev, formats[i], &props); + if ((props.linearTilingFeatures & flags) == flags) { + dev->depth_format = formats[i]; + return 1; + } else if ((props.optimalTilingFeatures & flags) == flags) { + dev->depth_format = formats[i]; + return 1; + } + } + return 0; +} + +uint32_t get_memory_index(struct vkdev *dev, uint32_t type, uint32_t flags) { + VkPhysicalDeviceMemoryProperties mem_props; + vkGetPhysicalDeviceMemoryProperties(dev->pdev, &mem_props); + + for (uint32_t i = 0; i < mem_props.memoryTypeCount; i++) { + uint32_t tmp = type & (1 << i); + uint32_t prop_flags = mem_props.memoryTypes[i].propertyFlags; + if (tmp & prop_flags & flags) + return i; + } + + log_output(LOG_WARN, "Unable to find suitable memory type"); + return -1; +} diff --git a/render/vulkan/device.h b/render/vulkan/device.h new file mode 100644 index 0000000..6bdd4f5 --- /dev/null +++ b/render/vulkan/device.h @@ -0,0 +1,60 @@ +/* + * Rune Game Engine + * Copyright 2024 Danny Holman + * + * 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 VKDEVICE_H +#define VKDEVICE_H + +#include +#include +#include + +struct vkswapchain_data { + VkSurfaceCapabilitiesKHR capabilities; + VkSurfaceFormatKHR *formats; + VkPresentModeKHR *present_modes; + uint32_t format_count; + uint32_t present_count; +}; + +struct vkdev { + VkPhysicalDevice pdev; + VkDevice ldev; + struct vkswapchain_data scdata; + uint32_t gfx_index; + uint32_t pres_index; + uint32_t tsfr_index; + uint32_t comp_index; + VkQueue gfx_queue; + VkQueue pres_queue; + VkQueue tsfr_queue; + VkQueue comp_queue; + VkCommandPool cmd_pool; + VkFormat depth_format; +}; + +struct vkdev* create_vkdev(VkInstance instance, VkSurfaceKHR surface); +void destroy_vkdev(struct vkdev *dev); + +void get_swapchain_data(struct vkdev *dev, VkSurfaceKHR *surface); +int get_depth_format(struct vkdev *dev); +uint32_t get_memory_index(struct vkdev *dev, uint32_t type, uint32_t flags); + +#endif diff --git a/render/vulkan/fence.c b/render/vulkan/fence.c new file mode 100644 index 0000000..6d869c9 --- /dev/null +++ b/render/vulkan/fence.c @@ -0,0 +1,63 @@ +#include "fence.h" +#include "vkassert.h" +#include +#include + +struct vkfence* create_vkfence(struct vkdev *dev, uint8_t signal) { + struct vkfence *ret = rune_alloc(sizeof(struct vkfence)); + + VkFenceCreateInfo fcinfo; + fcinfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + fcinfo.pNext = NULL; + if (signal == 1) + fcinfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; + else + fcinfo.flags = 0; + vkassert(vkCreateFence(dev->ldev, &fcinfo, NULL, &ret->handle), "Failed to create fence"); + return ret; +} + +void destroy_vkfence(struct vkfence *fence, struct vkdev *dev) { + if (fence->handle != NULL) { + vkDestroyFence(dev->ldev, fence->handle, NULL); + fence->handle = NULL; + } + rune_free(fence); +} + +uint8_t fence_lock(struct vkfence *fence, struct vkdev *dev, uint64_t timeout) { + if (fence->signal) + return 1; + + VkResult res = vkWaitForFences(dev->ldev, 1, &fence->handle, VK_TRUE, timeout); + switch (res) { + case VK_SUCCESS: + fence->signal = 1; + return 1; + case VK_TIMEOUT: + log_output(LOG_WARN, "Vulkan fence timed out"); + break; + case VK_ERROR_DEVICE_LOST: + log_output(LOG_ERROR, "Lost access to host device"); + break; + case VK_ERROR_OUT_OF_HOST_MEMORY: + log_output(LOG_ERROR, "Out of host memory"); + break; + case VK_ERROR_OUT_OF_DEVICE_MEMORY: + log_output(LOG_ERROR, "Out of device memory"); + break; + default: + log_output(LOG_ERROR, "Unknown error occurred on Vulkan fence"); + break; + } + + return 0; +} + +void fence_unlock(struct vkfence *fence, struct vkdev *dev) { + if (fence->signal == 0) + return; + + vkassert(vkResetFences(dev->ldev, 1, &fence->handle), "Cannot reset Vulkan fence"); + fence->signal = 1; +} diff --git a/render/vulkan/fence.h b/render/vulkan/fence.h new file mode 100644 index 0000000..34af87d --- /dev/null +++ b/render/vulkan/fence.h @@ -0,0 +1,19 @@ +#ifndef VKFENCE_H +#define VKFENCE_H + +#include "device.h" +#include +#include + +struct vkfence { + VkFence handle; + int signal; +}; + +struct vkfence* create_vkfence(struct vkdev *dev, uint8_t signal); +void destroy_vkfence(struct vkfence *fence, struct vkdev *dev); + +uint8_t fence_lock(struct vkfence *fence, struct vkdev *dev, uint64_t timeout); +void fence_unlock(struct vkfence *fence, struct vkdev *dev); + +#endif diff --git a/render/vulkan/framebuffer.c b/render/vulkan/framebuffer.c new file mode 100644 index 0000000..36d90c0 --- /dev/null +++ b/render/vulkan/framebuffer.c @@ -0,0 +1,34 @@ +#include "framebuffer.h" +#include "vkassert.h" +#include +#include + +struct vkframebuffer* create_vkframebuffer(struct vkdev *dev, struct vkrendpass *rendpass, uint32_t width, uint32_t height, uint32_t at_count, VkImageView *at) { + struct vkframebuffer *ret = rune_alloc(sizeof(struct vkframebuffer)); + ret->at_count = at_count; + ret->attachments = rune_alloc(sizeof(VkImageView) * at_count); + for (uint32_t i = 0; i < at_count; i++) + ret->attachments[i] = at[i]; + ret->rendpass = rendpass; + + VkFramebufferCreateInfo fbinfo; + fbinfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + fbinfo.pNext = NULL; + fbinfo.flags = 0; + fbinfo.renderPass = rendpass->handle; + fbinfo.attachmentCount = ret->at_count; + fbinfo.pAttachments = ret->attachments; + fbinfo.width = width; + fbinfo.height = height; + fbinfo.layers = 1; + vkassert(vkCreateFramebuffer(dev->ldev, &fbinfo, NULL, &ret->handle), "Failed to create Vulkan framebuffer"); + + return ret; +} + +void destroy_vkframebuffer(struct vkframebuffer *framebuffer, struct vkdev *dev) { + vkDestroyFramebuffer(dev->ldev, framebuffer->handle, NULL); + if (framebuffer->attachments) + rune_free(framebuffer->attachments); + rune_free(framebuffer); +} diff --git a/render/vulkan/framebuffer.h b/render/vulkan/framebuffer.h new file mode 100644 index 0000000..4bd7e86 --- /dev/null +++ b/render/vulkan/framebuffer.h @@ -0,0 +1,17 @@ +#ifndef VKFRAMEBUFFER_H +#define VKFRAMEBUFFER_H + +#include "device.h" +#include "renderpass.h" + +struct vkframebuffer { + VkFramebuffer handle; + uint32_t at_count; + VkImageView *attachments; + struct vkrendpass *rendpass; +}; + +struct vkframebuffer* create_vkframebuffer(struct vkdev *dev, struct vkrendpass *rendpass, uint32_t width, uint32_t height, uint32_t at_count, VkImageView *at); +void destroy_vkframebuffer(struct vkframebuffer *framebuffer, struct vkdev *dev); + +#endif diff --git a/render/vulkan/image.c b/render/vulkan/image.c new file mode 100644 index 0000000..9142f9e --- /dev/null +++ b/render/vulkan/image.c @@ -0,0 +1,71 @@ +#include "image.h" +#include "vkassert.h" +#include + +int _create_image_view(struct vkimage *image, struct vkdev *dev, VkFormat format, VkImageAspectFlags aflags) { + VkImageViewCreateInfo vcinfo; + vcinfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + vcinfo.pNext = NULL; + vcinfo.flags = 0; + vcinfo.image = image->handle; + vcinfo.format = format; + vcinfo.subresourceRange.aspectMask = aflags; + vcinfo.subresourceRange.baseMipLevel = 0; + vcinfo.subresourceRange.levelCount = 1; + vcinfo.subresourceRange.baseArrayLayer = 0; + vcinfo.subresourceRange.layerCount = 1; + vkassert(vkCreateImageView(dev->ldev, &vcinfo, NULL, &image->view), "Failed to create image view"); +} + +struct vkimage* create_vkimage(struct vkdev *dev, VkFormat format, uint32_t width, uint32_t height, uint32_t usage, uint32_t mem_flags, uint32_t aflags, int create_view) { + struct vkimage *ret = rune_alloc(sizeof(struct vkimage)); + ret->width = width; + ret->height = height; + + VkImageCreateInfo icinfo; + icinfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + icinfo.pNext = NULL; + icinfo.flags = 0; + icinfo.imageType = VK_IMAGE_TYPE_2D; + icinfo.extent.width = width; + icinfo.extent.height = height; + icinfo.extent.depth = 1; + icinfo.mipLevels = 1; + icinfo.arrayLayers = 1; + icinfo.format = format; + icinfo.tiling = VK_IMAGE_TILING_OPTIMAL; + icinfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + icinfo.usage = usage; + icinfo.samples = VK_SAMPLE_COUNT_1_BIT; + icinfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + vkassert(vkCreateImage(dev->ldev, &icinfo, NULL, &ret->handle), "Failed to create image"); + + VkMemoryRequirements mem_req; + vkGetImageMemoryRequirements(dev->ldev, ret->handle, &mem_req); + + int32_t mem_type = get_memory_index(dev, mem_req.memoryTypeBits, mem_flags); + if (mem_type == -1) + log_output(LOG_ERROR, "Image does not have a valid memory type"); + + VkMemoryAllocateInfo mainfo; + mainfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + mainfo.allocationSize = mem_req.size; + mainfo.memoryTypeIndex = mem_type; + vkassert(vkAllocateMemory(dev->ldev, &mainfo, NULL, &ret->memory), "Failed to allocate image memory"); + vkassert(vkBindImageMemory(dev->ldev, ret->handle, ret->memory, 0), "Failed to bind image memory"); + + if (create_view == 1) + _create_image_view(ret, dev, format, aflags); + + return ret; +} + +void destroy_vkimage(struct vkimage *image, struct vkdev *dev) { + if (image->view) + vkDestroyImageView(dev->ldev, image->view, NULL); + if (image->memory) + vkFreeMemory(dev->ldev, image->memory, NULL); + if (image->handle) + vkDestroyImage(dev->ldev, image->handle, NULL); + rune_free(image); +} diff --git a/render/vulkan/image.h b/render/vulkan/image.h new file mode 100644 index 0000000..9e5b101 --- /dev/null +++ b/render/vulkan/image.h @@ -0,0 +1,19 @@ +#ifndef VKIMAGE_H +#define VKIMAGE_H + +#include +#include "surface.h" +#include "device.h" + +struct vkimage { + VkImage handle; + VkDeviceMemory memory; + VkImageView view; + uint32_t width; + uint32_t height; +}; + +struct vkimage* create_vkimage(struct vkdev *dev, VkFormat format, uint32_t width, uint32_t height, uint32_t usage, uint32_t mem_flags, uint32_t aflags, int create_view); +void destroy_vkimage(struct vkimage *image, struct vkdev *dev); + +#endif diff --git a/render/vulkan/renderer.c b/render/vulkan/renderer.c new file mode 100644 index 0000000..91dc3fb --- /dev/null +++ b/render/vulkan/renderer.c @@ -0,0 +1,167 @@ +#include +#include +#include +#include +#include +#include +#include "context.h" +#include "image.h" +#include + +static struct vkcontext *context = NULL; + +void _init_cmdbuffers(void) { + uint32_t num_buffers = context->swapchain->img_count; + if (context->cmdbuffers == NULL) + context->cmdbuffers = rune_calloc(0, sizeof(struct vkcmdbuffer*) * num_buffers); + + for (uint32_t i = 0; i < num_buffers; i++) { + if (context->cmdbuffers[i] != NULL) + destroy_vkcmdbuffer(context->cmdbuffers[i], context->dev); + context->cmdbuffers[i] = create_vkcmdbuffer(context->dev, 1); + } + log_output(LOG_DEBUG, "Created %d command buffers", num_buffers); +} + +void _destroy_cmdbuffers(void) { + uint32_t num_buffers = context->swapchain->img_count; + if (context->cmdbuffers == NULL) + return; + for (uint32_t i = 0; i < num_buffers; i++) { + if (context->cmdbuffers[i] != NULL) + destroy_vkcmdbuffer(context->cmdbuffers[i], context->dev); + } + log_output(LOG_DEBUG, "Destroyed %d command buffers", num_buffers); +} + +void _init_framebuffers(void) { + uint32_t num_buffers = context->swapchain->img_count; + if (context->framebuffers == NULL) + context->framebuffers = rune_calloc(0, sizeof(struct vkframebuffer*) * num_buffers); + + uint32_t at_count = 2; + for (uint32_t i = 0; i < num_buffers; i++) { + VkImageView attachments[at_count]; + attachments[0] = context->swapchain->views[i]; + attachments[1] = context->swapchain->depth_attachment->view; + + if (context->framebuffers[i] != NULL) + destroy_vkframebuffer(context->framebuffers[i], context->dev); + context->framebuffers[i] = create_vkframebuffer(context->dev, + context->rendpass, + context->surface->width, + context->surface->height, + at_count, + attachments); + } + log_output(LOG_DEBUG, "Created %d frame buffers", num_buffers); +} + +void _destroy_framebuffers(void) { + uint32_t num_buffers = context->swapchain->img_count; + if (context->framebuffers == NULL) + return; + + for (uint32_t i = 0; i < num_buffers; i++) + destroy_vkframebuffer(context->framebuffers[i], context->dev); + log_output(LOG_DEBUG, "Destroyed %d frame buffers", num_buffers); +} + +int _init_vulkan(struct rune_window *window) { + struct timeval start; + struct timeval stop; + gettimeofday(&start, NULL); + + struct ext_container ext; + ext.extensions = glfwGetRequiredInstanceExtensions(&ext.ext_count); + struct vklayer_container *vklayers = init_vklayers(&ext); + + context = create_vkcontext(vklayers, &ext); + if (context == NULL) + return -1; + + VkResult res = glfwCreateWindowSurface(context->instance, + window->window, + NULL, + &context->surface->handle); + if (res != VK_SUCCESS) { + log_output(LOG_FATAL, "Cannot create rendering surface"); + return -1; + } + + context->dev = create_vkdev(context->instance, + context->surface->handle); + if (context->dev == NULL) + return -1; + + context->surface->width = window->winw; + context->surface->height = window->winh; + context->swapchain = create_swapchain(context->surface, context->dev); + if (context->swapchain == NULL) + return -1; + + vec4 area = {0, 0, context->surface->width, context->surface->height}; + vec4 color = {0, 0, 0, 1.0f}; + context->rendpass = create_vkrendpass(context->dev, context->swapchain, area, color, 1.0, 0); + if (context->rendpass == NULL) + return -1; + + _init_framebuffers(); + _init_cmdbuffers(); + + context->image_semaphores = rune_alloc(sizeof(VkSemaphore) * context->swapchain->max_frames); + context->queue_semaphores = rune_alloc(sizeof(VkSemaphore) * context->swapchain->max_frames); + context->fences_in_flight = rune_calloc(0, sizeof(struct vkfence*) * context->swapchain->max_frames); + + VkSemaphoreCreateInfo scinfo; + for (uint8_t i = 0; i < context->swapchain->max_frames; i++) { + scinfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + scinfo.pNext = NULL; + scinfo.flags = 0; + vkCreateSemaphore(context->dev->ldev, &scinfo, NULL, &context->image_semaphores[i]); + vkCreateSemaphore(context->dev->ldev, &scinfo, NULL, &context->queue_semaphores[i]); + context->fences_in_flight[i] = create_vkfence(context->dev, 1); + } + context->images_in_flight = rune_calloc(0, sizeof(struct vkfence*) * context->swapchain->img_count); + + gettimeofday(&stop, NULL); + log_output(LOG_INFO, "Finished initializing Vulkan in %lums", (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec); + return 0; +} + +void _close_vulkan(void) { + vkDeviceWaitIdle(context->dev->ldev); + for (uint8_t i = 0; i < context->swapchain->max_frames; i++) { + if (context->image_semaphores[i] != NULL) + vkDestroySemaphore(context->dev->ldev, context->image_semaphores[i], NULL); + if (context->queue_semaphores[i] != NULL) + vkDestroySemaphore(context->dev->ldev, context->queue_semaphores[i], NULL); + destroy_vkfence(context->fences_in_flight[i], context->dev); + } + + _destroy_cmdbuffers(); + _destroy_framebuffers(); + destroy_vkrendpass(context->rendpass, context->dev); + destroy_swapchain(context->swapchain, context->dev); + destroy_vkdev(context->dev); + destroy_vkcontext(context); +} + +void _draw_vulkan(void) { + if (fence_lock(context->fences_in_flight[context->swapchain->frame], context->dev, UINT64_MAX) != 0) + log_output(LOG_WARN, "In-flight fence locking failure"); +} + +void _clear_vulkan(void) { +} + +struct rune_renderer* select_render_vulkan(struct rune_window *window) { + struct rune_renderer *ret = rune_alloc(sizeof(struct rune_renderer)); + ret->init = _init_vulkan; + ret->close = _close_vulkan; + ret->draw = _draw_vulkan; + ret->clear = _clear_vulkan; + if ((*ret->init)(window) != 0) + rune_abort(); + return ret; +} diff --git a/render/vulkan/renderpass.c b/render/vulkan/renderpass.c new file mode 100644 index 0000000..59d1c26 --- /dev/null +++ b/render/vulkan/renderpass.c @@ -0,0 +1,172 @@ +#include "renderpass.h" +#include "vkassert.h" +#include + +struct vkcmdbuffer* create_vkcmdbuffer(struct vkdev *dev, int primary) { + struct vkcmdbuffer *ret = rune_calloc(0, sizeof(struct vkcmdbuffer)); + + VkCommandBufferAllocateInfo ainfo; + ainfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + ainfo.pNext = NULL; + ainfo.commandPool = dev->cmd_pool; + if (primary == 1) + ainfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + else + ainfo.level = VK_COMMAND_BUFFER_LEVEL_SECONDARY; + ainfo.commandBufferCount = 1; + vkassert(vkAllocateCommandBuffers(dev->ldev, &ainfo, &ret->handle), "Failed to initialize command buffer"); + + ret->state = CMDBUF_READY; + return ret; +} + +void destroy_vkcmdbuffer(struct vkcmdbuffer *cmdbuffer, struct vkdev *dev) { + vkFreeCommandBuffers(dev->ldev, dev->cmd_pool, 1, &cmdbuffer->handle); + rune_free(cmdbuffer); +} + +void cmdbuf_begin(struct vkcmdbuffer *cmdbuffer, int single, int rpass_cont, int sim_use) { + VkCommandBufferBeginInfo binfo; + binfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + binfo.flags = 0; + binfo.pNext = NULL; + if (single) + binfo.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + if (rpass_cont) + binfo.flags |= VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT; + if (sim_use) + binfo.flags |= VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT; + vkassert(vkBeginCommandBuffer(cmdbuffer->handle, &binfo), "Cannot record commands into command buffer"); + cmdbuffer->state = CMDBUF_RECORD; +} + +void cmdbuf_end(struct vkcmdbuffer *cmdbuffer) { + vkassert(vkEndCommandBuffer(cmdbuffer->handle), "Cannot end command buffer recording"); + cmdbuffer->state = CMDBUF_ENDREC; +} + +struct vkcmdbuffer* cmdbuf_begin_single_use(struct vkdev *dev) { + struct vkcmdbuffer *ret = create_vkcmdbuffer(dev, 1); + cmdbuf_begin(ret, 1, 0, 0); + return ret; +} + +void cmdbuf_end_single_use(struct vkcmdbuffer *cmdbuffer, struct vkdev *dev, VkQueue queue) { + cmdbuf_end(cmdbuffer); + VkSubmitInfo sinfo; + sinfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + sinfo.pNext = NULL; + sinfo.waitSemaphoreCount = 0; + sinfo.pWaitSemaphores = NULL; + sinfo.pWaitDstStageMask = NULL; + sinfo.commandBufferCount = 1; + sinfo.pCommandBuffers = &cmdbuffer->handle; + sinfo.signalSemaphoreCount = 0; + sinfo.pSignalSemaphores = NULL; + vkassert(vkQueueSubmit(queue, 1, &sinfo, 0), "Cannot submit single use command buffer"); + + vkassert(vkQueueWaitIdle(queue), "Error while waiting for Vulkan queue"); + destroy_vkcmdbuffer(cmdbuffer, dev); +} + +struct vkrendpass* create_vkrendpass(struct vkdev *dev, struct vkswapchain *swapchain, vec4 area, vec4 color, float depth, uint32_t stencil) { + VkAttachmentDescription atdesc[2]; + atdesc[0].flags = 0; + atdesc[0].format = swapchain->format; + atdesc[0].samples = VK_SAMPLE_COUNT_1_BIT; + atdesc[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + atdesc[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + atdesc[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + atdesc[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + atdesc[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + atdesc[0].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + + atdesc[1].flags = 0; + atdesc[1].format = dev->depth_format; + atdesc[1].samples = VK_SAMPLE_COUNT_1_BIT; + atdesc[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + atdesc[1].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + atdesc[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + atdesc[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + atdesc[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + atdesc[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + VkAttachmentReference cref; + cref.attachment = 0; + cref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + VkAttachmentReference dref; + dref.attachment = 1; + dref.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + VkSubpassDescription subpass; + subpass.flags = 0; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &cref; + subpass.pDepthStencilAttachment = &dref; + subpass.preserveAttachmentCount = 0; + subpass.pPreserveAttachments = NULL; + subpass.inputAttachmentCount = 0; + subpass.pInputAttachments = NULL; + + VkSubpassDependency dep; + dep.srcSubpass = VK_SUBPASS_EXTERNAL; + dep.dstSubpass = 0; + dep.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dep.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dep.srcAccessMask = 0; + dep.dstAccessMask = 0; + dep.dependencyFlags = 0; + + struct vkrendpass *ret = rune_alloc(sizeof(struct vkrendpass)); + VkRenderPassCreateInfo rcinfo; + rcinfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + rcinfo.pNext = NULL; + rcinfo.flags = 0; + rcinfo.attachmentCount = 2; + rcinfo.pAttachments = atdesc; + rcinfo.subpassCount = 1; + rcinfo.pSubpasses = &subpass; + rcinfo.dependencyCount = 1; + rcinfo.pDependencies = &dep; + vkassert(vkCreateRenderPass(dev->ldev, &rcinfo, NULL, &ret->handle), "Failed to create renderpass"); + + log_output(LOG_DEBUG, "Initialized renderpass"); + return ret; +} + +void destroy_vkrendpass(struct vkrendpass *rendpass, struct vkdev *dev) { + if (rendpass->handle) + vkDestroyRenderPass(dev->ldev, rendpass->handle, NULL); + rune_free(rendpass); +} + +void renderpass_begin(struct vkcmdbuffer *buf, struct vkrendpass *rendpass, VkFramebuffer framebuf) { + VkClearValue cvals[2]; + cvals[0].color.float32[0] = rendpass->color[0]; + cvals[0].color.float32[1] = rendpass->color[1]; + cvals[0].color.float32[2] = rendpass->color[2]; + cvals[0].color.float32[3] = rendpass->color[3]; + cvals[1].depthStencil.depth = rendpass->depth; + cvals[1].depthStencil.stencil = rendpass->stencil; + + VkRenderPassBeginInfo binfo; + binfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + binfo.renderPass = rendpass->handle; + binfo.framebuffer = framebuf; + binfo.renderArea.offset.x = rendpass->area[0]; + binfo.renderArea.offset.y = rendpass->area[1]; + binfo.renderArea.extent.width = rendpass->area[2]; + binfo.renderArea.extent.height = rendpass->area[3]; + binfo.clearValueCount = 2; + binfo.pClearValues = cvals; + + vkCmdBeginRenderPass(buf->handle, &binfo, VK_SUBPASS_CONTENTS_INLINE); + buf->state = CMDBUF_RPASS; +} + +void renderpass_end(struct vkcmdbuffer *buf, struct vkrendpass *rendpass) { + vkCmdEndRenderPass(buf->handle); + buf->state = CMDBUF_RECORD; +} diff --git a/render/vulkan/renderpass.h b/render/vulkan/renderpass.h new file mode 100644 index 0000000..9fbb417 --- /dev/null +++ b/render/vulkan/renderpass.h @@ -0,0 +1,42 @@ +#ifndef VKRENDERPASS_H +#define VKRENDERPASS_H + +#include "swapchain.h" +#include "device.h" +#include +#include + +enum cmdbuf_state { + CMDBUF_READY, + CMDBUF_RECORD, + CMDBUF_RPASS, + CMDBUF_ENDREC, + CMDBUF_SUBMIT +}; + +struct vkcmdbuffer { + VkCommandBuffer handle; + int state; +}; + +struct vkrendpass { + VkRenderPass handle; + vec4 area; + vec4 color; + float depth; + uint32_t stencil; +}; + +struct vkcmdbuffer* create_vkcmdbuffer(struct vkdev *dev, int primary); +void destroy_vkcmdbuffer(struct vkcmdbuffer *cmdbuffer, struct vkdev *dev); + +void cmdbuf_begin(struct vkcmdbuffer *cmdbuffer, int single, int rpass_cont, int sim_use); +void cmdbuf_end(struct vkcmdbuffer *cmdbuffer); + +struct vkrendpass* create_vkrendpass(struct vkdev *dev, struct vkswapchain *swapchain, vec4 area, vec4 color, float depth, uint32_t stencil); +void destroy_vkrendpass(struct vkrendpass *rendpass, struct vkdev *dev); + +void renderpass_begin(struct vkcmdbuffer *buf, struct vkrendpass *rendpass, VkFramebuffer framebuf); +void renderpass_end(struct vkcmdbuffer *buf, struct vkrendpass *rendpass); + +#endif diff --git a/render/vulkan/surface.h b/render/vulkan/surface.h new file mode 100644 index 0000000..8d59ab8 --- /dev/null +++ b/render/vulkan/surface.h @@ -0,0 +1,13 @@ +#ifndef VKSURFACE_H +#define VKSURFACE_H + +#include +#include + +struct vksurface { + VkSurfaceKHR handle; + uint32_t width; + uint32_t height; +}; + +#endif diff --git a/render/vulkan/swapchain.c b/render/vulkan/swapchain.c new file mode 100644 index 0000000..18b87a5 --- /dev/null +++ b/render/vulkan/swapchain.c @@ -0,0 +1,112 @@ +#include "swapchain.h" +#include "vkassert.h" +#include "image.h" +#include +#include +#include + +struct vkswapchain* create_swapchain(struct vksurface *surface, struct vkdev *dev) { + struct vkswapchain *swapchain = rune_alloc(sizeof(struct vkswapchain)); + VkExtent2D sc_extent = {surface->width, surface->height}; + swapchain->max_frames = 2; + get_swapchain_data(dev, &surface->handle); + swapchain->format_khr = dev->scdata.formats[0]; + if (dev->scdata.capabilities.currentExtent.width != UINT32_MAX) + sc_extent = dev->scdata.capabilities.currentExtent; + + VkExtent2D min = dev->scdata.capabilities.minImageExtent; + VkExtent2D max = dev->scdata.capabilities.maxImageExtent; + sc_extent.width = clamp(sc_extent.width, min.width, max.width); + sc_extent.height = clamp(sc_extent.height, min.height, max.height); + + uint32_t img_count = dev->scdata.capabilities.minImageCount + 1; + if (dev->scdata.capabilities.maxImageCount > 0 && img_count > dev->scdata.capabilities.maxImageCount) + img_count = dev->scdata.capabilities.maxImageCount; + + VkSwapchainCreateInfoKHR cinfo; + cinfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + cinfo.surface = surface->handle; + cinfo.minImageCount = img_count; + cinfo.imageFormat = swapchain->format_khr.format; + cinfo.imageColorSpace = swapchain->format_khr.colorSpace; + cinfo.imageExtent = sc_extent; + cinfo.imageArrayLayers = 1; + cinfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + cinfo.preTransform = dev->scdata.capabilities.currentTransform; + cinfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + cinfo.presentMode = VK_PRESENT_MODE_MAILBOX_KHR; + cinfo.clipped = VK_TRUE; + cinfo.oldSwapchain = NULL; + if (dev->gfx_index != dev->pres_index) { + uint32_t qfams[] = {dev->gfx_index, dev->pres_index}; + cinfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; + cinfo.queueFamilyIndexCount = 2; + cinfo.pQueueFamilyIndices = qfams; + } else { + cinfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + cinfo.queueFamilyIndexCount = 0; + cinfo.pQueueFamilyIndices = 0; + } + vkassert(vkCreateSwapchainKHR(dev->ldev, &cinfo, NULL, &swapchain->handle), "Failed to create swapchain"); + vkassert(vkGetSwapchainImagesKHR(dev->ldev, swapchain->handle, &swapchain->img_count, NULL), "Failed to aquire swapchain image count"); + + swapchain->images = rune_alloc(sizeof(VkImage) * swapchain->img_count); + swapchain->views = rune_alloc(sizeof(VkImageView) * swapchain->img_count); + vkassert(vkGetSwapchainImagesKHR(dev->ldev, swapchain->handle, &swapchain->img_count, swapchain->images), "Failed to aquire swapchain images"); + + VkImageViewCreateInfo vcinfo; + for (uint32_t i = 0; i < swapchain->img_count; i++) { + vcinfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + vcinfo.image = swapchain->images[i]; + vcinfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + vcinfo.format = swapchain->format; + vcinfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + vcinfo.subresourceRange.baseMipLevel = 0; + vcinfo.subresourceRange.levelCount = 1; + vcinfo.subresourceRange.baseArrayLayer = 0; + vcinfo.subresourceRange.layerCount = 1; + vkassert(vkCreateImageView(dev->ldev, &vcinfo, NULL, &swapchain->views[i]), "Failed to create image view"); + } + + if (get_depth_format(dev) == 0) { + log_output(LOG_FATAL, "Failed to find a supported image format"); + rune_abort(); + } + + swapchain->depth_attachment = create_vkimage(dev, + dev->depth_format, + surface->width, + surface->height, + VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + VK_IMAGE_ASPECT_DEPTH_BIT, + 1); + log_output(LOG_DEBUG, "Initialized swapchain"); + return swapchain; +} + +void destroy_swapchain(struct vkswapchain *swapchain, struct vkdev *dev) { + for (uint32_t i = 0; i < swapchain->img_count; i++) + vkDestroyImageView(dev->ldev, swapchain->views[i], NULL); + destroy_vkimage(swapchain->depth_attachment, dev); + vkDestroySwapchainKHR(dev->ldev, swapchain->handle, NULL); + rune_free(swapchain->images); + rune_free(swapchain->views); +} + +void vkswapchain_present(struct vkswapchain *swapchain, struct vkdev *dev) { + VkPresentInfoKHR pinfo = {VK_STRUCTURE_TYPE_PRESENT_INFO_KHR}; + pinfo.waitSemaphoreCount = 1; + pinfo.pWaitSemaphores = &swapchain->render_complete; + pinfo.swapchainCount = 1; + pinfo.pSwapchains = &swapchain->handle; + pinfo.pImageIndices = &dev->pres_index; + + VkResult res = vkQueuePresentKHR(dev->pres_queue, &pinfo); + if (res == VK_ERROR_OUT_OF_DATE_KHR || res == VK_SUBOPTIMAL_KHR) + STUBBED("Recreate swapchain"); + else if (res != VK_SUCCESS) + log_output(LOG_FATAL, "Cannot present swapchain image"); + + swapchain->frame = (swapchain->frame + 1) % swapchain->max_frames; +} diff --git a/render/vulkan/swapchain.h b/render/vulkan/swapchain.h new file mode 100644 index 0000000..d2f3d89 --- /dev/null +++ b/render/vulkan/swapchain.h @@ -0,0 +1,25 @@ +#ifndef VKSWAPCHAIN_H +#define VKSWAPCHAIN_H + +#include "surface.h" +#include "device.h" + +struct vkswapchain { + VkSwapchainKHR handle; + VkSurfaceFormatKHR format_khr; + VkFormat format; + VkSemaphore render_complete; + VkImage *images; + VkImageView *views; + struct vkimage *depth_attachment; + uint8_t max_frames; + uint32_t frame; + uint32_t img_count; +}; + +struct vkswapchain* create_swapchain(struct vksurface *surface, struct vkdev *dev); +void destroy_swapchain(struct vkswapchain *swapchain, struct vkdev *dev); + +void vkswapchain_present(struct vkswapchain *swapchain, struct vkdev *dev); + +#endif diff --git a/render/vulkan/vkassert.h b/render/vulkan/vkassert.h new file mode 100644 index 0000000..393e2de --- /dev/null +++ b/render/vulkan/vkassert.h @@ -0,0 +1,15 @@ +#ifndef VKASSERT_H +#define VKASSERT_H + +#include +#include +#include + +static inline void vkassert(VkResult value, const char *str) { + if (value != VK_SUCCESS) { + log_output(LOG_ERROR, "Vulkan assertion failed: %s", str); + rune_abort(); + } +} + +#endif -- cgit v1.2.3