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 #include "wsi.hpp"
24 #include "quirks.hpp"
25 #include <thread>
26 
27 using namespace std;
28 
29 namespace Vulkan
30 {
WSI()31 WSI::WSI()
32 {
33 }
34 
set_window_title(const string &)35 void WSIPlatform::set_window_title(const string &)
36 {
37 }
38 
get_fullscreen_monitor()39 uintptr_t WSIPlatform::get_fullscreen_monitor()
40 {
41 	return 0;
42 }
43 
set_window_title(const string & title)44 void WSI::set_window_title(const string &title)
45 {
46 	if (platform)
47 		platform->set_window_title(title);
48 }
49 
get_smooth_elapsed_time() const50 double WSI::get_smooth_elapsed_time() const
51 {
52 	return smooth_elapsed_time;
53 }
54 
get_smooth_frame_time() const55 double WSI::get_smooth_frame_time() const
56 {
57 	return smooth_frame_time;
58 }
59 
get_estimated_frame_presentation_duration()60 float WSIPlatform::get_estimated_frame_presentation_duration()
61 {
62 	// Just assume 60 FPS for now.
63 	// TODO: Be more intelligent.
64 	return 1.0f / 60.0f;
65 }
66 
get_estimated_video_latency()67 float WSI::get_estimated_video_latency()
68 {
69 	if (using_display_timing)
70 	{
71 		// Very accurate estimate.
72 		double latency = timing.get_current_latency();
73 		return float(latency);
74 	}
75 	else
76 	{
77 		// Very rough estimate.
78 		unsigned latency_frames = device->get_num_swapchain_images();
79 		if (latency_frames > 0)
80 			latency_frames--;
81 
82 		if (platform)
83 		{
84 			float frame_duration = platform->get_estimated_frame_presentation_duration();
85 			return frame_duration * float(latency_frames);
86 		}
87 		else
88 			return -1.0f;
89 	}
90 }
91 
init_external_context(unique_ptr<Context> fresh_context)92 bool WSI::init_external_context(unique_ptr<Context> fresh_context)
93 {
94 	context = move(fresh_context);
95 
96 	// Need to have a dummy swapchain in place before we issue create device events.
97 	device.reset(new Device);
98 	device->set_context(*context);
99 	device->init_external_swapchain({ ImageHandle(nullptr) });
100 	platform->event_device_created(device.get());
101 	table = &context->get_device_table();
102 	return true;
103 }
104 
init_external_swapchain(vector<ImageHandle> swapchain_images_)105 bool WSI::init_external_swapchain(vector<ImageHandle> swapchain_images_)
106 {
107 	swapchain_width = platform->get_surface_width();
108 	swapchain_height = platform->get_surface_height();
109 	swapchain_aspect_ratio = platform->get_aspect_ratio();
110 
111 	external_swapchain_images = move(swapchain_images_);
112 
113 	swapchain_width = external_swapchain_images.front()->get_width();
114 	swapchain_height = external_swapchain_images.front()->get_height();
115 	swapchain_format = external_swapchain_images.front()->get_format();
116 
117 	LOGI("Created swapchain %u x %u (fmt: %u).\n",
118 	     swapchain_width, swapchain_height, static_cast<unsigned>(swapchain_format));
119 
120 	platform->event_swapchain_destroyed();
121 	platform->event_swapchain_created(device.get(), swapchain_width, swapchain_height,
122 	                                  swapchain_aspect_ratio,
123 	                                  external_swapchain_images.size(),
124 	                                  swapchain_format, swapchain_current_prerotate);
125 
126 	device->init_external_swapchain(this->external_swapchain_images);
127 	platform->get_frame_timer().reset();
128 	external_acquire.reset();
129 	external_release.reset();
130 	return true;
131 }
132 
set_platform(WSIPlatform * platform_)133 void WSI::set_platform(WSIPlatform *platform_)
134 {
135 	platform = platform_;
136 }
137 
init(unsigned num_thread_indices)138 bool WSI::init(unsigned num_thread_indices)
139 {
140 	auto instance_ext = platform->get_instance_extensions();
141 	auto device_ext = platform->get_device_extensions();
142 	context.reset(new Context);
143 	context->set_num_thread_indices(num_thread_indices);
144 	if (!context->init_instance_and_device(instance_ext.data(), instance_ext.size(), device_ext.data(), device_ext.size()))
145 		return false;
146 
147 	device.reset(new Device);
148 	device->set_context(*context);
149 	table = &context->get_device_table();
150 
151 	platform->event_device_created(device.get());
152 
153 	surface = platform->create_surface(context->get_instance(), context->get_gpu());
154 	if (surface == VK_NULL_HANDLE)
155 		return false;
156 
157 	unsigned width = platform->get_surface_width();
158 	unsigned height = platform->get_surface_height();
159 	swapchain_aspect_ratio = platform->get_aspect_ratio();
160 
161 	VkBool32 supported = VK_FALSE;
162 	vkGetPhysicalDeviceSurfaceSupportKHR(context->get_gpu(), context->get_graphics_queue_family(), surface, &supported);
163 	if (!supported)
164 		return false;
165 
166 	if (!blocking_init_swapchain(width, height))
167 		return false;
168 
169 	device->init_swapchain(swapchain_images, swapchain_width, swapchain_height, swapchain_format);
170 	platform->get_frame_timer().reset();
171 	return true;
172 }
173 
init_surface_and_swapchain(VkSurfaceKHR new_surface)174 void WSI::init_surface_and_swapchain(VkSurfaceKHR new_surface)
175 {
176 	LOGI("init_surface_and_swapchain()\n");
177 	if (new_surface != VK_NULL_HANDLE)
178 	{
179 		VK_ASSERT(surface == VK_NULL_HANDLE);
180 		surface = new_surface;
181 	}
182 
183 	swapchain_width = platform->get_surface_width();
184 	swapchain_height = platform->get_surface_height();
185 	update_framebuffer(swapchain_width, swapchain_height);
186 }
187 
drain_swapchain()188 void WSI::drain_swapchain()
189 {
190 	release_semaphores.clear();
191 	device->set_acquire_semaphore(0, Semaphore{});
192 	device->consume_release_semaphore();
193 	device->wait_idle();
194 }
195 
tear_down_swapchain()196 void WSI::tear_down_swapchain()
197 {
198 	drain_swapchain();
199 
200 	if (swapchain != VK_NULL_HANDLE)
201 		table->vkDestroySwapchainKHR(context->get_device(), swapchain, nullptr);
202 	swapchain = VK_NULL_HANDLE;
203 	has_acquired_swapchain_index = false;
204 }
205 
deinit_surface_and_swapchain()206 void WSI::deinit_surface_and_swapchain()
207 {
208 	LOGI("deinit_surface_and_swapchain()\n");
209 
210 	tear_down_swapchain();
211 
212 	if (surface != VK_NULL_HANDLE)
213 		vkDestroySurfaceKHR(context->get_instance(), surface, nullptr);
214 	surface = VK_NULL_HANDLE;
215 
216 	platform->event_swapchain_destroyed();
217 }
218 
set_external_frame(unsigned index,Semaphore acquire_semaphore,double frame_time)219 void WSI::set_external_frame(unsigned index, Semaphore acquire_semaphore, double frame_time)
220 {
221 	external_frame_index = index;
222 	external_acquire = move(acquire_semaphore);
223 	frame_is_external = true;
224 	external_frame_time = frame_time;
225 }
226 
begin_frame_external()227 bool WSI::begin_frame_external()
228 {
229 	device->next_frame_context();
230 
231 	// Need to handle this stuff from outside.
232 	if (has_acquired_swapchain_index)
233 		return false;
234 
235 	auto frame_time = platform->get_frame_timer().frame(external_frame_time);
236 	auto elapsed_time = platform->get_frame_timer().get_elapsed();
237 
238 	// Assume we have been given a smooth frame pacing.
239 	smooth_frame_time = frame_time;
240 	smooth_elapsed_time = elapsed_time;
241 
242 	// Poll after acquire as well for optimal latency.
243 	platform->poll_input();
244 
245 	swapchain_index = external_frame_index;
246 	platform->event_frame_tick(frame_time, elapsed_time);
247 
248 	platform->event_swapchain_index(device.get(), swapchain_index);
249 	device->set_acquire_semaphore(swapchain_index, external_acquire);
250 	external_acquire.reset();
251 	return true;
252 }
253 
consume_external_release_semaphore()254 Semaphore WSI::consume_external_release_semaphore()
255 {
256 	Semaphore sem;
257 	swap(external_release, sem);
258 	return sem;
259 }
260 
261 //#define VULKAN_WSI_TIMING_DEBUG
262 
begin_frame()263 bool WSI::begin_frame()
264 {
265 	if (frame_is_external)
266 		return begin_frame_external();
267 
268 #ifdef VULKAN_WSI_TIMING_DEBUG
269 	auto next_frame_start = Util::get_current_time_nsecs();
270 #endif
271 
272 	device->next_frame_context();
273 
274 #ifdef VULKAN_WSI_TIMING_DEBUG
275 	auto next_frame_end = Util::get_current_time_nsecs();
276 	LOGI("Waited for vacant frame context for %.3f ms.\n", (next_frame_end - next_frame_start) * 1e-6);
277 #endif
278 
279 	if (swapchain == VK_NULL_HANDLE || platform->should_resize())
280 	{
281 		update_framebuffer(platform->get_surface_width(), platform->get_surface_height());
282 		platform->acknowledge_resize();
283 	}
284 
285 	if (swapchain == VK_NULL_HANDLE)
286 	{
287 		LOGE("Completely lost swapchain. Cannot continue.\n");
288 		return false;
289 	}
290 
291 	if (has_acquired_swapchain_index)
292 		return true;
293 
294 	external_release.reset();
295 
296 	VkResult result;
297 	do
298 	{
299 		auto acquire = device->request_legacy_semaphore();
300 
301 		// For adaptive low latency we don't want to observe the time it takes to wait for
302 		// WSI semaphore as part of our latency,
303 		// which means we will never get sub-frame latency on some implementations,
304 		// so block on that first.
305 		Fence fence;
306 		if (timing.get_options().latency_limiter == LatencyLimiter::AdaptiveLowLatency)
307 			fence = device->request_legacy_fence();
308 
309 #ifdef VULKAN_WSI_TIMING_DEBUG
310 		auto acquire_start = Util::get_current_time_nsecs();
311 #endif
312 
313 		auto acquire_ts = device->write_calibrated_timestamp();
314 		result = table->vkAcquireNextImageKHR(context->get_device(), swapchain, UINT64_MAX, acquire->get_semaphore(),
315 		                                      fence ? fence->get_fence() : VK_NULL_HANDLE, &swapchain_index);
316 		device->register_time_interval("WSI", std::move(acquire_ts), device->write_calibrated_timestamp(), "acquire");
317 
318 #ifdef ANDROID
319 		// Android 10 can return suboptimal here, only because of pre-transform.
320 		// We don't care about that, and treat this as success.
321 		if (result == VK_SUBOPTIMAL_KHR)
322 			result = VK_SUCCESS;
323 #endif
324 
325 		if (result == VK_SUCCESS && fence)
326 			fence->wait();
327 
328 		if (result == VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT)
329 		{
330 			LOGE("Lost exclusive full-screen ...\n");
331 		}
332 
333 #ifdef VULKAN_WSI_TIMING_DEBUG
334 		auto acquire_end = Util::get_current_time_nsecs();
335 		LOGI("vkAcquireNextImageKHR took %.3f ms.\n", (acquire_end - acquire_start) * 1e-6);
336 #endif
337 
338 		if (result == VK_SUCCESS)
339 		{
340 			has_acquired_swapchain_index = true;
341 			acquire->signal_external();
342 
343 			auto frame_time = platform->get_frame_timer().frame();
344 			auto elapsed_time = platform->get_frame_timer().get_elapsed();
345 
346 			if (using_display_timing)
347 				timing.begin_frame(frame_time, elapsed_time);
348 
349 			smooth_frame_time = frame_time;
350 			smooth_elapsed_time = elapsed_time;
351 
352 			// Poll after acquire as well for optimal latency.
353 			platform->poll_input();
354 			platform->event_frame_tick(frame_time, elapsed_time);
355 
356 			platform->event_swapchain_index(device.get(), swapchain_index);
357 
358 			if (device->get_workarounds().wsi_acquire_barrier_is_expensive)
359 			{
360 				// Acquire async. Use the async graphics queue, as it's most likely not being used right away.
361 				device->add_wait_semaphore(CommandBuffer::Type::AsyncGraphics, acquire, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, true);
362 				auto cmd = device->request_command_buffer(CommandBuffer::Type::AsyncGraphics);
363 				cmd->image_barrier(device->get_swapchain_view(swapchain_index).get_image(),
364 				                   VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
365 				                   VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0,
366 				                   VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0);
367 
368 				// Get a new acquire semaphore.
369 				acquire.reset();
370 				device->submit(cmd, nullptr, 1, &acquire);
371 			}
372 
373 			device->set_acquire_semaphore(swapchain_index, acquire);
374 		}
375 		else if (result == VK_SUBOPTIMAL_KHR || result == VK_ERROR_OUT_OF_DATE_KHR ||
376 		         result == VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT)
377 		{
378 			VK_ASSERT(swapchain_width != 0);
379 			VK_ASSERT(swapchain_height != 0);
380 
381 			tear_down_swapchain();
382 
383 			if (!blocking_init_swapchain(swapchain_width, swapchain_height))
384 				return false;
385 			device->init_swapchain(swapchain_images, swapchain_width, swapchain_height, swapchain_format);
386 		}
387 		else
388 		{
389 			return false;
390 		}
391 	} while (result != VK_SUCCESS);
392 	return true;
393 }
394 
end_frame()395 bool WSI::end_frame()
396 {
397 	device->end_frame_context();
398 
399 	// Take ownership of the release semaphore so that the external user can use it.
400 	if (frame_is_external)
401 	{
402 		// If we didn't render into the swapchain this frame, we will return a blank semaphore.
403 		external_release = device->consume_release_semaphore();
404 		if (external_release && !external_release->is_signalled())
405 			abort();
406 		frame_is_external = false;
407 	}
408 	else
409 	{
410 		if (!device->swapchain_touched())
411 			return true;
412 
413 		has_acquired_swapchain_index = false;
414 
415 		auto release = device->consume_release_semaphore();
416 		VK_ASSERT(release);
417 		VK_ASSERT(release->is_signalled());
418 		auto release_semaphore = release->get_semaphore();
419 		VK_ASSERT(release_semaphore != VK_NULL_HANDLE);
420 
421 		VkResult result = VK_SUCCESS;
422 		VkPresentInfoKHR info = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR };
423 		info.waitSemaphoreCount = 1;
424 		info.pWaitSemaphores = &release_semaphore;
425 		info.swapchainCount = 1;
426 		info.pSwapchains = &swapchain;
427 		info.pImageIndices = &swapchain_index;
428 		info.pResults = &result;
429 
430 		VkPresentTimeGOOGLE present_time;
431 		VkPresentTimesInfoGOOGLE present_timing = { VK_STRUCTURE_TYPE_PRESENT_TIMES_INFO_GOOGLE };
432 		present_timing.swapchainCount = 1;
433 		present_timing.pTimes = &present_time;
434 
435 		if (using_display_timing && timing.fill_present_info_timing(present_time))
436 		{
437 			info.pNext = &present_timing;
438 		}
439 
440 #ifdef VULKAN_WSI_TIMING_DEBUG
441 		auto present_start = Util::get_current_time_nsecs();
442 #endif
443 
444 		auto present_ts = device->write_calibrated_timestamp();
445 		VkResult overall = table->vkQueuePresentKHR(context->get_graphics_queue(), &info);
446 		device->register_time_interval("WSI", std::move(present_ts), device->write_calibrated_timestamp(), "present");
447 
448 #ifdef ANDROID
449 		// Android 10 can return suboptimal here, only because of pre-transform.
450 		// We don't care about that, and treat this as success.
451 		if (overall == VK_SUBOPTIMAL_KHR)
452 			overall = VK_SUCCESS;
453 		if (result == VK_SUBOPTIMAL_KHR)
454 			result = VK_SUCCESS;
455 #endif
456 
457 		if (overall == VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT ||
458 		    result == VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT)
459 		{
460 			LOGE("Lost exclusive full-screen ...\n");
461 		}
462 
463 #ifdef VULKAN_WSI_TIMING_DEBUG
464 		auto present_end = Util::get_current_time_nsecs();
465 		LOGI("vkQueuePresentKHR took %.3f ms.\n", (present_end - present_start) * 1e-6);
466 #endif
467 
468 		if (overall != VK_SUCCESS || result != VK_SUCCESS)
469 		{
470 			LOGE("vkQueuePresentKHR failed.\n");
471 			tear_down_swapchain();
472 			return false;
473 		}
474 		else
475 		{
476 			release->wait_external();
477 			// Cannot release the WSI wait semaphore until we observe that the image has been
478 			// waited on again.
479 			release_semaphores[swapchain_index] = release;
480 		}
481 
482 		// Re-init swapchain.
483 		if (present_mode != current_present_mode || srgb_backbuffer_enable != current_srgb_backbuffer_enable)
484 		{
485 			current_present_mode = present_mode;
486 			current_srgb_backbuffer_enable = srgb_backbuffer_enable;
487 			update_framebuffer(swapchain_width, swapchain_height);
488 		}
489 	}
490 
491 	return true;
492 }
493 
update_framebuffer(unsigned width,unsigned height)494 void WSI::update_framebuffer(unsigned width, unsigned height)
495 {
496 	if (context && device)
497 	{
498 		drain_swapchain();
499 		if (blocking_init_swapchain(width, height))
500 			device->init_swapchain(swapchain_images, swapchain_width, swapchain_height, swapchain_format);
501 	}
502 }
503 
set_present_mode(PresentMode mode)504 void WSI::set_present_mode(PresentMode mode)
505 {
506 	present_mode = mode;
507 	if (!has_acquired_swapchain_index && present_mode != current_present_mode)
508 	{
509 		current_present_mode = present_mode;
510 		update_framebuffer(swapchain_width, swapchain_height);
511 	}
512 }
513 
set_backbuffer_srgb(bool enable)514 void WSI::set_backbuffer_srgb(bool enable)
515 {
516 	srgb_backbuffer_enable = enable;
517 	if (!has_acquired_swapchain_index && srgb_backbuffer_enable != current_srgb_backbuffer_enable)
518 	{
519 		current_srgb_backbuffer_enable = srgb_backbuffer_enable;
520 		update_framebuffer(swapchain_width, swapchain_height);
521 	}
522 }
523 
deinit_external()524 void WSI::deinit_external()
525 {
526 	if (platform)
527 		platform->release_resources();
528 
529 	if (context)
530 	{
531 		tear_down_swapchain();
532 		platform->event_swapchain_destroyed();
533 	}
534 
535 	if (surface != VK_NULL_HANDLE)
536 		vkDestroySurfaceKHR(context->get_instance(), surface, nullptr);
537 
538 	if (platform)
539 		platform->event_device_destroyed();
540 	external_release.reset();
541 	external_acquire.reset();
542 	external_swapchain_images.clear();
543 	device.reset();
544 	context.reset();
545 
546 	using_display_timing = false;
547 }
548 
blocking_init_swapchain(unsigned width,unsigned height)549 bool WSI::blocking_init_swapchain(unsigned width, unsigned height)
550 {
551 	SwapchainError err;
552 	unsigned retry_counter = 0;
553 	do
554 	{
555 		swapchain_aspect_ratio = platform->get_aspect_ratio();
556 		err = init_swapchain(width, height);
557 		if (err == SwapchainError::Error)
558 		{
559 			if (++retry_counter > 3)
560 				return false;
561 
562 			// Try to not reuse the swapchain.
563 			tear_down_swapchain();
564 		}
565 		else if (err == SwapchainError::NoSurface && platform->alive(*this))
566 		{
567 			platform->poll_input();
568 			this_thread::sleep_for(chrono::milliseconds(10));
569 		}
570 	} while (err != SwapchainError::None);
571 
572 	return swapchain != VK_NULL_HANDLE;
573 }
574 
init_swapchain(unsigned width,unsigned height)575 WSI::SwapchainError WSI::init_swapchain(unsigned width, unsigned height)
576 {
577 	if (surface == VK_NULL_HANDLE)
578 	{
579 		LOGE("Cannot create swapchain with surface == VK_NULL_HANDLE.\n");
580 		return SwapchainError::Error;
581 	}
582 
583 	VkSurfaceCapabilitiesKHR surface_properties;
584 	VkPhysicalDeviceSurfaceInfo2KHR surface_info = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR };
585 	surface_info.surface = surface;
586 	bool use_surface_info = device->get_device_features().supports_surface_capabilities2;
587 	bool use_application_controlled_exclusive_fullscreen = false;
588 
589 #ifdef _WIN32
590 	VkSurfaceFullScreenExclusiveInfoEXT exclusive_info = { VK_STRUCTURE_TYPE_SURFACE_FULL_SCREEN_EXCLUSIVE_INFO_EXT };
591 	VkSurfaceFullScreenExclusiveWin32InfoEXT exclusive_info_win32 = { VK_STRUCTURE_TYPE_SURFACE_FULL_SCREEN_EXCLUSIVE_WIN32_INFO_EXT };
592 
593 	HMONITOR monitor = reinterpret_cast<HMONITOR>(platform->get_fullscreen_monitor());
594 	if (!device->get_device_features().supports_full_screen_exclusive)
595 		monitor = nullptr;
596 
597 	surface_info.pNext = &exclusive_info;
598 	if (monitor != nullptr)
599 	{
600 		exclusive_info.pNext = &exclusive_info_win32;
601 		exclusive_info_win32.hmonitor = monitor;
602 		LOGI("Win32: Got a full-screen monitor.\n");
603 	}
604 	else
605 		LOGI("Win32: Not running full-screen.\n");
606 
607 	const char *exclusive = getenv("GRANITE_EXCLUSIVE_FULL_SCREEN");
608 	bool prefer_exclusive = exclusive && strtoul(exclusive, nullptr, 0) != 0;
609 	if (prefer_exclusive)
610 	{
611 		LOGI("Win32: Opting in to exclusive full-screen!\n");
612 		exclusive_info.fullScreenExclusive = VK_FULL_SCREEN_EXCLUSIVE_ALLOWED_EXT;
613 	}
614 	else
615 	{
616 		LOGI("Win32: Opting out of exclusive full-screen!\n");
617 		exclusive_info.fullScreenExclusive = VK_FULL_SCREEN_EXCLUSIVE_DISALLOWED_EXT;
618 	}
619 #endif
620 
621 	auto gpu = context->get_gpu();
622 	if (use_surface_info)
623 	{
624 		VkSurfaceCapabilities2KHR surface_capabilities2 = { VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR };
625 
626 #ifdef _WIN32
627 		VkSurfaceCapabilitiesFullScreenExclusiveEXT capability_full_screen_exclusive = { VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_FULL_SCREEN_EXCLUSIVE_EXT };
628 		if (device->get_device_features().supports_full_screen_exclusive && exclusive_info_win32.hmonitor)
629 		{
630 			surface_capabilities2.pNext = &capability_full_screen_exclusive;
631 			capability_full_screen_exclusive.pNext = &exclusive_info_win32;
632 		}
633 #endif
634 
635 		if (vkGetPhysicalDeviceSurfaceCapabilities2KHR(gpu, &surface_info, &surface_capabilities2) != VK_SUCCESS)
636 			return SwapchainError::Error;
637 
638 		surface_properties = surface_capabilities2.surfaceCapabilities;
639 
640 #ifdef _WIN32
641 		if (capability_full_screen_exclusive.fullScreenExclusiveSupported)
642 			LOGI("Surface could support app-controlled exclusive fullscreen.\n");
643 
644 		use_application_controlled_exclusive_fullscreen = exclusive_info.fullScreenExclusive == VK_FULL_SCREEN_EXCLUSIVE_ALLOWED_EXT &&
645 		                                                  capability_full_screen_exclusive.fullScreenExclusiveSupported == VK_TRUE;
646 		if (monitor == nullptr)
647 			use_application_controlled_exclusive_fullscreen = false;
648 #endif
649 
650 		if (use_application_controlled_exclusive_fullscreen)
651 		{
652 			LOGI("Using app-controlled exclusive fullscreen.\n");
653 #ifdef _WIN32
654 			exclusive_info.fullScreenExclusive = VK_FULL_SCREEN_EXCLUSIVE_APPLICATION_CONTROLLED_EXT;
655 			exclusive_info.pNext = &exclusive_info_win32;
656 #endif
657 		}
658 		else
659 		{
660 			LOGI("Not using app-controlled exclusive fullscreen.\n");
661 		}
662 	}
663 	else
664 	{
665 		if (vkGetPhysicalDeviceSurfaceCapabilitiesKHR(gpu, surface, &surface_properties) != VK_SUCCESS)
666 			return SwapchainError::Error;
667 	}
668 
669 	// Happens on nVidia Windows when you minimize a window.
670 	if (surface_properties.maxImageExtent.width == 0 && surface_properties.maxImageExtent.height == 0)
671 		return SwapchainError::NoSurface;
672 
673 	uint32_t format_count;
674 	vector<VkSurfaceFormatKHR> formats;
675 
676 	if (use_surface_info)
677 	{
678 		if (vkGetPhysicalDeviceSurfaceFormats2KHR(gpu, &surface_info, &format_count, nullptr) != VK_SUCCESS)
679 			return SwapchainError::Error;
680 
681 		vector<VkSurfaceFormat2KHR> formats2(format_count);
682 
683 		for (auto &f : formats2)
684 		{
685 			f = {};
686 			f.sType = VK_STRUCTURE_TYPE_SURFACE_FORMAT_2_KHR;
687 		}
688 
689 		if (vkGetPhysicalDeviceSurfaceFormats2KHR(gpu, &surface_info, &format_count, formats2.data()) != VK_SUCCESS)
690 			return SwapchainError::Error;
691 
692 		formats.reserve(format_count);
693 		for (auto &f : formats2)
694 			formats.push_back(f.surfaceFormat);
695 	}
696 	else
697 	{
698 		if (vkGetPhysicalDeviceSurfaceFormatsKHR(gpu, surface, &format_count, nullptr) != VK_SUCCESS)
699 			return SwapchainError::Error;
700 		formats.resize(format_count);
701 		if (vkGetPhysicalDeviceSurfaceFormatsKHR(gpu, surface, &format_count, formats.data()) != VK_SUCCESS)
702 			return SwapchainError::Error;
703 	}
704 
705 	VkSurfaceFormatKHR format;
706 	if (format_count == 1 && formats[0].format == VK_FORMAT_UNDEFINED)
707 	{
708 		format = formats[0];
709 		format.format = VK_FORMAT_B8G8R8A8_UNORM;
710 	}
711 	else
712 	{
713 		if (format_count == 0)
714 		{
715 			LOGE("Surface has no formats.\n");
716 			return SwapchainError::Error;
717 		}
718 
719 		bool found = false;
720 		for (unsigned i = 0; i < format_count; i++)
721 		{
722 			if (current_srgb_backbuffer_enable)
723 			{
724 				if (formats[i].format == VK_FORMAT_R8G8B8A8_SRGB || formats[i].format == VK_FORMAT_B8G8R8A8_SRGB ||
725 				    formats[i].format == VK_FORMAT_A8B8G8R8_SRGB_PACK32)
726 				{
727 					format = formats[i];
728 					found = true;
729 				}
730 			}
731 			else
732 			{
733 				if (formats[i].format == VK_FORMAT_R8G8B8A8_UNORM || formats[i].format == VK_FORMAT_B8G8R8A8_UNORM ||
734 				    formats[i].format == VK_FORMAT_A8B8G8R8_UNORM_PACK32)
735 				{
736 					format = formats[i];
737 					found = true;
738 				}
739 			}
740 		}
741 
742 		if (!found)
743 			format = formats[0];
744 	}
745 
746 	static const char *transform_names[] = {
747 		"IDENTITY_BIT_KHR",
748 		"ROTATE_90_BIT_KHR",
749 		"ROTATE_180_BIT_KHR",
750 		"ROTATE_270_BIT_KHR",
751 		"HORIZONTAL_MIRROR_BIT_KHR",
752 		"HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR",
753 		"HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR",
754 		"HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR",
755 		"INHERIT_BIT_KHR",
756 	};
757 
758 	LOGI("Current transform is enum 0x%x.\n", unsigned(surface_properties.currentTransform));
759 
760 	for (unsigned i = 0; i <= 8; i++)
761 	{
762 		if (surface_properties.supportedTransforms & (1u << i))
763 			LOGI("Supported transform 0x%x: %s.\n", 1u << i, transform_names[i]);
764 	}
765 
766 	VkSurfaceTransformFlagBitsKHR pre_transform;
767 	if (!support_prerotate && (surface_properties.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) != 0)
768 		pre_transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
769 	else
770 		pre_transform = surface_properties.currentTransform;
771 
772 	if (pre_transform != surface_properties.currentTransform)
773 	{
774 		LOGW("surfaceTransform (0x%x) != currentTransform (0x%u). Might get performance penalty.\n",
775 		     unsigned(pre_transform), unsigned(surface_properties.currentTransform));
776 	}
777 
778 	swapchain_current_prerotate = pre_transform;
779 
780 	VkExtent2D swapchain_size;
781 	LOGI("Swapchain current extent: %d x %d\n",
782 	     int(surface_properties.currentExtent.width),
783 	     int(surface_properties.currentExtent.height));
784 
785 	// Try to match the swapchain size up with what we expect.
786 	float target_aspect_ratio = float(width) / float(height);
787 	if ((swapchain_aspect_ratio > 1.0f && target_aspect_ratio < 1.0f) ||
788 	    (swapchain_aspect_ratio < 1.0f && target_aspect_ratio > 1.0f))
789 	{
790 		swap(width, height);
791 	}
792 
793 	// If we are using pre-rotate of 90 or 270 degrees, we need to flip width and height again.
794 	if (swapchain_current_prerotate &
795 	    (VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR | VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR |
796 	     VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR |
797 	     VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR))
798 	{
799 		swap(width, height);
800 	}
801 
802 	// Clamp the target width, height to boundaries.
803 	swapchain_size.width =
804 	    max(min(width, surface_properties.maxImageExtent.width), surface_properties.minImageExtent.width);
805 	swapchain_size.height =
806 	    max(min(height, surface_properties.maxImageExtent.height), surface_properties.minImageExtent.height);
807 
808 	uint32_t num_present_modes;
809 
810 	vector<VkPresentModeKHR> present_modes;
811 
812 #ifdef _WIN32
813 	if (use_surface_info && device->get_device_features().supports_full_screen_exclusive)
814 	{
815 		if (vkGetPhysicalDeviceSurfacePresentModes2EXT(gpu, &surface_info, &num_present_modes, nullptr) != VK_SUCCESS)
816 			return SwapchainError::Error;
817 		present_modes.resize(num_present_modes);
818 		if (vkGetPhysicalDeviceSurfacePresentModes2EXT(gpu, &surface_info, &num_present_modes, present_modes.data()) !=
819 		    VK_SUCCESS)
820 			return SwapchainError::Error;
821 	}
822 	else
823 #endif
824 	{
825 		if (vkGetPhysicalDeviceSurfacePresentModesKHR(gpu, surface, &num_present_modes, nullptr) != VK_SUCCESS)
826 			return SwapchainError::Error;
827 		present_modes.resize(num_present_modes);
828 		if (vkGetPhysicalDeviceSurfacePresentModesKHR(gpu, surface, &num_present_modes, present_modes.data()) != VK_SUCCESS)
829 			return SwapchainError::Error;
830 	}
831 
832 	VkPresentModeKHR swapchain_present_mode = VK_PRESENT_MODE_FIFO_KHR;
833 	bool use_vsync = current_present_mode == PresentMode::SyncToVBlank;
834 	if (!use_vsync)
835 	{
836 		bool allow_mailbox = current_present_mode != PresentMode::UnlockedForceTearing;
837 		bool allow_immediate = current_present_mode != PresentMode::UnlockedNoTearing;
838 
839 #ifdef _WIN32
840 		if (device->get_gpu_properties().vendorID == VENDOR_ID_NVIDIA)
841 		{
842 			// If we're trying to go exclusive full-screen,
843 			// we need to ban certain types of present modes which apparently do not work as we expect.
844 			if (use_application_controlled_exclusive_fullscreen)
845 				allow_mailbox = false;
846 			else
847 				allow_immediate = false;
848 		}
849 #endif
850 
851 		for (uint32_t i = 0; i < num_present_modes; i++)
852 		{
853 			if ((allow_immediate && present_modes[i] == VK_PRESENT_MODE_IMMEDIATE_KHR) ||
854 			    (allow_mailbox && present_modes[i] == VK_PRESENT_MODE_MAILBOX_KHR))
855 			{
856 				swapchain_present_mode = present_modes[i];
857 				break;
858 			}
859 		}
860 	}
861 
862 	uint32_t desired_swapchain_images = 3;
863 	{
864 		const char *num_images = getenv("GRANITE_VULKAN_SWAPCHAIN_IMAGES");
865 		if (num_images)
866 			desired_swapchain_images = uint32_t(strtoul(num_images, nullptr, 0));
867 	}
868 
869 	LOGI("Targeting %u swapchain images.\n", desired_swapchain_images);
870 
871 	if (desired_swapchain_images < surface_properties.minImageCount)
872 		desired_swapchain_images = surface_properties.minImageCount;
873 
874 	if ((surface_properties.maxImageCount > 0) && (desired_swapchain_images > surface_properties.maxImageCount))
875 		desired_swapchain_images = surface_properties.maxImageCount;
876 
877 	VkCompositeAlphaFlagBitsKHR composite_mode = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
878 	if (surface_properties.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR)
879 		composite_mode = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR;
880 	if (surface_properties.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR)
881 		composite_mode = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
882 	if (surface_properties.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR)
883 		composite_mode = VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR;
884 	if (surface_properties.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR)
885 		composite_mode = VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR;
886 
887 	VkSwapchainKHR old_swapchain = swapchain;
888 
889 	VkSwapchainCreateInfoKHR info = { VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR };
890 	info.surface = surface;
891 	info.minImageCount = desired_swapchain_images;
892 	info.imageFormat = format.format;
893 	info.imageColorSpace = format.colorSpace;
894 	info.imageExtent.width = swapchain_size.width;
895 	info.imageExtent.height = swapchain_size.height;
896 	info.imageArrayLayers = 1;
897 	info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
898 	info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
899 	info.preTransform = pre_transform;
900 	info.compositeAlpha = composite_mode;
901 	info.presentMode = swapchain_present_mode;
902 	info.clipped = VK_TRUE;
903 	info.oldSwapchain = old_swapchain;
904 
905 #ifdef _WIN32
906 	if (device->get_device_features().supports_full_screen_exclusive)
907 		info.pNext = &exclusive_info;
908 #endif
909 
910 	auto res = table->vkCreateSwapchainKHR(context->get_device(), &info, nullptr, &swapchain);
911 	if (old_swapchain != VK_NULL_HANDLE)
912 		table->vkDestroySwapchainKHR(context->get_device(), old_swapchain, nullptr);
913 	has_acquired_swapchain_index = false;
914 
915 #ifdef _WIN32
916 	if (use_application_controlled_exclusive_fullscreen)
917 	{
918 		bool success = vkAcquireFullScreenExclusiveModeEXT(context->get_device(), swapchain) == VK_SUCCESS;
919 		if (success)
920 			LOGI("Successfully acquired exclusive full-screen.\n");
921 		else
922 			LOGI("Failed to acquire exclusive full-screen. Using borderless windowed.\n");
923 	}
924 #endif
925 
926 #if 0
927 	if (use_vsync && context->get_enabled_device_features().supports_google_display_timing)
928 	{
929 		WSITimingOptions timing_options;
930 		timing_options.swap_interval = 1;
931 		//timing_options.adaptive_swap_interval = true;
932 		//timing_options.latency_limiter = LatencyLimiter::IdealPipeline;
933 		timing.init(platform, device.get(), swapchain, timing_options);
934 		using_display_timing = true;
935 	}
936 	else
937 #endif
938 	using_display_timing = false;
939 
940 	if (res != VK_SUCCESS)
941 	{
942 		LOGE("Failed to create swapchain (code: %d)\n", int(res));
943 		swapchain = VK_NULL_HANDLE;
944 		return SwapchainError::Error;
945 	}
946 
947 	swapchain_width = swapchain_size.width;
948 	swapchain_height = swapchain_size.height;
949 	swapchain_format = format.format;
950 
951 	LOGI("Created swapchain %u x %u (fmt: %u).\n", swapchain_width, swapchain_height,
952 	     static_cast<unsigned>(swapchain_format));
953 
954 	uint32_t image_count;
955 	if (table->vkGetSwapchainImagesKHR(context->get_device(), swapchain, &image_count, nullptr) != VK_SUCCESS)
956 		return SwapchainError::Error;
957 	swapchain_images.resize(image_count);
958 	release_semaphores.resize(image_count);
959 	if (table->vkGetSwapchainImagesKHR(context->get_device(), swapchain, &image_count, swapchain_images.data()) != VK_SUCCESS)
960 		return SwapchainError::Error;
961 
962 	LOGI("Got %u swapchain images.\n", image_count);
963 
964 	platform->event_swapchain_destroyed();
965 	platform->event_swapchain_created(device.get(), swapchain_width, swapchain_height,
966 	                                  swapchain_aspect_ratio, image_count, info.imageFormat, swapchain_current_prerotate);
967 
968 	return SwapchainError::None;
969 }
970 
get_estimated_refresh_interval() const971 double WSI::get_estimated_refresh_interval() const
972 {
973 	uint64_t interval = timing.get_refresh_interval();
974 	if (interval)
975 		return interval * 1e-9;
976 	else if (platform)
977 		return platform->get_estimated_frame_presentation_duration();
978 	else
979 		return 0.0;
980 }
981 
set_support_prerotate(bool enable)982 void WSI::set_support_prerotate(bool enable)
983 {
984 	support_prerotate = enable;
985 }
986 
get_current_prerotate() const987 VkSurfaceTransformFlagBitsKHR WSI::get_current_prerotate() const
988 {
989 	return swapchain_current_prerotate;
990 }
991 
~WSI()992 WSI::~WSI()
993 {
994 	deinit_external();
995 }
996 
build_prerotate_matrix_2x2(VkSurfaceTransformFlagBitsKHR pre_rotate,float mat[4])997 void WSI::build_prerotate_matrix_2x2(VkSurfaceTransformFlagBitsKHR pre_rotate, float mat[4])
998 {
999 	// TODO: HORIZONTAL_MIRROR.
1000 	switch (pre_rotate)
1001 	{
1002 	default:
1003 		mat[0] = 1.0f;
1004 		mat[1] = 0.0f;
1005 		mat[2] = 0.0f;
1006 		mat[3] = 1.0f;
1007 		break;
1008 
1009 	case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR:
1010 		mat[0] = 0.0f;
1011 		mat[1] = 1.0f;
1012 		mat[2] = -1.0f;
1013 		mat[3] = 0.0f;
1014 		break;
1015 
1016 	case VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR:
1017 		mat[0] = 0.0f;
1018 		mat[1] = -1.0f;
1019 		mat[2] = 1.0f;
1020 		mat[3] = 0.0f;
1021 		break;
1022 
1023 	case VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR:
1024 		mat[0] = -1.0f;
1025 		mat[1] = 0.0f;
1026 		mat[2] = 0.0f;
1027 		mat[3] = -1.0f;
1028 		break;
1029 	}
1030 }
1031 
event_device_created(Device *)1032 void WSIPlatform::event_device_created(Device *) {}
event_device_destroyed()1033 void WSIPlatform::event_device_destroyed() {}
event_swapchain_created(Device *,unsigned,unsigned,float,size_t,VkFormat,VkSurfaceTransformFlagBitsKHR)1034 void WSIPlatform::event_swapchain_created(Device *, unsigned, unsigned, float, size_t, VkFormat, VkSurfaceTransformFlagBitsKHR) {}
event_swapchain_destroyed()1035 void WSIPlatform::event_swapchain_destroyed() {}
event_frame_tick(double,double)1036 void WSIPlatform::event_frame_tick(double, double) {}
event_swapchain_index(Device *,unsigned)1037 void WSIPlatform::event_swapchain_index(Device *, unsigned) {}
event_display_timing_stutter(uint32_t,uint32_t,uint32_t)1038 void WSIPlatform::event_display_timing_stutter(uint32_t, uint32_t, uint32_t) {}
1039 
1040 }
1041