diff --git a/CMake/FindWaylandProtocols.cmake b/CMake/FindWaylandProtocols.cmake new file mode 100644 index 0000000000..891903feaa --- /dev/null +++ b/CMake/FindWaylandProtocols.cmake @@ -0,0 +1,28 @@ +# from https://github.com/glfw/glfw/blob/master/CMake/modules/FindWaylandProtocols.cmake + +find_package(PkgConfig) + +pkg_check_modules(WaylandProtocols QUIET wayland-protocols>=${WaylandProtocols_FIND_VERSION}) + +execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE} --variable=pkgdatadir wayland-protocols + OUTPUT_VARIABLE WaylandProtocols_PKGDATADIR + RESULT_VARIABLE _pkgconfig_failed) +if (_pkgconfig_failed) + message(FATAL_ERROR "Missing wayland-protocols pkgdatadir") +endif() + +string(REGEX REPLACE "[\r\n]" "" WaylandProtocols_PKGDATADIR "${WaylandProtocols_PKGDATADIR}") + +find_package_handle_standard_args(WaylandProtocols + FOUND_VAR + WaylandProtocols_FOUND + REQUIRED_VARS + WaylandProtocols_PKGDATADIR + VERSION_VAR + WaylandProtocols_VERSION + HANDLE_COMPONENTS +) + +set(WAYLAND_PROTOCOLS_FOUND ${WaylandProtocols_FOUND}) +set(WAYLAND_PROTOCOLS_PKGDATADIR ${WaylandProtocols_PKGDATADIR}) +set(WAYLAND_PROTOCOLS_VERSION ${WaylandProtocols_VERSION}) diff --git a/CMake/FindXKBCommon.cmake b/CMake/FindXKBCommon.cmake new file mode 100644 index 0000000000..3cdb40b82d --- /dev/null +++ b/CMake/FindXKBCommon.cmake @@ -0,0 +1,33 @@ +# - Try to find XKBCommon +# Once done, this will define +# +# XKBCOMMON_FOUND - System has XKBCommon +# XKBCOMMON_INCLUDE_DIRS - The XKBCommon include directories +# XKBCOMMON_LIBRARIES - The libraries needed to use XKBCommon +# XKBCOMMON_DEFINITIONS - Compiler switches required for using XKBCommon + +find_package(PkgConfig) +pkg_check_modules(PC_XKBCOMMON QUIET xkbcommon) +set(XKBCOMMON_DEFINITIONS ${PC_XKBCOMMON_CFLAGS_OTHER}) + +find_path(XKBCOMMON_INCLUDE_DIR + NAMES xkbcommon/xkbcommon.h + HINTS ${PC_XKBCOMMON_INCLUDE_DIR} ${PC_XKBCOMMON_INCLUDE_DIRS} +) + +find_library(XKBCOMMON_LIBRARY + NAMES xkbcommon + HINTS ${PC_XKBCOMMON_LIBRARY} ${PC_XKBCOMMON_LIBRARY_DIRS} +) + +set(XKBCOMMON_LIBRARIES ${XKBCOMMON_LIBRARY}) +set(XKBCOMMON_LIBRARY_DIRS ${XKBCOMMON_LIBRARY_DIRS}) +set(XKBCOMMON_INCLUDE_DIRS ${XKBCOMMON_INCLUDE_DIR}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(XKBCommon DEFAULT_MSG + XKBCOMMON_LIBRARY + XKBCOMMON_INCLUDE_DIR +) + +mark_as_advanced(XKBCOMMON_LIBRARY XKBCOMMON_INCLUDE_DIR) diff --git a/CMakeLists.txt b/CMakeLists.txt index 04bcdde4c2..7d7892f47c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,6 +43,7 @@ set(DOLPHIN_DEFAULT_UPDATE_TRACK "" CACHE STRING "Name of the default update tra if(UNIX AND NOT APPLE AND NOT ANDROID) option(ENABLE_X11 "Enables X11 Support" ON) + option(ENABLE_WAYLAND "Enables Wayland Support" OFF) endif() if(NOT WIN32 AND NOT APPLE AND NOT HAIKU) option(ENABLE_EGL "Enables EGL OpenGL Interface" ON) @@ -549,6 +550,17 @@ if(ENABLE_X11) endif() endif() +if(ENABLE_WAYLAND) + find_package(ECM REQUIRED NO_MODULE) + list(APPEND CMAKE_MODULE_PATH "${ECM_MODULE_PATH}") + find_package(Wayland REQUIRED Client Egl) + find_package(WaylandScanner REQUIRED) + find_package(WaylandProtocols 1.15 REQUIRED) + find_package(XKBCommon REQUIRED) + add_definitions(-DHAVE_WAYLAND=1) + message(STATUS "Wayland support enabled") +endif() + if(ENABLE_EGL) find_package(EGL) if(EGL_FOUND) diff --git a/Source/Core/Common/CMakeLists.txt b/Source/Core/Common/CMakeLists.txt index 4dc764408d..98951537fe 100644 --- a/Source/Core/Common/CMakeLists.txt +++ b/Source/Core/Common/CMakeLists.txt @@ -261,11 +261,20 @@ if(ENABLE_EGL AND EGL_FOUND) GL/GLInterface/EGLAndroid.cpp GL/GLInterface/EGLAndroid.h ) - elseif(ENABLE_X11 AND X11_FOUND) - target_sources(common PRIVATE - GL/GLInterface/EGLX11.cpp - GL/GLInterface/EGLX11.h - ) + else() + if(ENABLE_X11 AND X11_FOUND) + target_sources(common PRIVATE + GL/GLInterface/EGLX11.cpp + GL/GLInterface/EGLX11.h + ) + endif() + if(ENABLE_WAYLAND AND WAYLAND_FOUND) + target_sources(common PRIVATE + GL/GLInterface/EGLWayland.cpp + GL/GLInterface/EGLWayland.h + ) + target_link_libraries(common PRIVATE Wayland::Egl) + endif() endif() target_include_directories(common PRIVATE ${EGL_INCLUDE_DIRS}) target_link_libraries(common PUBLIC ${EGL_LIBRARIES}) diff --git a/Source/Core/Common/GL/GLContext.cpp b/Source/Core/Common/GL/GLContext.cpp index f7b6ca8af8..7d07303617 100644 --- a/Source/Core/Common/GL/GLContext.cpp +++ b/Source/Core/Common/GL/GLContext.cpp @@ -25,6 +25,9 @@ #if defined(ANDROID) #include "Common/GL/GLInterface/EGLAndroid.h" #endif +#if HAVE_WAYLAND +#include "Common/GL/GLInterface/EGLWayland.h" +#endif #endif const std::array, 9> GLContext::s_desktop_opengl_versions = { @@ -57,11 +60,11 @@ bool GLContext::ClearCurrent() return false; } -void GLContext::Update() +void GLContext::UpdateDimensions(int window_width, int window_height) { } -void GLContext::UpdateSurface(void* window_handle) +void GLContext::UpdateSurface(void* window_handle, int window_width, int window_height) { } @@ -113,6 +116,10 @@ std::unique_ptr GLContext::Create(const WindowSystemInfo& wsi, bool s #endif } #endif +#if HAVE_WAYLAND + if (wsi.type == WindowSystemType::Wayland) + context = std::make_unique(); +#endif #if HAVE_EGL if (wsi.type == WindowSystemType::Headless || wsi.type == WindowSystemType::FBDev) context = std::make_unique(); diff --git a/Source/Core/Common/GL/GLContext.h b/Source/Core/Common/GL/GLContext.h index a1d0a931bf..7ccd68e156 100644 --- a/Source/Core/Common/GL/GLContext.h +++ b/Source/Core/Common/GL/GLContext.h @@ -36,8 +36,8 @@ public: virtual bool MakeCurrent(); virtual bool ClearCurrent(); - virtual void Update(); - virtual void UpdateSurface(void* window_handle); + virtual void UpdateDimensions(int window_width, int window_height); + virtual void UpdateSurface(void* window_handle, int window_width, int window_height); virtual void Swap(); virtual void SwapInterval(int interval); diff --git a/Source/Core/Common/GL/GLInterface/AGL.h b/Source/Core/Common/GL/GLInterface/AGL.h index dda9fee0e3..dd135087c3 100644 --- a/Source/Core/Common/GL/GLInterface/AGL.h +++ b/Source/Core/Common/GL/GLInterface/AGL.h @@ -27,6 +27,8 @@ public: bool MakeCurrent() override; bool ClearCurrent() override; + + void UpdateDimensions(int window_width, int window_height) override; void Update() override; diff --git a/Source/Core/Common/GL/GLInterface/AGL.mm b/Source/Core/Common/GL/GLInterface/AGL.mm index a6fc7f0c0e..b0358a17dd 100644 --- a/Source/Core/Common/GL/GLInterface/AGL.mm +++ b/Source/Core/Common/GL/GLInterface/AGL.mm @@ -144,7 +144,7 @@ bool GLContextAGL::ClearCurrent() return true; } -void GLContextAGL::Update() +void GLContextAGL::UpdateDimensions(int window_width, int window_height) { if (!m_view) return; diff --git a/Source/Core/Common/GL/GLInterface/EGL.cpp b/Source/Core/Common/GL/GLInterface/EGL.cpp index 30230e7d08..8fc9b339fb 100644 --- a/Source/Core/Common/GL/GLInterface/EGL.cpp +++ b/Source/Core/Common/GL/GLInterface/EGL.cpp @@ -292,8 +292,8 @@ bool GLContextEGL::CreateWindowSurface() { if (!IsHeadless()) { - EGLNativeWindowType native_window = GetEGLNativeWindow(m_config); - m_egl_surface = eglCreateWindowSurface(m_egl_display, m_config, native_window, nullptr); + m_native_window = GetEGLNativeWindow(m_config); + m_egl_surface = eglCreateWindowSurface(m_egl_display, m_config, m_native_window, nullptr); if (!m_egl_surface) { INFO_LOG_FMT(VIDEO, "Error: eglCreateWindowSurface failed"); @@ -301,15 +301,7 @@ bool GLContextEGL::CreateWindowSurface() } // Get dimensions from the surface. - EGLint surface_width = 1, surface_height = 1; - if (!eglQuerySurface(m_egl_display, m_egl_surface, EGL_WIDTH, &surface_width) || - !eglQuerySurface(m_egl_display, m_egl_surface, EGL_HEIGHT, &surface_height)) - { - WARN_LOG_FMT(VIDEO, - "Failed to get surface dimensions via eglQuerySurface. Size may be incorrect."); - } - m_backbuffer_width = static_cast(surface_width); - m_backbuffer_height = static_cast(surface_height); + QueryDimensions(); } else if (!m_supports_surfaceless) { @@ -347,9 +339,16 @@ bool GLContextEGL::MakeCurrent() return eglMakeCurrent(m_egl_display, m_egl_surface, m_egl_surface, m_egl_context); } -void GLContextEGL::UpdateSurface(void* window_handle) +void GLContextEGL::UpdateDimensions(int window_width, int window_height) +{ + QueryDimensions(); +} + +void GLContextEGL::UpdateSurface(void* window_handle, int window_width, int window_height) { m_wsi.render_surface = window_handle; + m_wsi.render_surface_width = window_width; + m_wsi.render_surface_height = window_height; ClearCurrent(); DestroyWindowSurface(); CreateWindowSurface(); @@ -376,3 +375,15 @@ void GLContextEGL::DestroyContext() m_egl_context = EGL_NO_CONTEXT; m_egl_display = EGL_NO_DISPLAY; } + +void GLContextEGL::QueryDimensions() +{ + EGLint surface_width = 1, surface_height = 1; + if (!eglQuerySurface(m_egl_display, m_egl_surface, EGL_WIDTH, &surface_width) || + !eglQuerySurface(m_egl_display, m_egl_surface, EGL_HEIGHT, &surface_height)) + { + INFO_LOG_FMT(VIDEO, "Failed to get surface dimensions via eglQuerySurface. Size may be incorrect."); + } + m_backbuffer_width = static_cast(surface_width); + m_backbuffer_height = static_cast(surface_height); +} diff --git a/Source/Core/Common/GL/GLInterface/EGL.h b/Source/Core/Common/GL/GLInterface/EGL.h index bed1b31ecc..5029765265 100644 --- a/Source/Core/Common/GL/GLInterface/EGL.h +++ b/Source/Core/Common/GL/GLInterface/EGL.h @@ -22,7 +22,8 @@ public: bool MakeCurrent() override; bool ClearCurrent() override; - void UpdateSurface(void* window_handle) override; + void UpdateDimensions(int window_width, int window_height); + void UpdateSurface(void* window_handle, int window_width, int window_height) override; void Swap() override; void SwapInterval(int interval) override; @@ -39,9 +40,12 @@ protected: void DestroyWindowSurface(); void DetectMode(); void DestroyContext(); + void QueryDimensions(); WindowSystemInfo m_wsi = {}; + EGLNativeWindowType m_native_window = {}; + EGLConfig m_config; bool m_supports_surfaceless = false; std::vector m_attribs; diff --git a/Source/Core/Common/GL/GLInterface/EGLWayland.cpp b/Source/Core/Common/GL/GLInterface/EGLWayland.cpp new file mode 100644 index 0000000000..422c167a2e --- /dev/null +++ b/Source/Core/Common/GL/GLInterface/EGLWayland.cpp @@ -0,0 +1,36 @@ +// Copyright 2019 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "Common/GL/GLInterface/EGLWayland.h" +#include + +GLContextEGLWayland::~GLContextEGLWayland() +{ + if (m_native_window) + wl_egl_window_destroy(reinterpret_cast(m_native_window)); +} + +EGLDisplay GLContextEGLWayland::OpenEGLDisplay() +{ + return eglGetDisplay(reinterpret_cast(m_wsi.display_connection)); +} + +void GLContextEGLWayland::UpdateDimensions(int window_width, int window_height) +{ + wl_egl_window_resize(reinterpret_cast(m_native_window), window_width, + window_height, 0, 0); + m_backbuffer_width = window_width; + m_backbuffer_height = window_height; +} + +EGLNativeWindowType GLContextEGLWayland::GetEGLNativeWindow(EGLConfig config) +{ + wl_egl_window* window = + wl_egl_window_create(static_cast(m_wsi.render_surface), + m_wsi.render_surface_width, m_wsi.render_surface_height); + if (!window) + return {}; + + return reinterpret_cast(window); +} diff --git a/Source/Core/Common/GL/GLInterface/EGLWayland.h b/Source/Core/Common/GL/GLInterface/EGLWayland.h new file mode 100644 index 0000000000..83f403a34f --- /dev/null +++ b/Source/Core/Common/GL/GLInterface/EGLWayland.h @@ -0,0 +1,19 @@ +// Copyright 2019 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include "Common/GL/GLInterface/EGL.h" + +class GLContextEGLWayland : public GLContextEGL +{ +public: + ~GLContextEGLWayland(); + + void UpdateDimensions(int window_width, int window_height) override; + +protected: + EGLDisplay OpenEGLDisplay() override; + EGLNativeWindowType GetEGLNativeWindow(EGLConfig config) override; +}; diff --git a/Source/Core/Common/GL/GLInterface/EGLX11.cpp b/Source/Core/Common/GL/GLInterface/EGLX11.cpp index f430898361..d2496c2cf4 100644 --- a/Source/Core/Common/GL/GLInterface/EGLX11.cpp +++ b/Source/Core/Common/GL/GLInterface/EGLX11.cpp @@ -11,7 +11,7 @@ GLContextEGLX11::~GLContextEGLX11() m_render_window.reset(); } -void GLContextEGLX11::Update() +void GLContextEGLX11::UpdateDimensions(int window_width, int window_height) { m_render_window->UpdateDimensions(); m_backbuffer_width = m_render_window->GetWidth(); diff --git a/Source/Core/Common/GL/GLInterface/EGLX11.h b/Source/Core/Common/GL/GLInterface/EGLX11.h index 6f30333341..7d7ba557e2 100644 --- a/Source/Core/Common/GL/GLInterface/EGLX11.h +++ b/Source/Core/Common/GL/GLInterface/EGLX11.h @@ -13,7 +13,7 @@ class GLContextEGLX11 final : public GLContextEGL public: ~GLContextEGLX11() override; - void Update() override; +void UpdateDimensions(int window_width, int window_height) override; protected: EGLDisplay OpenEGLDisplay() override; diff --git a/Source/Core/Common/GL/GLInterface/GLX.cpp b/Source/Core/Common/GL/GLInterface/GLX.cpp index b04296d096..e6116bc999 100644 --- a/Source/Core/Common/GL/GLInterface/GLX.cpp +++ b/Source/Core/Common/GL/GLInterface/GLX.cpp @@ -310,7 +310,7 @@ bool GLContextGLX::ClearCurrent() return glXMakeCurrent(m_display, None, nullptr); } -void GLContextGLX::Update() +void GLContextGLX::UpdateDimensions(int window_width, int window_height) { m_render_window->UpdateDimensions(); m_backbuffer_width = m_render_window->GetWidth(); diff --git a/Source/Core/Common/GL/GLInterface/GLX.h b/Source/Core/Common/GL/GLInterface/GLX.h index ec4bd29cb9..cf8b81a42d 100644 --- a/Source/Core/Common/GL/GLInterface/GLX.h +++ b/Source/Core/Common/GL/GLInterface/GLX.h @@ -24,7 +24,7 @@ public: bool MakeCurrent() override; bool ClearCurrent() override; - void Update() override; + void UpdateDimensions(int window_width, int window_height) override; void SwapInterval(int Interval) override; void Swap() override; diff --git a/Source/Core/Common/GL/GLInterface/WGL.cpp b/Source/Core/Common/GL/GLInterface/WGL.cpp index 286859daf6..cf2dbe0617 100644 --- a/Source/Core/Common/GL/GLInterface/WGL.cpp +++ b/Source/Core/Common/GL/GLInterface/WGL.cpp @@ -480,7 +480,7 @@ bool GLContextWGL::ClearCurrent() } // Update window width, size and etc. Called from Render.cpp -void GLContextWGL::Update() +void GLContextWGL::UpdateDimensions(int window_width, int window_height) { RECT rcWindow; GetClientRect(m_window_handle, &rcWindow); diff --git a/Source/Core/Common/GL/GLInterface/WGL.h b/Source/Core/Common/GL/GLInterface/WGL.h index ad2b665028..4ee867eb03 100644 --- a/Source/Core/Common/GL/GLInterface/WGL.h +++ b/Source/Core/Common/GL/GLInterface/WGL.h @@ -19,7 +19,7 @@ public: bool MakeCurrent() override; bool ClearCurrent() override; - void Update() override; + void UpdateDimensions(int window_width, int window_height) override; void Swap() override; void SwapInterval(int interval) override; diff --git a/Source/Core/Common/WindowSystemInfo.h b/Source/Core/Common/WindowSystemInfo.h index 8936ad1a02..105271e290 100644 --- a/Source/Core/Common/WindowSystemInfo.h +++ b/Source/Core/Common/WindowSystemInfo.h @@ -40,7 +40,11 @@ struct WindowSystemInfo // This is kept seperate as input may require a different handle to rendering, and // during video backend startup the surface pointer may change (MoltenVK). void* render_surface = nullptr; - + + // Dimensions of the render surface, if this is determined by the frontend. + int render_surface_width = 0; + int render_surface_height = 0; + // Scale of the render surface. For hidpi systems, this will be >1. float render_surface_scale = 1.0f; }; diff --git a/Source/Core/Core/Core.cpp b/Source/Core/Core/Core.cpp index 8a02534c57..9931ada05b 100644 --- a/Source/Core/Core/Core.cpp +++ b/Source/Core/Core/Core.cpp @@ -474,6 +474,8 @@ static void EmuThread(std::unique_ptr boot, WindowSystemInfo wsi // is relative to the render window, instead of the main window. ASSERT(g_controller_interface.IsInit()); g_controller_interface.ChangeWindow(wsi.render_window); + //g_controller_interface.ChangeWindow(wsi.render_surface, wsi.render_surface_width, + // wsi.render_surface_height); Pad::LoadConfig(); Pad::LoadGBAConfig(); diff --git a/Source/Core/Core/HW/GCPadEmu.cpp b/Source/Core/Core/HW/GCPadEmu.cpp index 470d2b8c2f..0defa182bc 100644 --- a/Source/Core/Core/HW/GCPadEmu.cpp +++ b/Source/Core/Core/HW/GCPadEmu.cpp @@ -24,6 +24,7 @@ static const u16 button_bitmasks[] = { PAD_BUTTON_X, PAD_BUTTON_Y, PAD_TRIGGER_Z, + PAD_BUTTON_HOTKEY, PAD_BUTTON_START, 0 // MIC HAX }; @@ -47,6 +48,9 @@ GCPad::GCPad(const unsigned int index) : m_index(index) // i18n: The START/PAUSE button on GameCube controllers m_buttons->AddInput(ControllerEmu::Translate, START_BUTTON, _trans("START")); + // Hotkey Button + m_buttons->AddInput(ControllerEmu::Translate, HOTKEY_BUTTON, _trans("HOTKEY")); + // sticks groups.emplace_back(m_main_stick = new ControllerEmu::OctagonAnalogStick( MAIN_STICK_GROUP, _trans("Control Stick"), MAIN_STICK_GATE_RADIUS)); diff --git a/Source/Core/Core/HW/GCPadEmu.h b/Source/Core/Core/HW/GCPadEmu.h index 66a1aee4e4..a03eaebcd3 100644 --- a/Source/Core/Core/HW/GCPadEmu.h +++ b/Source/Core/Core/HW/GCPadEmu.h @@ -65,6 +65,7 @@ public: static constexpr const char* X_BUTTON = "X"; static constexpr const char* Y_BUTTON = "Y"; static constexpr const char* Z_BUTTON = "Z"; + static constexpr const char* HOTKEY_BUTTON = "Hotkey"; static constexpr const char* START_BUTTON = "Start"; // i18n: The left trigger button (labeled L on real controllers) diff --git a/Source/Core/DolphinNoGUI/CMakeLists.txt b/Source/Core/DolphinNoGUI/CMakeLists.txt index f21955d809..c4de5e64bd 100644 --- a/Source/Core/DolphinNoGUI/CMakeLists.txt +++ b/Source/Core/DolphinNoGUI/CMakeLists.txt @@ -17,6 +17,22 @@ if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") target_sources(dolphin-nogui PRIVATE PlatformFBDev.cpp) endif() +if(ENABLE_WAYLAND AND WAYLAND_FOUND) + set(WAYLAND_PLATFORM_SRCS PlatformWayland.cpp) + + # Generate the xdg-shell and xdg-decoration protocols at build-time. + ecm_add_wayland_client_protocol(WAYLAND_PLATFORM_SRCS + PROTOCOL "${WAYLAND_PROTOCOLS_PKGDATADIR}/stable/xdg-shell/xdg-shell.xml" + BASENAME xdg-shell) + ecm_add_wayland_client_protocol(WAYLAND_PLATFORM_SRCS + PROTOCOL "${WAYLAND_PROTOCOLS_PKGDATADIR}/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml" + BASENAME xdg-decoration) + + target_include_directories(dolphin-nogui PRIVATE "${CMAKE_CURRENT_BINARY_DIR}") + target_sources(dolphin-nogui PRIVATE "${WAYLAND_PLATFORM_SRCS}") + target_link_libraries(dolphin-nogui PRIVATE Wayland::Client) +endif() + set_target_properties(dolphin-nogui PROPERTIES OUTPUT_NAME dolphin-emu-nogui) target_link_libraries(dolphin-nogui diff --git a/Source/Core/DolphinNoGUI/MainNoGUI.cpp b/Source/Core/DolphinNoGUI/MainNoGUI.cpp index 11bbf55da1..7ba9111b8a 100644 --- a/Source/Core/DolphinNoGUI/MainNoGUI.cpp +++ b/Source/Core/DolphinNoGUI/MainNoGUI.cpp @@ -155,6 +155,11 @@ static std::unique_ptr GetPlatform(const optparse::Values& options) { std::string platform_name = static_cast(options.get("platform")); +#if HAVE_WAYLAND + if (platform_name == "wayland") + return Platform::CreateWaylandPlatform(); +#endif + #if HAVE_X11 if (platform_name == "x11" || platform_name.empty()) return Platform::CreateX11Platform(); @@ -199,6 +204,10 @@ int main(int argc, char* argv[]) #ifdef _WIN32 , "win32" +#endif +#ifdef HAVE_WAYLAND + , + "wayland" #endif }); diff --git a/Source/Core/DolphinNoGUI/Platform.h b/Source/Core/DolphinNoGUI/Platform.h index 24ec06e307..294f7087d8 100644 --- a/Source/Core/DolphinNoGUI/Platform.h +++ b/Source/Core/DolphinNoGUI/Platform.h @@ -35,6 +35,10 @@ public: static std::unique_ptr CreateX11Platform(); #endif +#ifdef HAVE_WAYLAND + static std::unique_ptr CreateWaylandPlatform(); +#endif + #ifdef __linux__ static std::unique_ptr CreateFBDevPlatform(); #endif diff --git a/Source/Core/DolphinNoGUI/PlatformWayland.cpp b/Source/Core/DolphinNoGUI/PlatformWayland.cpp new file mode 100644 index 0000000000..cebff25198 --- /dev/null +++ b/Source/Core/DolphinNoGUI/PlatformWayland.cpp @@ -0,0 +1,323 @@ +// Copyright 2018 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include + +#include "DolphinNoGUI/Platform.h" + +#include "Common/MsgHandler.h" +#include "Core/Config/MainSettings.h" +#include "Core/Core.h" +#include "Core/State.h" + +#include "Core/HW/GCPad.h" +#include "InputCommon/GCPadStatus.h" +#include +#include "Core/Config/GraphicsSettings.h" +#include "VideoCommon/VideoConfig.h" + +#include +#include +#include + +#include +#include "wayland-xdg-decoration-client-protocol.h" +#include "wayland-xdg-shell-client-protocol.h" + +#include "UICommon/X11Utils.h" +#include "VideoCommon/RenderBase.h" + +namespace +{ +class PlatformWayland : public Platform +{ +public: + ~PlatformWayland() override; + + bool Init() override; + void SetTitle(const std::string& string) override; + void MainLoop() override; + + WindowSystemInfo GetWindowSystemInfo() const; + +private: + void ProcessEvents(); + + static void GlobalRegistryHandler(void* data, wl_registry* registry, uint32_t id, + const char* interface, uint32_t version); + static void GlobalRegistryRemover(void* data, wl_registry* registry, uint32_t id); + static void XDGWMBasePing(void* data, struct xdg_wm_base* xdg_wm_base, uint32_t serial); + static void XDGSurfaceConfigure(void* data, struct xdg_surface* xdg_surface, uint32_t serial); + static void TopLevelConfigure(void* data, struct xdg_toplevel* xdg_toplevel, int32_t width, + int32_t height, struct wl_array* states); + static void TopLevelClose(void* data, struct xdg_toplevel* xdg_toplevel); + + wl_display* m_display = nullptr; + wl_registry* m_registry = nullptr; + wl_compositor* m_compositor = nullptr; + xdg_wm_base* m_xdg_wm_base = nullptr; + wl_surface* m_surface = nullptr; + wl_region* m_region = nullptr; + xdg_surface* m_xdg_surface = nullptr; + xdg_toplevel* m_xdg_toplevel = nullptr; + zxdg_decoration_manager_v1* m_decoration_manager = nullptr; + zxdg_toplevel_decoration_v1* m_toplevel_decoration = nullptr; + + int m_surface_width = 0; + int m_surface_height = 0; +}; + +PlatformWayland::~PlatformWayland() +{ + if (m_xdg_toplevel) + xdg_toplevel_destroy(m_xdg_toplevel); + if (m_xdg_surface) + xdg_surface_destroy(m_xdg_surface); + if (m_surface) + wl_surface_destroy(m_surface); + if (m_region) + wl_region_destroy(m_region); + if (m_xdg_wm_base) + xdg_wm_base_destroy(m_xdg_wm_base); + if (m_compositor) + wl_compositor_destroy(m_compositor); + if (m_registry) + wl_registry_destroy(m_registry); + if (m_display) + wl_display_disconnect(m_display); +} + +void PlatformWayland::GlobalRegistryHandler(void* data, wl_registry* registry, uint32_t id, + const char* interface, uint32_t version) +{ + PlatformWayland* platform = static_cast(data); + if (std::strcmp(interface, wl_compositor_interface.name) == 0) + { + platform->m_compositor = static_cast( + wl_registry_bind(platform->m_registry, id, &wl_compositor_interface, 1)); + } + else if (std::strcmp(interface, xdg_wm_base_interface.name) == 0) + { + platform->m_xdg_wm_base = static_cast( + wl_registry_bind(platform->m_registry, id, &xdg_wm_base_interface, 1)); + } + else if (std::strcmp(interface, zxdg_decoration_manager_v1_interface.name) == 0) + { + platform->m_decoration_manager = static_cast( + wl_registry_bind(platform->m_registry, id, &zxdg_decoration_manager_v1_interface, 1)); + } +} + +void PlatformWayland::GlobalRegistryRemover(void* data, wl_registry* registry, uint32_t id) +{ +} + +void PlatformWayland::XDGWMBasePing(void* data, struct xdg_wm_base* xdg_wm_base, uint32_t serial) +{ + xdg_wm_base_pong(xdg_wm_base, serial); +} + +void PlatformWayland::XDGSurfaceConfigure(void* data, struct xdg_surface* xdg_surface, + uint32_t serial) +{ + xdg_surface_ack_configure(xdg_surface, serial); +} + +void PlatformWayland::TopLevelConfigure(void* data, struct xdg_toplevel* xdg_toplevel, + int32_t width, int32_t height, struct wl_array* states) +{ + // If this is zero, it's asking us to set the size. + if (width == 0 || height == 0) + return; + + PlatformWayland* platform = static_cast(data); + platform->m_surface_width = width; + platform->m_surface_height = height; + if (g_renderer) + g_renderer->ResizeSurface(width, height); + //if (g_controller_interface.IsInit()) + //g_controller_interface.OnWindowResized(width, height); +} + +void PlatformWayland::TopLevelClose(void* data, struct xdg_toplevel* xdg_toplevel) +{ + PlatformWayland* platform = static_cast(data); + platform->Stop(); +} + +bool PlatformWayland::Init() +{ + m_display = wl_display_connect(nullptr); + if (!m_display) + { + //PanicAlert("Failed to connect to Wayland display."); + return false; + } + + static const wl_registry_listener registry_listener = {GlobalRegistryHandler, + GlobalRegistryRemover}; + m_registry = wl_display_get_registry(m_display); + wl_registry_add_listener(m_registry, ®istry_listener, this); + + // Call back to registry listener to get compositor/shell. + wl_display_dispatch(m_display); + wl_display_roundtrip(m_display); + + // We need a shell/compositor, or at least one we understand. + if (!m_compositor || !m_display || !m_xdg_wm_base) + { + std::fprintf(stderr, "Missing Wayland shell/compositor\n"); + return false; + } + + // Create the compositor and shell surface. + if (!(m_surface = wl_compositor_create_surface(m_compositor)) || + !(m_xdg_surface = xdg_wm_base_get_xdg_surface(m_xdg_wm_base, m_surface)) || + !(m_xdg_toplevel = xdg_surface_get_toplevel(m_xdg_surface))) + { + std::fprintf(stderr, "Failed to create compositor/shell surfaces\n"); + return false; + } + + static const xdg_wm_base_listener xdg_wm_base_listener = {XDGWMBasePing}; + xdg_wm_base_add_listener(m_xdg_wm_base, &xdg_wm_base_listener, this); + + static const xdg_surface_listener shell_surface_listener = {XDGSurfaceConfigure}; + xdg_surface_add_listener(m_xdg_surface, &shell_surface_listener, this); + + static const xdg_toplevel_listener toplevel_listener = {TopLevelConfigure, TopLevelClose}; + xdg_toplevel_add_listener(m_xdg_toplevel, &toplevel_listener, this); + + // Create region in the surface to draw into. + m_surface_width = Config::Get(Config::MAIN_RENDER_WINDOW_WIDTH); + m_surface_height = Config::Get(Config::MAIN_RENDER_WINDOW_HEIGHT); + m_region = wl_compositor_create_region(m_compositor); + wl_region_add(m_region, 0, 0, m_surface_width, m_surface_height); + wl_surface_set_opaque_region(m_surface, m_region); + wl_surface_commit(m_surface); + + // This doesn't seem to have any effect on kwin... + xdg_surface_set_window_geometry(m_xdg_surface, Config::Get(Config::MAIN_RENDER_WINDOW_XPOS), + Config::Get(Config::MAIN_RENDER_WINDOW_YPOS), + Config::Get(Config::MAIN_RENDER_WINDOW_WIDTH), + Config::Get(Config::MAIN_RENDER_WINDOW_HEIGHT)); + + if (m_decoration_manager) + { + m_toplevel_decoration = + zxdg_decoration_manager_v1_get_toplevel_decoration(m_decoration_manager, m_xdg_toplevel); + if (m_toplevel_decoration) + zxdg_toplevel_decoration_v1_set_mode(m_toplevel_decoration, + ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); + } + + return true; +} + +void PlatformWayland::SetTitle(const std::string& string) +{ + xdg_toplevel_set_title(m_xdg_toplevel, string.c_str()); +} + +void PlatformWayland::MainLoop() +{ + while (IsRunning()) + { + static int hotkey = 0; + static int slot = 0; + static int fps = 0; + static int aspect = 0; + + UpdateRunningFlag(); + Core::HostDispatchJobs(); + ProcessEvents(); + + if(Pad::IsInitialized()) { + GCPadStatus x = Pad::GetStatus(0); + + if( (x.button & PAD_BUTTON_HOTKEY) == PAD_BUTTON_HOTKEY) { // hotkey pressed + if(hotkey == 1) { + hotkey = 2; + } + } else { + hotkey = 1; // assure hotkey is released between actions + } + + if(hotkey == 2) { // hotkey pressed + if( (x.button & PAD_BUTTON_START) == PAD_BUTTON_START) { + RequestShutdown(); + hotkey = 0; + } + + if( (x.button & PAD_TRIGGER_L) == PAD_TRIGGER_L) { + State::Load(slot); + hotkey = 0; + } + if( (x.button & PAD_TRIGGER_R) == PAD_TRIGGER_R) { + State::Save(slot); + hotkey = 0; + } + if( (x.button & PAD_BUTTON_DOWN) == PAD_BUTTON_DOWN) { + if(slot > 0) slot--; + Core::DisplayMessage(fmt::format("Slot {} selected", slot), 4000); + hotkey = 0; + } + if( (x.button & PAD_BUTTON_UP) == PAD_BUTTON_UP) { + if(slot < 10) slot++; + Core::DisplayMessage(fmt::format("Slot {} selected", slot), 4000); + hotkey = 0; + } + if( (x.button & PAD_BUTTON_A) == PAD_BUTTON_A) { + Core::SaveScreenShot(); + hotkey = 0; + } + if( (x.button & PAD_BUTTON_Y) == PAD_BUTTON_Y) { + if(fps == 0) { + Config::SetCurrent(Config::GFX_SHOW_FPS, True); + fps = 1; + } else { + Config::SetCurrent(Config::GFX_SHOW_FPS, False); + fps = 0; + } + hotkey = 0; + } + if( (x.button & PAD_BUTTON_X) == PAD_BUTTON_X) { + if(aspect == 0) { + Config::SetCurrent(Config::GFX_ASPECT_RATIO, AspectMode::Stretch); + aspect = 1; + } else { + Config::SetCurrent(Config::GFX_ASPECT_RATIO, AspectMode::Auto); + aspect = 0; + } + hotkey = 0; + } + } + } + + // TODO: Is this sleep appropriate? + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } +} + +WindowSystemInfo PlatformWayland::GetWindowSystemInfo() const +{ + WindowSystemInfo wsi; + wsi.type = WindowSystemType::Wayland; + wsi.display_connection = static_cast(m_display); + wsi.render_surface = reinterpret_cast(m_surface); + wsi.render_surface_width = m_surface_width; + wsi.render_surface_height = m_surface_height; + return wsi; +} + +void PlatformWayland::ProcessEvents() +{ + wl_display_dispatch_pending(m_display); +} +} // namespace + +std::unique_ptr Platform::CreateWaylandPlatform() +{ + return std::make_unique(); +} diff --git a/Source/Core/DolphinNoGUI/PlatformX11.cpp b/Source/Core/DolphinNoGUI/PlatformX11.cpp index 8dcd93bf52..f1a75e3b44 100644 --- a/Source/Core/DolphinNoGUI/PlatformX11.cpp +++ b/Source/Core/DolphinNoGUI/PlatformX11.cpp @@ -57,8 +57,8 @@ private: #endif int m_window_x = Config::Get(Config::MAIN_RENDER_WINDOW_XPOS); int m_window_y = Config::Get(Config::MAIN_RENDER_WINDOW_YPOS); - unsigned int m_window_width = Config::Get(Config::MAIN_RENDER_WINDOW_WIDTH); - unsigned int m_window_height = Config::Get(Config::MAIN_RENDER_WINDOW_HEIGHT); + int m_window_width = Config::Get(Config::MAIN_RENDER_WINDOW_WIDTH); + int m_window_height = Config::Get(Config::MAIN_RENDER_WINDOW_HEIGHT); }; PlatformX11::~PlatformX11() @@ -166,6 +166,8 @@ WindowSystemInfo PlatformX11::GetWindowSystemInfo() const wsi.display_connection = static_cast(m_display); wsi.render_window = reinterpret_cast(m_window); wsi.render_surface = reinterpret_cast(m_window); + wsi.render_surface_width = m_window_width; + wsi.render_surface_height = m_window_height; return wsi; } @@ -176,8 +178,9 @@ void PlatformX11::UpdateWindowPosition() Window winDummy; unsigned int borderDummy, depthDummy; - XGetGeometry(m_display, m_window, &winDummy, &m_window_x, &m_window_y, &m_window_width, - &m_window_height, &borderDummy, &depthDummy); + XGetGeometry(m_display, m_window, &winDummy, &m_window_x, &m_window_y, + reinterpret_cast(&m_window_width), + reinterpret_cast(&m_window_height), &borderDummy, &depthDummy); } void PlatformX11::ProcessEvents() @@ -264,7 +267,10 @@ void PlatformX11::ProcessEvents() case ConfigureNotify: { if (g_renderer) - g_renderer->ResizeSurface(); + { + UpdateWindowPosition(); + g_renderer->ResizeSurface(m_window_width, m_window_height); + } } break; } diff --git a/Source/Core/InputCommon/GCPadStatus.h b/Source/Core/InputCommon/GCPadStatus.h index 74849e5594..029ade5824 100644 --- a/Source/Core/InputCommon/GCPadStatus.h +++ b/Source/Core/InputCommon/GCPadStatus.h @@ -26,6 +26,7 @@ enum PadButton PAD_BUTTON_X = 0x0400, PAD_BUTTON_Y = 0x0800, PAD_BUTTON_START = 0x1000, + PAD_BUTTON_HOTKEY = 0x2000, }; struct GCPadStatus diff --git a/Source/Core/VideoBackends/OGL/OGLRender.cpp b/Source/Core/VideoBackends/OGL/OGLRender.cpp index 49400d73ff..dd812a7c87 100644 --- a/Source/Core/VideoBackends/OGL/OGLRender.cpp +++ b/Source/Core/VideoBackends/OGL/OGLRender.cpp @@ -1079,7 +1079,7 @@ void Renderer::CheckForSurfaceChange() if (!m_surface_changed.TestAndClear()) return; - m_main_gl_context->UpdateSurface(m_new_surface_handle); + m_main_gl_context->UpdateSurface(m_new_surface_handle, m_new_surface_width, m_new_surface_height); m_new_surface_handle = nullptr; // With a surface change, the window likely has new dimensions. @@ -1093,7 +1093,7 @@ void Renderer::CheckForSurfaceResize() if (!m_surface_resized.TestAndClear()) return; - m_main_gl_context->Update(); + m_main_gl_context->UpdateDimensions(m_new_surface_width, m_new_surface_height); m_backbuffer_width = m_main_gl_context->GetBackBufferWidth(); m_backbuffer_height = m_main_gl_context->GetBackBufferHeight(); m_system_framebuffer->UpdateDimensions(m_backbuffer_width, m_backbuffer_height); diff --git a/Source/Core/VideoBackends/Software/SWOGLWindow.cpp b/Source/Core/VideoBackends/Software/SWOGLWindow.cpp index a002e3e519..0d2190ea61 100644 --- a/Source/Core/VideoBackends/Software/SWOGLWindow.cpp +++ b/Source/Core/VideoBackends/Software/SWOGLWindow.cpp @@ -32,6 +32,16 @@ bool SWOGLWindow::IsHeadless() const return m_gl_context->IsHeadless(); } +u32 SWOGLWindow::GetWidth() const +{ + return m_gl_context->GetBackBufferWidth(); +} + +u32 SWOGLWindow::GetHeight() const +{ + return m_gl_context->GetBackBufferHeight(); +} + bool SWOGLWindow::Initialize(const WindowSystemInfo& wsi) { m_gl_context = GLContext::Create(wsi); @@ -84,11 +94,17 @@ bool SWOGLWindow::Initialize(const WindowSystemInfo& wsi) return true; } + +void SWOGLWindow::UpdateDimensions(int window_width, int window_height) +{ + // just updates the render window position and the backbuffer size + m_gl_context->UpdateDimensions(window_width, window_height); +} + void SWOGLWindow::ShowImage(const AbstractTexture* image, const MathUtil::Rectangle& xfb_region) { const SW::SWTexture* sw_image = static_cast(image); - m_gl_context->Update(); // just updates the render window position and the backbuffer size GLsizei glWidth = (GLsizei)m_gl_context->GetBackBufferWidth(); GLsizei glHeight = (GLsizei)m_gl_context->GetBackBufferHeight(); diff --git a/Source/Core/VideoBackends/Software/SWOGLWindow.h b/Source/Core/VideoBackends/Software/SWOGLWindow.h index 965f7f4e43..b18f8e6636 100644 --- a/Source/Core/VideoBackends/Software/SWOGLWindow.h +++ b/Source/Core/VideoBackends/Software/SWOGLWindow.h @@ -20,6 +20,10 @@ public: GLContext* GetContext() const { return m_gl_context.get(); } bool IsHeadless() const; + u32 GetWidth() const; + u32 GetHeight() const; + void UpdateDimensions(int window_width, int window_height); + // Image to show, will be swapped immediately void ShowImage(const AbstractTexture* image, const MathUtil::Rectangle& xfb_region); diff --git a/Source/Core/VideoBackends/Software/SWRenderer.cpp b/Source/Core/VideoBackends/Software/SWRenderer.cpp index 76eea93068..d3ef32736e 100644 --- a/Source/Core/VideoBackends/Software/SWRenderer.cpp +++ b/Source/Core/VideoBackends/Software/SWRenderer.cpp @@ -59,17 +59,17 @@ SWRenderer::CreateFramebuffer(AbstractTexture* color_attachment, AbstractTexture static_cast(depth_attachment)); } -void SWRenderer::BindBackbuffer(const ClearColor& clear_color) -{ +//void SWRenderer::BindBackbuffer(const ClearColor& clear_color) +//{ // Look for framebuffer resizes - if (!m_surface_resized.TestAndClear()) - return; + //if (!m_surface_resized.TestAndClear()) + //return; - GLContext* context = m_window->GetContext(); - context->Update(); - m_backbuffer_width = context->GetBackBufferWidth(); - m_backbuffer_height = context->GetBackBufferHeight(); -} + //GLContext* context = m_window->GetContext(); + //context->UpdateDimensions(window_width, window_height); + //m_backbuffer_width = context->GetBackBufferWidth(); + //m_backbuffer_height = context->GetBackBufferHeight(); +//} class SWShader final : public AbstractShader { @@ -107,7 +107,7 @@ std::unique_ptr SWRenderer::CreatePipeline(const AbstractPipel { return std::make_unique(); } - + // Called on the GPU thread void SWRenderer::RenderXFBToScreen(const MathUtil::Rectangle& target_rc, const AbstractTexture* source_texture, @@ -117,6 +117,16 @@ void SWRenderer::RenderXFBToScreen(const MathUtil::Rectangle& target_rc, m_window->ShowImage(source_texture, source_rc); } +void SWRenderer::CheckForSurfaceResize() +{ + if (!m_surface_resized.TestAndClear()) + return; + + m_window->UpdateDimensions(m_new_surface_width, m_new_surface_height); + m_backbuffer_width = static_cast(m_window->GetWidth()); + m_backbuffer_height = static_cast(m_window->GetHeight()); +} + u32 SWRenderer::AccessEFB(EFBAccessType type, u32 x, u32 y, u32 InputData) { u32 value = 0; diff --git a/Source/Core/VideoBackends/Software/SWRenderer.h b/Source/Core/VideoBackends/Software/SWRenderer.h index 8aa9aa4af5..7f5f98f4d3 100644 --- a/Source/Core/VideoBackends/Software/SWRenderer.h +++ b/Source/Core/VideoBackends/Software/SWRenderer.h @@ -29,7 +29,7 @@ public: std::unique_ptr CreateFramebuffer(AbstractTexture* color_attachment, AbstractTexture* depth_attachment) override; - void BindBackbuffer(const ClearColor& clear_color = {}) override; + //void BindBackbuffer(const ClearColor& clear_color = {}) override; std::unique_ptr CreateShaderFromSource(ShaderStage stage, std::string_view source, std::string_view name) override; @@ -64,6 +64,8 @@ protected: std::unique_ptr CreateBoundingBox() const override; private: +void CheckForSurfaceResize(); + std::unique_ptr m_window; }; } // namespace SW diff --git a/Source/Core/VideoBackends/Vulkan/VKRenderer.cpp b/Source/Core/VideoBackends/Vulkan/VKRenderer.cpp index 23dba4613b..5fb174b62f 100644 --- a/Source/Core/VideoBackends/Vulkan/VKRenderer.cpp +++ b/Source/Core/VideoBackends/Vulkan/VKRenderer.cpp @@ -306,7 +306,7 @@ void Renderer::BindBackbuffer(const ClearColor& clear_color) else if (res == VK_SUBOPTIMAL_KHR || res == VK_ERROR_OUT_OF_DATE_KHR) { INFO_LOG_FMT(VIDEO, "Resizing swap chain due to suboptimal/out-of-date"); - m_swap_chain->ResizeSwapChain(); + m_swap_chain->ResizeSwapChain(m_backbuffer_width, m_backbuffer_height); } else { @@ -384,8 +384,11 @@ void Renderer::CheckForSurfaceChange() g_command_buffer_mgr->CheckLastPresentFail(); // Recreate the surface. If this fails we're in trouble. - if (!m_swap_chain->RecreateSurface(m_new_surface_handle)) + if (!m_swap_chain->RecreateSurface(m_new_surface_handle, m_new_surface_width, + m_new_surface_height)) + { PanicAlertFmt("Failed to recreate Vulkan surface. Cannot continue."); + } m_new_surface_handle = nullptr; // Handle case where the dimensions are now different. @@ -412,7 +415,7 @@ void Renderer::CheckForSurfaceResize() g_command_buffer_mgr->CheckLastPresentFail(); // Resize the swap chain. - m_swap_chain->RecreateSwapChain(); + m_swap_chain->ResizeSwapChain(m_new_surface_width, m_new_surface_height); OnSwapChainResized(); } diff --git a/Source/Core/VideoBackends/Vulkan/VKSwapChain.cpp b/Source/Core/VideoBackends/Vulkan/VKSwapChain.cpp index a7f5bb929d..bc515a6d34 100644 --- a/Source/Core/VideoBackends/Vulkan/VKSwapChain.cpp +++ b/Source/Core/VideoBackends/Vulkan/VKSwapChain.cpp @@ -15,7 +15,6 @@ #include "VideoBackends/Vulkan/ObjectCache.h" #include "VideoBackends/Vulkan/VKTexture.h" #include "VideoBackends/Vulkan/VulkanContext.h" -#include "VideoCommon/RenderBase.h" #if defined(VK_USE_PLATFORM_XLIB_KHR) #include @@ -25,7 +24,8 @@ namespace Vulkan { SwapChain::SwapChain(const WindowSystemInfo& wsi, VkSurfaceKHR surface, bool vsync) : m_wsi(wsi), m_surface(surface), m_vsync_enabled(vsync), - m_fullscreen_supported(g_vulkan_context->SupportsExclusiveFullscreen(wsi, surface)) + m_fullscreen_supported(g_vulkan_context->SupportsExclusiveFullscreen(wsi, surface)), + m_width(wsi.render_surface_width), m_height(wsi.render_surface_height) { } @@ -84,6 +84,29 @@ VkSurfaceKHR SwapChain::CreateVulkanSurface(VkInstance instance, const WindowSys } #endif +#if defined(VK_USE_PLATFORM_WAYLAND_KHR) + if (wsi.type == WindowSystemType::Wayland) + { + VkWaylandSurfaceCreateInfoKHR surface_create_info = { + VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR, // VkStructureType sType + nullptr, // const void* pNext + 0, // VkWaylandSurfaceCreateFlagsKHR flags + static_cast(wsi.display_connection), // struct wl_display* display + static_cast(wsi.render_surface) // struct wl_surface* surface + }; + + VkSurfaceKHR surface; + VkResult res = vkCreateWaylandSurfaceKHR(instance, &surface_create_info, nullptr, &surface); + if (res != VK_SUCCESS) + { + LOG_VULKAN_ERROR(res, "vkCreateWaylandSurfaceKHR failed: "); + return VK_NULL_HANDLE; + } + + return surface; + } +#endif + #if defined(VK_USE_PLATFORM_ANDROID_KHR) if (wsi.type == WindowSystemType::Android) { @@ -265,8 +288,8 @@ bool SwapChain::CreateSwapChain() VkExtent2D size = surface_capabilities.currentExtent; if (size.width == UINT32_MAX) { - size.width = std::max(g_renderer->GetBackbufferWidth(), 1); - size.height = std::max(g_renderer->GetBackbufferHeight(), 1); + size.width = static_cast(m_wsi.render_surface_width); + size.height = static_cast(m_wsi.render_surface_height); } size.width = std::clamp(size.width, surface_capabilities.minImageExtent.width, surface_capabilities.maxImageExtent.width); @@ -463,9 +486,11 @@ VkResult SwapChain::AcquireNextImage() return res; } -bool SwapChain::ResizeSwapChain() +bool SwapChain::ResizeSwapChain(int window_width, int window_height) { DestroySwapChainImages(); + m_wsi.render_surface_width = window_width; + m_wsi.render_surface_height = window_height; if (!CreateSwapChain() || !SetupSwapChainImages()) { PanicAlertFmt("Failed to re-configure swap chain images, this is fatal (for now)"); @@ -531,7 +556,7 @@ bool SwapChain::SetFullscreenState(bool state) #endif } -bool SwapChain::RecreateSurface(void* native_handle) +bool SwapChain::RecreateSurface(void* native_handle, int window_width, int window_height) { // Destroy the old swap chain, images, and surface. DestroySwapChainImages(); @@ -540,6 +565,8 @@ bool SwapChain::RecreateSurface(void* native_handle) // Re-create the surface with the new native handle m_wsi.render_surface = native_handle; + m_wsi.render_surface_width = window_width; + m_wsi.render_surface_height = window_height; m_surface = CreateVulkanSurface(g_vulkan_context->GetVulkanInstance(), m_wsi); if (m_surface == VK_NULL_HANDLE) return false; diff --git a/Source/Core/VideoBackends/Vulkan/VKSwapChain.h b/Source/Core/VideoBackends/Vulkan/VKSwapChain.h index 5e67217f2d..7fb8c21f53 100644 --- a/Source/Core/VideoBackends/Vulkan/VKSwapChain.h +++ b/Source/Core/VideoBackends/Vulkan/VKSwapChain.h @@ -52,8 +52,8 @@ public: } VkResult AcquireNextImage(); - bool RecreateSurface(void* native_handle); - bool ResizeSwapChain(); + bool RecreateSurface(void* native_handle, int window_width, int window_height); + bool ResizeSwapChain(int window_width, int window_height); bool RecreateSwapChain(); // Change vsync enabled state. This may fail as it causes a swapchain recreation. diff --git a/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp b/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp index 3275cb9417..639069b0cb 100644 --- a/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp +++ b/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp @@ -209,6 +209,13 @@ bool VulkanContext::SelectInstanceExtensions(std::vector* extension return false; } #endif +#if defined(VK_USE_PLATFORM_WAYLAND_KHR) + if (wstype == WindowSystemType::Wayland && + !AddExtension(VK_KHR_WAYLAND_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)) diff --git a/Source/Core/VideoBackends/Vulkan/VulkanEntryPoints.inl b/Source/Core/VideoBackends/Vulkan/VulkanEntryPoints.inl index 3bb21c41e3..3ff64b524c 100644 --- a/Source/Core/VideoBackends/Vulkan/VulkanEntryPoints.inl +++ b/Source/Core/VideoBackends/Vulkan/VulkanEntryPoints.inl @@ -49,6 +49,11 @@ VULKAN_INSTANCE_ENTRY_POINT(vkCreateXlibSurfaceKHR, false) VULKAN_INSTANCE_ENTRY_POINT(vkGetPhysicalDeviceXlibPresentationSupportKHR, false) #endif +#if defined(VK_USE_PLATFORM_WAYLAND_KHR) +VULKAN_INSTANCE_ENTRY_POINT(vkCreateWaylandSurfaceKHR, false) +VULKAN_INSTANCE_ENTRY_POINT(vkGetPhysicalDeviceWaylandPresentationSupportKHR, false) +#endif + #if defined(VK_USE_PLATFORM_ANDROID_KHR) VULKAN_INSTANCE_ENTRY_POINT(vkCreateAndroidSurfaceKHR, false) #endif diff --git a/Source/Core/VideoBackends/Vulkan/VulkanLoader.h b/Source/Core/VideoBackends/Vulkan/VulkanLoader.h index c9b92f5ac8..a728d25186 100644 --- a/Source/Core/VideoBackends/Vulkan/VulkanLoader.h +++ b/Source/Core/VideoBackends/Vulkan/VulkanLoader.h @@ -13,6 +13,10 @@ #define VK_USE_PLATFORM_XLIB_KHR #endif +#if defined(HAVE_WAYLAND) +#define VK_USE_PLATFORM_WAYLAND_KHR +#endif + #if defined(ANDROID) #define VK_USE_PLATFORM_ANDROID_KHR #endif diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index f787ab7dab..abd50cdcca 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -726,16 +726,20 @@ bool Renderer::IsHeadless() const return true; } -void Renderer::ChangeSurface(void* new_surface_handle) +void Renderer::ChangeSurface(void* new_surface_handle, int new_width, int new_height) { std::lock_guard lock(m_swap_mutex); m_new_surface_handle = new_surface_handle; + m_new_surface_width = new_width; + m_new_surface_height = new_height; m_surface_changed.Set(); } -void Renderer::ResizeSurface() +void Renderer::ResizeSurface(int new_width, int new_height) { std::lock_guard lock(m_swap_mutex); + m_new_surface_width = new_width; + m_new_surface_height = new_height; m_surface_resized.Set(); } diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h index 8824e6ff77..4692036a96 100644 --- a/Source/Core/VideoCommon/RenderBase.h +++ b/Source/Core/VideoCommon/RenderBase.h @@ -245,8 +245,8 @@ public: VideoCommon::PostProcessing* GetPostProcessor() const { return m_post_processor.get(); } // Final surface changing // This is called when the surface is resized (WX) or the window changes (Android). - void ChangeSurface(void* new_surface_handle); - void ResizeSurface(); + void ChangeSurface(void* new_surface_handle, int new_width, int new_height); + void ResizeSurface(int new_width, int new_height); bool UseVertexDepthRange() const; void DoState(PointerWrap& p); @@ -340,6 +340,8 @@ protected: std::unique_ptr m_post_processor; void* m_new_surface_handle = nullptr; + int m_new_surface_width = 0; + int m_new_surface_height = 0; Common::Flag m_surface_changed; Common::Flag m_surface_resized; std::mutex m_swap_mutex;