Vulkan DRM/KMS Support
This commit is contained in:
parent
ada646a795
commit
b8a49d8526
|
@ -105,6 +105,7 @@ option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence, show the current gam
|
|||
option(USE_MGBA "Enables GBA controllers emulation using libmgba" ON)
|
||||
option(ENABLE_AUTOUPDATE "Enables support for automatic updates" ON)
|
||||
option(USE_RETRO_ACHIEVEMENTS "Enables integration with retroachievements.org" ON)
|
||||
option(ENABLE_DRM "Enables DRM support" OFF)
|
||||
|
||||
# Maintainers: if you consider blanket disabling this for your users, please
|
||||
# consider the following points:
|
||||
|
@ -501,6 +502,10 @@ if (OPENGL_GL)
|
|||
include_directories(${OPENGL_INCLUDE_DIR})
|
||||
endif()
|
||||
|
||||
if(ENABLE_DRM)
|
||||
add_definitions(-DHAVE_DRM)
|
||||
endif()
|
||||
|
||||
if(ENABLE_X11)
|
||||
pkg_check_modules(X11 x11 IMPORTED_TARGET)
|
||||
if(X11_FOUND)
|
||||
|
|
|
@ -13,6 +13,7 @@ enum class WindowSystemType
|
|||
Wayland,
|
||||
FBDev,
|
||||
Haiku,
|
||||
DRM,
|
||||
};
|
||||
|
||||
struct WindowSystemInfo
|
||||
|
|
|
@ -23,6 +23,10 @@ if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
|||
target_sources(dolphin-nogui PRIVATE PlatformFBDev.cpp)
|
||||
endif()
|
||||
|
||||
if(ENABLE_DRM)
|
||||
target_sources(dolphin-nogui PRIVATE PlatformDRM.cpp)
|
||||
endif()
|
||||
|
||||
set_target_properties(dolphin-nogui PROPERTIES OUTPUT_NAME dolphin-emu-nogui)
|
||||
|
||||
target_link_libraries(dolphin-nogui
|
||||
|
|
|
@ -172,6 +172,11 @@ static std::unique_ptr<Platform> GetPlatform(const optparse::Values& options)
|
|||
{
|
||||
std::string platform_name = static_cast<const char*>(options.get("platform"));
|
||||
|
||||
#if HAVE_DRM
|
||||
if (platform_name == "drm" || platform_name.empty())
|
||||
return Platform::CreateDRMPlatform();
|
||||
#endif
|
||||
|
||||
#if HAVE_X11
|
||||
if (platform_name == "x11" || platform_name.empty())
|
||||
return Platform::CreateX11Platform();
|
||||
|
@ -215,6 +220,10 @@ int main(int argc, char* argv[])
|
|||
,
|
||||
"fbdev"
|
||||
#endif
|
||||
#if HAVE_DRM
|
||||
,
|
||||
"drm"
|
||||
#endif
|
||||
#if HAVE_X11
|
||||
,
|
||||
"x11"
|
||||
|
|
|
@ -30,6 +30,10 @@ public:
|
|||
// Request an immediate shutdown.
|
||||
void Stop();
|
||||
|
||||
#if HAVE_DRM
|
||||
static std::unique_ptr<Platform> CreateDRMPlatform();
|
||||
#endif
|
||||
|
||||
static std::unique_ptr<Platform> CreateHeadlessPlatform();
|
||||
#ifdef HAVE_X11
|
||||
static std::unique_ptr<Platform> CreateX11Platform();
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
// Copyright 2018 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "DolphinNoGUI/Platform.h"
|
||||
|
||||
#include "Common/MsgHandler.h"
|
||||
#include "Core/ConfigManager.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/State.h"
|
||||
#include "Core/System.h"
|
||||
|
||||
#include <climits>
|
||||
#include <cstdio>
|
||||
#include <thread>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
class PlatformDRM : public Platform
|
||||
{
|
||||
public:
|
||||
void SetTitle(const std::string& string) override;
|
||||
void MainLoop() override;
|
||||
|
||||
WindowSystemInfo GetWindowSystemInfo() const override;
|
||||
};
|
||||
|
||||
void PlatformDRM::SetTitle(const std::string& string)
|
||||
{
|
||||
std::fprintf(stdout, "%s\n", string.c_str());
|
||||
}
|
||||
|
||||
void PlatformDRM::MainLoop()
|
||||
{
|
||||
while (IsRunning())
|
||||
{
|
||||
UpdateRunningFlag();
|
||||
Core::HostDispatchJobs(Core::System::GetInstance());
|
||||
|
||||
// TODO: Is this sleep appropriate?
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
}
|
||||
|
||||
WindowSystemInfo PlatformDRM::GetWindowSystemInfo() const
|
||||
{
|
||||
WindowSystemInfo wsi;
|
||||
wsi.type = WindowSystemType::DRM;
|
||||
wsi.display_connection = nullptr; // EGL_DEFAULT_DISPLAY
|
||||
wsi.render_window = nullptr;
|
||||
wsi.render_surface = nullptr;
|
||||
return wsi;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<Platform> Platform::CreateDRMPlatform()
|
||||
{
|
||||
return std::make_unique<PlatformDRM>();
|
||||
}
|
|
@ -57,10 +57,10 @@ endif()
|
|||
|
||||
# Only include the Vulkan headers when building the Vulkan backend
|
||||
target_include_directories(videovulkan
|
||||
PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/Externals/Vulkan-Headers/include
|
||||
${CMAKE_SOURCE_DIR}/Externals/VulkanMemoryAllocator/include
|
||||
${CMAKE_SOURCE_DIR}/Externals/libadrenotools/include
|
||||
PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/Externals/Vulkan-Headers/include
|
||||
${CMAKE_SOURCE_DIR}/Externals/VulkanMemoryAllocator/include
|
||||
${CMAKE_SOURCE_DIR}/Externals/libadrenotools/include
|
||||
)
|
||||
|
||||
if(MSVC)
|
||||
|
|
|
@ -147,20 +147,6 @@ bool VideoBackend::Initialize(const WindowSystemInfo& wsi)
|
|||
VulkanContext::PopulateBackendInfo(&g_Config);
|
||||
VulkanContext::PopulateBackendInfoAdapters(&g_Config, gpu_list);
|
||||
|
||||
// We need the surface before we can create a device, as some parameters depend on it.
|
||||
VkSurfaceKHR surface = VK_NULL_HANDLE;
|
||||
if (enable_surface)
|
||||
{
|
||||
surface = SwapChain::CreateVulkanSurface(instance, wsi);
|
||||
if (surface == VK_NULL_HANDLE)
|
||||
{
|
||||
PanicAlertFmt("Failed to create Vulkan surface.");
|
||||
vkDestroyInstance(instance, nullptr);
|
||||
UnloadVulkanLibrary();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Since we haven't called InitializeShared yet, iAdapter may be out of range,
|
||||
// so we have to check it ourselves.
|
||||
size_t selected_adapter_index = static_cast<size_t>(g_Config.iAdapter);
|
||||
|
@ -170,6 +156,20 @@ bool VideoBackend::Initialize(const WindowSystemInfo& wsi)
|
|||
selected_adapter_index = 0;
|
||||
}
|
||||
|
||||
// We need the surface before we can create a device, as some parameters depend on it.
|
||||
VkSurfaceKHR surface = VK_NULL_HANDLE;
|
||||
if (enable_surface)
|
||||
{
|
||||
surface = SwapChain::CreateVulkanSurface(instance, gpu_list[selected_adapter_index], wsi);
|
||||
if (surface == VK_NULL_HANDLE)
|
||||
{
|
||||
PanicAlertFmt("Failed to create Vulkan surface.");
|
||||
vkDestroyInstance(instance, nullptr);
|
||||
UnloadVulkanLibrary();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Now we can create the Vulkan device. VulkanContext takes ownership of the instance and surface.
|
||||
g_vulkan_context =
|
||||
VulkanContext::Create(instance, gpu_list[selected_adapter_index], surface, enable_debug_utils,
|
||||
|
|
|
@ -36,8 +36,174 @@ SwapChain::~SwapChain()
|
|||
DestroySurface();
|
||||
}
|
||||
|
||||
VkSurfaceKHR SwapChain::CreateVulkanSurface(VkInstance instance, const WindowSystemInfo& wsi)
|
||||
VkSurfaceKHR SwapChain::CreateVulkanSurface(VkInstance instance, VkPhysicalDevice physical_device,
|
||||
const WindowSystemInfo& wsi)
|
||||
{
|
||||
#if defined(VK_USE_PLATFORM_DISPLAY_KHR)
|
||||
if (wsi.type == WindowSystemType::DRM)
|
||||
{
|
||||
// Get the first display
|
||||
uint32_t display_count = 1;
|
||||
VkDisplayPropertiesKHR display_props;
|
||||
if (VkResult err = vkGetPhysicalDeviceDisplayPropertiesKHR(physical_device, &display_count,
|
||||
&display_props);
|
||||
err != VK_SUCCESS && err != VK_INCOMPLETE)
|
||||
{
|
||||
LOG_VULKAN_ERROR(err, "vkGetPhysicalDeviceDisplayPropertiesKHR failed: ");
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
// Get the first mode of the display
|
||||
uint32_t mode_count = 0;
|
||||
if (VkResult err = vkGetDisplayModePropertiesKHR(physical_device, display_props.display,
|
||||
&mode_count, nullptr);
|
||||
err != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(err, "vkGetDisplayModePropertiesKHR failed: ");
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
if (mode_count == 0)
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO, "Cannot find any mode for the display!");
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
VkDisplayModePropertiesKHR mode_props;
|
||||
mode_count = 1;
|
||||
if (VkResult err = vkGetDisplayModePropertiesKHR(physical_device, display_props.display,
|
||||
&mode_count, &mode_props);
|
||||
err != VK_SUCCESS && err != VK_INCOMPLETE)
|
||||
{
|
||||
LOG_VULKAN_ERROR(err, "vkGetDisplayModePropertiesKHR failed: ");
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
// Get the list of planes
|
||||
uint32_t plane_count = 0;
|
||||
if (VkResult err =
|
||||
vkGetPhysicalDeviceDisplayPlanePropertiesKHR(physical_device, &plane_count, nullptr);
|
||||
err != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(err, "vkGetPhysicalDeviceDisplayPlanePropertiesKHR failed: ");
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
if (plane_count == 0)
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO, "No display planes found!");
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
// Find a plane compatible with the display
|
||||
// Find a plane compatible with the display
|
||||
uint32_t compatible_plane_index = UINT32_MAX;
|
||||
|
||||
for (uint32_t plane_index = 0; plane_index < plane_count; plane_index++)
|
||||
{
|
||||
// Query the number of displays supported by the plane
|
||||
display_count = 0;
|
||||
VkResult err = vkGetDisplayPlaneSupportedDisplaysKHR(physical_device, plane_index,
|
||||
&display_count, nullptr);
|
||||
if (err != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(err, "vkGetDisplayPlaneSupportedDisplaysKHR (count query) failed: ");
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
if (display_count == 0)
|
||||
continue; // Skip planes that support no displays
|
||||
|
||||
// Allocate memory to hold the supported displays
|
||||
std::vector<VkDisplayKHR> displays(display_count);
|
||||
err = vkGetDisplayPlaneSupportedDisplaysKHR(physical_device, plane_index, &display_count,
|
||||
displays.data());
|
||||
if (err != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(err, "vkGetDisplayPlaneSupportedDisplaysKHR (fetch displays) failed: ");
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
// Check if the target display is among the supported displays
|
||||
for (const auto& display : displays)
|
||||
{
|
||||
if (display == display_props.display)
|
||||
{
|
||||
compatible_plane_index = plane_index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (compatible_plane_index != UINT32_MAX)
|
||||
break; // Exit early if a compatible plane is found
|
||||
}
|
||||
|
||||
if (compatible_plane_index == UINT32_MAX)
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO, "No compatible plane found for the display!");
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
if (compatible_plane_index == UINT32_MAX)
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO, "No compatible plane found for the display!");
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
// Get capabilities of the compatible plane
|
||||
VkDisplayPlaneCapabilitiesKHR plane_capabilities;
|
||||
if (VkResult err = vkGetDisplayPlaneCapabilitiesKHR(
|
||||
physical_device, mode_props.displayMode, compatible_plane_index, &plane_capabilities);
|
||||
err != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(err, "vkGetDisplayPlaneCapabilitiesKHR failed: ");
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
// Find a supported alpha mode
|
||||
VkDisplayPlaneAlphaFlagBitsKHR alpha_mode = VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR;
|
||||
VkDisplayPlaneAlphaFlagBitsKHR alpha_modes[] = {
|
||||
VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR,
|
||||
VK_DISPLAY_PLANE_ALPHA_GLOBAL_BIT_KHR,
|
||||
VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_BIT_KHR,
|
||||
VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_PREMULTIPLIED_BIT_KHR,
|
||||
};
|
||||
|
||||
for (auto& curr_alpha_mode : alpha_modes)
|
||||
{
|
||||
if (plane_capabilities.supportedAlpha & curr_alpha_mode)
|
||||
{
|
||||
alpha_mode = curr_alpha_mode;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Create the display surface
|
||||
VkDisplaySurfaceCreateInfoKHR surface_create_info = {};
|
||||
surface_create_info.sType = VK_STRUCTURE_TYPE_DISPLAY_SURFACE_CREATE_INFO_KHR;
|
||||
surface_create_info.displayMode = mode_props.displayMode;
|
||||
surface_create_info.planeIndex = compatible_plane_index;
|
||||
surface_create_info.planeStackIndex = 0;
|
||||
surface_create_info.transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
|
||||
surface_create_info.globalAlpha = 1.0f;
|
||||
surface_create_info.alphaMode = alpha_mode;
|
||||
surface_create_info.imageExtent.width = display_props.physicalResolution.width;
|
||||
surface_create_info.imageExtent.height = display_props.physicalResolution.height;
|
||||
|
||||
VkSurfaceKHR surface;
|
||||
if (VkResult res =
|
||||
vkCreateDisplayPlaneSurfaceKHR(instance, &surface_create_info, nullptr, &surface);
|
||||
res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkCreateDisplayPlaneSurfaceKHR failed: ");
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
return surface;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(VK_USE_PLATFORM_WIN32_KHR)
|
||||
if (wsi.type == WindowSystemType::Windows)
|
||||
{
|
||||
|
@ -583,7 +749,8 @@ bool SwapChain::RecreateSurface(void* native_handle)
|
|||
|
||||
// Re-create the surface with the new native handle
|
||||
m_wsi.render_surface = native_handle;
|
||||
m_surface = CreateVulkanSurface(g_vulkan_context->GetVulkanInstance(), m_wsi);
|
||||
m_surface = CreateVulkanSurface(g_vulkan_context->GetVulkanInstance(),
|
||||
g_vulkan_context->GetPhysicalDevice(), m_wsi);
|
||||
if (m_surface == VK_NULL_HANDLE)
|
||||
return false;
|
||||
|
||||
|
|
|
@ -25,7 +25,8 @@ public:
|
|||
~SwapChain();
|
||||
|
||||
// Creates a vulkan-renderable surface for the specified window handle.
|
||||
static VkSurfaceKHR CreateVulkanSurface(VkInstance instance, const WindowSystemInfo& wsi);
|
||||
static VkSurfaceKHR CreateVulkanSurface(VkInstance instance, VkPhysicalDevice physical_device,
|
||||
const WindowSystemInfo& wsi);
|
||||
|
||||
// Create a new swap chain from a pre-existing surface.
|
||||
static std::unique_ptr<SwapChain> Create(const WindowSystemInfo& wsi, VkSurfaceKHR surface,
|
||||
|
|
|
@ -373,6 +373,20 @@ bool VulkanContext::SelectInstanceExtensions(std::vector<const char*>* extension
|
|||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(VK_USE_PLATFORM_DISPLAY_KHR)
|
||||
if (wstype == WindowSystemType::DRM)
|
||||
{
|
||||
if (!AddExtension(VK_KHR_DISPLAY_EXTENSION_NAME, true))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!AddExtension(VK_KHR_SURFACE_EXTENSION_NAME, true))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if defined(VK_USE_PLATFORM_ANDROID_KHR)
|
||||
if (wstype == WindowSystemType::Android &&
|
||||
!AddExtension(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME, true))
|
||||
|
|
|
@ -49,6 +49,15 @@ VULKAN_INSTANCE_ENTRY_POINT(vkCreateXlibSurfaceKHR, false)
|
|||
VULKAN_INSTANCE_ENTRY_POINT(vkGetPhysicalDeviceXlibPresentationSupportKHR, false)
|
||||
#endif
|
||||
|
||||
#if defined(VK_USE_PLATFORM_DISPLAY_KHR)
|
||||
VULKAN_INSTANCE_ENTRY_POINT(vkCreateDisplayPlaneSurfaceKHR, false)
|
||||
VULKAN_INSTANCE_ENTRY_POINT(vkGetDisplayPlaneCapabilitiesKHR, false)
|
||||
VULKAN_INSTANCE_ENTRY_POINT(vkGetPhysicalDeviceDisplayPlanePropertiesKHR, false)
|
||||
VULKAN_INSTANCE_ENTRY_POINT(vkGetDisplayPlaneSupportedDisplaysKHR, false)
|
||||
VULKAN_INSTANCE_ENTRY_POINT(vkGetDisplayModePropertiesKHR, false)
|
||||
VULKAN_INSTANCE_ENTRY_POINT(vkGetPhysicalDeviceDisplayPropertiesKHR, false)
|
||||
#endif
|
||||
|
||||
#if defined(VK_USE_PLATFORM_ANDROID_KHR)
|
||||
VULKAN_INSTANCE_ENTRY_POINT(vkCreateAndroidSurfaceKHR, false)
|
||||
#endif
|
||||
|
|
|
@ -36,6 +36,7 @@ static void ResetVulkanLibraryFunctionPointers()
|
|||
#define VULKAN_INSTANCE_ENTRY_POINT(name, required) name = nullptr;
|
||||
#define VULKAN_DEVICE_ENTRY_POINT(name, required) name = nullptr;
|
||||
#include "VideoBackends/Vulkan/VulkanEntryPoints.inl"
|
||||
|
||||
#undef VULKAN_DEVICE_ENTRY_POINT
|
||||
#undef VULKAN_INSTANCE_ENTRY_POINT
|
||||
#undef VULKAN_MODULE_ENTRY_POINT
|
||||
|
@ -108,6 +109,7 @@ bool LoadVulkanLibrary(bool force_system_library)
|
|||
return false; \
|
||||
}
|
||||
#include "VideoBackends/Vulkan/VulkanEntryPoints.inl"
|
||||
|
||||
#undef VULKAN_MODULE_ENTRY_POINT
|
||||
|
||||
return true;
|
||||
|
@ -135,6 +137,7 @@ bool LoadVulkanInstanceFunctions(VkInstance instance)
|
|||
#define VULKAN_INSTANCE_ENTRY_POINT(name, required) \
|
||||
LoadFunction(reinterpret_cast<PFN_vkVoidFunction*>(&name), #name, required);
|
||||
#include "VideoBackends/Vulkan/VulkanEntryPoints.inl"
|
||||
|
||||
#undef VULKAN_INSTANCE_ENTRY_POINT
|
||||
|
||||
return !required_functions_missing;
|
||||
|
@ -155,6 +158,7 @@ bool LoadVulkanDeviceFunctions(VkDevice device)
|
|||
#define VULKAN_DEVICE_ENTRY_POINT(name, required) \
|
||||
LoadFunction(reinterpret_cast<PFN_vkVoidFunction*>(&name), #name, required);
|
||||
#include "VideoBackends/Vulkan/VulkanEntryPoints.inl"
|
||||
|
||||
#undef VULKAN_DEVICE_ENTRY_POINT
|
||||
|
||||
return !required_functions_missing;
|
||||
|
|
|
@ -9,6 +9,11 @@
|
|||
#define VK_USE_PLATFORM_WIN32_KHR
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_DRM)
|
||||
#define VK_USE_PLATFORM_DISPLAY_KHR
|
||||
// #define VK_KHR_display
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_X11)
|
||||
#define VK_USE_PLATFORM_XLIB_KHR
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue