Successful Rebuilding of the Swapchain
All checks were successful
ci/woodpecker/push/woodpecker.json Pipeline was successful

This commit is contained in:
Rebekah 2024-07-19 10:27:50 -04:00
parent 30ed1f679b
commit bf2a65020b
Signed by: oneechanhax
GPG Key ID: 0074BF373B812798

View File

@ -111,11 +111,10 @@ private:
std::unique_ptr<vk::raii::SurfaceKHR> screen_surface;
std::optional<std::uint32_t> graphics_family;
std::optional<std::uint32_t> present_family;
vk::SurfaceCapabilitiesKHR surface_capabilities;
std::vector<vk::PresentModeKHR> surface_present_modes;
std::vector<vk::SurfaceFormatKHR> surface_formats;
std::optional<std::uint32_t> surface_present_mode_count;
std::optional<std::uint32_t> 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<SDL2pp::SDL>(SDL_INIT_VIDEO);
SDL_Vulkan_LoadLibrary(nullptr); // optional
this->window = std::make_unique<SDL2pp::Window>(this->app_name, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 800, 480, SDL_WINDOW_VULKAN | SDL_WINDOW_SHOWN);
this->window = std::make_unique<SDL2pp::Window>(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<vk::raii::Context>();
@ -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<vk::raii::SurfaceKHR> {
VkSurfaceKHR _screen_surface;
SDL_Vulkan_CreateSurface(window.Get(), static_cast<VkInstance>(static_cast<vk::Instance>(vk_inst)), &_screen_surface);
return std::make_unique<vk::raii::SurfaceKHR>(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<vk::raii::SurfaceKHR>(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<vk::raii::Queue>(*this->vk_gpu, this->vk_physical_card_info.graphics_family.value(), 0);
this->vk_queue_present = std::make_unique<vk::raii::Queue>(*this->vk_gpu, this->vk_physical_card_info.present_family.value(), 0);
}
{ // Swapchain setup
const vk::SurfaceFormatKHR surface_format = [](const std::vector<vk::SurfaceFormatKHR>& 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<vk::PresentModeKHR>& 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<std::uint32_t>::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<std::uint32_t>(sdl_width), capabilities.minImageExtent.width, capabilities.maxImageExtent.width),
std::clamp(static_cast<std::uint32_t>(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<std::uint32_t, 2> 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<vk::raii::SwapchainKHR>(*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<vk::raii::ShaderModule>(*this->vk_gpu, vk::ShaderModuleCreateInfo { .codeSize = embeded_shader_vertex_glsl_spv.size, .pCode = reinterpret_cast<const std::uint32_t*>(embeded_shader_vertex_glsl_spv.begin) });
this->vk_shader_frag = std::make_unique<vk::raii::ShaderModule>(*this->vk_gpu, vk::ShaderModuleCreateInfo { .codeSize = embeded_shader_frag_glsl_spv.size, .pCode = reinterpret_cast<const std::uint32_t*>(embeded_shader_frag_glsl_spv.begin) });
@ -685,21 +583,8 @@ public:
};
this->vk_pipeline = std::make_unique<vk::raii::Pipeline>(*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<vk::ImageView, 1> 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<vk::raii::SurfaceKHR> CreateSurface(const SDL2pp::Window& window, const vk::raii::Instance& vk_inst) {
VkSurfaceKHR _screen_surface;
SDL_Vulkan_CreateSurface(window.Get(), static_cast<VkInstance>(static_cast<vk::Instance>(vk_inst)), &_screen_surface);
return std::make_unique<vk::raii::SurfaceKHR>(vk_inst, _screen_surface);
}
void RecreateSwapchain() {
this->vk_gpu->waitIdle();
this->CreateSwapchain();
this->CreateFramebuffer();
}
void CreateSwapchain() {
const vk::SurfaceFormatKHR surface_format = [](const std::vector<vk::SurfaceFormatKHR>& 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<vk::PresentModeKHR>& 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<std::uint32_t>::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<std::uint32_t>(sdl_width), capabilities.minImageExtent.width, capabilities.maxImageExtent.width),
std::clamp(static_cast<std::uint32_t>(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<std::uint32_t, 2> 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<vk::raii::SwapchainKHR>(*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<vk::ImageView, 1> 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<std::uint64_t>::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!");
}