1 /* Copyright (c) 2017-2020 Hans-Kristian Arntzen 2 * 3 * Permission is hereby granted, free of charge, to any person obtaining 4 * a copy of this software and associated documentation files (the 5 * "Software"), to deal in the Software without restriction, including 6 * without limitation the rights to use, copy, modify, merge, publish, 7 * distribute, sublicense, and/or sell copies of the Software, and to 8 * permit persons to whom the Software is furnished to do so, subject to 9 * the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be 12 * included in all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 18 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 19 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 20 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 */ 22 23 #pragma once 24 25 #include "device.hpp" 26 #include "semaphore_manager.hpp" 27 #include "vulkan_headers.hpp" 28 #include "timer.hpp" 29 #include "wsi_timing.hpp" 30 #include <memory> 31 #include <vector> 32 33 namespace Vulkan 34 { 35 class WSI; 36 37 class WSIPlatform 38 { 39 public: 40 virtual ~WSIPlatform() = default; 41 42 virtual VkSurfaceKHR create_surface(VkInstance instance, VkPhysicalDevice gpu) = 0; 43 virtual std::vector<const char *> get_instance_extensions() = 0; get_device_extensions()44 virtual std::vector<const char *> get_device_extensions() 45 { 46 return { "VK_KHR_swapchain" }; 47 } 48 get_preferred_format()49 virtual VkFormat get_preferred_format() 50 { 51 return VK_FORMAT_B8G8R8A8_SRGB; 52 } 53 should_resize()54 bool should_resize() 55 { 56 return resize; 57 } 58 acknowledge_resize()59 void acknowledge_resize() 60 { 61 resize = false; 62 } 63 64 virtual uint32_t get_surface_width() = 0; 65 virtual uint32_t get_surface_height() = 0; 66 get_aspect_ratio()67 virtual float get_aspect_ratio() 68 { 69 return float(get_surface_width()) / float(get_surface_height()); 70 } 71 72 virtual bool alive(Vulkan::WSI &wsi) = 0; 73 virtual void poll_input() = 0; has_external_swapchain()74 virtual bool has_external_swapchain() 75 { 76 return false; 77 } 78 get_frame_timer()79 Util::FrameTimer &get_frame_timer() 80 { 81 return timer; 82 } 83 release_resources()84 virtual void release_resources() 85 { 86 } 87 88 virtual void event_device_created(Device *device); 89 virtual void event_device_destroyed(); 90 virtual void event_swapchain_created(Device *device, unsigned width, unsigned height, 91 float aspect_ratio, size_t num_swapchain_images, VkFormat format, VkSurfaceTransformFlagBitsKHR pre_rotate); 92 virtual void event_swapchain_destroyed(); 93 virtual void event_frame_tick(double frame, double elapsed); 94 virtual void event_swapchain_index(Device *device, unsigned index); 95 virtual void event_display_timing_stutter(uint32_t current_serial, uint32_t observed_serial, 96 unsigned dropped_frames); 97 98 virtual float get_estimated_frame_presentation_duration(); 99 100 virtual void set_window_title(const std::string &title); 101 102 virtual uintptr_t get_fullscreen_monitor(); 103 104 protected: 105 bool resize = false; 106 107 private: 108 Util::FrameTimer timer; 109 }; 110 111 enum class PresentMode 112 { 113 SyncToVBlank, // Force FIFO 114 UnlockedMaybeTear, // MAILBOX or IMMEDIATE 115 UnlockedForceTearing, // Force IMMEDIATE 116 UnlockedNoTearing // Force MAILBOX 117 }; 118 119 class WSI 120 { 121 public: 122 WSI(); 123 void set_platform(WSIPlatform *platform); 124 void set_present_mode(PresentMode mode); 125 void set_backbuffer_srgb(bool enable); 126 void set_support_prerotate(bool enable); 127 VkSurfaceTransformFlagBitsKHR get_current_prerotate() const; 128 get_present_mode() const129 PresentMode get_present_mode() const 130 { 131 return present_mode; 132 } 133 get_backbuffer_srgb() const134 bool get_backbuffer_srgb() const 135 { 136 return srgb_backbuffer_enable; 137 } 138 139 bool init(unsigned num_thread_indices); 140 bool init_external_context(std::unique_ptr<Vulkan::Context> context); 141 bool init_external_swapchain(std::vector<Vulkan::ImageHandle> external_images); 142 void deinit_external(); 143 144 ~WSI(); 145 get_context()146 inline Context &get_context() 147 { 148 return *context; 149 } 150 get_device()151 inline Device &get_device() 152 { 153 return *device; 154 } 155 156 bool begin_frame(); 157 bool end_frame(); 158 void set_external_frame(unsigned index, Vulkan::Semaphore acquire_semaphore, double frame_time); 159 Vulkan::Semaphore consume_external_release_semaphore(); 160 get_platform()161 WSIPlatform &get_platform() 162 { 163 VK_ASSERT(platform); 164 return *platform; 165 } 166 167 void deinit_surface_and_swapchain(); 168 void init_surface_and_swapchain(VkSurfaceKHR new_surface); 169 170 float get_estimated_video_latency(); 171 void set_window_title(const std::string &title); 172 173 double get_smooth_frame_time() const; 174 double get_smooth_elapsed_time() const; 175 176 double get_estimated_refresh_interval() const; 177 get_timing()178 WSITiming &get_timing() 179 { 180 return timing; 181 } 182 183 static void build_prerotate_matrix_2x2(VkSurfaceTransformFlagBitsKHR pre_rotate, float mat[4]); 184 185 private: 186 void update_framebuffer(unsigned width, unsigned height); 187 188 std::unique_ptr<Context> context; 189 VkSurfaceKHR surface = VK_NULL_HANDLE; 190 VkSwapchainKHR swapchain = VK_NULL_HANDLE; 191 std::vector<VkImage> swapchain_images; 192 std::vector<Semaphore> release_semaphores; 193 std::unique_ptr<Device> device; 194 const VolkDeviceTable *table = nullptr; 195 196 unsigned swapchain_width = 0; 197 unsigned swapchain_height = 0; 198 float swapchain_aspect_ratio = 1.0f; 199 VkFormat swapchain_format = VK_FORMAT_UNDEFINED; 200 PresentMode current_present_mode = PresentMode::SyncToVBlank; 201 PresentMode present_mode = PresentMode::SyncToVBlank; 202 203 enum class SwapchainError 204 { 205 None, 206 NoSurface, 207 Error 208 }; 209 SwapchainError init_swapchain(unsigned width, unsigned height); 210 bool blocking_init_swapchain(unsigned width, unsigned height); 211 212 uint32_t swapchain_index = 0; 213 bool has_acquired_swapchain_index = false; 214 215 WSIPlatform *platform = nullptr; 216 217 std::vector<Vulkan::ImageHandle> external_swapchain_images; 218 219 unsigned external_frame_index = 0; 220 Vulkan::Semaphore external_acquire; 221 Vulkan::Semaphore external_release; 222 bool frame_is_external = false; 223 bool using_display_timing = false; 224 bool srgb_backbuffer_enable = true; 225 bool current_srgb_backbuffer_enable = true; 226 bool support_prerotate = false; 227 VkSurfaceTransformFlagBitsKHR swapchain_current_prerotate = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; 228 229 bool begin_frame_external(); 230 double external_frame_time = 0.0; 231 232 double smooth_frame_time = 0.0; 233 double smooth_elapsed_time = 0.0; 234 235 WSITiming timing; 236 237 void tear_down_swapchain(); 238 void drain_swapchain(); 239 }; 240 } 241