From fbceec47b84c16d73ab53188ee33e504fbbb0808 Mon Sep 17 00:00:00 2001 From: Greg V Date: Sun, 31 Dec 2017 18:33:43 +0300 Subject: [PATCH] Add support for Vulkan on Wayland The variable VK_USE_PLATFORM_WAYLAND_KHR is actually used by the Vulkan header, so use it here too. --- Utilities/variant.hpp | 17 +++++++ Utilities/variant_visitor.hpp | 45 +++++++++++++++++ rpcs3/CMakeLists.txt | 8 ++- rpcs3/Emu/RSX/GSRender.h | 26 +++++++++- rpcs3/Emu/RSX/VK/VKGSRender.cpp | 46 ++++++------------ rpcs3/Emu/RSX/VK/VKHelpers.h | 70 ++++++++++++++++----------- rpcs3/cmake_modules/FindWayland.cmake | 66 +++++++++++++++++++++++++ rpcs3/rpcs3qt/gs_frame.cpp | 27 ++++++++++- rpcs3/rpcs3qt/gs_frame.h | 2 +- 9 files changed, 245 insertions(+), 62 deletions(-) create mode 100644 Utilities/variant_visitor.hpp create mode 100644 rpcs3/cmake_modules/FindWayland.cmake diff --git a/Utilities/variant.hpp b/Utilities/variant.hpp index eb52484ea4..b63a4e773d 100644 --- a/Utilities/variant.hpp +++ b/Utilities/variant.hpp @@ -12,6 +12,7 @@ #include #include "recursive_wrapper.hpp" +#include "variant_visitor.hpp" // clang-format off // [[deprecated]] is only available in C++14, use this for the time being @@ -849,6 +850,22 @@ namespace std { return detail::binary_dispatcher::apply(v0, v1, std::forward(f)); } + // match + // unary + template + auto VARIANT_INLINE match(Fs&&... fs) const + -> decltype(variant::visit(*this, ::std::make_visitor(std::forward(fs)...))) + { + return variant::visit(*this, ::std::make_visitor(std::forward(fs)...)); + } + // non-const + template + auto VARIANT_INLINE match(Fs&&... fs) + -> decltype(variant::visit(*this, ::std::make_visitor(std::forward(fs)...))) + { + return variant::visit(*this, ::std::make_visitor(std::forward(fs)...)); + } + ~variant() noexcept // no-throw destructor { helper_type::destroy(type_index, &data); diff --git a/Utilities/variant_visitor.hpp b/Utilities/variant_visitor.hpp new file mode 100644 index 0000000000..2ae15a0b4e --- /dev/null +++ b/Utilities/variant_visitor.hpp @@ -0,0 +1,45 @@ +#ifndef MAPBOX_UTIL_VARIANT_VISITOR_HPP +#define MAPBOX_UTIL_VARIANT_VISITOR_HPP + +#include + +//namespace mapbox { +//namespace util { +namespace std { + +template +struct visitor; + +template +struct visitor : Fn +{ + using Fn::operator(); + + template + visitor(T&& fn) : Fn(std::forward(fn)) {} +}; + +template +struct visitor : Fn, visitor +{ + using Fn::operator(); + using visitor::operator(); + + template + visitor(T&& fn, Ts&&... fns) + : Fn(std::forward(fn)) + , visitor(std::forward(fns)...) {} +}; + +template +visitor::type...> make_visitor(Fns&&... fns) +{ + return visitor::type...> + (std::forward(fns)...); +} + +} +//} // namespace util +//} // namespace mapbox + +#endif // MAPBOX_UTIL_VARIANT_VISITOR_HPP diff --git a/rpcs3/CMakeLists.txt b/rpcs3/CMakeLists.txt index efa34e9481..bd6ae58a52 100644 --- a/rpcs3/CMakeLists.txt +++ b/rpcs3/CMakeLists.txt @@ -11,13 +11,14 @@ if(WIN32) find_package(Qt5 5.7 COMPONENTS WinExtras REQUIRED) set(RPCS3_QT_LIBS Qt5::Widgets Qt5::WinExtras Qt5::Network) else() - find_package(Qt5 5.7 COMPONENTS DBus) + find_package(Qt5 5.7 COMPONENTS DBus Gui) if(Qt5DBus_FOUND) set(RPCS3_QT_LIBS Qt5::Widgets Qt5::DBus Qt5::Network) add_definitions(-DHAVE_QTDBUS) else() set(RPCS3_QT_LIBS Qt5::Widgets Qt5::Network) endif() + include_directories(${Qt5Gui_PRIVATE_INCLUDE_DIRS}) endif() # Let's make sure we have Qt before we continue @@ -153,6 +154,11 @@ elseif(NOT MSVC AND NOT CMAKE_CXX_FLAGS MATCHES "LIBICONV_PLUG") endif() if(UNIX AND NOT APPLE) set(ADDITIONAL_LIBS ${ADDITIONAL_LIBS} "X11") + find_package(Wayland) + if (WAYLAND_FOUND) + include_directories(${WAYLAND_INCLUDE_DIR}) + add_definitions(-DVK_USE_PLATFORM_WAYLAND_KHR) + endif() endif() if(NOT RPCS3_SRC_DIR) diff --git a/rpcs3/Emu/RSX/GSRender.h b/rpcs3/Emu/RSX/GSRender.h index c5cee1429a..bc26d61072 100644 --- a/rpcs3/Emu/RSX/GSRender.h +++ b/rpcs3/Emu/RSX/GSRender.h @@ -3,6 +3,19 @@ #include "Emu/RSX/RSXThread.h" #include +#ifdef _WIN32 +#include +#else +// Cannot include Xlib.h before Qt5 +// and we don't need all of Xlib anyway +typedef struct _XDisplay Display; +typedef unsigned long Window; +#endif + +#ifdef VK_USE_PLATFORM_WAYLAND_KHR +#include +#endif + struct RSXDebuggerProgram { u32 id; @@ -34,6 +47,17 @@ using RSXDebuggerPrograms = std::vector; using draw_context_t = void*; +#ifdef _WIN32 + using display_handle_t = HWND; +#else + using display_handle_t = std::variant< + std::pair +#ifdef VK_USE_PLATFORM_WAYLAND_KHR + , std::pair +#endif + >; +#endif + class GSFrameBase { public: @@ -52,7 +76,7 @@ public: virtual int client_width() = 0; virtual int client_height() = 0; - virtual void* handle() const = 0; + virtual display_handle_t handle() const = 0; protected: diff --git a/rpcs3/Emu/RSX/VK/VKGSRender.cpp b/rpcs3/Emu/RSX/VK/VKGSRender.cpp index eb84c0f93a..2edccd8d43 100644 --- a/rpcs3/Emu/RSX/VK/VKGSRender.cpp +++ b/rpcs3/Emu/RSX/VK/VKGSRender.cpp @@ -494,12 +494,8 @@ VKGSRender::VKGSRender() : GSRender() return; } -#ifdef _WIN32 - HINSTANCE hInstance = NULL; - HWND hWnd = (HWND)m_frame->handle(); + std::vector& gpus = m_thread_context.enumerateDevices(); - std::vector& gpus = m_thread_context.enumerateDevices(); - //Actually confirm that the loader found at least one compatible device //This should not happen unless something is wrong with the driver setup on the target system if (gpus.size() == 0) @@ -512,11 +508,18 @@ VKGSRender::VKGSRender() : GSRender() bool gpu_found = false; std::string adapter_name = g_cfg.video.vk.adapter; + + display_handle_t display = m_frame->handle(); + +#ifdef _WIN32 + + HINSTANCE hInstance = NULL; + for (auto &gpu : gpus) { if (gpu.name() == adapter_name) { - m_swap_chain = m_thread_context.createSwapChain(hInstance, hWnd, gpu); + m_swap_chain = m_thread_context.createSwapChain(hInstance, display, gpu); gpu_found = true; break; } @@ -524,35 +527,18 @@ VKGSRender::VKGSRender() : GSRender() if (!gpu_found || adapter_name.empty()) { - m_swap_chain = m_thread_context.createSwapChain(hInstance, hWnd, gpus[0]); + m_swap_chain = m_thread_context.createSwapChain(hInstance, display, gpus[0]); } - + #elif HAVE_VULKAN - Window window = (Window)m_frame->handle(); - Display *display = XOpenDisplay(0); + display.match([](std::pair p) { XFlush(p.first); }, [](auto _) {}); - std::vector& gpus = m_thread_context.enumerateDevices(); - - //Actually confirm that the loader found at least one compatible device - //This should not happen unless something is wrong with the driver setup on the target system - if (gpus.size() == 0) - { - //We can't throw in Emulator::Load, so we show error and return - LOG_FATAL(RSX, "No compatible GPU devices found"); - m_device = VK_NULL_HANDLE; - return; - } - - XFlush(display); - - bool gpu_found = false; - std::string adapter_name = g_cfg.video.vk.adapter; for (auto &gpu : gpus) { if (gpu.name() == adapter_name) { - m_swap_chain = m_thread_context.createSwapChain(display, window, gpu); + m_swap_chain = m_thread_context.createSwapChain(display, gpu); gpu_found = true; break; } @@ -560,10 +546,10 @@ VKGSRender::VKGSRender() : GSRender() if (!gpu_found || adapter_name.empty()) { - m_swap_chain = m_thread_context.createSwapChain(display, window, gpus[0]); + m_swap_chain = m_thread_context.createSwapChain(display, gpus[0]); } - - m_display_handle = display; + + display.match([&](std::pair p) { m_display_handle = p.first; }, [](auto _) {}); #endif diff --git a/rpcs3/Emu/RSX/VK/VKHelpers.h b/rpcs3/Emu/RSX/VK/VKHelpers.h index 0b158b8d86..8ad89f7ccc 100644 --- a/rpcs3/Emu/RSX/VK/VKHelpers.h +++ b/rpcs3/Emu/RSX/VK/VKHelpers.h @@ -8,10 +8,8 @@ #include #include -#ifdef __linux__ -#include -#endif - +#include "Utilities/variant.hpp" +#include "Emu/RSX/GSRender.h" #include "Emu/System.h" #include "VulkanAPI.h" #include "../GCM.h" @@ -199,7 +197,7 @@ namespace vk //Set up instance information const char *requested_extensions[] = { - "VK_KHR_swapchain" + VK_KHR_SWAPCHAIN_EXTENSION_NAME }; std::vector layers; @@ -324,7 +322,7 @@ namespace vk VkDevice dev = (VkDevice)(*owner); u32 access_mask = 0; - + if (host_visible) access_mask |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; @@ -412,7 +410,7 @@ namespace vk VkMemoryRequirements memory_req; vkGetImageMemoryRequirements(m_device, value, &memory_req); - + if (!(memory_req.memoryTypeBits & (1 << memory_type_index))) { //Suggested memory type is incompatible with this memory type. @@ -953,7 +951,7 @@ namespace vk nb_swap_images = 0; getSwapchainImagesKHR(dev, m_vk_swapchain, &nb_swap_images, nullptr); - + if (!nb_swap_images) fmt::throw_exception("Driver returned 0 images for swapchain" HERE); std::vector swap_images; @@ -1185,11 +1183,11 @@ namespace vk m_instance = nullptr; m_vk_instances.resize(0); } - + void enable_debugging() { if (!g_cfg.video.debug_output) return; - + PFN_vkDebugReportCallbackEXT callback = vk::dbgFunc; createDebugReportCallback = (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(m_instance, "vkCreateDebugReportCallbackEXT"); @@ -1220,13 +1218,16 @@ namespace vk //Set up instance information const char *requested_extensions[] = { - "VK_KHR_surface", + VK_KHR_SURFACE_EXTENSION_NAME, #ifdef _WIN32 - "VK_KHR_win32_surface", + VK_KHR_WIN32_SURFACE_EXTENSION_NAME, #else - "VK_KHR_xlib_surface", + VK_KHR_XLIB_SURFACE_EXTENSION_NAME, #endif - "VK_EXT_debug_report", +#ifdef VK_USE_PLATFORM_WAYLAND_KHR + VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME, +#endif + VK_EXT_DEBUG_REPORT_EXTENSION_NAME, }; std::vector layers; @@ -1239,7 +1240,7 @@ namespace vk instance_info.pApplicationInfo = &app; instance_info.enabledLayerCount = static_cast(layers.size()); instance_info.ppEnabledLayerNames = layers.data(); - instance_info.enabledExtensionCount = fast? 0: 3; + instance_info.enabledExtensionCount = fast? 0: sizeof(requested_extensions)/sizeof(char*); instance_info.ppEnabledExtensionNames = fast? nullptr: requested_extensions; VkInstance instance; @@ -1304,8 +1305,8 @@ namespace vk } #ifdef _WIN32 - - vk::swap_chain* createSwapChain(HINSTANCE hInstance, HWND hWnd, vk::physical_device &dev) + + vk::swap_chain* createSwapChain(HINSTANCE hInstance, display_handle_t hWnd, vk::physical_device &dev) { VkWin32SurfaceCreateInfoKHR createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; @@ -1315,16 +1316,31 @@ namespace vk VkSurfaceKHR surface; CHECK_RESULT(vkCreateWin32SurfaceKHR(m_instance, &createInfo, NULL, &surface)); #elif HAVE_VULKAN - - vk::swap_chain* createSwapChain(Display *display, Window window, vk::physical_device &dev) + + vk::swap_chain* createSwapChain(display_handle_t ctx, vk::physical_device &dev) { - VkXlibSurfaceCreateInfoKHR createInfo = {}; - createInfo.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; - createInfo.dpy = display; - createInfo.window = window; - VkSurfaceKHR surface; - CHECK_RESULT(vkCreateXlibSurfaceKHR(m_instance, &createInfo, nullptr, &surface)); + + ctx.match( + [&](std::pair p) + { + VkXlibSurfaceCreateInfoKHR createInfo = {}; + createInfo.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; + createInfo.dpy = p.first; + createInfo.window = p.second; + CHECK_RESULT(vkCreateXlibSurfaceKHR(this->m_instance, &createInfo, nullptr, &surface)); + } +#ifdef VK_USE_PLATFORM_WAYLAND_KHR + , [&](std::pair p) + { + VkWaylandSurfaceCreateInfoKHR createInfo = {}; + createInfo.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR; + createInfo.display = p.first; + createInfo.surface = p.second; + CHECK_RESULT(vkCreateWaylandSurfaceKHR(this->m_instance, &createInfo, nullptr, &surface)); + } +#endif + ); #endif uint32_t device_queues = dev.get_queue_count(); @@ -1439,7 +1455,7 @@ namespace vk void destroy() { if (!pool) return; - + vkDestroyDescriptorPool((*owner), pool, nullptr); owner = nullptr; pool = nullptr; @@ -1591,7 +1607,7 @@ namespace vk { ::glsl::program_domain domain; program_input_type type; - + bound_buffer as_buffer; bound_sampler as_sampler; diff --git a/rpcs3/cmake_modules/FindWayland.cmake b/rpcs3/cmake_modules/FindWayland.cmake new file mode 100644 index 0000000000..f93218b873 --- /dev/null +++ b/rpcs3/cmake_modules/FindWayland.cmake @@ -0,0 +1,66 @@ +# Try to find Wayland on a Unix system +# +# This will define: +# +# WAYLAND_FOUND - True if Wayland is found +# WAYLAND_LIBRARIES - Link these to use Wayland +# WAYLAND_INCLUDE_DIR - Include directory for Wayland +# WAYLAND_DEFINITIONS - Compiler flags for using Wayland +# +# In addition the following more fine grained variables will be defined: +# +# WAYLAND_CLIENT_FOUND WAYLAND_CLIENT_INCLUDE_DIR WAYLAND_CLIENT_LIBRARIES +# WAYLAND_SERVER_FOUND WAYLAND_SERVER_INCLUDE_DIR WAYLAND_SERVER_LIBRARIES +# WAYLAND_EGL_FOUND WAYLAND_EGL_INCLUDE_DIR WAYLAND_EGL_LIBRARIES +# +# Copyright (c) 2013 Martin Gräßlin +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + +IF (NOT WIN32) + IF (WAYLAND_INCLUDE_DIR AND WAYLAND_LIBRARIES) + # In the cache already + SET(WAYLAND_FIND_QUIETLY TRUE) + ENDIF () + + # Use pkg-config to get the directories and then use these values + # in the FIND_PATH() and FIND_LIBRARY() calls + FIND_PACKAGE(PkgConfig) + PKG_CHECK_MODULES(PKG_WAYLAND QUIET wayland-client wayland-server wayland-egl wayland-cursor) + + SET(WAYLAND_DEFINITIONS ${PKG_WAYLAND_CFLAGS}) + + FIND_PATH(WAYLAND_CLIENT_INCLUDE_DIR NAMES wayland-client.h HINTS ${PKG_WAYLAND_INCLUDE_DIRS}) + FIND_PATH(WAYLAND_SERVER_INCLUDE_DIR NAMES wayland-server.h HINTS ${PKG_WAYLAND_INCLUDE_DIRS}) + FIND_PATH(WAYLAND_EGL_INCLUDE_DIR NAMES wayland-egl.h HINTS ${PKG_WAYLAND_INCLUDE_DIRS}) + FIND_PATH(WAYLAND_CURSOR_INCLUDE_DIR NAMES wayland-cursor.h HINTS ${PKG_WAYLAND_INCLUDE_DIRS}) + + FIND_LIBRARY(WAYLAND_CLIENT_LIBRARIES NAMES wayland-client HINTS ${PKG_WAYLAND_LIBRARY_DIRS}) + FIND_LIBRARY(WAYLAND_SERVER_LIBRARIES NAMES wayland-server HINTS ${PKG_WAYLAND_LIBRARY_DIRS}) + FIND_LIBRARY(WAYLAND_EGL_LIBRARIES NAMES wayland-egl HINTS ${PKG_WAYLAND_LIBRARY_DIRS}) + FIND_LIBRARY(WAYLAND_CURSOR_LIBRARIES NAMES wayland-cursor HINTS ${PKG_WAYLAND_LIBRARY_DIRS}) + + set(WAYLAND_INCLUDE_DIR ${WAYLAND_CLIENT_INCLUDE_DIR} ${WAYLAND_SERVER_INCLUDE_DIR} ${WAYLAND_EGL_INCLUDE_DIR} ${WAYLAND_CURSOR_INCLUDE_DIR}) + + set(WAYLAND_LIBRARIES ${WAYLAND_CLIENT_LIBRARIES} ${WAYLAND_SERVER_LIBRARIES} ${WAYLAND_EGL_LIBRARIES} ${WAYLAND_CURSOR_LIBRARIES}) + + list(REMOVE_DUPLICATES WAYLAND_INCLUDE_DIR) + + include(FindPackageHandleStandardArgs) + + FIND_PACKAGE_HANDLE_STANDARD_ARGS(WAYLAND_CLIENT DEFAULT_MSG WAYLAND_CLIENT_LIBRARIES WAYLAND_CLIENT_INCLUDE_DIR) + FIND_PACKAGE_HANDLE_STANDARD_ARGS(WAYLAND_SERVER DEFAULT_MSG WAYLAND_SERVER_LIBRARIES WAYLAND_SERVER_INCLUDE_DIR) + FIND_PACKAGE_HANDLE_STANDARD_ARGS(WAYLAND_EGL DEFAULT_MSG WAYLAND_EGL_LIBRARIES WAYLAND_EGL_INCLUDE_DIR) + FIND_PACKAGE_HANDLE_STANDARD_ARGS(WAYLAND_CURSOR DEFAULT_MSG WAYLAND_CURSOR_LIBRARIES WAYLAND_CURSOR_INCLUDE_DIR) + FIND_PACKAGE_HANDLE_STANDARD_ARGS(WAYLAND DEFAULT_MSG WAYLAND_LIBRARIES WAYLAND_INCLUDE_DIR) + + MARK_AS_ADVANCED( + WAYLAND_INCLUDE_DIR WAYLAND_LIBRARIES + WAYLAND_CLIENT_INCLUDE_DIR WAYLAND_CLIENT_LIBRARIES + WAYLAND_SERVER_INCLUDE_DIR WAYLAND_SERVER_LIBRARIES + WAYLAND_EGL_INCLUDE_DIR WAYLAND_EGL_LIBRARIES + WAYLAND_CURSOR_INCLUDE_DIR WAYLAND_CURSOR_LIBRARIES + ) + +ENDIF () diff --git a/rpcs3/rpcs3qt/gs_frame.cpp b/rpcs3/rpcs3qt/gs_frame.cpp index 96277edd80..e0dcaaa6fc 100644 --- a/rpcs3/rpcs3qt/gs_frame.cpp +++ b/rpcs3/rpcs3qt/gs_frame.cpp @@ -8,6 +8,13 @@ #include #include +#ifdef VK_USE_PLATFORM_WAYLAND_KHR +#include +#include +#endif + +#include + #include #include "rpcs3_version.h" @@ -145,12 +152,28 @@ void gs_frame::show() }); } -void* gs_frame::handle() const +display_handle_t gs_frame::handle() const { #ifdef _WIN32 return (HWND) this->winId(); #else - return (void *)this->winId(); +#ifdef VK_USE_PLATFORM_WAYLAND_KHR + QPlatformNativeInterface *native = QGuiApplication::platformNativeInterface(); + struct wl_display *wl_dpy = static_cast( + native->nativeResourceForWindow("display", NULL)); + struct wl_surface *wl_surf = static_cast( + native->nativeResourceForWindow("surface", (QWindow *)this)); + if (wl_dpy != nullptr && wl_surf != nullptr) + { + return std::make_pair(wl_dpy, wl_surf); + } + else + { +#endif + return std::make_pair(XOpenDisplay(0), (unsigned long)(this->winId())); +#ifdef VK_USE_PLATFORM_WAYLAND_KHR + } +#endif #endif } diff --git a/rpcs3/rpcs3qt/gs_frame.h b/rpcs3/rpcs3qt/gs_frame.h index 4f30e447b8..1122c60459 100644 --- a/rpcs3/rpcs3qt/gs_frame.h +++ b/rpcs3/rpcs3qt/gs_frame.h @@ -40,7 +40,7 @@ protected: void show() override; void mouseDoubleClickEvent(QMouseEvent* ev) override; - void* handle() const override; + display_handle_t handle() const override; void flip(draw_context_t context, bool skip_frame=false) override; int client_width() override;