/* * Copyright 2022-2024 Frederico de Oliveira Linhares * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "swapchain.hpp" #include "../core.hpp" #include namespace { void load_swapchain(void *obj) { auto self = static_cast(obj); // Surface formats. uint32_t vk_surface_format_count; std::vector vk_surface_formats; vkGetPhysicalDeviceSurfaceFormatsKHR( cg_core.vk_device_with_swapchain->physical_device, cg_core.window_surface, &vk_surface_format_count, nullptr); vk_surface_formats.resize(vk_surface_format_count); vkGetPhysicalDeviceSurfaceFormatsKHR( cg_core.vk_device_with_swapchain->physical_device, cg_core.window_surface, &vk_surface_format_count, vk_surface_formats.data()); VkSwapchainCreateInfoKHR swapchain_create_info = {}; swapchain_create_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; swapchain_create_info.pNext = nullptr; swapchain_create_info.flags = 0; swapchain_create_info.surface = cg_core.window_surface; swapchain_create_info.minImageCount = 3; // triple buffering. self->image_format = vk_surface_formats[0].format; swapchain_create_info.imageFormat = self->image_format; swapchain_create_info.imageColorSpace = vk_surface_formats[0].colorSpace; swapchain_create_info.imageExtent = { cg_core.display_width, cg_core.display_height}; swapchain_create_info.imageArrayLayers = 1; swapchain_create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; swapchain_create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; swapchain_create_info.queueFamilyIndexCount = 0; swapchain_create_info.pQueueFamilyIndices = nullptr; swapchain_create_info.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; swapchain_create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; swapchain_create_info.presentMode = VK_PRESENT_MODE_FIFO_KHR; swapchain_create_info.clipped = VK_FALSE; swapchain_create_info.oldSwapchain = VK_NULL_HANDLE; if(vkCreateSwapchainKHR( cg_core.vk_device_with_swapchain->device, &swapchain_create_info, nullptr, &self->swapchain) != VK_SUCCESS) throw CommandError{"Vulkan failed to create swapchain."}; vkGetSwapchainImagesKHR( cg_core.vk_device_with_swapchain->device, self->swapchain, &self->images_count, nullptr); self->images = new VkImage[self->images_count]; vkGetSwapchainImagesKHR( cg_core.vk_device_with_swapchain->device, self->swapchain, &self->images_count, self->images); } void unload_swapchain(void *obj) { auto self = static_cast(obj); delete[] self->images; vkDestroySwapchainKHR( cg_core.vk_device_with_swapchain->device, self->swapchain, nullptr); } void load_image_view(void *obj) { auto self = static_cast(obj); self->image_views = new VkImageView[self->images_count]; for(auto i{0}; i < self->images_count; i++) { VkImageViewCreateInfo create_info = {}; create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; create_info.image = self->images[i]; create_info.viewType = VK_IMAGE_VIEW_TYPE_2D; create_info.format = self->image_format; create_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; create_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; create_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; create_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; create_info.subresourceRange.baseMipLevel = 0; create_info.subresourceRange.levelCount = 1; create_info.subresourceRange.baseArrayLayer = 0; create_info.subresourceRange.layerCount = 1; if(vkCreateImageView( cg_core.vk_device_with_swapchain->device, &create_info, nullptr, &self->image_views[i])) throw CommandError{"Could no create Image View for swapchain."}; } } void unload_image_view(void *obj) { auto self = static_cast(obj); for(auto i{0}; i < self->images_count; i++) vkDestroyImageView( cg_core.vk_device_with_swapchain->device, self->image_views[i], nullptr); } void load_frame_sync(void *obj) { auto self = static_cast(obj); self->image_available_semaphores.resize(self->max_frames_in_flight); self->render_finished_semaphores.resize(self->max_frames_in_flight); self->in_flight_fences.resize(self->max_frames_in_flight); VkSemaphoreCreateInfo semaphore_info = {}; semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; semaphore_info.pNext = nullptr; semaphore_info.flags = 0; VkFenceCreateInfo fence_info = {}; fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; fence_info.pNext = nullptr; fence_info.flags = VK_FENCE_CREATE_SIGNALED_BIT; // FIXME: if this loop fails, it will not destroy the semaphores. for(auto i{0}; i < self->max_frames_in_flight; i++) { if(vkCreateSemaphore( cg_core.vk_device_with_swapchain->device, &semaphore_info, nullptr, &self->image_available_semaphores[i]) != VK_SUCCESS || vkCreateSemaphore( cg_core.vk_device_with_swapchain->device, &semaphore_info, nullptr, &self->render_finished_semaphores[i]) != VK_SUCCESS || vkCreateFence(cg_core.vk_device_with_swapchain->device, &fence_info, nullptr, &self->in_flight_fences[i]) != VK_SUCCESS) throw CommandError{"Failed to create semaphores."}; } } void unload_frame_sync(void *obj) { auto self = static_cast(obj); vkDeviceWaitIdle(cg_core.vk_device_with_swapchain->device); for(auto i{0}; i < self->max_frames_in_flight; i++) { vkDestroySemaphore(cg_core.vk_device_with_swapchain->device, self->render_finished_semaphores[i], nullptr); vkDestroySemaphore(cg_core.vk_device_with_swapchain->device, self->image_available_semaphores[i], nullptr); vkDestroyFence(cg_core.vk_device_with_swapchain->device, self->in_flight_fences[i], nullptr); } } const CommandChain loader{ {&load_swapchain, &unload_swapchain}, {&load_image_view, &unload_image_view}, {&load_frame_sync, &unload_frame_sync} }; } namespace BluCat { Swapchain::Swapchain(): current_frame{0} { loader.execute(this); } Swapchain::~Swapchain() { loader.revert(this); } }