Skip to content

Commit fca7f5d

Browse files
authored
Linux: Add Vulkan support for wayland (#553)
1 parent 2c81d24 commit fca7f5d

File tree

10 files changed

+178
-17
lines changed

10 files changed

+178
-17
lines changed

src/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ elseif(UNIX)
2727
add_compile_definitions(
2828
VK_USE_PLATFORM_XLIB_KHR # legacy. Do we need to support XLIB surfaces?
2929
VK_USE_PLATFORM_XCB_KHR
30+
VK_USE_PLATFORM_WAYLAND_KHR
3031
)
3132
endif()
3233
add_compile_options(-maes)

src/Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ VKFUNC_DEVICE(vkCmdBindPipeline);
129129
#if BOOST_OS_LINUX
130130
VKFUNC_INSTANCE(vkCreateXlibSurfaceKHR);
131131
VKFUNC_INSTANCE(vkCreateXcbSurfaceKHR);
132+
VKFUNC_INSTANCE(vkCreateWaylandSurfaceKHR);
132133
#endif
133134

134135
#if BOOST_OS_WINDOWS

src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,11 @@ std::vector<VulkanRenderer::DeviceInfo> VulkanRenderer::GetDevices()
107107
#if BOOST_OS_WINDOWS
108108
requiredExtensions.emplace_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME);
109109
#elif BOOST_OS_LINUX
110-
requiredExtensions.emplace_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME);
110+
auto backend = gui_getWindowInfo().window_main.backend;
111+
if(backend == WindowHandleInfo::Backend::X11)
112+
requiredExtensions.emplace_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME);
113+
else if (backend == WindowHandleInfo::Backend::WAYLAND)
114+
requiredExtensions.emplace_back(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME);
111115
#elif BOOST_OS_MACOS
112116
requiredExtensions.emplace_back(VK_EXT_METAL_SURFACE_EXTENSION_NAME);
113117
#endif
@@ -1149,7 +1153,11 @@ std::vector<const char*> VulkanRenderer::CheckInstanceExtensionSupport(FeatureCo
11491153
#if BOOST_OS_WINDOWS
11501154
requiredInstanceExtensions.emplace_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME);
11511155
#elif BOOST_OS_LINUX
1152-
requiredInstanceExtensions.emplace_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME);
1156+
auto backend = gui_getWindowInfo().window_main.backend;
1157+
if(backend == WindowHandleInfo::Backend::X11)
1158+
requiredInstanceExtensions.emplace_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME);
1159+
else if (backend == WindowHandleInfo::Backend::WAYLAND)
1160+
requiredInstanceExtensions.emplace_back(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME);
11531161
#elif BOOST_OS_MACOS
11541162
requiredInstanceExtensions.emplace_back(VK_EXT_METAL_SURFACE_EXTENSION_NAME);
11551163
#endif
@@ -1267,14 +1275,37 @@ VkSurfaceKHR VulkanRenderer::CreateXcbSurface(VkInstance instance, xcb_connectio
12671275

12681276
return result;
12691277
}
1278+
1279+
VkSurfaceKHR VulkanRenderer::CreateWaylandSurface(VkInstance instance, wl_display* display, wl_surface* surface)
1280+
{
1281+
VkWaylandSurfaceCreateInfoKHR sci{};
1282+
sci.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR;
1283+
sci.flags = 0;
1284+
sci.display = display;
1285+
sci.surface = surface;
1286+
1287+
VkSurfaceKHR result;
1288+
VkResult err;
1289+
if ((err = vkCreateWaylandSurfaceKHR(instance, &sci, nullptr, &result)) != VK_SUCCESS)
1290+
{
1291+
forceLog_printf("Cannot create a Wayland Vulkan surface: %d", (sint32)err);
1292+
throw std::runtime_error(fmt::format("Cannot create a Wayland Vulkan surface: {}", err));
1293+
}
1294+
1295+
return result;
1296+
}
12701297
#endif
12711298

