From bf2a65020bcb0c872395c8d6bf4efd02c35d5b4a Mon Sep 17 00:00:00 2001 From: Rebekah Rowe Date: Fri, 19 Jul 2024 10:27:50 -0400 Subject: [PATCH] Successful Rebuilding of the Swapchain --- src/main.cpp | 269 +++++++++++++++++++++++++++------------------------ 1 file changed, 143 insertions(+), 126 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 64f5fc0..7543af7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -111,11 +111,10 @@ private: std::unique_ptr screen_surface; std::optional graphics_family; std::optional present_family; - vk::SurfaceCapabilitiesKHR surface_capabilities; - std::vector surface_present_modes; - std::vector surface_formats; + std::optional surface_present_mode_count; + std::optional surface_format_count; bool IsGoodCard() const { - return this->graphics_family.has_value() && this->present_family.has_value() && this->screen_surface && !surface_present_modes.empty() && !surface_formats.empty(); + return this->graphics_family.has_value() && this->present_family.has_value() && this->screen_surface && surface_present_mode_count.has_value() && *surface_present_mode_count && surface_format_count.has_value() && *surface_format_count; } } vk_physical_card_info; @@ -189,7 +188,7 @@ public: { // Window init this->libsdl = std::make_unique(SDL_INIT_VIDEO); SDL_Vulkan_LoadLibrary(nullptr); // optional - this->window = std::make_unique(this->app_name, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 800, 480, SDL_WINDOW_VULKAN | SDL_WINDOW_SHOWN); + this->window = std::make_unique(this->app_name, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 800, 480, SDL_WINDOW_VULKAN | SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE); } { // Vulkan Init this->vk_ctx = std::make_unique(); @@ -278,11 +277,7 @@ public: const auto found_graphics_family_idx = std::distance(queue_families.begin(), found_graphics_family); ret.graphics_family = found_graphics_family_idx; - auto found_screen_surface = [](const SDL2pp::Window& window, const vk::raii::Instance& vk_inst) -> std::unique_ptr { - VkSurfaceKHR _screen_surface; - SDL_Vulkan_CreateSurface(window.Get(), static_cast(static_cast(vk_inst)), &_screen_surface); - return std::make_unique(vk_inst, _screen_surface); - }(*this->window, *this->vk_inst); + auto found_screen_surface = this->CreateSurface(*this->window, *this->vk_inst); const auto found_present_family = gfx_card.getSurfaceSupportKHR(found_graphics_family_idx, **found_screen_surface) ? found_graphics_family : [&]() -> decltype(queue_families)::const_iterator { // https://github.com/KhronosGroup/Vulkan-Hpp/blob/6f72ceca515d59f40d64b64cf2734f6261e1f9f2/RAII_Samples/05_InitSwapchain/05_InitSwapchain.cpp#L50 decltype(queue_families)::const_iterator queue_family_to_test; for (queue_family_to_test = queue_families.begin(); queue_family_to_test != queue_families.end(); queue_family_to_test++) { @@ -320,15 +315,14 @@ public: if (!device_features.samplerAnisotropy) return ret; - ret.surface_capabilities = gfx_card.getSurfaceCapabilitiesKHR(*found_screen_surface); const auto surface_present_modes = gfx_card.getSurfacePresentModesKHR(*found_screen_surface); const auto surface_formats = gfx_card.getSurfaceFormatsKHR(*found_screen_surface); if (surface_formats.empty() || surface_present_modes.empty()) return ret; ret.screen_surface = std::make_unique(std::move(*found_screen_surface)); - ret.surface_formats = std::move(surface_formats); - ret.surface_present_modes = std::move(surface_present_modes); + ret.surface_format_count = surface_formats.size(); + ret.surface_present_mode_count = surface_present_modes.size(); return ret; }; @@ -383,105 +377,9 @@ public: this->vk_queue_graphics = std::make_unique(*this->vk_gpu, this->vk_physical_card_info.graphics_family.value(), 0); this->vk_queue_present = std::make_unique(*this->vk_gpu, this->vk_physical_card_info.present_family.value(), 0); } - { // Swapchain setup - const vk::SurfaceFormatKHR surface_format = [](const std::vector& available_formats) -> vk::SurfaceFormatKHR { - vk::SurfaceFormatKHR chosen_format = available_formats.at(0); - if (available_formats.size() == 1) { - if (available_formats[0].format == vk::Format::eUndefined) { - chosen_format.format = required_vulkan_default_format; - chosen_format.colorSpace = required_vulkan_default_screen_colorspace; - } - } else { - for (const auto& format_we_want : required_vulkan_screen_formats) { - const auto find = std::find_if(available_formats.begin(), available_formats.end(), [&](const auto& format_to_test) { return format_to_test.format == format_we_want && format_to_test.colorSpace == required_vulkan_screen_colorspace; }); - if (find != available_formats.end()) { - chosen_format = *find; - break; - } - } - } - return chosen_format; - }(this->vk_physical_card_info.surface_formats); - const vk::PresentModeKHR present_mode = [](const std::vector& available_present_modes) -> vk::PresentModeKHR { - for (const auto& present_mode_to_test : available_present_modes) - if (present_mode_to_test == vk::PresentModeKHR::eMailbox) - return present_mode_to_test; + this->CreateSwapchain(); - return vk::PresentModeKHR::eFifo; - }(this->vk_physical_card_info.surface_present_modes); - - const vk::Extent2D extent = [this](const vk::SurfaceCapabilitiesKHR& capabilities) -> vk::Extent2D { - if (capabilities.currentExtent.width != std::numeric_limits::max()) { - return capabilities.currentExtent; - } else { - int sdl_width = 0, sdl_height = 0; - SDL_Vulkan_GetDrawableSize(this->window->Get(), &sdl_width, &sdl_height); // using equivalent of glfwGetFramebufferSize(window, &width, &height); // https://wiki.libsdl.org/SDL2/SDL_Vulkan_GetDrawableSize - return { - std::clamp(static_cast(sdl_width), capabilities.minImageExtent.width, capabilities.maxImageExtent.width), - std::clamp(static_cast(sdl_height), capabilities.minImageExtent.height, capabilities.maxImageExtent.height) - }; - } - }(this->vk_physical_card_info.surface_capabilities); - - const std::uint32_t image_count = [this]() { - std::uint32_t image_count = this->vk_physical_card_info.surface_capabilities.minImageCount + 1; - if (this->vk_physical_card_info.surface_capabilities.maxImageCount > 0 && image_count > this->vk_physical_card_info.surface_capabilities.maxImageCount) - image_count = this->vk_physical_card_info.surface_capabilities.maxImageCount; - return image_count; - }(); - - const auto swapchain_create_info = [&]() -> vk::SwapchainCreateInfoKHR { - vk::SwapchainCreateInfoKHR swapchain_create_info { - .surface = **this->vk_screen_surface, - .minImageCount = image_count, - .imageFormat = surface_format.format, - .imageColorSpace = surface_format.colorSpace, - .imageExtent = extent, - .imageArrayLayers = 1, - .imageUsage = vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eTransferSrc, - .preTransform = this->vk_physical_card_info.surface_capabilities.currentTransform, - .compositeAlpha = vk::CompositeAlphaFlagBitsKHR::eOpaque, // for window manager, we dont need transparency with windows... - .presentMode = present_mode, - .clipped = vk::True, - .oldSwapchain = nullptr - }; - - const std::array queue_family_indices = { this->vk_physical_card_info.graphics_family.value(), this->vk_physical_card_info.present_family.value() }; - if ((this->vk_physical_card_info.graphics_family.has_value() && this->vk_physical_card_info.present_family.has_value()) && this->vk_physical_card_info.graphics_family.value() != this->vk_physical_card_info.present_family.value()) { // https://docs.vulkan.org/tutorial/latest/03_Drawing_a_triangle/01_Presentation/01_Swap_chain.html#_creating_the_swap_chain - swapchain_create_info.imageSharingMode = vk::SharingMode::eConcurrent; - swapchain_create_info.queueFamilyIndexCount = queue_family_indices.size(); - swapchain_create_info.pQueueFamilyIndices = queue_family_indices.data(); - } else { - swapchain_create_info.imageSharingMode = vk::SharingMode::eExclusive; - swapchain_create_info.queueFamilyIndexCount = 0; // Optional - swapchain_create_info.pQueueFamilyIndices = nullptr; // Optional - } - return swapchain_create_info; - }(); - this->vk_swapchain = std::make_unique(*this->vk_gpu, swapchain_create_info); - - auto swapchain_images = this->vk_swapchain->getImages(); - this->vk_swapchain_image_format = surface_format.format; - this->vk_swapchain_extent = extent; - - assert(this->vk_swapchain_image_views.empty()); - this->vk_swapchain_image_views.reserve(swapchain_images.size()); - for (const auto& image : swapchain_images) { // requires images - const auto imageview_create_info = vk::ImageViewCreateInfo { - .image = image, - .viewType = vk::ImageViewType::e2D, - .format = this->vk_swapchain_image_format, - .components = { - .r = vk::ComponentSwizzle::eIdentity, - .g = vk::ComponentSwizzle::eIdentity, - .b = vk::ComponentSwizzle::eIdentity, - .a = vk::ComponentSwizzle::eIdentity }, - .subresourceRange = { .aspectMask = vk::ImageAspectFlagBits::eColor, .baseMipLevel = 0, .levelCount = 1, .baseArrayLayer = 0, .layerCount = 1 }, - }; - this->vk_swapchain_image_views.emplace_back(*this->vk_gpu, imageview_create_info); - } - } { // Load Shaders this->vk_shader_vertex = std::make_unique(*this->vk_gpu, vk::ShaderModuleCreateInfo { .codeSize = embeded_shader_vertex_glsl_spv.size, .pCode = reinterpret_cast(embeded_shader_vertex_glsl_spv.begin) }); this->vk_shader_frag = std::make_unique(*this->vk_gpu, vk::ShaderModuleCreateInfo { .codeSize = embeded_shader_frag_glsl_spv.size, .pCode = reinterpret_cast(embeded_shader_frag_glsl_spv.begin) }); @@ -685,21 +583,8 @@ public: }; this->vk_pipeline = std::make_unique(*this->vk_gpu, nullptr, pipeline_create_info); } + this->CreateFramebuffer(); { // command pool setup - assert(this->vk_swapchain_framebuffers.empty()); - this->vk_swapchain_framebuffers.reserve(this->vk_swapchain_image_views.size()); - for (const auto& i : this->vk_swapchain_image_views) { - const std::array attachments = { *i }; - const vk::FramebufferCreateInfo framebuffer_create_info { - .renderPass = **this->vk_render_pass, - .attachmentCount = attachments.size(), - .pAttachments = attachments.data(), - .width = this->vk_swapchain_extent.width, - .height = this->vk_swapchain_extent.height, - .layers = 1 - }; - this->vk_swapchain_framebuffers.emplace_back(*this->vk_gpu, framebuffer_create_info); - } const vk::CommandPoolCreateInfo command_pool_create_info { // https://docs.vulkan.org/tutorial/latest/03_Drawing_a_triangle/03_Drawing/01_Command_buffers.html .flags = vk::CommandPoolCreateFlagBits::eResetCommandBuffer, .queueFamilyIndex = this->vk_physical_card_info.graphics_family.value() @@ -873,6 +758,136 @@ public: } } +private: + std::unique_ptr CreateSurface(const SDL2pp::Window& window, const vk::raii::Instance& vk_inst) { + VkSurfaceKHR _screen_surface; + SDL_Vulkan_CreateSurface(window.Get(), static_cast(static_cast(vk_inst)), &_screen_surface); + return std::make_unique(vk_inst, _screen_surface); + } + void RecreateSwapchain() { + this->vk_gpu->waitIdle(); + this->CreateSwapchain(); + this->CreateFramebuffer(); + } + void CreateSwapchain() { + const vk::SurfaceFormatKHR surface_format = [](const std::vector& available_formats) -> vk::SurfaceFormatKHR { + vk::SurfaceFormatKHR chosen_format = available_formats.at(0); + if (available_formats.size() == 1) { + if (available_formats[0].format == vk::Format::eUndefined) { + chosen_format.format = required_vulkan_default_format; + chosen_format.colorSpace = required_vulkan_default_screen_colorspace; + } + } else { + for (const auto& format_we_want : required_vulkan_screen_formats) { + const auto find = std::find_if(available_formats.begin(), available_formats.end(), [&](const auto& format_to_test) { return format_to_test.format == format_we_want && format_to_test.colorSpace == required_vulkan_screen_colorspace; }); + if (find != available_formats.end()) { + chosen_format = *find; + break; + } + } + } + return chosen_format; + }(this->vk_gfx_card->getSurfaceFormatsKHR(*this->vk_screen_surface)); + + const vk::PresentModeKHR present_mode = [](const std::vector& available_present_modes) -> vk::PresentModeKHR { + for (const auto& present_mode_to_test : available_present_modes) + if (present_mode_to_test == vk::PresentModeKHR::eMailbox) + return present_mode_to_test; + + return vk::PresentModeKHR::eFifo; + }(this->vk_gfx_card->getSurfacePresentModesKHR(*this->vk_screen_surface)); + + const auto surface_capabilities = this->vk_gfx_card->getSurfaceCapabilitiesKHR(*this->vk_screen_surface); + const vk::Extent2D extent = [this](const vk::SurfaceCapabilitiesKHR& capabilities) -> vk::Extent2D { + if (capabilities.currentExtent.width != std::numeric_limits::max()) { + return capabilities.currentExtent; + } else { + int sdl_width = 0, sdl_height = 0; + SDL_Vulkan_GetDrawableSize(this->window->Get(), &sdl_width, &sdl_height); // using equivalent of glfwGetFramebufferSize(window, &width, &height); // https://wiki.libsdl.org/SDL2/SDL_Vulkan_GetDrawableSize + return { + std::clamp(static_cast(sdl_width), capabilities.minImageExtent.width, capabilities.maxImageExtent.width), + std::clamp(static_cast(sdl_height), capabilities.minImageExtent.height, capabilities.maxImageExtent.height) + }; + } + }(surface_capabilities); + + std::cout << "Actual Range: " << std::to_string(extent.width) << " x " << std::to_string(extent.height) << std::endl; + const std::uint32_t image_count = [&surface_capabilities]() { + std::uint32_t image_count = surface_capabilities.minImageCount + 1; + if (surface_capabilities.maxImageCount > 0 && image_count > surface_capabilities.maxImageCount) + image_count = surface_capabilities.maxImageCount; + return image_count; + }(); + + const auto swapchain_create_info = [&]() -> vk::SwapchainCreateInfoKHR { + vk::SwapchainCreateInfoKHR swapchain_create_info { + .surface = **this->vk_screen_surface, + .minImageCount = image_count, + .imageFormat = surface_format.format, + .imageColorSpace = surface_format.colorSpace, + .imageExtent = extent, + .imageArrayLayers = 1, + .imageUsage = vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eTransferSrc, + .preTransform = surface_capabilities.currentTransform, + .compositeAlpha = vk::CompositeAlphaFlagBitsKHR::eOpaque, // for window manager, we dont need transparency with windows... + .presentMode = present_mode, + .clipped = vk::True, + .oldSwapchain = this->vk_swapchain ? **this->vk_swapchain : nullptr + }; + + const std::array queue_family_indices = { this->vk_physical_card_info.graphics_family.value(), this->vk_physical_card_info.present_family.value() }; + if ((this->vk_physical_card_info.graphics_family.has_value() && this->vk_physical_card_info.present_family.has_value()) && this->vk_physical_card_info.graphics_family.value() != this->vk_physical_card_info.present_family.value()) { // https://docs.vulkan.org/tutorial/latest/03_Drawing_a_triangle/01_Presentation/01_Swap_chain.html#_creating_the_swap_chain + swapchain_create_info.imageSharingMode = vk::SharingMode::eConcurrent; + swapchain_create_info.queueFamilyIndexCount = queue_family_indices.size(); + swapchain_create_info.pQueueFamilyIndices = queue_family_indices.data(); + } else { + swapchain_create_info.imageSharingMode = vk::SharingMode::eExclusive; + swapchain_create_info.queueFamilyIndexCount = 0; // Optional + swapchain_create_info.pQueueFamilyIndices = nullptr; // Optional + } + return swapchain_create_info; + }(); + this->vk_swapchain = std::make_unique(*this->vk_gpu, swapchain_create_info); + + auto swapchain_images = this->vk_swapchain->getImages(); + this->vk_swapchain_image_format = surface_format.format; + this->vk_swapchain_extent = extent; + + this->vk_swapchain_image_views.clear(); + this->vk_swapchain_image_views.reserve(swapchain_images.size()); + for (const auto& image : swapchain_images) { // requires images + const auto imageview_create_info = vk::ImageViewCreateInfo { + .image = image, + .viewType = vk::ImageViewType::e2D, + .format = this->vk_swapchain_image_format, + .components = { + .r = vk::ComponentSwizzle::eIdentity, + .g = vk::ComponentSwizzle::eIdentity, + .b = vk::ComponentSwizzle::eIdentity, + .a = vk::ComponentSwizzle::eIdentity }, + .subresourceRange = { .aspectMask = vk::ImageAspectFlagBits::eColor, .baseMipLevel = 0, .levelCount = 1, .baseArrayLayer = 0, .layerCount = 1 }, + }; + this->vk_swapchain_image_views.emplace_back(*this->vk_gpu, imageview_create_info); + } + } + void CreateFramebuffer() { + assert(this->vk_render_pass); + this->vk_swapchain_framebuffers.clear(); + this->vk_swapchain_framebuffers.reserve(this->vk_swapchain_image_views.size()); + for (const auto& i : this->vk_swapchain_image_views) { + const std::array attachments = { *i }; + const vk::FramebufferCreateInfo framebuffer_create_info { + .renderPass = **this->vk_render_pass, + .attachmentCount = attachments.size(), + .pAttachments = attachments.data(), + .width = this->vk_swapchain_extent.width, + .height = this->vk_swapchain_extent.height, + .layers = 1 + }; + this->vk_swapchain_framebuffers.emplace_back(*this->vk_gpu, framebuffer_create_info); + } + } + protected: VulkanBuffer CreateBuffer(vk::DeviceSize wanted_size, vk::BufferUsageFlags wanted_usage, vk::MemoryPropertyFlags wanted_properties) const { const vk::BufferCreateInfo buffer_create_info { @@ -1130,7 +1145,8 @@ public: const auto next_image = this->vk_swapchain->acquireNextImage(std::numeric_limits::max(), *this->vk_semephores_image_available[current_frame_index]); if (next_image.first == vk::Result::eErrorOutOfDateKHR) { - throw std::runtime_error("Unable to rebuild Swapchain!"); + this->RecreateSwapchain(); + return; } else if (next_image.first != vk::Result::eSuccess && next_image.first != vk::Result::eSuboptimalKHR) throw std::runtime_error("failed to acquire next swap chain image!"); @@ -1172,7 +1188,8 @@ public: }; const vk::Result present_result = this->vk_queue_present->presentKHR(present_info); if (present_result == vk::Result::eErrorOutOfDateKHR || present_result == vk::Result::eSuboptimalKHR) { - throw std::runtime_error("Unable to rebuild Swapchain!"); + this->RecreateSwapchain(); + return; } else if (present_result != vk::Result::eSuccess) throw std::runtime_error("failed to present image!"); }