diff --git a/src/Cafe/CMakeLists.txt b/src/Cafe/CMakeLists.txt index c4f9ce30..61b4815e 100644 --- a/src/Cafe/CMakeLists.txt +++ b/src/Cafe/CMakeLists.txt @@ -178,6 +178,8 @@ add_library(CemuCafe HW/Latte/Renderer/Vulkan/LatteTextureVk.h HW/Latte/Renderer/Vulkan/RendererShaderVk.cpp HW/Latte/Renderer/Vulkan/RendererShaderVk.h + HW/Latte/Renderer/Vulkan/SwapchainInfoVk.cpp + HW/Latte/Renderer/Vulkan/SwapchainInfoVk.h HW/Latte/Renderer/Vulkan/TextureReadbackVk.cpp HW/Latte/Renderer/Vulkan/VKRBase.h HW/Latte/Renderer/Vulkan/VKRMemoryManager.cpp diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/SwapchainInfoVk.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/SwapchainInfoVk.cpp new file mode 100644 index 00000000..fd227106 --- /dev/null +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/SwapchainInfoVk.cpp @@ -0,0 +1,367 @@ +#include "SwapchainInfoVk.h" + +#include "config/CemuConfig.h" +#include "Cafe/HW/Latte/Core/Latte.h" +#include "Cafe/HW/Latte/Core/LatteTiming.h" +#include "Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h" + +void SwapchainInfoVk::Create(VkPhysicalDevice physicalDevice, VkDevice logicalDevice) +{ + m_physicalDevice = physicalDevice; + m_logicalDevice = logicalDevice; + const auto details = QuerySwapchainSupport(surface, physicalDevice); + m_surfaceFormat = ChooseSurfaceFormat(details.formats); + swapchainExtent = ChooseSwapExtent(details.capabilities, getSize()); + + // calculate number of swapchain presentation images + uint32_t image_count = details.capabilities.minImageCount + 1; + if (details.capabilities.maxImageCount > 0 && image_count > details.capabilities.maxImageCount) + image_count = details.capabilities.maxImageCount; + + VkSwapchainCreateInfoKHR create_info = CreateSwapchainCreateInfo(surface, details, m_surfaceFormat, image_count, swapchainExtent); + create_info.oldSwapchain = nullptr; + create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; + + VkResult result = vkCreateSwapchainKHR(logicalDevice, &create_info, nullptr, &swapchain); + if (result != VK_SUCCESS) + UnrecoverableError("Error attempting to create a swapchain"); + + sizeOutOfDate = false; + + result = vkGetSwapchainImagesKHR(logicalDevice, swapchain, &image_count, nullptr); + if (result != VK_SUCCESS) + UnrecoverableError("Error attempting to retrieve the count of swapchain images"); + + + m_swapchainImages.resize(image_count); + result = vkGetSwapchainImagesKHR(logicalDevice, swapchain, &image_count, m_swapchainImages.data()); + if (result != VK_SUCCESS) + UnrecoverableError("Error attempting to retrieve swapchain images"); + // create default renderpass + VkAttachmentDescription colorAttachment = {}; + colorAttachment.format = m_surfaceFormat.format; + colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT; + colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + + VkAttachmentReference colorAttachmentRef = {}; + colorAttachmentRef.attachment = 0; + colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + VkSubpassDescription subpass = {}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &colorAttachmentRef; + + VkRenderPassCreateInfo renderPassInfo = {}; + renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + renderPassInfo.attachmentCount = 1; + renderPassInfo.pAttachments = &colorAttachment; + renderPassInfo.subpassCount = 1; + renderPassInfo.pSubpasses = &subpass; + result = vkCreateRenderPass(logicalDevice, &renderPassInfo, nullptr, &m_swapchainRenderPass); + if (result != VK_SUCCESS) + UnrecoverableError("Failed to create renderpass for swapchain"); + + // create swapchain image views + m_swapchainImageViews.resize(m_swapchainImages.size()); + for (sint32 i = 0; i < m_swapchainImages.size(); i++) + { + VkImageViewCreateInfo createInfo = {}; + createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + createInfo.image = m_swapchainImages[i]; + createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + createInfo.format = m_surfaceFormat.format; + createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + createInfo.subresourceRange.baseMipLevel = 0; + createInfo.subresourceRange.levelCount = 1; + createInfo.subresourceRange.baseArrayLayer = 0; + createInfo.subresourceRange.layerCount = 1; + result = vkCreateImageView(logicalDevice, &createInfo, nullptr, &m_swapchainImageViews[i]); + if (result != VK_SUCCESS) + UnrecoverableError("Failed to create imageviews for swapchain"); + } + + // create swapchain framebuffers + m_swapchainFramebuffers.resize(m_swapchainImages.size()); + for (size_t i = 0; i < m_swapchainImages.size(); i++) + { + VkImageView attachments[1]; + attachments[0] = m_swapchainImageViews[i]; + // create framebuffer + VkFramebufferCreateInfo framebufferInfo = {}; + framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + framebufferInfo.renderPass = m_swapchainRenderPass; + framebufferInfo.attachmentCount = 1; + framebufferInfo.pAttachments = attachments; + framebufferInfo.width = swapchainExtent.width; + framebufferInfo.height = swapchainExtent.height; + framebufferInfo.layers = 1; + result = vkCreateFramebuffer(logicalDevice, &framebufferInfo, nullptr, &m_swapchainFramebuffers[i]); + if (result != VK_SUCCESS) + UnrecoverableError("Failed to create framebuffer for swapchain"); + } + m_swapchainPresentSemaphores.resize(m_swapchainImages.size()); + // create present semaphore + VkSemaphoreCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + for (auto& semaphore : m_swapchainPresentSemaphores){ + if (vkCreateSemaphore(logicalDevice, &info, nullptr, &semaphore) != VK_SUCCESS) + UnrecoverableError("Failed to create semaphore for swapchain present"); + } + + m_acquireSemaphores.resize(m_swapchainImages.size()); + for (auto& semaphore : m_acquireSemaphores) + { + VkSemaphoreCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + if (vkCreateSemaphore(logicalDevice, &info, nullptr, &semaphore) != VK_SUCCESS) + UnrecoverableError("Failed to create semaphore for swapchain acquire"); + } + m_acquireIndex = 0; + + VkFenceCreateInfo fenceInfo = {}; + fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; + result = vkCreateFence(logicalDevice, &fenceInfo, nullptr, &m_imageAvailableFence); + if (result != VK_SUCCESS) + UnrecoverableError("Failed to create fence for swapchain"); +} + +void SwapchainInfoVk::Cleanup() +{ + m_swapchainImages.clear(); + + for (auto& sem: m_swapchainPresentSemaphores) + vkDestroySemaphore(m_logicalDevice, sem, nullptr); + m_swapchainPresentSemaphores.clear(); + + for (auto& itr: m_acquireSemaphores) + vkDestroySemaphore(m_logicalDevice, itr, nullptr); + m_acquireSemaphores.clear(); + + if (m_swapchainRenderPass) + { + vkDestroyRenderPass(m_logicalDevice, m_swapchainRenderPass, nullptr); + m_swapchainRenderPass = nullptr; + } + + for (auto& imageView : m_swapchainImageViews) + vkDestroyImageView(m_logicalDevice, imageView, nullptr); + m_swapchainImageViews.clear(); + + for (auto& framebuffer : m_swapchainFramebuffers) + vkDestroyFramebuffer(m_logicalDevice, framebuffer, nullptr); + m_swapchainFramebuffers.clear(); + + + if (m_imageAvailableFence) + { + vkDestroyFence(m_logicalDevice, m_imageAvailableFence, nullptr); + m_imageAvailableFence = nullptr; + } + if (swapchain) + { + vkDestroySwapchainKHR(m_logicalDevice, swapchain, nullptr); + swapchain = VK_NULL_HANDLE; + } +} + +bool SwapchainInfoVk::IsValid() const +{ + return swapchain && m_imageAvailableFence; +} + +void SwapchainInfoVk::UnrecoverableError(const char* errMsg) +{ + forceLog_printf("Unrecoverable error in Vulkan swapchain"); + forceLog_printf("Msg: %s", errMsg); + throw std::runtime_error(errMsg); +} + +SwapchainInfoVk::QueueFamilyIndices SwapchainInfoVk::FindQueueFamilies(VkSurfaceKHR surface, VkPhysicalDevice device) +{ + uint32_t queueFamilyCount = 0; + vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr); + + std::vector queueFamilies(queueFamilyCount); + vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data()); + + QueueFamilyIndices indices; + for (int i = 0; i < (int)queueFamilies.size(); ++i) + { + const auto& queueFamily = queueFamilies[i]; + if (queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) + indices.graphicsFamily = i; + + VkBool32 presentSupport = false; + const VkResult result = vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport); + if (result != VK_SUCCESS) + throw std::runtime_error(fmt::format("Error while attempting to check if a surface supports presentation: {}", result)); + + if (queueFamily.queueCount > 0 && presentSupport) + indices.presentFamily = i; + + if (indices.IsComplete()) + break; + } + + return indices; +} + +SwapchainInfoVk::SwapchainSupportDetails SwapchainInfoVk::QuerySwapchainSupport(VkSurfaceKHR surface, const VkPhysicalDevice& device) +{ + SwapchainSupportDetails details; + + VkResult result = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities); + if (result != VK_SUCCESS) + { + if (result != VK_ERROR_SURFACE_LOST_KHR) + forceLog_printf("vkGetPhysicalDeviceSurfaceCapabilitiesKHR failed. Error %d", (sint32)result); + throw std::runtime_error(fmt::format("Unable to retrieve physical device surface capabilities: {}", result)); + } + + uint32_t formatCount = 0; + result = vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr); + if (result != VK_SUCCESS) + { + forceLog_printf("vkGetPhysicalDeviceSurfaceFormatsKHR failed. Error %d", (sint32)result); + throw std::runtime_error(fmt::format("Unable to retrieve the number of formats for a surface on a physical device: {}", result)); + } + + if (formatCount != 0) + { + details.formats.resize(formatCount); + result = vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, details.formats.data()); + if (result != VK_SUCCESS) + { + forceLog_printf("vkGetPhysicalDeviceSurfaceFormatsKHR failed. Error %d", (sint32)result); + throw std::runtime_error(fmt::format("Unable to retrieve the formats for a surface on a physical device: {}", result)); + } + } + + uint32_t presentModeCount = 0; + result = vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, nullptr); + if (result != VK_SUCCESS) + { + forceLog_printf("vkGetPhysicalDeviceSurfacePresentModesKHR failed. Error %d", (sint32)result); + throw std::runtime_error(fmt::format("Unable to retrieve the count of present modes for a surface on a physical device: {}", result)); + } + + if (presentModeCount != 0) + { + details.presentModes.resize(presentModeCount); + result = vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, details.presentModes.data()); + if (result != VK_SUCCESS) + { + forceLog_printf("vkGetPhysicalDeviceSurfacePresentModesKHR failed. Error %d", (sint32)result); + throw std::runtime_error(fmt::format("Unable to retrieve the present modes for a surface on a physical device: {}", result)); + } + } + + return details; +} + +VkSurfaceFormatKHR SwapchainInfoVk::ChooseSurfaceFormat(const std::vector& formats) const +{ + if (formats.size() == 1 && formats[0].format == VK_FORMAT_UNDEFINED) + return{ VK_FORMAT_B8G8R8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR }; + + for (const auto& format : formats) + { + bool useSRGB = mainWindow ? LatteGPUState.tvBufferUsesSRGB : LatteGPUState.drcBufferUsesSRGB; + + if (useSRGB) + { + if (format.format == VK_FORMAT_B8G8R8A8_SRGB && format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) + return format; + } + else + { + if (format.format == VK_FORMAT_B8G8R8A8_UNORM && format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) + return format; + } + } + + return formats[0]; +} + +VkExtent2D SwapchainInfoVk::ChooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities, const Vector2i& size) const +{ + if (capabilities.currentExtent.width != std::numeric_limits::max()) + return capabilities.currentExtent; + + VkExtent2D actualExtent = { (uint32)size.x, (uint32)size.y }; + actualExtent.width = std::max(capabilities.minImageExtent.width, std::min(capabilities.maxImageExtent.width, actualExtent.width)); + actualExtent.height = std::max(capabilities.minImageExtent.height, std::min(capabilities.maxImageExtent.height, actualExtent.height)); + return actualExtent; +} + +VkPresentModeKHR SwapchainInfoVk::ChoosePresentMode(const std::vector& modes) +{ + const auto vsyncState = (VSync)GetConfig().vsync.GetValue(); + if (vsyncState == VSync::MAILBOX) + { + if (std::find(modes.cbegin(), modes.cend(), VK_PRESENT_MODE_MAILBOX_KHR) != modes.cend()) + return VK_PRESENT_MODE_MAILBOX_KHR; + + forceLog_printf("Vulkan: Can't find mailbox present mode"); + } + else if (vsyncState == VSync::Immediate) + { + if (std::find(modes.cbegin(), modes.cend(), VK_PRESENT_MODE_IMMEDIATE_KHR) != modes.cend()) + return VK_PRESENT_MODE_IMMEDIATE_KHR; + + forceLog_printf("Vulkan: Can't find immediate present mode"); + } + else if (vsyncState == VSync::SYNC_AND_LIMIT) + { + LatteTiming_EnableHostDrivenVSync(); + // use immediate mode if available, other wise fall back to + //if (std::find(modes.cbegin(), modes.cend(), VK_PRESENT_MODE_IMMEDIATE_KHR) != modes.cend()) + // return VK_PRESENT_MODE_IMMEDIATE_KHR; + //else + // forceLog_printf("Vulkan: Present mode 'immediate' not available. Vsync might not behave as intended"); + return VK_PRESENT_MODE_FIFO_KHR; + } + + return VK_PRESENT_MODE_FIFO_KHR; +} + +VkSwapchainCreateInfoKHR SwapchainInfoVk::CreateSwapchainCreateInfo(VkSurfaceKHR surface, const SwapchainSupportDetails& swapchainSupport, const VkSurfaceFormatKHR& surfaceFormat, uint32 imageCount, const VkExtent2D& extent) +{ + VkSwapchainCreateInfoKHR createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + createInfo.surface = surface; + createInfo.minImageCount = imageCount; + createInfo.imageFormat = surfaceFormat.format; + createInfo.imageExtent = extent; + createInfo.imageArrayLayers = 1; + createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + + const QueueFamilyIndices indices = FindQueueFamilies(surface, m_physicalDevice); + uint32_t queueFamilyIndices[] = { (uint32)indices.graphicsFamily, (uint32)indices.presentFamily }; + if (indices.graphicsFamily != indices.presentFamily) + { + createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; + createInfo.queueFamilyIndexCount = 2; + createInfo.pQueueFamilyIndices = queueFamilyIndices; + } + else + createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + + createInfo.preTransform = swapchainSupport.capabilities.currentTransform; + createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + createInfo.presentMode = ChoosePresentMode(swapchainSupport.presentModes); + createInfo.clipped = VK_TRUE; + + forceLogDebug_printf("vulkan presentation mode: %d", createInfo.presentMode); + return createInfo; +} diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/SwapchainInfoVk.h b/src/Cafe/HW/Latte/Renderer/Vulkan/SwapchainInfoVk.h new file mode 100644 index 00000000..9201c30a --- /dev/null +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/SwapchainInfoVk.h @@ -0,0 +1,101 @@ +#pragma once + +#include "util/math/vector2.h" +#include + +struct SwapchainInfoVk +{ + enum class VSync + { + // values here must match GeneralSettings2::m_vsync + Immediate = 0, + FIFO = 1, + MAILBOX = 2, + SYNC_AND_LIMIT = 3, // synchronize emulated vsync events to monitor vsync. But skip events if rate higher than virtual vsync period + }; + + struct QueueFamilyIndices + { + int32_t graphicsFamily = -1; + int32_t presentFamily = -1; + + bool IsComplete() const { return graphicsFamily >= 0 && presentFamily >= 0; } + }; + + struct SwapchainSupportDetails + { + VkSurfaceCapabilitiesKHR capabilities; + std::vector formats; + std::vector presentModes; + }; + + void Cleanup(); + void Create(VkPhysicalDevice physicalDevice, VkDevice logicalDevice); + + bool IsValid() const; + + static void UnrecoverableError(const char* errMsg); + + // todo: move this function somewhere more sensible. Not directly swapchain related + static QueueFamilyIndices FindQueueFamilies(VkSurfaceKHR surface, VkPhysicalDevice device); + static SwapchainSupportDetails QuerySwapchainSupport(VkSurfaceKHR surface, const VkPhysicalDevice& device); + + VkPresentModeKHR ChoosePresentMode(const std::vector& modes); + VkSurfaceFormatKHR ChooseSurfaceFormat(const std::vector& formats) const; + VkExtent2D ChooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities, const Vector2i& size) const; + + VkSwapchainCreateInfoKHR CreateSwapchainCreateInfo(VkSurfaceKHR surface, const SwapchainSupportDetails& swapchainSupport, const VkSurfaceFormatKHR& surfaceFormat, uint32 imageCount, const VkExtent2D& extent); + + + void setSize(const Vector2i& newSize) + { + desiredExtent = newSize; + sizeOutOfDate = true; + } + + const Vector2i& getSize() const + { + return desiredExtent; + } + + SwapchainInfoVk(VkSurfaceKHR surface, bool mainWindow) + : surface(surface), mainWindow(mainWindow) {} + SwapchainInfoVk(const SwapchainInfoVk&) = delete; + SwapchainInfoVk(SwapchainInfoVk&&) noexcept = default; + ~SwapchainInfoVk() + { + Cleanup(); + } + + bool mainWindow{}; + + bool sizeOutOfDate{}; + bool m_usesSRGB = false; + bool hasDefinedSwapchainImage{}; // indicates if the swapchain image is in a defined state + + VkPhysicalDevice m_physicalDevice{}; + VkDevice m_logicalDevice{}; + VkSurfaceKHR surface{}; + VkSurfaceFormatKHR m_surfaceFormat{}; + VkSwapchainKHR swapchain{}; + VkExtent2D swapchainExtent{}; + VkFence m_imageAvailableFence{}; + uint32 swapchainImageIndex = (uint32)-1; + uint32 m_acquireIndex = 0; // increases with every successful vkAcquireNextImageKHR + VSync m_vsyncState = VSync::Immediate; + + + // swapchain image ringbuffer (indexed by swapchainImageIndex) + std::vector m_swapchainImages; + std::vector m_swapchainImageViews; + std::vector m_swapchainFramebuffers; + std::vector m_swapchainPresentSemaphores; + std::vector m_acquireSemaphores; // indexed by acquireIndex + + VkRenderPass m_swapchainRenderPass = nullptr; + + + +private: + Vector2i desiredExtent; +}; diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp index cbe1a142..f3a3ab46 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp @@ -180,7 +180,7 @@ void VulkanRenderer::DetermineVendor() if (m_featureControl.deviceExtensions.driver_properties) properties.pNext = &driverProperties; - vkGetPhysicalDeviceProperties2(m_physical_device, &properties); + vkGetPhysicalDeviceProperties2(m_physicalDevice, &properties); switch (properties.properties.vendorID) { case 0x10DE: @@ -241,7 +241,7 @@ void VulkanRenderer::GetDeviceFeatures() physicalDeviceFeatures2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; physicalDeviceFeatures2.pNext = &pcc; - vkGetPhysicalDeviceFeatures2(m_physical_device, &physicalDeviceFeatures2); + vkGetPhysicalDeviceFeatures2(m_physicalDevice, &physicalDeviceFeatures2); m_featureControl.deviceExtensions.pipeline_creation_cache_control = pcc.pipelineCreationCacheControl; m_featureControl.deviceExtensions.custom_border_color_without_format = m_featureControl.deviceExtensions.custom_border_color && bcf.customBorderColorWithoutFormat; @@ -267,7 +267,7 @@ void VulkanRenderer::GetDeviceFeatures() // retrieve limits VkPhysicalDeviceProperties2 p2{}; p2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; - vkGetPhysicalDeviceProperties2(m_physical_device, &p2); + vkGetPhysicalDeviceProperties2(m_physicalDevice, &p2); m_featureControl.limits.minUniformBufferOffsetAlignment = std::max(p2.properties.limits.minUniformBufferOffsetAlignment, (VkDeviceSize)4); m_featureControl.limits.nonCoherentAtomSize = std::max(p2.properties.limits.nonCoherentAtomSize, (VkDeviceSize)4); cemuLog_log(LogType::Force, fmt::format("VulkanLimits: UBAlignment {0} nonCoherentAtomSize {1}", p2.properties.limits.minUniformBufferOffsetAlignment, p2.properties.limits.nonCoherentAtomSize)); @@ -358,24 +358,24 @@ VulkanRenderer::VulkanRenderer() continue; } - m_physical_device = device; + m_physicalDevice = device; break; } } - if (m_physical_device == VK_NULL_HANDLE && fallbackDevice != VK_NULL_HANDLE) + if (m_physicalDevice == VK_NULL_HANDLE && fallbackDevice != VK_NULL_HANDLE) { forceLog_printf("The selected GPU could not be found or is not suitable. Falling back to first available device instead"); - m_physical_device = fallbackDevice; + m_physicalDevice = fallbackDevice; config.graphic_device_uuid = {}; // resetting device selection } - else if (m_physical_device == VK_NULL_HANDLE) + else if (m_physicalDevice == VK_NULL_HANDLE) { forceLog_printf("No physical GPU could be found with the required extensions and swap chain support."); throw std::runtime_error("No physical GPU could be found with the required extensions and swap chain support."); } - CheckDeviceExtensionSupport(m_physical_device, m_featureControl); // todo - merge this with GetDeviceFeatures and separate from IsDeviceSuitable? + CheckDeviceExtensionSupport(m_physicalDevice, m_featureControl); // todo - merge this with GetDeviceFeatures and separate from IsDeviceSuitable? if (m_featureControl.debugMarkersSupported) forceLog_printf("Debug: Frame debugger attached, will use vkDebugMarkerSetObjectNameEXT"); @@ -390,7 +390,7 @@ VulkanRenderer::VulkanRenderer() VkPhysicalDeviceIDProperties physDeviceIDProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES }; VkPhysicalDeviceProperties2 physDeviceProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2 }; physDeviceProps.pNext = &physDeviceIDProps; - vkGetPhysicalDeviceProperties2(m_physical_device, &physDeviceProps); + vkGetPhysicalDeviceProperties2(m_physicalDevice, &physDeviceProps); #if BOOST_OS_WINDOWS m_dxgi_wrapper = std::make_unique(physDeviceIDProps.deviceLUID); @@ -402,7 +402,7 @@ VulkanRenderer::VulkanRenderer() } // create logical device - m_indices = FindQueueFamilies(surface, m_physical_device); + m_indices = SwapchainInfoVk::FindQueueFamilies(surface, m_physicalDevice); std::set uniqueQueueFamilies = { m_indices.graphicsFamily, m_indices.presentFamily }; std::vector queueCreateInfos = CreateQueueCreateInfos(uniqueQueueFamilies); VkPhysicalDeviceFeatures deviceFeatures = {}; @@ -452,7 +452,7 @@ VulkanRenderer::VulkanRenderer() std::vector used_extensions; VkDeviceCreateInfo createInfo = CreateDeviceCreateInfo(queueCreateInfos, deviceFeatures, deviceExtensionFeatures, used_extensions); - VkResult result = vkCreateDevice(m_physical_device, &createInfo, nullptr, &m_logicalDevice); + VkResult result = vkCreateDevice(m_physicalDevice, &createInfo, nullptr, &m_logicalDevice); if (result != VK_SUCCESS) { forceLog_printf("Vulkan: Unable to create a logical device. Error %d", (sint32)result); @@ -557,7 +557,7 @@ VulkanRenderer::VulkanRenderer() VulkanRenderer::~VulkanRenderer() { SubmitCommandBuffer(); - vkDeviceWaitIdle(m_logicalDevice); + WaitDeviceIdle(); WaitCommandBufferFinished(GetCurrentCommandBufferId()); // shut down pipeline save thread m_destructionRequested = true; @@ -643,26 +643,39 @@ VulkanRenderer* VulkanRenderer::GetInstance() return (VulkanRenderer*)g_renderer.get(); } -void VulkanRenderer::Initialize(const Vector2i& size, bool isMainWindow) +void VulkanRenderer::Initialize(const Vector2i& size, bool mainWindow) { - auto& windowHandleInfo = isMainWindow ? gui_getWindowInfo().canvas_main : gui_getWindowInfo().canvas_pad; + auto& windowHandleInfo = mainWindow ? gui_getWindowInfo().canvas_main : gui_getWindowInfo().canvas_pad; const auto surface = CreateFramebufferSurface(m_instance, windowHandleInfo); - if (isMainWindow) + if (mainWindow) { - m_mainSwapchainInfo = std::make_unique(m_logicalDevice, surface); - CreateSwapChain(*m_mainSwapchainInfo, size, isMainWindow); + m_mainSwapchainInfo = std::make_unique(surface, mainWindow); + SetSwapchainTargetSize(size, mainWindow); + m_mainSwapchainInfo->Create(m_physicalDevice, m_logicalDevice); // aquire first command buffer InitFirstCommandBuffer(); } else { - m_padSwapchainInfo = std::make_unique(m_logicalDevice, surface); - CreateSwapChain(*m_padSwapchainInfo, size, isMainWindow); + m_padSwapchainInfo = std::make_unique(surface, mainWindow); + SetSwapchainTargetSize(size, mainWindow); + // todo: figure out a way to exclusively create swapchain on main LatteThread + m_padSwapchainInfo->Create(m_physicalDevice, m_logicalDevice); } } +const std::unique_ptr& VulkanRenderer::GetChainInfoPtr(bool mainWindow) const +{ + return mainWindow ? m_mainSwapchainInfo : m_padSwapchainInfo; +} + +SwapchainInfoVk& VulkanRenderer::GetChainInfo(bool mainWindow) const +{ + return *GetChainInfoPtr(mainWindow); +} + bool VulkanRenderer::IsPadWindowActive() { return IsSwapchainInfoValid(false); @@ -712,13 +725,13 @@ void VulkanRenderer::HandleScreenshotRequest(LatteTextureView* texView, bool pad if (format != VK_FORMAT_R8G8B8A8_UNORM && format != VK_FORMAT_R8G8B8A8_SRGB && format != VK_FORMAT_R8G8B8_UNORM && format != VK_FORMAT_R8G8B8_SNORM) { VkFormatProperties formatProps; - vkGetPhysicalDeviceFormatProperties(m_physical_device, format, &formatProps); + vkGetPhysicalDeviceFormatProperties(m_physicalDevice, format, &formatProps); bool supportsBlit = (formatProps.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT) != 0; const bool dstUsesSRGB = (!padView && LatteGPUState.tvBufferUsesSRGB) || (padView && LatteGPUState.drcBufferUsesSRGB); const auto blitFormat = dstUsesSRGB ? VK_FORMAT_R8G8B8A8_SRGB : VK_FORMAT_R8G8B8A8_UNORM; - vkGetPhysicalDeviceFormatProperties(m_physical_device, blitFormat, &formatProps); + vkGetPhysicalDeviceFormatProperties(m_physicalDevice, blitFormat, &formatProps); supportsBlit &= (formatProps.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT) != 0; // convert texture using blitting @@ -958,18 +971,9 @@ void VulkanRenderer::HandleScreenshotRequest(LatteTextureView* texView, bool pad SaveScreenshot(rgb_data, width, height, !padView); } -void VulkanRenderer::ResizeSwapchain(const Vector2i& size, bool isMainWindow) +void VulkanRenderer::SetSwapchainTargetSize(const Vector2i& size, bool mainWindow) { - if (isMainWindow) - { - m_swapchainState.newExtentMainWindow = size; - m_swapchainState.resizeRequestedMainWindow = true; - } - else - { - m_swapchainState.newExtentPadWindow = size; - m_swapchainState.resizeRequestedPadWindow = true; - } + GetChainInfo(mainWindow).setSize(size); } static const float kQueuePriority = 1.0f; @@ -1050,36 +1054,6 @@ void VulkanRenderer::shader_unbind(RendererShader::ShaderType shaderType) // does nothing on Vulkan } -VulkanRenderer::QueueFamilyIndices VulkanRenderer::FindQueueFamilies(VkSurfaceKHR surface, const VkPhysicalDevice& device) -{ - uint32_t queueFamilyCount = 0; - vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr); - - std::vector queueFamilies(queueFamilyCount); - vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data()); - - QueueFamilyIndices indices; - for (int i = 0; i < (int)queueFamilies.size(); ++i) - { - const auto& queueFamily = queueFamilies[i]; - if (queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) - indices.graphicsFamily = i; - - VkBool32 presentSupport = false; - const VkResult result = vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport); - if (result != VK_SUCCESS) - throw std::runtime_error(fmt::format("Error while attempting to check if a surface supports presentation: {}", result)); - - if (queueFamily.queueCount > 0 && presentSupport) - indices.presentFamily = i; - - if (indices.IsComplete()) - break; - } - - return indices; -} - bool VulkanRenderer::CheckDeviceExtensionSupport(const VkPhysicalDevice device, FeatureControl& info) { std::vector availableDeviceExtensions; @@ -1212,62 +1186,9 @@ std::vector VulkanRenderer::CheckInstanceExtensionSupport(FeatureCo return enabledInstanceExtensions; } -VulkanRenderer::SwapChainSupportDetails VulkanRenderer::QuerySwapChainSupport(VkSurfaceKHR surface, const VkPhysicalDevice& device) -{ - SwapChainSupportDetails details; - - VkResult result = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities); - if (result != VK_SUCCESS) - { - if (result != VK_ERROR_SURFACE_LOST_KHR) - forceLog_printf("vkGetPhysicalDeviceSurfaceCapabilitiesKHR failed. Error %d", (sint32)result); - throw std::runtime_error(fmt::format("Unable to retrieve physical device surface capabilities: {}", result)); - } - - uint32_t formatCount = 0; - result = vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr); - if (result != VK_SUCCESS) - { - forceLog_printf("vkGetPhysicalDeviceSurfaceFormatsKHR failed. Error %d", (sint32)result); - throw std::runtime_error(fmt::format("Unable to retrieve the number of formats for a surface on a physical device: {}", result)); - } - - if (formatCount != 0) - { - details.formats.resize(formatCount); - result = vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, details.formats.data()); - if (result != VK_SUCCESS) - { - forceLog_printf("vkGetPhysicalDeviceSurfaceFormatsKHR failed. Error %d", (sint32)result); - throw std::runtime_error(fmt::format("Unable to retrieve the formats for a surface on a physical device: {}", result)); - } - } - - uint32_t presentModeCount = 0; - result = vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, nullptr); - if (result != VK_SUCCESS) - { - forceLog_printf("vkGetPhysicalDeviceSurfacePresentModesKHR failed. Error %d", (sint32)result); - throw std::runtime_error(fmt::format("Unable to retrieve the count of present modes for a surface on a physical device: {}", result)); - } - - if (presentModeCount != 0) - { - details.presentModes.resize(presentModeCount); - result = vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, details.presentModes.data()); - if (result != VK_SUCCESS) - { - forceLog_printf("vkGetPhysicalDeviceSurfacePresentModesKHR failed. Error %d", (sint32)result); - throw std::runtime_error(fmt::format("Unable to retrieve the present modes for a surface on a physical device: {}", result)); - } - } - - return details; -} - bool VulkanRenderer::IsDeviceSuitable(VkSurfaceKHR surface, const VkPhysicalDevice& device) { - if (!FindQueueFamilies(surface, device).IsComplete()) + if (!SwapchainInfoVk::FindQueueFamilies(surface, device).IsComplete()) return false; // check API version (using Vulkan 1.0 way of querying properties) @@ -1282,9 +1203,9 @@ bool VulkanRenderer::IsDeviceSuitable(VkSurfaceKHR surface, const VkPhysicalDevi if (!CheckDeviceExtensionSupport(device, info)) return false; - const SwapChainSupportDetails swapChainSupport = QuerySwapChainSupport(surface, device); + const auto swapchainSupport = SwapchainInfoVk::QuerySwapchainSupport(surface, device); - return !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty(); + return !swapchainSupport.formats.empty() && !swapchainSupport.presentModes.empty(); } #if BOOST_OS_WINDOWS @@ -1358,70 +1279,6 @@ VkSurfaceKHR VulkanRenderer::CreateFramebufferSurface(VkInstance instance, struc #endif } -VkSurfaceFormatKHR VulkanRenderer::ChooseSwapSurfaceFormat(const std::vector& formats, bool mainWindow) const -{ - if (formats.size() == 1 && formats[0].format == VK_FORMAT_UNDEFINED) - return{ VK_FORMAT_B8G8R8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR }; - - for (const auto& format : formats) - { - if ((mainWindow && LatteGPUState.tvBufferUsesSRGB) || (!mainWindow && LatteGPUState.drcBufferUsesSRGB)) - { - if (format.format == VK_FORMAT_B8G8R8A8_SRGB && format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) - return format; - } - else - { - if (format.format == VK_FORMAT_B8G8R8A8_UNORM && format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) - return format; - } - } - - return formats[0]; -} - -VkPresentModeKHR VulkanRenderer::ChooseSwapPresentMode(const std::vector& modes) -{ - const auto vsyncState = (VSync)GetConfig().vsync.GetValue(); - if (vsyncState == VSync::MAILBOX) - { - if (std::find(modes.cbegin(), modes.cend(), VK_PRESENT_MODE_MAILBOX_KHR) != modes.cend()) - return VK_PRESENT_MODE_MAILBOX_KHR; - - forceLog_printf("Vulkan: Can't find mailbox present mode"); - } - else if (vsyncState == VSync::Immediate) - { - if (std::find(modes.cbegin(), modes.cend(), VK_PRESENT_MODE_IMMEDIATE_KHR) != modes.cend()) - return VK_PRESENT_MODE_IMMEDIATE_KHR; - - forceLog_printf("Vulkan: Can't find immediate present mode"); - } - else if (vsyncState == VSync::SYNC_AND_LIMIT) - { - LatteTiming_EnableHostDrivenVSync(); - // use immediate mode if available, other wise fall back to - //if (std::find(modes.cbegin(), modes.cend(), VK_PRESENT_MODE_IMMEDIATE_KHR) != modes.cend()) - // return VK_PRESENT_MODE_IMMEDIATE_KHR; - //else - // forceLog_printf("Vulkan: Present mode 'immediate' not available. Vsync might not behave as intended"); - return VK_PRESENT_MODE_FIFO_KHR; - } - - return VK_PRESENT_MODE_FIFO_KHR; -} - -VkExtent2D VulkanRenderer::ChooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities, const Vector2i& size) const -{ - if (capabilities.currentExtent.width != std::numeric_limits::max()) - return capabilities.currentExtent; - - VkExtent2D actualExtent = { (uint32)size.x, (uint32)size.y }; - actualExtent.width = std::max(capabilities.minImageExtent.width, std::min(capabilities.maxImageExtent.width, actualExtent.width)); - actualExtent.height = std::max(capabilities.minImageExtent.height, std::min(capabilities.maxImageExtent.height, actualExtent.height)); - return actualExtent; -} - void VulkanRenderer::CreateCommandPool() { VkCommandPoolCreateInfo poolInfo{}; @@ -1470,209 +1327,12 @@ void VulkanRenderer::CreateCommandBuffers() } } -VkSwapchainCreateInfoKHR VulkanRenderer::CreateSwapchainCreateInfo(VkSurfaceKHR surface, const SwapChainSupportDetails& swapChainSupport, const VkSurfaceFormatKHR& surfaceFormat, uint32 imageCount, const VkExtent2D& extent) -{ - VkSwapchainCreateInfoKHR createInfo{}; - createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; - createInfo.surface = surface; - createInfo.minImageCount = imageCount; - createInfo.imageFormat = surfaceFormat.format; - createInfo.imageExtent = extent; - createInfo.imageArrayLayers = 1; - createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; - - const QueueFamilyIndices indices = FindQueueFamilies(surface, m_physical_device); - uint32_t queueFamilyIndices[] = { (uint32)indices.graphicsFamily, (uint32)indices.presentFamily }; - if (indices.graphicsFamily != indices.presentFamily) - { - createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; - createInfo.queueFamilyIndexCount = 2; - createInfo.pQueueFamilyIndices = queueFamilyIndices; - } - else - createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; - - createInfo.preTransform = swapChainSupport.capabilities.currentTransform; - createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; - createInfo.presentMode = ChooseSwapPresentMode(swapChainSupport.presentModes); - createInfo.clipped = VK_TRUE; - - forceLogDebug_printf("vulkan presentation mode: %d", createInfo.presentMode); - return createInfo; -} - bool VulkanRenderer::IsSwapchainInfoValid(bool mainWindow) const { - if (mainWindow) - return m_mainSwapchainInfo && m_mainSwapchainInfo->swapChain && m_mainSwapchainInfo->m_imageAvailableFence; - - return m_padSwapchainInfo && m_padSwapchainInfo->swapChain && m_padSwapchainInfo->m_imageAvailableFence; + auto& chainInfo = GetChainInfoPtr(mainWindow); + return chainInfo && chainInfo->IsValid(); } -VkSwapchainKHR VulkanRenderer::CreateSwapChain(SwapChainInfo& swap_chain_info, const Vector2i& size, bool mainwindow) -{ - const SwapChainSupportDetails details = QuerySwapChainSupport(swap_chain_info.surface, m_physical_device); - m_swapchainFormat = ChooseSwapSurfaceFormat(details.formats, mainwindow); - swap_chain_info.swapchainExtend = ChooseSwapExtent(details.capabilities, size); - // calculate number of swapchain presentation images - uint32_t image_count = details.capabilities.minImageCount + 1; - if (details.capabilities.maxImageCount > 0 && image_count > details.capabilities.maxImageCount) - image_count = details.capabilities.maxImageCount; - - VkSwapchainCreateInfoKHR create_info = CreateSwapchainCreateInfo(swap_chain_info.surface, details, m_swapchainFormat, image_count, swap_chain_info.swapchainExtend); - create_info.oldSwapchain = swap_chain_info.swapChain; - swap_chain_info.swapChain = nullptr; - create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; - - VkResult result = vkCreateSwapchainKHR(m_logicalDevice, &create_info, nullptr, &swap_chain_info.swapChain); - if (result != VK_SUCCESS) - UnrecoverableError("Error attempting to create a swapchain"); - - result = vkGetSwapchainImagesKHR(m_logicalDevice, swap_chain_info.swapChain, &image_count, nullptr); - if (result != VK_SUCCESS) - UnrecoverableError("Error attempting to retrieve the count of swapchain images"); - - // clean up previously initialized swapchain - for (const auto& image : swap_chain_info.m_swapchainImages) - vkDestroyImage(m_logicalDevice, image, nullptr); - swap_chain_info.m_swapchainImages.clear(); - for (auto& sem : swap_chain_info.m_swapchainPresentSemaphores) - vkDestroySemaphore(m_logicalDevice, sem, nullptr); - swap_chain_info.m_swapchainPresentSemaphores.clear(); - - - swap_chain_info.m_swapchainImages.resize(image_count); - result = vkGetSwapchainImagesKHR(m_logicalDevice, swap_chain_info.swapChain, &image_count, swap_chain_info.m_swapchainImages.data()); - if (result != VK_SUCCESS) - UnrecoverableError("Error attempting to retrieve swapchain images"); - // create default renderpass - VkAttachmentDescription colorAttachment = {}; - colorAttachment.format = m_swapchainFormat.format; - colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT; - colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - - VkAttachmentReference colorAttachmentRef = {}; - colorAttachmentRef.attachment = 0; - colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - VkSubpassDescription subpass = {}; - subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpass.colorAttachmentCount = 1; - subpass.pColorAttachments = &colorAttachmentRef; - - if (swap_chain_info.m_swapChainRenderPass) - { - vkDestroyRenderPass(m_logicalDevice, swap_chain_info.m_swapChainRenderPass, nullptr); - swap_chain_info.m_swapChainRenderPass = nullptr; - } - - VkRenderPassCreateInfo renderPassInfo = {}; - renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; - renderPassInfo.attachmentCount = 1; - renderPassInfo.pAttachments = &colorAttachment; - renderPassInfo.subpassCount = 1; - renderPassInfo.pSubpasses = &subpass; - result = vkCreateRenderPass(m_logicalDevice, &renderPassInfo, nullptr, &swap_chain_info.m_swapChainRenderPass); - if (result != VK_SUCCESS) - UnrecoverableError("Failed to create renderpass for swapchain"); - - // create swapchain image views - for (const auto& image_view : swap_chain_info.m_swapchainImageViews) - { - vkDestroyImageView(m_logicalDevice, image_view, nullptr); - } - swap_chain_info.m_swapchainImageViews.clear(); - - swap_chain_info.m_swapchainImageViews.resize(swap_chain_info.m_swapchainImages.size()); - for (sint32 i = 0; i < swap_chain_info.m_swapchainImages.size(); i++) - { - VkImageViewCreateInfo createInfo = {}; - createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - createInfo.image = swap_chain_info.m_swapchainImages[i]; - createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; - createInfo.format = m_swapchainFormat.format; - createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - createInfo.subresourceRange.baseMipLevel = 0; - createInfo.subresourceRange.levelCount = 1; - createInfo.subresourceRange.baseArrayLayer = 0; - createInfo.subresourceRange.layerCount = 1; - result = vkCreateImageView(m_logicalDevice, &createInfo, nullptr, &swap_chain_info.m_swapchainImageViews[i]); - if (result != VK_SUCCESS) - UnrecoverableError("Failed to create imageviews for swapchain"); - } - - // create swapchain framebuffers - for (const auto& framebuffer : swap_chain_info.m_swapchainFramebuffers) - { - vkDestroyFramebuffer(m_logicalDevice, framebuffer, nullptr); - } - swap_chain_info.m_swapchainFramebuffers.clear(); - - swap_chain_info.m_swapchainFramebuffers.resize(swap_chain_info.m_swapchainImages.size()); - swap_chain_info.m_swapchainPresentSemaphores.resize(swap_chain_info.m_swapchainImages.size()); - for (size_t i = 0; i < swap_chain_info.m_swapchainImages.size(); i++) - { - VkImageView attachments[1]; - attachments[0] = swap_chain_info.m_swapchainImageViews[i]; - // create framebuffer - VkFramebufferCreateInfo framebufferInfo = {}; - framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; - framebufferInfo.renderPass = swap_chain_info.m_swapChainRenderPass; - framebufferInfo.attachmentCount = 1; - framebufferInfo.pAttachments = attachments; - framebufferInfo.width = swap_chain_info.swapchainExtend.width; - framebufferInfo.height = swap_chain_info.swapchainExtend.height; - framebufferInfo.layers = 1; - result = vkCreateFramebuffer(m_logicalDevice, &framebufferInfo, nullptr, &swap_chain_info.m_swapchainFramebuffers[i]); - if (result != VK_SUCCESS) - UnrecoverableError("Failed to create framebuffer for swapchain"); - // create present semaphore - VkSemaphoreCreateInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - if (vkCreateSemaphore(m_logicalDevice, &info, nullptr, &swap_chain_info.m_swapchainPresentSemaphores[i]) != VK_SUCCESS) - UnrecoverableError("Failed to create semaphore for swapchain present"); - } - - // init m_acquireInfo - for (auto& itr : swap_chain_info.m_acquireInfo) - { - vkDestroySemaphore(m_logicalDevice, itr.acquireSemaphore, nullptr); - } - swap_chain_info.m_acquireInfo.clear(); - swap_chain_info.m_acquireInfo.resize(swap_chain_info.m_swapchainImages.size()); - for (auto& itr : swap_chain_info.m_acquireInfo) - { - VkSemaphoreCreateInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - if (vkCreateSemaphore(m_logicalDevice, &info, nullptr, &itr.acquireSemaphore) != VK_SUCCESS) - UnrecoverableError("Failed to create semaphore for swapchain acquire"); - } - swap_chain_info.m_acquireIndex = 0; - - - if (swap_chain_info.m_imageAvailableFence) - { - vkDestroyFence(m_logicalDevice, swap_chain_info.m_imageAvailableFence, nullptr); - swap_chain_info.m_imageAvailableFence = nullptr; - } - - VkFenceCreateInfo fenceInfo = {}; - fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; - fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; - result = vkCreateFence(m_logicalDevice, &fenceInfo, nullptr, &swap_chain_info.m_imageAvailableFence); - if (result != VK_SUCCESS) - UnrecoverableError("Failed to create fence for swapchain"); - - return swap_chain_info.swapChain; -} void VulkanRenderer::CreateNullTexture(NullTexture& nullTex, VkImageType imageType) { @@ -1775,7 +1435,7 @@ void VulkanRenderer::ImguiInit() { // TODO: renderpass swapchain format may change between srgb and rgb -> need reinit VkAttachmentDescription colorAttachment = {}; - colorAttachment.format = m_swapchainFormat.format; + colorAttachment.format = m_mainSwapchainInfo->m_surfaceFormat.format; colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT; colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; @@ -1798,13 +1458,13 @@ void VulkanRenderer::ImguiInit() renderPassInfo.pAttachments = &colorAttachment; renderPassInfo.subpassCount = 1; renderPassInfo.pSubpasses = &subpass; - const auto result = vkCreateRenderPass(m_logicalDevice, &renderPassInfo, nullptr, &m_mainSwapchainInfo->m_imguiRenderPass); + const auto result = vkCreateRenderPass(m_logicalDevice, &renderPassInfo, nullptr, &m_imguiRenderPass); if (result != VK_SUCCESS) throw VkException(result, "can't create imgui renderpass"); ImGui_ImplVulkan_InitInfo info{}; info.Instance = m_instance; - info.PhysicalDevice = m_physical_device; + info.PhysicalDevice = m_physicalDevice; info.Device = m_logicalDevice; info.QueueFamily = m_indices.presentFamily; info.Queue = m_presentQueue; @@ -1813,7 +1473,7 @@ void VulkanRenderer::ImguiInit() info.MinImageCount = m_mainSwapchainInfo->m_swapchainImages.size(); info.ImageCount = info.MinImageCount; - ImGui_ImplVulkan_Init(&info, m_mainSwapchainInfo->m_imguiRenderPass); + ImGui_ImplVulkan_Init(&info, m_imguiRenderPass); } void VulkanRenderer::Initialize() @@ -1826,7 +1486,7 @@ void VulkanRenderer::Initialize() void VulkanRenderer::Shutdown() { SubmitCommandBuffer(); - vkDeviceWaitIdle(m_logicalDevice); + WaitDeviceIdle(); } void VulkanRenderer::UnrecoverableError(const char* errMsg) const @@ -1901,7 +1561,7 @@ VulkanRequestedFormat_t requestedFormatList[] = void VulkanRenderer::QueryMemoryInfo() { VkPhysicalDeviceMemoryProperties memProperties; - vkGetPhysicalDeviceMemoryProperties(m_physical_device, &memProperties); + vkGetPhysicalDeviceMemoryProperties(m_physicalDevice, &memProperties); forceLog_printf("Vulkan device memory info:"); for (uint32 i = 0; i < memProperties.memoryHeapCount; i++) { @@ -1916,7 +1576,7 @@ void VulkanRenderer::QueryMemoryInfo() void VulkanRenderer::QueryAvailableFormats() { VkFormatProperties fmtProp{}; - vkGetPhysicalDeviceFormatProperties(m_physical_device, VK_FORMAT_D24_UNORM_S8_UINT, &fmtProp); + vkGetPhysicalDeviceFormatProperties(m_physicalDevice, VK_FORMAT_D24_UNORM_S8_UINT, &fmtProp); // D24S8 if (fmtProp.optimalTilingFeatures != 0) // todo - more restrictive check { @@ -1924,28 +1584,28 @@ void VulkanRenderer::QueryAvailableFormats() } // R4G4 fmtProp = {}; - vkGetPhysicalDeviceFormatProperties(m_physical_device, VK_FORMAT_R4G4_UNORM_PACK8, &fmtProp); + vkGetPhysicalDeviceFormatProperties(m_physicalDevice, VK_FORMAT_R4G4_UNORM_PACK8, &fmtProp); if (fmtProp.optimalTilingFeatures != 0) { m_supportedFormatInfo.fmt_r4g4_unorm_pack = true; } // R5G6B5 fmtProp = {}; - vkGetPhysicalDeviceFormatProperties(m_physical_device, VK_FORMAT_R5G6B5_UNORM_PACK16, &fmtProp); + vkGetPhysicalDeviceFormatProperties(m_physicalDevice, VK_FORMAT_R5G6B5_UNORM_PACK16, &fmtProp); if (fmtProp.optimalTilingFeatures != 0) { m_supportedFormatInfo.fmt_r5g6b5_unorm_pack = true; } // R4G4B4A4 fmtProp = {}; - vkGetPhysicalDeviceFormatProperties(m_physical_device, VK_FORMAT_R4G4B4A4_UNORM_PACK16, &fmtProp); + vkGetPhysicalDeviceFormatProperties(m_physicalDevice, VK_FORMAT_R4G4B4A4_UNORM_PACK16, &fmtProp); if (fmtProp.optimalTilingFeatures != 0) { m_supportedFormatInfo.fmt_r4g4b4a4_unorm_pack = true; } // A1R5G5B5 fmtProp = {}; - vkGetPhysicalDeviceFormatProperties(m_physical_device, VK_FORMAT_A1R5G5B5_UNORM_PACK16, &fmtProp); + vkGetPhysicalDeviceFormatProperties(m_physicalDevice, VK_FORMAT_A1R5G5B5_UNORM_PACK16, &fmtProp); if (fmtProp.optimalTilingFeatures != 0) { m_supportedFormatInfo.fmt_a1r5g5b5_unorm_pack = true; @@ -1954,7 +1614,7 @@ void VulkanRenderer::QueryAvailableFormats() for (auto& it : requestedFormatList) { fmtProp = {}; - vkGetPhysicalDeviceFormatProperties(m_physical_device, it.fmt, &fmtProp); + vkGetPhysicalDeviceFormatProperties(m_physicalDevice, it.fmt, &fmtProp); VkFormatFeatureFlags requestedBits = 0; if (it.mustSupportAttachment) { @@ -1996,6 +1656,8 @@ bool VulkanRenderer::ImguiBegin(bool mainWindow) if (!Renderer::ImguiBegin(mainWindow)) return false; + auto& chainInfo = GetChainInfo(mainWindow); + if (!IsSwapchainInfoValid(mainWindow)) return false; @@ -2004,16 +1666,14 @@ bool VulkanRenderer::ImguiBegin(bool mainWindow) AcquireNextSwapchainImage(mainWindow); - auto& swapchain_info = mainWindow ? *m_mainSwapchainInfo : *m_padSwapchainInfo; ImGui_ImplVulkan_CreateFontsTexture(m_state.currentCommandBuffer); - ImGui_ImplVulkan_NewFrame(m_state.currentCommandBuffer, swapchain_info.m_swapchainFramebuffers[swapchain_info.swapchainImageIndex], swapchain_info.swapchainExtend); + ImGui_ImplVulkan_NewFrame(m_state.currentCommandBuffer, chainInfo.m_swapchainFramebuffers[chainInfo.swapchainImageIndex], chainInfo.swapchainExtent); ImGui_UpdateWindowInformation(mainWindow); ImGui::NewFrame(); return true; } - void VulkanRenderer::ImguiEnd() { ImGui::Render(); @@ -2046,7 +1706,7 @@ ImTextureID VulkanRenderer::GenerateTexture(const std::vector& data, cons void VulkanRenderer::DeleteTexture(ImTextureID id) { - vkDeviceWaitIdle(m_logicalDevice); + WaitDeviceIdle(); ImGui_ImplVulkan_DeleteTexture(id); } @@ -2063,16 +1723,13 @@ bool VulkanRenderer::BeginFrame(bool mainWindow) AcquireNextSwapchainImage(mainWindow); - auto& swap_info = mainWindow ? *m_mainSwapchainInfo : *m_padSwapchainInfo; + auto& chainInfo = GetChainInfo(mainWindow); VkClearColorValue clearColor{ 0, 0, 0, 0 }; - ClearColorImageRaw(swap_info.m_swapchainImages[swap_info.swapchainImageIndex], 0, 0, clearColor, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); + ClearColorImageRaw(chainInfo.m_swapchainImages[chainInfo.swapchainImageIndex], 0, 0, clearColor, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); // mark current swapchain image as well defined - if (!mainWindow) - m_swapchainState.drcHasDefinedSwapchainImage = true; - else - m_swapchainState.tvHasDefinedSwapchainImage = true; + chainInfo.hasDefinedSwapchainImage = true; return true; } @@ -2758,7 +2415,7 @@ VkPipelineShaderStageCreateInfo VulkanRenderer::CreatePipelineShaderStageCreateI VkPipeline VulkanRenderer::backbufferBlit_createGraphicsPipeline(VkDescriptorSetLayout descriptorLayout, bool padView, RendererOutputShader* shader) { - auto& swap_info = !padView ? *m_mainSwapchainInfo : *m_padSwapchainInfo; + auto& chainInfo = GetChainInfo(!padView); RendererShaderVk* vertexRendererShader = static_cast(shader->GetVertexShader()); RendererShaderVk* fragmentRendererShader = static_cast(shader->GetFragmentShader()); @@ -2766,7 +2423,7 @@ VkPipeline VulkanRenderer::backbufferBlit_createGraphicsPipeline(VkDescriptorSet uint64 hash = 0; hash += (uint64)vertexRendererShader; hash += (uint64)fragmentRendererShader; - hash += (uint64)(padView ? m_drvBufferUsesSRGB : m_tvBufferUsesSRGB); + hash += (uint64)(chainInfo.m_usesSRGB); hash += ((uint64)padView) << 1; static std::unordered_map s_pipeline_cache; @@ -2854,7 +2511,7 @@ VkPipeline VulkanRenderer::backbufferBlit_createGraphicsPipeline(VkDescriptorSet pipelineInfo.pMultisampleState = &multisampling; pipelineInfo.pColorBlendState = &colorBlending; pipelineInfo.layout = m_pipelineLayout; - pipelineInfo.renderPass = swap_info.m_swapChainRenderPass; + pipelineInfo.renderPass = chainInfo.m_swapchainRenderPass; pipelineInfo.subpass = 0; pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; @@ -2873,29 +2530,28 @@ VkPipeline VulkanRenderer::backbufferBlit_createGraphicsPipeline(VkDescriptorSet return pipeline; } -void VulkanRenderer::AcquireNextSwapchainImage(bool main_window) +void VulkanRenderer::AcquireNextSwapchainImage(bool mainWindow) { - auto& swapInfo = main_window ? *m_mainSwapchainInfo : *m_padSwapchainInfo; - if (swapInfo.swapchainImageIndex != -1) + auto& chainInfo = GetChainInfo(mainWindow); + if (chainInfo.swapchainImageIndex != -1) return; // image already reserved + vkWaitForFences(m_logicalDevice, 1, &chainInfo.m_imageAvailableFence, VK_TRUE, std::numeric_limits::max()); + vkResetFences(m_logicalDevice, 1, &chainInfo.m_imageAvailableFence); - vkWaitForFences(m_logicalDevice, 1, &swapInfo.m_imageAvailableFence, VK_TRUE, std::numeric_limits::max()); - vkResetFences(m_logicalDevice, 1, &swapInfo.m_imageAvailableFence); + auto& acquireSemaphore = chainInfo.m_acquireSemaphores[chainInfo.m_acquireIndex]; - auto& acquireInfo = swapInfo.m_acquireInfo[swapInfo.m_acquireIndex]; - - VkResult result = vkAcquireNextImageKHR(m_logicalDevice, swapInfo.swapChain, std::numeric_limits::max(), acquireInfo.acquireSemaphore, swapInfo.m_imageAvailableFence, &swapInfo.swapchainImageIndex); + VkResult result = vkAcquireNextImageKHR(m_logicalDevice, chainInfo.swapchain, std::numeric_limits::max(), acquireSemaphore, chainInfo.m_imageAvailableFence, &chainInfo.swapchainImageIndex); if (result != VK_SUCCESS) { while (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) // todo: handle error state correctly. Looping doesnt always make sense? { try { - RecreateSwapchain(main_window); - if (vkWaitForFences(m_logicalDevice, 1, &swapInfo.m_imageAvailableFence, VK_TRUE, 0) == VK_SUCCESS) - vkResetFences(m_logicalDevice, 1, &swapInfo.m_imageAvailableFence); - result = vkAcquireNextImageKHR(m_logicalDevice, swapInfo.swapChain, std::numeric_limits::max(), acquireInfo.acquireSemaphore, swapInfo.m_imageAvailableFence, &swapInfo.swapchainImageIndex); + RecreateSwapchain(mainWindow); + if (vkWaitForFences(m_logicalDevice, 1, &chainInfo.m_imageAvailableFence, VK_TRUE, 0) == VK_SUCCESS) + vkResetFences(m_logicalDevice, 1, &chainInfo.m_imageAvailableFence); + result = vkAcquireNextImageKHR(m_logicalDevice, chainInfo.swapchain, std::numeric_limits::max(), acquireSemaphore, chainInfo.m_imageAvailableFence, &chainInfo.swapchainImageIndex); if (result == VK_SUCCESS) return; } @@ -2908,22 +2564,22 @@ void VulkanRenderer::AcquireNextSwapchainImage(bool main_window) throw std::runtime_error(fmt::format("Failed to acquire next image: {}", result)); } - swapInfo.m_acquireIndex = (swapInfo.m_acquireIndex + 1) % swapInfo.m_acquireInfo.size(); + chainInfo.m_acquireIndex = (chainInfo.m_acquireIndex + 1) % chainInfo.m_acquireSemaphores.size(); - SubmitCommandBuffer(nullptr, &acquireInfo.acquireSemaphore); + SubmitCommandBuffer(nullptr, &acquireSemaphore); } -void VulkanRenderer::RecreateSwapchain(bool main_window) +void VulkanRenderer::RecreateSwapchain(bool mainWindow) { SubmitCommandBuffer(); - vkDeviceWaitIdle(m_logicalDevice); - auto& swapinfo = main_window ? *m_mainSwapchainInfo : *m_padSwapchainInfo; - vkWaitForFences(m_logicalDevice, 1, &swapinfo.m_imageAvailableFence, VK_TRUE, + WaitDeviceIdle(); + auto& chainInfo = GetChainInfo(mainWindow); + vkWaitForFences(m_logicalDevice, 1, &chainInfo.m_imageAvailableFence, VK_TRUE, std::chrono::duration_cast(std::chrono::milliseconds(10)).count() ); Vector2i size; - if (main_window) + if (mainWindow) { ImGui_ImplVulkan_Shutdown(); gui_getWindowSize(&size.x, &size.y); @@ -2933,80 +2589,50 @@ void VulkanRenderer::RecreateSwapchain(bool main_window) gui_getPadWindowSize(&size.x, &size.y); } - if (swapinfo.swapChain != VK_NULL_HANDLE) - { - // todo - for some reason using the swapchain replacement method (old var being set) causes crashes and other issues - vkDestroySwapchainKHR(m_logicalDevice, swapinfo.swapChain, nullptr); - swapinfo.m_swapchainImages.clear(); // swapchain images are automatically destroyed - swapinfo.swapChain = VK_NULL_HANDLE; - } + chainInfo.Cleanup(); + chainInfo.setSize(size); + chainInfo.Create(m_physicalDevice, m_logicalDevice); + chainInfo.swapchainImageIndex = -1; - swapinfo.swapChain = nullptr; - swapinfo.swapChain = CreateSwapChain(swapinfo, size, main_window); - swapinfo.swapchainImageIndex = -1; - - if (main_window) + if (mainWindow) ImguiInit(); } -void VulkanRenderer::UpdateVSyncState(bool main_window) +void VulkanRenderer::UpdateVSyncState(bool mainWindow) { - auto& swapInfo = main_window ? *m_mainSwapchainInfo : *m_padSwapchainInfo; + auto& chainInfo = GetChainInfo(mainWindow); const auto configValue = (VSync)GetConfig().vsync.GetValue(); - if(swapInfo.m_activeVSyncState != configValue){ - RecreateSwapchain(main_window); - swapInfo.m_activeVSyncState = configValue; + if(chainInfo.m_vsyncState != configValue){ + RecreateSwapchain(mainWindow); + chainInfo.m_vsyncState = configValue; } } -void VulkanRenderer::SwapBuffer(bool main_window) +void VulkanRenderer::SwapBuffer(bool mainWindow) { - auto& swapInfo = main_window ? *m_mainSwapchainInfo : *m_padSwapchainInfo; + auto& chainInfo = GetChainInfo(mainWindow); - if (main_window) + const bool latteBufferUsesSRGB = mainWindow ? LatteGPUState.tvBufferUsesSRGB : LatteGPUState.drcBufferUsesSRGB; + if (chainInfo.sizeOutOfDate || chainInfo.m_usesSRGB != latteBufferUsesSRGB) { - const bool resize = m_swapchainState.resizeRequestedMainWindow; - m_swapchainState.resizeRequestedMainWindow = false; - - if (resize || m_tvBufferUsesSRGB != LatteGPUState.tvBufferUsesSRGB) + try { - try - { - RecreateSwapchain(main_window); - m_tvBufferUsesSRGB = LatteGPUState.tvBufferUsesSRGB; - } - catch (std::exception&) { cemu_assert_debug(false); } - return; - } - } - else - { - const bool resize = m_swapchainState.resizeRequestedPadWindow; - m_swapchainState.resizeRequestedPadWindow = false; - - if (resize || m_drvBufferUsesSRGB != LatteGPUState.drcBufferUsesSRGB) - { - try - { - RecreateSwapchain(main_window); - m_drvBufferUsesSRGB = LatteGPUState.drcBufferUsesSRGB; - } - catch (std::exception&) { cemu_assert_debug(false); } - return; + RecreateSwapchain(mainWindow); + chainInfo.m_usesSRGB = latteBufferUsesSRGB; } + catch (std::exception&) { cemu_assert_debug(false); } + return; } - UpdateVSyncState(main_window); + UpdateVSyncState(mainWindow); - auto& swapinfo = main_window ? *m_mainSwapchainInfo : *m_padSwapchainInfo; - AcquireNextSwapchainImage(main_window); + AcquireNextSwapchainImage(mainWindow); - if ((main_window && m_swapchainState.tvHasDefinedSwapchainImage == false) || - (!main_window && m_swapchainState.drcHasDefinedSwapchainImage == false)) + if (!chainInfo.hasDefinedSwapchainImage) { // set the swapchain image to a defined state VkClearColorValue clearColor{ 0, 0, 0, 0 }; - ClearColorImageRaw(swapInfo.m_swapchainImages[swapInfo.swapchainImageIndex], 0, 0, clearColor, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); + ClearColorImageRaw(chainInfo.m_swapchainImages[chainInfo.swapchainImageIndex], 0, 0, clearColor, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); } // make sure any writes to the image have finished (is this necessary? End of command buffer implicitly flushes everything?) @@ -3019,7 +2645,7 @@ void VulkanRenderer::SwapBuffer(bool main_window) vkCmdPipelineBarrier(m_state.currentCommandBuffer, srcStage, dstStage, 0, 1, &memoryBarrier, 0, nullptr, 0, nullptr); - VkSemaphore presentSemaphore = swapInfo.m_swapchainPresentSemaphores[swapInfo.swapchainImageIndex]; + VkSemaphore presentSemaphore = chainInfo.m_swapchainPresentSemaphores[chainInfo.swapchainImageIndex]; SubmitCommandBuffer(&presentSemaphore); // submit all command and signal semaphore cemu_assert_debug(m_numSubmittedCmdBuffers > 0); @@ -3028,8 +2654,8 @@ void VulkanRenderer::SwapBuffer(bool main_window) VkPresentInfoKHR presentInfo = {}; presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; presentInfo.swapchainCount = 1; - presentInfo.pSwapchains = &swapinfo.swapChain; - presentInfo.pImageIndices = &swapinfo.swapchainImageIndex; + presentInfo.pSwapchains = &chainInfo.swapchain; + presentInfo.pImageIndices = &chainInfo.swapchainImageIndex; // wait on command buffer semaphore presentInfo.waitSemaphoreCount = 1; presentInfo.pWaitSemaphores = &presentSemaphore; @@ -3044,7 +2670,7 @@ void VulkanRenderer::SwapBuffer(bool main_window) { try { - RecreateSwapchain(main_window); + RecreateSwapchain(mainWindow); return; } catch (std::exception&) @@ -3070,12 +2696,9 @@ void VulkanRenderer::SwapBuffer(bool main_window) throw std::runtime_error(fmt::format("Failed to present draw command buffer: {}", result)); } - if (main_window) - m_swapchainState.tvHasDefinedSwapchainImage = false; - else - m_swapchainState.drcHasDefinedSwapchainImage = false; + chainInfo.hasDefinedSwapchainImage = false; - swapinfo.swapchainImageIndex = -1; + chainInfo.swapchainImageIndex = -1; } void VulkanRenderer::Flush(bool waitIdle) @@ -3108,12 +2731,12 @@ void VulkanRenderer::ClearColorbuffer(bool padView) if (!IsSwapchainInfoValid(!padView)) return; - auto& swap_info = padView ? *m_padSwapchainInfo : *m_mainSwapchainInfo; - if (swap_info.swapchainImageIndex == -1) + auto& chainInfo = GetChainInfo(!padView); + if (chainInfo.swapchainImageIndex == -1) return; VkClearColorValue clearColor{ 0, 0, 0, 0 }; - ClearColorImageRaw(swap_info.m_swapchainImages[swap_info.swapchainImageIndex], 0, 0, clearColor, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL); + ClearColorImageRaw(chainInfo.m_swapchainImages[chainInfo.swapchainImageIndex], 0, 0, clearColor, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL); } void VulkanRenderer::ClearColorImageRaw(VkImage image, uint32 sliceIndex, uint32 mipIndex, const VkClearColorValue& color, VkImageLayout inputLayout, VkImageLayout outputLayout) @@ -3198,7 +2821,7 @@ void VulkanRenderer::DrawBackbufferQuad(LatteTextureView* texView, RendererOutpu if (!IsSwapchainInfoValid(!padView)) return; - auto& swapInfo = !padView ? *m_mainSwapchainInfo : *m_padSwapchainInfo; + auto& chainInfo = GetChainInfo(!padView); LatteTextureViewVk* texViewVk = (LatteTextureViewVk*)texView; draw_endRenderPass(); @@ -3220,10 +2843,10 @@ void VulkanRenderer::DrawBackbufferQuad(LatteTextureView* texView, RendererOutpu VkRenderPassBeginInfo renderPassInfo = {}; renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; - renderPassInfo.renderPass = swapInfo.m_swapChainRenderPass; - renderPassInfo.framebuffer = swapInfo.m_swapchainFramebuffers[swapInfo.swapchainImageIndex]; + renderPassInfo.renderPass = chainInfo.m_swapchainRenderPass; + renderPassInfo.framebuffer = chainInfo.m_swapchainFramebuffers[chainInfo.swapchainImageIndex]; renderPassInfo.renderArea.offset = { 0, 0 }; - renderPassInfo.renderArea.extent = swapInfo.swapchainExtend; + renderPassInfo.renderArea.extent = chainInfo.swapchainExtent; renderPassInfo.clearValueCount = 0; VkViewport viewport{}; @@ -3236,7 +2859,7 @@ void VulkanRenderer::DrawBackbufferQuad(LatteTextureView* texView, RendererOutpu vkCmdSetViewport(m_state.currentCommandBuffer, 0, 1, &viewport); VkRect2D scissor{}; - scissor.extent = swapInfo.swapchainExtend; + scissor.extent = chainInfo.swapchainExtent; vkCmdSetScissor(m_state.currentCommandBuffer, 0, 1, &scissor); auto descriptSet = backbufferBlit_createDescriptorSet(m_swapchainDescriptorSetLayout, texViewVk, useLinearTexFilter); @@ -3258,10 +2881,7 @@ void VulkanRenderer::DrawBackbufferQuad(LatteTextureView* texView, RendererOutpu vkCmdSetViewport(m_state.currentCommandBuffer, 0, 1, &m_state.currentViewport); // mark current swapchain image as well defined - if (padView) - m_swapchainState.drcHasDefinedSwapchainImage = true; - else - m_swapchainState.tvHasDefinedSwapchainImage = true; + chainInfo.hasDefinedSwapchainImage = true; } void VulkanRenderer::CreateDescriptorPool() diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h index 65b6dae6..6ff2f237 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h @@ -1,4 +1,5 @@ #pragma once + #include "Cafe/HW/Latte/Renderer/Renderer.h" #include "Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h" #include "Cafe/HW/Latte/Renderer/Vulkan/RendererShaderVk.h" @@ -6,6 +7,7 @@ #include "Cafe/HW/Latte/Renderer/Vulkan/LatteTextureViewVk.h" #include "Cafe/HW/Latte/Renderer/Vulkan/CachedFBOVk.h" #include "Cafe/HW/Latte/Renderer/Vulkan/VKRMemoryManager.h" +#include "Cafe/HW/Latte/Renderer/Vulkan/SwapchainInfoVk.h" #include "util/math/vector2.h" #include "util/helpers/Semaphore.h" #include "util/containers/flat_hash_map.hpp" @@ -124,6 +126,9 @@ class VulkanRenderer : public Renderer friend class LatteTextureReadbackInfoVk; friend class PipelineCompiler; + using VSync = SwapchainInfoVk::VSync; + using QueueFamilyIndices = SwapchainInfoVk::QueueFamilyIndices; + static const inline int UNIFORMVAR_RINGBUFFER_SIZE = 1024 * 1024 * 16; // 16MB static const inline int INDEX_STREAM_BUFFER_SIZE = 16 * 1024 * 1024; // 16 MB @@ -132,15 +137,7 @@ class VulkanRenderer : public Renderer static const inline int OCCLUSION_QUERY_POOL_SIZE = 1024; public: - enum class VSync - { - // values here must match GeneralSettings2::m_vsync - Immediate = 0, - FIFO = 1, - MAILBOX = 2, - SYNC_AND_LIMIT = 3, // synchronize emulated vsync events to monitor vsync. But skip events if rate higher than virtual vsync period - }; - + // memory management VKRMemoryManager* memoryManager{}; VKRMemoryManager* GetMemoryManager() const { return memoryManager; }; @@ -185,12 +182,15 @@ public: void GetDeviceFeatures(); void DetermineVendor(); - void Initialize(const Vector2i& size, bool isMainWindow); + void Initialize(const Vector2i& size, bool mainWindow); + + const std::unique_ptr& GetChainInfoPtr(bool mainWindow) const; + SwapchainInfoVk& GetChainInfo(bool mainWindow) const; bool IsPadWindowActive() override; void HandleScreenshotRequest(LatteTextureView* texView, bool padView) override; - void ResizeSwapchain(const Vector2i& size, bool isMainWindow); + void SetSwapchainTargetSize(const Vector2i& size, bool mainWindow); void QueryMemoryInfo(); void QueryAvailableFormats(); @@ -210,7 +210,7 @@ public: void ImguiInit(); VkInstance GetVkInstance() const { return m_instance; } VkDevice GetLogicalDevice() const { return m_logicalDevice; } - VkPhysicalDevice GetPhysicalDevice() const { return m_physical_device; } + VkPhysicalDevice GetPhysicalDevice() const { return m_physicalDevice; } VkDescriptorPool GetDescriptorPool() const { return m_descriptorPool; } @@ -431,86 +431,12 @@ private: bool drawSequenceSkip; // if true, skip draw_execute() }m_state; - // swapchain - todo move this to m_swapchainState - struct SwapChainInfo - { - SwapChainInfo(VkDevice device, VkSurfaceKHR surface) : m_device(device), surface(surface) {} - SwapChainInfo(const SwapChainInfo&) = delete; - SwapChainInfo(SwapChainInfo&&) noexcept = default; - ~SwapChainInfo() - { - if (m_swapChainRenderPass) - { - vkDestroyRenderPass(m_device, m_swapChainRenderPass, nullptr); - m_swapChainRenderPass = VK_NULL_HANDLE; - } - if (swapChain) - { - vkDestroySwapchainKHR(m_device, swapChain, nullptr); - swapChain = VK_NULL_HANDLE; - } - for (auto& imageView : m_swapchainImageViews) - vkDestroyImageView(m_device, imageView, nullptr); - m_swapchainImageViews.clear(); - for (auto& framebuffer : m_swapchainFramebuffers) - vkDestroyFramebuffer(m_device, framebuffer, nullptr); - m_swapchainFramebuffers.clear(); - } - - VkDevice m_device; - - VkSurfaceKHR surface{}; - VkSwapchainKHR swapChain{}; - VkExtent2D swapchainExtend{}; - uint32 swapchainImageIndex = (uint32)-1; - uint32 m_acquireIndex = 0; // increases with every successful vkAcquireNextImageKHR - VSync m_activeVSyncState = VSync::Immediate; - - struct AcquireInfo - { - // move fence here? - VkSemaphore acquireSemaphore; - }; - std::vector m_acquireInfo; // indexed by acquireIndex - - // swapchain image ringbuffer (indexed by swapchainImageIndex) - std::vector m_swapchainImages; - std::vector m_swapchainImageViews; - std::vector m_swapchainFramebuffers; - std::vector m_swapchainPresentSemaphores; - - VkFence m_imageAvailableFence = nullptr; - VkRenderPass m_swapChainRenderPass = nullptr; - VkRenderPass m_imguiRenderPass = nullptr; - }; - std::unique_ptr m_mainSwapchainInfo{}, m_padSwapchainInfo{}; + std::unique_ptr m_mainSwapchainInfo{}, m_padSwapchainInfo{}; bool IsSwapchainInfoValid(bool mainWindow) const; - VkSwapchainKHR CreateSwapChain(SwapChainInfo& swap_chain_info, const Vector2i& size, bool mainwindow); - struct - { - bool resizeRequestedMainWindow{}; - Vector2i newExtentMainWindow; - bool resizeRequestedPadWindow{}; - Vector2i newExtentPadWindow; - bool tvHasDefinedSwapchainImage{}; // indicates if the swapchain image is in a defined state - bool drcHasDefinedSwapchainImage{}; - }m_swapchainState; + VkRenderPass m_imguiRenderPass = nullptr; VkDescriptorPool m_descriptorPool; - VkSurfaceFormatKHR m_swapchainFormat; - - bool m_tvBufferUsesSRGB = false; - bool m_drvBufferUsesSRGB = false; - - struct QueueFamilyIndices - { - int32_t graphicsFamily = -1; - int32_t presentFamily = -1; - - bool IsComplete() const { return graphicsFamily >= 0 && presentFamily >= 0; } - }; - static QueueFamilyIndices FindQueueFamilies(VkSurfaceKHR surface, const VkPhysicalDevice& device); struct FeatureControl { @@ -556,15 +482,8 @@ private: static bool CheckDeviceExtensionSupport(const VkPhysicalDevice device, FeatureControl& info); static std::vector CheckInstanceExtensionSupport(FeatureControl& info); - void UpdateVSyncState(bool main_window); - void SwapBuffer(bool main_window); - - struct SwapChainSupportDetails - { - VkSurfaceCapabilitiesKHR capabilities; - std::vector formats; - std::vector presentModes; - }; + void UpdateVSyncState(bool mainWindow); + void SwapBuffer(bool mainWindow); VkDescriptorSetLayout m_swapchainDescriptorSetLayout; @@ -572,14 +491,9 @@ private: // swapchain - static SwapChainSupportDetails QuerySwapChainSupport(VkSurfaceKHR surface, const VkPhysicalDevice& device); std::vector CreateQueueCreateInfos(const std::set& uniqueQueueFamilies) const; VkDeviceCreateInfo CreateDeviceCreateInfo(const std::vector& queueCreateInfos, const VkPhysicalDeviceFeatures& deviceFeatures, const void* deviceExtensionStructs, std::vector& used_extensions) const; static bool IsDeviceSuitable(VkSurfaceKHR surface, const VkPhysicalDevice& device); - VkSurfaceFormatKHR ChooseSwapSurfaceFormat(const std::vector& formats, bool mainWindow) const; - VkPresentModeKHR ChooseSwapPresentMode(const std::vector& modes); - VkExtent2D ChooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities, const Vector2i& size) const; - VkSwapchainCreateInfoKHR CreateSwapchainCreateInfo(VkSurfaceKHR surface, const SwapChainSupportDetails& swapChainSupport, const VkSurfaceFormatKHR& surfaceFormat, uint32 imageCount, const VkExtent2D& extent); void CreateCommandPool(); void CreateCommandBuffers(); @@ -641,8 +555,8 @@ private: void CreatePipelineCache(); VkPipelineShaderStageCreateInfo CreatePipelineShaderStageCreateInfo(VkShaderStageFlagBits stage, VkShaderModule& module, const char* entryName) const; VkPipeline backbufferBlit_createGraphicsPipeline(VkDescriptorSetLayout descriptorLayout, bool padView, RendererOutputShader* shader); - void AcquireNextSwapchainImage(bool main_window); - void RecreateSwapchain(bool mainwindow); + void AcquireNextSwapchainImage(bool mainWindow); + void RecreateSwapchain(bool mainWindow); // streamout void streamout_setupXfbBuffer(uint32 bufferIndex, sint32 ringBufferOffset, uint32 rangeAddr, uint32 rangeSize) override; @@ -665,7 +579,7 @@ private: std::vector m_layerNames; VkInstance m_instance = VK_NULL_HANDLE; - VkPhysicalDevice m_physical_device = VK_NULL_HANDLE; + VkPhysicalDevice m_physicalDevice = VK_NULL_HANDLE; VkDevice m_logicalDevice = VK_NULL_HANDLE; VkDebugUtilsMessengerEXT m_debugCallback = nullptr; volatile bool m_destructionRequested = false; diff --git a/src/gui/canvas/VulkanCanvas.cpp b/src/gui/canvas/VulkanCanvas.cpp index b8faa8c7..46428216 100644 --- a/src/gui/canvas/VulkanCanvas.cpp +++ b/src/gui/canvas/VulkanCanvas.cpp @@ -60,5 +60,5 @@ void VulkanCanvas::OnResize(wxSizeEvent& event) return; auto vulkan_renderer = VulkanRenderer::GetInstance(); - vulkan_renderer->ResizeSwapchain({ size.x, size.y }, m_is_main_window); -} \ No newline at end of file + vulkan_renderer->SetSwapchainTargetSize({size.x, size.y}, m_is_main_window); +} diff --git a/src/imgui/imgui_impl_vulkan.cpp b/src/imgui/imgui_impl_vulkan.cpp index 89f94e64..4c8695f4 100644 --- a/src/imgui/imgui_impl_vulkan.cpp +++ b/src/imgui/imgui_impl_vulkan.cpp @@ -97,7 +97,7 @@ void ImGui_ImplVulkanH_DestroyFrame(VkDevice device, ImGui_ImplVulkanH_Frame* fd void ImGui_ImplVulkanH_DestroyFrameSemaphores(VkDevice device, ImGui_ImplVulkanH_FrameSemaphores* fsd, const VkAllocationCallbacks* allocator); void ImGui_ImplVulkanH_DestroyFrameRenderBuffers(VkDevice device, ImGui_ImplVulkanH_FrameRenderBuffers* buffers, const VkAllocationCallbacks* allocator); void ImGui_ImplVulkanH_DestroyWindowRenderBuffers(VkDevice device, ImGui_ImplVulkanH_WindowRenderBuffers* buffers, const VkAllocationCallbacks* allocator); -void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, const VkAllocationCallbacks* allocator, int w, int h, uint32_t min_image_count); +void ImGui_ImplVulkanH_CreateWindowSwapchain(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, const VkAllocationCallbacks* allocator, int w, int h, uint32_t min_image_count); void ImGui_ImplVulkanH_CreateWindowCommandBuffers(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, uint32_t queue_family, const VkAllocationCallbacks* allocator); struct ImGuiTexture @@ -1082,7 +1082,7 @@ int ImGui_ImplVulkanH_GetMinImageCountFromPresentMode(VkPresentModeKHR present_m } // Also destroy old swap chain and in-flight frames data, if any. -void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, const VkAllocationCallbacks* allocator, int w, int h, uint32_t min_image_count) +void ImGui_ImplVulkanH_CreateWindowSwapchain(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, const VkAllocationCallbacks* allocator, int w, int h, uint32_t min_image_count) { VkResult err; VkSwapchainKHR old_swapchain = wd->Swapchain; @@ -1245,7 +1245,7 @@ void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, V void ImGui_ImplVulkanH_CreateWindow(VkInstance instance, VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, uint32_t queue_family, const VkAllocationCallbacks* allocator, int width, int height, uint32_t min_image_count) { (void)instance; - ImGui_ImplVulkanH_CreateWindowSwapChain(physical_device, device, wd, allocator, width, height, min_image_count); + ImGui_ImplVulkanH_CreateWindowSwapchain(physical_device, device, wd, allocator, width, height, min_image_count); ImGui_ImplVulkanH_CreateWindowCommandBuffers(physical_device, device, wd, queue_family, allocator); }