From f1ee761290e643ab5d724ad30d8a73590e7eecf7 Mon Sep 17 00:00:00 2001
From: Philip Rebohle <philip.rebohle@tu-dortmund.de>
Date: Mon, 4 Dec 2017 22:21:02 +0100
Subject: [PATCH] [dxgi] DxgiPresenter now picks format depending on swap chain
 format

---
 src/dxgi/dxgi_presenter.cpp | 53 ++++++++++++++++++++++++++++++++-----
 src/dxgi/dxgi_presenter.h   | 18 +++++++++++--
 src/dxgi/dxgi_swapchain.cpp | 42 ++++++++++++++++++++++++-----
 src/dxgi/dxgi_swapchain.h   |  2 ++
 src/dxvk/dxvk_surface.cpp   | 42 ++++++++++++++++++-----------
 src/dxvk/dxvk_surface.h     | 12 ++++++---
 src/dxvk/dxvk_swapchain.cpp |  4 +--
 7 files changed, 136 insertions(+), 37 deletions(-)

diff --git a/src/dxgi/dxgi_presenter.cpp b/src/dxgi/dxgi_presenter.cpp
index 6f8a7efd..1ad17d3a 100644
--- a/src/dxgi/dxgi_presenter.cpp
+++ b/src/dxgi/dxgi_presenter.cpp
@@ -7,8 +7,9 @@ namespace dxvk {
   DxgiPresenter::DxgiPresenter(
     const Rc<DxvkDevice>& device,
           HWND            window,
-          UINT            bufferWidth,
-          UINT            bufferHeight)
+          uint32_t        bufferWidth,
+          uint32_t        bufferHeight,
+          DXGI_FORMAT     bufferFormat)
   : m_device  (device),
     m_context (device->createContext()) {
     
@@ -20,11 +21,10 @@ namespace dxvk {
     
     // Create swap chain for the surface
     DxvkSwapchainProperties swapchainProperties;
-    swapchainProperties.preferredSurfaceFormat.format     = VK_FORMAT_B8G8R8A8_SRGB;
-    swapchainProperties.preferredSurfaceFormat.colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
-    swapchainProperties.preferredPresentMode              = VK_PRESENT_MODE_FIFO_KHR;
-    swapchainProperties.preferredBufferSize.width         = bufferWidth;
-    swapchainProperties.preferredBufferSize.height        = bufferHeight;
+    swapchainProperties.preferredSurfaceFormat      = this->pickFormat(bufferFormat);
+    swapchainProperties.preferredPresentMode        = VK_PRESENT_MODE_FIFO_KHR;
+    swapchainProperties.preferredBufferSize.width   = bufferWidth;
+    swapchainProperties.preferredBufferSize.height  = bufferHeight;
     
     m_swapchain = m_device->createSwapchain(
       m_surface, swapchainProperties);
@@ -173,6 +173,45 @@ namespace dxvk {
   }
   
   
+  void DxgiPresenter::recreateSwapchain(
+        uint32_t        bufferWidth,
+        uint32_t        bufferHeight,
+        DXGI_FORMAT     bufferFormat) {
+    DxvkSwapchainProperties swapchainProperties;
+    swapchainProperties.preferredSurfaceFormat      = this->pickFormat(bufferFormat);
+    swapchainProperties.preferredPresentMode        = VK_PRESENT_MODE_FIFO_KHR;
+    swapchainProperties.preferredBufferSize.width   = bufferWidth;
+    swapchainProperties.preferredBufferSize.height  = bufferHeight;
+    
+    m_swapchain->changeProperties(swapchainProperties);
+  }
+  
+  
+  VkSurfaceFormatKHR DxgiPresenter::pickFormat(DXGI_FORMAT fmt) const {
+    std::vector<VkSurfaceFormatKHR> formats;
+    
+    switch (fmt) {
+      case DXGI_FORMAT_R8G8B8A8_UNORM:
+      case DXGI_FORMAT_B8G8R8A8_UNORM: {
+        formats.push_back({ VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR });
+        formats.push_back({ VK_FORMAT_B8G8R8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR });
+      } break;
+      
+      case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
+      case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: {
+        formats.push_back({ VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR });
+        formats.push_back({ VK_FORMAT_B8G8R8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR });
+      } break;
+      
+      default:
+        Logger::warn(str::format("DxgiPresenter: Unknown format: ", fmt));
+    }
+    
+    return m_surface->pickSurfaceFormat(
+      formats.size(), formats.data());
+  }
+  
+  
   Rc<DxvkShader> DxgiPresenter::createVertexShader() {
     SpirvModule module;
     
diff --git a/src/dxgi/dxgi_presenter.h b/src/dxgi/dxgi_presenter.h
index 7db2ed1f..5c8b9da8 100644
--- a/src/dxgi/dxgi_presenter.h
+++ b/src/dxgi/dxgi_presenter.h
@@ -4,6 +4,8 @@
 #include <dxvk_surface.h>
 #include <dxvk_swapchain.h>
 
+#include "dxgi_include.h"
+
 #include "../spirv/spirv_module.h"
 
 namespace dxvk {
@@ -22,8 +24,9 @@ namespace dxvk {
     DxgiPresenter(
       const Rc<DxvkDevice>& device,
             HWND            window,
-            UINT            bufferWidth,
-            UINT            bufferHeight);
+            uint32_t        bufferWidth,
+            uint32_t        bufferHeight,
+            DXGI_FORMAT     bufferFormat);
     
     ~DxgiPresenter();
     
@@ -41,6 +44,15 @@ namespace dxvk {
     void presentImage(
       const Rc<DxvkImageView>& view);
     
+    /**
+     * \brief Renders image to the screen
+     * \param [in] view Source image view
+     */
+    void recreateSwapchain(
+          uint32_t        bufferWidth,
+          uint32_t        bufferHeight,
+          DXGI_FORMAT     bufferFormat);
+    
   private:
     
     enum BindingIds : uint32_t {
@@ -59,6 +71,8 @@ namespace dxvk {
     
     Rc<DxvkSampler>     m_sampler;
     
+    VkSurfaceFormatKHR pickFormat(DXGI_FORMAT fmt) const;
+    
     Rc<DxvkShader> createVertexShader();
     Rc<DxvkShader> createFragmentShader();
     
diff --git a/src/dxgi/dxgi_swapchain.cpp b/src/dxgi/dxgi_swapchain.cpp
index 207f6821..65383acc 100644
--- a/src/dxgi/dxgi_swapchain.cpp
+++ b/src/dxgi/dxgi_swapchain.cpp
@@ -46,6 +46,13 @@ namespace dxvk {
         SDL_GetError()));
     }
     
+    // Adjust initial back buffer size. If zero, these
+    // shall be set to the current window size.
+    VkExtent2D windowSize = this->getWindowSize();
+    
+    if (m_desc.BufferDesc.Width  == 0) m_desc.BufferDesc.Width  = windowSize.width;
+    if (m_desc.BufferDesc.Height == 0) m_desc.BufferDesc.Height = windowSize.height;
+    
     // Set initial window mode and fullscreen state
     if (FAILED(this->SetFullscreenState(!pDesc->Windowed, nullptr)))
       throw DxvkError("DxgiSwapChain::DxgiSwapChain: Failed to set initial fullscreen state");
@@ -187,15 +194,24 @@ namespace dxvk {
           UINT        SwapChainFlags) {
     std::lock_guard<std::mutex> lock(m_mutex);
     
-    m_desc.BufferDesc.Width  = Width;
-    m_desc.BufferDesc.Height = Height;
-    m_desc.BufferDesc.Format = NewFormat;
-    m_desc.Flags             = SwapChainFlags;
+    VkExtent2D windowSize = this->getWindowSize();
+    
+    m_desc.BufferDesc.Width = Width != 0 ? Width : windowSize.width;
+    m_desc.BufferDesc.Height = Height != 0 ? Height : windowSize.height;
+    
+    m_desc.Flags = SwapChainFlags;
     
     if (BufferCount != 0)
-      m_desc.BufferCount     = BufferCount;
+      m_desc.BufferCount = BufferCount;
+    
+    if (NewFormat != DXGI_FORMAT_UNKNOWN)
+      m_desc.BufferDesc.Format = NewFormat;
     
     try {
+      m_presenter->recreateSwapchain(
+        m_desc.BufferDesc.Width,
+        m_desc.BufferDesc.Height,
+        m_desc.BufferDesc.Format);
       this->createBackBuffer();
       return S_OK;
     } catch (const DxvkError& err) {
@@ -289,7 +305,8 @@ namespace dxvk {
       m_device->GetDXVKDevice(),
       m_desc.OutputWindow,
       m_desc.BufferDesc.Width,
-      m_desc.BufferDesc.Height);
+      m_desc.BufferDesc.Height,
+      m_desc.BufferDesc.Format);
   }
   
   
@@ -369,4 +386,17 @@ namespace dxvk {
     m_presenter->initBackBuffer(m_backBuffer);
   }
   
+  
+  VkExtent2D DxgiSwapChain::getWindowSize() const {
+    int winWidth = 0;
+    int winHeight = 0;
+    
+    SDL_GetWindowSize(m_window, &winWidth, &winHeight);
+    
+    VkExtent2D result;
+    result.width  = winWidth;
+    result.height = winHeight;
+    return result;
+  }
+  
 }
diff --git a/src/dxgi/dxgi_swapchain.h b/src/dxgi/dxgi_swapchain.h
index 673c8776..15828bde 100644
--- a/src/dxgi/dxgi_swapchain.h
+++ b/src/dxgi/dxgi_swapchain.h
@@ -113,6 +113,8 @@ namespace dxvk {
     
     void createContext();
     
+    VkExtent2D getWindowSize() const;
+        
   };
   
 }
diff --git a/src/dxvk/dxvk_surface.cpp b/src/dxvk/dxvk_surface.cpp
index 302ca6da..630ee6fa 100644
--- a/src/dxvk/dxvk_surface.cpp
+++ b/src/dxvk/dxvk_surface.cpp
@@ -32,18 +32,24 @@ namespace dxvk {
   }
   
   
-  VkSurfaceFormatKHR DxvkSurface::pickSurfaceFormat(VkSurfaceFormatKHR preferred) const {
-    // If the implementation allows us to freely choose
-    // the format, we'll just use the preferred format.
-    if (m_surfaceFormats.size() == 1 && m_surfaceFormats.at(0).format == VK_FORMAT_UNDEFINED)
-      return preferred;
-    
-    // If the preferred format is explicitly listed in
-    // the array of supported surface formats, use it
-    for (auto fmt : m_surfaceFormats) {
-      if (fmt.format     == preferred.format
-       && fmt.colorSpace == preferred.colorSpace)
-        return fmt;
+  VkSurfaceFormatKHR DxvkSurface::pickSurfaceFormat(
+          uint32_t            preferredCount,
+    const VkSurfaceFormatKHR* preferred) const {
+    if (preferredCount > 0) {
+      // If the implementation allows us to freely choose
+      // the format, we'll just use the preferred format.
+      if (m_surfaceFormats.size() == 1 && m_surfaceFormats.at(0).format == VK_FORMAT_UNDEFINED)
+        return preferred[0];
+      
+      // If the preferred format is explicitly listed in
+      // the array of supported surface formats, use it
+      for (uint32_t i = 0; i < preferredCount; i++) {
+        for (auto fmt : m_surfaceFormats) {
+          if (fmt.format     == preferred[i].format
+           && fmt.colorSpace == preferred[i].colorSpace)
+            return fmt;
+        }
+      }
     }
     
     // Otherwise, fall back to the first format
@@ -51,10 +57,14 @@ namespace dxvk {
   }
   
   
-  VkPresentModeKHR DxvkSurface::pickPresentMode(VkPresentModeKHR preferred) const {
-    for (auto mode : m_presentModes) {
-      if (mode == preferred)
-        return mode;
+  VkPresentModeKHR DxvkSurface::pickPresentMode(
+          uint32_t            preferredCount,
+    const VkPresentModeKHR*   preferred) const {
+    for (uint32_t i = 0; i < preferredCount; i++) {
+      for (auto mode : m_presentModes) {
+        if (mode == preferred[i])
+          return mode;
+      }
     }
     
     // This mode is guaranteed to be available
diff --git a/src/dxvk/dxvk_surface.h b/src/dxvk/dxvk_surface.h
index ee25508e..252f29b4 100644
--- a/src/dxvk/dxvk_surface.h
+++ b/src/dxvk/dxvk_surface.h
@@ -41,20 +41,24 @@ namespace dxvk {
     /**
      * \brief Picks a suitable surface format
      * 
-     * \param [in] preferred Preferred surface format
+     * \param [in] preferredCount Number of formats to probe
+     * \param [in] preferred Preferred surface formats
      * \returns The actual surface format
      */
     VkSurfaceFormatKHR pickSurfaceFormat(
-      VkSurfaceFormatKHR preferred) const;
+            uint32_t            preferredCount,
+      const VkSurfaceFormatKHR* preferred) const;
     
     /**
      * \brief Picks a supported present mode
      * 
-     * \param [in] preferred The preferred present mode
+     * \param [in] preferredCount Number of modes to probe
+     * \param [in] preferred Preferred present modes
      * \returns The actual present mode
      */
     VkPresentModeKHR pickPresentMode(
-      VkPresentModeKHR preferred) const;
+            uint32_t            preferredCount,
+      const VkPresentModeKHR*   preferred) const;
     
     /**
      * \brief Picks a suitable image count for a swap chain
diff --git a/src/dxvk/dxvk_swapchain.cpp b/src/dxvk/dxvk_swapchain.cpp
index ffdd76eb..060e0f89 100644
--- a/src/dxvk/dxvk_swapchain.cpp
+++ b/src/dxvk/dxvk_swapchain.cpp
@@ -94,8 +94,8 @@ namespace dxvk {
     
     // Recreate the actual swapchain object
     auto caps = m_surface->getSurfaceCapabilities();
-    auto fmt  = m_surface->pickSurfaceFormat(m_properties.preferredSurfaceFormat);
-    auto mode = m_surface->pickPresentMode  (m_properties.preferredPresentMode);
+    auto fmt  = m_surface->pickSurfaceFormat(1, &m_properties.preferredSurfaceFormat);
+    auto mode = m_surface->pickPresentMode  (1, &m_properties.preferredPresentMode);
     
     VkSwapchainCreateInfoKHR swapInfo;
     swapInfo.sType                  = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;