Merge pull request #8372 from stenzek/vulkan-exclusive-fullscreen
Vulkan: Exclusive fullscreen support via VK_EXT_full_screen_exclusive
This commit is contained in:
commit
c6841a027a
|
@ -371,13 +371,16 @@ void CommandBufferManager::SubmitCommandBuffer(u32 command_buffer_index,
|
||||||
&present_image_index,
|
&present_image_index,
|
||||||
nullptr};
|
nullptr};
|
||||||
|
|
||||||
res = vkQueuePresentKHR(g_vulkan_context->GetPresentQueue(), &present_info);
|
m_last_present_result = vkQueuePresentKHR(g_vulkan_context->GetPresentQueue(), &present_info);
|
||||||
if (res != VK_SUCCESS)
|
if (m_last_present_result != VK_SUCCESS)
|
||||||
{
|
{
|
||||||
// VK_ERROR_OUT_OF_DATE_KHR is not fatal, just means we need to recreate our swap chain.
|
// VK_ERROR_OUT_OF_DATE_KHR is not fatal, just means we need to recreate our swap chain.
|
||||||
if (res != VK_ERROR_OUT_OF_DATE_KHR && res != VK_SUBOPTIMAL_KHR)
|
if (m_last_present_result != VK_ERROR_OUT_OF_DATE_KHR && res != VK_SUBOPTIMAL_KHR &&
|
||||||
LOG_VULKAN_ERROR(res, "vkQueuePresentKHR failed: ");
|
m_last_present_result != VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT)
|
||||||
m_present_failed_flag.Set();
|
{
|
||||||
|
LOG_VULKAN_ERROR(m_last_present_result, "vkQueuePresentKHR failed: ");
|
||||||
|
}
|
||||||
|
m_last_present_failed.Set();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -79,7 +79,8 @@ public:
|
||||||
uint32_t present_image_index = 0xFFFFFFFF);
|
uint32_t present_image_index = 0xFFFFFFFF);
|
||||||
|
|
||||||
// Was the last present submitted to the queue a failure? If so, we must recreate our swapchain.
|
// Was the last present submitted to the queue a failure? If so, we must recreate our swapchain.
|
||||||
bool CheckLastPresentFail() { return m_present_failed_flag.TestAndClear(); }
|
bool CheckLastPresentFail() { return m_last_present_failed.TestAndClear(); }
|
||||||
|
VkResult GetLastPresentResult() const { return m_last_present_result; }
|
||||||
|
|
||||||
// Schedule a vulkan resource for destruction later on. This will occur when the command buffer
|
// Schedule a vulkan resource for destruction later on. This will occur when the command buffer
|
||||||
// is next re-used, and the GPU has finished working with the specified resource.
|
// is next re-used, and the GPU has finished working with the specified resource.
|
||||||
|
@ -136,7 +137,8 @@ private:
|
||||||
VkSemaphore m_present_semaphore = VK_NULL_HANDLE;
|
VkSemaphore m_present_semaphore = VK_NULL_HANDLE;
|
||||||
std::deque<PendingCommandBufferSubmit> m_pending_submits;
|
std::deque<PendingCommandBufferSubmit> m_pending_submits;
|
||||||
std::mutex m_pending_submit_lock;
|
std::mutex m_pending_submit_lock;
|
||||||
Common::Flag m_present_failed_flag;
|
Common::Flag m_last_present_failed;
|
||||||
|
VkResult m_last_present_result = VK_SUCCESS;
|
||||||
bool m_use_threaded_submission = false;
|
bool m_use_threaded_submission = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -280,13 +280,39 @@ void Renderer::BindBackbuffer(const ClearColor& clear_color)
|
||||||
CheckForSurfaceChange();
|
CheckForSurfaceChange();
|
||||||
CheckForSurfaceResize();
|
CheckForSurfaceResize();
|
||||||
|
|
||||||
VkResult res = g_command_buffer_mgr->CheckLastPresentFail() ? VK_ERROR_OUT_OF_DATE_KHR :
|
// Check for exclusive fullscreen request.
|
||||||
|
if (m_swap_chain->GetCurrentFullscreenState() != m_swap_chain->GetNextFullscreenState() &&
|
||||||
|
!m_swap_chain->SetFullscreenState(m_swap_chain->GetNextFullscreenState()))
|
||||||
|
{
|
||||||
|
// if it fails, don't keep trying
|
||||||
|
m_swap_chain->SetNextFullscreenState(m_swap_chain->GetCurrentFullscreenState());
|
||||||
|
}
|
||||||
|
|
||||||
|
VkResult res = g_command_buffer_mgr->CheckLastPresentFail() ?
|
||||||
|
g_command_buffer_mgr->GetLastPresentResult() :
|
||||||
m_swap_chain->AcquireNextImage();
|
m_swap_chain->AcquireNextImage();
|
||||||
if (res == VK_SUBOPTIMAL_KHR || res == VK_ERROR_OUT_OF_DATE_KHR)
|
if (res != VK_SUCCESS)
|
||||||
{
|
{
|
||||||
// Execute cmdbuffer before resizing, as the last frame could still be presenting.
|
// Execute cmdbuffer before resizing, as the last frame could still be presenting.
|
||||||
ExecuteCommandBuffer(false, true);
|
ExecuteCommandBuffer(false, true);
|
||||||
|
|
||||||
|
// Was this a lost exclusive fullscreen?
|
||||||
|
if (res == VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT)
|
||||||
|
{
|
||||||
|
// The present keeps returning exclusive mode lost unless we re-create the swap chain.
|
||||||
|
INFO_LOG(VIDEO, "Lost exclusive fullscreen.");
|
||||||
|
m_swap_chain->RecreateSwapChain();
|
||||||
|
}
|
||||||
|
else if (res == VK_SUBOPTIMAL_KHR || res == VK_ERROR_OUT_OF_DATE_KHR)
|
||||||
|
{
|
||||||
m_swap_chain->ResizeSwapChain();
|
m_swap_chain->ResizeSwapChain();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ERROR_LOG(VIDEO, "Unknown present error 0x%08X, please report.", res);
|
||||||
|
m_swap_chain->RecreateSwapChain();
|
||||||
|
}
|
||||||
|
|
||||||
res = m_swap_chain->AcquireNextImage();
|
res = m_swap_chain->AcquireNextImage();
|
||||||
}
|
}
|
||||||
if (res != VK_SUCCESS)
|
if (res != VK_SUCCESS)
|
||||||
|
@ -323,6 +349,19 @@ void Renderer::PresentBackbuffer()
|
||||||
StateTracker::GetInstance()->InvalidateCachedState();
|
StateTracker::GetInstance()->InvalidateCachedState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Renderer::SetFullscreen(bool enable_fullscreen)
|
||||||
|
{
|
||||||
|
if (!m_swap_chain->IsFullscreenSupported())
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_swap_chain->SetNextFullscreenState(enable_fullscreen);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Renderer::IsFullscreen() const
|
||||||
|
{
|
||||||
|
return m_swap_chain && m_swap_chain->GetCurrentFullscreenState();
|
||||||
|
}
|
||||||
|
|
||||||
void Renderer::ExecuteCommandBuffer(bool submit_off_thread, bool wait_for_completion)
|
void Renderer::ExecuteCommandBuffer(bool submit_off_thread, bool wait_for_completion)
|
||||||
{
|
{
|
||||||
StateTracker::GetInstance()->EndRenderPass();
|
StateTracker::GetInstance()->EndRenderPass();
|
||||||
|
|
|
@ -83,6 +83,8 @@ public:
|
||||||
u32 groups_z) override;
|
u32 groups_z) override;
|
||||||
void BindBackbuffer(const ClearColor& clear_color = {}) override;
|
void BindBackbuffer(const ClearColor& clear_color = {}) override;
|
||||||
void PresentBackbuffer() override;
|
void PresentBackbuffer() override;
|
||||||
|
void SetFullscreen(bool enable_fullscreen) override;
|
||||||
|
bool IsFullscreen() const override;
|
||||||
|
|
||||||
// Completes the current render pass, executes the command buffer, and restores state ready for
|
// Completes the current render pass, executes the command buffer, and restores state ready for
|
||||||
// next render. Use when you want to kick the current buffer to make room for new data.
|
// next render. Use when you want to kick the current buffer to make room for new data.
|
||||||
|
|
|
@ -25,7 +25,8 @@
|
||||||
namespace Vulkan
|
namespace Vulkan
|
||||||
{
|
{
|
||||||
SwapChain::SwapChain(const WindowSystemInfo& wsi, VkSurfaceKHR surface, bool vsync)
|
SwapChain::SwapChain(const WindowSystemInfo& wsi, VkSurfaceKHR surface, bool vsync)
|
||||||
: m_wsi(wsi), m_surface(surface), m_vsync_enabled(vsync)
|
: m_wsi(wsi), m_surface(surface), m_vsync_enabled(vsync),
|
||||||
|
m_fullscreen_supported(g_vulkan_context->SupportsExclusiveFullscreen(wsi, surface))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -290,6 +291,7 @@ bool SwapChain::CreateSwapChain()
|
||||||
|
|
||||||
// Store the old/current swap chain when recreating for resize
|
// Store the old/current swap chain when recreating for resize
|
||||||
VkSwapchainKHR old_swap_chain = m_swap_chain;
|
VkSwapchainKHR old_swap_chain = m_swap_chain;
|
||||||
|
m_swap_chain = VK_NULL_HANDLE;
|
||||||
|
|
||||||
// Now we can actually create the swap chain
|
// Now we can actually create the swap chain
|
||||||
VkSwapchainCreateInfoKHR swap_chain_info = {VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
|
VkSwapchainCreateInfoKHR swap_chain_info = {VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
|
||||||
|
@ -322,8 +324,36 @@ bool SwapChain::CreateSwapChain()
|
||||||
swap_chain_info.pQueueFamilyIndices = indices.data();
|
swap_chain_info.pQueueFamilyIndices = indices.data();
|
||||||
}
|
}
|
||||||
|
|
||||||
res =
|
#ifdef SUPPORTS_VULKAN_EXCLUSIVE_FULLSCREEN
|
||||||
vkCreateSwapchainKHR(g_vulkan_context->GetDevice(), &swap_chain_info, nullptr, &m_swap_chain);
|
if (m_fullscreen_supported)
|
||||||
|
{
|
||||||
|
VkSurfaceFullScreenExclusiveInfoEXT fullscreen_support = {};
|
||||||
|
swap_chain_info.pNext = &fullscreen_support;
|
||||||
|
fullscreen_support.sType = VK_STRUCTURE_TYPE_SURFACE_FULL_SCREEN_EXCLUSIVE_INFO_EXT;
|
||||||
|
fullscreen_support.fullScreenExclusive = VK_FULL_SCREEN_EXCLUSIVE_APPLICATION_CONTROLLED_EXT;
|
||||||
|
|
||||||
|
auto platform_info = g_vulkan_context->GetPlatformExclusiveFullscreenInfo(m_wsi);
|
||||||
|
fullscreen_support.pNext = &platform_info;
|
||||||
|
|
||||||
|
res = vkCreateSwapchainKHR(g_vulkan_context->GetDevice(), &swap_chain_info, nullptr,
|
||||||
|
&m_swap_chain);
|
||||||
|
if (res != VK_SUCCESS)
|
||||||
|
{
|
||||||
|
// Try without exclusive fullscreen.
|
||||||
|
WARN_LOG(VIDEO, "Failed to create exclusive fullscreen swapchain, trying without.");
|
||||||
|
swap_chain_info.pNext = nullptr;
|
||||||
|
g_Config.backend_info.bSupportsExclusiveFullscreen = false;
|
||||||
|
g_ActiveConfig.backend_info.bSupportsExclusiveFullscreen = false;
|
||||||
|
m_fullscreen_supported = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (m_swap_chain == VK_NULL_HANDLE)
|
||||||
|
{
|
||||||
|
res = vkCreateSwapchainKHR(g_vulkan_context->GetDevice(), &swap_chain_info, nullptr,
|
||||||
|
&m_swap_chain);
|
||||||
|
}
|
||||||
if (res != VK_SUCCESS)
|
if (res != VK_SUCCESS)
|
||||||
{
|
{
|
||||||
LOG_VULKAN_ERROR(res, "vkCreateSwapchainKHR failed: ");
|
LOG_VULKAN_ERROR(res, "vkCreateSwapchainKHR failed: ");
|
||||||
|
@ -414,6 +444,10 @@ void SwapChain::DestroySwapChain()
|
||||||
if (m_swap_chain == VK_NULL_HANDLE)
|
if (m_swap_chain == VK_NULL_HANDLE)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// Release exclusive fullscreen before destroying.
|
||||||
|
if (m_current_fullscreen_state)
|
||||||
|
SetFullscreenState(false);
|
||||||
|
|
||||||
vkDestroySwapchainKHR(g_vulkan_context->GetDevice(), m_swap_chain, nullptr);
|
vkDestroySwapchainKHR(g_vulkan_context->GetDevice(), m_swap_chain, nullptr);
|
||||||
m_swap_chain = VK_NULL_HANDLE;
|
m_swap_chain = VK_NULL_HANDLE;
|
||||||
}
|
}
|
||||||
|
@ -464,6 +498,39 @@ bool SwapChain::SetVSync(bool enabled)
|
||||||
return RecreateSwapChain();
|
return RecreateSwapChain();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SwapChain::SetFullscreenState(bool state)
|
||||||
|
{
|
||||||
|
#ifdef SUPPORTS_VULKAN_EXCLUSIVE_FULLSCREEN
|
||||||
|
if (m_current_fullscreen_state == state)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (state)
|
||||||
|
{
|
||||||
|
VkResult res = vkAcquireFullScreenExclusiveModeEXT(g_vulkan_context->GetDevice(), m_swap_chain);
|
||||||
|
if (res != VK_SUCCESS)
|
||||||
|
{
|
||||||
|
LOG_VULKAN_ERROR(res, "vkAcquireFullScreenExclusiveModeEXT failed:");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
INFO_LOG(VIDEO, "Exclusive fullscreen acquired.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
VkResult res = vkReleaseFullScreenExclusiveModeEXT(g_vulkan_context->GetDevice(), m_swap_chain);
|
||||||
|
if (res != VK_SUCCESS)
|
||||||
|
LOG_VULKAN_ERROR(res, "vkReleaseFullScreenExclusiveModeEXT failed:");
|
||||||
|
|
||||||
|
INFO_LOG(VIDEO, "Exclusive fullscreen released.");
|
||||||
|
}
|
||||||
|
|
||||||
|
m_current_fullscreen_state = state;
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
bool SwapChain::RecreateSurface(void* native_handle)
|
bool SwapChain::RecreateSurface(void* native_handle)
|
||||||
{
|
{
|
||||||
// Destroy the old swap chain, images, and surface.
|
// Destroy the old swap chain, images, and surface.
|
||||||
|
@ -493,6 +560,13 @@ bool SwapChain::RecreateSurface(void* native_handle)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update exclusive fullscreen support (unlikely to change).
|
||||||
|
m_fullscreen_supported = g_vulkan_context->SupportsExclusiveFullscreen(m_wsi, m_surface);
|
||||||
|
g_Config.backend_info.bSupportsExclusiveFullscreen = m_fullscreen_supported;
|
||||||
|
g_ActiveConfig.backend_info.bSupportsExclusiveFullscreen = m_fullscreen_supported;
|
||||||
|
m_current_fullscreen_state = false;
|
||||||
|
m_next_fullscreen_state = false;
|
||||||
|
|
||||||
// Finally re-create the swap chain
|
// Finally re-create the swap chain
|
||||||
if (!CreateSwapChain() || !SetupSwapChainImages())
|
if (!CreateSwapChain() || !SetupSwapChainImages())
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -62,6 +62,17 @@ public:
|
||||||
// Change vsync enabled state. This may fail as it causes a swapchain recreation.
|
// Change vsync enabled state. This may fail as it causes a swapchain recreation.
|
||||||
bool SetVSync(bool enabled);
|
bool SetVSync(bool enabled);
|
||||||
|
|
||||||
|
// Is exclusive fullscreen supported?
|
||||||
|
bool IsFullscreenSupported() const { return m_fullscreen_supported; }
|
||||||
|
|
||||||
|
// Retrieves the "next" fullscreen state. Safe to call off-thread.
|
||||||
|
bool GetCurrentFullscreenState() const { return m_current_fullscreen_state; }
|
||||||
|
bool GetNextFullscreenState() const { return m_next_fullscreen_state; }
|
||||||
|
void SetNextFullscreenState(bool state) { m_next_fullscreen_state = state; }
|
||||||
|
|
||||||
|
// Updates the fullscreen state. Must call on-thread.
|
||||||
|
bool SetFullscreenState(bool state);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool SelectSurfaceFormat();
|
bool SelectSurfaceFormat();
|
||||||
bool SelectPresentMode();
|
bool SelectPresentMode();
|
||||||
|
@ -86,7 +97,10 @@ private:
|
||||||
VkSurfaceFormatKHR m_surface_format = {};
|
VkSurfaceFormatKHR m_surface_format = {};
|
||||||
VkPresentModeKHR m_present_mode = VK_PRESENT_MODE_RANGE_SIZE_KHR;
|
VkPresentModeKHR m_present_mode = VK_PRESENT_MODE_RANGE_SIZE_KHR;
|
||||||
AbstractTextureFormat m_texture_format = AbstractTextureFormat::Undefined;
|
AbstractTextureFormat m_texture_format = AbstractTextureFormat::Undefined;
|
||||||
bool m_vsync_enabled;
|
bool m_vsync_enabled = false;
|
||||||
|
bool m_fullscreen_supported = false;
|
||||||
|
bool m_current_fullscreen_state = false;
|
||||||
|
bool m_next_fullscreen_state = false;
|
||||||
|
|
||||||
VkSwapchainKHR m_swap_chain = VK_NULL_HANDLE;
|
VkSwapchainKHR m_swap_chain = VK_NULL_HANDLE;
|
||||||
std::vector<SwapChainImage> m_swap_chain_images;
|
std::vector<SwapChainImage> m_swap_chain_images;
|
||||||
|
|
|
@ -88,7 +88,7 @@ bool VulkanContext::CheckValidationLayerAvailablility()
|
||||||
VkInstance VulkanContext::CreateVulkanInstance(WindowSystemType wstype, bool enable_debug_report,
|
VkInstance VulkanContext::CreateVulkanInstance(WindowSystemType wstype, bool enable_debug_report,
|
||||||
bool enable_validation_layer)
|
bool enable_validation_layer)
|
||||||
{
|
{
|
||||||
ExtensionList enabled_extensions;
|
std::vector<const char*> enabled_extensions;
|
||||||
if (!SelectInstanceExtensions(&enabled_extensions, wstype, enable_debug_report))
|
if (!SelectInstanceExtensions(&enabled_extensions, wstype, enable_debug_report))
|
||||||
return VK_NULL_HANDLE;
|
return VK_NULL_HANDLE;
|
||||||
|
|
||||||
|
@ -143,8 +143,8 @@ VkInstance VulkanContext::CreateVulkanInstance(WindowSystemType wstype, bool ena
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VulkanContext::SelectInstanceExtensions(ExtensionList* extension_list, WindowSystemType wstype,
|
bool VulkanContext::SelectInstanceExtensions(std::vector<const char*>* extension_list,
|
||||||
bool enable_debug_report)
|
WindowSystemType wstype, bool enable_debug_report)
|
||||||
{
|
{
|
||||||
u32 extension_count = 0;
|
u32 extension_count = 0;
|
||||||
VkResult res = vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, nullptr);
|
VkResult res = vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, nullptr);
|
||||||
|
@ -168,7 +168,7 @@ bool VulkanContext::SelectInstanceExtensions(ExtensionList* extension_list, Wind
|
||||||
for (const auto& extension_properties : available_extension_list)
|
for (const auto& extension_properties : available_extension_list)
|
||||||
INFO_LOG(VIDEO, "Available extension: %s", extension_properties.extensionName);
|
INFO_LOG(VIDEO, "Available extension: %s", extension_properties.extensionName);
|
||||||
|
|
||||||
auto SupportsExtension = [&](const char* name, bool required) {
|
auto AddExtension = [&](const char* name, bool required) {
|
||||||
if (std::find_if(available_extension_list.begin(), available_extension_list.end(),
|
if (std::find_if(available_extension_list.begin(), available_extension_list.end(),
|
||||||
[&](const VkExtensionProperties& properties) {
|
[&](const VkExtensionProperties& properties) {
|
||||||
return !strcmp(name, properties.extensionName);
|
return !strcmp(name, properties.extensionName);
|
||||||
|
@ -186,45 +186,45 @@ bool VulkanContext::SelectInstanceExtensions(ExtensionList* extension_list, Wind
|
||||||
};
|
};
|
||||||
|
|
||||||
// Common extensions
|
// Common extensions
|
||||||
if (wstype != WindowSystemType::Headless &&
|
if (wstype != WindowSystemType::Headless && !AddExtension(VK_KHR_SURFACE_EXTENSION_NAME, true))
|
||||||
!SupportsExtension(VK_KHR_SURFACE_EXTENSION_NAME, true))
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(VK_USE_PLATFORM_WIN32_KHR)
|
#if defined(VK_USE_PLATFORM_WIN32_KHR)
|
||||||
if (wstype == WindowSystemType::Windows &&
|
if (wstype == WindowSystemType::Windows &&
|
||||||
!SupportsExtension(VK_KHR_WIN32_SURFACE_EXTENSION_NAME, true))
|
!AddExtension(VK_KHR_WIN32_SURFACE_EXTENSION_NAME, true))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#if defined(VK_USE_PLATFORM_XLIB_KHR)
|
#if defined(VK_USE_PLATFORM_XLIB_KHR)
|
||||||
if (wstype == WindowSystemType::X11 &&
|
if (wstype == WindowSystemType::X11 && !AddExtension(VK_KHR_XLIB_SURFACE_EXTENSION_NAME, true))
|
||||||
!SupportsExtension(VK_KHR_XLIB_SURFACE_EXTENSION_NAME, true))
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#if defined(VK_USE_PLATFORM_ANDROID_KHR)
|
#if defined(VK_USE_PLATFORM_ANDROID_KHR)
|
||||||
if (wstype == WindowSystemType::Android &&
|
if (wstype == WindowSystemType::Android &&
|
||||||
!SupportsExtension(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME, true))
|
!AddExtension(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME, true))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#if defined(VK_USE_PLATFORM_MACOS_MVK)
|
#if defined(VK_USE_PLATFORM_MACOS_MVK)
|
||||||
if (wstype == WindowSystemType::MacOS &&
|
if (wstype == WindowSystemType::MacOS && !AddExtension(VK_MVK_MACOS_SURFACE_EXTENSION_NAME, true))
|
||||||
!SupportsExtension(VK_MVK_MACOS_SURFACE_EXTENSION_NAME, true))
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// VK_EXT_debug_report
|
// VK_EXT_debug_report
|
||||||
if (enable_debug_report && !SupportsExtension(VK_EXT_DEBUG_REPORT_EXTENSION_NAME, false))
|
if (enable_debug_report && !AddExtension(VK_EXT_DEBUG_REPORT_EXTENSION_NAME, false))
|
||||||
WARN_LOG(VIDEO, "Vulkan: Debug report requested, but extension is not available.");
|
WARN_LOG(VIDEO, "Vulkan: Debug report requested, but extension is not available.");
|
||||||
|
|
||||||
|
AddExtension(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, false);
|
||||||
|
AddExtension(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME, false);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -254,7 +254,6 @@ VulkanContext::GPUList VulkanContext::EnumerateGPUs(VkInstance instance)
|
||||||
void VulkanContext::PopulateBackendInfo(VideoConfig* config)
|
void VulkanContext::PopulateBackendInfo(VideoConfig* config)
|
||||||
{
|
{
|
||||||
config->backend_info.api_type = APIType::Vulkan;
|
config->backend_info.api_type = APIType::Vulkan;
|
||||||
config->backend_info.bSupportsExclusiveFullscreen = false; // Currently WSI does not allow this.
|
|
||||||
config->backend_info.bSupports3DVision = false; // D3D-exclusive.
|
config->backend_info.bSupports3DVision = false; // D3D-exclusive.
|
||||||
config->backend_info.bSupportsOversizedViewports = true; // Assumed support.
|
config->backend_info.bSupportsOversizedViewports = true; // Assumed support.
|
||||||
config->backend_info.bSupportsEarlyZ = true; // Assumed support.
|
config->backend_info.bSupportsEarlyZ = true; // Assumed support.
|
||||||
|
@ -274,6 +273,7 @@ void VulkanContext::PopulateBackendInfo(VideoConfig* config)
|
||||||
config->backend_info.bSupportsBackgroundCompiling = true; // Assumed support.
|
config->backend_info.bSupportsBackgroundCompiling = true; // Assumed support.
|
||||||
config->backend_info.bSupportsCopyToVram = true; // Assumed support.
|
config->backend_info.bSupportsCopyToVram = true; // Assumed support.
|
||||||
config->backend_info.bSupportsReversedDepthRange = true; // Assumed support.
|
config->backend_info.bSupportsReversedDepthRange = true; // Assumed support.
|
||||||
|
config->backend_info.bSupportsExclusiveFullscreen = false; // Dependent on OS and features.
|
||||||
config->backend_info.bSupportsDualSourceBlend = false; // Dependent on features.
|
config->backend_info.bSupportsDualSourceBlend = false; // Dependent on features.
|
||||||
config->backend_info.bSupportsGeometryShaders = false; // Dependent on features.
|
config->backend_info.bSupportsGeometryShaders = false; // Dependent on features.
|
||||||
config->backend_info.bSupportsGSInstancing = false; // Dependent on features.
|
config->backend_info.bSupportsGSInstancing = false; // Dependent on features.
|
||||||
|
@ -424,7 +424,7 @@ std::unique_ptr<VulkanContext> VulkanContext::Create(VkInstance instance, VkPhys
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VulkanContext::SelectDeviceExtensions(ExtensionList* extension_list, bool enable_surface)
|
bool VulkanContext::SelectDeviceExtensions(bool enable_surface)
|
||||||
{
|
{
|
||||||
u32 extension_count = 0;
|
u32 extension_count = 0;
|
||||||
VkResult res =
|
VkResult res =
|
||||||
|
@ -449,14 +449,14 @@ bool VulkanContext::SelectDeviceExtensions(ExtensionList* extension_list, bool e
|
||||||
for (const auto& extension_properties : available_extension_list)
|
for (const auto& extension_properties : available_extension_list)
|
||||||
INFO_LOG(VIDEO, "Available extension: %s", extension_properties.extensionName);
|
INFO_LOG(VIDEO, "Available extension: %s", extension_properties.extensionName);
|
||||||
|
|
||||||
auto SupportsExtension = [&](const char* name, bool required) {
|
auto AddExtension = [&](const char* name, bool required) {
|
||||||
if (std::find_if(available_extension_list.begin(), available_extension_list.end(),
|
if (std::find_if(available_extension_list.begin(), available_extension_list.end(),
|
||||||
[&](const VkExtensionProperties& properties) {
|
[&](const VkExtensionProperties& properties) {
|
||||||
return !strcmp(name, properties.extensionName);
|
return !strcmp(name, properties.extensionName);
|
||||||
}) != available_extension_list.end())
|
}) != available_extension_list.end())
|
||||||
{
|
{
|
||||||
INFO_LOG(VIDEO, "Enabling extension: %s", name);
|
INFO_LOG(VIDEO, "Enabling extension: %s", name);
|
||||||
extension_list->push_back(name);
|
m_device_extensions.push_back(name);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -466,9 +466,15 @@ bool VulkanContext::SelectDeviceExtensions(ExtensionList* extension_list, bool e
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (enable_surface && !SupportsExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME, true))
|
if (enable_surface && !AddExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME, true))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
#ifdef SUPPORTS_VULKAN_EXCLUSIVE_FULLSCREEN
|
||||||
|
// VK_EXT_full_screen_exclusive
|
||||||
|
if (AddExtension(VK_EXT_FULL_SCREEN_EXCLUSIVE_EXTENSION_NAME, true))
|
||||||
|
INFO_LOG(VIDEO, "Using VK_EXT_full_screen_exclusive for exclusive fullscreen.");
|
||||||
|
#endif
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -606,14 +612,18 @@ bool VulkanContext::CreateDevice(VkSurfaceKHR surface, bool enable_validation_la
|
||||||
}
|
}
|
||||||
device_info.pQueueCreateInfos = queue_infos.data();
|
device_info.pQueueCreateInfos = queue_infos.data();
|
||||||
|
|
||||||
ExtensionList enabled_extensions;
|
if (!SelectDeviceExtensions(surface != VK_NULL_HANDLE))
|
||||||
if (!SelectDeviceExtensions(&enabled_extensions, surface != VK_NULL_HANDLE))
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
// convert std::string list to a char pointer list which we can feed in
|
||||||
|
std::vector<const char*> extension_name_pointers;
|
||||||
|
for (const std::string& name : m_device_extensions)
|
||||||
|
extension_name_pointers.push_back(name.c_str());
|
||||||
|
|
||||||
device_info.enabledLayerCount = 0;
|
device_info.enabledLayerCount = 0;
|
||||||
device_info.ppEnabledLayerNames = nullptr;
|
device_info.ppEnabledLayerNames = nullptr;
|
||||||
device_info.enabledExtensionCount = static_cast<uint32_t>(enabled_extensions.size());
|
device_info.enabledExtensionCount = static_cast<uint32_t>(extension_name_pointers.size());
|
||||||
device_info.ppEnabledExtensionNames = enabled_extensions.data();
|
device_info.ppEnabledExtensionNames = extension_name_pointers.data();
|
||||||
|
|
||||||
// Check for required features before creating.
|
// Check for required features before creating.
|
||||||
if (!SelectDeviceFeatures())
|
if (!SelectDeviceFeatures())
|
||||||
|
@ -813,6 +823,12 @@ u32 VulkanContext::GetReadbackMemoryType(u32 bits, bool* is_coherent)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool VulkanContext::SupportsDeviceExtension(const char* name) const
|
||||||
|
{
|
||||||
|
return std::any_of(m_device_extensions.begin(), m_device_extensions.end(),
|
||||||
|
[name](const std::string& extension) { return extension == name; });
|
||||||
|
}
|
||||||
|
|
||||||
void VulkanContext::InitDriverDetails()
|
void VulkanContext::InitDriverDetails()
|
||||||
{
|
{
|
||||||
DriverDetails::Vendor vendor;
|
DriverDetails::Vendor vendor;
|
||||||
|
@ -928,4 +944,54 @@ void VulkanContext::PopulateShaderSubgroupSupport()
|
||||||
(subgroup_properties.supportedOperations & required_operations) == required_operations &&
|
(subgroup_properties.supportedOperations & required_operations) == required_operations &&
|
||||||
subgroup_properties.supportedStages & VK_SHADER_STAGE_FRAGMENT_BIT;
|
subgroup_properties.supportedStages & VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool VulkanContext::SupportsExclusiveFullscreen(const WindowSystemInfo& wsi, VkSurfaceKHR surface)
|
||||||
|
{
|
||||||
|
#ifdef SUPPORTS_VULKAN_EXCLUSIVE_FULLSCREEN
|
||||||
|
if (!surface || !vkGetPhysicalDeviceSurfaceCapabilities2KHR ||
|
||||||
|
!SupportsDeviceExtension(VK_EXT_FULL_SCREEN_EXCLUSIVE_EXTENSION_NAME))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkPhysicalDeviceSurfaceInfo2KHR si = {};
|
||||||
|
si.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR;
|
||||||
|
si.surface = surface;
|
||||||
|
|
||||||
|
auto platform_info = GetPlatformExclusiveFullscreenInfo(wsi);
|
||||||
|
si.pNext = &platform_info;
|
||||||
|
|
||||||
|
VkSurfaceCapabilities2KHR caps = {};
|
||||||
|
caps.sType = VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR;
|
||||||
|
|
||||||
|
VkSurfaceCapabilitiesFullScreenExclusiveEXT fullscreen_caps = {};
|
||||||
|
fullscreen_caps.sType = VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_FULL_SCREEN_EXCLUSIVE_EXT;
|
||||||
|
fullscreen_caps.fullScreenExclusiveSupported = VK_TRUE;
|
||||||
|
caps.pNext = &fullscreen_caps;
|
||||||
|
|
||||||
|
VkResult res = vkGetPhysicalDeviceSurfaceCapabilities2KHR(m_physical_device, &si, &caps);
|
||||||
|
if (res != VK_SUCCESS)
|
||||||
|
{
|
||||||
|
LOG_VULKAN_ERROR(res, "vkGetPhysicalDeviceSurfaceCapabilities2KHR failed:");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fullscreen_caps.fullScreenExclusiveSupported;
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
VkSurfaceFullScreenExclusiveWin32InfoEXT
|
||||||
|
VulkanContext::GetPlatformExclusiveFullscreenInfo(const WindowSystemInfo& wsi)
|
||||||
|
{
|
||||||
|
VkSurfaceFullScreenExclusiveWin32InfoEXT info = {};
|
||||||
|
info.sType = VK_STRUCTURE_TYPE_SURFACE_FULL_SCREEN_EXCLUSIVE_WIN32_INFO_EXT;
|
||||||
|
info.hmonitor =
|
||||||
|
MonitorFromWindow(static_cast<HWND>(wsi.render_surface), MONITOR_DEFAULTTOPRIMARY);
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace Vulkan
|
} // namespace Vulkan
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
|
@ -107,11 +108,22 @@ public:
|
||||||
u32 GetUploadMemoryType(u32 bits, bool* is_coherent = nullptr);
|
u32 GetUploadMemoryType(u32 bits, bool* is_coherent = nullptr);
|
||||||
u32 GetReadbackMemoryType(u32 bits, bool* is_coherent = nullptr);
|
u32 GetReadbackMemoryType(u32 bits, bool* is_coherent = nullptr);
|
||||||
|
|
||||||
|
// Returns true if the specified extension is supported and enabled.
|
||||||
|
bool SupportsDeviceExtension(const char* name) const;
|
||||||
|
|
||||||
|
// Returns true if exclusive fullscreen is supported for the given surface.
|
||||||
|
bool SupportsExclusiveFullscreen(const WindowSystemInfo& wsi, VkSurfaceKHR surface);
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
// Returns the platform-specific exclusive fullscreen structure.
|
||||||
|
VkSurfaceFullScreenExclusiveWin32InfoEXT
|
||||||
|
GetPlatformExclusiveFullscreenInfo(const WindowSystemInfo& wsi);
|
||||||
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using ExtensionList = std::vector<const char*>;
|
static bool SelectInstanceExtensions(std::vector<const char*>* extension_list,
|
||||||
static bool SelectInstanceExtensions(ExtensionList* extension_list, WindowSystemType wstype,
|
WindowSystemType wstype, bool enable_debug_report);
|
||||||
bool enable_debug_report);
|
bool SelectDeviceExtensions(bool enable_surface);
|
||||||
bool SelectDeviceExtensions(ExtensionList* extension_list, bool enable_surface);
|
|
||||||
bool SelectDeviceFeatures();
|
bool SelectDeviceFeatures();
|
||||||
bool CreateDevice(VkSurfaceKHR surface, bool enable_validation_layer);
|
bool CreateDevice(VkSurfaceKHR surface, bool enable_validation_layer);
|
||||||
void InitDriverDetails();
|
void InitDriverDetails();
|
||||||
|
@ -135,6 +147,8 @@ private:
|
||||||
|
|
||||||
u32 m_shader_subgroup_size = 1;
|
u32 m_shader_subgroup_size = 1;
|
||||||
bool m_supports_shader_subgroup_operations = false;
|
bool m_supports_shader_subgroup_operations = false;
|
||||||
|
|
||||||
|
std::vector<std::string> m_device_extensions;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern std::unique_ptr<VulkanContext> g_vulkan_context;
|
extern std::unique_ptr<VulkanContext> g_vulkan_context;
|
||||||
|
|
|
@ -61,6 +61,7 @@ VULKAN_INSTANCE_ENTRY_POINT(vkCreateDebugReportCallbackEXT, false)
|
||||||
VULKAN_INSTANCE_ENTRY_POINT(vkDestroyDebugReportCallbackEXT, false)
|
VULKAN_INSTANCE_ENTRY_POINT(vkDestroyDebugReportCallbackEXT, false)
|
||||||
VULKAN_INSTANCE_ENTRY_POINT(vkDebugReportMessageEXT, false)
|
VULKAN_INSTANCE_ENTRY_POINT(vkDebugReportMessageEXT, false)
|
||||||
VULKAN_INSTANCE_ENTRY_POINT(vkGetPhysicalDeviceProperties2, false)
|
VULKAN_INSTANCE_ENTRY_POINT(vkGetPhysicalDeviceProperties2, false)
|
||||||
|
VULKAN_INSTANCE_ENTRY_POINT(vkGetPhysicalDeviceSurfaceCapabilities2KHR, false)
|
||||||
|
|
||||||
#endif // VULKAN_INSTANCE_ENTRY_POINT
|
#endif // VULKAN_INSTANCE_ENTRY_POINT
|
||||||
|
|
||||||
|
@ -192,4 +193,9 @@ VULKAN_DEVICE_ENTRY_POINT(vkGetSwapchainImagesKHR, false)
|
||||||
VULKAN_DEVICE_ENTRY_POINT(vkAcquireNextImageKHR, false)
|
VULKAN_DEVICE_ENTRY_POINT(vkAcquireNextImageKHR, false)
|
||||||
VULKAN_DEVICE_ENTRY_POINT(vkQueuePresentKHR, false)
|
VULKAN_DEVICE_ENTRY_POINT(vkQueuePresentKHR, false)
|
||||||
|
|
||||||
|
#ifdef SUPPORTS_VULKAN_EXCLUSIVE_FULLSCREEN
|
||||||
|
VULKAN_DEVICE_ENTRY_POINT(vkAcquireFullScreenExclusiveModeEXT, false)
|
||||||
|
VULKAN_DEVICE_ENTRY_POINT(vkReleaseFullScreenExclusiveModeEXT, false)
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif // VULKAN_DEVICE_ENTRY_POINT
|
#endif // VULKAN_DEVICE_ENTRY_POINT
|
||||||
|
|
|
@ -24,6 +24,11 @@
|
||||||
|
|
||||||
#include "vulkan/vulkan.h"
|
#include "vulkan/vulkan.h"
|
||||||
|
|
||||||
|
// Currently, exclusive fullscreen is only supported on Windows.
|
||||||
|
#if defined(WIN32)
|
||||||
|
#define SUPPORTS_VULKAN_EXCLUSIVE_FULLSCREEN 1
|
||||||
|
#endif
|
||||||
|
|
||||||
// We abuse the preprocessor here to only need to specify function names once.
|
// We abuse the preprocessor here to only need to specify function names once.
|
||||||
#define VULKAN_MODULE_ENTRY_POINT(name, required) extern PFN_##name name;
|
#define VULKAN_MODULE_ENTRY_POINT(name, required) extern PFN_##name name;
|
||||||
#define VULKAN_INSTANCE_ENTRY_POINT(name, required) extern PFN_##name name;
|
#define VULKAN_INSTANCE_ENTRY_POINT(name, required) extern PFN_##name name;
|
||||||
|
|
|
@ -186,6 +186,8 @@ bool VideoBackend::Initialize(const WindowSystemInfo& wsi)
|
||||||
g_vulkan_context->GetDeviceFeatures());
|
g_vulkan_context->GetDeviceFeatures());
|
||||||
VulkanContext::PopulateBackendInfoMultisampleModes(
|
VulkanContext::PopulateBackendInfoMultisampleModes(
|
||||||
&g_Config, g_vulkan_context->GetPhysicalDevice(), g_vulkan_context->GetDeviceProperties());
|
&g_Config, g_vulkan_context->GetPhysicalDevice(), g_vulkan_context->GetDeviceProperties());
|
||||||
|
g_Config.backend_info.bSupportsExclusiveFullscreen =
|
||||||
|
enable_surface && g_vulkan_context->SupportsExclusiveFullscreen(wsi, surface);
|
||||||
|
|
||||||
// With the backend information populated, we can now initialize videocommon.
|
// With the backend information populated, we can now initialize videocommon.
|
||||||
InitializeShared();
|
InitializeShared();
|
||||||
|
|
Loading…
Reference in New Issue