diff options
author | bunnei <bunneidev@gmail.com> | 2020-04-11 02:06:55 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-04-11 02:06:55 +0200 |
commit | 51c6688e214a9bdcf335fb0598dc7e5bc194f8f6 (patch) | |
tree | 37ef2214634c2423ca617e9e22a2c9d57693ed86 | |
parent | Merge pull request #3607 from FearlessTobi/input-kms (diff) | |
parent | yuzu: Drop SDL2 and Qt frontend Vulkan requirements (diff) | |
download | yuzu-51c6688e214a9bdcf335fb0598dc7e5bc194f8f6.tar yuzu-51c6688e214a9bdcf335fb0598dc7e5bc194f8f6.tar.gz yuzu-51c6688e214a9bdcf335fb0598dc7e5bc194f8f6.tar.bz2 yuzu-51c6688e214a9bdcf335fb0598dc7e5bc194f8f6.tar.lz yuzu-51c6688e214a9bdcf335fb0598dc7e5bc194f8f6.tar.xz yuzu-51c6688e214a9bdcf335fb0598dc7e5bc194f8f6.tar.zst yuzu-51c6688e214a9bdcf335fb0598dc7e5bc194f8f6.zip |
-rw-r--r-- | src/common/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/common/dynamic_library.cpp | 106 | ||||
-rw-r--r-- | src/common/dynamic_library.h | 75 | ||||
-rw-r--r-- | src/core/frontend/emu_window.h | 41 | ||||
-rw-r--r-- | src/video_core/renderer_vulkan/declarations.h | 2 | ||||
-rw-r--r-- | src/video_core/renderer_vulkan/renderer_vulkan.cpp | 264 | ||||
-rw-r--r-- | src/video_core/renderer_vulkan/renderer_vulkan.h | 19 | ||||
-rw-r--r-- | src/video_core/renderer_vulkan/vk_device.cpp | 92 | ||||
-rw-r--r-- | src/video_core/renderer_vulkan/vk_device.h | 21 | ||||
-rw-r--r-- | src/yuzu/CMakeLists.txt | 4 | ||||
-rw-r--r-- | src/yuzu/bootmanager.cpp | 92 | ||||
-rw-r--r-- | src/yuzu/bootmanager.h | 9 | ||||
-rw-r--r-- | src/yuzu/configuration/configure_graphics.cpp | 42 | ||||
-rw-r--r-- | src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp | 6 | ||||
-rw-r--r-- | src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h | 4 | ||||
-rw-r--r-- | src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp | 159 | ||||
-rw-r--r-- | src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h | 18 | ||||
-rw-r--r-- | src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp | 4 | ||||
-rw-r--r-- | src/yuzu_tester/emu_window/emu_window_sdl2_hide.h | 4 |
19 files changed, 624 insertions, 340 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index fbebed715..eeceaa655 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -106,6 +106,8 @@ add_library(common STATIC common_funcs.h common_paths.h common_types.h + dynamic_library.cpp + dynamic_library.h file_util.cpp file_util.h hash.h diff --git a/src/common/dynamic_library.cpp b/src/common/dynamic_library.cpp new file mode 100644 index 000000000..7ab54e9e4 --- /dev/null +++ b/src/common/dynamic_library.cpp @@ -0,0 +1,106 @@ +// Copyright 2019 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include <cstring> +#include <string> +#include <utility> + +#include <fmt/format.h> + +#include "common/dynamic_library.h" + +#ifdef _WIN32 +#include <windows.h> +#else +#include <dlfcn.h> +#endif + +namespace Common { + +DynamicLibrary::DynamicLibrary() = default; + +DynamicLibrary::DynamicLibrary(const char* filename) { + Open(filename); +} + +DynamicLibrary::DynamicLibrary(DynamicLibrary&& rhs) noexcept + : handle{std::exchange(rhs.handle, nullptr)} {} + +DynamicLibrary& DynamicLibrary::operator=(DynamicLibrary&& rhs) noexcept { + Close(); + handle = std::exchange(rhs.handle, nullptr); + return *this; +} + +DynamicLibrary::~DynamicLibrary() { + Close(); +} + +std::string DynamicLibrary::GetUnprefixedFilename(const char* filename) { +#if defined(_WIN32) + return std::string(filename) + ".dll"; +#elif defined(__APPLE__) + return std::string(filename) + ".dylib"; +#else + return std::string(filename) + ".so"; +#endif +} + +std::string DynamicLibrary::GetVersionedFilename(const char* libname, int major, int minor) { +#if defined(_WIN32) + if (major >= 0 && minor >= 0) + return fmt::format("{}-{}-{}.dll", libname, major, minor); + else if (major >= 0) + return fmt::format("{}-{}.dll", libname, major); + else + return fmt::format("{}.dll", libname); +#elif defined(__APPLE__) + const char* prefix = std::strncmp(libname, "lib", 3) ? "lib" : ""; + if (major >= 0 && minor >= 0) + return fmt::format("{}{}.{}.{}.dylib", prefix, libname, major, minor); + else if (major >= 0) + return fmt::format("{}{}.{}.dylib", prefix, libname, major); + else + return fmt::format("{}{}.dylib", prefix, libname); +#else + const char* prefix = std::strncmp(libname, "lib", 3) ? "lib" : ""; + if (major >= 0 && minor >= 0) + return fmt::format("{}{}.so.{}.{}", prefix, libname, major, minor); + else if (major >= 0) + return fmt::format("{}{}.so.{}", prefix, libname, major); + else + return fmt::format("{}{}.so", prefix, libname); +#endif +} + +bool DynamicLibrary::Open(const char* filename) { +#ifdef _WIN32 + handle = reinterpret_cast<void*>(LoadLibraryA(filename)); +#else + handle = dlopen(filename, RTLD_NOW); +#endif + return handle != nullptr; +} + +void DynamicLibrary::Close() { + if (!IsOpen()) + return; + +#ifdef _WIN32 + FreeLibrary(reinterpret_cast<HMODULE>(handle)); +#else + dlclose(handle); +#endif + handle = nullptr; +} + +void* DynamicLibrary::GetSymbolAddress(const char* name) const { +#ifdef _WIN32 + return reinterpret_cast<void*>(GetProcAddress(reinterpret_cast<HMODULE>(handle), name)); +#else + return reinterpret_cast<void*>(dlsym(handle, name)); +#endif +} + +} // namespace Common diff --git a/src/common/dynamic_library.h b/src/common/dynamic_library.h new file mode 100644 index 000000000..2a06372fd --- /dev/null +++ b/src/common/dynamic_library.h @@ -0,0 +1,75 @@ +// Copyright 2019 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include <string> + +namespace Common { + +/** + * Provides a platform-independent interface for loading a dynamic library and retrieving symbols. + * The interface maintains an internal reference count to allow one handle to be shared between + * multiple users. + */ +class DynamicLibrary final { +public: + /// Default constructor, does not load a library. + explicit DynamicLibrary(); + + /// Automatically loads the specified library. Call IsOpen() to check validity before use. + explicit DynamicLibrary(const char* filename); + + /// Moves the library. + DynamicLibrary(DynamicLibrary&&) noexcept; + DynamicLibrary& operator=(DynamicLibrary&&) noexcept; + + /// Delete copies, we can't copy a dynamic library. + DynamicLibrary(const DynamicLibrary&) = delete; + DynamicLibrary& operator=(const DynamicLibrary&) = delete; + + /// Closes the library. + ~DynamicLibrary(); + + /// Returns the specified library name with the platform-specific suffix added. + static std::string GetUnprefixedFilename(const char* filename); + + /// Returns the specified library name in platform-specific format. + /// Major/minor versions will not be included if set to -1. + /// If libname already contains the "lib" prefix, it will not be added again. + /// Windows: LIBNAME-MAJOR-MINOR.dll + /// Linux: libLIBNAME.so.MAJOR.MINOR + /// Mac: libLIBNAME.MAJOR.MINOR.dylib + static std::string GetVersionedFilename(const char* libname, int major = -1, int minor = -1); + + /// Returns true if a module is loaded, otherwise false. + bool IsOpen() const { + return handle != nullptr; + } + + /// Loads (or replaces) the handle with the specified library file name. + /// Returns true if the library was loaded and can be used. + bool Open(const char* filename); + + /// Unloads the library, any function pointers from this library are no longer valid. + void Close(); + + /// Returns the address of the specified symbol (function or variable) as an untyped pointer. + /// If the specified symbol does not exist in this library, nullptr is returned. + void* GetSymbolAddress(const char* name) const; + + /// Obtains the address of the specified symbol, automatically casting to the correct type. + /// Returns true if the symbol was found and assigned, otherwise false. + template <typename T> + bool GetSymbol(const char* name, T* ptr) const { + *ptr = reinterpret_cast<T>(GetSymbolAddress(name)); + return *ptr != nullptr; + } + +private: + /// Platform-dependent data type representing a dynamic library handle. + void* handle = nullptr; +}; + +} // namespace Common diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h index 72294d4d8..13aa14934 100644 --- a/src/core/frontend/emu_window.h +++ b/src/core/frontend/emu_window.h @@ -12,6 +12,15 @@ namespace Core::Frontend { +/// Information for the Graphics Backends signifying what type of screen pointer is in +/// WindowInformation +enum class WindowSystemType { + Headless, + Windows, + X11, + Wayland, +}; + /** * Represents a drawing context that supports graphics operations. */ @@ -76,6 +85,23 @@ public: std::pair<unsigned, unsigned> min_client_area_size; }; + /// Data describing host window system information + struct WindowSystemInfo { + // Window system type. Determines which GL context or Vulkan WSI is used. + WindowSystemType type = WindowSystemType::Headless; + + // Connection to a display server. This is used on X11 and Wayland platforms. + void* display_connection = nullptr; + + // Render surface. This is a pointer to the native window handle, which depends + // on the platform. e.g. HWND for Windows, Window for X11. If the surface is + // set to nullptr, the video backend will run in headless mode. + void* render_surface = nullptr; + + // Scale of the render surface. For hidpi systems, this will be >1. + float render_surface_scale = 1.0f; + }; + /// Polls window events virtual void PollEvents() = 0; @@ -87,10 +113,6 @@ public: /// Returns if window is shown (not minimized) virtual bool IsShown() const = 0; - /// Retrieves Vulkan specific handlers from the window - virtual void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, - void* surface) const = 0; - /** * Signal that a touch pressed event has occurred (e.g. mouse click pressed) * @param framebuffer_x Framebuffer x-coordinate that was pressed @@ -128,6 +150,13 @@ public: } /** + * Returns system information about the drawing area. + */ + const WindowSystemInfo& GetWindowInfo() const { + return window_info; + } + + /** * Gets the framebuffer layout (width, height, and screen regions) * @note This method is thread-safe */ @@ -142,7 +171,7 @@ public: void UpdateCurrentFramebufferLayout(unsigned width, unsigned height); protected: - EmuWindow(); + explicit EmuWindow(); virtual ~EmuWindow(); /** @@ -179,6 +208,8 @@ protected: client_area_height = size.second; } + WindowSystemInfo window_info; + private: /** * Handler called when the minimal client area was requested to be changed via SetConfig. diff --git a/src/video_core/renderer_vulkan/declarations.h b/src/video_core/renderer_vulkan/declarations.h index 323bf6b39..89a035ca4 100644 --- a/src/video_core/renderer_vulkan/declarations.h +++ b/src/video_core/renderer_vulkan/declarations.h @@ -39,6 +39,7 @@ using UniqueFence = UniqueHandle<vk::Fence>; using UniqueFramebuffer = UniqueHandle<vk::Framebuffer>; using UniqueImage = UniqueHandle<vk::Image>; using UniqueImageView = UniqueHandle<vk::ImageView>; +using UniqueInstance = UniqueHandle<vk::Instance>; using UniqueIndirectCommandsLayoutNVX = UniqueHandle<vk::IndirectCommandsLayoutNVX>; using UniqueObjectTableNVX = UniqueHandle<vk::ObjectTableNVX>; using UniquePipeline = UniqueHandle<vk::Pipeline>; @@ -50,6 +51,7 @@ using UniqueSampler = UniqueHandle<vk::Sampler>; using UniqueSamplerYcbcrConversion = UniqueHandle<vk::SamplerYcbcrConversion>; using UniqueSemaphore = UniqueHandle<vk::Semaphore>; using UniqueShaderModule = UniqueHandle<vk::ShaderModule>; +using UniqueSurfaceKHR = UniqueHandle<vk::SurfaceKHR>; using UniqueSwapchainKHR = UniqueHandle<vk::SwapchainKHR>; using UniqueValidationCacheEXT = UniqueHandle<vk::ValidationCacheEXT>; using UniqueDebugReportCallbackEXT = UniqueHandle<vk::DebugReportCallbackEXT>; diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index 6953aaafe..9cdb4b627 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -2,13 +2,18 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <algorithm> +#include <array> +#include <cstring> #include <memory> #include <optional> +#include <string> #include <vector> #include <fmt/format.h> #include "common/assert.h" +#include "common/dynamic_library.h" #include "common/logging/log.h" #include "common/telemetry.h" #include "core/core.h" @@ -30,15 +35,30 @@ #include "video_core/renderer_vulkan/vk_state_tracker.h" #include "video_core/renderer_vulkan/vk_swapchain.h" +// Include these late to avoid changing Vulkan-Hpp's dynamic dispatcher size +#ifdef _WIN32 +#include <windows.h> +// ensure include order +#include <vulkan/vulkan_win32.h> +#endif + +#ifdef __linux__ +#include <X11/Xlib.h> +#include <vulkan/vulkan_wayland.h> +#include <vulkan/vulkan_xlib.h> +#endif + namespace Vulkan { namespace { +using Core::Frontend::WindowSystemType; + VkBool32 DebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity_, VkDebugUtilsMessageTypeFlagsEXT type, const VkDebugUtilsMessengerCallbackDataEXT* data, [[maybe_unused]] void* user_data) { - const vk::DebugUtilsMessageSeverityFlagBitsEXT severity{severity_}; + const auto severity{static_cast<vk::DebugUtilsMessageSeverityFlagBitsEXT>(severity_)}; const char* message{data->pMessage}; if (severity & vk::DebugUtilsMessageSeverityFlagBitsEXT::eError) { @@ -53,6 +73,110 @@ VkBool32 DebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity_, return VK_FALSE; } +Common::DynamicLibrary OpenVulkanLibrary() { + Common::DynamicLibrary library; +#ifdef __APPLE__ + // Check if a path to a specific Vulkan library has been specified. + char* libvulkan_env = getenv("LIBVULKAN_PATH"); + if (!libvulkan_env || !library.Open(libvulkan_env)) { + // Use the libvulkan.dylib from the application bundle. + std::string filename = File::GetBundleDirectory() + "/Contents/Frameworks/libvulkan.dylib"; + library.Open(filename.c_str()); + } +#else + std::string filename = Common::DynamicLibrary::GetVersionedFilename("vulkan", 1); + if (!library.Open(filename.c_str())) { + // Android devices may not have libvulkan.so.1, only libvulkan.so. + filename = Common::DynamicLibrary::GetVersionedFilename("vulkan"); + library.Open(filename.c_str()); + } +#endif + return library; +} + +UniqueInstance CreateInstance(Common::DynamicLibrary& library, vk::DispatchLoaderDynamic& dld, + WindowSystemType window_type = WindowSystemType::Headless, + bool enable_layers = false) { + if (!library.IsOpen()) { + LOG_ERROR(Render_Vulkan, "Vulkan library not available"); + return UniqueInstance{}; + } + PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr; + if (!library.GetSymbol("vkGetInstanceProcAddr", &vkGetInstanceProcAddr)) { + LOG_ERROR(Render_Vulkan, "vkGetInstanceProcAddr not present in Vulkan"); + return UniqueInstance{}; + } + dld.init(vkGetInstanceProcAddr); + + std::vector<const char*> extensions; + extensions.reserve(4); + switch (window_type) { + case Core::Frontend::WindowSystemType::Headless: + break; +#ifdef _WIN32 + case Core::Frontend::WindowSystemType::Windows: + extensions.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME); + break; +#endif +#ifdef __linux__ + case Core::Frontend::WindowSystemType::X11: + extensions.push_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME); + break; + case Core::Frontend::WindowSystemType::Wayland: + extensions.push_back(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME); + break; +#endif + default: + LOG_ERROR(Render_Vulkan, "Presentation not supported on this platform"); + break; + } + if (window_type != Core::Frontend::WindowSystemType::Headless) { + extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME); + } + if (enable_layers) { + extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); + } + + u32 num_properties; + if (vk::enumerateInstanceExtensionProperties(nullptr, &num_properties, nullptr, dld) != + vk::Result::eSuccess) { + LOG_ERROR(Render_Vulkan, "Failed to query number of extension properties"); + return UniqueInstance{}; + } + std::vector<vk::ExtensionProperties> properties(num_properties); + if (vk::enumerateInstanceExtensionProperties(nullptr, &num_properties, properties.data(), + dld) != vk::Result::eSuccess) { + LOG_ERROR(Render_Vulkan, "Failed to query extension properties"); + return UniqueInstance{}; + } + + for (const char* extension : extensions) { + const auto it = + std::find_if(properties.begin(), properties.end(), [extension](const auto& prop) { + return !std::strcmp(extension, prop.extensionName); + }); + if (it == properties.end()) { + LOG_ERROR(Render_Vulkan, "Required instance extension {} is not available", extension); + return UniqueInstance{}; + } + } + + const vk::ApplicationInfo application_info("yuzu Emulator", VK_MAKE_VERSION(0, 1, 0), + "yuzu Emulator", VK_MAKE_VERSION(0, 1, 0), + VK_API_VERSION_1_1); + const std::array layers = {"VK_LAYER_LUNARG_standard_validation"}; + const vk::InstanceCreateInfo instance_ci( + {}, &application_info, enable_layers ? static_cast<u32>(layers.size()) : 0, layers.data(), + static_cast<u32>(extensions.size()), extensions.data()); + vk::Instance unsafe_instance; + if (vk::createInstance(&instance_ci, nullptr, &unsafe_instance, dld) != vk::Result::eSuccess) { + LOG_ERROR(Render_Vulkan, "Failed to create Vulkan instance"); + return UniqueInstance{}; + } + dld.init(unsafe_instance); + return UniqueInstance(unsafe_instance, {nullptr, dld}); +} + std::string GetReadableVersion(u32 version) { return fmt::format("{}.{}.{}", VK_VERSION_MAJOR(version), VK_VERSION_MINOR(version), VK_VERSION_PATCH(version)); @@ -147,27 +271,12 @@ bool RendererVulkan::TryPresent(int /*timeout_ms*/) { } bool RendererVulkan::Init() { - PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr{}; - render_window.RetrieveVulkanHandlers(&vkGetInstanceProcAddr, &instance, &surface); - const vk::DispatchLoaderDynamic dldi(instance, vkGetInstanceProcAddr); - - std::optional<vk::DebugUtilsMessengerEXT> callback; - if (Settings::values.renderer_debug && dldi.vkCreateDebugUtilsMessengerEXT) { - callback = CreateDebugCallback(dldi); - if (!callback) { - return false; - } - } - - if (!PickDevices(dldi)) { - if (callback) { - instance.destroy(*callback, nullptr, dldi); - } + library = OpenVulkanLibrary(); + instance = CreateInstance(library, dld, render_window.GetWindowInfo().type, + Settings::values.renderer_debug); + if (!instance || !CreateDebugCallback() || !CreateSurface() || !PickDevices()) { return false; } - debug_callback = UniqueDebugUtilsMessengerEXT( - *callback, vk::ObjectDestroy<vk::Instance, vk::DispatchLoaderDynamic>( - instance, nullptr, device->GetDispatchLoader())); Report(); @@ -176,7 +285,7 @@ bool RendererVulkan::Init() { resource_manager = std::make_unique<VKResourceManager>(*device); const auto& framebuffer = render_window.GetFramebufferLayout(); - swapchain = std::make_unique<VKSwapchain>(surface, *device); + swapchain = std::make_unique<VKSwapchain>(*surface, *device); swapchain->Create(framebuffer.width, framebuffer.height, false); state_tracker = std::make_unique<StateTracker>(system); @@ -213,8 +322,10 @@ void RendererVulkan::ShutDown() { device.reset(); } -std::optional<vk::DebugUtilsMessengerEXT> RendererVulkan::CreateDebugCallback( - const vk::DispatchLoaderDynamic& dldi) { +bool RendererVulkan::CreateDebugCallback() { + if (!Settings::values.renderer_debug) { + return true; + } const vk::DebugUtilsMessengerCreateInfoEXT callback_ci( {}, vk::DebugUtilsMessageSeverityFlagBitsEXT::eError | @@ -225,32 +336,88 @@ std::optional<vk::DebugUtilsMessengerEXT> RendererVulkan::CreateDebugCallback( vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation | vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance, &DebugCallback, nullptr); - vk::DebugUtilsMessengerEXT callback; - if (instance.createDebugUtilsMessengerEXT(&callback_ci, nullptr, &callback, dldi) != + vk::DebugUtilsMessengerEXT unsafe_callback; + if (instance->createDebugUtilsMessengerEXT(&callback_ci, nullptr, &unsafe_callback, dld) != vk::Result::eSuccess) { LOG_ERROR(Render_Vulkan, "Failed to create debug callback"); - return {}; + return false; + } + debug_callback = UniqueDebugUtilsMessengerEXT(unsafe_callback, {*instance, nullptr, dld}); + return true; +} + +bool RendererVulkan::CreateSurface() { + [[maybe_unused]] const auto& window_info = render_window.GetWindowInfo(); + VkSurfaceKHR unsafe_surface = nullptr; + +#ifdef _WIN32 + if (window_info.type == Core::Frontend::WindowSystemType::Windows) { + const HWND hWnd = static_cast<HWND>(window_info.render_surface); + const VkWin32SurfaceCreateInfoKHR win32_ci{VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR, + nullptr, 0, nullptr, hWnd}; + const auto vkCreateWin32SurfaceKHR = reinterpret_cast<PFN_vkCreateWin32SurfaceKHR>( + dld.vkGetInstanceProcAddr(*instance, "vkCreateWin32SurfaceKHR")); + if (!vkCreateWin32SurfaceKHR || vkCreateWin32SurfaceKHR(instance.get(), &win32_ci, nullptr, + &unsafe_surface) != VK_SUCCESS) { + LOG_ERROR(Render_Vulkan, "Failed to initialize Win32 surface"); + return false; + } + } +#endif +#ifdef __linux__ + if (window_info.type == Core::Frontend::WindowSystemType::X11) { + const VkXlibSurfaceCreateInfoKHR xlib_ci{ + VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, nullptr, 0, + static_cast<Display*>(window_info.display_connection), + reinterpret_cast<Window>(window_info.render_surface)}; + const auto vkCreateXlibSurfaceKHR = reinterpret_cast<PFN_vkCreateXlibSurfaceKHR>( + dld.vkGetInstanceProcAddr(*instance, "vkCreateXlibSurfaceKHR")); + if (!vkCreateXlibSurfaceKHR || vkCreateXlibSurfaceKHR(instance.get(), &xlib_ci, nullptr, + &unsafe_surface) != VK_SUCCESS) { + LOG_ERROR(Render_Vulkan, "Failed to initialize Xlib surface"); + return false; + } + } + if (window_info.type == Core::Frontend::WindowSystemType::Wayland) { + const VkWaylandSurfaceCreateInfoKHR wayland_ci{ + VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR, nullptr, 0, + static_cast<wl_display*>(window_info.display_connection), + static_cast<wl_surface*>(window_info.render_surface)}; + const auto vkCreateWaylandSurfaceKHR = reinterpret_cast<PFN_vkCreateWaylandSurfaceKHR>( + dld.vkGetInstanceProcAddr(*instance, "vkCreateWaylandSurfaceKHR")); + if (!vkCreateWaylandSurfaceKHR || + vkCreateWaylandSurfaceKHR(instance.get(), &wayland_ci, nullptr, &unsafe_surface) != + VK_SUCCESS) { + LOG_ERROR(Render_Vulkan, "Failed to initialize Wayland surface"); + return false; + } + } +#endif + if (!unsafe_surface) { + LOG_ERROR(Render_Vulkan, "Presentation not supported on this platform"); + return false; } - return callback; + + surface = UniqueSurfaceKHR(unsafe_surface, {*instance, nullptr, dld}); + return true; } -bool RendererVulkan::PickDevices(const vk::DispatchLoaderDynamic& dldi) { - const auto devices = instance.enumeratePhysicalDevices(dldi); +bool RendererVulkan::PickDevices() { + const auto devices = instance->enumeratePhysicalDevices(dld); - // TODO(Rodrigo): Choose device from config file const s32 device_index = Settings::values.vulkan_device; if (device_index < 0 || device_index >= static_cast<s32>(devices.size())) { LOG_ERROR(Render_Vulkan, "Invalid device index {}!", device_index); return false; } - const vk::PhysicalDevice physical_device = devices[device_index]; + const vk::PhysicalDevice physical_device = devices[static_cast<std::size_t>(device_index)]; - if (!VKDevice::IsSuitable(dldi, physical_device, surface)) { + if (!VKDevice::IsSuitable(physical_device, *surface, dld)) { return false; } - device = std::make_unique<VKDevice>(dldi, physical_device, surface); - return device->Create(dldi, instance); + device = std::make_unique<VKDevice>(dld, physical_device, *surface); + return device->Create(*instance); } void RendererVulkan::Report() const { @@ -276,4 +443,33 @@ void RendererVulkan::Report() const { telemetry_session.AddField(field, "GPU_Vulkan_Extensions", extensions); } +std::vector<std::string> RendererVulkan::EnumerateDevices() { + // Avoid putting DispatchLoaderDynamic, it's too large + auto dld_memory = std::make_unique<vk::DispatchLoaderDynamic>(); + auto& dld = *dld_memory; + + Common::DynamicLibrary library = OpenVulkanLibrary(); + UniqueInstance instance = CreateInstance(library, dld); + if (!instance) { + return {}; + } + + u32 num_devices; + if (instance->enumeratePhysicalDevices(&num_devices, nullptr, dld) != vk::Result::eSuccess) { + return {}; + } + std::vector<vk::PhysicalDevice> devices(num_devices); + if (instance->enumeratePhysicalDevices(&num_devices, devices.data(), dld) != + vk::Result::eSuccess) { + return {}; + } + + std::vector<std::string> names; + names.reserve(num_devices); + for (auto& device : devices) { + names.push_back(device.getProperties(dld).deviceName); + } + return names; +} + } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h index d14384e79..42e253de5 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.h +++ b/src/video_core/renderer_vulkan/renderer_vulkan.h @@ -6,8 +6,11 @@ #include <memory> #include <optional> +#include <string> #include <vector> +#include "common/dynamic_library.h" + #include "video_core/renderer_base.h" #include "video_core/renderer_vulkan/declarations.h" @@ -44,18 +47,24 @@ public: void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; bool TryPresent(int timeout_ms) override; + static std::vector<std::string> EnumerateDevices(); + private: - std::optional<vk::DebugUtilsMessengerEXT> CreateDebugCallback( - const vk::DispatchLoaderDynamic& dldi); + bool CreateDebugCallback(); - bool PickDevices(const vk::DispatchLoaderDynamic& dldi); + bool CreateSurface(); + + bool PickDevices(); void Report() const; Core::System& system; - vk::Instance instance; - vk::SurfaceKHR surface; + Common::DynamicLibrary library; + vk::DispatchLoaderDynamic dld; + + UniqueInstance instance; + UniqueSurfaceKHR surface; VKScreenInfo screen_info; diff --git a/src/video_core/renderer_vulkan/vk_device.cpp b/src/video_core/renderer_vulkan/vk_device.cpp index 7aafb5e59..6f4ae9132 100644 --- a/src/video_core/renderer_vulkan/vk_device.cpp +++ b/src/video_core/renderer_vulkan/vk_device.cpp @@ -10,6 +10,7 @@ #include <string_view> #include <thread> #include <vector> + #include "common/assert.h" #include "core/settings.h" #include "video_core/renderer_vulkan/declarations.h" @@ -35,20 +36,20 @@ void SetNext(void**& next, T& data) { } template <typename T> -T GetFeatures(vk::PhysicalDevice physical, const vk::DispatchLoaderDynamic& dldi) { +T GetFeatures(vk::PhysicalDevice physical, const vk::DispatchLoaderDynamic& dld) { vk::PhysicalDeviceFeatures2 features; T extension_features; features.pNext = &extension_features; - physical.getFeatures2(&features, dldi); + physical.getFeatures2(&features, dld); return extension_features; } template <typename T> -T GetProperties(vk::PhysicalDevice physical, const vk::DispatchLoaderDynamic& dldi) { +T GetProperties(vk::PhysicalDevice physical, const vk::DispatchLoaderDynamic& dld) { vk::PhysicalDeviceProperties2 properties; T extension_properties; properties.pNext = &extension_properties; - physical.getProperties2(&properties, dldi); + physical.getProperties2(&properties, dld); return extension_properties; } @@ -78,19 +79,19 @@ vk::FormatFeatureFlags GetFormatFeatures(vk::FormatProperties properties, Format } // Anonymous namespace -VKDevice::VKDevice(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDevice physical, +VKDevice::VKDevice(const vk::DispatchLoaderDynamic& dld, vk::PhysicalDevice physical, vk::SurfaceKHR surface) - : physical{physical}, properties{physical.getProperties(dldi)}, - format_properties{GetFormatProperties(dldi, physical)} { - SetupFamilies(dldi, surface); - SetupFeatures(dldi); + : dld{dld}, physical{physical}, properties{physical.getProperties(dld)}, + format_properties{GetFormatProperties(dld, physical)} { + SetupFamilies(surface); + SetupFeatures(); } VKDevice::~VKDevice() = default; -bool VKDevice::Create(const vk::DispatchLoaderDynamic& dldi, vk::Instance instance) { +bool VKDevice::Create(vk::Instance instance) { const auto queue_cis = GetDeviceQueueCreateInfos(); - const std::vector extensions = LoadExtensions(dldi); + const std::vector extensions = LoadExtensions(); vk::PhysicalDeviceFeatures2 features2; void** next = &features2.pNext; @@ -165,15 +166,13 @@ bool VKDevice::Create(const vk::DispatchLoaderDynamic& dldi, vk::Instance instan nullptr); device_ci.pNext = &features2; - vk::Device dummy_logical; - if (physical.createDevice(&device_ci, nullptr, &dummy_logical, dldi) != vk::Result::eSuccess) { + vk::Device unsafe_logical; + if (physical.createDevice(&device_ci, nullptr, &unsafe_logical, dld) != vk::Result::eSuccess) { LOG_CRITICAL(Render_Vulkan, "Logical device failed to be created!"); return false; } - - dld.init(instance, dldi.vkGetInstanceProcAddr, dummy_logical, dldi.vkGetDeviceProcAddr); - logical = UniqueDevice( - dummy_logical, vk::ObjectDestroy<vk::NoParent, vk::DispatchLoaderDynamic>(nullptr, dld)); + dld.init(instance, dld.vkGetInstanceProcAddr, unsafe_logical); + logical = UniqueDevice(unsafe_logical, {nullptr, dld}); CollectTelemetryParameters(); @@ -235,8 +234,8 @@ void VKDevice::ReportLoss() const { // *(VKGraphicsPipeline*)data[0] } -bool VKDevice::IsOptimalAstcSupported(const vk::PhysicalDeviceFeatures& features, - const vk::DispatchLoaderDynamic& dldi) const { +bool VKDevice::IsOptimalAstcSupported(const vk::PhysicalDeviceFeatures& features) const { + // Disable for now to avoid converting ASTC twice. static constexpr std::array astc_formats = { vk::Format::eAstc4x4UnormBlock, vk::Format::eAstc4x4SrgbBlock, vk::Format::eAstc5x4UnormBlock, vk::Format::eAstc5x4SrgbBlock, @@ -260,7 +259,7 @@ bool VKDevice::IsOptimalAstcSupported(const vk::PhysicalDeviceFeatures& features vk::FormatFeatureFlagBits::eBlitDst | vk::FormatFeatureFlagBits::eTransferSrc | vk::FormatFeatureFlagBits::eTransferDst}; for (const auto format : astc_formats) { - const auto format_properties{physical.getFormatProperties(format, dldi)}; + const auto format_properties{physical.getFormatProperties(format, dld)}; if (!(format_properties.optimalTilingFeatures & format_feature_usage)) { return false; } @@ -279,11 +278,9 @@ bool VKDevice::IsFormatSupported(vk::Format wanted_format, vk::FormatFeatureFlag return (supported_usage & wanted_usage) == wanted_usage; } -bool VKDevice::IsSuitable(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDevice physical, - vk::SurfaceKHR surface) { - bool is_suitable = true; - - constexpr std::array required_extensions = { +bool VKDevice::IsSuitable(vk::PhysicalDevice physical, vk::SurfaceKHR surface, + const vk::DispatchLoaderDynamic& dld) { + static constexpr std::array required_extensions = { VK_KHR_SWAPCHAIN_EXTENSION_NAME, VK_KHR_16BIT_STORAGE_EXTENSION_NAME, VK_KHR_8BIT_STORAGE_EXTENSION_NAME, @@ -293,9 +290,10 @@ bool VKDevice::IsSuitable(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDev VK_EXT_SHADER_SUBGROUP_VOTE_EXTENSION_NAME, VK_EXT_HOST_QUERY_RESET_EXTENSION_NAME, }; + bool is_suitable = true; std::bitset<required_extensions.size()> available_extensions{}; - for (const auto& prop : physical.enumerateDeviceExtensionProperties(nullptr, dldi)) { + for (const auto& prop : physical.enumerateDeviceExtensionProperties(nullptr, dld)) { for (std::size_t i = 0; i < required_extensions.size(); ++i) { if (available_extensions[i]) { continue; @@ -315,7 +313,7 @@ bool VKDevice::IsSuitable(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDev } bool has_graphics{}, has_present{}; - const auto queue_family_properties = physical.getQueueFamilyProperties(dldi); + const auto queue_family_properties = physical.getQueueFamilyProperties(dld); for (u32 i = 0; i < static_cast<u32>(queue_family_properties.size()); ++i) { const auto& family = queue_family_properties[i]; if (family.queueCount == 0) { @@ -323,7 +321,7 @@ bool VKDevice::IsSuitable(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDev } has_graphics |= (family.queueFlags & vk::QueueFlagBits::eGraphics) != static_cast<vk::QueueFlagBits>(0); - has_present |= physical.getSurfaceSupportKHR(i, surface, dldi) != 0; + has_present |= physical.getSurfaceSupportKHR(i, surface, dld) != 0; } if (!has_graphics || !has_present) { LOG_ERROR(Render_Vulkan, "Device lacks a graphics and present queue"); @@ -331,7 +329,7 @@ bool VKDevice::IsSuitable(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDev } // TODO(Rodrigo): Check if the device matches all requeriments. - const auto properties{physical.getProperties(dldi)}; + const auto properties{physical.getProperties(dld)}; const auto& limits{properties.limits}; constexpr u32 required_ubo_size = 65536; @@ -348,7 +346,7 @@ bool VKDevice::IsSuitable(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDev is_suitable = false; } - const auto features{physical.getFeatures(dldi)}; + const auto features{physical.getFeatures(dld)}; const std::array feature_report = { std::make_pair(features.vertexPipelineStoresAndAtomics, "vertexPipelineStoresAndAtomics"), std::make_pair(features.independentBlend, "independentBlend"), @@ -380,7 +378,7 @@ bool VKDevice::IsSuitable(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDev return is_suitable; } -std::vector<const char*> VKDevice::LoadExtensions(const vk::DispatchLoaderDynamic& dldi) { +std::vector<const char*> VKDevice::LoadExtensions() { std::vector<const char*> extensions; const auto Test = [&](const vk::ExtensionProperties& extension, std::optional<std::reference_wrapper<bool>> status, const char* name, @@ -411,7 +409,7 @@ std::vector<const char*> VKDevice::LoadExtensions(const vk::DispatchLoaderDynami bool has_khr_shader_float16_int8{}; bool has_ext_subgroup_size_control{}; bool has_ext_transform_feedback{}; - for (const auto& extension : physical.enumerateDeviceExtensionProperties(nullptr, dldi)) { + for (const auto& extension : physical.enumerateDeviceExtensionProperties(nullptr, dld)) { Test(extension, khr_uniform_buffer_standard_layout, VK_KHR_UNIFORM_BUFFER_STANDARD_LAYOUT_EXTENSION_NAME, true); Test(extension, has_khr_shader_float16_int8, VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME, @@ -433,15 +431,15 @@ std::vector<const char*> VKDevice::LoadExtensions(const vk::DispatchLoaderDynami if (has_khr_shader_float16_int8) { is_float16_supported = - GetFeatures<vk::PhysicalDeviceFloat16Int8FeaturesKHR>(physical, dldi).shaderFloat16; + GetFeatures<vk::PhysicalDeviceFloat16Int8FeaturesKHR>(physical, dld).shaderFloat16; extensions.push_back(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME); } if (has_ext_subgroup_size_control) { const auto features = - GetFeatures<vk::PhysicalDeviceSubgroupSizeControlFeaturesEXT>(physical, dldi); + GetFeatures<vk::PhysicalDeviceSubgroupSizeControlFeaturesEXT>(physical, dld); const auto properties = - GetProperties<vk::PhysicalDeviceSubgroupSizeControlPropertiesEXT>(physical, dldi); + GetProperties<vk::PhysicalDeviceSubgroupSizeControlPropertiesEXT>(physical, dld); is_warp_potentially_bigger = properties.maxSubgroupSize > GuestWarpSize; @@ -456,9 +454,9 @@ std::vector<const char*> VKDevice::LoadExtensions(const vk::DispatchLoaderDynami if (has_ext_transform_feedback) { const auto features = - GetFeatures<vk::PhysicalDeviceTransformFeedbackFeaturesEXT>(physical, dldi); + GetFeatures<vk::PhysicalDeviceTransformFeedbackFeaturesEXT>(physical, dld); const auto properties = - GetProperties<vk::PhysicalDeviceTransformFeedbackPropertiesEXT>(physical, dldi); + GetProperties<vk::PhysicalDeviceTransformFeedbackPropertiesEXT>(physical, dld); if (features.transformFeedback && features.geometryStreams && properties.maxTransformFeedbackStreams >= 4 && properties.maxTransformFeedbackBuffers && @@ -471,10 +469,10 @@ std::vector<const char*> VKDevice::LoadExtensions(const vk::DispatchLoaderDynami return extensions; } -void VKDevice::SetupFamilies(const vk::DispatchLoaderDynamic& dldi, vk::SurfaceKHR surface) { +void VKDevice::SetupFamilies(vk::SurfaceKHR surface) { std::optional<u32> graphics_family_, present_family_; - const auto queue_family_properties = physical.getQueueFamilyProperties(dldi); + const auto queue_family_properties = physical.getQueueFamilyProperties(dld); for (u32 i = 0; i < static_cast<u32>(queue_family_properties.size()); ++i) { if (graphics_family_ && present_family_) break; @@ -483,10 +481,12 @@ void VKDevice::SetupFamilies(const vk::DispatchLoaderDynamic& dldi, vk::SurfaceK if (queue_family.queueCount == 0) continue; - if (queue_family.queueFlags & vk::QueueFlagBits::eGraphics) + if (queue_family.queueFlags & vk::QueueFlagBits::eGraphics) { graphics_family_ = i; - if (physical.getSurfaceSupportKHR(i, surface, dldi)) + } + if (physical.getSurfaceSupportKHR(i, surface, dld)) { present_family_ = i; + } } ASSERT(graphics_family_ && present_family_); @@ -494,10 +494,10 @@ void VKDevice::SetupFamilies(const vk::DispatchLoaderDynamic& dldi, vk::SurfaceK present_family = *present_family_; } -void VKDevice::SetupFeatures(const vk::DispatchLoaderDynamic& dldi) { - const auto supported_features{physical.getFeatures(dldi)}; +void VKDevice::SetupFeatures() { + const auto supported_features{physical.getFeatures(dld)}; is_formatless_image_load_supported = supported_features.shaderStorageImageReadWithoutFormat; - is_optimal_astc_supported = IsOptimalAstcSupported(supported_features, dldi); + is_optimal_astc_supported = IsOptimalAstcSupported(supported_features); } void VKDevice::CollectTelemetryParameters() { @@ -525,7 +525,7 @@ std::vector<vk::DeviceQueueCreateInfo> VKDevice::GetDeviceQueueCreateInfos() con } std::unordered_map<vk::Format, vk::FormatProperties> VKDevice::GetFormatProperties( - const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDevice physical) { + const vk::DispatchLoaderDynamic& dld, vk::PhysicalDevice physical) { static constexpr std::array formats{vk::Format::eA8B8G8R8UnormPack32, vk::Format::eA8B8G8R8UintPack32, vk::Format::eA8B8G8R8SnormPack32, @@ -606,7 +606,7 @@ std::unordered_map<vk::Format, vk::FormatProperties> VKDevice::GetFormatProperti vk::Format::eE5B9G9R9UfloatPack32}; std::unordered_map<vk::Format, vk::FormatProperties> format_properties; for (const auto format : formats) { - format_properties.emplace(format, physical.getFormatProperties(format, dldi)); + format_properties.emplace(format, physical.getFormatProperties(format, dld)); } return format_properties; } diff --git a/src/video_core/renderer_vulkan/vk_device.h b/src/video_core/renderer_vulkan/vk_device.h index 6e656517f..d9d809852 100644 --- a/src/video_core/renderer_vulkan/vk_device.h +++ b/src/video_core/renderer_vulkan/vk_device.h @@ -22,12 +22,12 @@ const u32 GuestWarpSize = 32; /// Handles data specific to a physical device. class VKDevice final { public: - explicit VKDevice(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDevice physical, + explicit VKDevice(const vk::DispatchLoaderDynamic& dld, vk::PhysicalDevice physical, vk::SurfaceKHR surface); ~VKDevice(); /// Initializes the device. Returns true on success. - bool Create(const vk::DispatchLoaderDynamic& dldi, vk::Instance instance); + bool Create(vk::Instance instance); /** * Returns a format supported by the device for the passed requeriments. @@ -188,18 +188,18 @@ public: } /// Checks if the physical device is suitable. - static bool IsSuitable(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDevice physical, - vk::SurfaceKHR surface); + static bool IsSuitable(vk::PhysicalDevice physical, vk::SurfaceKHR surface, + const vk::DispatchLoaderDynamic& dld); private: /// Loads extensions into a vector and stores available ones in this object. - std::vector<const char*> LoadExtensions(const vk::DispatchLoaderDynamic& dldi); + std::vector<const char*> LoadExtensions(); /// Sets up queue families. - void SetupFamilies(const vk::DispatchLoaderDynamic& dldi, vk::SurfaceKHR surface); + void SetupFamilies(vk::SurfaceKHR surface); /// Sets up device features. - void SetupFeatures(const vk::DispatchLoaderDynamic& dldi); + void SetupFeatures(); /// Collects telemetry information from the device. void CollectTelemetryParameters(); @@ -208,8 +208,7 @@ private: std::vector<vk::DeviceQueueCreateInfo> GetDeviceQueueCreateInfos() const; /// Returns true if ASTC textures are natively supported. - bool IsOptimalAstcSupported(const vk::PhysicalDeviceFeatures& features, - const vk::DispatchLoaderDynamic& dldi) const; + bool IsOptimalAstcSupported(const vk::PhysicalDeviceFeatures& features) const; /// Returns true if a format is supported. bool IsFormatSupported(vk::Format wanted_format, vk::FormatFeatureFlags wanted_usage, @@ -217,10 +216,10 @@ private: /// Returns the device properties for Vulkan formats. static std::unordered_map<vk::Format, vk::FormatProperties> GetFormatProperties( - const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDevice physical); + const vk::DispatchLoaderDynamic& dld, vk::PhysicalDevice physical); - const vk::PhysicalDevice physical; ///< Physical device. vk::DispatchLoaderDynamic dld; ///< Device function pointers. + vk::PhysicalDevice physical; ///< Physical device. vk::PhysicalDeviceProperties properties; ///< Device properties. UniqueDevice logical; ///< Logical device. vk::Queue graphics_queue; ///< Main graphics queue. diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index d34b47b3f..8b9404718 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -150,6 +150,10 @@ target_link_libraries(yuzu PRIVATE common core input_common video_core) target_link_libraries(yuzu PRIVATE Boost::boost glad Qt5::OpenGL Qt5::Widgets) target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) +if (ENABLE_VULKAN AND NOT WIN32) + target_include_directories(yuzu PRIVATE ${Qt5Gui_PRIVATE_INCLUDE_DIRS}) +endif() + target_compile_definitions(yuzu PRIVATE # Use QStringBuilder for string concatenation to reduce # the overall number of temporary strings created. diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 7b211bd32..1cac2f942 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -14,8 +14,9 @@ #include <QScreen> #include <QStringList> #include <QWindow> -#ifdef HAS_VULKAN -#include <QVulkanWindow> + +#if !defined(WIN32) && HAS_VULKAN +#include <qpa/qplatformnativeinterface.h> #endif #include <fmt/format.h> @@ -237,16 +238,50 @@ private: #ifdef HAS_VULKAN class VulkanRenderWidget : public RenderWidget { public: - explicit VulkanRenderWidget(GRenderWindow* parent, QVulkanInstance* instance) - : RenderWidget(parent) { + explicit VulkanRenderWidget(GRenderWindow* parent) : RenderWidget(parent) { windowHandle()->setSurfaceType(QWindow::VulkanSurface); - windowHandle()->setVulkanInstance(instance); } }; #endif -GRenderWindow::GRenderWindow(GMainWindow* parent_, EmuThread* emu_thread) - : QWidget(parent_), emu_thread(emu_thread) { +static Core::Frontend::WindowSystemType GetWindowSystemType() { + // Determine WSI type based on Qt platform. + QString platform_name = QGuiApplication::platformName(); + if (platform_name == QStringLiteral("windows")) + return Core::Frontend::WindowSystemType::Windows; + else if (platform_name == QStringLiteral("xcb")) + return Core::Frontend::WindowSystemType::X11; + else if (platform_name == QStringLiteral("wayland")) + return Core::Frontend::WindowSystemType::Wayland; + + LOG_CRITICAL(Frontend, "Unknown Qt platform!"); + return Core::Frontend::WindowSystemType::Windows; +} + +static Core::Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow* window) { + Core::Frontend::EmuWindow::WindowSystemInfo wsi; + wsi.type = GetWindowSystemType(); + +#ifdef HAS_VULKAN + // Our Win32 Qt external doesn't have the private API. +#if defined(WIN32) || defined(__APPLE__) + wsi.render_surface = window ? reinterpret_cast<void*>(window->winId()) : nullptr; +#else + QPlatformNativeInterface* pni = QGuiApplication::platformNativeInterface(); + wsi.display_connection = pni->nativeResourceForWindow("display", window); + if (wsi.type == Core::Frontend::WindowSystemType::Wayland) + wsi.render_surface = window ? pni->nativeResourceForWindow("surface", window) : nullptr; + else + wsi.render_surface = window ? reinterpret_cast<void*>(window->winId()) : nullptr; +#endif + wsi.render_surface_scale = window ? static_cast<float>(window->devicePixelRatio()) : 1.0f; +#endif + + return wsi; +} + +GRenderWindow::GRenderWindow(GMainWindow* parent_, EmuThread* emu_thread_) + : QWidget(parent_), emu_thread(emu_thread_) { setWindowTitle(QStringLiteral("yuzu %1 | %2-%3") .arg(QString::fromUtf8(Common::g_build_name), QString::fromUtf8(Common::g_scm_branch), @@ -459,6 +494,9 @@ bool GRenderWindow::InitRenderTarget() { break; } + // Update the Window System information with the new render target + window_info = GetWindowSystemInfo(child_widget->windowHandle()); + child_widget->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); layout()->addWidget(child_widget); // Reset minimum required size to avoid resizing issues on the main window after restarting. @@ -530,30 +568,7 @@ bool GRenderWindow::InitializeOpenGL() { bool GRenderWindow::InitializeVulkan() { #ifdef HAS_VULKAN - vk_instance = std::make_unique<QVulkanInstance>(); - vk_instance->setApiVersion(QVersionNumber(1, 1, 0)); - vk_instance->setFlags(QVulkanInstance::Flag::NoDebugOutputRedirect); - if (Settings::values.renderer_debug) { - const auto supported_layers{vk_instance->supportedLayers()}; - const bool found = - std::find_if(supported_layers.begin(), supported_layers.end(), [](const auto& layer) { - constexpr const char searched_layer[] = "VK_LAYER_LUNARG_standard_validation"; - return layer.name == searched_layer; - }); - if (found) { - vk_instance->setLayers(QByteArrayList() << "VK_LAYER_LUNARG_standard_validation"); - vk_instance->setExtensions(QByteArrayList() << VK_EXT_DEBUG_UTILS_EXTENSION_NAME); - } - } - if (!vk_instance->create()) { - QMessageBox::critical( - this, tr("Error while initializing Vulkan 1.1!"), - tr("Your OS doesn't seem to support Vulkan 1.1 instances, or you do not have the " - "latest graphics drivers.")); - return false; - } - - auto child = new VulkanRenderWidget(this, vk_instance.get()); + auto child = new VulkanRenderWidget(this); child_widget = child; child_widget->windowHandle()->create(); main_context = std::make_unique<DummyContext>(); @@ -566,21 +581,6 @@ bool GRenderWindow::InitializeVulkan() { #endif } -void GRenderWindow::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, - void* surface) const { -#ifdef HAS_VULKAN - const auto instance_proc_addr = vk_instance->getInstanceProcAddr("vkGetInstanceProcAddr"); - const VkInstance instance_copy = vk_instance->vkInstance(); - const VkSurfaceKHR surface_copy = vk_instance->surfaceForWindow(child_widget->windowHandle()); - - std::memcpy(get_instance_proc_addr, &instance_proc_addr, sizeof(instance_proc_addr)); - std::memcpy(instance, &instance_copy, sizeof(instance_copy)); - std::memcpy(surface, &surface_copy, sizeof(surface_copy)); -#else - UNREACHABLE_MSG("Executing Vulkan code without compiling Vulkan"); -#endif -} - bool GRenderWindow::LoadOpenGL() { auto context = CreateSharedContext(); auto scope = context->Acquire(); diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index d69078df1..3626604ca 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h @@ -22,9 +22,6 @@ class GMainWindow; class QKeyEvent; class QTouchEvent; class QStringList; -#ifdef HAS_VULKAN -class QVulkanInstance; -#endif namespace VideoCore { enum class LoadCallbackStage; @@ -122,8 +119,6 @@ public: // EmuWindow implementation. void PollEvents() override; bool IsShown() const override; - void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, - void* surface) const override; std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; void BackupGeometry(); @@ -186,10 +181,6 @@ private: // should instead be shared from std::shared_ptr<Core::Frontend::GraphicsContext> main_context; -#ifdef HAS_VULKAN - std::unique_ptr<QVulkanInstance> vk_instance; -#endif - /// Temporary storage of the screenshot taken QImage screenshot_image; diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp index a821c7b3c..ea667caef 100644 --- a/src/yuzu/configuration/configure_graphics.cpp +++ b/src/yuzu/configuration/configure_graphics.cpp @@ -15,6 +15,10 @@ #include "ui_configure_graphics.h" #include "yuzu/configuration/configure_graphics.h" +#ifdef HAS_VULKAN +#include "video_core/renderer_vulkan/renderer_vulkan.h" +#endif + namespace { enum class Resolution : int { Auto, @@ -165,41 +169,9 @@ void ConfigureGraphics::UpdateDeviceComboBox() { void ConfigureGraphics::RetrieveVulkanDevices() { #ifdef HAS_VULKAN - QVulkanInstance instance; - instance.setApiVersion(QVersionNumber(1, 1, 0)); - if (!instance.create()) { - LOG_INFO(Frontend, "Vulkan 1.1 not available"); - return; - } - const auto vkEnumeratePhysicalDevices{reinterpret_cast<PFN_vkEnumeratePhysicalDevices>( - instance.getInstanceProcAddr("vkEnumeratePhysicalDevices"))}; - if (vkEnumeratePhysicalDevices == nullptr) { - LOG_INFO(Frontend, "Failed to get pointer to vkEnumeratePhysicalDevices"); - return; - } - u32 physical_device_count; - if (vkEnumeratePhysicalDevices(instance.vkInstance(), &physical_device_count, nullptr) != - VK_SUCCESS) { - LOG_INFO(Frontend, "Failed to get physical devices count"); - return; - } - std::vector<VkPhysicalDevice> physical_devices(physical_device_count); - if (vkEnumeratePhysicalDevices(instance.vkInstance(), &physical_device_count, - physical_devices.data()) != VK_SUCCESS) { - LOG_INFO(Frontend, "Failed to get physical devices"); - return; - } - - const auto vkGetPhysicalDeviceProperties{reinterpret_cast<PFN_vkGetPhysicalDeviceProperties>( - instance.getInstanceProcAddr("vkGetPhysicalDeviceProperties"))}; - if (vkGetPhysicalDeviceProperties == nullptr) { - LOG_INFO(Frontend, "Failed to get pointer to vkGetPhysicalDeviceProperties"); - return; - } - for (const auto physical_device : physical_devices) { - VkPhysicalDeviceProperties properties; - vkGetPhysicalDeviceProperties(physical_device, &properties); - vulkan_devices.push_back(QString::fromUtf8(properties.deviceName)); + vulkan_devices.clear(); + for (auto& name : Vulkan::RendererVulkan::EnumerateDevices()) { + vulkan_devices.push_back(QString::fromStdString(name)); } #endif } diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp index 3522dcf6d..411e7e647 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp @@ -156,12 +156,6 @@ EmuWindow_SDL2_GL::~EmuWindow_SDL2_GL() { SDL_GL_DeleteContext(window_context); } -void EmuWindow_SDL2_GL::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, - void* surface) const { - // Should not have been called from OpenGL - UNREACHABLE(); -} - std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_GL::CreateSharedContext() const { return std::make_unique<SDLGLContext>(); } diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h index e092021d7..48bb41683 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h @@ -15,10 +15,6 @@ public: void Present() override; - /// Ignored in OpenGL - void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, - void* surface) const override; - std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; private: diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp index 46d053f04..f2990910e 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp @@ -2,102 +2,62 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include <algorithm> +#include <cstdlib> +#include <memory> #include <string> -#include <vector> -#include <SDL.h> -#include <SDL_vulkan.h> + #include <fmt/format.h> -#include <vulkan/vulkan.h> + #include "common/assert.h" #include "common/logging/log.h" #include "common/scm_rev.h" #include "core/settings.h" +#include "video_core/renderer_vulkan/renderer_vulkan.h" #include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h" +// Include these late to avoid polluting everything with Xlib macros +#include <SDL.h> +#include <SDL_syswm.h> + EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(Core::System& system, bool fullscreen) : EmuWindow_SDL2{system, fullscreen} { - if (SDL_Vulkan_LoadLibrary(nullptr) != 0) { - LOG_CRITICAL(Frontend, "SDL failed to load the Vulkan library: {}", SDL_GetError()); - exit(EXIT_FAILURE); - } - - vkGetInstanceProcAddr = - reinterpret_cast<PFN_vkGetInstanceProcAddr>(SDL_Vulkan_GetVkGetInstanceProcAddr()); - if (vkGetInstanceProcAddr == nullptr) { - LOG_CRITICAL(Frontend, "Failed to retrieve Vulkan function pointer!"); - exit(EXIT_FAILURE); - } - const std::string window_title = fmt::format("yuzu {} | {}-{} (Vulkan)", Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc); render_window = - SDL_CreateWindow(window_title.c_str(), - SDL_WINDOWPOS_UNDEFINED, // x position - SDL_WINDOWPOS_UNDEFINED, // y position + SDL_CreateWindow(window_title.c_str(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height, - SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_VULKAN); - - const bool use_standard_layers = UseStandardLayers(vkGetInstanceProcAddr); - - u32 extra_ext_count{}; - if (!SDL_Vulkan_GetInstanceExtensions(render_window, &extra_ext_count, NULL)) { - LOG_CRITICAL(Frontend, "Failed to query Vulkan extensions count from SDL! {}", - SDL_GetError()); - exit(1); - } - - auto extra_ext_names = std::make_unique<const char* []>(extra_ext_count); - if (!SDL_Vulkan_GetInstanceExtensions(render_window, &extra_ext_count, extra_ext_names.get())) { - LOG_CRITICAL(Frontend, "Failed to query Vulkan extensions from SDL! {}", SDL_GetError()); - exit(1); - } - std::vector<const char*> enabled_extensions; - enabled_extensions.insert(enabled_extensions.begin(), extra_ext_names.get(), - extra_ext_names.get() + extra_ext_count); - - std::vector<const char*> enabled_layers; - if (use_standard_layers) { - enabled_extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); - enabled_layers.push_back("VK_LAYER_LUNARG_standard_validation"); - } - - VkApplicationInfo app_info{}; - app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; - app_info.apiVersion = VK_API_VERSION_1_1; - app_info.applicationVersion = VK_MAKE_VERSION(0, 1, 0); - app_info.pApplicationName = "yuzu-emu"; - app_info.engineVersion = VK_MAKE_VERSION(0, 1, 0); - app_info.pEngineName = "yuzu-emu"; + SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); - VkInstanceCreateInfo instance_ci{}; - instance_ci.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; - instance_ci.pApplicationInfo = &app_info; - instance_ci.enabledExtensionCount = static_cast<u32>(enabled_extensions.size()); - instance_ci.ppEnabledExtensionNames = enabled_extensions.data(); - if (Settings::values.renderer_debug) { - instance_ci.enabledLayerCount = static_cast<u32>(enabled_layers.size()); - instance_ci.ppEnabledLayerNames = enabled_layers.data(); + SDL_SysWMinfo wm; + if (SDL_GetWindowWMInfo(render_window, &wm) == SDL_FALSE) { + LOG_CRITICAL(Frontend, "Failed to get information from the window manager"); + std::exit(EXIT_FAILURE); } - const auto vkCreateInstance = - reinterpret_cast<PFN_vkCreateInstance>(vkGetInstanceProcAddr(nullptr, "vkCreateInstance")); - if (vkCreateInstance == nullptr || - vkCreateInstance(&instance_ci, nullptr, &vk_instance) != VK_SUCCESS) { - LOG_CRITICAL(Frontend, "Failed to create Vulkan instance!"); - exit(EXIT_FAILURE); - } - - vkDestroyInstance = reinterpret_cast<PFN_vkDestroyInstance>( - vkGetInstanceProcAddr(vk_instance, "vkDestroyInstance")); - if (vkDestroyInstance == nullptr) { - LOG_CRITICAL(Frontend, "Failed to retrieve Vulkan function pointer!"); - exit(EXIT_FAILURE); - } - - if (!SDL_Vulkan_CreateSurface(render_window, vk_instance, &vk_surface)) { - LOG_CRITICAL(Frontend, "Failed to create Vulkan surface! {}", SDL_GetError()); - exit(EXIT_FAILURE); + switch (wm.subsystem) { +#ifdef SDL_VIDEO_DRIVER_WINDOWS + case SDL_SYSWM_TYPE::SDL_SYSWM_WINDOWS: + window_info.type = Core::Frontend::WindowSystemType::Windows; + window_info.render_surface = reinterpret_cast<void*>(wm.info.win.window); + break; +#endif +#ifdef SDL_VIDEO_DRIVER_X11 + case SDL_SYSWM_TYPE::SDL_SYSWM_X11: + window_info.type = Core::Frontend::WindowSystemType::X11; + window_info.display_connection = wm.info.x11.display; + window_info.render_surface = reinterpret_cast<void*>(wm.info.x11.window); + break; +#endif +#ifdef SDL_VIDEO_DRIVER_WAYLAND + case SDL_SYSWM_TYPE::SDL_SYSWM_WAYLAND: + window_info.type = Core::Frontend::WindowSystemType::Wayland; + window_info.display_connection = wm.info.wl.display; + window_info.render_surface = wm.info.wl.surface; + break; +#endif + default: + LOG_CRITICAL(Frontend, "Window manager subsystem not implemented"); + std::exit(EXIT_FAILURE); } OnResize(); @@ -107,51 +67,12 @@ EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(Core::System& system, bool fullscreen) Common::g_scm_branch, Common::g_scm_desc); } -EmuWindow_SDL2_VK::~EmuWindow_SDL2_VK() { - vkDestroyInstance(vk_instance, nullptr); -} - -void EmuWindow_SDL2_VK::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, - void* surface) const { - const auto instance_proc_addr = vkGetInstanceProcAddr; - std::memcpy(get_instance_proc_addr, &instance_proc_addr, sizeof(instance_proc_addr)); - std::memcpy(instance, &vk_instance, sizeof(vk_instance)); - std::memcpy(surface, &vk_surface, sizeof(vk_surface)); -} +EmuWindow_SDL2_VK::~EmuWindow_SDL2_VK() = default; std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_VK::CreateSharedContext() const { return nullptr; } -bool EmuWindow_SDL2_VK::UseStandardLayers(PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr) const { - if (!Settings::values.renderer_debug) { - return false; - } - - const auto vkEnumerateInstanceLayerProperties = - reinterpret_cast<PFN_vkEnumerateInstanceLayerProperties>( - vkGetInstanceProcAddr(nullptr, "vkEnumerateInstanceLayerProperties")); - if (vkEnumerateInstanceLayerProperties == nullptr) { - LOG_CRITICAL(Frontend, "Failed to retrieve Vulkan function pointer!"); - return false; - } - - u32 available_layers_count{}; - if (vkEnumerateInstanceLayerProperties(&available_layers_count, nullptr) != VK_SUCCESS) { - LOG_CRITICAL(Frontend, "Failed to enumerate Vulkan validation layers!"); - return false; - } - std::vector<VkLayerProperties> layers(available_layers_count); - if (vkEnumerateInstanceLayerProperties(&available_layers_count, layers.data()) != VK_SUCCESS) { - LOG_CRITICAL(Frontend, "Failed to enumerate Vulkan validation layers!"); - return false; - } - - return std::find_if(layers.begin(), layers.end(), [&](const auto& layer) { - return layer.layerName == std::string("VK_LAYER_LUNARG_standard_validation"); - }) != layers.end(); -} - void EmuWindow_SDL2_VK::Present() { // TODO (bunnei): ImplementMe } diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h index 3dd1f3f61..b8021ebea 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h @@ -4,27 +4,21 @@ #pragma once -#include <vulkan/vulkan.h> +#include <memory> + #include "core/frontend/emu_window.h" #include "yuzu_cmd/emu_window/emu_window_sdl2.h" +namespace Core { +class System; +} + class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 { public: explicit EmuWindow_SDL2_VK(Core::System& system, bool fullscreen); ~EmuWindow_SDL2_VK(); void Present() override; - void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, - void* surface) const override; std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; - -private: - bool UseStandardLayers(PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr) const; - - VkInstance vk_instance{}; - VkSurfaceKHR vk_surface{}; - - PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr{}; - PFN_vkDestroyInstance vkDestroyInstance{}; }; diff --git a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp index a837430cc..8584f6671 100644 --- a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp +++ b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp @@ -116,10 +116,6 @@ bool EmuWindow_SDL2_Hide::IsShown() const { return false; } -void EmuWindow_SDL2_Hide::RetrieveVulkanHandlers(void*, void*, void*) const { - UNREACHABLE(); -} - class SDLGLContext : public Core::Frontend::GraphicsContext { public: explicit SDLGLContext() { diff --git a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h index 9f5d04fca..c13a82df2 100644 --- a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h +++ b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h @@ -19,10 +19,6 @@ public: /// Whether the screen is being shown or not. bool IsShown() const override; - /// Retrieves Vulkan specific handlers from the window - void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, - void* surface) const override; - std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; private: |