diff --git a/dep/vulkan-loader/include/vulkan_entry_points.inl b/dep/vulkan-loader/include/vulkan_entry_points.inl index 155710f05..0d2cf4382 100644 --- a/dep/vulkan-loader/include/vulkan_entry_points.inl +++ b/dep/vulkan-loader/include/vulkan_entry_points.inl @@ -72,6 +72,14 @@ VULKAN_INSTANCE_ENTRY_POINT(vkDebugReportMessageEXT, false) VULKAN_INSTANCE_ENTRY_POINT(vkGetPhysicalDeviceProperties2, false) VULKAN_INSTANCE_ENTRY_POINT(vkGetPhysicalDeviceSurfaceCapabilities2KHR, false) +VULKAN_INSTANCE_ENTRY_POINT(vkGetPhysicalDeviceDisplayPropertiesKHR, false) +VULKAN_INSTANCE_ENTRY_POINT(vkGetPhysicalDeviceDisplayPlanePropertiesKHR, false) +VULKAN_INSTANCE_ENTRY_POINT(vkGetDisplayPlaneSupportedDisplaysKHR, false) +VULKAN_INSTANCE_ENTRY_POINT(vkGetDisplayModePropertiesKHR, false) +VULKAN_INSTANCE_ENTRY_POINT(vkCreateDisplayModeKHR, false) +VULKAN_INSTANCE_ENTRY_POINT(vkGetDisplayPlaneCapabilitiesKHR, false) +VULKAN_INSTANCE_ENTRY_POINT(vkCreateDisplayPlaneSurfaceKHR, false) + #endif // VULKAN_INSTANCE_ENTRY_POINT #ifdef VULKAN_DEVICE_ENTRY_POINT diff --git a/src/common/vulkan/context.cpp b/src/common/vulkan/context.cpp index 6d9466460..77fe47e37 100644 --- a/src/common/vulkan/context.cpp +++ b/src/common/vulkan/context.cpp @@ -97,10 +97,10 @@ bool Context::CheckValidationLayerAvailablility() }) != layer_list.end()); } -VkInstance Context::CreateVulkanInstance(bool enable_surface, bool enable_debug_report, bool enable_validation_layer) +VkInstance Context::CreateVulkanInstance(const WindowInfo* wi, bool enable_debug_report, bool enable_validation_layer) { ExtensionList enabled_extensions; - if (!SelectInstanceExtensions(&enabled_extensions, enable_surface, enable_debug_report)) + if (!SelectInstanceExtensions(&enabled_extensions, wi, enable_debug_report)) return VK_NULL_HANDLE; VkApplicationInfo app_info = {}; @@ -141,7 +141,7 @@ VkInstance Context::CreateVulkanInstance(bool enable_surface, bool enable_debug_ return instance; } -bool Context::SelectInstanceExtensions(ExtensionList* extension_list, bool enable_surface, bool enable_debug_report) +bool Context::SelectInstanceExtensions(ExtensionList* extension_list, const WindowInfo* wi, bool enable_debug_report) { u32 extension_count = 0; VkResult res = vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, nullptr); @@ -182,31 +182,31 @@ bool Context::SelectInstanceExtensions(ExtensionList* extension_list, bool enabl }; // Common extensions - if (enable_surface && !SupportsExtension(VK_KHR_SURFACE_EXTENSION_NAME, true)) + if (wi && wi->type != WindowInfo::Type::Surfaceless && !SupportsExtension(VK_KHR_SURFACE_EXTENSION_NAME, true)) return false; #if defined(VK_USE_PLATFORM_WIN32_KHR) - if (enable_surface && !SupportsExtension(VK_KHR_WIN32_SURFACE_EXTENSION_NAME, true)) + if (wi && wi->type == WindowInfo::Type::Win32 && !SupportsExtension(VK_KHR_WIN32_SURFACE_EXTENSION_NAME, true)) return false; #endif #if defined(VK_USE_PLATFORM_XLIB_KHR) - if (enable_surface && !SupportsExtension(VK_KHR_XLIB_SURFACE_EXTENSION_NAME, true)) + if (wi && wi->type == WindowInfo::Type::X11 && !SupportsExtension(VK_KHR_XLIB_SURFACE_EXTENSION_NAME, true)) return false; #endif #if defined(VK_USE_PLATFORM_WAYLAND_KHR) - if (enable_surface && !SupportsExtension(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME, true)) + if (wi && wi->type == WindowInfo::Type::Wayland && !SupportsExtension(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME, true)) return false; #endif #if defined(VK_USE_PLATFORM_ANDROID_KHR) - if (enable_surface && !SupportsExtension(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME, true)) + if (wi && wi->type == WindowInfo::Type::Android && !SupportsExtension(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME, true)) return false; #endif -#if defined(VK_USE_PLATFORM_MACOS_MVK) - if (enable_surface && !SupportsExtension(VK_MVK_MACOS_SURFACE_EXTENSION_NAME, true)) +#if defined(VK_USE_PLATFORM_METAL_EXT) + if (wi && wi->type == WindowInfo::Type::MacOS && !SupportsExtension(VK_EXT_METAL_SURFACE_EXTENSION_NAME, true)) return false; #endif -#if defined(VK_USE_PLATFORM_METAL_EXT) - if (enable_surface && !SupportsExtension(VK_EXT_METAL_SURFACE_EXTENSION_NAME, true)) +#ifndef _WIN32 + if (wi && wi->type == WindowInfo::Type::DRM && !SupportsExtension(VK_KHR_DISPLAY_EXTENSION_NAME, true)) return false; #endif @@ -302,7 +302,7 @@ bool Context::Create(std::string_view gpu_name, const WindowInfo* wi, std::uniqu } const bool enable_surface = (wi && wi->type != WindowInfo::Type::Surfaceless); - VkInstance instance = CreateVulkanInstance(enable_surface, enable_debug_reports, enable_validation_layer); + VkInstance instance = CreateVulkanInstance(wi, enable_debug_reports, enable_validation_layer); if (instance == VK_NULL_HANDLE) { Vulkan::UnloadVulkanLibrary(); @@ -349,8 +349,12 @@ bool Context::Create(std::string_view gpu_name, const WindowInfo* wi, std::uniqu } VkSurfaceKHR surface = VK_NULL_HANDLE; - WindowInfo wi_copy(*wi); - if (enable_surface && (surface = SwapChain::CreateVulkanSurface(instance, wi_copy)) == VK_NULL_HANDLE) + WindowInfo wi_copy; + if (wi) + wi_copy = *wi; + + if (enable_surface && + (surface = SwapChain::CreateVulkanSurface(instance, gpus[gpu_index], wi_copy)) == VK_NULL_HANDLE) { vkDestroyInstance(instance, nullptr); Vulkan::UnloadVulkanLibrary(); diff --git a/src/common/vulkan/context.h b/src/common/vulkan/context.h index 48b3a76c3..e08363676 100644 --- a/src/common/vulkan/context.h +++ b/src/common/vulkan/context.h @@ -38,7 +38,7 @@ public: static bool CheckValidationLayerAvailablility(); // Helper method to create a Vulkan instance. - static VkInstance CreateVulkanInstance(bool enable_surface, bool enable_debug_report, bool enable_validation_layer); + static VkInstance CreateVulkanInstance(const WindowInfo* wi, bool enable_debug_report, bool enable_validation_layer); // Returns a list of Vulkan-compatible GPUs. using GPUList = std::vector; @@ -180,7 +180,7 @@ private: Context(VkInstance instance, VkPhysicalDevice physical_device, bool owns_device); using ExtensionList = std::vector; - static bool SelectInstanceExtensions(ExtensionList* extension_list, bool enable_surface, bool enable_debug_report); + static bool SelectInstanceExtensions(ExtensionList* extension_list, const WindowInfo* wi, bool enable_debug_report); bool SelectDeviceExtensions(ExtensionList* extension_list, bool enable_surface); bool SelectDeviceFeatures(const VkPhysicalDeviceFeatures* required_features); bool CreateDevice(VkSurfaceKHR surface, bool enable_validation_layer, const char** required_device_extensions, diff --git a/src/common/vulkan/swap_chain.cpp b/src/common/vulkan/swap_chain.cpp index c57215dae..5bd594e35 100644 --- a/src/common/vulkan/swap_chain.cpp +++ b/src/common/vulkan/swap_chain.cpp @@ -87,7 +87,142 @@ SwapChain::~SwapChain() DestroySurface(); } -VkSurfaceKHR SwapChain::CreateVulkanSurface(VkInstance instance, WindowInfo& wi) +#ifndef _WIN32 + +static VkSurfaceKHR CreateDisplaySurface(VkInstance instance, VkPhysicalDevice physical_device, const WindowInfo& wi) +{ + Log_InfoPrintf("Trying to create a VK_KHR_display surface of %ux%u", wi.surface_width, wi.surface_height); + + u32 num_displays; + VkResult res = vkGetPhysicalDeviceDisplayPropertiesKHR(physical_device, &num_displays, nullptr); + if (res != VK_SUCCESS || num_displays == 0) + { + LOG_VULKAN_ERROR(res, "vkGetPhysicalDeviceDisplayPropertiesKHR() failed:"); + return {}; + } + + std::vector displays(num_displays); + res = vkGetPhysicalDeviceDisplayPropertiesKHR(physical_device, &num_displays, displays.data()); + if (res != VK_SUCCESS || num_displays != displays.size()) + { + LOG_VULKAN_ERROR(res, "vkGetPhysicalDeviceDisplayPropertiesKHR() failed:"); + return {}; + } + + for (u32 display_index = 0; display_index < num_displays; display_index++) + { + const VkDisplayPropertiesKHR& props = displays[display_index]; + Log_DevPrintf("Testing display '%s'", props.displayName); + + u32 num_modes; + res = vkGetDisplayModePropertiesKHR(physical_device, props.display, &num_modes, nullptr); + if (res != VK_SUCCESS || num_modes == 0) + { + LOG_VULKAN_ERROR(res, "vkGetDisplayModePropertiesKHR() failed:"); + continue; + } + + std::vector modes(num_modes); + res = vkGetDisplayModePropertiesKHR(physical_device, props.display, &num_modes, modes.data()); + if (res != VK_SUCCESS || num_modes != modes.size()) + { + LOG_VULKAN_ERROR(res, "vkGetDisplayModePropertiesKHR() failed:"); + continue; + } + + const VkDisplayModePropertiesKHR* matched_mode = nullptr; + for (const VkDisplayModePropertiesKHR& mode : modes) + { + Log_DevPrintf(" Mode %ux%u @ %u", mode.parameters.visibleRegion.width, mode.parameters.visibleRegion.height, + mode.parameters.refreshRate); + if (!matched_mode && mode.parameters.visibleRegion.width == wi.surface_width && + mode.parameters.visibleRegion.height) + { + matched_mode = &mode; + } + } + + if (!matched_mode) + { + Log_DevPrintf("No modes matched on '%s'", props.displayName); + continue; + } + + u32 num_planes; + res = vkGetPhysicalDeviceDisplayPlanePropertiesKHR(physical_device, &num_planes, nullptr); + if (res != VK_SUCCESS || num_planes == 0) + { + LOG_VULKAN_ERROR(res, "vkGetPhysicalDeviceDisplayPlanePropertiesKHR() failed:"); + continue; + } + + std::vector planes(num_planes); + res = vkGetPhysicalDeviceDisplayPlanePropertiesKHR(physical_device, &num_planes, planes.data()); + if (res != VK_SUCCESS || num_planes != planes.size()) + { + LOG_VULKAN_ERROR(res, "vkGetPhysicalDeviceDisplayPlanePropertiesKHR() failed:"); + continue; + } + + u32 plane_index = 0; + for (; plane_index < num_planes; plane_index++) + { + u32 supported_display_count; + res = vkGetDisplayPlaneSupportedDisplaysKHR(physical_device, plane_index, &supported_display_count, nullptr); + if (res != VK_SUCCESS || supported_display_count == 0) + { + LOG_VULKAN_ERROR(res, "vkGetPhysicalDeviceDisplayPlanePropertiesKHR() failed:"); + continue; + } + + std::vector supported_displays(supported_display_count); + res = vkGetDisplayPlaneSupportedDisplaysKHR(physical_device, plane_index, &supported_display_count, + supported_displays.data()); + if (res != VK_SUCCESS || supported_display_count == 0) + { + LOG_VULKAN_ERROR(res, "vkGetPhysicalDeviceDisplayPlanePropertiesKHR() failed:"); + continue; + } + + const bool is_supported = + std::find(supported_displays.begin(), supported_displays.end(), props.display) != supported_displays.end(); + if (!is_supported) + continue; + + break; + } + + if (plane_index == num_planes) + { + Log_DevPrintf("No planes matched on '%s'", props.displayName); + continue; + } + + VkDisplaySurfaceCreateInfoKHR info = {}; + info.sType = VK_STRUCTURE_TYPE_DISPLAY_SURFACE_CREATE_INFO_KHR; + info.displayMode = matched_mode->displayMode; + info.planeIndex = plane_index; + info.planeStackIndex = planes[plane_index].currentStackIndex; + info.transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; + info.globalAlpha = 1.0f; + info.alphaMode = VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR; + info.imageExtent = matched_mode->parameters.visibleRegion; + + VkSurfaceKHR surface; + res = vkCreateDisplayPlaneSurfaceKHR(instance, &info, nullptr, &surface); + if (res != VK_SUCCESS) + { + LOG_VULKAN_ERROR(res, "vkCreateDisplayPlaneSurfaceKHR() failed: "); + continue; + } + + return surface; + } +} + +#endif + +VkSurfaceKHR SwapChain::CreateVulkanSurface(VkInstance instance, VkPhysicalDevice physical_device, WindowInfo& wi) { #if defined(VK_USE_PLATFORM_WIN32_KHR) if (wi.type == WindowInfo::Type::Win32) @@ -213,6 +348,11 @@ VkSurfaceKHR SwapChain::CreateVulkanSurface(VkInstance instance, WindowInfo& wi) } #endif +#ifndef _WIN32 + if (wi.type == WindowInfo::Type::DRM) + return CreateDisplaySurface(instance, physical_device, wi); +#endif + return VK_NULL_HANDLE; } @@ -559,7 +699,7 @@ bool SwapChain::RecreateSurface(const WindowInfo& new_wi) // Re-create the surface with the new native handle m_wi = new_wi; - m_surface = CreateVulkanSurface(g_vulkan_context->GetVulkanInstance(), m_wi); + m_surface = CreateVulkanSurface(g_vulkan_context->GetVulkanInstance(), g_vulkan_context->GetPhysicalDevice(), m_wi); if (m_surface == VK_NULL_HANDLE) return false; diff --git a/src/common/vulkan/swap_chain.h b/src/common/vulkan/swap_chain.h index eb2ab335c..6fd872e32 100644 --- a/src/common/vulkan/swap_chain.h +++ b/src/common/vulkan/swap_chain.h @@ -21,7 +21,7 @@ public: ~SwapChain(); // Creates a vulkan-renderable surface for the specified window handle. - static VkSurfaceKHR CreateVulkanSurface(VkInstance instance, WindowInfo& wi); + static VkSurfaceKHR CreateVulkanSurface(VkInstance instance, VkPhysicalDevice physical_device, WindowInfo& wi); // Destroys a previously-created surface. static void DestroyVulkanSurface(VkInstance instance, WindowInfo& wi, VkSurfaceKHR surface); diff --git a/src/frontend-common/vulkan_host_display.cpp b/src/frontend-common/vulkan_host_display.cpp index 7c6d2366d..0053a5f16 100644 --- a/src/frontend-common/vulkan_host_display.cpp +++ b/src/frontend-common/vulkan_host_display.cpp @@ -10,10 +10,10 @@ #include "common/vulkan/swap_chain.h" #include "common/vulkan/util.h" #include "core/shader_cache_version.h" -#include "postprocessing_shadergen.h" -#include #include "imgui.h" #include "imgui_impl_vulkan.h" +#include "postprocessing_shadergen.h" +#include Log_SetChannel(VulkanHostDisplay); namespace FrontendCommon { @@ -79,7 +79,8 @@ bool VulkanHostDisplay::ChangeRenderWindow(const WindowInfo& new_wi) } WindowInfo wi_copy(new_wi); - VkSurfaceKHR surface = Vulkan::SwapChain::CreateVulkanSurface(g_vulkan_context->GetVulkanInstance(), wi_copy); + VkSurfaceKHR surface = Vulkan::SwapChain::CreateVulkanSurface(g_vulkan_context->GetVulkanInstance(), + g_vulkan_context->GetPhysicalDevice(), wi_copy); if (surface == VK_NULL_HANDLE) { Log_ErrorPrintf("Failed to create new surface for swap chain"); @@ -755,7 +756,7 @@ std::vector VulkanHostDisplay::EnumerateAdapterNames() { Common::ScopeGuard lib_guard([]() { Vulkan::UnloadVulkanLibrary(); }); - VkInstance instance = Vulkan::Context::CreateVulkanInstance(false, false, false); + VkInstance instance = Vulkan::Context::CreateVulkanInstance(nullptr, false, false); if (instance != VK_NULL_HANDLE) { Common::ScopeGuard instance_guard([&instance]() { vkDestroyInstance(instance, nullptr); });