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