From f632d00eb1add39f43617d147466bb310ce646fd Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Fri, 6 Dec 2019 22:41:42 -0300
Subject: [PATCH] vk_swapchain: Add support for swapping sRGB

We don't know until the game is running if it's using an sRGB color
space or not. Add support for hot-swapping swapchain surface formats.
---
 .../renderer_vulkan/vk_swapchain.cpp          | 45 ++++++++++---------
 src/video_core/renderer_vulkan/vk_swapchain.h | 10 ++++-
 2 files changed, 31 insertions(+), 24 deletions(-)

diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp
index 08279e562e..ebc68f0305 100644
--- a/src/video_core/renderer_vulkan/vk_swapchain.cpp
+++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp
@@ -19,12 +19,18 @@
 namespace Vulkan {
 
 namespace {
-vk::SurfaceFormatKHR ChooseSwapSurfaceFormat(const std::vector<vk::SurfaceFormatKHR>& formats) {
+
+vk::SurfaceFormatKHR ChooseSwapSurfaceFormat(const std::vector<vk::SurfaceFormatKHR>& formats,
+                                             bool srgb) {
     if (formats.size() == 1 && formats[0].format == vk::Format::eUndefined) {
-        return {vk::Format::eB8G8R8A8Unorm, vk::ColorSpaceKHR::eSrgbNonlinear};
+        vk::SurfaceFormatKHR format;
+        format.format = vk::Format::eB8G8R8A8Unorm;
+        format.colorSpace = vk::ColorSpaceKHR::eSrgbNonlinear;
+        return format;
     }
-    const auto& found = std::find_if(formats.begin(), formats.end(), [](const auto& format) {
-        return format.format == vk::Format::eB8G8R8A8Unorm &&
+    const auto& found = std::find_if(formats.begin(), formats.end(), [srgb](const auto& format) {
+        const auto request_format = srgb ? vk::Format::eB8G8R8A8Srgb : vk::Format::eB8G8R8A8Unorm;
+        return format.format == request_format &&
                format.colorSpace == vk::ColorSpaceKHR::eSrgbNonlinear;
     });
     return found != formats.end() ? *found : formats[0];
@@ -51,28 +57,26 @@ vk::Extent2D ChooseSwapExtent(const vk::SurfaceCapabilitiesKHR& capabilities, u3
                              std::min(capabilities.maxImageExtent.height, extent.height));
     return extent;
 }
-} // namespace
+
+} // Anonymous namespace
 
 VKSwapchain::VKSwapchain(vk::SurfaceKHR surface, const VKDevice& device)
     : surface{surface}, device{device} {}
 
 VKSwapchain::~VKSwapchain() = default;
 
-void VKSwapchain::Create(u32 width, u32 height) {
-    const auto dev = device.GetLogical();
+void VKSwapchain::Create(u32 width, u32 height, bool srgb) {
     const auto& dld = device.GetDispatchLoader();
     const auto physical_device = device.GetPhysical();
-
-    const vk::SurfaceCapabilitiesKHR capabilities{
-        physical_device.getSurfaceCapabilitiesKHR(surface, dld)};
+    const auto capabilities{physical_device.getSurfaceCapabilitiesKHR(surface, dld)};
     if (capabilities.maxImageExtent.width == 0 || capabilities.maxImageExtent.height == 0) {
         return;
     }
 
-    dev.waitIdle(dld);
+    device.GetLogical().waitIdle(dld);
     Destroy();
 
-    CreateSwapchain(capabilities, width, height);
+    CreateSwapchain(capabilities, width, height, srgb);
     CreateSemaphores();
     CreateImageViews();
 
@@ -107,7 +111,7 @@ bool VKSwapchain::Present(vk::Semaphore render_semaphore, VKFence& fence) {
         break;
     case vk::Result::eErrorOutOfDateKHR:
         if (current_width > 0 && current_height > 0) {
-            Create(current_width, current_height);
+            Create(current_width, current_height, current_srgb);
             recreated = true;
         }
         break;
@@ -129,23 +133,19 @@ bool VKSwapchain::HasFramebufferChanged(const Layout::FramebufferLayout& framebu
 }
 
 void VKSwapchain::CreateSwapchain(const vk::SurfaceCapabilitiesKHR& capabilities, u32 width,
-                                  u32 height) {
-    const auto dev{device.GetLogical()};
+                                  u32 height, bool srgb) {
     const auto& dld{device.GetDispatchLoader()};
     const auto physical_device{device.GetPhysical()};
+    const auto formats{physical_device.getSurfaceFormatsKHR(surface, dld)};
+    const auto present_modes{physical_device.getSurfacePresentModesKHR(surface, dld)};
 
-    const std::vector<vk::SurfaceFormatKHR> formats{
-        physical_device.getSurfaceFormatsKHR(surface, dld)};
-
-    const std::vector<vk::PresentModeKHR> present_modes{
-        physical_device.getSurfacePresentModesKHR(surface, dld)};
-
-    const vk::SurfaceFormatKHR surface_format{ChooseSwapSurfaceFormat(formats)};
+    const vk::SurfaceFormatKHR surface_format{ChooseSwapSurfaceFormat(formats, srgb)};
     const vk::PresentModeKHR present_mode{ChooseSwapPresentMode(present_modes)};
     extent = ChooseSwapExtent(capabilities, width, height);
 
     current_width = extent.width;
     current_height = extent.height;
+    current_srgb = srgb;
 
     u32 requested_image_count{capabilities.minImageCount + 1};
     if (capabilities.maxImageCount > 0 && requested_image_count > capabilities.maxImageCount) {
@@ -169,6 +169,7 @@ void VKSwapchain::CreateSwapchain(const vk::SurfaceCapabilitiesKHR& capabilities
         swapchain_ci.imageSharingMode = vk::SharingMode::eExclusive;
     }
 
+    const auto dev{device.GetLogical()};
     swapchain = dev.createSwapchainKHRUnique(swapchain_ci, nullptr, dld);
 
     images = dev.getSwapchainImagesKHR(*swapchain, dld);
diff --git a/src/video_core/renderer_vulkan/vk_swapchain.h b/src/video_core/renderer_vulkan/vk_swapchain.h
index 2ad84f185f..a1e7938d2f 100644
--- a/src/video_core/renderer_vulkan/vk_swapchain.h
+++ b/src/video_core/renderer_vulkan/vk_swapchain.h
@@ -24,7 +24,7 @@ public:
     ~VKSwapchain();
 
     /// Creates (or recreates) the swapchain with a given size.
-    void Create(u32 width, u32 height);
+    void Create(u32 width, u32 height, bool srgb);
 
     /// Acquires the next image in the swapchain, waits as needed.
     void AcquireNextImage();
@@ -60,8 +60,13 @@ public:
         return image_format;
     }
 
+    bool GetSrgbState() const {
+        return current_srgb;
+    }
+
 private:
-    void CreateSwapchain(const vk::SurfaceCapabilitiesKHR& capabilities, u32 width, u32 height);
+    void CreateSwapchain(const vk::SurfaceCapabilitiesKHR& capabilities, u32 width, u32 height,
+                         bool srgb);
     void CreateSemaphores();
     void CreateImageViews();
 
@@ -87,6 +92,7 @@ private:
 
     u32 current_width{};
     u32 current_height{};
+    bool current_srgb{};
 };
 
 } // namespace Vulkan