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