Linux: Add Vulkan support for wayland (#553)

This commit is contained in:
SSimco 2022-12-07 15:45:25 +00:00 committed by GitHub
parent 2c81d240a5
commit fca7f5dfe4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 177 additions and 16 deletions

View File

@ -27,6 +27,7 @@ elseif(UNIX)
add_compile_definitions( add_compile_definitions(
VK_USE_PLATFORM_XLIB_KHR # legacy. Do we need to support XLIB surfaces? VK_USE_PLATFORM_XLIB_KHR # legacy. Do we need to support XLIB surfaces?
VK_USE_PLATFORM_XCB_KHR VK_USE_PLATFORM_XCB_KHR
VK_USE_PLATFORM_WAYLAND_KHR
) )
endif() endif()
add_compile_options(-maes) add_compile_options(-maes)

View File

@ -129,6 +129,7 @@ VKFUNC_DEVICE(vkCmdBindPipeline);
#if BOOST_OS_LINUX #if BOOST_OS_LINUX
VKFUNC_INSTANCE(vkCreateXlibSurfaceKHR); VKFUNC_INSTANCE(vkCreateXlibSurfaceKHR);
VKFUNC_INSTANCE(vkCreateXcbSurfaceKHR); VKFUNC_INSTANCE(vkCreateXcbSurfaceKHR);
VKFUNC_INSTANCE(vkCreateWaylandSurfaceKHR);
#endif #endif
#if BOOST_OS_WINDOWS #if BOOST_OS_WINDOWS

View File

@ -107,7 +107,11 @@ std::vector<VulkanRenderer::DeviceInfo> VulkanRenderer::GetDevices()
#if BOOST_OS_WINDOWS #if BOOST_OS_WINDOWS
requiredExtensions.emplace_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME); requiredExtensions.emplace_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME);
#elif BOOST_OS_LINUX #elif BOOST_OS_LINUX
auto backend = gui_getWindowInfo().window_main.backend;
if(backend == WindowHandleInfo::Backend::X11)
requiredExtensions.emplace_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME); requiredExtensions.emplace_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME);
else if (backend == WindowHandleInfo::Backend::WAYLAND)
requiredExtensions.emplace_back(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME);
#elif BOOST_OS_MACOS #elif BOOST_OS_MACOS
requiredExtensions.emplace_back(VK_EXT_METAL_SURFACE_EXTENSION_NAME); requiredExtensions.emplace_back(VK_EXT_METAL_SURFACE_EXTENSION_NAME);
#endif #endif
@ -1149,7 +1153,11 @@ std::vector<const char*> VulkanRenderer::CheckInstanceExtensionSupport(FeatureCo
#if BOOST_OS_WINDOWS #if BOOST_OS_WINDOWS
requiredInstanceExtensions.emplace_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME); requiredInstanceExtensions.emplace_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME);
#elif BOOST_OS_LINUX #elif BOOST_OS_LINUX
auto backend = gui_getWindowInfo().window_main.backend;
if(backend == WindowHandleInfo::Backend::X11)
requiredInstanceExtensions.emplace_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME); requiredInstanceExtensions.emplace_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME);
else if (backend == WindowHandleInfo::Backend::WAYLAND)
requiredInstanceExtensions.emplace_back(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME);
#elif BOOST_OS_MACOS #elif BOOST_OS_MACOS
requiredInstanceExtensions.emplace_back(VK_EXT_METAL_SURFACE_EXTENSION_NAME); requiredInstanceExtensions.emplace_back(VK_EXT_METAL_SURFACE_EXTENSION_NAME);
#endif #endif
@ -1267,6 +1275,25 @@ VkSurfaceKHR VulkanRenderer::CreateXcbSurface(VkInstance instance, xcb_connectio
return result; return result;
} }
VkSurfaceKHR VulkanRenderer::CreateWaylandSurface(VkInstance instance, wl_display* display, wl_surface* surface)
{
VkWaylandSurfaceCreateInfoKHR sci{};
sci.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR;
sci.flags = 0;
sci.display = display;
sci.surface = surface;
VkSurfaceKHR result;
VkResult err;
if ((err = vkCreateWaylandSurfaceKHR(instance, &sci, nullptr, &result)) != VK_SUCCESS)
{
forceLog_printf("Cannot create a Wayland Vulkan surface: %d", (sint32)err);
throw std::runtime_error(fmt::format("Cannot create a Wayland Vulkan surface: {}", err));
}
return result;
}
#endif #endif
VkSurfaceKHR VulkanRenderer::CreateFramebufferSurface(VkInstance instance, struct WindowHandleInfo& windowInfo) VkSurfaceKHR VulkanRenderer::CreateFramebufferSurface(VkInstance instance, struct WindowHandleInfo& windowInfo)
@ -1274,7 +1301,11 @@ VkSurfaceKHR VulkanRenderer::CreateFramebufferSurface(VkInstance instance, struc
#if BOOST_OS_WINDOWS #if BOOST_OS_WINDOWS
return CreateWinSurface(instance, windowInfo.hwnd); return CreateWinSurface(instance, windowInfo.hwnd);
#elif BOOST_OS_LINUX #elif BOOST_OS_LINUX
if(windowInfo.backend == WindowHandleInfo::Backend::X11)
return CreateXlibSurface(instance, windowInfo.xlib_display, windowInfo.xlib_window); return CreateXlibSurface(instance, windowInfo.xlib_display, windowInfo.xlib_window);
if(windowInfo.backend == WindowHandleInfo::Backend::WAYLAND)
return CreateWaylandSurface(instance, windowInfo.display, windowInfo.surface);
return {};
#elif BOOST_OS_MACOS #elif BOOST_OS_MACOS
return CreateCocoaSurface(instance, windowInfo.handle); return CreateCocoaSurface(instance, windowInfo.handle);
#endif #endif
@ -2596,6 +2627,15 @@ bool VulkanRenderer::UpdateSwapchainProperties(bool mainWindow)
if (chainInfo.m_usesSRGB != latteBufferUsesSRGB) if (chainInfo.m_usesSRGB != latteBufferUsesSRGB)
stateChanged = true; stateChanged = true;
int width, height;
if (mainWindow)
gui_getWindowSize(width, height);
else
gui_getPadWindowSize(width, height);
auto extent = chainInfo.getExtent();
if (width != extent.width || height != extent.height)
stateChanged = true;
if(stateChanged) if(stateChanged)
{ {
try try

View File

@ -201,6 +201,7 @@ public:
#if BOOST_OS_LINUX #if BOOST_OS_LINUX
static VkSurfaceKHR CreateXlibSurface(VkInstance instance, Display* dpy, Window window); static VkSurfaceKHR CreateXlibSurface(VkInstance instance, Display* dpy, Window window);
static VkSurfaceKHR CreateXcbSurface(VkInstance instance, xcb_connection_t* connection, xcb_window_t window); static VkSurfaceKHR CreateXcbSurface(VkInstance instance, xcb_connection_t* connection, xcb_window_t window);
static VkSurfaceKHR CreateWaylandSurface(VkInstance instance, wl_display* display, wl_surface* surface);
#endif #endif
static VkSurfaceKHR CreateFramebufferSurface(VkInstance instance, struct WindowHandleInfo& windowInfo); static VkSurfaceKHR CreateFramebufferSurface(VkInstance instance, struct WindowHandleInfo& windowInfo);

View File

@ -87,6 +87,7 @@ void PadViewFrame::InitializeRenderCanvas()
m_render_canvas->Bind(wxEVT_GESTURE_PAN, &PadViewFrame::OnGesturePan, this); m_render_canvas->Bind(wxEVT_GESTURE_PAN, &PadViewFrame::OnGesturePan, this);
m_render_canvas->SetFocus(); m_render_canvas->SetFocus();
SendSizeEvent();
} }
void PadViewFrame::OnSizeEvent(wxSizeEvent& event) void PadViewFrame::OnSizeEvent(wxSizeEvent& event)

View File

@ -11,7 +11,17 @@ VulkanCanvas::VulkanCanvas(wxWindow* parent, const wxSize& size, bool is_main_wi
Bind(wxEVT_SIZE, &VulkanCanvas::OnResize, this); Bind(wxEVT_SIZE, &VulkanCanvas::OnResize, this);
if(is_main_window) if(is_main_window)
gui_initHandleContextFromWxWidgetsWindow(gui_getWindowInfo().canvas_main, this); {
WindowHandleInfo& canvasMain = gui_getWindowInfo().canvas_main;
gui_initHandleContextFromWxWidgetsWindow(canvasMain, this);
#if BOOST_OS_LINUX
if(canvasMain.backend == WindowHandleInfo::Backend::WAYLAND)
{
m_subsurface = std::make_unique<wxWlSubsurface>(this);
canvasMain.surface = m_subsurface->getSurface();
}
#endif
}
else else
gui_initHandleContextFromWxWidgetsWindow(gui_getWindowInfo().canvas_pad, this); gui_initHandleContextFromWxWidgetsWindow(gui_getWindowInfo().canvas_pad, this);
@ -55,6 +65,14 @@ void VulkanCanvas::OnPaint(wxPaintEvent& event)
void VulkanCanvas::OnResize(wxSizeEvent& event) void VulkanCanvas::OnResize(wxSizeEvent& event)
{ {
#if BOOST_OS_LINUX
if(m_subsurface)
{
int32_t x,y;
GetScreenPosition(&x,&y);
m_subsurface->setPosition(x, y);
}
#endif
const wxSize size = GetSize(); const wxSize size = GetSize();
if (size.GetWidth() == 0 || size.GetHeight() == 0) if (size.GetWidth() == 0 || size.GetHeight() == 0)
return; return;

View File

@ -6,11 +6,15 @@
#include "Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h" #include "Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h"
#include <set> #include <set>
#if BOOST_OS_LINUX
#include "gui/helpers/wxWayland.h"
#endif
class VulkanCanvas : public IRenderCanvas, public wxWindow class VulkanCanvas : public IRenderCanvas, public wxWindow
{ {
#if BOOST_OS_LINUX
std::unique_ptr<wxWlSubsurface> m_subsurface;
#endif
public: public:
VulkanCanvas(wxWindow* parent, const wxSize& size, bool is_main_window); VulkanCanvas(wxWindow* parent, const wxSize& size, bool is_main_window);
~VulkanCanvas(); ~VulkanCanvas();

View File

@ -3,6 +3,7 @@
#include <gdk/gdk.h> #include <gdk/gdk.h>
#include <gdk/gdkwindow.h> #include <gdk/gdkwindow.h>
#include <gdk/gdkx.h> #include <gdk/gdkx.h>
#include <gdk/gdkwayland.h>
#endif #endif
#include "gui/wxgui.h" #include "gui/wxgui.h"
@ -204,19 +205,31 @@ void gui_initHandleContextFromWxWidgetsWindow(WindowHandleInfo& handleInfoOut, c
#if BOOST_OS_WINDOWS #if BOOST_OS_WINDOWS
handleInfoOut.hwnd = wxw->GetHWND(); handleInfoOut.hwnd = wxw->GetHWND();
#elif BOOST_OS_LINUX #elif BOOST_OS_LINUX
// get window
GtkWidget* gtkWidget = (GtkWidget*)wxw->GetHandle(); // returns GtkWidget GtkWidget* gtkWidget = (GtkWidget*)wxw->GetHandle(); // returns GtkWidget
gtk_widget_realize(gtkWidget); gtk_widget_realize(gtkWidget);
GdkWindow* gdkWindow = gtk_widget_get_window(gtkWidget); GdkWindow* gdkWindow = gtk_widget_get_window(gtkWidget);
handleInfoOut.xlib_window = gdk_x11_window_get_xid(gdkWindow);
// get display
GdkDisplay* gdkDisplay = gdk_window_get_display(gdkWindow); GdkDisplay* gdkDisplay = gdk_window_get_display(gdkWindow);
if(GDK_IS_X11_WINDOW(gdkWindow))
{
handleInfoOut.backend = WindowHandleInfo::Backend::X11;
handleInfoOut.xlib_window = gdk_x11_window_get_xid(gdkWindow);
handleInfoOut.xlib_display = gdk_x11_display_get_xdisplay(gdkDisplay); handleInfoOut.xlib_display = gdk_x11_display_get_xdisplay(gdkDisplay);
if(!handleInfoOut.xlib_display) if(!handleInfoOut.xlib_display)
{ {
cemuLog_log(LogType::Force, "Unable to get xlib display"); cemuLog_log(LogType::Force, "Unable to get xlib display");
} }
}
else if(GDK_IS_WAYLAND_WINDOW(gdkWindow))
{
handleInfoOut.backend = WindowHandleInfo::Backend::WAYLAND;
handleInfoOut.surface = gdk_wayland_window_get_wl_surface(gdkWindow);
handleInfoOut.display = gdk_wayland_display_get_wl_display(gdkDisplay);
}
else
{
cemuLog_log(LogType::Force, "Unsuported GTK backend");
}
#else #else
handleInfoOut.handle = wxw->GetHandle(); handleInfoOut.handle = wxw->GetHandle();
#endif #endif

View File

@ -5,6 +5,7 @@
#if BOOST_OS_LINUX #if BOOST_OS_LINUX
#include "xcb/xproto.h" #include "xcb/xproto.h"
#include <gdk/gdkkeysyms.h> #include <gdk/gdkkeysyms.h>
#include <wayland-client.h>
#endif #endif
#if BOOST_OS_MACOS #if BOOST_OS_MACOS
@ -16,6 +17,11 @@ struct WindowHandleInfo
#if BOOST_OS_WINDOWS #if BOOST_OS_WINDOWS
std::atomic<HWND> hwnd; std::atomic<HWND> hwnd;
#elif BOOST_OS_LINUX #elif BOOST_OS_LINUX
enum class Backend
{
X11,
WAYLAND,
} backend;
// XLIB // XLIB
Display* xlib_display{}; Display* xlib_display{};
Window xlib_window{}; Window xlib_window{};
@ -24,7 +30,8 @@ struct WindowHandleInfo
//xcb_connection_t* xcb_con{}; //xcb_connection_t* xcb_con{};
//xcb_window_t xcb_window{}; //xcb_window_t xcb_window{};
// Wayland // Wayland
// todo wl_display* display;
wl_surface* surface;
#else #else
void* handle; void* handle;
#endif #endif

View File

@ -0,0 +1,75 @@
#pragma once
#if BOOST_OS_LINUX
#include <gdk/gdk.h>
#include <gdk/gdkwayland.h>
#include <gtk/gtk.h>
#include <wayland-client.h>
#include <wx/wx.h>
class wxWlSubsurface
{
static void registry_add_object(void* data, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t version)
{
if (!strcmp(interface, wl_subcompositor_interface.name))
{
auto wlSubsurface = static_cast<wxWlSubsurface*>(data);
wlSubsurface->m_subcompositor = static_cast<wl_subcompositor*>(wl_registry_bind(registry, name, &wl_subcompositor_interface, 1));
}
}
static void registry_remove_object(void* /*data*/, struct wl_registry* /*registry*/, uint32_t /*name*/) {}
wl_registry_listener m_registry_listener = {&registry_add_object, &registry_remove_object};
wl_subcompositor* m_subcompositor;
wl_surface* m_surface;
wl_subsurface* m_subsurface;
int32_t m_xPos = 0;
int32_t m_yPos = 0;
public:
wxWlSubsurface(wxWindow* window)
{
GtkWidget* widget = static_cast<GtkWidget*>(window->GetHandle());
gtk_widget_realize(widget);
GdkWindow* gdkWindow = gtk_widget_get_window(widget);
GdkDisplay* gdkDisplay = gdk_window_get_display(gdkWindow);
wl_display* display = gdk_wayland_display_get_wl_display(gdkDisplay);
wl_surface* surface = gdk_wayland_window_get_wl_surface(gdkWindow);
wl_compositor* compositor = gdk_wayland_display_get_wl_compositor(gdkDisplay);
wl_registry* registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &m_registry_listener, this);
wl_display_roundtrip(display);
m_surface = wl_compositor_create_surface(compositor);
wl_region* region = wl_compositor_create_region(compositor);
wl_surface_set_input_region(m_surface, region);
m_subsurface = wl_subcompositor_get_subsurface(m_subcompositor, m_surface, surface);
wl_subsurface_set_desync(m_subsurface);
window->GetScreenPosition(&m_xPos, &m_yPos);
wl_subsurface_set_position(m_subsurface, m_xPos, m_yPos);
wl_surface_commit(m_surface);
wl_region_destroy(region);
}
wl_surface* getSurface() const { return m_surface; }
void setPosition(int32_t xPos, int32_t yPos)
{
if (xPos != m_xPos || m_yPos != yPos)
{
m_xPos = xPos;
m_yPos = yPos;
wl_subsurface_set_position(m_subsurface, m_xPos, m_yPos);
wl_surface_commit(m_surface);
}
}
~wxWlSubsurface()
{
wl_subsurface_destroy(m_subsurface);
wl_surface_destroy(m_surface);
wl_subcompositor_destroy(m_subcompositor);
}
};
#endif // BOOST_OS_LINUX