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);