Vulkan: Support VK_KHR_display

This commit is contained in:
Connor McLaughlin 2021-02-03 01:56:53 +10:00
parent 469010868e
commit 8f9bbb0bba
6 changed files with 178 additions and 25 deletions

View File

@ -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

View File

@ -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))
return false;
#endif
#if defined(VK_USE_PLATFORM_MACOS_MVK)
if (enable_surface && !SupportsExtension(VK_MVK_MACOS_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_METAL_EXT)
if (enable_surface && !SupportsExtension(VK_EXT_METAL_SURFACE_EXTENSION_NAME, true))
if (wi && wi->type == WindowInfo::Type::MacOS && !SupportsExtension(VK_EXT_METAL_SURFACE_EXTENSION_NAME, true))
return false;
#endif
#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();

View File

@ -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<VkPhysicalDevice>;
@ -180,7 +180,7 @@ private:
Context(VkInstance instance, VkPhysicalDevice physical_device, bool owns_device);
using ExtensionList = std::vector<const char*>;
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,

View File

@ -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<VkDisplayPropertiesKHR> 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<VkDisplayModePropertiesKHR> 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<VkDisplayPlanePropertiesKHR> 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<VkDisplayKHR> 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;

View File

@ -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);

View File

@ -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 <array>
#include "imgui.h"
#include "imgui_impl_vulkan.h"
#include "postprocessing_shadergen.h"
#include <array>
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<std::string> 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); });