Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vulkan DRM/KMS support #13222

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions Source/Core/Common/WindowSystemInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ enum class WindowSystemType
Wayland,
FBDev,
Haiku,
DRM,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure why this is highlighted in blue, its not a C++ keyword. Anyone know?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably just a syntax highlighting glitch

};

struct WindowSystemInfo
Expand Down
4 changes: 4 additions & 0 deletions Source/Core/DolphinNoGUI/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 9 additions & 0 deletions Source/Core/DolphinNoGUI/MainNoGUI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -215,6 +220,10 @@ int main(int argc, char* argv[])
,
"fbdev"
#endif
#if HAVE_DRM
,
"drm"
#endif
#if HAVE_X11
,
"x11"
Expand Down
4 changes: 4 additions & 0 deletions Source/Core/DolphinNoGUI/Platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ class Platform
// 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();
Expand Down
63 changes: 63 additions & 0 deletions Source/Core/DolphinNoGUI/PlatformDRM.cpp
Original file line number Diff line number Diff line change
@@ -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));
Comment on lines +44 to +45
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am interested to hear those more experienced with Dolphin who can weigh on for this.
Once finalized I can do a bunch of user testing of this branch.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just copied and pasted from a different platform

}
}

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>();
}
8 changes: 4 additions & 4 deletions Source/Core/VideoBackends/Vulkan/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Comment on lines -60 to +63
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't disagree with this formatting change, but since there are no other changes besides formatting I'm not sure it should be in this PR.
Nitpick though, leave it or not. I have no preference, but a graphics reviewer might.

)

if(MSVC)
Expand Down
20 changes: 10 additions & 10 deletions Source/Core/VideoBackends/Vulkan/VKMain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,11 +147,20 @@ bool VideoBackend::Initialize(const WindowSystemInfo& wsi)
VulkanContext::PopulateBackendInfo(&g_Config);
VulkanContext::PopulateBackendInfoAdapters(&g_Config, gpu_list);

// 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);
if (selected_adapter_index >= gpu_list.size())
{
WARN_LOG_FMT(VIDEO, "Vulkan adapter index out of range, selecting first adapter.");
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, wsi);
surface = SwapChain::CreateVulkanSurface(instance, gpu_list[selected_adapter_index], wsi);
if (surface == VK_NULL_HANDLE)
{
PanicAlertFmt("Failed to create Vulkan surface.");
Expand All @@ -161,15 +170,6 @@ bool VideoBackend::Initialize(const WindowSystemInfo& wsi)
}
}

// 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);
if (selected_adapter_index >= gpu_list.size())
{
WARN_LOG_FMT(VIDEO, "Vulkan adapter index out of range, selecting first adapter.");
selected_adapter_index = 0;
}

// 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,
Expand Down
170 changes: 168 additions & 2 deletions Source/Core/VideoBackends/Vulkan/VKSwapChain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,173 @@ 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
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)
{
Expand Down Expand Up @@ -583,7 +748,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;

Expand Down
3 changes: 2 additions & 1 deletion Source/Core/VideoBackends/Vulkan/VKSwapChain.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ class SwapChain
~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,
Expand Down
Loading