vulkan_swap_chain.h (5041B)
1 // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com> 2 // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) 3 4 #pragma once 5 6 #include "vulkan_loader.h" 7 #include "vulkan_texture.h" 8 #include "window_info.h" 9 10 #include "common/types.h" 11 12 #include <array> 13 #include <memory> 14 #include <optional> 15 #include <vector> 16 17 class VulkanSwapChain 18 { 19 public: 20 // We don't actually need +1 semaphores, or, more than one really. 21 // But, the validation layer gets cranky if we don't fence wait before the next image acquire. 22 // So, add an additional semaphore to ensure that we're never acquiring before fence waiting. 23 static constexpr u32 NUM_SEMAPHORES = 4; // Should be command buffers + 1 24 25 ~VulkanSwapChain(); 26 27 // Creates a vulkan-renderable surface for the specified window handle. 28 static VkSurfaceKHR CreateVulkanSurface(VkInstance instance, VkPhysicalDevice physical_device, WindowInfo* wi); 29 30 // Destroys a previously-created surface. 31 static void DestroyVulkanSurface(VkInstance instance, WindowInfo* wi, VkSurfaceKHR surface); 32 33 // Create a new swap chain from a pre-existing surface. 34 static std::unique_ptr<VulkanSwapChain> Create(const WindowInfo& wi, VkSurfaceKHR surface, 35 VkPresentModeKHR present_mode, 36 std::optional<bool> exclusive_fullscreen_control); 37 38 // Determines present mode to use. 39 static bool SelectPresentMode(VkSurfaceKHR surface, GPUVSyncMode* vsync_mode, VkPresentModeKHR* present_mode); 40 41 ALWAYS_INLINE VkSurfaceKHR GetSurface() const { return m_surface; } 42 ALWAYS_INLINE VkSwapchainKHR GetSwapChain() const { return m_swap_chain; } 43 ALWAYS_INLINE const VkSwapchainKHR* GetSwapChainPtr() const { return &m_swap_chain; } 44 ALWAYS_INLINE const WindowInfo& GetWindowInfo() const { return m_window_info; } 45 ALWAYS_INLINE u32 GetWidth() const { return m_window_info.surface_width; } 46 ALWAYS_INLINE u32 GetHeight() const { return m_window_info.surface_height; } 47 ALWAYS_INLINE float GetScale() const { return m_window_info.surface_scale; } 48 ALWAYS_INLINE u32 GetCurrentImageIndex() const { return m_current_image; } 49 ALWAYS_INLINE const u32* GetCurrentImageIndexPtr() const { return &m_current_image; } 50 ALWAYS_INLINE u32 GetImageCount() const { return static_cast<u32>(m_images.size()); } 51 ALWAYS_INLINE VkFormat GetImageFormat() const { return m_format; } 52 ALWAYS_INLINE VkImage GetCurrentImage() const { return m_images[m_current_image].image; } 53 ALWAYS_INLINE VkImageView GetCurrentImageView() const { return m_images[m_current_image].view; } 54 ALWAYS_INLINE VkFramebuffer GetCurrentFramebuffer() const { return m_images[m_current_image].framebuffer; } 55 ALWAYS_INLINE VkSemaphore GetImageAvailableSemaphore() const 56 { 57 return m_semaphores[m_current_semaphore].available_semaphore; 58 } 59 ALWAYS_INLINE const VkSemaphore* GetImageAvailableSemaphorePtr() const 60 { 61 return &m_semaphores[m_current_semaphore].available_semaphore; 62 } 63 ALWAYS_INLINE VkSemaphore GetRenderingFinishedSemaphore() const 64 { 65 return m_semaphores[m_current_semaphore].rendering_finished_semaphore; 66 } 67 ALWAYS_INLINE const VkSemaphore* GetRenderingFinishedSemaphorePtr() const 68 { 69 return &m_semaphores[m_current_semaphore].rendering_finished_semaphore; 70 } 71 72 // Returns true if the current present mode is synchronizing (adaptive or hard). 73 ALWAYS_INLINE bool IsPresentModeSynchronizing() const { return (m_present_mode == VK_PRESENT_MODE_FIFO_KHR); } 74 ALWAYS_INLINE VkPresentModeKHR GetPresentMode() const { return m_present_mode; } 75 76 VkResult AcquireNextImage(); 77 void ReleaseCurrentImage(); 78 void ResetImageAcquireResult(); 79 80 bool RecreateSurface(const WindowInfo& new_wi); 81 bool ResizeSwapChain(u32 new_width = 0, u32 new_height = 0, float new_scale = 1.0f); 82 83 // Change vsync enabled state. This may fail as it causes a swapchain recreation. 84 bool SetPresentMode(VkPresentModeKHR present_mode); 85 86 private: 87 VulkanSwapChain(const WindowInfo& wi, VkSurfaceKHR surface, VkPresentModeKHR present_mode, 88 std::optional<bool> exclusive_fullscreen_control); 89 90 static std::optional<VkSurfaceFormatKHR> SelectSurfaceFormat(VkSurfaceKHR surface); 91 92 bool CreateSwapChain(); 93 void DestroySwapChain(); 94 95 void DestroySwapChainImages(); 96 97 void DestroySurface(); 98 99 struct Image 100 { 101 VkImage image; 102 VkImageView view; 103 VkFramebuffer framebuffer; 104 }; 105 106 struct ImageSemaphores 107 { 108 VkSemaphore available_semaphore; 109 VkSemaphore rendering_finished_semaphore; 110 }; 111 112 WindowInfo m_window_info; 113 114 VkSurfaceKHR m_surface = VK_NULL_HANDLE; 115 VkSwapchainKHR m_swap_chain = VK_NULL_HANDLE; 116 117 std::vector<Image> m_images; 118 std::array<ImageSemaphores, NUM_SEMAPHORES> m_semaphores = {}; 119 120 u32 m_current_image = 0; 121 u32 m_current_semaphore = 0; 122 123 VkFormat m_format = VK_FORMAT_UNDEFINED; 124 VkPresentModeKHR m_present_mode = VK_PRESENT_MODE_IMMEDIATE_KHR; 125 126 std::optional<VkResult> m_image_acquire_result; 127 std::optional<bool> m_exclusive_fullscreen_control; 128 };