From 4dd6d1a5186c911aabe5badf1f215216742044fc Mon Sep 17 00:00:00 2001 From: jacekpoz Date: Sat, 5 Oct 2024 00:59:58 +0200 Subject: [PATCH] huge refactor merge builds and partly works but spams a fuckton of errors because the descriptor set layout is set up to expect an image sampler with an image view BUT the descriptors are already created before doing anything with images both the amogus and button examples work but they spam errors the image example crashes because of some sampler fuckery that I think I got a hang of back when I started doing this but right now I don't remember, I'm hoping to move this forward this weekend --- Makefile | 2 +- default.nix | 2 +- include/ptk.h | 6 +- src/ptk.c | 57 +- src/ptk_color.c | 2 +- src/ptk_list.c | 2 +- src/ptk_list.h | 2 +- src/ptk_log.c | 2 +- src/ptk_vk/backend.c | 346 +++++++ src/ptk_vk/backend.h | 39 + src/ptk_vk/buffer.c | 130 +++ src/ptk_vk/buffer.h | 24 + src/ptk_vk/command_buffers.c | 31 + src/ptk_vk/command_buffers.h | 18 + src/ptk_vk/command_pool.c | 87 ++ src/ptk_vk/command_pool.h | 22 + src/ptk_vk/components.c | 10 +- src/ptk_vk/components.h | 4 +- src/ptk_vk/descriptors.c | 163 ++++ src/ptk_vk/descriptors.h | 22 + src/ptk_vk/device.c | 140 +++ src/ptk_vk/device.h | 25 + src/ptk_vk/draw.c | 98 -- src/ptk_vk/draw.h | 14 - src/ptk_vk/image.c | 59 +- src/ptk_vk/image.h | 8 +- src/ptk_vk/init.c | 1702 +--------------------------------- src/ptk_vk/init.h | 47 +- src/ptk_vk/instance.c | 89 ++ src/ptk_vk/instance.h | 14 + src/ptk_vk/physical_device.c | 81 ++ src/ptk_vk/physical_device.h | 14 + src/ptk_vk/pipeline.c | 339 +++++++ src/ptk_vk/pipeline.h | 15 + src/ptk_vk/render_pass.c | 64 ++ src/ptk_vk/render_pass.h | 14 + src/ptk_vk/swapchain.c | 295 ++++++ src/ptk_vk/swapchain.h | 39 + src/ptk_vk/sync_objects.c | 49 + src/ptk_vk/sync_objects.h | 21 + src/ptk_vk/uniform_buffers.c | 44 + src/ptk_vk/uniform_buffers.h | 27 + src/ptk_vk/utils.c | 2 +- src/ptk_vk/utils.h | 3 +- test/list.c | 5 +- 45 files changed, 2264 insertions(+), 1915 deletions(-) create mode 100644 src/ptk_vk/backend.c create mode 100644 src/ptk_vk/backend.h create mode 100644 src/ptk_vk/buffer.c create mode 100644 src/ptk_vk/buffer.h create mode 100644 src/ptk_vk/command_buffers.c create mode 100644 src/ptk_vk/command_buffers.h create mode 100644 src/ptk_vk/command_pool.c create mode 100644 src/ptk_vk/command_pool.h create mode 100644 src/ptk_vk/descriptors.c create mode 100644 src/ptk_vk/descriptors.h create mode 100644 src/ptk_vk/device.c create mode 100644 src/ptk_vk/device.h delete mode 100644 src/ptk_vk/draw.c delete mode 100644 src/ptk_vk/draw.h create mode 100644 src/ptk_vk/instance.c create mode 100644 src/ptk_vk/instance.h create mode 100644 src/ptk_vk/physical_device.c create mode 100644 src/ptk_vk/physical_device.h create mode 100644 src/ptk_vk/pipeline.c create mode 100644 src/ptk_vk/pipeline.h create mode 100644 src/ptk_vk/render_pass.c create mode 100644 src/ptk_vk/render_pass.h create mode 100644 src/ptk_vk/swapchain.c create mode 100644 src/ptk_vk/swapchain.h create mode 100644 src/ptk_vk/sync_objects.c create mode 100644 src/ptk_vk/sync_objects.h create mode 100644 src/ptk_vk/uniform_buffers.c create mode 100644 src/ptk_vk/uniform_buffers.h diff --git a/Makefile b/Makefile index 2f62239..c175aff 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,7 @@ CFLAGS += -DPTK_VERSION_PATCH=0 INCLUDE = include SRC = src -CFLAGS += -I$(INCLUDE) -I$(SRC) +CFLAGS += -I$(INCLUDE) -iquote$(SRC) BIN = target diff --git a/default.nix b/default.nix index c6b7d50..8bb0cdd 100644 --- a/default.nix +++ b/default.nix @@ -44,7 +44,7 @@ in stdenv.mkDerivation { ''; meta = with lib; { - homepage = "https://git.jacekpoz.pl/jacekpoz/${pname}"; + homepage = "https://git.jacekpoz.pl/poz/${pname}"; description = "poz toolkit"; license = licenses.eupl12; }; diff --git a/include/ptk.h b/include/ptk.h index 2da1165..6f9836a 100644 --- a/include/ptk.h +++ b/include/ptk.h @@ -1,9 +1,9 @@ #ifndef PTK_PTK_H_ #define PTK_PTK_H_ -#include -#include -#include +#include "ptk_color.h" +#include "ptk_list.h" +#include "ptk_vec.h" #include #include diff --git a/src/ptk.c b/src/ptk.c index 81de216..b0e3de1 100644 --- a/src/ptk.c +++ b/src/ptk.c @@ -1,14 +1,11 @@ // Copyright (jacekpoz 2024). Licensed under the EUPL-1.2 or later. -#include -#include -#include -#include +#include "ptk.h" +#include "ptk_log.h" +#include "ptk_option.h" -#include -#include -#include -#include +#include "ptk_vk/backend.h" +#include "ptk_vk/components.h" #include #ifndef GLFW_INCLUDE_VULKAN @@ -21,29 +18,6 @@ #include #include -static GLFWwindow *m_window = NULL; - -static void framebuffer_resize_callback(GLFWwindow *window, int width, int height) { - (void)window; (void)width; (void)height; - g_framebuffer_resized = true; -} - -static void key_callback(GLFWwindow *window, int key, int scancode, int action, int mods) { - (void)window; (void)scancode; (void)action; (void)mods; - if (key == GLFW_KEY_SPACE) { - PTK_LIST_FOR_EACH_P(Vertex, g_vertices, vertex, { - vertex->pos.x += 1.0f; - }); - vk_transfer_vertex_data(); - } -} - -static void mouse_button_callback(GLFWwindow *window, int button, int action, int mods) { - double x, y; - glfwGetCursorPos(window, &x, &y); - vk_handle_mouse_button_input((PtkPos){ .x = x, .y = y }, button, action, mods); -} - PTK_OPTION_DEFINE(PtkLogLevel); PTK_OPTION(PtkLogLevel) get_log_level(void) { @@ -111,19 +85,13 @@ bool ptk_init(const size_t width, const size_t height, const char *title, const glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); - m_window = glfwCreateWindow(width, height, title, NULL, NULL); + GLFWwindow *window = glfwCreateWindow(width, height, title, NULL, NULL); - if (m_window == NULL) { + if (window == NULL) { PTK_ERR("failed creating GLFW window"); return false; } - glfwSetFramebufferSizeCallback(m_window, framebuffer_resize_callback); - glfwSetKeyCallback(m_window, key_callback); - glfwSetMouseButtonCallback(m_window, mouse_button_callback); - - vk_init_vertices(); - // on tiling desktops (most standalone X11 WMs / Wayland compositors) // the size of the window won't necessarily be what the user set due to tiling // so we do this ugly workaround where we fetch the size again @@ -132,9 +100,9 @@ bool ptk_init(const size_t width, const size_t height, const char *title, const // this fixes an issue where components would get drawn squished // and their hitboxes wouldn't match up with them visually int actual_width = 0, actual_height = 0; - glfwGetFramebufferSize(m_window, &actual_width, &actual_height); + glfwGetFramebufferSize(window, &actual_width, &actual_height); - if (!vk_init(m_window, actual_width, actual_height, title, application_version)) { + if (!vk_init(window, actual_width, actual_height, title, application_version)) { PTK_ERR("failed initializing vulkan"); return false; } @@ -198,17 +166,14 @@ PtkHandle ptk_image(const char *path, const PtkPos top_left, const PtkSize size) int ptk_run(PtkHandle root) { vk_init_components(root); - while (!glfwWindowShouldClose(m_window)) { + while (!vk_is_done()) { glfwPollEvents(); if (!vk_draw_frame()) { break; } } - vkDeviceWaitIdle(g_dev); - - vk_cleanup(); - vk_components_cleanup(); + vk_finish(); return EXIT_SUCCESS; } diff --git a/src/ptk_color.c b/src/ptk_color.c index 3c0abd2..e023a9a 100644 --- a/src/ptk_color.c +++ b/src/ptk_color.c @@ -1,4 +1,4 @@ -#include +#include "ptk_color.h" #include diff --git a/src/ptk_list.c b/src/ptk_list.c index 87547fa..5d24bda 100644 --- a/src/ptk_list.c +++ b/src/ptk_list.c @@ -1,6 +1,6 @@ // Copyright (jacekpoz 2024). Licensed under the EUPL-1.2 or later. -#include +#include "ptk_list.h" #include #include diff --git a/src/ptk_list.h b/src/ptk_list.h index 6fa8910..9c73651 100644 --- a/src/ptk_list.h +++ b/src/ptk_list.h @@ -4,8 +4,8 @@ #define PTK_PTK_LIST_H_ #include -#include #include +#include #define PTK_LIST(T) struct PtkList_##T diff --git a/src/ptk_log.c b/src/ptk_log.c index 7e3cbf5..70dffc5 100644 --- a/src/ptk_log.c +++ b/src/ptk_log.c @@ -1,6 +1,6 @@ // Copyright (jacekpoz 2024). Licensed under the EUPL-1.2 or later. -#include +#include "ptk_log.h" #include diff --git a/src/ptk_vk/backend.c b/src/ptk_vk/backend.c new file mode 100644 index 0000000..962f215 --- /dev/null +++ b/src/ptk_vk/backend.c @@ -0,0 +1,346 @@ +// Copyright (jacekpoz 2024). Licensed under the EUPL-1.2 or later. + +#include "ptk_vk/backend.h" + +#include "ptk_vk/buffer.h" +#include "ptk_vk/command_buffers.h" +#include "ptk_vk/command_pool.h" +#include "ptk_vk/components.h" +#include "ptk_vk/descriptors.h" +#include "ptk_vk/device.h" +#include "ptk_vk/init.h" +#include "ptk_vk/instance.h" +#include "ptk_vk/physical_device.h" + +#include "ptk_log.h" +#include "ptk_vk/pipeline.h" +#include "ptk_vk/render_pass.h" +#include "ptk_vk/swapchain.h" +#include "ptk_vk/sync_objects.h" +#include "ptk_vk/uniform_buffers.h" +#include "ptk_vk/utils.h" + +#include + +bool g_framebuffer_resized = false; + +GLFWwindow *g_window; +VkSurfaceKHR g_surface; + +VkBuffer g_vertex_buffer; +VkDeviceMemory g_vertex_buffer_memory; +VkBuffer g_index_buffer; +VkDeviceMemory g_index_buffer_memory; + +#ifdef DEBUG +const PTK_ARRAY(constcharptr) g_validation_layers = PTK_ARRAY_NEW(constcharptr, { + "VK_LAYER_KHRONOS_validation" +}); +#else +const PTK_ARRAY(constcharptr) g_validation_layers = PTK_ARRAY_EMPTY(constcharptr); +#endif + +static uint32_t m_current_frame = 0; + +static void mouse_button_callback(GLFWwindow *window, int button, int action, int mods) { + double x, y; + glfwGetCursorPos(window, &x, &y); + vk_handle_mouse_button_input((PtkPos){ .x = x, .y = y }, button, action, mods); +} + +static void framebuffer_resized(GLFWwindow *window, int width, int height) { + (void)window; (void)width; (void)height; + g_framebuffer_resized = true; +} + +bool record_command_buffer(const VkCommandBuffer command_buffer, const uint32_t image_index) { + VK_TRY(false, + vkBeginCommandBuffer( + command_buffer, + &(VkCommandBufferBeginInfo){ + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .pNext = NULL, + .flags = 0, + .pInheritanceInfo = NULL, + } + ) + ); + + vkCmdBeginRenderPass( + command_buffer, + &(VkRenderPassBeginInfo){ + .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, + .pNext = NULL, + .renderPass = g_render_pass, + .framebuffer = g_swapchain_framebuffers.data[image_index], + .renderArea = (VkRect2D){ + .offset = (VkOffset2D){ + .x = 0, + .y = 0, + }, + .extent = g_swapchain_extent, + }, + .clearValueCount = 1, + .pClearValues = &(VkClearValue){ + .color = (VkClearColorValue){ + .float32[0] = 0.0f, + .float32[1] = 0.0f, + .float32[2] = 0.0f, + .float32[3] = 1.0f, + }, + }, + }, + VK_SUBPASS_CONTENTS_INLINE + ); + + vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, g_pipeline); + + vkCmdBindVertexBuffers(command_buffer, 0, 1, (VkBuffer []){g_vertex_buffer}, (VkDeviceSize []){0}); + + vkCmdBindIndexBuffer(command_buffer, g_index_buffer, 0, VK_INDEX_TYPE_UINT32); + + vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, g_pipeline_layout, 0, 1, &g_descriptor_sets.data[m_current_frame], 0, NULL); + + vkCmdDrawIndexed(command_buffer, g_indices.size, 1, 0, 0, 0); + + vkCmdEndRenderPass(command_buffer); + + VK_TRY(false, + vkEndCommandBuffer(command_buffer) + ); + + return true; +} + +bool vk_init(GLFWwindow *window, const size_t width, const size_t height, const char *title, const PtkVersion version) { + g_window = window; + g_uniform_buffer_object.initial_window_size.w = width; + g_uniform_buffer_object.initial_window_size.h = height; + + vk_init_vertices(); + + glfwSetFramebufferSizeCallback(window, framebuffer_resized); + glfwSetMouseButtonCallback(window, mouse_button_callback); + + if (!vk_instance_create(title, version)) { + PTK_ERR("failed creating VkInstance"); + return false; + } + + if (!vk_select_physical_dev()) { + PTK_ERR("failed selecting physical device"); + return false; + } + + VK_TRY(false, glfwCreateWindowSurface(g_instance, g_window, NULL, &g_surface)); + + if (!vk_create_logical_dev()) { + PTK_ERR("failed creating logical device"); + return false; + } + + if (!vk_create_swapchain()) { + PTK_ERR("failed creating swapchain"); + return false; + } + + if (!vk_create_image_views()) { + PTK_ERR("failed creating image views"); + return false; + } + + if (!vk_create_render_pass()) { + PTK_ERR("failed creating render pass"); + return false; + } + + if (!vk_create_descriptor_set_layout()) { + PTK_ERR("failed creating descriptor set layout"); + return false; + } + + if (!vk_create_pipeline()) { + PTK_ERR("failed creating graphics pipeline"); + return false; + } + + if (!vk_create_framebuffers()) { + PTK_ERR("failed creating framebuffers"); + return false; + } + + if (!vk_create_command_pool()) { + PTK_ERR("failed creating command pool"); + return false; + } + + const VkDeviceSize buffer_size = 65536; + + PTK_OPTION(BufferStuff) vertex_buffer_stuff_opt = vk_create_buffer( + g_dev, + g_physical_dev, + buffer_size, + VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT + ); + + if (!vertex_buffer_stuff_opt.exists) { + PTK_ERR("failed creating vertex buffer"); + return false; + } + + g_vertex_buffer = vertex_buffer_stuff_opt.value.buffer; + g_vertex_buffer_memory = vertex_buffer_stuff_opt.value.buffer_memory; + + PTK_OPTION(BufferStuff) index_buffer_stuff_opt = vk_create_buffer( + g_dev, + g_physical_dev, + buffer_size, + VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT + ); + + if (!index_buffer_stuff_opt.exists) { + PTK_ERR("failed creating index buffer"); + return false; + } + + g_index_buffer = index_buffer_stuff_opt.value.buffer; + g_index_buffer_memory = index_buffer_stuff_opt.value.buffer_memory; + + if (!vk_create_uniform_buffers()) { + PTK_ERR("failed creating uniform buffers"); + return false; + } + + if (!vk_create_descriptor_pool()) { + PTK_ERR("failed creating descriptor pool"); + return false; + } + + if (!vk_create_descriptor_sets()) { + PTK_ERR("failed creating descriptor sets"); + return false; + } + + if (!vk_allocate_command_buffers()) { + PTK_ERR("failed allocating command buffers"); + return false; + } + + if (!vk_create_sync_objects()) { + PTK_ERR("failed creating sync objects"); + return false; + } + + return true; +} + +bool vk_is_done(void) { + return glfwWindowShouldClose(g_window); +} + +bool update_uniform_buffer(const size_t current_frame) { + g_uniform_buffer_object.window_size.w = g_swapchain_extent.width; + g_uniform_buffer_object.window_size.h = g_swapchain_extent.height; + + memcpy(g_uniform_buffers_mapped.data[current_frame], &g_uniform_buffer_object, sizeof(g_uniform_buffer_object)); + + return true; +} + +bool vk_draw_frame(void) { + vkWaitForFences(g_dev, 1, &g_in_flight_fences.data[m_current_frame], VK_TRUE, UINT64_MAX); + + uint32_t image_index; + const VkResult acquire_next_image_result = vkAcquireNextImageKHR( + g_dev, + g_swapchain, + UINT64_MAX, + g_image_available_semaphores.data[m_current_frame], + VK_NULL_HANDLE, + &image_index + ); + + if (acquire_next_image_result == VK_ERROR_OUT_OF_DATE_KHR) { + if (!vk_recreate_swapchain()) { + return false; + } + return true; + } else if (acquire_next_image_result != VK_SUCCESS && acquire_next_image_result != VK_SUBOPTIMAL_KHR) { + PTK_ERR("%s", vk_result_string(acquire_next_image_result)); + return false; + } + + update_uniform_buffer(m_current_frame); + + vkResetFences(g_dev, 1, &g_in_flight_fences.data[m_current_frame]); + + vkResetCommandBuffer(g_command_buffers.data[m_current_frame], 0); + if (!record_command_buffer(g_command_buffers.data[m_current_frame], image_index)) { + PTK_ERR("failed recording command buffer"); + return false; + } + + const VkSemaphore signal_semaphores[] = {g_render_finished_semaphores.data[m_current_frame]}; + + VK_TRY(false, + vkQueueSubmit( + g_graphics_queue, + 1, + &(VkSubmitInfo){ + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .pNext = NULL, + .waitSemaphoreCount = 1, + .pWaitSemaphores = &g_image_available_semaphores.data[m_current_frame], + .pWaitDstStageMask = &(VkPipelineStageFlags){VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}, + .commandBufferCount = 1, + .pCommandBuffers = &g_command_buffers.data[m_current_frame], + .signalSemaphoreCount = 1, + .pSignalSemaphores = signal_semaphores, + }, + g_in_flight_fences.data[m_current_frame] + ) + ); + + const VkResult queue_present_result = vkQueuePresentKHR( + g_present_queue, + &(VkPresentInfoKHR){ + .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, + .pNext = NULL, + .waitSemaphoreCount = 1, + .pWaitSemaphores = signal_semaphores, + .swapchainCount = 1, + .pSwapchains = &(VkSwapchainKHR){g_swapchain}, + .pImageIndices = &image_index, + .pResults = NULL, + } + ); + + if ( + queue_present_result == VK_ERROR_OUT_OF_DATE_KHR || + queue_present_result == VK_SUBOPTIMAL_KHR || + g_framebuffer_resized + ) { + g_framebuffer_resized = false; + if (!vk_recreate_swapchain()) { + return false; + } + PTK_TRACE("recreated swapchain"); + } else if (queue_present_result != VK_SUCCESS) { + PTK_ERR("%s", vk_result_string(queue_present_result)); + return false; + } + + m_current_frame = (m_current_frame + 1) % g_max_frames_in_flight; + + return true; +} + + +void vk_finish(void) { + vkDeviceWaitIdle(g_dev); + + vk_cleanup(); + vk_components_cleanup(); +} diff --git a/src/ptk_vk/backend.h b/src/ptk_vk/backend.h new file mode 100644 index 0000000..8197b99 --- /dev/null +++ b/src/ptk_vk/backend.h @@ -0,0 +1,39 @@ +// Copyright (jacekpoz 2024). Licensed under the EUPL-1.2 or later. + +#ifndef PTK_PTK_VK_BACKEND_H_ +#define PTK_PTK_VK_BACKEND_H_ + +#include "ptk.h" +#include "ptk_array.h" + +#ifndef GLFW_INCLUDE_VULKAN +#define GLFW_INCLUDE_VULKAN +#endif +#include + +#include + +extern bool g_framebuffer_resized; + +extern GLFWwindow *g_window; +extern VkSurfaceKHR g_surface; + +extern VkBuffer g_vertex_buffer; +extern VkDeviceMemory g_vertex_buffer_memory; +extern VkBuffer g_index_buffer; +extern VkDeviceMemory g_index_buffer_memory; + +typedef const char *constcharptr; +PTK_ARRAY_DEFINE(constcharptr); + +extern const PTK_ARRAY(constcharptr) g_validation_layers; + +bool vk_init(GLFWwindow *window, const size_t width, const size_t height, const char *title, const PtkVersion version); + +bool vk_is_done(void); + +bool vk_draw_frame(void); + +void vk_finish(void); + +#endif // PTK_PTK_VK_BACKEND_H_ diff --git a/src/ptk_vk/buffer.c b/src/ptk_vk/buffer.c new file mode 100644 index 0000000..0572b70 --- /dev/null +++ b/src/ptk_vk/buffer.c @@ -0,0 +1,130 @@ +// Copyright (jacekpoz 2024). Licensed under the EUPL-1.2 or later. + +#include "ptk_vk/buffer.h" + +#include "ptk_vk/command_pool.h" +#include "ptk_vk/utils.h" + +#include + +PTK_OPTION(uint32_t) vk_find_memory_type(VkPhysicalDevice physical_dev, const uint32_t type_filter, const VkMemoryPropertyFlags props) { + VkPhysicalDeviceMemoryProperties mem_props; + vkGetPhysicalDeviceMemoryProperties(physical_dev, &mem_props); + + for (uint32_t i = 0; i < mem_props.memoryTypeCount; ++i) { + if (type_filter & (1 << i) && (mem_props.memoryTypes[i].propertyFlags & props) == props) { + return PTK_OPTION_SOME(uint32_t, i); + } + } + + return PTK_OPTION_NONE(uint32_t); +} + +PTK_OPTION(BufferStuff) vk_create_buffer(VkDevice dev, VkPhysicalDevice physical_dev, const VkDeviceSize size, const VkBufferUsageFlags usage, const VkMemoryPropertyFlags props) { + BufferStuff ret; + + VK_TRY(PTK_OPTION_NONE(BufferStuff), + vkCreateBuffer( + dev, + &(VkBufferCreateInfo){ + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .pNext = NULL, + .flags = 0, + .size = size, + .usage = usage, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + .queueFamilyIndexCount = 0, + .pQueueFamilyIndices = NULL, + }, + NULL, + &ret.buffer + ) + ); + + VkMemoryRequirements mem_reqs; + vkGetBufferMemoryRequirements(dev, ret.buffer, &mem_reqs); + + const PTK_OPTION(uint32_t) memory_type = vk_find_memory_type(physical_dev, mem_reqs.memoryTypeBits, props); + + if (!memory_type.exists) { + PTK_ERR("failed to find suitable memory type"); + return PTK_OPTION_NONE(BufferStuff); + } + + VK_TRY(PTK_OPTION_NONE(BufferStuff), + vkAllocateMemory( + dev, + &(VkMemoryAllocateInfo){ + .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + .pNext = NULL, + .allocationSize = mem_reqs.size, + .memoryTypeIndex = memory_type.value, + }, + NULL, + &ret.buffer_memory + ) + ); + + vkBindBufferMemory(dev, ret.buffer, ret.buffer_memory, 0); + + return PTK_OPTION_SOME(BufferStuff, ret); +} + +bool copy_buffer(const VkBuffer src, const VkBuffer dst, const VkDeviceSize size) { + PTK_OPTION(VkCommandBuffer) command_buffer_opt = vk_begin_single_time_commands(); + + if (!command_buffer_opt.exists) { + PTK_ERR("failed to create command buffer"); + return false; + } + + VkCommandBuffer command_buffer = command_buffer_opt.value; + + vkCmdCopyBuffer(command_buffer, src, dst, 1, &(VkBufferCopy){ + .srcOffset = 0, + .dstOffset = 0, + .size = size, + }); + + if (!vk_end_single_time_commands(command_buffer)) { + PTK_ERR("failed to end command buffer"); + return false; + } + + return true; +} + +bool transfer_to_buffer(VkDevice dev, VkPhysicalDevice physical_dev, void *src, size_t src_size, VkBuffer buffer) { + const VkDeviceSize buffer_size = src_size; + + PTK_OPTION(BufferStuff) staging_buffer_stuff_opt = vk_create_buffer( + dev, + physical_dev, + buffer_size, + VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT + ); + + if (!staging_buffer_stuff_opt.exists) { + PTK_ERR("failed creating staging vertex buffer"); + return false; + } + + VkBuffer staging_buffer = staging_buffer_stuff_opt.value.buffer; + VkDeviceMemory staging_buffer_memory = staging_buffer_stuff_opt.value.buffer_memory; + + void *data; + vkMapMemory(dev, staging_buffer_memory, 0, buffer_size, 0, &data); + memcpy(data, src, (size_t)buffer_size); + vkUnmapMemory(dev, staging_buffer_memory); + + if (!copy_buffer(staging_buffer, buffer, buffer_size)) { + PTK_ERR("failed copying staging buffer to vertex buffer"); + return false; + } + + vkDestroyBuffer(dev, staging_buffer, NULL); + vkFreeMemory(dev, staging_buffer_memory, NULL); + + return true; +} diff --git a/src/ptk_vk/buffer.h b/src/ptk_vk/buffer.h new file mode 100644 index 0000000..2abb3da --- /dev/null +++ b/src/ptk_vk/buffer.h @@ -0,0 +1,24 @@ +// Copyright (jacekpoz 2024). Licensed under the EUPL-1.2 or later. + +#ifndef PTK_PTK_VK_BUFFER_H_ +#define PTK_PTK_VK_BUFFER_H_ + +#include "ptk_option.h" + +#include + +typedef struct { + VkBuffer buffer; + VkDeviceMemory buffer_memory; +} BufferStuff; + +PTK_OPTION_DEFINE(BufferStuff); +PTK_OPTION_DEFINE(uint32_t); + +PTK_OPTION(uint32_t) vk_find_memory_type(VkPhysicalDevice physical_dev, const uint32_t type_filter, const VkMemoryPropertyFlags props); + +PTK_OPTION(BufferStuff) vk_create_buffer(VkDevice dev, VkPhysicalDevice physical_dev, const VkDeviceSize size, const VkBufferUsageFlags usage, const VkMemoryPropertyFlags props); + +bool transfer_to_buffer(VkDevice dev, VkPhysicalDevice physical_dev, void *src, size_t src_size, VkBuffer buffer); + +#endif // PTK_PTK_VK_BUFFER_H_ diff --git a/src/ptk_vk/command_buffers.c b/src/ptk_vk/command_buffers.c new file mode 100644 index 0000000..51a77ba --- /dev/null +++ b/src/ptk_vk/command_buffers.c @@ -0,0 +1,31 @@ +// Copyright (jacekpoz 2024). Licensed under the EUPL-1.2 or later. + +#include "ptk_vk/command_buffers.h" + +#include "ptk_vk/command_pool.h" +#include "ptk_vk/device.h" +#include "ptk_vk/sync_objects.h" +#include "ptk_vk/utils.h" + +PTK_LIST(VkCommandBuffer) g_command_buffers; + +bool vk_allocate_command_buffers(void) { + g_command_buffers = PTK_LIST_NEW(VkCommandBuffer, g_max_frames_in_flight); + + VK_TRY(false, + vkAllocateCommandBuffers( + g_dev, + &(VkCommandBufferAllocateInfo){ + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + .pNext = NULL, + .commandPool = g_command_pool, + .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, + .commandBufferCount = g_command_buffers.allocated, + }, + g_command_buffers.data + ) + ); + PTK_LIST_FILLED(g_command_buffers); + + return true; +} diff --git a/src/ptk_vk/command_buffers.h b/src/ptk_vk/command_buffers.h new file mode 100644 index 0000000..6b9a61d --- /dev/null +++ b/src/ptk_vk/command_buffers.h @@ -0,0 +1,18 @@ +// Copyright (jacekpoz 2024). Licensed under the EUPL-1.2 or later. + +#ifndef PTK_PTK_VK_COMMAND_BUFFERS_H_ +#define PTK_PTK_VK_COMMAND_BUFFERS_H_ + +#include "ptk_list.h" + +#include + +#include + +PTK_LIST_DEFINE(VkCommandBuffer); + +extern PTK_LIST(VkCommandBuffer) g_command_buffers; + +bool vk_allocate_command_buffers(void); + +#endif // PTK_PTK_VK_COMMAND_BUFFERS_H_ diff --git a/src/ptk_vk/command_pool.c b/src/ptk_vk/command_pool.c new file mode 100644 index 0000000..3503718 --- /dev/null +++ b/src/ptk_vk/command_pool.c @@ -0,0 +1,87 @@ +// Copyright (jacekpoz 2024). Licensed under the EUPL-1.2 or later. + +#include "ptk_vk/command_pool.h" + +#include "ptk_vk/device.h" +#include "ptk_vk/utils.h" + +PTK_OPTION(VkCommandBuffer) vk_begin_single_time_commands(void) { + VkCommandBuffer command_buffer; + + VK_TRY(PTK_OPTION_NONE(VkCommandBuffer), + vkAllocateCommandBuffers( + g_dev, + &(VkCommandBufferAllocateInfo){ + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + .pNext = NULL, + .commandPool = g_command_pool, + .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, + .commandBufferCount = 1, + }, + &command_buffer + ) + ); + + VK_TRY(PTK_OPTION_NONE(VkCommandBuffer), + vkBeginCommandBuffer( + command_buffer, + &(VkCommandBufferBeginInfo){ + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .pNext = NULL, + .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, + .pInheritanceInfo = NULL, + } + ) + ); + + return PTK_OPTION_SOME(VkCommandBuffer, command_buffer); +} + +bool vk_end_single_time_commands(VkCommandBuffer command_buffer) { + VK_TRY(false, vkEndCommandBuffer(command_buffer)); + + VK_TRY(false, + vkQueueSubmit( + g_graphics_queue, + 1, + &(VkSubmitInfo){ + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .pNext = NULL, + .waitSemaphoreCount = 0, + .pWaitSemaphores = NULL, + .pWaitDstStageMask = NULL, + .commandBufferCount = 1, + .pCommandBuffers = &command_buffer, + .signalSemaphoreCount = 0, + .pSignalSemaphores = NULL, + }, + VK_NULL_HANDLE + ); + ); + + VK_TRY(false, vkQueueWaitIdle(g_graphics_queue)); + + vkFreeCommandBuffers(g_dev, g_command_pool, 1, &command_buffer); + + return true; +} + +VkCommandPool g_command_pool; + +bool vk_create_command_pool(void) { + VK_TRY(false, + vkCreateCommandPool( + g_dev, + &(VkCommandPoolCreateInfo){ + .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, + .pNext = NULL, + .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, + .queueFamilyIndex = g_queue_family_indices.graphics.value, + }, + NULL, + &g_command_pool + ) + ); + + return true; +} diff --git a/src/ptk_vk/command_pool.h b/src/ptk_vk/command_pool.h new file mode 100644 index 0000000..da33144 --- /dev/null +++ b/src/ptk_vk/command_pool.h @@ -0,0 +1,22 @@ +// Copyright (jacekpoz 2024). Licensed under the EUPL-1.2 or later. + +#ifndef PTK_PTK_VK_COMMAND_POOL_H_ +#define PTK_PTK_VK_COMMAND_POOL_H_ + +#include "ptk_option.h" + +#include + +#include + +PTK_OPTION_DEFINE(VkCommandBuffer); + +PTK_OPTION(VkCommandBuffer) vk_begin_single_time_commands(void); + +bool vk_end_single_time_commands(VkCommandBuffer command_buffer); + +extern VkCommandPool g_command_pool; + +bool vk_create_command_pool(void); + +#endif // PTK_PTK_VK_COMMAND_POOL_H_ diff --git a/src/ptk_vk/components.c b/src/ptk_vk/components.c index 9513669..22bf753 100644 --- a/src/ptk_vk/components.c +++ b/src/ptk_vk/components.c @@ -1,10 +1,10 @@ // Copyright (jacekpoz 2024). Licensed under the EUPL-1.2 or later. -#include -#include -#include -#include -#include +#include "ptk_vk/components.h" +#include "ptk_vk/init.h" +#include "ptk_vk/image.h" + +#include "ptk.h" #include #include diff --git a/src/ptk_vk/components.h b/src/ptk_vk/components.h index b43a25c..4201f4c 100644 --- a/src/ptk_vk/components.h +++ b/src/ptk_vk/components.h @@ -3,8 +3,8 @@ #ifndef PTK_PTK_VK_COMPONENTS_H_ #define PTK_PTK_VK_COMPONENTS_H_ -#include -#include +#include "ptk.h" +#include "ptk_list.h" typedef struct { PtkPos pos; diff --git a/src/ptk_vk/descriptors.c b/src/ptk_vk/descriptors.c new file mode 100644 index 0000000..266433b --- /dev/null +++ b/src/ptk_vk/descriptors.c @@ -0,0 +1,163 @@ +// Copyright (jacekpoz 2024). Licensed under the EUPL-1.2 or later. + +#include "ptk_vk/descriptors.h" + +#include "ptk_vk/device.h" +#include "ptk_vk/image.h" +#include "ptk_vk/sync_objects.h" +#include "ptk_vk/uniform_buffers.h" +#include "ptk_vk/utils.h" + +#include "ptk_array.h" + +VkDescriptorSetLayout g_descriptor_set_layout; +VkDescriptorPool g_descriptor_pool; +PTK_LIST(VkDescriptorSet) g_descriptor_sets; + +bool vk_create_descriptor_set_layout(void) { + VK_TRY(false, + vkCreateDescriptorSetLayout( + g_dev, + &(VkDescriptorSetLayoutCreateInfo){ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, + .pNext = NULL, + .flags = 0, + .bindingCount = 2, + .pBindings = (VkDescriptorSetLayoutBinding []){ + (VkDescriptorSetLayoutBinding){ + .binding = 0, + .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + .descriptorCount = 1, + .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, + .pImmutableSamplers = NULL, + }, + (VkDescriptorSetLayoutBinding){ + .binding = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .descriptorCount = 1, + .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, + .pImmutableSamplers = NULL, + }, + }, + }, + NULL, + &g_descriptor_set_layout + ) + ); + + return true; +} + +bool vk_create_descriptor_pool(void) { + VK_TRY(false, + vkCreateDescriptorPool( + g_dev, + &(VkDescriptorPoolCreateInfo){ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, + .pNext = NULL, + .flags = 0, + .maxSets = g_max_frames_in_flight, + .poolSizeCount = 2, + .pPoolSizes = (VkDescriptorPoolSize []){ + (VkDescriptorPoolSize){ + .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + .descriptorCount = g_max_frames_in_flight, + }, + (VkDescriptorPoolSize){ + .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .descriptorCount = g_max_frames_in_flight, + }, + }, + }, + NULL, + &g_descriptor_pool + ) + ); + + return true; +} + +PTK_LIST_DEFINE(VkDescriptorSetLayout); + +bool vk_create_descriptor_sets(void) { + PTK_LIST(VkDescriptorSetLayout) layouts = PTK_LIST_NEW(VkDescriptorSetLayout, g_max_frames_in_flight); + for (size_t i = 0; i < g_max_frames_in_flight; ++i) { + PTK_LIST_ADD(VkDescriptorSetLayout, layouts, g_descriptor_set_layout); + } + + g_descriptor_sets = PTK_LIST_NEW(VkDescriptorSet, g_max_frames_in_flight); + + VK_TRY(false, + vkAllocateDescriptorSets( + g_dev, + &(VkDescriptorSetAllocateInfo){ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, + .pNext = NULL, + .descriptorPool = g_descriptor_pool, + .descriptorSetCount = layouts.size, + .pSetLayouts = layouts.data, + }, + g_descriptor_sets.data + ) + ); + + vk_update_descriptor_sets(); + + return true; +} + +PTK_ARRAY_DEFINE(VkWriteDescriptorSet); + +void vk_update_descriptor_sets(void) { + for (size_t i = 0; i < g_max_frames_in_flight; ++i) { + VkWriteDescriptorSet uniform_buffer_descriptor_set = (VkWriteDescriptorSet){ + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .pNext = NULL, + .dstSet = g_descriptor_sets.data[i], + .dstBinding = 0, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + .pImageInfo = NULL, + .pBufferInfo = &(VkDescriptorBufferInfo){ + .buffer = g_uniform_buffers.data[i], + .offset = 0, + .range = sizeof(UniformBufferObject), + }, + .pTexelBufferView = NULL, + }; + + PTK_ARRAY(VkWriteDescriptorSet) descriptor_writes; + if (g_image_view == VK_NULL_HANDLE) { + descriptor_writes = PTK_ARRAY_NEW(VkWriteDescriptorSet, {uniform_buffer_descriptor_set}); + } else { + descriptor_writes = PTK_ARRAY_NEW(VkWriteDescriptorSet, { + uniform_buffer_descriptor_set, + (VkWriteDescriptorSet){ + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .pNext = NULL, + .dstSet = g_descriptor_sets.data[i], + .dstBinding = 1, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .pImageInfo = &(VkDescriptorImageInfo){ + .sampler = g_sampler, + .imageView = g_image_view, + .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + }, + .pBufferInfo = NULL, + .pTexelBufferView = NULL, + }, + }); + } + + vkUpdateDescriptorSets( + g_dev, + descriptor_writes.size, + descriptor_writes.data, + 0, + NULL + ); + } +} diff --git a/src/ptk_vk/descriptors.h b/src/ptk_vk/descriptors.h new file mode 100644 index 0000000..6d645ef --- /dev/null +++ b/src/ptk_vk/descriptors.h @@ -0,0 +1,22 @@ +// Copyright (jacekpoz 2024). Licensed under the EUPL-1.2 or later. + +#ifndef PTK_PTK_VK_DESCRIPTORS_H_ +#define PTK_PTK_VK_DESCRIPTORS_H_ + +#include "ptk_list.h" + +#include + +PTK_LIST_DEFINE(VkDescriptorSet); + +extern VkDescriptorSetLayout g_descriptor_set_layout; +extern VkDescriptorPool g_descriptor_pool; +extern PTK_LIST(VkDescriptorSet) g_descriptor_sets; + +bool vk_create_descriptor_set_layout(void); +bool vk_create_descriptor_pool(void); +bool vk_create_descriptor_sets(void); + +void vk_update_descriptor_sets(void); + +#endif // PTK_PTK_VK_DESCRIPTORS_H_ diff --git a/src/ptk_vk/device.c b/src/ptk_vk/device.c new file mode 100644 index 0000000..c53e894 --- /dev/null +++ b/src/ptk_vk/device.c @@ -0,0 +1,140 @@ +// Copyright (jacekpoz 2024). Licensed under the EUPL-1.2 or later. + +#include "ptk_vk/device.h" + +#include "ptk_vk/backend.h" +#include "ptk_vk/physical_device.h" +#include "ptk_vk/swapchain.h" +#include "ptk_vk/utils.h" + +#include "ptk_array.h" +#include "ptk_list.h" +#include "ptk_log.h" +#include "ptk_option.h" + +#include +#include + +VkDevice g_dev; +QueueFamilyIndices g_queue_family_indices; +VkQueue g_graphics_queue; +VkQueue g_present_queue; + +static const size_t m_queue_family_count = sizeof(QueueFamilyIndices) / sizeof(PTK_OPTION(uint32_t)); + +PTK_LIST_DEFINE(VkQueueFamilyProperties); + +static const PTK_ARRAY(constcharptr) m_device_extensions = PTK_ARRAY_NEW(constcharptr, { + VK_KHR_SWAPCHAIN_EXTENSION_NAME +}); + +PTK_LIST_DEFINE(VkExtensionProperties); + +bool are_extensions_supported(const VkPhysicalDevice physical_dev) { + PTK_LIST(VkExtensionProperties) available_extensions; + vkEnumerateDeviceExtensionProperties(physical_dev, NULL, &available_extensions.allocated, NULL); + + available_extensions = PTK_LIST_NEW(VkExtensionProperties, available_extensions.allocated); + vkEnumerateDeviceExtensionProperties(physical_dev, NULL, &available_extensions.allocated, available_extensions.data); + PTK_LIST_FILLED(available_extensions); + + size_t supported_extensions = 0; + + for (size_t i = 0; i < m_device_extensions.size; ++i) { + PTK_LIST_FOR_EACH(const VkExtensionProperties, available_extensions, current_extension, { + if (strcmp(m_device_extensions.data[i], current_extension.extensionName) == 0) { + supported_extensions += 1; + break; + } + }) + } + + return supported_extensions == m_device_extensions.size; +} + +static bool is_device_suitable(const VkPhysicalDevice physical_dev, const VkSurfaceKHR surface) { + PTK_LIST(VkQueueFamilyProperties) queue_families; + vkGetPhysicalDeviceQueueFamilyProperties(physical_dev, &queue_families.allocated, NULL); + + queue_families = PTK_LIST_NEW(VkQueueFamilyProperties, queue_families.allocated); + vkGetPhysicalDeviceQueueFamilyProperties(physical_dev, &queue_families.allocated, queue_families.data); + PTK_LIST_FILLED(queue_families); + + g_queue_family_indices.graphics = PTK_OPTION_NONE(uint32_t); + g_queue_family_indices.present = PTK_OPTION_NONE(uint32_t); + PTK_LIST_FOR_EACH_E(const VkQueueFamilyProperties, queue_families, i, queue_family, { + if (queue_family.queueFlags & VK_QUEUE_GRAPHICS_BIT) { + g_queue_family_indices.graphics = PTK_OPTION_SOME(uint32_t, i); + } + + VkBool32 present_support = VK_FALSE; + vkGetPhysicalDeviceSurfaceSupportKHR(physical_dev, i, surface, &present_support); + if (present_support) { + g_queue_family_indices.present = PTK_OPTION_SOME(uint32_t, i); + } + }) + + const bool indices_found = g_queue_family_indices.graphics.exists + && g_queue_family_indices.present.exists; + const bool extensions_supported = are_extensions_supported(physical_dev); + bool swapchain_adequate = false; + + if (extensions_supported) { + const SwapchainSupportInfo swapchain_support = query_swapchain_support(physical_dev, surface); + swapchain_adequate = swapchain_support.formats.size != 0 + && swapchain_support.present_modes.size != 0; + PTK_LIST_FREE(swapchain_support.formats); + PTK_LIST_FREE(swapchain_support.present_modes); + } + + PTK_LIST_FREE(queue_families); + + return indices_found && extensions_supported && swapchain_adequate; +} + +bool vk_create_logical_dev(void) { + if (!is_device_suitable(g_physical_dev, g_surface)) { + PTK_ERR("physical device isn't suitable"); + return false; + } + + VkDeviceQueueCreateInfo queue_create_infos[m_queue_family_count]; + + for (size_t i = 0; i < m_queue_family_count; ++i) { + const PTK_OPTION(uint32_t) index = *(((PTK_OPTION(uint32_t) *)&g_queue_family_indices) + i); + + queue_create_infos[i] = (VkDeviceQueueCreateInfo){ + .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, + .pNext = NULL, + .flags = 0, + .queueFamilyIndex = index.value, + .queueCount = 1, + .pQueuePriorities = &(float){1.0f}, + }; + } + + VK_TRY(false, + vkCreateDevice( + g_physical_dev, + &(VkDeviceCreateInfo){ + .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, + .pNext = NULL, + .flags = 0, + .queueCreateInfoCount = m_queue_family_count, + .pQueueCreateInfos = queue_create_infos, + .enabledLayerCount = g_validation_layers.size, + .ppEnabledLayerNames = g_validation_layers.data, + .enabledExtensionCount = m_device_extensions.size, + .ppEnabledExtensionNames = m_device_extensions.data, + .pEnabledFeatures = &(VkPhysicalDeviceFeatures){0}, + }, + NULL, + &g_dev + ) + ); + + vkGetDeviceQueue(g_dev, g_queue_family_indices.graphics.value, 0, &g_graphics_queue); + vkGetDeviceQueue(g_dev, g_queue_family_indices.present.value, 0, &g_present_queue); + + return true; +} diff --git a/src/ptk_vk/device.h b/src/ptk_vk/device.h new file mode 100644 index 0000000..3ea5165 --- /dev/null +++ b/src/ptk_vk/device.h @@ -0,0 +1,25 @@ +// Copyright (jacekpoz 2024). Licensed under the EUPL-1.2 or later. + +#ifndef PTK_PTK_VK_DEVICE_H_ +#define PTK_PTK_VK_DEVICE_H_ + +// PTK_OPTION(uint32_t) +#include "ptk_vk/buffer.h" + +#include "ptk_option.h" + +#include + +typedef struct { + PTK_OPTION(uint32_t) graphics; + PTK_OPTION(uint32_t) present; +} QueueFamilyIndices; + +extern VkDevice g_dev; +extern QueueFamilyIndices g_queue_family_indices; +extern VkQueue g_graphics_queue; +extern VkQueue g_present_queue; + +bool vk_create_logical_dev(void); + +#endif // PTK_PTK_VK_DEVICE_H_ diff --git a/src/ptk_vk/draw.c b/src/ptk_vk/draw.c deleted file mode 100644 index c58d75d..0000000 --- a/src/ptk_vk/draw.c +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright (jacekpoz 2024). Licensed under the EUPL-1.2 or later. - -#include - -#include -#include - -#include - -#include - -bool g_framebuffer_resized = false; -uint32_t g_current_frame = 0; - -bool vk_draw_frame(void) { - vkWaitForFences(g_dev, 1, &g_in_flight_fences.data[g_current_frame], VK_TRUE, UINT64_MAX); - - uint32_t image_index; - const VkResult acquire_next_image_result = vkAcquireNextImageKHR( - g_dev, - g_swapchain, - UINT64_MAX, - g_image_available_semaphores.data[g_current_frame], - VK_NULL_HANDLE, - &image_index - ); - - if (acquire_next_image_result == VK_ERROR_OUT_OF_DATE_KHR) { - if (!vk_recreate_swapchain()) { - return false; - } - return true; - } else if (acquire_next_image_result != VK_SUCCESS && acquire_next_image_result != VK_SUBOPTIMAL_KHR) { - PTK_ERR("%s", vk_result_string(acquire_next_image_result)); - return false; - } - - vk_update_uniform_buffer(g_current_frame); - - vkResetFences(g_dev, 1, &g_in_flight_fences.data[g_current_frame]); - - vkResetCommandBuffer(g_command_buffers.data[g_current_frame], 0); - if (!vk_record_command_buffer(g_command_buffers.data[g_current_frame], image_index)) { - PTK_ERR("failed recording command buffer"); - return false; - } - - const VkSemaphore signal_semaphores[] = {g_render_finished_semaphores.data[g_current_frame]}; - - VK_TRY(false, - vkQueueSubmit( - g_graphics_queue, - 1, - &(VkSubmitInfo){ - .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, - .pNext = NULL, - .waitSemaphoreCount = 1, - .pWaitSemaphores = &g_image_available_semaphores.data[g_current_frame], - .pWaitDstStageMask = &(VkPipelineStageFlags){VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}, - .commandBufferCount = 1, - .pCommandBuffers = &g_command_buffers.data[g_current_frame], - .signalSemaphoreCount = 1, - .pSignalSemaphores = signal_semaphores, - }, - g_in_flight_fences.data[g_current_frame] - ) - ); - - const VkResult queue_present_result = vkQueuePresentKHR(g_present_queue, &(VkPresentInfoKHR){ - .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, - .pNext = NULL, - .waitSemaphoreCount = 1, - .pWaitSemaphores = signal_semaphores, - .swapchainCount = 1, - .pSwapchains = &g_swapchain, - .pImageIndices = &image_index, - .pResults = NULL, - }); - - if ( - queue_present_result == VK_ERROR_OUT_OF_DATE_KHR || - queue_present_result == VK_SUBOPTIMAL_KHR || - g_framebuffer_resized - ) { - g_framebuffer_resized = false; - if (!vk_recreate_swapchain()) { - return false; - } - PTK_TRACE("recreated swapchain"); - } else if (queue_present_result != VK_SUCCESS) { - PTK_ERR("%s", vk_result_string(queue_present_result)); - return false; - } - - g_current_frame = (g_current_frame + 1) % g_max_frames_in_flight; - - return true; -} diff --git a/src/ptk_vk/draw.h b/src/ptk_vk/draw.h deleted file mode 100644 index 2fcc99a..0000000 --- a/src/ptk_vk/draw.h +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (jacekpoz 2024). Licensed under the EUPL-1.2 or later. - -#ifndef PTK_PTK_VK_DRAW_H_ -#define PTK_PTK_VK_DRAW_H_ - -#include -#include - -extern bool g_framebuffer_resized; -extern uint32_t g_current_frame; - -bool vk_draw_frame(void); - -#endif // PTK_PTK_VK_DRAW_H_ diff --git a/src/ptk_vk/image.c b/src/ptk_vk/image.c index b12ea96..e6063d4 100644 --- a/src/ptk_vk/image.c +++ b/src/ptk_vk/image.c @@ -1,18 +1,23 @@ // Copyright (jacekpoz 2024). Licensed under the EUPL-1.2 or later. -#include +#include "ptk_vk/image.h" -#include -#include +#include "ptk_vk/buffer.h" +#include "ptk_vk/command_pool.h" +#include "ptk_vk/descriptors.h" +#include "ptk_vk/device.h" +#include "ptk_vk/physical_device.h" +// PTK_LIST(VkDeviceMemory) +#include "ptk_vk/uniform_buffers.h" +#include "ptk_vk/utils.h" -#include -#include +#include "ptk_log.h" +#include "ptk_option.h" #define STB_IMAGE_IMPLEMENTATION #include PTK_LIST_DEFINE(VkImage); -PTK_LIST_DEFINE(VkDeviceMemory); const size_t g_max_images = 64; @@ -56,7 +61,7 @@ bool create_image(uint32_t width, uint32_t height, VkFormat format, VkImageTilin VkMemoryRequirements mem_reqs; vkGetImageMemoryRequirements(g_dev, *image, &mem_reqs); - PTK_OPTION(uint32_t) mem_type = vk_find_memory_type(mem_reqs.memoryTypeBits, props); + PTK_OPTION(uint32_t) mem_type = vk_find_memory_type(g_physical_dev, mem_reqs.memoryTypeBits, props); if (!mem_type.exists) { PTK_ERR("failed to find suitable memory type"); @@ -91,13 +96,15 @@ bool create_image(uint32_t width, uint32_t height, VkFormat format, VkImageTilin bool transition_image_layout(VkImage image, VkFormat format, VkImageLayout old_layout, VkImageLayout new_layout) { (void)format; - PTK_OPTION(VkCommandBuffer) command_buffer = vk_begin_single_time_commands(); + PTK_OPTION(VkCommandBuffer) command_buffer_opt = vk_begin_single_time_commands(); - if (!command_buffer.exists) { + if (!command_buffer_opt.exists) { PTK_ERR("failed to create command buffer"); return false; } + VkCommandBuffer command_buffer = command_buffer_opt.value; + VkAccessFlags source_access_mask; VkAccessFlags destination_access_mask; VkPipelineStageFlags source_stage; @@ -127,7 +134,7 @@ bool transition_image_layout(VkImage image, VkFormat format, VkImageLayout old_l } vkCmdPipelineBarrier( - command_buffer.value, + command_buffer, source_stage, destination_stage, 0, @@ -156,7 +163,7 @@ bool transition_image_layout(VkImage image, VkFormat format, VkImageLayout old_l } ); - if (!vk_end_single_time_commands(command_buffer.value)) { + if (!vk_end_single_time_commands(command_buffer)) { PTK_ERR("failed to end command buffer"); return false; } @@ -165,15 +172,17 @@ bool transition_image_layout(VkImage image, VkFormat format, VkImageLayout old_l } bool copy_buffer_to_image(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height) { - PTK_OPTION(VkCommandBuffer) command_buffer = vk_begin_single_time_commands(); + PTK_OPTION(VkCommandBuffer) command_buffer_opt = vk_begin_single_time_commands(); - if (!command_buffer.exists) { + if (!command_buffer_opt.exists) { PTK_ERR("failed to create command buffer"); return false; } + VkCommandBuffer command_buffer = command_buffer_opt.value; + vkCmdCopyBufferToImage( - command_buffer.value, + command_buffer, buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, @@ -201,7 +210,7 @@ bool copy_buffer_to_image(VkBuffer buffer, VkImage image, uint32_t width, uint32 } ); - if (!vk_end_single_time_commands(command_buffer.value)) { + if (!vk_end_single_time_commands(command_buffer)) { PTK_ERR("failed to end command buffer"); return false; } @@ -292,20 +301,22 @@ bool vk_create_image(const char *path) { VkBuffer staging_buffer; VkDeviceMemory staging_buffer_memory; + PTK_OPTION(BufferStuff) staging_buffer_stuff_opt = vk_create_buffer( + g_dev, + g_physical_dev, + image_size, + VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT + ); - if ( - !vk_create_buffer( - image_size, - VK_BUFFER_USAGE_TRANSFER_SRC_BIT, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, - &staging_buffer, - &staging_buffer_memory - ) - ) { + if (!staging_buffer_stuff_opt.exists) { PTK_ERR("failed creating staging image buffer"); return false; } + staging_buffer = staging_buffer_stuff_opt.value.buffer; + staging_buffer_memory = staging_buffer_stuff_opt.value.buffer_memory; + void *data; vkMapMemory(g_dev, staging_buffer_memory, 0, image_size, 0, &data); memcpy(data, pixels, image_size); diff --git a/src/ptk_vk/image.h b/src/ptk_vk/image.h index 171c460..ccd1ca1 100644 --- a/src/ptk_vk/image.h +++ b/src/ptk_vk/image.h @@ -3,15 +3,11 @@ #ifndef PTK_PTK_VK_IMAGE_H_ #define PTK_PTK_VK_IMAGE_H_ +#include + #include #include -#include -#ifndef GLFW_INCLUDE_VULKAN -#define GLFW_INCLUDE_VULKAN -#endif -#include - extern const size_t g_max_images; extern size_t g_image_count; diff --git a/src/ptk_vk/init.c b/src/ptk_vk/init.c index 2e019ca..be6635f 100644 --- a/src/ptk_vk/init.c +++ b/src/ptk_vk/init.c @@ -1,1205 +1,27 @@ // Copyright (jacekpoz 2024). Licensed under the EUPL-1.2 or later. -#include +#include "ptk_vk/init.h" -#include -#include -#include -#include +#include "ptk_vk/backend.h" +#include "ptk_vk/buffer.h" +#include "ptk_vk/command_pool.h" +#include "ptk_vk/components.h" +#include "ptk_vk/descriptors.h" +#include "ptk_vk/device.h" +#include "ptk_vk/image.h" +#include "ptk_vk/instance.h" +#include "ptk_vk/physical_device.h" +#include "ptk_vk/pipeline.h" +#include "ptk_vk/render_pass.h" +#include "ptk_vk/swapchain.h" +#include "ptk_vk/sync_objects.h" + +#include "ptk_list.h" +#include "ptk_log.h" -#include -#include -#include -#include #include -#include -#include - -PTK_LIST_DEFINE(VkImage); -PTK_LIST_DEFINE(VkSurfaceFormatKHR); -PTK_LIST_DEFINE(VkPresentModeKHR); -PTK_LIST_DEFINE(VkFramebuffer); -PTK_ARRAY_DEFINE(VkVertexInputAttributeDescription); -PTK_LIST_DEFINE(VkExtensionProperties); -PTK_LIST_DEFINE(VkQueueFamilyProperties); -PTK_OPTION_DEFINE(VkShaderModule); -PTK_LIST_DEFINE(VkBuffer); -PTK_LIST_DEFINE(VkDeviceMemory); -typedef void *voidptr; -PTK_LIST_DEFINE(voidptr); -PTK_LIST_DEFINE(VkDescriptorSet); -typedef const char *constcharptr; -PTK_ARRAY_DEFINE(constcharptr); -PTK_LIST_DEFINE(constcharptr); -PTK_LIST_DEFINE(VkImageView); - -#ifdef DEBUG -PTK_LIST_DEFINE(VkLayerProperties); -#endif - -typedef struct { - VkSurfaceCapabilitiesKHR capabilities; - PTK_LIST(VkSurfaceFormatKHR) formats; - PTK_LIST(VkPresentModeKHR) present_modes; -} SwapchainSupportInfo; - -static GLFWwindow *m_window = NULL; - -static VkInstance m_instance = VK_NULL_HANDLE; -VkPhysicalDevice g_physical_dev = VK_NULL_HANDLE; -VkDevice g_dev = VK_NULL_HANDLE; - -static VkSurfaceKHR m_surface = VK_NULL_HANDLE; - -VkSwapchainKHR g_swapchain = VK_NULL_HANDLE; -static VkFormat m_swapchain_image_format; -static VkExtent2D m_swapchain_extent; - -static PTK_LIST(VkImage) m_swapchain_images; - -static PTK_LIST(VkImageView) m_swapchain_image_views; - -static struct { - PTK_OPTION(uint32_t) graphics; - PTK_OPTION(uint32_t) present; -} m_queue_family_indices = {0}; -static const size_t m_queue_family_count = sizeof(m_queue_family_indices) / sizeof(PTK_OPTION(uint32_t)); - -VkQueue g_graphics_queue = VK_NULL_HANDLE; -VkQueue g_present_queue = VK_NULL_HANDLE; - -static const PTK_ARRAY(constcharptr) m_device_extensions = PTK_ARRAY_NEW(constcharptr, { - VK_KHR_SWAPCHAIN_EXTENSION_NAME -}); - -static VkRenderPass m_render_pass; -static VkDescriptorSetLayout m_descriptor_set_layout; -static VkPipelineLayout m_pipeline_layout; -static VkPipeline m_pipeline; - -static PTK_LIST(VkFramebuffer) m_swapchain_framebuffers; - -const size_t g_max_frames_in_flight = 2; - -static VkCommandPool m_command_pool; -PTK_LIST(VkCommandBuffer) g_command_buffers; - -PTK_LIST(VkSemaphore) g_image_available_semaphores; -PTK_LIST(VkSemaphore) g_render_finished_semaphores; -PTK_LIST(VkFence) g_in_flight_fences; - -static const VkVertexInputBindingDescription m_vertex_binding_description = { - .binding = 0, - .stride = sizeof(Vertex), - .inputRate = VK_VERTEX_INPUT_RATE_VERTEX, -}; - -static PTK_ARRAY(VkVertexInputAttributeDescription) m_vertex_attribute_descriptions = PTK_ARRAY_NEW(VkVertexInputAttributeDescription, { - (VkVertexInputAttributeDescription){ - .location = 0, - .binding = 0, - .format = VK_FORMAT_R32G32_SFLOAT, - .offset = offsetof(Vertex, pos), - }, - (VkVertexInputAttributeDescription){ - .location = 1, - .binding = 0, - .format = VK_FORMAT_R32G32B32_SFLOAT, - .offset = offsetof(Vertex, color), - }, - (VkVertexInputAttributeDescription){ - .location = 2, - .binding = 0, - .format = VK_FORMAT_R32_SINT, - .offset = offsetof(Vertex, shape_type), - }, - (VkVertexInputAttributeDescription){ - .location = 3, - .binding = 0, - .format = VK_FORMAT_R32G32_SFLOAT, - .offset = offsetof(Vertex, uv), - }, -}); - -static VkBuffer m_vertex_buffer; -static VkDeviceMemory m_vertex_buffer_memory; -static VkBuffer m_index_buffer; -static VkDeviceMemory m_index_buffer_memory; - -typedef struct { - PtkSize initial_window_size; - PtkSize window_size; -} UniformBufferObject; - -static UniformBufferObject m_uniform_buffer_object; - -static PTK_LIST(VkBuffer) m_uniform_buffers; -static PTK_LIST(VkDeviceMemory) m_uniform_buffers_memory; -static PTK_LIST(voidptr) m_uniform_buffers_mapped; - -static VkDescriptorPool m_descriptor_pool; -static PTK_LIST(VkDescriptorSet) m_descriptor_sets; - -#ifdef DEBUG -static const PTK_ARRAY(constcharptr) m_validation_layers = PTK_ARRAY_NEW(constcharptr, { - "VK_LAYER_KHRONOS_validation" -}); - -bool check_validation_layers(const PTK_ARRAY(constcharptr) validation_layers) { - PTK_LIST(VkLayerProperties) available_layers; - - vkEnumerateInstanceLayerProperties(&available_layers.allocated, NULL); - - available_layers = PTK_LIST_NEW(VkLayerProperties, available_layers.allocated); - vkEnumerateInstanceLayerProperties(&available_layers.allocated, available_layers.data); - PTK_LIST_FILLED(available_layers); - - for (size_t i = 0; i < validation_layers.size; ++i) { - const char *layer_name = validation_layers.data[i]; - - bool layer_found = false; - - PTK_LIST_FOR_EACH(const VkLayerProperties, available_layers, layer_properties, { - if (strcmp(layer_name, layer_properties.layerName) == 0) { - layer_found = true; - break; - } - }) - - if (!layer_found) { - return false; - } - } - - return true; -} -#else -static const PTK_ARRAY(constcharptr) m_validation_layers = PTK_ARRAY_EMPTY(constcharptr); -#endif - -bool create_vk_instance(const char *title, const PtkVersion version) { -#ifdef DEBUG - if (!check_validation_layers(m_validation_layers)) { - PTK_ERR("couldn't find requested validation layer"); - return false; - } -#endif - - PTK_ARRAY(constcharptr) extension_names = PTK_ARRAY_EMPTY(constcharptr); - extension_names.data = glfwGetRequiredInstanceExtensions(&extension_names.size); - - VK_TRY(false, - vkCreateInstance( - &(VkInstanceCreateInfo){ - .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, - .pNext = NULL, - .flags = 0, - .pApplicationInfo = &(VkApplicationInfo){ - .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, - .pNext = NULL, - .pApplicationName = title, - .applicationVersion = VK_MAKE_API_VERSION(0, version.major, version.minor, version.patch), - .pEngineName = PTK_ENGINE_NAME, - .engineVersion = VK_MAKE_API_VERSION(0, PTK_VERSION_MAJOR, PTK_VERSION_MINOR, PTK_VERSION_PATCH), - .apiVersion = VK_API_VERSION_1_3, - }, - .enabledLayerCount = m_validation_layers.size, - .ppEnabledLayerNames = m_validation_layers.data, - .enabledExtensionCount = extension_names.size, - .ppEnabledExtensionNames = extension_names.data, - }, - NULL, - &m_instance - ) - ); - - return true; -} - -bool select_physical_dev(void) { - uint32_t dev_count = 0; - vkEnumeratePhysicalDevices(m_instance, &dev_count, NULL); - if (dev_count == 0) { - PTK_ERR("failed to find GPU with vulkan support"); - return false; - } - VkPhysicalDevice devs[dev_count]; - vkEnumeratePhysicalDevices(m_instance, &dev_count, devs); - - VkPhysicalDeviceProperties dev_props[dev_count]; - uint32_t dgpus[dev_count]; - uint32_t dgpu_count = 0; - uint32_t igpus[dev_count]; - uint32_t igpu_count = 0; - - VkPhysicalDeviceMemoryProperties dev_mem_props[dev_count]; - uint32_t dev_mem_counts[dev_count]; - VkDeviceSize dev_mem_totals[dev_count]; - - for (size_t i = 0; i < dev_count; ++i) { - vkGetPhysicalDeviceProperties(devs[i], &dev_props[i]); - - if (dev_props[i].deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) { - dgpus[dgpu_count] = i; - dgpu_count += 1; - } else if (dev_props[i].deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU) { - igpus[igpu_count] = i; - igpu_count += 1; - } - - vkGetPhysicalDeviceMemoryProperties(devs[i], &dev_mem_props[i]); - - dev_mem_counts[i] = dev_mem_props[i].memoryHeapCount; - dev_mem_totals[i] = 0; - - for (size_t j = 0; j < dev_mem_counts[i]; ++j) { - dev_mem_totals[i] += dev_mem_props[i].memoryHeaps[j].size; - } - } - - VkDeviceSize max_mem_size = 0; - size_t dev_best_index = 0; - - if (dgpu_count != 0) { - for (size_t i = 0; i < dgpu_count; ++i) { - if (dev_mem_totals[i] > max_mem_size) { - dev_best_index = dgpus[i]; - max_mem_size = dev_mem_totals[i]; - } - } - } else if (igpu_count != 0) { - for (size_t i = 0; i < igpu_count; ++i) { - if (dev_mem_totals[i] > max_mem_size) { - dev_best_index = igpus[i]; - max_mem_size = dev_mem_totals[i]; - } - } - } - - PTK_DEBUG("best device index: %ld", dev_best_index); - PTK_DEBUG("device name: %s", dev_props[dev_best_index].deviceName); - - PTK_DEBUG("device type: %s", (dgpu_count != 0 ? "dgpu" : (igpu_count != 0 ? "igpu" : "probably cpu"))); - - PTK_DEBUG("total memory: %lu", dev_mem_totals[dev_best_index]); - - g_physical_dev = devs[dev_best_index]; - - return true; -} - -VkSurfaceFormatKHR select_swap_surface_format(const PTK_LIST(VkSurfaceFormatKHR) available_formats) { - PTK_LIST_FOR_EACH(const VkSurfaceFormatKHR, available_formats, current_format, { - if (current_format.format == VK_FORMAT_B8G8R8A8_SRGB && current_format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { - return current_format; - } - }) - - return available_formats.data[0]; -} - -VkPresentModeKHR select_swap_present_mode(const PTK_LIST(VkPresentModeKHR) available_present_modes) { - PTK_LIST_FOR_EACH(const VkPresentModeKHR, available_present_modes, current_present_mode, { - if (current_present_mode == VK_PRESENT_MODE_MAILBOX_KHR) { - return current_present_mode; - } - }) - - return VK_PRESENT_MODE_FIFO_KHR; -} - -VkExtent2D select_swap_extent(const VkSurfaceCapabilitiesKHR capabilities) { - if (capabilities.currentExtent.width != UINT32_MAX) { - return capabilities.currentExtent; - } - - int width, height; - - glfwGetFramebufferSize(m_window, &width, &height); - - VkExtent2D actual_extent = { - .width = (uint32_t)width, - .height = (uint32_t)height, - }; - - if (actual_extent.width < capabilities.minImageExtent.width) { - actual_extent.width = capabilities.minImageExtent.width; - } else if (actual_extent.width > capabilities.maxImageExtent.width) { - actual_extent.width = capabilities.maxImageExtent.width; - } - - if (actual_extent.height < capabilities.minImageExtent.height) { - actual_extent.height = capabilities.minImageExtent.height; - } else if (actual_extent.height > capabilities.maxImageExtent.height) { - actual_extent.height = capabilities.maxImageExtent.height; - } - - return actual_extent; -} - -SwapchainSupportInfo query_swapchain_support(const VkPhysicalDevice dev) { - SwapchainSupportInfo info; - - vkGetPhysicalDeviceSurfaceCapabilitiesKHR(dev, m_surface, &info.capabilities); - - const uint32_t width = info.capabilities.currentExtent.width; - const uint32_t height = info.capabilities.currentExtent.height; - - if ( - info.capabilities.currentTransform & VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR - || info.capabilities.currentTransform & VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR - ) { - info.capabilities.currentExtent.height = width; - info.capabilities.currentExtent.width = height; - } - - vkGetPhysicalDeviceSurfaceFormatsKHR(dev, m_surface, &info.formats.allocated, NULL); - - if (info.formats.allocated != 0) { - info.formats = PTK_LIST_NEW(VkSurfaceFormatKHR, info.formats.allocated); - vkGetPhysicalDeviceSurfaceFormatsKHR(dev, m_surface, &info.formats.allocated, info.formats.data); - info.formats.size = info.formats.allocated; - } - - vkGetPhysicalDeviceSurfacePresentModesKHR(dev, m_surface, &info.present_modes.allocated, NULL); - - if (info.present_modes.allocated != 0) { - info.present_modes = PTK_LIST_NEW(VkPresentModeKHR, info.present_modes.allocated); - vkGetPhysicalDeviceSurfacePresentModesKHR(dev, m_surface, &info.present_modes.allocated, info.present_modes.data); - info.present_modes.size = info.present_modes.allocated; - } - - return info; -} - -bool are_extensions_supported(const VkPhysicalDevice dev) { - PTK_LIST(VkExtensionProperties) available_extensions; - vkEnumerateDeviceExtensionProperties(dev, NULL, &available_extensions.allocated, NULL); - - available_extensions = PTK_LIST_NEW(VkExtensionProperties, available_extensions.allocated); - vkEnumerateDeviceExtensionProperties(dev, NULL, &available_extensions.allocated, available_extensions.data); - PTK_LIST_FILLED(available_extensions); - - size_t supported_extensions = 0; - - for (size_t i = 0; i < m_device_extensions.size; ++i) { - PTK_LIST_FOR_EACH(const VkExtensionProperties, available_extensions, current_extension, { - if (strcmp(m_device_extensions.data[i], current_extension.extensionName) == 0) { - supported_extensions += 1; - break; - } - }) - } - - return supported_extensions == m_device_extensions.size; -} - -bool is_device_suitable(const VkPhysicalDevice dev) { - PTK_LIST(VkQueueFamilyProperties) queue_families; - vkGetPhysicalDeviceQueueFamilyProperties(dev, &queue_families.allocated, NULL); - - queue_families = PTK_LIST_NEW(VkQueueFamilyProperties, queue_families.allocated); - vkGetPhysicalDeviceQueueFamilyProperties(dev, &queue_families.allocated, queue_families.data); - PTK_LIST_FILLED(queue_families); - - m_queue_family_indices.graphics = PTK_OPTION_NONE(uint32_t); - m_queue_family_indices.present = PTK_OPTION_NONE(uint32_t); - PTK_LIST_FOR_EACH_E(const VkQueueFamilyProperties, queue_families, i, queue_family, { - if (queue_family.queueFlags & VK_QUEUE_GRAPHICS_BIT) { - m_queue_family_indices.graphics = PTK_OPTION_SOME(uint32_t, i); - } - - VkBool32 present_support = VK_FALSE; - vkGetPhysicalDeviceSurfaceSupportKHR(dev, i, m_surface, &present_support); - if (present_support) { - m_queue_family_indices.present = PTK_OPTION_SOME(uint32_t, i); - } - }) - - const bool indices_found = m_queue_family_indices.graphics.exists - && m_queue_family_indices.present.exists; - const bool extensions_supported = are_extensions_supported(dev); - bool swapchain_adequate = false; - - if (extensions_supported) { - const SwapchainSupportInfo swapchain_support = query_swapchain_support(dev); - swapchain_adequate = swapchain_support.formats.size != 0 - && swapchain_support.present_modes.size != 0; - PTK_LIST_FREE(swapchain_support.formats); - PTK_LIST_FREE(swapchain_support.present_modes); - } - - PTK_LIST_FREE(queue_families); - - VkPhysicalDeviceFeatures supported_features; - vkGetPhysicalDeviceFeatures(dev, &supported_features); - - return indices_found && extensions_supported && swapchain_adequate && supported_features.samplerAnisotropy; -} - -bool create_logical_dev(void) { - if (!is_device_suitable(g_physical_dev)) { - PTK_ERR("physical device isn't suitable"); - return false; - } - - VkDeviceQueueCreateInfo queue_create_infos[m_queue_family_count]; - - for (size_t i = 0; i < m_queue_family_count; ++i) { - const PTK_OPTION(uint32_t) index = *(((PTK_OPTION(uint32_t) *)&m_queue_family_indices) + i); - - queue_create_infos[i] = (VkDeviceQueueCreateInfo){ - .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, - .pNext = NULL, - .flags = 0, - .queueFamilyIndex = index.value, - .queueCount = 1, - .pQueuePriorities = &(float){1.0f}, - }; - } - - VK_TRY(false, - vkCreateDevice( - g_physical_dev, - &(VkDeviceCreateInfo){ - .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, - .pNext = NULL, - .flags = 0, - .queueCreateInfoCount = m_queue_family_count, - .pQueueCreateInfos = queue_create_infos, - .enabledLayerCount = m_validation_layers.size, - .ppEnabledLayerNames = m_validation_layers.data, - .enabledExtensionCount = m_device_extensions.size, - .ppEnabledExtensionNames = m_device_extensions.data, - .pEnabledFeatures = &(VkPhysicalDeviceFeatures){ - .samplerAnisotropy = VK_TRUE, - }, - }, - NULL, - &g_dev - ) - ); - - vkGetDeviceQueue(g_dev, m_queue_family_indices.graphics.value, 0, &g_graphics_queue); - vkGetDeviceQueue(g_dev, m_queue_family_indices.present.value, 0, &g_present_queue); - - return true; -} - -bool create_swapchain(void) { - const SwapchainSupportInfo swapchain_support = query_swapchain_support(g_physical_dev); - - const VkSurfaceFormatKHR surface_format = select_swap_surface_format(swapchain_support.formats); - const VkPresentModeKHR present_mode = select_swap_present_mode(swapchain_support.present_modes); - const VkExtent2D extent = select_swap_extent(swapchain_support.capabilities); - - uint32_t image_count = swapchain_support.capabilities.minImageCount + 1; - - if (swapchain_support.capabilities.maxImageCount > 0 && image_count > swapchain_support.capabilities.maxImageCount) { - image_count = swapchain_support.capabilities.maxImageCount; - } - - const bool queue_families_differ = m_queue_family_indices.graphics.value != m_queue_family_indices.present.value; - - VK_TRY(false, - vkCreateSwapchainKHR( - g_dev, - &(VkSwapchainCreateInfoKHR){ - .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, - .pNext = NULL, - .flags = 0, - .surface = m_surface, - .minImageCount = image_count, - .imageFormat = surface_format.format, - .imageColorSpace = surface_format.colorSpace, - .imageExtent = extent, - .imageArrayLayers = 1, - .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, - .imageSharingMode = - queue_families_differ - ? VK_SHARING_MODE_CONCURRENT - : VK_SHARING_MODE_EXCLUSIVE, - .queueFamilyIndexCount = - queue_families_differ - ? 2 - : 0, - .pQueueFamilyIndices = - queue_families_differ - ? (const uint32_t []){ - m_queue_family_indices.graphics.value, - m_queue_family_indices.present.value - } - : NULL, - .preTransform = swapchain_support.capabilities.currentTransform, - .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, - .presentMode = present_mode, - .clipped = VK_TRUE, - .oldSwapchain = VK_NULL_HANDLE, - }, - NULL, - &g_swapchain - ) - ); - - vkGetSwapchainImagesKHR(g_dev, g_swapchain, &m_swapchain_images.allocated, NULL); - m_swapchain_images = PTK_LIST_NEW(VkImage, m_swapchain_images.allocated); - vkGetSwapchainImagesKHR(g_dev, g_swapchain, &m_swapchain_images.allocated, m_swapchain_images.data); - PTK_LIST_FILLED(m_swapchain_images); - - m_swapchain_image_format = surface_format.format; - m_swapchain_extent = extent; - - PTK_LIST_FREE(swapchain_support.formats); - PTK_LIST_FREE(swapchain_support.present_modes); - - return true; -} - -bool create_image_views(void) { - m_swapchain_image_views = PTK_LIST_NEW(VkImageView, m_swapchain_images.size); - - PTK_LIST_FOR_EACH(VkImage, m_swapchain_images, swapchain_image, { - VkImageView image_view; - - VK_TRY(false, - vkCreateImageView( - g_dev, - &(VkImageViewCreateInfo){ - .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, - .pNext = NULL, - .flags = 0, - .image = swapchain_image, - .viewType = VK_IMAGE_VIEW_TYPE_2D, - .format = m_swapchain_image_format, - .components = (VkComponentMapping){ - .r = VK_COMPONENT_SWIZZLE_IDENTITY, - .g = VK_COMPONENT_SWIZZLE_IDENTITY, - .b = VK_COMPONENT_SWIZZLE_IDENTITY, - .a = VK_COMPONENT_SWIZZLE_IDENTITY, - }, - .subresourceRange = (VkImageSubresourceRange){ - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .baseMipLevel = 0, - .levelCount = 1, - .baseArrayLayer = 0, - .layerCount = 1, - }, - }, - NULL, - &image_view - ) - ); - - PTK_LIST_ADD(VkImageView, m_swapchain_image_views, image_view); - }) - - return true; -} - -PTK_STRING read_spv(const char *filename) { - errno = 0; - FILE *file = fopen(filename, "r"); - - if (errno != 0) { - switch (errno) { - case EACCES: PTK_ERR("insufficient permissions to open %s", filename); break; - default: PTK_ERR("unknown error while reading %s", filename); break; - } - return PTK_LIST_NEW(char, 0); - } - - PTK_STRING buffer; - - fseek(file, 0L, SEEK_END); - buffer.allocated = ftell(file); - rewind(file); - - buffer = PTK_STRING_NEW(buffer.allocated); - - const size_t items_read = fread(buffer.data, sizeof(char), buffer.allocated, file); - - if (items_read != buffer.allocated) { - if (feof(file)) { - PTK_ERR("unexpected end of file while reading %s", filename); - } else if (ferror(file)) { - PTK_ERR("error reading %s", filename); - } - return PTK_STRING_NEW(0); - } - - PTK_LIST_FILLED(buffer); - - fclose(file); - - return buffer; -} - -PTK_OPTION(VkShaderModule) create_shader_module(const PTK_STRING code) { - VkShaderModule shader_module; - - VK_TRY(PTK_OPTION_NONE(VkShaderModule), - vkCreateShaderModule( - g_dev, - &(VkShaderModuleCreateInfo){ - .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, - .pNext = NULL, - .flags = 0, - .codeSize = code.size, - .pCode = (const uint32_t *)code.data, - }, - NULL, - &shader_module - ) - ); - - return PTK_OPTION_SOME(VkShaderModule, shader_module); -} - -bool create_render_pass(void) { - VK_TRY(false, - vkCreateRenderPass( - g_dev, - &(VkRenderPassCreateInfo){ - .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, - .pNext = NULL, - .flags = 0, - .attachmentCount = 1, - .pAttachments = &(VkAttachmentDescription){ - .flags = 0, - .format = m_swapchain_image_format, - .samples = VK_SAMPLE_COUNT_1_BIT, - .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, - .storeOp = VK_ATTACHMENT_STORE_OP_STORE, - .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, - .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, - .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, - .finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, - }, - .subpassCount = 1, - .pSubpasses = &(VkSubpassDescription){ - .flags = 0, - .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, - .inputAttachmentCount = 0, - .pInputAttachments = NULL, - .colorAttachmentCount = 1, - .pColorAttachments = &(VkAttachmentReference){ - .attachment = 0, - .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, - }, - .pResolveAttachments = NULL, - .pDepthStencilAttachment = NULL, - .preserveAttachmentCount = 0, - .pPreserveAttachments = NULL, - }, - .dependencyCount = 1, - .pDependencies = &(VkSubpassDependency){ - .srcSubpass = VK_SUBPASS_EXTERNAL, - .dstSubpass = 0, - .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - .srcAccessMask = 0, - .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, - .dependencyFlags = 0, - }, - }, - NULL, - &m_render_pass - ) - ); - - return true; -} - -PTK_ARRAY_DEFINE(VkDescriptorSetLayoutBinding); - -bool create_descriptor_set_layout(void) { - PTK_ARRAY(VkDescriptorSetLayoutBinding) bindings = PTK_ARRAY_NEW(VkDescriptorSetLayoutBinding, { - (VkDescriptorSetLayoutBinding){ - .binding = 0, - .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, - .descriptorCount = 1, - .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, - .pImmutableSamplers = NULL, - }, - (VkDescriptorSetLayoutBinding){ - .binding = 1, - .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - .descriptorCount = 1, - .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, - .pImmutableSamplers = NULL, - }, - }); - - VK_TRY(false, - vkCreateDescriptorSetLayout( - g_dev, - &(VkDescriptorSetLayoutCreateInfo){ - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, - .pNext = NULL, - .flags = 0, - .bindingCount = bindings.size, - .pBindings = bindings.data, - }, - NULL, - &m_descriptor_set_layout - ) - ); - - return true; -} - -PTK_ARRAY_DEFINE(VkSpecializationMapEntry); - -bool create_graphics_pipeline(void) { - const PTK_STRING vert_shader_code = read_spv("target/shaders/shader.vert.spv"); - const PTK_STRING frag_shader_code = read_spv("target/shaders/shader.frag.spv"); - - const PTK_OPTION(VkShaderModule) vert_shader_module = create_shader_module(vert_shader_code); - - if (!vert_shader_module.exists) { - PTK_ERR("failed creating vert shader module"); - return false; - } - - const PTK_OPTION(VkShaderModule) frag_shader_module = create_shader_module(frag_shader_code); - - if (!frag_shader_module.exists) { - PTK_ERR("failed creating frag shader module"); - return false; - } - - PTK_ARRAY(VkSpecializationMapEntry) frag_spec_map_entries = PTK_ARRAY_NEW(VkSpecializationMapEntry, { - (VkSpecializationMapEntry){ - .constantID = 0, - .offset = 0, - .size = sizeof(int), - }, - (VkSpecializationMapEntry){ - .constantID = 1, - .offset = sizeof(int), - .size = sizeof(int), - }, - (VkSpecializationMapEntry){ - .constantID = 2, - .offset = sizeof(int) * 2, - .size = sizeof(int), - }, - }); - - const VkSpecializationInfo frag_spec_info = { - .mapEntryCount = frag_spec_map_entries.size, - .pMapEntries = frag_spec_map_entries.data, - .dataSize = sizeof(int) * 3, - .pData = (int []){ - PTK_COMPONENT_TYPE_ELLIPSE, - PTK_COMPONENT_TYPE_IMAGE, - g_max_images - }, - }; - - const VkPipelineShaderStageCreateInfo shader_stages[] = { - (VkPipelineShaderStageCreateInfo){ - .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, - .pNext = NULL, - .flags = 0, - .stage = VK_SHADER_STAGE_VERTEX_BIT, - .module = vert_shader_module.value, - .pName = "main", - .pSpecializationInfo = NULL, - }, - (VkPipelineShaderStageCreateInfo){ - .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, - .pNext = NULL, - .flags = 0, - .stage = VK_SHADER_STAGE_FRAGMENT_BIT, - .module = frag_shader_module.value, - .pName = "main", - .pSpecializationInfo = &frag_spec_info, - }, - }; - - const VkPipelineVertexInputStateCreateInfo vertex_input_info = { - .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, - .pNext = NULL, - .flags = 0, - .vertexBindingDescriptionCount = 1, - .pVertexBindingDescriptions = &m_vertex_binding_description, - .vertexAttributeDescriptionCount = m_vertex_attribute_descriptions.size, - .pVertexAttributeDescriptions = m_vertex_attribute_descriptions.data, - }; - - const VkPipelineDynamicStateCreateInfo dynamic_state = { - .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, - .pNext = NULL, - .flags = 0, - .dynamicStateCount = 0, - .pDynamicStates = NULL, - }; - - const VkPipelineInputAssemblyStateCreateInfo input_assembly = { - .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, - .pNext = NULL, - .flags = 0, - .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, - .primitiveRestartEnable = VK_FALSE, - }; - - const VkPipelineViewportStateCreateInfo viewport_state = { - .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, - .pNext = NULL, - .flags = 0, - .viewportCount = 1, - .pViewports = &(VkViewport){ - .x = 0.0f, - .y = 0.0f, - .width = (float) m_swapchain_extent.width, - .height = (float) m_swapchain_extent.height, - .minDepth = 0.0f, - .maxDepth = 1.0f, - }, - .scissorCount = 1, - .pScissors = &(VkRect2D){ - .offset = (VkOffset2D){ - .x = 0, - .y = 0, - }, - .extent = m_swapchain_extent, - }, - }; - - const VkPipelineRasterizationStateCreateInfo rasterizer = { - .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, - .pNext = NULL, - .flags = 0, - .depthClampEnable = VK_FALSE, - .rasterizerDiscardEnable = VK_FALSE, - .polygonMode = VK_POLYGON_MODE_FILL, - .cullMode = VK_CULL_MODE_NONE, - .frontFace = VK_FRONT_FACE_CLOCKWISE, - .depthBiasEnable = VK_FALSE, - .depthBiasConstantFactor = 0.0f, - .depthBiasClamp = 0.0f, - .depthBiasSlopeFactor = 0.0f, - .lineWidth = 1.0f, - }; - - const VkPipelineMultisampleStateCreateInfo multisampling = { - .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, - .pNext = NULL, - .flags = 0, - .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT, - .sampleShadingEnable = VK_FALSE, - .minSampleShading = 1.0f, - .pSampleMask = NULL, - .alphaToCoverageEnable = VK_FALSE, - .alphaToOneEnable = VK_FALSE, - }; - - const VkPipelineColorBlendAttachmentState color_blend_attachment = { - .blendEnable = VK_TRUE, - .srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA, - .dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, - .colorBlendOp = VK_BLEND_OP_ADD, - .srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE, - .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, - .alphaBlendOp = VK_BLEND_OP_ADD, - .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, - }; - - const VkPipelineColorBlendStateCreateInfo color_blending = { - .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, - .pNext = NULL, - .flags = 0, - .logicOpEnable = VK_FALSE, - .logicOp = VK_LOGIC_OP_COPY, - .attachmentCount = 1, - .pAttachments = &color_blend_attachment, - .blendConstants[0] = 0.0f, - .blendConstants[1] = 0.0f, - .blendConstants[2] = 0.0f, - .blendConstants[3] = 0.0f, - }; - - VK_TRY(false, - vkCreatePipelineLayout( - g_dev, - &(VkPipelineLayoutCreateInfo){ - .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, - .pNext = NULL, - .flags = 0, - .setLayoutCount = 1, - .pSetLayouts = &m_descriptor_set_layout, - .pushConstantRangeCount = 0, - .pPushConstantRanges = NULL, - }, - NULL, - &m_pipeline_layout - ) - ); - - VK_TRY(false, - vkCreateGraphicsPipelines( - g_dev, - VK_NULL_HANDLE, - 1, - &(VkGraphicsPipelineCreateInfo){ - .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, - .pNext = NULL, - .flags = 0, - .stageCount = 2, - .pStages = shader_stages, - .pVertexInputState = &vertex_input_info, - .pInputAssemblyState = &input_assembly, - .pTessellationState = NULL, - .pViewportState = &viewport_state, - .pRasterizationState = &rasterizer, - .pMultisampleState = &multisampling, - .pDepthStencilState = NULL, - .pColorBlendState = &color_blending, - .pDynamicState = &dynamic_state, - .layout = m_pipeline_layout, - .renderPass = m_render_pass, - .subpass = 0, - .basePipelineHandle = VK_NULL_HANDLE, - .basePipelineIndex = -1, - }, - NULL, - &m_pipeline - ) - ); - - vkDestroyShaderModule(g_dev, vert_shader_module.value, NULL); - vkDestroyShaderModule(g_dev, frag_shader_module.value, NULL); - - return true; -} - -bool create_framebuffers(void) { - m_swapchain_framebuffers = PTK_LIST_NEW(VkFramebuffer, m_swapchain_image_views.size); - - VkFramebuffer fb; - - PTK_LIST_FOR_EACH(VkImageView, m_swapchain_image_views, swapchain_image_view, { - VK_TRY(false, - vkCreateFramebuffer( - g_dev, - &(VkFramebufferCreateInfo){ - .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, - .pNext = NULL, - .flags = 0, - .renderPass = m_render_pass, - .attachmentCount = 1, - .pAttachments = &swapchain_image_view, - .width = m_swapchain_extent.width, - .height = m_swapchain_extent.height, - .layers = 1, - }, - NULL, - &fb - ) - ); - PTK_LIST_ADD(VkFramebuffer, m_swapchain_framebuffers, fb); - }) - - return true; -} - -bool create_command_pool(void) { - VK_TRY(false, - vkCreateCommandPool( - g_dev, - &(VkCommandPoolCreateInfo){ - .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, - .pNext = NULL, - .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, - .queueFamilyIndex = m_queue_family_indices.graphics.value, - }, - NULL, - &m_command_pool - ) - ); - - return true; -} - -PTK_OPTION(uint32_t) vk_find_memory_type(const uint32_t type_filter, const VkMemoryPropertyFlags props) { - VkPhysicalDeviceMemoryProperties mem_props; - vkGetPhysicalDeviceMemoryProperties(g_physical_dev, &mem_props); - - for (uint32_t i = 0; i < mem_props.memoryTypeCount; ++i) { - if (type_filter & (1 << i) && (mem_props.memoryTypes[i].propertyFlags & props) == props) { - return PTK_OPTION_SOME(uint32_t, i); - } - } - - return PTK_OPTION_NONE(uint32_t); -} - -bool vk_create_buffer(const VkDeviceSize size, const VkBufferUsageFlags usage, const VkMemoryPropertyFlags props, VkBuffer *buffer, VkDeviceMemory *buffer_memory) { - VK_TRY(false, - vkCreateBuffer( - g_dev, - &(VkBufferCreateInfo){ - .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, - .pNext = NULL, - .flags = 0, - .size = size, - .usage = usage, - .sharingMode = VK_SHARING_MODE_EXCLUSIVE, - .queueFamilyIndexCount = 0, - .pQueueFamilyIndices = NULL, - }, - NULL, - buffer - ) - ); - - VkMemoryRequirements mem_reqs; - vkGetBufferMemoryRequirements(g_dev, *buffer, &mem_reqs); - - const PTK_OPTION(uint32_t) memory_type = vk_find_memory_type(mem_reqs.memoryTypeBits, props); - - if (!memory_type.exists) { - PTK_ERR("failed to find suitable memory type"); - return false; - } - - VK_TRY(false, - vkAllocateMemory( - g_dev, - &(VkMemoryAllocateInfo){ - .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, - .pNext = NULL, - .allocationSize = mem_reqs.size, - .memoryTypeIndex = memory_type.value, - }, - NULL, - buffer_memory - ) - ); - - vkBindBufferMemory(g_dev, *buffer, *buffer_memory, 0); - - return true; -} - -PTK_OPTION(VkCommandBuffer) vk_begin_single_time_commands(void) { - VkCommandBuffer command_buffer; - - VK_TRY(PTK_OPTION_NONE(VkCommandBuffer), - vkAllocateCommandBuffers( - g_dev, - &(VkCommandBufferAllocateInfo){ - .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, - .pNext = NULL, - .commandPool = m_command_pool, - .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, - .commandBufferCount = 1, - }, - &command_buffer - ) - ); - - VK_TRY(PTK_OPTION_NONE(VkCommandBuffer), - vkBeginCommandBuffer( - command_buffer, - &(VkCommandBufferBeginInfo){ - .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, - .pNext = NULL, - .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, - .pInheritanceInfo = NULL, - } - ) - ); - - return PTK_OPTION_SOME(VkCommandBuffer, command_buffer); -} - -bool vk_end_single_time_commands(VkCommandBuffer command_buffer) { - VK_TRY(false, vkEndCommandBuffer(command_buffer)); - - VK_TRY(false, - vkQueueSubmit( - g_graphics_queue, - 1, - &(VkSubmitInfo){ - .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, - .pNext = NULL, - .waitSemaphoreCount = 0, - .pWaitSemaphores = NULL, - .pWaitDstStageMask = NULL, - .commandBufferCount = 1, - .pCommandBuffers = &command_buffer, - .signalSemaphoreCount = 0, - .pSignalSemaphores = NULL, - }, - VK_NULL_HANDLE - ); - ); - - VK_TRY(false, vkQueueWaitIdle(g_graphics_queue)); - - vkFreeCommandBuffers(g_dev, m_command_pool, 1, &command_buffer); - - return true; -} - -bool copy_buffer(const VkBuffer src, const VkBuffer dst, const VkDeviceSize size) { - PTK_OPTION(VkCommandBuffer) command_buffer = vk_begin_single_time_commands(); - - if (!command_buffer.exists) { - PTK_ERR("failed to create command buffer"); - return false; - } - - vkCmdCopyBuffer(command_buffer.value, src, dst, 1, &(VkBufferCopy){ - .srcOffset = 0, - .dstOffset = 0, - .size = size, - }); - - if (!vk_end_single_time_commands(command_buffer.value)) { - PTK_ERR("failed to end command buffer"); - return false; - } - - return true; -} - -bool transfer_to_buffer(void *src, size_t src_size, VkBuffer buffer) { - const VkDeviceSize buffer_size = src_size; - - VkBuffer staging_buffer; - VkDeviceMemory staging_buffer_memory; - if ( - !vk_create_buffer( - buffer_size, - VK_BUFFER_USAGE_TRANSFER_SRC_BIT, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, - &staging_buffer, - &staging_buffer_memory - ) - ) { - PTK_ERR("failed creating staging vertex buffer"); - return false; - } - - void *data; - vkMapMemory(g_dev, staging_buffer_memory, 0, buffer_size, 0, &data); - memcpy(data, src, (size_t)buffer_size); - vkUnmapMemory(g_dev, staging_buffer_memory); - - if (!copy_buffer(staging_buffer, buffer, buffer_size)) { - PTK_ERR("failed copying staging buffer to vertex buffer"); - return false; - } - - vkDestroyBuffer(g_dev, staging_buffer, NULL); - vkFreeMemory(g_dev, staging_buffer_memory, NULL); - - return true; -} +UniformBufferObject g_uniform_buffer_object; bool vk_transfer_vertex_data(void) { PTK_DEBUG("vertices updated!"); @@ -1211,7 +33,7 @@ bool vk_transfer_vertex_data(void) { PTK_DEBUG("transferring vertices to gpu…"); const size_t vertices_size = sizeof(g_vertices.data[0]) * g_vertices.size; - if (!transfer_to_buffer(g_vertices.data, vertices_size, m_vertex_buffer)) { + if (!transfer_to_buffer(g_dev, g_physical_dev, g_vertices.data, vertices_size, g_vertex_buffer)) { PTK_ERR("failed transferring vertices"); return false; } @@ -1223,7 +45,7 @@ bool vk_transfer_vertex_data(void) { PTK_DEBUG("transferring indices to gpu…"); const size_t indices_size = sizeof(g_indices.data[0]) * g_indices.size; - if (!transfer_to_buffer(g_indices.data, indices_size, m_index_buffer)) { + if (!transfer_to_buffer(g_dev, g_physical_dev, g_indices.data, indices_size, g_index_buffer)) { PTK_ERR("failed transferring indices"); return false; } @@ -1231,479 +53,33 @@ bool vk_transfer_vertex_data(void) { return true; } -bool create_vertex_buffer(void) { - // const VkDeviceSize buffer_size = sizeof(g_vertices.data[0]) * g_vertices.size; - const VkDeviceSize buffer_size = 65536; - if ( - !vk_create_buffer( - buffer_size, - VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, - &m_vertex_buffer, - &m_vertex_buffer_memory - ) - ) { - PTK_ERR("failed creating vertex buffer"); - return false; - } - - return true; -} - -bool create_index_buffer(void) { - const VkDeviceSize buffer_size = 65536; - if ( - !vk_create_buffer( - buffer_size, - VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, - &m_index_buffer, - &m_index_buffer_memory - ) - ) { - PTK_ERR("failed creating index buffer"); - return false; - } - - return true; -} - -bool create_uniform_buffers(void) { - const VkDeviceSize buffer_size = sizeof(UniformBufferObject); - - m_uniform_buffers = PTK_LIST_NEW(VkBuffer, g_max_frames_in_flight); - m_uniform_buffers_memory = PTK_LIST_NEW(VkDeviceMemory, g_max_frames_in_flight); - m_uniform_buffers_mapped = PTK_LIST_NEW(voidptr, g_max_frames_in_flight); - - for (size_t i = 0; i < g_max_frames_in_flight; ++i) { - if ( - !vk_create_buffer( - buffer_size, - VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, - &m_uniform_buffers.data[i], - &m_uniform_buffers_memory.data[i] - ) - ) { - PTK_ERR("failed creating vertex buffer"); - return false; - } - - vkMapMemory(g_dev, m_uniform_buffers_memory.data[i], 0, buffer_size, 0, &m_uniform_buffers_mapped.data[i]); - } - - return true; -} - -PTK_ARRAY_DEFINE(VkDescriptorPoolSize); - -bool create_descriptor_pool(void) { - PTK_ARRAY(VkDescriptorPoolSize) pool_sizes = PTK_ARRAY_NEW(VkDescriptorPoolSize, { - (VkDescriptorPoolSize){ - .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, - .descriptorCount = g_max_frames_in_flight, - }, - (VkDescriptorPoolSize){ - .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - .descriptorCount = g_max_frames_in_flight, - }, - }); - - VK_TRY(false, - vkCreateDescriptorPool( - g_dev, - &(VkDescriptorPoolCreateInfo){ - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, - .pNext = NULL, - .flags = 0, - .maxSets = g_max_frames_in_flight, - .poolSizeCount = pool_sizes.size, - .pPoolSizes = pool_sizes.data, - }, - NULL, - &m_descriptor_pool - ) - ); - - return true; -} - -PTK_LIST_DEFINE(VkDescriptorSetLayout); -PTK_ARRAY_DEFINE(VkWriteDescriptorSet); - -void vk_update_descriptor_sets(void) { - for (size_t i = 0; i < g_max_frames_in_flight; ++i) { - PTK_ARRAY(VkWriteDescriptorSet) descriptor_writes = PTK_ARRAY_NEW(VkWriteDescriptorSet, { - (VkWriteDescriptorSet){ - .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - .pNext = NULL, - .dstSet = m_descriptor_sets.data[i], - .dstBinding = 0, - .dstArrayElement = 0, - .descriptorCount = 1, - .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, - .pImageInfo = NULL, - .pBufferInfo = &(VkDescriptorBufferInfo){ - .buffer = m_uniform_buffers.data[i], - .offset = 0, - .range = sizeof(UniformBufferObject), - }, - .pTexelBufferView = NULL, - }, - (VkWriteDescriptorSet){ - .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - .pNext = NULL, - .dstSet = m_descriptor_sets.data[i], - .dstBinding = 1, - .dstArrayElement = 0, - .descriptorCount = 1, - .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - .pImageInfo = &(VkDescriptorImageInfo){ - .sampler = g_sampler, - .imageView = g_image_view, - .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - }, - .pBufferInfo = NULL, - .pTexelBufferView = NULL, - }, - }); - - vkUpdateDescriptorSets( - g_dev, - descriptor_writes.size, - descriptor_writes.data, - 0, - NULL - ); - } -} - -bool create_descriptor_sets(void) { - PTK_LIST(VkDescriptorSetLayout) layouts = PTK_LIST_NEW(VkDescriptorSetLayout, g_max_frames_in_flight); - for (size_t i = 0; i < g_max_frames_in_flight; ++i) { - PTK_LIST_ADD(VkDescriptorSetLayout, layouts, m_descriptor_set_layout); - } - - m_descriptor_sets = PTK_LIST_NEW(VkDescriptorSet, g_max_frames_in_flight); - - VK_TRY(false, - vkAllocateDescriptorSets( - g_dev, - &(VkDescriptorSetAllocateInfo){ - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, - .pNext = NULL, - .descriptorPool = m_descriptor_pool, - .descriptorSetCount = layouts.size, - .pSetLayouts = layouts.data, - }, - m_descriptor_sets.data - ) - ); - - vk_update_descriptor_sets(); - - return true; -} - -bool allocate_command_buffers(void) { - g_command_buffers = PTK_LIST_NEW(VkCommandBuffer, g_max_frames_in_flight); - - VK_TRY(false, - vkAllocateCommandBuffers( - g_dev, - &(VkCommandBufferAllocateInfo){ - .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, - .pNext = NULL, - .commandPool = m_command_pool, - .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, - .commandBufferCount = g_command_buffers.allocated, - }, - g_command_buffers.data - ) - ); - PTK_LIST_FILLED(g_command_buffers); - - return true; -} - -bool vk_record_command_buffer(const VkCommandBuffer command_buffer, const uint32_t image_index) { - VK_TRY(false, - vkBeginCommandBuffer( - command_buffer, - &(VkCommandBufferBeginInfo){ - .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, - .pNext = NULL, - .flags = 0, - .pInheritanceInfo = NULL, - } - ) - ); - - vkCmdBeginRenderPass( - command_buffer, - &(VkRenderPassBeginInfo){ - .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, - .pNext = NULL, - .renderPass = m_render_pass, - .framebuffer = m_swapchain_framebuffers.data[image_index], - .renderArea = (VkRect2D){ - .offset = (VkOffset2D){ - .x = 0, - .y = 0, - }, - .extent = m_swapchain_extent, - }, - .clearValueCount = 1, - .pClearValues = &(VkClearValue){ - .color = (VkClearColorValue){ - .float32[0] = 0.0f, - .float32[1] = 0.0f, - .float32[2] = 0.0f, - .float32[3] = 1.0f, - }, - }, - }, - VK_SUBPASS_CONTENTS_INLINE - ); - - vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline); - - vkCmdBindVertexBuffers(command_buffer, 0, 1, (VkBuffer []){m_vertex_buffer}, (VkDeviceSize []){0}); - - vkCmdBindIndexBuffer(command_buffer, m_index_buffer, 0, VK_INDEX_TYPE_UINT32); - - vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline_layout, 0, 1, &m_descriptor_sets.data[g_current_frame], 0, NULL); - - vkCmdDrawIndexed(command_buffer, g_indices.size, 1, 0, 0, 0); - - vkCmdEndRenderPass(command_buffer); - - VK_TRY(false, - vkEndCommandBuffer(command_buffer) - ); - - return true; -} - -bool create_sync_objects(void) { - g_image_available_semaphores = PTK_LIST_NEW(VkSemaphore, g_max_frames_in_flight); - g_render_finished_semaphores = PTK_LIST_NEW(VkSemaphore, g_max_frames_in_flight); - g_in_flight_fences = PTK_LIST_NEW(VkFence, g_max_frames_in_flight); - - const VkSemaphoreCreateInfo semaphore_info = { - .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, - .pNext = NULL, - .flags = 0, - }; - - for (size_t i = 0; i < g_max_frames_in_flight; ++i) { - VK_TRY(false, - vkCreateSemaphore(g_dev, &semaphore_info, NULL, &g_image_available_semaphores.data[i]) - ); - - VK_TRY(false, - vkCreateSemaphore(g_dev, &semaphore_info, NULL, &g_render_finished_semaphores.data[i]) - ); - - VK_TRY(false, - vkCreateFence( - g_dev, - &(VkFenceCreateInfo){ - .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, - .pNext = NULL, - .flags = VK_FENCE_CREATE_SIGNALED_BIT, - }, - NULL, - &g_in_flight_fences.data[i] - ) - ); - } - - return true; -} - -void cleanup_swapchain(void) { - PTK_LIST_FOR_EACH(VkFramebuffer, m_swapchain_framebuffers, fb, { - vkDestroyFramebuffer(g_dev, fb, NULL); - }) - PTK_LIST_FREE(m_swapchain_framebuffers); - - PTK_LIST_FREE(m_swapchain_images); - PTK_LIST_FOR_EACH(VkImageView, m_swapchain_image_views, iv, { - vkDestroyImageView(g_dev, iv, NULL); - }) - PTK_LIST_FREE(m_swapchain_image_views); - - vkDestroySwapchainKHR(g_dev, g_swapchain, NULL); -} - -bool vk_recreate_swapchain(void) { - int width = 0, height = 0; - glfwGetFramebufferSize(m_window, &width, &height); - - while (width == 0 || height == 0) { - glfwGetFramebufferSize(m_window, &width, &height); - glfwWaitEvents(); - } - - vkDeviceWaitIdle(g_dev); - - cleanup_swapchain(); - - if (!create_swapchain()) { - PTK_ERR("failed creating new swapchain"); - return false; - } - - if (!create_image_views()) { - PTK_ERR("failed creating new image views"); - return false; - } - - if (!create_framebuffers()) { - PTK_ERR("failed creating new framebuffers"); - return false; - } - - return true; -} - -bool vk_update_uniform_buffer(const size_t current_frame) { - m_uniform_buffer_object.window_size.w = m_swapchain_extent.width; - m_uniform_buffer_object.window_size.h = m_swapchain_extent.height; - - memcpy(m_uniform_buffers_mapped.data[current_frame], &m_uniform_buffer_object, sizeof(m_uniform_buffer_object)); - - return true; -} - -bool vk_init(GLFWwindow *window, const size_t width, const size_t height, const char *title, const PtkVersion version) { - m_window = window; - m_uniform_buffer_object.initial_window_size.w = width; - m_uniform_buffer_object.initial_window_size.h = height; - - if (!create_vk_instance(title, version)) { - PTK_ERR("failed creating VkInstance"); - return false; - } - - if (!select_physical_dev()) { - PTK_ERR("failed selecting physical device"); - return false; - } - - VK_TRY(false, glfwCreateWindowSurface(m_instance, m_window, NULL, &m_surface)); - - if (!create_logical_dev()) { - PTK_ERR("failed creating logical device"); - return false; - } - - if (!create_swapchain()) { - PTK_ERR("failed creating swapchain"); - return false; - } - - if (!create_image_views()) { - PTK_ERR("failed creating image views"); - return false; - } - - if (!create_render_pass()) { - PTK_ERR("failed creating render pass"); - return false; - } - - if (!vk_create_sampler()) { - PTK_ERR("failed creating sampler"); - return false; - } - - if (!create_descriptor_set_layout()) { - PTK_ERR("failed creating descriptor set layout"); - return false; - } - - if (!create_graphics_pipeline()) { - PTK_ERR("failed creating graphics pipeline"); - return false; - } - - if (!create_framebuffers()) { - PTK_ERR("failed creating framebuffers"); - return false; - } - - if (!create_command_pool()) { - PTK_ERR("failed creating command pool"); - return false; - } - - if (!create_vertex_buffer()) { - PTK_ERR("failed creating vertex buffer"); - return false; - } - - if (!create_index_buffer()) { - PTK_ERR("failed creating index buffer"); - return false; - } - - if (!create_uniform_buffers()) { - PTK_ERR("failed creating uniform buffers"); - return false; - } - - if (!create_descriptor_pool()) { - PTK_ERR("failed creating descriptor pool"); - return false; - } - - if (!create_descriptor_sets()) { - PTK_ERR("failed creating descriptor sets"); - return false; - } - - if (!allocate_command_buffers()) { - PTK_ERR("failed allocating command buffers"); - return false; - } - - if (!create_sync_objects()) { - PTK_ERR("failed creating sync objects"); - return false; - } - - return true; -} - void vk_cleanup(void) { - cleanup_swapchain(); + vk_cleanup_swapchain(); vk_image_cleanup(); - vkDestroyPipeline(g_dev, m_pipeline, NULL); - vkDestroyPipelineLayout(g_dev, m_pipeline_layout, NULL); + vkDestroyPipeline(g_dev, g_pipeline, NULL); + vkDestroyPipelineLayout(g_dev, g_pipeline_layout, NULL); - vkDestroyRenderPass(g_dev, m_render_pass, NULL); + vkDestroyRenderPass(g_dev, g_render_pass, NULL); for (size_t i = 0; i < g_max_frames_in_flight; ++i) { - vkDestroyBuffer(g_dev, m_uniform_buffers.data[i], NULL); - vkFreeMemory(g_dev, m_uniform_buffers_memory.data[i], NULL); + vkDestroyBuffer(g_dev, g_uniform_buffers.data[i], NULL); + vkFreeMemory(g_dev, g_uniform_buffer_memories.data[i], NULL); } - PTK_LIST_FREE(m_uniform_buffers); - PTK_LIST_FREE(m_uniform_buffers_memory); - PTK_LIST_FREE(m_uniform_buffers_mapped); + PTK_LIST_FREE(g_uniform_buffers); + PTK_LIST_FREE(g_uniform_buffer_memories); + PTK_LIST_FREE(g_uniform_buffers_mapped); - vkDestroyDescriptorPool(g_dev, m_descriptor_pool, NULL); + vkDestroyDescriptorPool(g_dev, g_descriptor_pool, NULL); - vkDestroyDescriptorSetLayout(g_dev, m_descriptor_set_layout, NULL); + vkDestroyDescriptorSetLayout(g_dev, g_descriptor_set_layout, NULL); - vkDestroyBuffer(g_dev, m_index_buffer, NULL); - vkFreeMemory(g_dev, m_index_buffer_memory, NULL); + vkDestroyBuffer(g_dev, g_index_buffer, NULL); + vkFreeMemory(g_dev, g_index_buffer_memory, NULL); - vkDestroyBuffer(g_dev, m_vertex_buffer, NULL); - vkFreeMemory(g_dev, m_vertex_buffer_memory, NULL); + vkDestroyBuffer(g_dev, g_vertex_buffer, NULL); + vkFreeMemory(g_dev, g_vertex_buffer_memory, NULL); for (size_t i = 0; i < g_max_frames_in_flight; ++i) { vkDestroySemaphore(g_dev, g_image_available_semaphores.data[i], NULL); @@ -1711,14 +87,14 @@ void vk_cleanup(void) { vkDestroyFence(g_dev, g_in_flight_fences.data[i], NULL); } - vkDestroyCommandPool(g_dev, m_command_pool, NULL); + vkDestroyCommandPool(g_dev, g_command_pool, NULL); vkDestroyDevice(g_dev, NULL); - vkDestroySurfaceKHR(m_instance, m_surface, NULL); - vkDestroyInstance(m_instance, NULL); + vkDestroySurfaceKHR(g_instance, g_surface, NULL); + vkDestroyInstance(g_instance, NULL); - glfwDestroyWindow(m_window); + glfwDestroyWindow(g_window); glfwTerminate(); } diff --git a/src/ptk_vk/init.h b/src/ptk_vk/init.h index d6609ce..0b19147 100644 --- a/src/ptk_vk/init.h +++ b/src/ptk_vk/init.h @@ -3,55 +3,14 @@ #ifndef PTK_PTK_VK_INIT_H_ #define PTK_PTK_VK_INIT_H_ -#ifndef GLFW_INCLUDE_VULKAN -#define GLFW_INCLUDE_VULKAN -#endif -#include -#include -#include -#include +#include "ptk_vk/uniform_buffers.h" -PTK_LIST_DEFINE(VkCommandBuffer); -PTK_LIST_DEFINE(VkSemaphore); -PTK_LIST_DEFINE(VkFence); -PTK_OPTION_DEFINE(VkCommandBuffer); -PTK_OPTION_DEFINE(uint32_t); +#include -extern VkDevice g_dev; -extern VkPhysicalDevice g_physical_dev; -extern VkSwapchainKHR g_swapchain; - -extern VkQueue g_graphics_queue; -extern VkQueue g_present_queue; - -extern const size_t g_max_frames_in_flight; - -extern PTK_LIST(VkCommandBuffer) g_command_buffers; - -extern PTK_LIST(VkSemaphore) g_image_available_semaphores; -extern PTK_LIST(VkSemaphore) g_render_finished_semaphores; -extern PTK_LIST(VkFence) g_in_flight_fences; - -bool vk_init(GLFWwindow *window, const size_t width, const size_t height, const char *title, const PtkVersion version); +extern UniformBufferObject g_uniform_buffer_object; bool vk_transfer_vertex_data(void); -bool vk_record_command_buffer(const VkCommandBuffer command_buffer, const uint32_t image_index); - -bool vk_recreate_swapchain(void); - -bool vk_update_uniform_buffer(const size_t current_frame); - -bool vk_create_buffer(const VkDeviceSize size, const VkBufferUsageFlags usage, const VkMemoryPropertyFlags props, VkBuffer *buffer, VkDeviceMemory *buffer_memory); - -PTK_OPTION(uint32_t) vk_find_memory_type(const uint32_t type_filter, const VkMemoryPropertyFlags props); - -PTK_OPTION(VkCommandBuffer) vk_begin_single_time_commands(void); - -bool vk_end_single_time_commands(VkCommandBuffer command_buffer); - -void vk_update_descriptor_sets(void); - void vk_cleanup(void); #endif // PTK_PTK_VK_INIT_H_ diff --git a/src/ptk_vk/instance.c b/src/ptk_vk/instance.c new file mode 100644 index 0000000..ddff035 --- /dev/null +++ b/src/ptk_vk/instance.c @@ -0,0 +1,89 @@ +// Copyright (jacekpoz 2024). Licensed under the EUPL-1.2 or later. + +#include "ptk_vk/instance.h" + +#include "ptk_vk/backend.h" +#include "ptk_vk/utils.h" + +#include "ptk_array.h" + +#ifndef GLFW_INCLUDE_VULKAN +#define GLFW_INCLUDE_VULKAN +#endif +#include + +#ifdef DEBUG +#include + +PTK_LIST_DEFINE(VkLayerProperties); + +bool check_validation_layers(const PTK_ARRAY(constcharptr) validation_layers) { + PTK_LIST(VkLayerProperties) available_layers; + + vkEnumerateInstanceLayerProperties(&available_layers.allocated, NULL); + + available_layers = PTK_LIST_NEW(VkLayerProperties, available_layers.allocated); + vkEnumerateInstanceLayerProperties(&available_layers.allocated, available_layers.data); + PTK_LIST_FILLED(available_layers); + + for (size_t i = 0; i < validation_layers.size; ++i) { + const char *layer_name = validation_layers.data[i]; + + bool layer_found = false; + + PTK_LIST_FOR_EACH(const VkLayerProperties, available_layers, layer_properties, { + if (strcmp(layer_name, layer_properties.layerName) == 0) { + layer_found = true; + break; + } + }) + + if (!layer_found) { + return false; + } + } + + return true; +} +#endif + +VkInstance g_instance; + +bool vk_instance_create(const char *title, const PtkVersion version) { +#ifdef DEBUG + if (!check_validation_layers(g_validation_layers)) { + PTK_ERR("couldn't find requested validation layer"); + return false; + } +#endif + + PTK_ARRAY(constcharptr) extension_names = PTK_ARRAY_EMPTY(constcharptr); + extension_names.data = glfwGetRequiredInstanceExtensions(&extension_names.size); + + VK_TRY(false, + vkCreateInstance( + &(VkInstanceCreateInfo){ + .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, + .pNext = NULL, + .flags = 0, + .pApplicationInfo = &(VkApplicationInfo){ + .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, + .pNext = NULL, + .pApplicationName = title, + .applicationVersion = VK_MAKE_API_VERSION(0, version.major, version.minor, version.patch), + .pEngineName = PTK_ENGINE_NAME, + .engineVersion = VK_MAKE_API_VERSION(0, PTK_VERSION_MAJOR, PTK_VERSION_MINOR, PTK_VERSION_PATCH), + .apiVersion = VK_API_VERSION_1_3, + }, + .enabledLayerCount = g_validation_layers.size, + .ppEnabledLayerNames = g_validation_layers.data, + .enabledExtensionCount = extension_names.size, + .ppEnabledExtensionNames = extension_names.data, + }, + NULL, + &g_instance + ) + ); + + return true; +} diff --git a/src/ptk_vk/instance.h b/src/ptk_vk/instance.h new file mode 100644 index 0000000..c7b6f6d --- /dev/null +++ b/src/ptk_vk/instance.h @@ -0,0 +1,14 @@ +// Copyright (jacekpoz 2024). Licensed under the EUPL-1.2 or later. + +#ifndef PTK_PTK_VK_INSTANCE_H_ +#define PTK_PTK_VK_INSTANCE_H_ + +#include "ptk.h" + +#include + +extern VkInstance g_instance; + +bool vk_instance_create(const char *title, const PtkVersion version); + +#endif // PTK_PTK_VK_INSTANCE_H_ diff --git a/src/ptk_vk/physical_device.c b/src/ptk_vk/physical_device.c new file mode 100644 index 0000000..d340736 --- /dev/null +++ b/src/ptk_vk/physical_device.c @@ -0,0 +1,81 @@ +// Copyright (jacekpoz 2024). Licensed under the EUPL-1.2 or later. + +#include "ptk_vk/physical_device.h" + +#include "ptk_vk/instance.h" + +#include "ptk_log.h" + +VkPhysicalDevice g_physical_dev; + +bool vk_select_physical_dev(void) { + uint32_t dev_count = 0; + vkEnumeratePhysicalDevices(g_instance, &dev_count, NULL); + if (dev_count == 0) { + PTK_ERR("failed to find GPU with vulkan support"); + return false; + } + VkPhysicalDevice devs[dev_count]; + vkEnumeratePhysicalDevices(g_instance, &dev_count, devs); + + VkPhysicalDeviceProperties dev_props[dev_count]; + uint32_t dgpus[dev_count]; + uint32_t dgpu_count = 0; + uint32_t igpus[dev_count]; + uint32_t igpu_count = 0; + + VkPhysicalDeviceMemoryProperties dev_mem_props[dev_count]; + uint32_t dev_mem_counts[dev_count]; + VkDeviceSize dev_mem_totals[dev_count]; + + for (size_t i = 0; i < dev_count; ++i) { + vkGetPhysicalDeviceProperties(devs[i], &dev_props[i]); + + if (dev_props[i].deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) { + dgpus[dgpu_count] = i; + dgpu_count += 1; + } else if (dev_props[i].deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU) { + igpus[igpu_count] = i; + igpu_count += 1; + } + + vkGetPhysicalDeviceMemoryProperties(devs[i], &dev_mem_props[i]); + + dev_mem_counts[i] = dev_mem_props[i].memoryHeapCount; + dev_mem_totals[i] = 0; + + for (size_t j = 0; j < dev_mem_counts[i]; ++j) { + dev_mem_totals[i] += dev_mem_props[i].memoryHeaps[j].size; + } + } + + VkDeviceSize max_mem_size = 0; + size_t dev_best_index = 0; + + if (dgpu_count != 0) { + for (size_t i = 0; i < dgpu_count; ++i) { + if (dev_mem_totals[i] > max_mem_size) { + dev_best_index = dgpus[i]; + max_mem_size = dev_mem_totals[i]; + } + } + } else if (igpu_count != 0) { + for (size_t i = 0; i < igpu_count; ++i) { + if (dev_mem_totals[i] > max_mem_size) { + dev_best_index = igpus[i]; + max_mem_size = dev_mem_totals[i]; + } + } + } + + PTK_DEBUG("best device index: %ld", dev_best_index); + PTK_DEBUG("device name: %s", dev_props[dev_best_index].deviceName); + + PTK_DEBUG("device type: %s", (dgpu_count != 0 ? "dgpu" : (igpu_count != 0 ? "igpu" : "probably cpu"))); + + PTK_DEBUG("total memory: %lu", dev_mem_totals[dev_best_index]); + + g_physical_dev = devs[dev_best_index]; + + return true; +} diff --git a/src/ptk_vk/physical_device.h b/src/ptk_vk/physical_device.h new file mode 100644 index 0000000..2e1fb25 --- /dev/null +++ b/src/ptk_vk/physical_device.h @@ -0,0 +1,14 @@ +// Copyright (jacekpoz 2024). Licensed under the EUPL-1.2 or later. + +#ifndef PTK_PTK_VK_PHYSICAL_DEVICE_H_ +#define PTK_PTK_VK_PHYSICAL_DEVICE_H_ + +#include + +#include + +extern VkPhysicalDevice g_physical_dev; + +bool vk_select_physical_dev(void); + +#endif // PTK_PTK_VK_PHYSICAL_DEVICE_H_ diff --git a/src/ptk_vk/pipeline.c b/src/ptk_vk/pipeline.c new file mode 100644 index 0000000..a72059d --- /dev/null +++ b/src/ptk_vk/pipeline.c @@ -0,0 +1,339 @@ +// Copyright (jacekpoz 2024). Licensed under the EUPL-1.2 or later. + +#include "ptk_vk/pipeline.h" + +#include "ptk_vk/components.h" +#include "ptk_vk/descriptors.h" +#include "ptk_vk/device.h" +#include "ptk_vk/image.h" +#include "ptk_vk/render_pass.h" +#include "ptk_vk/swapchain.h" +#include "ptk_vk/utils.h" + +#include "ptk_array.h" +#include "ptk_option.h" +#include "ptk_log.h" + +#include "errno.h" +#include "stdio.h" + +VkPipeline g_pipeline; +VkPipelineLayout g_pipeline_layout; + +PTK_OPTION_DEFINE(VkShaderModule); +PTK_ARRAY_DEFINE(VkVertexInputAttributeDescription); + +static const VkVertexInputBindingDescription m_vertex_binding_description = { + .binding = 0, + .stride = sizeof(Vertex), + .inputRate = VK_VERTEX_INPUT_RATE_VERTEX, +}; + +static PTK_ARRAY(VkVertexInputAttributeDescription) m_vertex_attribute_descriptions = PTK_ARRAY_NEW(VkVertexInputAttributeDescription, { + (VkVertexInputAttributeDescription){ + .location = 0, + .binding = 0, + .format = VK_FORMAT_R32G32_SFLOAT, + .offset = offsetof(Vertex, pos), + }, + (VkVertexInputAttributeDescription){ + .location = 1, + .binding = 0, + .format = VK_FORMAT_R32G32B32_SFLOAT, + .offset = offsetof(Vertex, color), + }, + (VkVertexInputAttributeDescription){ + .location = 2, + .binding = 0, + .format = VK_FORMAT_R32_SINT, + .offset = offsetof(Vertex, shape_type), + }, + (VkVertexInputAttributeDescription){ + .location = 3, + .binding = 0, + .format = VK_FORMAT_R32G32_SFLOAT, + .offset = offsetof(Vertex, uv), + }, +}); + + +PTK_STRING read_spv(const char *filename) { + errno = 0; + FILE *file = fopen(filename, "r"); + + if (errno != 0) { + switch (errno) { + case EACCES: PTK_ERR("insufficient permissions to open %s", filename); break; + default: PTK_ERR("unknown error while reading %s", filename); break; + } + return PTK_LIST_NEW(char, 0); + } + + PTK_STRING buffer; + + fseek(file, 0L, SEEK_END); + buffer.allocated = ftell(file); + rewind(file); + + buffer = PTK_STRING_NEW(buffer.allocated); + + const size_t items_read = fread(buffer.data, sizeof(char), buffer.allocated, file); + + if (items_read != buffer.allocated) { + if (feof(file)) { + PTK_ERR("unexpected end of file while reading %s", filename); + } else if (ferror(file)) { + PTK_ERR("error reading %s", filename); + } + return PTK_STRING_NEW(0); + } + + PTK_LIST_FILLED(buffer); + + fclose(file); + + return buffer; +} + +PTK_OPTION(VkShaderModule) create_shader_module(VkDevice dev, const PTK_STRING code) { + VkShaderModule shader_module; + + VK_TRY(PTK_OPTION_NONE(VkShaderModule), + vkCreateShaderModule( + dev, + &(VkShaderModuleCreateInfo){ + .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, + .pNext = NULL, + .flags = 0, + .codeSize = code.size, + .pCode = (const uint32_t *)code.data, + }, + NULL, + &shader_module + ) + ); + + return PTK_OPTION_SOME(VkShaderModule, shader_module); +} + +bool vk_create_pipeline(void) { + const PTK_STRING vert_shader_code = read_spv("target/shaders/shader.vert.spv"); + const PTK_STRING frag_shader_code = read_spv("target/shaders/shader.frag.spv"); + + const PTK_OPTION(VkShaderModule) vert_shader_module = create_shader_module(g_dev, vert_shader_code); + + if (!vert_shader_module.exists) { + PTK_ERR("failed creating vert shader module"); + return false; + } + + const PTK_OPTION(VkShaderModule) frag_shader_module = create_shader_module(g_dev, frag_shader_code); + + if (!frag_shader_module.exists) { + PTK_ERR("failed creating frag shader module"); + return false; + } + + const VkSpecializationInfo spec_info = { + .mapEntryCount = 3, + .pMapEntries = (VkSpecializationMapEntry []){ + (VkSpecializationMapEntry){ + .constantID = 0, + .offset = 0, + .size = sizeof(int), + }, + (VkSpecializationMapEntry){ + .constantID = 1, + .offset = sizeof(int), + .size = sizeof(int), + }, + (VkSpecializationMapEntry){ + .constantID = 2, + .offset = 2 * sizeof(int), + .size = sizeof(int), + }, + }, + .dataSize = 3 * sizeof(int), + .pData = (int []){ + PTK_COMPONENT_TYPE_ELLIPSE, + PTK_COMPONENT_TYPE_IMAGE, + g_max_images + }, + }; + + const VkPipelineShaderStageCreateInfo shader_stages[] = { + (VkPipelineShaderStageCreateInfo){ + .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + .pNext = NULL, + .flags = 0, + .stage = VK_SHADER_STAGE_VERTEX_BIT, + .module = vert_shader_module.value, + .pName = "main", + .pSpecializationInfo = NULL, + }, + (VkPipelineShaderStageCreateInfo){ + .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + .pNext = NULL, + .flags = 0, + .stage = VK_SHADER_STAGE_FRAGMENT_BIT, + .module = frag_shader_module.value, + .pName = "main", + .pSpecializationInfo = &spec_info, + }, + }; + + const VkPipelineVertexInputStateCreateInfo vertex_input_info = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, + .pNext = NULL, + .flags = 0, + .vertexBindingDescriptionCount = 1, + .pVertexBindingDescriptions = &m_vertex_binding_description, + .vertexAttributeDescriptionCount = m_vertex_attribute_descriptions.size, + .pVertexAttributeDescriptions = m_vertex_attribute_descriptions.data, + }; + + const VkPipelineDynamicStateCreateInfo dynamic_state = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, + .pNext = NULL, + .flags = 0, + .dynamicStateCount = 0, + .pDynamicStates = NULL, + }; + + const VkPipelineInputAssemblyStateCreateInfo input_assembly = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, + .pNext = NULL, + .flags = 0, + .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, + .primitiveRestartEnable = VK_FALSE, + }; + + const VkPipelineViewportStateCreateInfo viewport_state = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, + .pNext = NULL, + .flags = 0, + .viewportCount = 1, + .pViewports = &(VkViewport){ + .x = 0.0f, + .y = 0.0f, + .width = (float) g_swapchain_extent.width, + .height = (float) g_swapchain_extent.height, + .minDepth = 0.0f, + .maxDepth = 1.0f, + }, + .scissorCount = 1, + .pScissors = &(VkRect2D){ + .offset = (VkOffset2D){ + .x = 0, + .y = 0, + }, + .extent = g_swapchain_extent, + }, + }; + + const VkPipelineRasterizationStateCreateInfo rasterizer = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, + .pNext = NULL, + .flags = 0, + .depthClampEnable = VK_FALSE, + .rasterizerDiscardEnable = VK_FALSE, + .polygonMode = VK_POLYGON_MODE_FILL, + .cullMode = VK_CULL_MODE_NONE, + .frontFace = VK_FRONT_FACE_CLOCKWISE, + .depthBiasEnable = VK_FALSE, + .depthBiasConstantFactor = 0.0f, + .depthBiasClamp = 0.0f, + .depthBiasSlopeFactor = 0.0f, + .lineWidth = 1.0f, + }; + + const VkPipelineMultisampleStateCreateInfo multisampling = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, + .pNext = NULL, + .flags = 0, + .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT, + .sampleShadingEnable = VK_FALSE, + .minSampleShading = 1.0f, + .pSampleMask = NULL, + .alphaToCoverageEnable = VK_FALSE, + .alphaToOneEnable = VK_FALSE, + }; + + const VkPipelineColorBlendAttachmentState color_blend_attachment = { + .blendEnable = VK_TRUE, + .srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA, + .dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, + .colorBlendOp = VK_BLEND_OP_ADD, + .srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE, + .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, + .alphaBlendOp = VK_BLEND_OP_ADD, + .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, + }; + + const VkPipelineColorBlendStateCreateInfo color_blending = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, + .pNext = NULL, + .flags = 0, + .logicOpEnable = VK_FALSE, + .logicOp = VK_LOGIC_OP_COPY, + .attachmentCount = 1, + .pAttachments = &color_blend_attachment, + .blendConstants[0] = 0.0f, + .blendConstants[1] = 0.0f, + .blendConstants[2] = 0.0f, + .blendConstants[3] = 0.0f, + }; + + VK_TRY(false, + vkCreatePipelineLayout( + g_dev, + &(VkPipelineLayoutCreateInfo){ + .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, + .pNext = NULL, + .flags = 0, + .setLayoutCount = 1, + .pSetLayouts = &g_descriptor_set_layout, + .pushConstantRangeCount = 0, + .pPushConstantRanges = NULL, + }, + NULL, + &g_pipeline_layout + ) + ); + + VK_TRY(false, + vkCreateGraphicsPipelines( + g_dev, + VK_NULL_HANDLE, + 1, + &(VkGraphicsPipelineCreateInfo){ + .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, + .pNext = NULL, + .flags = 0, + .stageCount = 2, + .pStages = shader_stages, + .pVertexInputState = &vertex_input_info, + .pInputAssemblyState = &input_assembly, + .pTessellationState = NULL, + .pViewportState = &viewport_state, + .pRasterizationState = &rasterizer, + .pMultisampleState = &multisampling, + .pDepthStencilState = NULL, + .pColorBlendState = &color_blending, + .pDynamicState = &dynamic_state, + .layout = g_pipeline_layout, + .renderPass = g_render_pass, + .subpass = 0, + .basePipelineHandle = VK_NULL_HANDLE, + .basePipelineIndex = -1, + }, + NULL, + &g_pipeline + ) + ); + + vkDestroyShaderModule(g_dev, vert_shader_module.value, NULL); + vkDestroyShaderModule(g_dev, frag_shader_module.value, NULL); + + return true; +} diff --git a/src/ptk_vk/pipeline.h b/src/ptk_vk/pipeline.h new file mode 100644 index 0000000..e3b9441 --- /dev/null +++ b/src/ptk_vk/pipeline.h @@ -0,0 +1,15 @@ +// Copyright (jacekpoz 2024). Licensed under the EUPL-1.2 or later. + +#ifndef PTK_PTK_VK_PIPELINE_H_ +#define PTK_PTK_VK_PIPELINE_H_ + +#include + +#include + +extern VkPipeline g_pipeline; +extern VkPipelineLayout g_pipeline_layout; + +bool vk_create_pipeline(void); + +#endif // PTK_PTK_VK_PIPELINE_H_ diff --git a/src/ptk_vk/render_pass.c b/src/ptk_vk/render_pass.c new file mode 100644 index 0000000..dfa848c --- /dev/null +++ b/src/ptk_vk/render_pass.c @@ -0,0 +1,64 @@ +// Copyright (jacekpoz 2024). Licensed under the EUPL-1.2 or later. + +#include "ptk_vk/render_pass.h" + +#include "ptk_vk/device.h" +#include "ptk_vk/swapchain.h" +#include "ptk_vk/utils.h" + +VkRenderPass g_render_pass; + +bool vk_create_render_pass(void) { + VK_TRY(false, + vkCreateRenderPass( + g_dev, + &(VkRenderPassCreateInfo){ + .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, + .pNext = NULL, + .flags = 0, + .attachmentCount = 1, + .pAttachments = &(VkAttachmentDescription){ + .flags = 0, + .format = g_swapchain_image_format, + .samples = VK_SAMPLE_COUNT_1_BIT, + .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, + .storeOp = VK_ATTACHMENT_STORE_OP_STORE, + .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, + .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, + .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, + }, + .subpassCount = 1, + .pSubpasses = &(VkSubpassDescription){ + .flags = 0, + .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, + .inputAttachmentCount = 0, + .pInputAttachments = NULL, + .colorAttachmentCount = 1, + .pColorAttachments = &(VkAttachmentReference){ + .attachment = 0, + .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + }, + .pResolveAttachments = NULL, + .pDepthStencilAttachment = NULL, + .preserveAttachmentCount = 0, + .pPreserveAttachments = NULL, + }, + .dependencyCount = 1, + .pDependencies = &(VkSubpassDependency){ + .srcSubpass = VK_SUBPASS_EXTERNAL, + .dstSubpass = 0, + .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + .srcAccessMask = 0, + .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + .dependencyFlags = 0, + }, + }, + NULL, + &g_render_pass + ) + ); + + return true; +} diff --git a/src/ptk_vk/render_pass.h b/src/ptk_vk/render_pass.h new file mode 100644 index 0000000..d811129 --- /dev/null +++ b/src/ptk_vk/render_pass.h @@ -0,0 +1,14 @@ +// Copyright (jacekpoz 2024). Licensed under the EUPL-1.2 or later. + +#ifndef PTK_PTK_VK_RENDER_PASS_H_ +#define PTK_PTK_VK_RENDER_PASS_H_ + +#include + +#include + +extern VkRenderPass g_render_pass; + +bool vk_create_render_pass(void); + +#endif // PTK_PTK_VK_RENDER_PASS_H_ diff --git a/src/ptk_vk/swapchain.c b/src/ptk_vk/swapchain.c new file mode 100644 index 0000000..0208f1f --- /dev/null +++ b/src/ptk_vk/swapchain.c @@ -0,0 +1,295 @@ +// Copyright (jacekpoz 2024). Licensed under the EUPL-1.2 or later. + +#include "ptk_vk/swapchain.h" + +#include "ptk_vk/backend.h" +#include "ptk_vk/device.h" +#include "ptk_vk/physical_device.h" +#include "ptk_vk/render_pass.h" +#include "ptk_vk/utils.h" + +#include "ptk_list.h" + +#ifndef GLFW_INCLUDE_VULKAN +#define GLFW_INCLUDE_VULKAN +#endif +#include + +PTK_LIST_DEFINE(VkImage); +PTK_LIST_DEFINE(VkImageView); + +static PTK_LIST(VkImage) m_swapchain_images; +static PTK_LIST(VkImageView) m_swapchain_image_views; + +VkSwapchainKHR g_swapchain; +VkFormat g_swapchain_image_format; +VkExtent2D g_swapchain_extent; +PTK_LIST(VkFramebuffer) g_swapchain_framebuffers; + +VkSurfaceFormatKHR select_swap_surface_format(const PTK_LIST(VkSurfaceFormatKHR) available_formats) { + PTK_LIST_FOR_EACH(const VkSurfaceFormatKHR, available_formats, current_format, { + if (current_format.format == VK_FORMAT_B8G8R8A8_SRGB && current_format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { + return current_format; + } + }) + + return available_formats.data[0]; +} + +VkPresentModeKHR select_swap_present_mode(const PTK_LIST(VkPresentModeKHR) available_present_modes) { + PTK_LIST_FOR_EACH(const VkPresentModeKHR, available_present_modes, current_present_mode, { + if (current_present_mode == VK_PRESENT_MODE_MAILBOX_KHR) { + return current_present_mode; + } + }) + + return VK_PRESENT_MODE_FIFO_KHR; +} + +VkExtent2D select_swap_extent(const VkSurfaceCapabilitiesKHR capabilities, GLFWwindow *window) { + if (capabilities.currentExtent.width != UINT32_MAX) { + return capabilities.currentExtent; + } + + int width, height; + + glfwGetFramebufferSize(window, &width, &height); + + VkExtent2D actual_extent = { + .width = (uint32_t)width, + .height = (uint32_t)height, + }; + + if (actual_extent.width < capabilities.minImageExtent.width) { + actual_extent.width = capabilities.minImageExtent.width; + } else if (actual_extent.width > capabilities.maxImageExtent.width) { + actual_extent.width = capabilities.maxImageExtent.width; + } + + if (actual_extent.height < capabilities.minImageExtent.height) { + actual_extent.height = capabilities.minImageExtent.height; + } else if (actual_extent.height > capabilities.maxImageExtent.height) { + actual_extent.height = capabilities.maxImageExtent.height; + } + + return actual_extent; +} + +SwapchainSupportInfo query_swapchain_support(const VkPhysicalDevice dev, const VkSurfaceKHR surface) { + SwapchainSupportInfo info; + + vkGetPhysicalDeviceSurfaceCapabilitiesKHR(dev, surface, &info.capabilities); + + const uint32_t width = info.capabilities.currentExtent.width; + const uint32_t height = info.capabilities.currentExtent.height; + + if ( + info.capabilities.currentTransform & VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR + || info.capabilities.currentTransform & VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR + ) { + info.capabilities.currentExtent.height = width; + info.capabilities.currentExtent.width = height; + } + + vkGetPhysicalDeviceSurfaceFormatsKHR(dev, surface, &info.formats.allocated, NULL); + + if (info.formats.allocated != 0) { + info.formats = PTK_LIST_NEW(VkSurfaceFormatKHR, info.formats.allocated); + vkGetPhysicalDeviceSurfaceFormatsKHR(dev, surface, &info.formats.allocated, info.formats.data); + info.formats.size = info.formats.allocated; + } + + vkGetPhysicalDeviceSurfacePresentModesKHR(dev, surface, &info.present_modes.allocated, NULL); + + if (info.present_modes.allocated != 0) { + info.present_modes = PTK_LIST_NEW(VkPresentModeKHR, info.present_modes.allocated); + vkGetPhysicalDeviceSurfacePresentModesKHR(dev, surface, &info.present_modes.allocated, info.present_modes.data); + info.present_modes.size = info.present_modes.allocated; + } + + return info; +} + +bool vk_create_swapchain(void) { + const SwapchainSupportInfo swapchain_support = query_swapchain_support(g_physical_dev, g_surface); + + const VkSurfaceFormatKHR surface_format = select_swap_surface_format(swapchain_support.formats); + const VkPresentModeKHR present_mode = select_swap_present_mode(swapchain_support.present_modes); + const VkExtent2D extent = select_swap_extent(swapchain_support.capabilities, g_window); + + uint32_t image_count = swapchain_support.capabilities.minImageCount + 1; + + if (swapchain_support.capabilities.maxImageCount > 0 && image_count > swapchain_support.capabilities.maxImageCount) { + image_count = swapchain_support.capabilities.maxImageCount; + } + + const bool queue_families_differ = g_queue_family_indices.graphics.value != g_queue_family_indices.present.value; + + VK_TRY(false, + vkCreateSwapchainKHR( + g_dev, + &(VkSwapchainCreateInfoKHR){ + .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, + .pNext = NULL, + .flags = 0, + .surface = g_surface, + .minImageCount = image_count, + .imageFormat = surface_format.format, + .imageColorSpace = surface_format.colorSpace, + .imageExtent = extent, + .imageArrayLayers = 1, + .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, + .imageSharingMode = + queue_families_differ + ? VK_SHARING_MODE_CONCURRENT + : VK_SHARING_MODE_EXCLUSIVE, + .queueFamilyIndexCount = + queue_families_differ + ? 2 + : 0, + .pQueueFamilyIndices = + queue_families_differ + ? (const uint32_t []){ + g_queue_family_indices.graphics.value, + g_queue_family_indices.present.value + } + : NULL, + .preTransform = swapchain_support.capabilities.currentTransform, + .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, + .presentMode = present_mode, + .clipped = VK_TRUE, + .oldSwapchain = VK_NULL_HANDLE, + }, + NULL, + &g_swapchain + ) + ); + + vkGetSwapchainImagesKHR(g_dev, g_swapchain, &m_swapchain_images.allocated, NULL); + m_swapchain_images = PTK_LIST_NEW(VkImage, m_swapchain_images.allocated); + vkGetSwapchainImagesKHR(g_dev, g_swapchain, &m_swapchain_images.allocated, m_swapchain_images.data); + PTK_LIST_FILLED(m_swapchain_images); + + g_swapchain_image_format = surface_format.format; + g_swapchain_extent = extent; + + PTK_LIST_FREE(swapchain_support.formats); + PTK_LIST_FREE(swapchain_support.present_modes); + + return true; +} + +bool vk_create_image_views(void) { + m_swapchain_image_views = PTK_LIST_NEW(VkImageView, m_swapchain_images.size); + + PTK_LIST_FOR_EACH_E(VkImage, m_swapchain_images, i, swapchain_image, { + VK_TRY(false, + vkCreateImageView( + g_dev, + &(VkImageViewCreateInfo){ + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .pNext = NULL, + .flags = 0, + .image = swapchain_image, + .viewType = VK_IMAGE_VIEW_TYPE_2D, + .format = g_swapchain_image_format, + .components = (VkComponentMapping){ + .r = VK_COMPONENT_SWIZZLE_IDENTITY, + .g = VK_COMPONENT_SWIZZLE_IDENTITY, + .b = VK_COMPONENT_SWIZZLE_IDENTITY, + .a = VK_COMPONENT_SWIZZLE_IDENTITY, + }, + .subresourceRange = (VkImageSubresourceRange){ + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + }, + }, + NULL, + &m_swapchain_image_views.data[i] + ) + ); + m_swapchain_image_views.size += 1; + }) + + return true; +} + +bool vk_recreate_swapchain(void) { + int width = 0, height = 0; + glfwGetFramebufferSize(g_window, &width, &height); + + while (width == 0 || height == 0) { + glfwGetFramebufferSize(g_window, &width, &height); + glfwWaitEvents(); + } + + vkDeviceWaitIdle(g_dev); + + vk_cleanup_swapchain(); + + if (!vk_create_swapchain()) { + PTK_ERR("failed creating new swapchain"); + return false; + } + + if (!vk_create_image_views()) { + PTK_ERR("failed creating new image views"); + return false; + } + + if (!vk_create_framebuffers()) { + PTK_ERR("failed creating new framebuffers"); + return false; + } + + return true; +} + +bool vk_create_framebuffers(void) { + g_swapchain_framebuffers = PTK_LIST_NEW(VkFramebuffer, m_swapchain_image_views.size); + + VkFramebuffer fb; + + PTK_LIST_FOR_EACH(VkImageView, m_swapchain_image_views, swapchain_image_view, { + VK_TRY(false, + vkCreateFramebuffer( + g_dev, + &(VkFramebufferCreateInfo){ + .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, + .pNext = NULL, + .flags = 0, + .renderPass = g_render_pass, + .attachmentCount = 1, + .pAttachments = &swapchain_image_view, + .width = g_swapchain_extent.width, + .height = g_swapchain_extent.height, + .layers = 1, + }, + NULL, + &fb + ) + ); + PTK_LIST_ADD(VkFramebuffer, g_swapchain_framebuffers, fb); + }) + + return true; +} + + +void vk_cleanup_swapchain(void) { + PTK_LIST_FOR_EACH(VkFramebuffer, g_swapchain_framebuffers, fb, { + vkDestroyFramebuffer(g_dev, fb, NULL); + }) + PTK_LIST_FREE(g_swapchain_framebuffers); + + PTK_LIST_FREE(m_swapchain_images); + PTK_LIST_FOR_EACH(VkImageView, m_swapchain_image_views, iv, { + vkDestroyImageView(g_dev, iv, NULL); + }) + PTK_LIST_FREE(m_swapchain_image_views); + + vkDestroySwapchainKHR(g_dev, g_swapchain, NULL); +} diff --git a/src/ptk_vk/swapchain.h b/src/ptk_vk/swapchain.h new file mode 100644 index 0000000..43286f1 --- /dev/null +++ b/src/ptk_vk/swapchain.h @@ -0,0 +1,39 @@ +// Copyright (jacekpoz 2024). Licensed under the EUPL-1.2 or later. + +#ifndef PTK_PTK_VK_SWAPCHAIN_H_ +#define PTK_PTK_VK_SWAPCHAIN_H_ + +#include "ptk_list.h" + +#include +#include + +PTK_LIST_DEFINE(VkFramebuffer); + +PTK_LIST_DEFINE(VkSurfaceFormatKHR); +PTK_LIST_DEFINE(VkPresentModeKHR); + +extern VkSwapchainKHR g_swapchain; +extern VkFormat g_swapchain_image_format; +extern VkExtent2D g_swapchain_extent; +extern PTK_LIST(VkFramebuffer) g_swapchain_framebuffers; + +typedef struct { + VkSurfaceCapabilitiesKHR capabilities; + PTK_LIST(VkSurfaceFormatKHR) formats; + PTK_LIST(VkPresentModeKHR) present_modes; +} SwapchainSupportInfo; + +SwapchainSupportInfo query_swapchain_support(const VkPhysicalDevice dev, const VkSurfaceKHR surface); + +bool vk_create_swapchain(void); + +bool vk_create_image_views(void); + +bool vk_recreate_swapchain(void); + +bool vk_create_framebuffers(void); + +void vk_cleanup_swapchain(void); + +#endif // PTK_PTK_VK_SWAPCHAIN_H_ diff --git a/src/ptk_vk/sync_objects.c b/src/ptk_vk/sync_objects.c new file mode 100644 index 0000000..4c21942 --- /dev/null +++ b/src/ptk_vk/sync_objects.c @@ -0,0 +1,49 @@ +// Copyright (jacekpoz 2024). Licensed under the EUPL-1.2 or later. + +#include "ptk_vk/sync_objects.h" + +#include "ptk_vk/device.h" +#include "ptk_vk/utils.h" + +PTK_LIST(VkSemaphore) g_image_available_semaphores; +PTK_LIST(VkSemaphore) g_render_finished_semaphores; +PTK_LIST(VkFence) g_in_flight_fences; + +const size_t g_max_frames_in_flight = 2; + +bool vk_create_sync_objects(void) { + g_image_available_semaphores = PTK_LIST_NEW(VkSemaphore, g_max_frames_in_flight); + g_render_finished_semaphores = PTK_LIST_NEW(VkSemaphore, g_max_frames_in_flight); + g_in_flight_fences = PTK_LIST_NEW(VkFence, g_max_frames_in_flight); + + const VkSemaphoreCreateInfo semaphore_info = { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, + .pNext = NULL, + .flags = 0, + }; + + for (size_t i = 0; i < g_max_frames_in_flight; ++i) { + VK_TRY(false, + vkCreateSemaphore(g_dev, &semaphore_info, NULL, &g_image_available_semaphores.data[i]) + ); + + VK_TRY(false, + vkCreateSemaphore(g_dev, &semaphore_info, NULL, &g_render_finished_semaphores.data[i]) + ); + + VK_TRY(false, + vkCreateFence( + g_dev, + &(VkFenceCreateInfo){ + .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, + .pNext = NULL, + .flags = VK_FENCE_CREATE_SIGNALED_BIT, + }, + NULL, + &g_in_flight_fences.data[i] + ) + ); + } + + return true; +} diff --git a/src/ptk_vk/sync_objects.h b/src/ptk_vk/sync_objects.h new file mode 100644 index 0000000..1c38d79 --- /dev/null +++ b/src/ptk_vk/sync_objects.h @@ -0,0 +1,21 @@ +// Copyright (jacekpoz 2024). Licensed under the EUPL-1.2 or later. + +#ifndef PTK_PTK_VK_SYNC_OBJECTS_H_ +#define PTK_PTK_VK_SYNC_OBJECTS_H_ + +#include "ptk_list.h" + +#include + +PTK_LIST_DEFINE(VkSemaphore); +PTK_LIST_DEFINE(VkFence); + +extern PTK_LIST(VkSemaphore) g_image_available_semaphores; +extern PTK_LIST(VkSemaphore) g_render_finished_semaphores; +extern PTK_LIST(VkFence) g_in_flight_fences; + +extern const size_t g_max_frames_in_flight; + +bool vk_create_sync_objects(void); + +#endif // PTK_PTK_VK_SYNC_OBJECTS_H_ diff --git a/src/ptk_vk/uniform_buffers.c b/src/ptk_vk/uniform_buffers.c new file mode 100644 index 0000000..0373728 --- /dev/null +++ b/src/ptk_vk/uniform_buffers.c @@ -0,0 +1,44 @@ +// Copyright (jacekpoz 2024). Licensed under the EUPL-1.2 or later. + +#include "ptk_vk/uniform_buffers.h" + +#include "ptk_vk/buffer.h" +#include "ptk_vk/device.h" +#include "ptk_vk/physical_device.h" +#include "ptk_vk/sync_objects.h" + +#include "ptk_log.h" + +PTK_LIST(VkBuffer) g_uniform_buffers; +PTK_LIST(VkDeviceMemory) g_uniform_buffer_memories; +PTK_LIST(voidptr) g_uniform_buffers_mapped; + +bool vk_create_uniform_buffers(void) { + const VkDeviceSize buffer_size = sizeof(UniformBufferObject); + + g_uniform_buffers = PTK_LIST_NEW(VkBuffer, g_max_frames_in_flight); + g_uniform_buffer_memories = PTK_LIST_NEW(VkDeviceMemory, g_max_frames_in_flight); + g_uniform_buffers_mapped = PTK_LIST_NEW(voidptr, g_max_frames_in_flight); + + for (size_t i = 0; i < g_max_frames_in_flight; ++i) { + PTK_OPTION(BufferStuff) uniform_buffer_stuff_opt = vk_create_buffer( + g_dev, + g_physical_dev, + buffer_size, + VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT + ); + + if (!uniform_buffer_stuff_opt.exists) { + PTK_ERR("failed creating index buffer"); + return false; + } + + g_uniform_buffers.data[i] = uniform_buffer_stuff_opt.value.buffer; + g_uniform_buffer_memories.data[i] = uniform_buffer_stuff_opt.value.buffer_memory; + + vkMapMemory(g_dev, g_uniform_buffer_memories.data[i], 0, buffer_size, 0, &g_uniform_buffers_mapped.data[i]); + } + + return true; +} diff --git a/src/ptk_vk/uniform_buffers.h b/src/ptk_vk/uniform_buffers.h new file mode 100644 index 0000000..cae2f8d --- /dev/null +++ b/src/ptk_vk/uniform_buffers.h @@ -0,0 +1,27 @@ +// Copyright (jacekpoz 2024). Licensed under the EUPL-1.2 or later. + +#ifndef PTK_PTK_VK_UNIFORM_BUFFERS_H_ +#define PTK_PTK_VK_UNIFORM_BUFFERS_H_ + +#include "ptk_list.h" +#include "ptk_vec.h" + +#include + +typedef struct { + PtkSize initial_window_size; + PtkSize window_size; +} UniformBufferObject; + +PTK_LIST_DEFINE(VkBuffer); +PTK_LIST_DEFINE(VkDeviceMemory); +typedef void *voidptr; +PTK_LIST_DEFINE(voidptr); + +extern PTK_LIST(VkBuffer) g_uniform_buffers; +extern PTK_LIST(VkDeviceMemory) g_uniform_buffer_memories; +extern PTK_LIST(voidptr) g_uniform_buffers_mapped; + +bool vk_create_uniform_buffers(void); + +#endif // PTK_PTK_VK_UNIFORM_BUFFERS_H_ diff --git a/src/ptk_vk/utils.c b/src/ptk_vk/utils.c index 8d1fa99..6c349b5 100644 --- a/src/ptk_vk/utils.c +++ b/src/ptk_vk/utils.c @@ -1,6 +1,6 @@ // Copyright (jacekpoz 2024). Licensed under the EUPL-1.2 or later. -#include +#include "ptk_vk/utils.h" const char *vk_result_string(VkResult res) { const char *str; diff --git a/src/ptk_vk/utils.h b/src/ptk_vk/utils.h index df9e214..2f2b5c5 100644 --- a/src/ptk_vk/utils.h +++ b/src/ptk_vk/utils.h @@ -3,8 +3,9 @@ #ifndef PTK_PTK_VK_UTILS_H_ #define PTK_PTK_VK_UTILS_H_ +#include "ptk_log.h" + #include -#include const char *vk_result_string(VkResult res); diff --git a/test/list.c b/test/list.c index 6d2d355..3ac6092 100644 --- a/test/list.c +++ b/test/list.c @@ -1,7 +1,8 @@ // Copyright (jacekpoz 2024). Licensed under the EUPL-1.2 or later. -#include -#include +#include "test.h" + +#include "ptk_list.h" PTK_LIST_DEFINE(uint32_t);