#include <stdafx.h> #include "SwapChain.h" #include "Buffer/DepthBuffer.h" #include "Context.h" #include "RenderPass.h" #include "Scene.h" #include "Texture.h" #include "VulkanWindow.h" #include "Model.h" namespace fpr { SwapChain::SwapChain(fpr::Device* device, vk::SurfaceKHR& surface, std::optional<vk::UniqueSwapchainKHR> old_swapchain) { if(old_swapchain != std::nullopt) { m_old_swapchain = std::move(*old_swapchain); is_recreation = true; } CreateSwapchain(device, surface); CreateImageViews(device); m_depth_buffer = std::make_unique<fpr::DepthBuffer>(device, this); CreateRenderPasses(device); CreateFrameBuffers(device); CreateSynchronisation(device); m_viewport_push_constant.viewport_size_tile_size = { m_extent.width, m_extent.height, DEFAULT_TILE_SIZE }; } vk::UniqueSwapchainKHR& SwapChain::GetUniqueHandle() { return m_swapchain; } void SwapChain::CreateSwapchain(fpr::Device* device, vk::SurfaceKHR& surface) { auto [surface_capabilities, surface_formats, present_modes] = QuerySwapChainDetails(device, surface); auto fifo_or_immediate = [&](const std::vector<vk::PresentModeKHR>& present_modes) { auto search_result = std::find(present_modes.begin(), present_modes.end(), vk::PresentModeKHR::eFifo); if(search_result != present_modes.end()) return *search_result; else return vk::PresentModeKHR::eImmediate; }; m_surface_format = QueryFirstSuitableFormat(surface_formats); m_present_mode = fifo_or_immediate(present_modes); m_extent = QueryExtenfpromCapabilities(surface_capabilities); vk::SwapchainCreateInfoKHR swap_chain_create_info{}; // TODO: Refactor this, instead of commenting make it actually readable // instead uint32_t image_count = surface_capabilities.minImageCount + 1; if(surface_capabilities.maxImageCount > 0 && image_count > surface_capabilities.maxImageCount) { if(image_count < surface_capabilities.maxImageCount) { image_count++; } else { image_count = surface_capabilities.minImageCount; } } MAX_IMAGES_IN_FLIGHT = image_count; // end of confusion swap_chain_create_info.setSurface(surface); swap_chain_create_info.imageFormat = m_surface_format.format; swap_chain_create_info.imageColorSpace = m_surface_format.colorSpace; swap_chain_create_info.imageExtent = m_extent; swap_chain_create_info.presentMode = m_present_mode; swap_chain_create_info.minImageCount = image_count; swap_chain_create_info.imageArrayLayers = 1; swap_chain_create_info.imageUsage = vk::ImageUsageFlagBits::eColorAttachment; swap_chain_create_info.preTransform = surface_capabilities.currentTransform; swap_chain_create_info.compositeAlpha = vk::CompositeAlphaFlagBitsKHR::eOpaque; swap_chain_create_info.clipped = VK_TRUE; swap_chain_create_info.oldSwapchain = is_recreation ? *m_old_swapchain : VK_NULL_HANDLE; uint32_t queue_family_indices[] = { device->GetGraphicsQueueIndex(), device->GetPresentQueueIndex() }; if(queue_family_indices[0] != queue_family_indices[1]) { swap_chain_create_info.imageSharingMode = vk::SharingMode::eConcurrent; swap_chain_create_info.queueFamilyIndexCount = 2; swap_chain_create_info.pQueueFamilyIndices = queue_family_indices; } else { swap_chain_create_info.imageSharingMode = vk::SharingMode::eExclusive; swap_chain_create_info.queueFamilyIndexCount = 0; swap_chain_create_info.pQueueFamilyIndices = nullptr; } auto [swapchain_result, swapchain] = device->GetLogicalDeviceHandle().createSwapchainKHRUnique(swap_chain_create_info); // todo: swapchain result m_swapchain = std::move(swapchain); } void SwapChain::CreateImageViews(fpr::Device* device) { auto [images_result, swapchain_images] = device->GetLogicalDeviceHandle().getSwapchainImagesKHR(*m_swapchain); for(const auto& image : swapchain_images) { vk::ImageViewCreateInfo image_view_create_info{}; image_view_create_info.setImage(image) .setViewType(IMAGE_VIEW_TYPE) .setFormat(m_surface_format.format) .setSubresourceRange(vk::ImageSubresourceRange() .setAspectMask(ASPECT_FLAGS) .setBaseArrayLayer(0) .setBaseMipLevel(0) .setLayerCount(1) .setLevelCount(1)); auto [result_signal, image_view] = device->GetLogicalDeviceHandle().createImageViewUnique(image_view_create_info); assert(("Failed to create image view!", result_signal == vk::Result::eSuccess)); m_swapchain_images.emplace_back(std::make_pair(image, std::move(image_view))); } } void SwapChain::SyncPresentation(fpr::Device* device, uint32_t image_index) { vk::PresentInfoKHR present_info{}; present_info.setImageIndices(image_index); present_info.setSwapchains(m_swapchain.get()); present_info.setWaitSemaphores(m_render_done_semaphores[m_current_frame].get()); [[maybe_unused]] auto present_res = device->GetGraphicsQueue().presentKHR(present_info); assert(present_res == vk::Result::eSuccess); } void SwapChain::SyncColorPass(fpr::Device* device) { std::array<vk::PipelineStageFlags, 2> wait_flags = { vk::PipelineStageFlagBits::eEarlyFragmentTests, vk::PipelineStageFlagBits::eColorAttachmentOutput }; std::array<vk::Semaphore, 2> wait_semaphores{ m_earlyz_semaphores[m_current_frame].get(), m_image_ready_semaphores[m_current_frame].get() }; vk::SubmitInfo submit_info{}; submit_info.setWaitSemaphores(wait_semaphores); submit_info.commandBufferCount = 1; submit_info.pCommandBuffers = &m_command_buffers[m_current_frame].get(); submit_info.setWaitDstStageMask(wait_flags); submit_info.signalSemaphoreCount = 1; submit_info.pSignalSemaphores = &m_render_done_semaphores[m_current_frame].get(); [[maybe_unused]] auto submit_res = device->GetGraphicsQueue().submit(submit_info, *m_draw_fence); assert(submit_res == vk::Result::eSuccess); } void SwapChain::CreateRenderPasses(fpr::Device* device) { auto& context = fpr::Context::Get(); vk::Format depth_format = m_depth_buffer->GetDepthImageFormat(); const fpr::RenderPassOptions& color_pass_options = RenderPass::MakeDefaultRenderPass(m_surface_format.format, depth_format); const fpr::RenderPassOptions& early_z_options = RenderPass::MakeDefaultDepthPass(depth_format); context.m_render_graph->RenderPasses.Add("Default", std::make_unique<fpr::RenderPass>(color_pass_options, device)); context.m_render_graph->RenderPasses.Add("EarlyZ", std::make_unique<fpr::RenderPass>(early_z_options, device)); } void SwapChain::SyncDepthPass(fpr::Device* device) { vk::SubmitInfo depth_submit_info{}; depth_submit_info.setSignalSemaphores(m_earlyz_semaphores[m_current_frame].get()) .setCommandBuffers(m_depth_command_buffers[m_current_frame].get()); [[maybe_unused]] vk::Result depth_submit_res = device->GetGraphicsQueue().submit(depth_submit_info); assert(("Failed to sync depth pass.", depth_submit_res == vk::Result::eSuccess)); } void SwapChain::CreateFrameBuffers(fpr::Device* device) { auto& context = fpr::Context::Get(); auto renderpass = context.m_render_graph->RenderPasses.Get("Default"); auto depth_pass = context.m_render_graph->RenderPasses.Get("EarlyZ"); m_swapchain_framebuffers.reserve(m_swapchain_images.size()); for(size_t i = 0; i < m_swapchain_images.size(); ++i) { std::array<vk::ImageView, 2> attachments = { m_swapchain_images[i].second.get(), m_depth_buffer->GetDepthBufferView() }; vk::FramebufferCreateInfo framebuffer_create_info{}; framebuffer_create_info.renderPass = renderpass->GetvkRenderPass(); framebuffer_create_info.pAttachments = attachments.data(); framebuffer_create_info.attachmentCount = (uint32_t)attachments.size(); framebuffer_create_info.width = m_extent.width; framebuffer_create_info.height = m_extent.height; framebuffer_create_info.layers = 1; auto [fb_result, framebuffer] = device->GetLogicalDeviceHandle().createFramebufferUnique(framebuffer_create_info); assert(("Failed to create framebuffer!!", fb_result == vk::Result::eSuccess)); m_swapchain_framebuffers.push_back(std::move(framebuffer)); } std::array<vk::ImageView, 1> depth_image_view = { m_depth_buffer->GetDepthBufferView() }; for(size_t i = 0; i < m_swapchain_images.size(); ++i) { vk::FramebufferCreateInfo framebuffer_create_info = {}; framebuffer_create_info.renderPass = depth_pass->GetvkRenderPass(); framebuffer_create_info.setAttachments(depth_image_view); framebuffer_create_info.width = m_extent.width; framebuffer_create_info.height = m_extent.height; framebuffer_create_info.layers = 1; auto [fb_result, depth_framebuffer] = device->GetLogicalDeviceHandle().createFramebufferUnique(framebuffer_create_info); assert(("Failed to create depth framebuffer!!", fb_result == vk::Result::eSuccess)); m_depth_framebuffers.emplace_back(std::move(depth_framebuffer)); } } void SwapChain::RecordCommands() { RecordDepthCommands(); RecordComputeCommands(); RecordRenderCommands(); } void SwapChain::CreateCommandBuffers() { auto& context = fpr::Context::Get(); // vk::commandbuffer vk::CommandBufferAllocateInfo command_buffer_alloc_info{}; command_buffer_alloc_info.commandPool = context.GetGraphicsCmdPool(); command_buffer_alloc_info.level = vk::CommandBufferLevel::ePrimary; command_buffer_alloc_info.commandBufferCount = (uint32_t)m_swapchain_framebuffers.size(); auto [cmd_alloc_result, cmd_buffers] = context.GetDevice()->GetLogicalDeviceHandle().allocateCommandBuffersUnique(command_buffer_alloc_info); assert(("Failed to create command buffers!", cmd_alloc_result == vk::Result::eSuccess)); m_command_buffers = std::move(cmd_buffers); vk::CommandBufferAllocateInfo depth_buffer_alloc{}; depth_buffer_alloc.commandPool = context.GetGraphicsCmdPool(); depth_buffer_alloc.level = vk::CommandBufferLevel::ePrimary; depth_buffer_alloc.commandBufferCount = (uint32_t)m_swapchain_framebuffers.size(); auto [depth_cmd_alloc_result, depth_cmd_buffers] = context.GetDevice()->GetLogicalDeviceHandle().allocateCommandBuffersUnique(depth_buffer_alloc); assert(("Failed to create depth command buffers", depth_cmd_alloc_result == vk::Result::eSuccess)); m_depth_command_buffers = std::move(depth_cmd_buffers); } void SwapChain::CreateSynchronisation(fpr::Device* device) { m_image_ready_semaphores.resize(MAX_IMAGES_IN_FLIGHT); m_render_done_semaphores.resize(MAX_IMAGES_IN_FLIGHT); m_earlyz_semaphores.resize(MAX_IMAGES_IN_FLIGHT); vk::SemaphoreCreateInfo semaphore_create_info{}; vk::FenceCreateInfo fence_create_info(vk::FenceCreateFlagBits::eSignaled); auto create_semaphore = [&](vk::UniqueSemaphore& s) { auto [semaphore_result, semaphore] = device->GetLogicalDeviceHandle().createSemaphoreUnique(semaphore_create_info); assert(("Semaphore creation failed!", semaphore_result == vk::Result::eSuccess)); s = std::move(semaphore); }; auto create_fence = [&](vk::UniqueFence& f) { auto [fence_result, fence] = device->GetLogicalDeviceHandle().createFenceUnique(fence_create_info); assert(("Failed to create fence!", fence_result == vk::Result::eSuccess)); f = std::move(fence); }; std::for_each(m_image_ready_semaphores.begin(), m_image_ready_semaphores.end(), create_semaphore); std::for_each(m_render_done_semaphores.begin(), m_render_done_semaphores.end(), create_semaphore); std::for_each(m_earlyz_semaphores.begin(), m_earlyz_semaphores.end(), create_semaphore); create_fence(m_draw_fence); } vk::Result SwapChain::SubmitFrame() { auto device = fpr::Context::Get().GetDevice(); uint32_t image_index; [[maybe_unused]] vk::Result fence_result = device->GetLogicalDeviceHandle().waitForFences(*m_draw_fence, false, std::numeric_limits<uint64_t>::max()); [[maybe_unused]] vk::Result fence_reset_result = device->GetLogicalDeviceHandle().resetFences(*m_draw_fence); assert(("Timed out waiting for draw fence", fence_result == vk::Result::eSuccess)); assert(("Failed to reset draw fence", fence_reset_result == vk::Result::eSuccess)); vk::Result acquire_image_result = device->GetLogicalDeviceHandle().acquireNextImageKHR( *m_swapchain, std::numeric_limits<uint64_t>::max(), *m_image_ready_semaphores[m_current_frame], nullptr, &image_index); if(acquire_image_result != vk::Result::eSuccess) return acquire_image_result; SyncDepthPass(device); SyncColorPass(device); SyncPresentation(device, image_index); m_current_frame = (m_current_frame + 1) % MAX_IMAGES_IN_FLIGHT; return acquire_image_result; } SwapChain::SwapchainDetails SwapChain::QuerySwapChainDetails(fpr::Device* device, vk::SurfaceKHR& surface) { auto [capabilities_result, surface_capabilities] = device->GetPhysicalDeviceHandle().getSurfaceCapabilitiesKHR(surface); // todo: assert result auto [formats_result, formats] = device->GetPhysicalDeviceHandle().getSurfaceFormatsKHR(surface); // todo: assert formats auto [modes_result, present_modes] = device->GetPhysicalDeviceHandle().getSurfacePresentModesKHR(surface); // todo: assert modes return std::make_tuple(surface_capabilities, formats, present_modes); } vk::SurfaceFormatKHR SwapChain::QueryFirstSuitableFormat(const std::vector<vk::SurfaceFormatKHR>& formats) { for(const auto& available_format : formats) { for(const auto& wanted_format : wanted_formats) { if(wanted_format == available_format.format && wanted_color_space == available_format.colorSpace) { return available_format; } } } return vk::SurfaceFormatKHR{}; } const std::vector<SwapChain::SwapChainImage>& SwapChain::GetImages() { return m_swapchain_images; } void SwapChain::RecordRenderCommands() { auto& context = fpr::Context::Get(); auto render_graph = context.m_render_graph.get(); auto graphics_pipeline = render_graph->GraphicsPipelines.Get("Graphics"); for(size_t i = 0; i < m_command_buffers.size(); ++i) { vk::CommandBufferBeginInfo cmd_begin_info; cmd_begin_info.flags = vk::CommandBufferUsageFlagBits::eSimultaneousUse; [[maybe_unused]] auto cmd_begin_result = m_command_buffers[i]->begin(cmd_begin_info); assert(cmd_begin_result == vk::Result::eSuccess); vk::RenderPassBeginInfo render_pass_info = {}; render_pass_info.renderPass = render_graph->RenderPasses.Get("Default")->GetvkRenderPass(); render_pass_info.framebuffer = m_swapchain_framebuffers[i].get(); render_pass_info.setRenderArea({ { 0, 0 }, m_extent }); render_pass_info.setClearValues(m_clear_values); m_command_buffers[i]->beginRenderPass(render_pass_info, vk::SubpassContents::eInline); vk::Viewport viewport; viewport.x = 0.0f; viewport.y = static_cast<float>(m_extent.height); viewport.width = static_cast<float>(m_extent.width); viewport.height = -static_cast<float>(m_extent.height); viewport.minDepth = 0.0f; viewport.maxDepth = 1.0f; vk::Rect2D scissor{}; scissor.setOffset({ 0, 0 }); scissor.extent = m_extent; m_command_buffers[i]->setViewport(0, viewport); m_command_buffers[i]->setScissor(0, scissor); m_command_buffers[i]->bindPipeline(vk::PipelineBindPoint::eGraphics, graphics_pipeline->GetPipeline()); m_command_buffers[i]->pushConstants<ViewportConstants>( graphics_pipeline->GetLayout(), vk::ShaderStageFlagBits::eFragment, 0, m_viewport_push_constant); fpr::Scene* scene = context.m_loaded_scene; auto cam_desc_set = render_graph->DescriptorSets.Get("Camera" + std::to_string(i)); auto light_desc_set = render_graph->DescriptorSets.Get("PointLight" + std::to_string(i)); for(auto& model : scene->GetModels()) { m_command_buffers[i]->bindIndexBuffer(model->GetIndexBuffer().GetBuffer(), 0, vk::IndexType::eUint32); for(auto&& mesh : model->GetMeshes()) { m_command_buffers[i]->bindVertexBuffers(0, model->GetVertexBuffer().GetBuffer(), mesh.GetVertexOffset()); std::vector<vk::DescriptorSet> desc_sets = { cam_desc_set, }; m_command_buffers[i]->bindDescriptorSets( vk::PipelineBindPoint::eGraphics, graphics_pipeline->GetLayout(), 0, cam_desc_set, nullptr); m_command_buffers[i]->bindDescriptorSets( vk::PipelineBindPoint::eGraphics, graphics_pipeline->GetLayout(), 1, render_graph->DescriptorSets.Get("Model" + std::to_string(i)), (uint32_t)(mesh.GetMeshIndex() * context.GetRequiredDynamicAlignment())); m_command_buffers[i]->bindDescriptorSets( vk::PipelineBindPoint::eGraphics, graphics_pipeline->GetLayout(), 2, mesh.GetTexture()->GetDescriptorSet(), nullptr); m_command_buffers[i]->bindDescriptorSets( vk::PipelineBindPoint::eGraphics, graphics_pipeline->GetLayout(), 3, light_desc_set, nullptr); m_command_buffers[i]->drawIndexed(mesh.GetIndexCount(), 1, (uint32_t)mesh.GetIndexOffset(), 0, 0); } } m_command_buffers[i]->endRenderPass(); [[maybe_unused]] auto cmd_end_result = m_command_buffers[i]->end(); assert(cmd_end_result == vk::Result::eSuccess); } } void SwapChain::RecordDepthCommands() { auto& context = fpr::Context::Get(); fpr::RenderGraph* render_graph = context.m_render_graph.get(); fpr::Pipeline* depth_pipeline = render_graph->GraphicsPipelines.Get("Depth"); for(size_t i = 0; i < m_depth_command_buffers.size(); ++i) { vk::CommandBufferBeginInfo cmd_begin_info; cmd_begin_info.flags = vk::CommandBufferUsageFlagBits::eSimultaneousUse; [[maybe_unused]] auto cmd_begin_result = m_depth_command_buffers[i]->begin(cmd_begin_info); assert(cmd_begin_result == vk::Result::eSuccess); vk::RenderPassBeginInfo render_pass_info = {}; render_pass_info.renderPass = context.m_render_graph->RenderPasses.Get("EarlyZ")->GetvkRenderPass(); render_pass_info.framebuffer = m_depth_framebuffers[i].get(); render_pass_info.setRenderArea({ { 0, 0 }, m_extent }); render_pass_info.setClearValues(m_clear_values[1]); m_depth_command_buffers[i]->beginRenderPass(render_pass_info, vk::SubpassContents::eInline); vk::Viewport viewport; viewport.x = 0.0f; viewport.y = static_cast<float>(m_extent.height); viewport.width = static_cast<float>(m_extent.width); viewport.height = -static_cast<float>(m_extent.height); viewport.minDepth = 0.0f; viewport.maxDepth = 1.0f; vk::Rect2D scissor{}; scissor.setOffset({ 0, 0 }); scissor.extent = m_extent; m_depth_command_buffers[i]->setViewport(0, viewport); m_depth_command_buffers[i]->setScissor(0, scissor); m_depth_command_buffers[i]->bindPipeline(vk::PipelineBindPoint::eGraphics, depth_pipeline->GetPipeline()); auto scene = context.m_loaded_scene; std::array<vk::DescriptorSet, 2> desc_sets = { render_graph->DescriptorSets.Get("Camera" + std::to_string(i)), render_graph->DescriptorSets.Get("Model" + std::to_string(i)), }; for(auto&& model : scene->GetModels()) { m_depth_command_buffers[i]->bindIndexBuffer(model->GetIndexBuffer().GetBuffer(), 0, vk::IndexType::eUint32); for(auto&& mesh : model->GetMeshes()) { m_depth_command_buffers[i]->bindVertexBuffers(0U, model->GetVertexBuffer().GetBuffer(), mesh.GetVertexOffset()); m_depth_command_buffers[i]->bindDescriptorSets( vk::PipelineBindPoint::eGraphics, depth_pipeline->GetLayout(), 0, render_graph->DescriptorSets.Get("Camera" + std::to_string(i)), nullptr); m_depth_command_buffers[i]->bindDescriptorSets( vk::PipelineBindPoint::eGraphics, depth_pipeline->GetLayout(), 1, render_graph->DescriptorSets.Get("Model" + std::to_string(i)), (uint32_t)(mesh.GetMeshIndex() * context.GetRequiredDynamicAlignment())); m_depth_command_buffers[i]->drawIndexed(mesh.GetIndexCount(), 1, (uint32_t)mesh.GetIndexOffset(), 0, 0); } } m_depth_command_buffers[i]->endRenderPass(); [[maybe_unused]] auto cmd_end_result = m_depth_command_buffers[i]->end(); assert(cmd_end_result == vk::Result::eSuccess); } } void SwapChain::RecordComputeCommands() { auto& context = fpr::Context::Get(); fpr::RenderGraph* render_graph = context.m_render_graph.get(); fpr::Pipeline* compute_pipeline = render_graph->ComputePipelines.Get("LightCulling"); for(size_t i = 0; i < m_compute_command_buffers.size(); ++i) { vk::CommandBufferBeginInfo cmd_begin_info; cmd_begin_info.flags = vk::CommandBufferUsageFlagBits::eSimultaneousUse; [[maybe_unused]] auto cmd_begin_result = m_compute_command_buffers[i]->begin(cmd_begin_info); assert(cmd_begin_result == vk::Result::eSuccess); //todo barriers std::array<vk::DescriptorSet, 3> desc_sets = { render_graph->DescriptorSets.Get("Camera" + std::to_string(i)), render_graph->DescriptorSets.Get("PointLight" + std::to_string(i)), render_graph->DescriptorSets.Get("DepthSampler"), }; m_compute_command_buffers[i]->bindDescriptorSets( vk::PipelineBindPoint::eCompute, compute_pipeline->GetLayout(), 0, desc_sets, nullptr); m_compute_command_buffers[i]-> pushConstants<ViewportConstants>( compute_pipeline->GetLayout(), vk::ShaderStageFlagBits::eCompute, 0, m_viewport_push_constant); glm::vec3 group_count = { m_viewport_push_constant.viewport_size_tile_size.z, m_viewport_push_constant.viewport_size_tile_size.w, 1 }; m_compute_command_buffers[i]->dispatch((uint32_t)group_count.x, (uint32_t)group_count.y, 1); [[maybe_unused]] auto cmd_end_result = m_compute_command_buffers[i]->end(); assert(cmd_end_result == vk::Result::eSuccess); } } vk::Extent2D SwapChain::QueryExtenfpromCapabilities(vk::SurfaceCapabilitiesKHR capabilities) { vk::Extent2D result{}; if(capabilities.currentExtent.width != std::numeric_limits<uint32_t>::max()) { result = capabilities.currentExtent; } else { int width; int height; glfwGetFramebufferSize(fpr::Context::Get().GetWindow()->GetWindowHandle(), &width, &height); vk::Extent2D new_extent{}; uint32_t width_constrained = std::min(capabilities.maxImageExtent.width, static_cast<uint32_t>(width)); uint32_t height_constrained = std::min(capabilities.maxImageExtent.height, static_cast<uint32_t>(height)); new_extent.width = std::max(static_cast<uint32_t>(width), width_constrained); new_extent.height = std::max(static_cast<uint32_t>(height), height_constrained); result = new_extent; } return result; } const vk::Format& SwapChain::GetFormat() const FPR_NOEXCEPT { return m_surface_format.format; } uint32_t SwapChain::GetCurrentFrameIdx() const { return m_current_frame; } uint32_t SwapChain::GetMaxFrameIdx() const { return MAX_IMAGES_IN_FLIGHT; } const vk::Extent2D& SwapChain::GetCurrentExtent() { return m_extent; } fpr::DepthBuffer* SwapChain::GetDepthBuffer() FPR_NOEXCEPT { return m_depth_buffer.get(); } } // namespace fpr