12721299
VkSurfaceKHR VulkanRenderer::CreateFramebufferSurface(VkInstance instance, struct WindowHandleInfo& windowInfo)
12731300
{
12741301
#if BOOST_OS_WINDOWS
12751302
return CreateWinSurface(instance, windowInfo.hwnd);
12761303
#elif BOOST_OS_LINUX
1277-
return CreateXlibSurface(instance, windowInfo.xlib_display, windowInfo.xlib_window);
1304+
if(windowInfo.backend == WindowHandleInfo::Backend::X11)
1305+
return CreateXlibSurface(instance, windowInfo.xlib_display, windowInfo.xlib_window);
1306+
if(windowInfo.backend == WindowHandleInfo::Backend::WAYLAND)
1307+
return CreateWaylandSurface(instance, windowInfo.display, windowInfo.surface);
1308+
return {};
12781309
#elif BOOST_OS_MACOS
12791310
return CreateCocoaSurface(instance, windowInfo.handle);
12801311
#endif
@@ -2596,6 +2627,15 @@ bool VulkanRenderer::UpdateSwapchainProperties(bool mainWindow)
25962627
if (chainInfo.m_usesSRGB != latteBufferUsesSRGB)
25972628
stateChanged = true;
25982629

2630+
int width, height;
2631+
if (mainWindow)
2632+
gui_getWindowSize(width, height);
2633+
else
2634+
gui_getPadWindowSize(width, height);
2635+
auto extent = chainInfo.getExtent();
2636+
if (width != extent.width || height != extent.height)
2637+
stateChanged = true;
2638+
25992639
if(stateChanged)
26002640
{
26012641
try

src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ class VulkanRenderer : public Renderer
201201
#if BOOST_OS_LINUX
202202
static VkSurfaceKHR CreateXlibSurface(VkInstance instance, Display* dpy, Window window);
203203
static VkSurfaceKHR CreateXcbSurface(VkInstance instance, xcb_connection_t* connection, xcb_window_t window);
204+
static VkSurfaceKHR CreateWaylandSurface(VkInstance instance, wl_display* display, wl_surface* surface);
204205
#endif
205206

206207
static VkSurfaceKHR CreateFramebufferSurface(VkInstance instance, struct WindowHandleInfo& windowInfo);

src/gui/PadViewFrame.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ void PadViewFrame::InitializeRenderCanvas()
8787
m_render_canvas->Bind(wxEVT_GESTURE_PAN, &PadViewFrame::OnGesturePan, this);
8888

8989
m_render_canvas->SetFocus();
90+
SendSizeEvent();
9091
}
9192

9293
void PadViewFrame::OnSizeEvent(wxSizeEvent& event)

src/gui/canvas/VulkanCanvas.cpp

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,17 @@ VulkanCanvas::VulkanCanvas(wxWindow* parent, const wxSize& size, bool is_main_wi
1111
Bind(wxEVT_SIZE, &VulkanCanvas::OnResize, this);
1212

1313
if(is_main_window)
14-
gui_initHandleContextFromWxWidgetsWindow(gui_getWindowInfo().canvas_main, this);
14+
{
15+
WindowHandleInfo& canvasMain = gui_getWindowInfo().canvas_main;
16+
gui_initHandleContextFromWxWidgetsWindow(canvasMain, this);
17+
#if BOOST_OS_LINUX
18+
if(canvasMain.backend == WindowHandleInfo::Backend::WAYLAND)
19+
{
20+
m_subsurface = std::make_unique<wxWlSubsurface>(this);
21+
canvasMain.surface = m_subsurface->getSurface();
22+
}
23+
#endif
24+
}
1525
else
1626
gui_initHandleContextFromWxWidgetsWindow(gui_getWindowInfo().canvas_pad, this);
1727

@@ -55,6 +65,14 @@ void VulkanCanvas::OnPaint(wxPaintEvent& event)
5565

5666
void VulkanCanvas::OnResize(wxSizeEvent& event)
5767
{
68+
#if BOOST_OS_LINUX
69+
if(m_subsurface)
70+
{
71+
int32_t x,y;
72+
GetScreenPosition(&x,&y);
73+
m_subsurface->setPosition(x, y);
74+
}
75+
#endif
5876
const wxSize size = GetSize();
5977
if (size.GetWidth() == 0 || size.GetHeight() == 0)
6078
return;

src/gui/canvas/VulkanCanvas.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,15 @@
66

77
#include "Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h"
88
#include <set>
9-
10-
9+
#if BOOST_OS_LINUX
10+
#include "gui/helpers/wxWayland.h"
11+
#endif
1112

1213
class VulkanCanvas : public IRenderCanvas, public wxWindow
1314
{
15+
#if BOOST_OS_LINUX
16+
std::unique_ptr<wxWlSubsurface> m_subsurface;
17+
#endif
1418
public:
1519
VulkanCanvas(wxWindow* parent, const wxSize& size, bool is_main_window);
1620
~VulkanCanvas();

src/gui/guiWrapper.cpp

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include <gdk/gdk.h>
44
#include <gdk/gdkwindow.h>
55
#include <gdk/gdkx.h>
6+
#include <gdk/gdkwayland.h>
67
#endif
78

89
#include "gui/wxgui.h"
@@ -204,19 +205,31 @@ void gui_initHandleContextFromWxWidgetsWindow(WindowHandleInfo& handleInfoOut, c
204205
#if BOOST_OS_WINDOWS
205206
handleInfoOut.hwnd = wxw->GetHWND();
206207
#elif BOOST_OS_LINUX
207-
// get window
208208
GtkWidget* gtkWidget = (GtkWidget*)wxw->GetHandle(); // returns GtkWidget
209209
gtk_widget_realize(gtkWidget);
210210
GdkWindow* gdkWindow = gtk_widget_get_window(gtkWidget);
211-
handleInfoOut.xlib_window = gdk_x11_window_get_xid(gdkWindow);
212-
213-
// get display
214-
GdkDisplay* gdkDisplay = gdk_window_get_display(gdkWindow);
215-
handleInfoOut.xlib_display = gdk_x11_display_get_xdisplay(gdkDisplay);
216-
if(!handleInfoOut.xlib_display)
217-
{
218-
cemuLog_log(LogType::Force, "Unable to get xlib display");
219-
}
211+
GdkDisplay* gdkDisplay = gdk_window_get_display(gdkWindow);
212+
if(GDK_IS_X11_WINDOW(gdkWindow))
213+
{
214+
handleInfoOut.backend = WindowHandleInfo::Backend::X11;
215+
handleInfoOut.xlib_window = gdk_x11_window_get_xid(gdkWindow);
216+
handleInfoOut.xlib_display = gdk_x11_display_get_xdisplay(gdkDisplay);
217+
if(!handleInfoOut.xlib_display)
218+
{
219+
cemuLog_log(LogType::Force, "Unable to get xlib display");
220+
}
221+
}
222+
else if(GDK_IS_WAYLAND_WINDOW(gdkWindow))
223+
{
224+
handleInfoOut.backend = WindowHandleInfo::Backend::WAYLAND;
225+
handleInfoOut.surface = gdk_wayland_window_get_wl_surface(gdkWindow);
226+
handleInfoOut.display = gdk_wayland_display_get_wl_display(gdkDisplay);
227+
}
228+
else
229+
{
230+
cemuLog_log(LogType::Force, "Unsuported GTK backend");
231+
232+
}
220233
#else
221234
handleInfoOut.handle = wxw->GetHandle();
222235
#endif

src/gui/guiWrapper.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#if BOOST_OS_LINUX
66
#include "xcb/xproto.h"
77
#include <gdk/gdkkeysyms.h>
8+
#include <wayland-client.h>
89
#endif
910

1011
#if BOOST_OS_MACOS
@@ -16,6 +17,11 @@ struct WindowHandleInfo
1617
#if BOOST_OS_WINDOWS
1718
std::atomic<HWND> hwnd;
1819
#elif BOOST_OS_LINUX
20+
enum class Backend
21+
{
22+
X11,
23+
WAYLAND,
24+
} backend;
1925
// XLIB
2026
Display* xlib_display{};
2127
Window xlib_window{};
@@ -24,7 +30,8 @@ struct WindowHandleInfo
2430
//xcb_connection_t* xcb_con{};
2531
//xcb_window_t xcb_window{};
2632
// Wayland
27-
// todo
33+
wl_display* display;
34+
wl_surface* surface;
2835
#else
2936
void* handle;
3037
#endif

src/gui/helpers/wxWayland.h

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
#pragma once
2+
3+
#if BOOST_OS_LINUX
4+
5+
#include <gdk/gdk.h>
6+
#include <gdk/gdkwayland.h>
7+
#include <gtk/gtk.h>
8+
#include <wayland-client.h>
9+
#include <wx/wx.h>
10+
11+
class wxWlSubsurface
12+
{
13+
static void registry_add_object(void* data, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t version)
14+
{
15+
if (!strcmp(interface, wl_subcompositor_interface.name))
16+
{
17+
auto wlSubsurface = static_cast<wxWlSubsurface*>(data);
18+
wlSubsurface->m_subcompositor = static_cast<wl_subcompositor*>(wl_registry_bind(registry, name, &wl_subcompositor_interface, 1));
19+
}
20+
}
21+
static void registry_remove_object(void* /*data*/, struct wl_registry* /*registry*/, uint32_t /*name*/) {}
22+
23+
wl_registry_listener m_registry_listener = {&registry_add_object, &registry_remove_object};
24+
wl_subcompositor* m_subcompositor;
25+
wl_surface* m_surface;
26+
wl_subsurface* m_subsurface;
27+
int32_t m_xPos = 0;
28+
int32_t m_yPos = 0;
29+
30+
public:
31+
wxWlSubsurface(wxWindow* window)
32+
{
33+
GtkWidget* widget = static_cast<GtkWidget*>(window->GetHandle());
34+
gtk_widget_realize(widget);
35+
GdkWindow* gdkWindow = gtk_widget_get_window(widget);
36+
GdkDisplay* gdkDisplay = gdk_window_get_display(gdkWindow);
37+
wl_display* display = gdk_wayland_display_get_wl_display(gdkDisplay);
38+
wl_surface* surface = gdk_wayland_window_get_wl_surface(gdkWindow);
39+
wl_compositor* compositor = gdk_wayland_display_get_wl_compositor(gdkDisplay);
40+
wl_registry* registry = wl_display_get_registry(display);
41+
wl_registry_add_listener(registry, &m_registry_listener, this);
42+
wl_display_roundtrip(display);
43+
m_surface = wl_compositor_create_surface(compositor);
44+
wl_region* region = wl_compositor_create_region(compositor);
45+
wl_surface_set_input_region(m_surface, region);
46+
m_subsurface = wl_subcompositor_get_subsurface(m_subcompositor, m_surface, surface);
47+
wl_subsurface_set_desync(m_subsurface);
48+
window->GetScreenPosition(&m_xPos, &m_yPos);
49+
wl_subsurface_set_position(m_subsurface, m_xPos, m_yPos);
50+
wl_surface_commit(m_surface);
51+
wl_region_destroy(region);
52+
}
53+
54+
wl_surface* getSurface() const { return m_surface; }
55+
56+
void setPosition(int32_t xPos, int32_t yPos)
57+
{
58+
if (xPos != m_xPos || m_yPos != yPos)
59+
{
60+
m_xPos = xPos;
61+
m_yPos = yPos;
62+
wl_subsurface_set_position(m_subsurface, m_xPos, m_yPos);
63+
wl_surface_commit(m_surface);
64+
}
65+
}
66+
67+
~wxWlSubsurface()
68+
{
69+
wl_subsurface_destroy(m_subsurface);
70+
wl_surface_destroy(m_surface);
71+
wl_subcompositor_destroy(m_subcompositor);
72+
}
73+
};
74+
75+
#endif // BOOST_OS_LINUX

0 commit comments

Comments
 (0)