#include #include #include #include #include #include namespace orange { static constexpr uint32_t DefaultFramesInFlight = 2; static Result ChooseSwapChainFormat(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, bool hdr) { SmallVector surfaceFormats; if (!VkEnumerate(vkGetPhysicalDeviceSurfaceFormatsKHR, surfaceFormats, physicalDevice, surface)) { log::err("Surface supports no formats"); return Result::Error(BasicErrorCode::NotFound); } for (auto& surfaceFormat : surfaceFormats) { if (hdr) { if (surfaceFormat.format == VK_FORMAT_A2R10G10B10_UNORM_PACK32 && surfaceFormat.colorSpace == VK_COLOR_SPACE_HDR10_ST2084_EXT) return Result::Success(surfaceFormat); if (surfaceFormat.format == VK_FORMAT_A2B10G10R10_UNORM_PACK32 && surfaceFormat.colorSpace == VK_COLOR_SPACE_HDR10_ST2084_EXT) return Result::Success(surfaceFormat); } else { if (surfaceFormat.format == VK_FORMAT_B8G8R8A8_UNORM && surfaceFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) return Result::Success(surfaceFormat); } } return Result::Error(BasicErrorCode::NotFound); } static VkPresentModeKHR ChoosePresentMode(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface) { SmallVector presentModes; VkEnumerate(vkGetPhysicalDeviceSurfacePresentModesKHR, presentModes, physicalDevice, surface); //for (auto& presentMode : presentModes) { if (presentMode == VK_PRESENT_MODE_MAILBOX_KHR) return presentMode; } //for (auto& presentMode : presentModes) { if (presentMode == VK_PRESENT_MODE_IMMEDIATE_KHR) return presentMode; } return VK_PRESENT_MODE_FIFO_KHR; } static VkExtent2D ChooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities) { if (capabilities.currentExtent.width != ~0u) return capabilities.currentExtent; uint32_t width = 1280; uint32_t height = 800; return VkExtent2D { Clamp(width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width), Clamp(height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height) }; } static uint32_t ChooseImageCount(const VkSurfaceCapabilitiesKHR& capabilities) { uint32_t imageCount = 3; if (capabilities.minImageCount != 0) imageCount = Max(imageCount, capabilities.minImageCount); if (capabilities.maxImageCount != 0) imageCount = Min(imageCount, capabilities.maxImageCount); return imageCount; } Result Swapchain::Create(RenderContext& context, VkSurfaceKHR surface, bool hdr) { VkSurfaceCapabilitiesKHR capabilities; vkGetPhysicalDeviceSurfaceCapabilitiesKHR(context.PhysicalDevice(), surface, &capabilities); Result r_format = ChooseSwapChainFormat(context.PhysicalDevice(), surface, hdr); if (!r_format) return Result::PrintForwardError(r_format, "Failed to pick swapchain format"); const VkExtent2D extent = ChooseSwapExtent(capabilities); VkFormat viewFormats[2] = { r_format->format, FormatToSrgbFormat(r_format->format), }; VkImageFormatListCreateInfo formatListInfo = { .sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO, .viewFormatCount = Size(viewFormats), .pViewFormats = viewFormats, }; VkSwapchainCreateInfoKHR swapchainInfo = { .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, .pNext = &formatListInfo, .flags = VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR, .surface = surface, .minImageCount = ChooseImageCount(capabilities), .imageFormat = r_format->format, .imageColorSpace = r_format->colorSpace, .imageExtent = extent, .imageArrayLayers = 1, .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE, .preTransform = capabilities.currentTransform, .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, .presentMode = ChoosePresentMode(context.PhysicalDevice(), surface), .clipped = VK_TRUE, .oldSwapchain = VK_NULL_HANDLE, // TODO }; VkSwapchainKHR swapchain = VK_NULL_HANDLE; if (vkCreateSwapchainKHR(context.Device(), &swapchainInfo, nullptr, &swapchain) != VK_SUCCESS) return Result::PrintError("Failed to create swapchain"); SmallVector swapchainImages; if (!VkEnumerate(vkGetSwapchainImagesKHR, swapchainImages, context.Device(), swapchain)) return Result::PrintError("Failed to get swapchain images"); SmallVector swapchainImageViews{ swapchainImages.size() }; for (size_t i = 0; i < swapchainImages.size(); i++) { VkImageViewCreateInfo imageViewInfo = { .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, .image = swapchainImages[i], .viewType = VK_IMAGE_VIEW_TYPE_2D, .format = FormatToSrgbFormat(r_format->format), .subresourceRange = FirstColorMipSubresourceRange, }; if (vkCreateImageView(context.Device(), &imageViewInfo, nullptr, &swapchainImageViews[i]) != VK_SUCCESS) return Result::PrintError("Failed to get swapchain image view"); } SmallVector commandBuffers{ DefaultFramesInFlight }; { VkCommandBufferAllocateInfo commandBufferInfo = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, .commandPool = context.CommandPool(), .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, .commandBufferCount = DefaultFramesInFlight, }; if (vkAllocateCommandBuffers(context.Device(), &commandBufferInfo, commandBuffers.data()) != VK_SUCCESS) return Result::PrintError("Failed to get swapchain image view"); } SmallVector imageAvailableSemaphores{ DefaultFramesInFlight }; SmallVector renderFinishedSemaphores{ DefaultFramesInFlight }; SmallVector inFlightFences { DefaultFramesInFlight }; for (uint32_t i = 0; i < DefaultFramesInFlight; i++) { auto r_fence = context.CreateFence(true); if (!r_fence) return Result::PrintError("Failed to create fence for swapchain"); inFlightFences[i] = *r_fence; auto r_imageSem = context.CreateSemaphore(); if (!r_imageSem) return Result::PrintError("Failed to create semaphore for swapchain"); imageAvailableSemaphores[i] = *r_imageSem; auto r_renderSem = context.CreateSemaphore(); if (!r_renderSem) return Result::PrintError("Failed to create semaphore for swapchain"); renderFinishedSemaphores[i] = *r_renderSem; } // TODO: Handle failure uint32_t imageIndex; vkAcquireNextImageKHR(context.Device(), swapchain, ~0u, imageAvailableSemaphores[0], VK_NULL_HANDLE, &imageIndex); return Result::Success( context, surface, r_format->format, extent, swapchain, swapchainImages, swapchainImageViews, commandBuffers, imageAvailableSemaphores, renderFinishedSemaphores, inFlightFences, imageIndex); } Swapchain::~Swapchain() { for (const auto& imageView : m_imageViews) vkDestroyImageView(m_ctx.Device(), imageView, nullptr); vkDestroySwapchainKHR(m_ctx.Device(), m_swapchain, nullptr); } void Swapchain::Present() { VkResult res = VK_SUCCESS; vkResetFences(m_ctx.Device(), 1, &m_inFlightFences[m_currentFrame]); const VkPipelineStageFlags waitStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; VkSubmitInfo submitInfo = { .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .waitSemaphoreCount = 1, .pWaitSemaphores = &m_imageAvailableSemaphores[m_currentFrame], .pWaitDstStageMask = &waitStage, .commandBufferCount = 1, .pCommandBuffers = &m_commandBuffers[m_currentFrame], .signalSemaphoreCount = 1, .pSignalSemaphores = &m_renderFinishedSemaphores[m_currentFrame], }; // TODO: Handle failure properly. if ((res = vkQueueSubmit(m_ctx.Queue(), 1, &submitInfo, m_inFlightFences[m_currentFrame])) != VK_SUCCESS) { log::err("Failed to submit work: %d", res); return; } VkPresentInfoKHR presentInfo = { .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, .waitSemaphoreCount = 1, .pWaitSemaphores = &m_renderFinishedSemaphores[m_currentFrame], .swapchainCount = 1, .pSwapchains = &m_swapchain, .pImageIndices = &m_currentImage, }; res = vkQueuePresentKHR(m_ctx.Queue(), &presentInfo); if (res != VK_SUCCESS && res != VK_SUBOPTIMAL_KHR) { log::err("Failed to submit present: %d", res); return; } // Prepare for next frame. m_currentFrame = (m_currentFrame + 1) % DefaultFramesInFlight; vkWaitForFences(m_ctx.Device(), 1, &m_inFlightFences[m_currentFrame], VK_TRUE, UINT64_MAX); vkAcquireNextImageKHR(m_ctx.Device(), m_swapchain, ~0u, m_imageAvailableSemaphores[m_currentFrame], VK_NULL_HANDLE, &m_currentImage); vkResetCommandBuffer(m_commandBuffers[m_currentFrame], 0u); } }