diff --git a/graphics/src/detail/graphics.h b/graphics/src/detail/graphics.h index 08bc5a9..4f167f2 100644 --- a/graphics/src/detail/graphics.h +++ b/graphics/src/detail/graphics.h @@ -10,6 +10,7 @@ #include #define ACTIVE_VALIDATION_LAYER true +#define MAX_FRAMES_IN_FLIGHT 2 class GLFWwindow; @@ -32,33 +33,34 @@ namespace deerith { extern GraphicsConfig graphics_config; extern GLFWwindow* window; + extern bool frame_buffer_resize; // Vulkan Specific extern VkInstance instance; extern const std::vector validation_layers; - extern const std::vector device_extensions; + extern VkSurfaceKHR surface; extern VkPhysicalDevice physical_device; extern QueueFamilyIndices queue_family_indices; extern SwapChainSupportDetails swap_chain_support_details; + extern const std::vector device_extensions; extern VkDevice device; extern VkQueue graphics_queue; extern VkQueue pressent_queue; - extern VkSurfaceKHR surface; extern VkSwapchainKHR swap_chain; extern std::vector swap_chain_images; extern VkFormat swap_chain_image_format; extern VkExtent2D swap_chain_extent; extern std::vector swap_chain_image_views; - extern VkPipelineLayout pipeline_layout; extern VkRenderPass render_pass; + extern VkPipelineLayout pipeline_layout; extern VkPipeline graphics_pipeline; extern std::vector swap_chain_frame_buffers; extern VkCommandPool command_pool; - extern VkCommandBuffer command_buffer; - - extern VkSemaphore image_available_semaphore; - extern VkSemaphore render_finished_semaphore; - extern VkFence in_flight_fence; + extern std::vector command_buffers; + extern std::vector image_available_semaphores; + extern std::vector render_finished_semaphores; + extern std::vector in_flight_fences; + extern uint32_t current_frame; void init_glfw(); void shutdown_glfw(); @@ -69,7 +71,7 @@ namespace deerith { void create_vulkan_instance(); void create_vulkan_validation_layer(); void create_vulkan_surface(); - void create_vulkan_phisical_device(); + void pick_vulkan_phisical_device(); void create_vulkan_logical_device(); void create_vulkan_swap_chain(); void create_vulkan_image_view(); @@ -79,6 +81,8 @@ namespace deerith { void create_vulkan_command_buffer(); void create_vulkan_sync_objects(); + void vulkan_clean_swap_chain(); + void vulkan_recreate_swap_chain(); void vulkan_test_loop(); QueueFamilyIndices find_queue_families(VkPhysicalDevice device); diff --git a/graphics/src/glfw.cpp b/graphics/src/glfw.cpp index d9e5383..94049a1 100644 --- a/graphics/src/glfw.cpp +++ b/graphics/src/glfw.cpp @@ -3,15 +3,19 @@ #include "detail/graphics.h" namespace deerith { + namespace graphics { + void frame_buffer_resize_callback(GLFWwindow* window, int width, int height); + } + void graphics::init_glfw() { deerith_graphics_trace("Initializing glfw"); glfwInit(); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); - glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); window = glfwCreateWindow(graphics_config.window_width, graphics_config.window_height, graphics_config.window_title, nullptr, nullptr); + glfwSetFramebufferSizeCallback(window, frame_buffer_resize_callback); if (!window) { deerith_graphics_error("failed to init glfw window"); @@ -24,4 +28,13 @@ namespace deerith { glfwDestroyWindow(window); glfwTerminate(); } + + void graphics::frame_buffer_resize_callback(GLFWwindow* window, int width, int height) { + deerith_graphics_trace("called frame buffer resize"); + + graphics_config.window_height = height; + graphics_config.window_width = width; + + frame_buffer_resize = true; + } } // namespace deerith \ No newline at end of file diff --git a/graphics/src/variables.cpp b/graphics/src/variables.cpp index 7d55259..23bcf2c 100644 --- a/graphics/src/variables.cpp +++ b/graphics/src/variables.cpp @@ -27,8 +27,10 @@ namespace deerith { VkPipeline graphics::graphics_pipeline; std::vector graphics::swap_chain_frame_buffers; VkCommandPool graphics::command_pool; - VkCommandBuffer graphics::command_buffer; - VkSemaphore graphics::image_available_semaphore; - VkSemaphore graphics::render_finished_semaphore; - VkFence graphics::in_flight_fence; + std::vector graphics::command_buffers; + std::vector graphics::image_available_semaphores; + std::vector graphics::render_finished_semaphores; + std::vector graphics::in_flight_fences; + uint32_t graphics::current_frame = 0; + bool graphics::frame_buffer_resize = false; } // namespace deerith \ No newline at end of file diff --git a/graphics/src/vulkan.cpp b/graphics/src/vulkan.cpp index 83d7a83..bfce36b 100644 --- a/graphics/src/vulkan.cpp +++ b/graphics/src/vulkan.cpp @@ -7,7 +7,7 @@ namespace deerith { create_vulkan_validation_layer(); create_vulkan_instance(); create_vulkan_surface(); - create_vulkan_phisical_device(); + pick_vulkan_phisical_device(); create_vulkan_logical_device(); create_vulkan_swap_chain(); create_vulkan_image_view(); @@ -20,25 +20,48 @@ namespace deerith { } void graphics::shutdown_vulkan() { - deerith_graphics_trace("Shutting Vulkan"); - + deerith_graphics_trace("Waiting for Vulkan device to finish"); vkDeviceWaitIdle(device); - vkDestroyCommandPool(device, command_pool, nullptr); - for (auto framebuffer : swap_chain_frame_buffers) { - vkDestroyFramebuffer(device, framebuffer, nullptr); + + deerith_graphics_trace("Shutting Vulkan"); + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { + vkDestroySemaphore(device, render_finished_semaphores[i], nullptr); + vkDestroySemaphore(device, image_available_semaphores[i], nullptr); + vkDestroyFence(device, in_flight_fences[i], nullptr); } + vkDestroyCommandPool(device, command_pool, nullptr); + + vulkan_clean_swap_chain(); + vkDestroyPipeline(device, graphics_pipeline, nullptr); vkDestroyRenderPass(device, render_pass, nullptr); vkDestroyPipelineLayout(device, pipeline_layout, nullptr); - for (auto image_view : swap_chain_image_views) { - vkDestroyImageView(device, image_view, nullptr); - } - - vkDestroySwapchainKHR(device, swap_chain, nullptr); vkDestroySurfaceKHR(instance, surface, nullptr); vkDestroyDevice(device, nullptr); vkDestroyInstance(instance, nullptr); } + + void graphics::vulkan_clean_swap_chain() { + for (auto framebuffer : swap_chain_frame_buffers) { + vkDestroyFramebuffer(device, framebuffer, nullptr); + } + + for (auto imageView : swap_chain_image_views) { + vkDestroyImageView(device, imageView, nullptr); + } + + vkDestroySwapchainKHR(device, swap_chain, nullptr); + } + + void graphics::vulkan_recreate_swap_chain() { + vkDeviceWaitIdle(device); + + vulkan_clean_swap_chain(); + + create_vulkan_swap_chain(); + create_vulkan_image_view(); + create_vulkan_frame_buffer(); + } } // namespace deerith \ No newline at end of file diff --git a/graphics/src/vulkan_frame_buffer.cpp b/graphics/src/vulkan_frame_buffer.cpp index 406fb3f..4401f04 100644 --- a/graphics/src/vulkan_frame_buffer.cpp +++ b/graphics/src/vulkan_frame_buffer.cpp @@ -17,7 +17,7 @@ namespace deerith { frame_buffer_info.layers = 1; if (vkCreateFramebuffer(device, &frame_buffer_info, nullptr, &swap_chain_frame_buffers[i]) != VK_SUCCESS) { - deerith_graphics_error("failed to create framebuffer!"); + deerith_graphics_error("failed to create frame_buffer!"); } } } diff --git a/graphics/src/vulkan_phisical_device.cpp b/graphics/src/vulkan_phisical_device.cpp index bb55170..e361cdb 100644 --- a/graphics/src/vulkan_phisical_device.cpp +++ b/graphics/src/vulkan_phisical_device.cpp @@ -8,19 +8,19 @@ namespace deerith { bool check_device_extension_support(VkPhysicalDevice device); } // namespace graphics - void graphics::create_vulkan_phisical_device() { - deerith_graphics_trace("Initializing Vulkan phisical device"); + void graphics::pick_vulkan_phisical_device() { + deerith_graphics_trace("Choosing Vulkan phisical device"); - uint32_t deviceCount = 0; - vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr); + uint32_t device_count = 0; + vkEnumeratePhysicalDevices(instance, &device_count, nullptr); - if (deviceCount == 0) { + if (device_count == 0) { deerith_graphics_error("failed to find GPUs with Vulkan support!"); return; } - std::vector devices(deviceCount); - vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data()); + std::vector devices(device_count); + vkEnumeratePhysicalDevices(instance, &device_count, devices.data()); for (const auto& device : devices) { if (is_device_suitable(device)) { diff --git a/graphics/src/vulkan_presentation.cpp b/graphics/src/vulkan_presentation.cpp index 5e9a6db..f063784 100644 --- a/graphics/src/vulkan_presentation.cpp +++ b/graphics/src/vulkan_presentation.cpp @@ -2,7 +2,7 @@ namespace deerith { void graphics::create_vulkan_surface() { - deerith_graphics_trace("Initializing Vulkan pressentation/surface"); + deerith_graphics_trace("Initializing Vulkan surface"); if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) { deerith_graphics_error("failed to create window surface!"); diff --git a/graphics/src/vulkan_render_command.cpp b/graphics/src/vulkan_render_command.cpp index 1c5c96c..5c55a8b 100644 --- a/graphics/src/vulkan_render_command.cpp +++ b/graphics/src/vulkan_render_command.cpp @@ -13,13 +13,15 @@ namespace deerith { deerith_graphics_error("failed to create command pool!"); } + command_buffers.resize(MAX_FRAMES_IN_FLIGHT); + VkCommandBufferAllocateInfo alloc_info{}; alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; alloc_info.commandPool = command_pool; alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - alloc_info.commandBufferCount = 1; + alloc_info.commandBufferCount = MAX_FRAMES_IN_FLIGHT; - if (vkAllocateCommandBuffers(device, &alloc_info, &command_buffer) != VK_SUCCESS) { + if (vkAllocateCommandBuffers(device, &alloc_info, command_buffers.data()) != VK_SUCCESS) { deerith_graphics_error("failed to allocate command buffers!"); } } diff --git a/graphics/src/vulkan_render_pass.cpp b/graphics/src/vulkan_render_pass.cpp index dd87103..905b8c1 100644 --- a/graphics/src/vulkan_render_pass.cpp +++ b/graphics/src/vulkan_render_pass.cpp @@ -15,22 +15,22 @@ namespace deerith { color_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; color_attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - VkAttachmentReference color_attachmentRef{}; - color_attachmentRef.attachment = 0; - color_attachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + VkAttachmentReference color_attachment_ref{}; + color_attachment_ref.attachment = 0; + color_attachment_ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - VkSubpassDescription subpass{}; - subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + VkSubpassDescription sub_pass{}; + sub_pass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpass.colorAttachmentCount = 1; - subpass.pColorAttachments = &color_attachmentRef; + sub_pass.colorAttachmentCount = 1; + sub_pass.pColorAttachments = &color_attachment_ref; VkRenderPassCreateInfo render_pass_info{}; render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; render_pass_info.attachmentCount = 1; render_pass_info.pAttachments = &color_attachment; render_pass_info.subpassCount = 1; - render_pass_info.pSubpasses = &subpass; + render_pass_info.pSubpasses = &sub_pass; if (vkCreateRenderPass(device, &render_pass_info, nullptr, &render_pass) != VK_SUCCESS) { deerith_graphics_error("failed to create render pass!"); diff --git a/graphics/src/vulkan_rendering.cpp b/graphics/src/vulkan_rendering.cpp index c8e9598..2049357 100644 --- a/graphics/src/vulkan_rendering.cpp +++ b/graphics/src/vulkan_rendering.cpp @@ -13,6 +13,10 @@ namespace deerith { } void graphics::create_vulkan_sync_objects() { + image_available_semaphores.resize(MAX_FRAMES_IN_FLIGHT); + render_finished_semaphores.resize(MAX_FRAMES_IN_FLIGHT); + in_flight_fences.resize(MAX_FRAMES_IN_FLIGHT); + VkSemaphoreCreateInfo semaphoreInfo{}; semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; @@ -20,41 +24,52 @@ namespace deerith { fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; - if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &image_available_semaphore) != VK_SUCCESS || - vkCreateSemaphore(device, &semaphoreInfo, nullptr, &render_finished_semaphore) != VK_SUCCESS || - vkCreateFence(device, &fenceInfo, nullptr, &in_flight_fence) != VK_SUCCESS) { - deerith_graphics_error("failed to create semaphores!"); + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { + if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &image_available_semaphores[i]) != VK_SUCCESS || + vkCreateSemaphore(device, &semaphoreInfo, nullptr, &render_finished_semaphores[i]) != VK_SUCCESS || + vkCreateFence(device, &fenceInfo, nullptr, &in_flight_fences[i]) != VK_SUCCESS) { + + deerith_graphics_error("failed to create synchronization objects for a frame!"); + } } } void graphics::drawFrame() { - vkWaitForFences(device, 1, &in_flight_fence, VK_TRUE, UINT64_MAX); - vkResetFences(device, 1, &in_flight_fence); + vkWaitForFences(device, 1, &in_flight_fences[current_frame], VK_TRUE, UINT64_MAX); uint32_t image_index; - vkAcquireNextImageKHR(device, swap_chain, UINT64_MAX, image_available_semaphore, VK_NULL_HANDLE, &image_index); + VkResult result = vkAcquireNextImageKHR(device, swap_chain, UINT64_MAX, image_available_semaphores[current_frame], VK_NULL_HANDLE, &image_index); - vkResetCommandBuffer(command_buffer, 0); + if (result == VK_ERROR_OUT_OF_DATE_KHR) { + frame_buffer_resize = false; + vulkan_recreate_swap_chain(); + return; + } else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) { + deerith_graphics_error("failed to acquire swap chain image!"); + } - record_command_buffer(command_buffer, image_index); + vkResetFences(device, 1, &in_flight_fences[current_frame]); + + vkResetCommandBuffer(command_buffers[current_frame], 0); + record_command_buffer(command_buffers[current_frame], image_index); VkSubmitInfo submit_info{}; submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - VkSemaphore waitSemaphores[] = {image_available_semaphore}; + VkSemaphore waitSemaphores[] = {image_available_semaphores[current_frame]}; VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; submit_info.waitSemaphoreCount = 1; submit_info.pWaitSemaphores = waitSemaphores; submit_info.pWaitDstStageMask = waitStages; submit_info.commandBufferCount = 1; - submit_info.pCommandBuffers = &command_buffer; + submit_info.pCommandBuffers = &command_buffers[current_frame]; - VkSemaphore signalSemaphores[] = {render_finished_semaphore}; + VkSemaphore signalSemaphores[] = {render_finished_semaphores[current_frame]}; submit_info.signalSemaphoreCount = 1; submit_info.pSignalSemaphores = signalSemaphores; - if (vkQueueSubmit(graphics_queue, 1, &submit_info, in_flight_fence) != VK_SUCCESS) { + if (vkQueueSubmit(graphics_queue, 1, &submit_info, in_flight_fences[current_frame]) != VK_SUCCESS) { deerith_graphics_error("failed to submit draw command buffer!"); } @@ -78,8 +93,17 @@ namespace deerith { presentInfo.swapchainCount = 1; presentInfo.pSwapchains = swapChains; presentInfo.pImageIndices = &image_index; - presentInfo.pResults = nullptr; // Optional + presentInfo.pResults = nullptr; - vkQueuePresentKHR(pressent_queue, &presentInfo); + result = vkQueuePresentKHR(pressent_queue, &presentInfo); + + if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || frame_buffer_resize) { + vulkan_recreate_swap_chain(); + frame_buffer_resize = false; + } else if (result != VK_SUCCESS) { + deerith_graphics_error("failed to present swap chain image!"); + } + + current_frame = (current_frame + 1) % MAX_FRAMES_IN_FLIGHT; } } // namespace deerith \ No newline at end of file diff --git a/graphics/src/vulkan_swap_chain.cpp b/graphics/src/vulkan_swap_chain.cpp index 17b2f31..1730924 100644 --- a/graphics/src/vulkan_swap_chain.cpp +++ b/graphics/src/vulkan_swap_chain.cpp @@ -15,10 +15,10 @@ namespace deerith { SwapChainSupportDetails swap_chain_support = query_swap_chain_support(physical_device); VkSurfaceFormatKHR surface_format = choose_swap_surface_format(swap_chain_support.formats); - VkPresentModeKHR presentMode = choose_swap_present_mode(swap_chain_support.present_modes); + VkPresentModeKHR present_mode = choose_swap_present_mode(swap_chain_support.present_modes); VkExtent2D extent = choose_swap_extent(swap_chain_support.capabilities); - uint32_t image_count = swap_chain_support.capabilities.minImageCount; + uint32_t image_count = swap_chain_support.capabilities.minImageCount + 1; if (swap_chain_support.capabilities.maxImageCount > 0 && image_count > swap_chain_support.capabilities.maxImageCount) { image_count = swap_chain_support.capabilities.maxImageCount; } @@ -48,7 +48,7 @@ namespace deerith { create_info.preTransform = swap_chain_support.capabilities.currentTransform; create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; - create_info.presentMode = presentMode; + create_info.presentMode = present_mode; create_info.clipped = VK_TRUE; create_info.oldSwapchain = VK_NULL_HANDLE; diff --git a/studio/src/main.cpp b/studio/src/main.cpp index a213948..601aab5 100644 --- a/studio/src/main.cpp +++ b/studio/src/main.cpp @@ -10,8 +10,8 @@ int main() { graphics_config.window_title = "Deerith Studio"; deerith::graphics::init(graphics_config); - deerith::graphics::shutdown(); + deerith::engine::shutdown(); return 0; } \ No newline at end of file