diff --git a/include/vulkan/basalt_swapchain.h b/include/vulkan/basalt_swapchain.h index 26e6b83..18129f3 100644 --- a/include/vulkan/basalt_swapchain.h +++ b/include/vulkan/basalt_swapchain.h @@ -18,9 +18,10 @@ namespace basalt const std::initializer_list allowed_present_modes); ~Swapchain(); + void recreate(VkRenderPass new_renderpass=VK_NULL_HANDLE); + void create_framebuffers(VkRenderPass render_pass); - void wait_and_reset(bool wait_all=VK_TRUE, u64 timeout=UINT64_MAX); - u32 acquire_next_image(VkFence fence=VK_NULL_HANDLE, u64 timeout=UINT64_MAX); + u32 wait_acquire_reset(bool wait_all=VK_TRUE, u64 timeout=UINT64_MAX, VkFence fence=VK_NULL_HANDLE); basalt::darray semaphores_image_available; basalt::darray semaphores_render_finished; @@ -36,5 +37,9 @@ namespace basalt u32 image_count = -1; u32 max_frames_in_flight = 2; u32 current_frame = 0; + bool framebuffers_created = false; + private: + void create (const std::initializer_list& allowed_formats, + const std::initializer_list allowed_present_modes, VkSwapchainKHR prev_swapchain=VK_NULL_HANDLE); }; } \ No newline at end of file diff --git a/src/vulkan/basalt_physical_device.cpp b/src/vulkan/basalt_physical_device.cpp index 843f653..ebb60c3 100644 --- a/src/vulkan/basalt_physical_device.cpp +++ b/src/vulkan/basalt_physical_device.cpp @@ -404,8 +404,6 @@ VkPresentModeKHR basalt::SwapchainSupportDetails::get_present_mode(const std::in VkExtent2D basalt::SwapchainSupportDetails::get_framebuffer_extent(basalt::Window& window) { - if (this->surface_capabilities.currentExtent.width != -1) - return this->surface_capabilities.currentExtent; i32 width, height; glfwGetFramebufferSize(window, &width, &height); VkExtent2D ext = { diff --git a/src/vulkan/basalt_swapchain.cpp b/src/vulkan/basalt_swapchain.cpp index d04d048..a893ad7 100644 --- a/src/vulkan/basalt_swapchain.cpp +++ b/src/vulkan/basalt_swapchain.cpp @@ -11,70 +11,10 @@ basalt::Swapchain::Swapchain(basalt::Device& device, basalt::Window& window, semaphores_render_finished(MEMORY_TAG_CLASS_ARRAY | MEMORY_TAG_ZONE_ENGINE | MEMORY_TAG_ALIGN_ANY), fences_in_flight(MEMORY_TAG_CLASS_ARRAY | MEMORY_TAG_ZONE_ENGINE | MEMORY_TAG_ALIGN_ANY) { - this->image_count = details.surface_capabilities.minImageCount + 1; - BCLAMP(this->image_count, details.surface_capabilities.minImageCount, details.surface_capabilities.maxImageCount); - if (this->image_count == 0) - this->image_count = details.surface_capabilities.minImageCount + 1; - - VkSwapchainCreateInfoKHR ci = {VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR}; - ci.surface = window; - ci.minImageCount = this->image_count; - ci.imageFormat = details.get_surface_format(allowed_formats).format; - ci.imageColorSpace = details.get_surface_format(allowed_formats).colorSpace; - ci.imageExtent = details.get_framebuffer_extent(window); - ci.imageArrayLayers = 1; - ci.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; - ci.presentMode = details.get_present_mode(allowed_present_modes); - ci.clipped = VK_TRUE; - ci.oldSwapchain = VK_NULL_HANDLE; - - ci.preTransform = details.surface_capabilities.currentTransform; - ci.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; - u32 queue_family_indicies[] = { device.phys->indicies.graphics, device.phys->indicies.present }; - if (device.phys->indicies.graphics == device.phys->indicies.present) - { - ci.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; - ci.queueFamilyIndexCount = 0; - ci.pQueueFamilyIndices = 0; - } - else - { - ci.imageSharingMode = VK_SHARING_MODE_CONCURRENT; - ci.queueFamilyIndexCount = sizeof(queue_family_indicies)/sizeof(queue_family_indicies[0]); - ci.pQueueFamilyIndices = queue_family_indicies; - } + this->framebuffers_created = false; + this->create(allowed_formats, allowed_present_modes, VK_NULL_HANDLE); VkResult err = VK_SUCCESS; - this->swapchain_extent = ci.imageExtent; - VK_ASSERT(vkCreateSwapchainKHR(device, &ci, device.ctx->vk_alloc, &this->swapchain), "Failed to create swapchain\n\tAt %s:%d\n\tError %s\n"); - - u32 n_images = 0; - vkGetSwapchainImagesKHR(device, this->swapchain, &n_images, nullptr); - this->swapchain_images.resize(n_images); - vkGetSwapchainImagesKHR(device, this->swapchain, &n_images, this->swapchain_images.m_pdata); - this->swapchain_images.resize(n_images); - - this->swapchain_image_views.reserve(n_images); - for (u32 i = 0; i < n_images; ++i) - { - VkImageViewCreateInfo view_ci = {VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO}; - view_ci.image = swapchain_images[i]; - view_ci.viewType = VK_IMAGE_VIEW_TYPE_2D; - view_ci.format = this->details.cached_format.format; - view_ci.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; - view_ci.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; - view_ci.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; - view_ci.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; - view_ci.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - view_ci.subresourceRange.baseMipLevel = 0; - view_ci.subresourceRange.levelCount = 1; - view_ci.subresourceRange.baseArrayLayer = 0; - view_ci.subresourceRange.layerCount = 1; - - VK_ASSERT(vkCreateImageView(device, &view_ci, device.ctx->vk_alloc, &swapchain_image_views[i]), "Failed to create image view\n\tAt %s:%d\n\tError %s\n\tImage index: %u\n", i); - } - swapchain_image_views.m_nelements = n_images; - VkSemaphoreCreateInfo semaphore_ci = {VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO}; VkFenceCreateInfo fence_ci = {VK_STRUCTURE_TYPE_FENCE_CREATE_INFO}; fence_ci.flags = VK_FENCE_CREATE_SIGNALED_BIT; @@ -110,10 +50,28 @@ basalt::Swapchain::~Swapchain() vkDestroySwapchainKHR(*device, this->swapchain, device->ctx->vk_alloc); } +void basalt::Swapchain::recreate(VkRenderPass new_renderpass) +{ + swapchain_extent = details.get_framebuffer_extent(*window); + if (swapchain_extent.width == 0 || swapchain_extent.height == 0) + return; + vkDeviceWaitIdle(device->logical); + + for (VkImageView& view : this->swapchain_image_views) + vkDestroyImageView(*this->device, view, this->device->ctx->vk_alloc); + vkDestroySwapchainKHR(*device, this->swapchain, device->ctx->vk_alloc); + + // TODO: store the prefered format/present list and use them + this->create({ details.cached_format }, { details.cached_present_mode }, VK_NULL_HANDLE); + if (new_renderpass != VK_NULL_HANDLE) + this->create_framebuffers(new_renderpass); +} + void basalt::Swapchain::create_framebuffers(VkRenderPass render_pass) { - if (framebuffers.m_nelements != 0) + if (framebuffers.m_nelements != 0 || framebuffers_created) { + vkDeviceWaitIdle(*device); for (VkFramebuffer& buffer : framebuffers) vkDestroyFramebuffer(*device, buffer, device->ctx->vk_alloc); } @@ -133,17 +91,92 @@ void basalt::Swapchain::create_framebuffers(VkRenderPass render_pass) VK_ASSERT(vkCreateFramebuffer(*device, &ci, device->ctx->vk_alloc, framebuffers.m_pdata+i), "Failed to create framebuffer\n\tAt %s:%d\n\tError %s\n\tIndex: %llu\n", i); } framebuffers.m_nelements = swapchain_image_views.m_nelements; + framebuffers_created = true; } -void basalt::Swapchain::wait_and_reset(bool wait_all, u64 timeout) +u32 basalt::Swapchain::wait_acquire_reset(bool wait_all, u64 timeout, VkFence fence) { + if (!framebuffers_created) + return - 1; vkWaitForFences(device->logical, 1, &fences_in_flight.m_pdata[current_frame], wait_all, timeout); - vkResetFences(device->logical, 1, &fences_in_flight.m_pdata[current_frame]); -} - -u32 basalt::Swapchain::acquire_next_image(VkFence fence, u64 timeout) -{ u32 img = -1; - vkAcquireNextImageKHR(device->logical, swapchain, timeout, semaphores_image_available[current_frame], fence, &img); + VkResult err = vkAcquireNextImageKHR(device->logical, swapchain, timeout, semaphores_image_available[current_frame], fence, &img); + if (err == VK_ERROR_OUT_OF_DATE_KHR) + { + framebuffers_created = false; + this->recreate(); + return -1; + } + BASSERT_FATAL(err == VK_SUCCESS, "Assertion %s failed\n\tFailed to acquire next image of swapchain\n\tAt %s:%d\n\tError %s\n\tSwapchain %p (%p)\n", string_VkResult(err), this, this->swapchain); + vkResetFences(device->logical, 1, &fences_in_flight.m_pdata[current_frame]); return img; } + + +void basalt::Swapchain::create(const std::initializer_list& allowed_formats, + const std::initializer_list allowed_present_modes, VkSwapchainKHR prev_swapchain) +{ + this->image_count = details.surface_capabilities.minImageCount + 1; + BCLAMP(this->image_count, details.surface_capabilities.minImageCount, details.surface_capabilities.maxImageCount); + if (this->image_count == 0) + this->image_count = details.surface_capabilities.minImageCount + 1; + + VkSwapchainCreateInfoKHR ci = { VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR }; + ci.surface = *window; + ci.minImageCount = this->image_count; + ci.imageFormat = details.get_surface_format(allowed_formats).format; + ci.imageColorSpace = details.get_surface_format(allowed_formats).colorSpace; + ci.imageExtent = details.get_framebuffer_extent(*window); + ci.imageArrayLayers = 1; + ci.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + ci.presentMode = details.get_present_mode(allowed_present_modes); + ci.clipped = VK_TRUE; + ci.oldSwapchain = prev_swapchain; + + ci.preTransform = details.surface_capabilities.currentTransform; + ci.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + u32 queue_family_indicies[] = { device->phys->indicies.graphics, device->phys->indicies.present }; + if (device->phys->indicies.graphics == device->phys->indicies.present) + { + ci.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + ci.queueFamilyIndexCount = 0; + ci.pQueueFamilyIndices = 0; + } + else + { + ci.imageSharingMode = VK_SHARING_MODE_CONCURRENT; + ci.queueFamilyIndexCount = sizeof(queue_family_indicies) / sizeof(queue_family_indicies[0]); + ci.pQueueFamilyIndices = queue_family_indicies; + } + + VkResult err = VK_SUCCESS; + this->swapchain_extent = ci.imageExtent; + VK_ASSERT(vkCreateSwapchainKHR(device->logical, &ci, device->ctx->vk_alloc, &this->swapchain), "Failed to create swapchain\n\tAt %s:%d\n\tError %s\n"); + + u32 n_images = 0; + vkGetSwapchainImagesKHR(device->logical, this->swapchain, &n_images, nullptr); + this->swapchain_images.resize(n_images); + vkGetSwapchainImagesKHR(device->logical, this->swapchain, &n_images, this->swapchain_images.m_pdata); + this->swapchain_images.resize(n_images); + + this->swapchain_image_views.reserve(n_images); + for (u32 i = 0; i < n_images; ++i) + { + VkImageViewCreateInfo view_ci = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; + view_ci.image = swapchain_images[i]; + view_ci.viewType = VK_IMAGE_VIEW_TYPE_2D; + view_ci.format = this->details.cached_format.format; + view_ci.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + view_ci.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + view_ci.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + view_ci.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + view_ci.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + view_ci.subresourceRange.baseMipLevel = 0; + view_ci.subresourceRange.levelCount = 1; + view_ci.subresourceRange.baseArrayLayer = 0; + view_ci.subresourceRange.layerCount = 1; + + VK_ASSERT(vkCreateImageView(device->logical, &view_ci, device->ctx->vk_alloc, &swapchain_image_views[i]), "Failed to create image view\n\tAt %s:%d\n\tError %s\n\tImage index: %u\n", i); + } + swapchain_image_views.m_nelements = n_images; +} diff --git a/src/vulkan/basalt_window.cpp b/src/vulkan/basalt_window.cpp index f897241..14c9cee 100644 --- a/src/vulkan/basalt_window.cpp +++ b/src/vulkan/basalt_window.cpp @@ -37,7 +37,7 @@ basalt::Window::Window(basalt::Context& ctx, uint16_t width, uint16_t height, co { glfwInit(); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); - glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); + glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE); this->window = glfwCreateWindow(width, height, title, nullptr, nullptr); VkResult err = VK_SUCCESS; diff --git a/tests/basic/src/main.cpp b/tests/basic/src/main.cpp index 660e362..59e43fd 100644 --- a/tests/basic/src/main.cpp +++ b/tests/basic/src/main.cpp @@ -1,6 +1,8 @@ #include "vulkan/basalt_command_buffer.h" #include "vulkan/basalt_swapchain.h" +void framebuffer_resized(GLFWwindow* window, int width, int height); + void application() { basalt::Context ctx("Basic", @@ -11,7 +13,10 @@ void application() VK_EXT_DEBUG_UTILS_EXTENSION_NAME }, MEMORY_TAG_CLASS_ARRAY | MEMORY_TAG_ZONE_APPLICATION | MEMORY_TAG_ALIGN_ANY) ); - basalt::Window window(ctx, 640, 480, "Hello Vulkan!");; + basalt::Window window(ctx, 640, 480, "Hello Vulkan!"); + glfwSetFramebufferSizeCallback(window, framebuffer_resized); + glfwSetWindowUserPointer(window, nullptr); + basalt::PhysicalDevice phy_dev = basalt::PhysicalDeviceSelector(ctx, window) .prefer_types(VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU, 2.0f, 0.0f) .prefer_types(VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU, 1.0f, 0.0f) @@ -32,6 +37,8 @@ void application() { {VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR} }, { VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_FIFO_KHR } ); + glfwSetWindowUserPointer(window, &swapchain); + basalt::RenderPass pass = basalt::RenderPassBuilder(dev) .add_attachment({ .format = swapchain.details.cached_format.format, @@ -104,9 +111,14 @@ void application() while (!window.should_close()) { glfwPollEvents(); - - swapchain.wait_and_reset(); - u32 image_index = swapchain.acquire_next_image(); + + u32 image_index = swapchain.wait_acquire_reset(); + if (image_index == -1 || !swapchain.framebuffers_created) + { + swapchain.create_framebuffers(pass.render_pass); + swapchain.current_frame = (swapchain.current_frame + 1) % swapchain.max_frames_in_flight; + continue; + } cmdbufs[swapchain.current_frame].reset(); cmdbufs[swapchain.current_frame].start(pass, swapchain.framebuffers[image_index], render_area); @@ -138,10 +150,17 @@ void application() present_info.pSwapchains = &swapchain.swapchain; present_info.pImageIndices = &image_index; - VK_ASSERT(vkQueuePresentKHR(dev.queues[1].queue, &present_info), "Failed to queue presentation of rendered frame\n\tAt %s:%d\n\tError %s\n"); + VkResult err = vkQueuePresentKHR(dev.queues[1].queue, &present_info); + if (err == VK_ERROR_OUT_OF_DATE_KHR) + { + swapchain.framebuffers_created = false; + swapchain.current_frame = (swapchain.current_frame + 1) % swapchain.max_frames_in_flight; + continue; + } + + VK_ASSERT(err, "Failed to queue presentation of rendered frame\n\tAt %s:%d\n\tError %s\n"); swapchain.current_frame = (swapchain.current_frame + 1) % swapchain.max_frames_in_flight; } - cmdbufs.resize(0); } int main() @@ -165,4 +184,11 @@ int main() terminate_logger(); return 0; } - \ No newline at end of file + +void framebuffer_resized(GLFWwindow* window, int width, int height) +{ + basalt::Swapchain* swapchain = (basalt::Swapchain*)glfwGetWindowUserPointer(window); + if (swapchain == nullptr) + return; + swapchain->framebuffers_created = false; +} \ No newline at end of file