diff options
-rw-r--r-- | externals/CMakeLists.txt | 24 | ||||
-rw-r--r-- | src/common/fs/fs_util.cpp | 14 | ||||
-rw-r--r-- | src/common/fs/fs_util.h | 33 | ||||
-rw-r--r-- | src/common/fs/path_util.cpp | 6 | ||||
-rw-r--r-- | src/common/fs/path_util.h | 9 | ||||
-rw-r--r-- | src/input_common/sdl/sdl_impl.cpp | 281 | ||||
-rw-r--r-- | src/input_common/sdl/sdl_impl.h | 37 | ||||
-rw-r--r-- | src/video_core/vulkan_common/vulkan_memory_allocator.cpp | 67 | ||||
-rw-r--r-- | src/video_core/vulkan_common/vulkan_memory_allocator.h | 7 | ||||
-rw-r--r-- | src/yuzu/configuration/config.cpp | 4 | ||||
-rw-r--r-- | src/yuzu/configuration/config.h | 4 | ||||
-rw-r--r-- | src/yuzu/configuration/configure_per_game.cpp | 9 | ||||
-rw-r--r-- | src/yuzu/configuration/configure_per_game.h | 3 | ||||
-rw-r--r-- | src/yuzu/game_list.cpp | 8 | ||||
-rw-r--r-- | src/yuzu/game_list.h | 3 | ||||
-rw-r--r-- | src/yuzu/main.cpp | 21 | ||||
-rw-r--r-- | src/yuzu/main.h | 5 |
17 files changed, 396 insertions, 139 deletions
diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index fe1c088ca2..aae0baa0bb 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -47,19 +47,21 @@ target_include_directories(unicorn-headers INTERFACE ./unicorn/include) # SDL2 if (NOT SDL2_FOUND AND ENABLE_SDL2) - # Yuzu itself needs: Events Joystick Haptic Sensor Timers - # Yuzu-cmd also needs: Video (depends on Loadso/Dlopen) - set(SDL_UNUSED_SUBSYSTEMS - Atomic Audio Render Power Threads - File CPUinfo Filesystem Locale) - foreach(_SUB ${SDL_UNUSED_SUBSYSTEMS}) - string(TOUPPER ${_SUB} _OPT) - option(SDL_${_OPT} "" OFF) - endforeach() - + if (NOT WIN32) + # Yuzu itself needs: Events Joystick Haptic Sensor Timers + # Yuzu-cmd also needs: Video (depends on Loadso/Dlopen) + set(SDL_UNUSED_SUBSYSTEMS + Atomic Audio Render Power Threads + File CPUinfo Filesystem Locale) + foreach(_SUB ${SDL_UNUSED_SUBSYSTEMS}) + string(TOUPPER ${_SUB} _OPT) + option(SDL_${_OPT} "" OFF) + endforeach() + + option(HIDAPI "" ON) + endif() set(SDL_STATIC ON) set(SDL_SHARED OFF) - option(HIDAPI "" ON) add_subdirectory(SDL EXCLUDE_FROM_ALL) add_library(SDL2 ALIAS SDL2-static) diff --git a/src/common/fs/fs_util.cpp b/src/common/fs/fs_util.cpp index 0ddfc31316..357cf5855d 100644 --- a/src/common/fs/fs_util.cpp +++ b/src/common/fs/fs_util.cpp @@ -2,6 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <algorithm> + #include "common/fs/fs_util.h" namespace Common::FS { @@ -10,4 +12,16 @@ std::u8string ToU8String(std::string_view utf8_string) { return std::u8string{utf8_string.begin(), utf8_string.end()}; } +std::u8string BufferToU8String(std::span<const u8> buffer) { + return std::u8string{buffer.begin(), std::ranges::find(buffer, u8{0})}; +} + +std::string ToUTF8String(std::u8string_view u8_string) { + return std::string{u8_string.begin(), u8_string.end()}; +} + +std::string PathToUTF8String(const std::filesystem::path& path) { + return ToUTF8String(path.u8string()); +} + } // namespace Common::FS diff --git a/src/common/fs/fs_util.h b/src/common/fs/fs_util.h index 951df53b6c..ec9950ee72 100644 --- a/src/common/fs/fs_util.h +++ b/src/common/fs/fs_util.h @@ -5,9 +5,13 @@ #pragma once #include <concepts> +#include <filesystem> +#include <span> #include <string> #include <string_view> +#include "common/common_types.h" + namespace Common::FS { template <typename T> @@ -22,4 +26,33 @@ concept IsChar = std::same_as<T, char>; */ [[nodiscard]] std::u8string ToU8String(std::string_view utf8_string); +/** + * Converts a buffer of bytes to a UTF8-encoded std::u8string. + * This converts from the start of the buffer until the first encountered null-terminator. + * If no null-terminator is found, this converts the entire buffer instead. + * + * @param buffer Buffer of bytes + * + * @returns UTF-8 encoded std::u8string. + */ +[[nodiscard]] std::u8string BufferToU8String(std::span<const u8> buffer); + +/** + * Converts a std::u8string or std::u8string_view to a UTF-8 encoded std::string. + * + * @param u8_string UTF-8 encoded u8string + * + * @returns UTF-8 encoded std::string. + */ +[[nodiscard]] std::string ToUTF8String(std::u8string_view u8_string); + +/** + * Converts a filesystem path to a UTF-8 encoded std::string. + * + * @param path Filesystem path + * + * @returns UTF-8 encoded std::string. + */ +[[nodiscard]] std::string PathToUTF8String(const std::filesystem::path& path); + } // namespace Common::FS diff --git a/src/common/fs/path_util.cpp b/src/common/fs/path_util.cpp index 8b732a21c3..6cdd14f138 100644 --- a/src/common/fs/path_util.cpp +++ b/src/common/fs/path_util.cpp @@ -129,12 +129,6 @@ private: std::unordered_map<YuzuPath, fs::path> yuzu_paths; }; -std::string PathToUTF8String(const fs::path& path) { - const auto utf8_string = path.u8string(); - - return std::string{utf8_string.begin(), utf8_string.end()}; -} - bool ValidatePath(const fs::path& path) { if (path.empty()) { LOG_ERROR(Common_Filesystem, "Input path is empty, path={}", PathToUTF8String(path)); diff --git a/src/common/fs/path_util.h b/src/common/fs/path_util.h index a9fadbceb9..14e8c35d74 100644 --- a/src/common/fs/path_util.h +++ b/src/common/fs/path_util.h @@ -26,15 +26,6 @@ enum class YuzuPath { }; /** - * Converts a filesystem path to a UTF-8 encoded std::string. - * - * @param path Filesystem path - * - * @returns UTF-8 encoded std::string. - */ -[[nodiscard]] std::string PathToUTF8String(const std::filesystem::path& path); - -/** * Validates a given path. * * A given path is valid if it meets these conditions: diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp index b9b584b2a3..68672a92be 100644 --- a/src/input_common/sdl/sdl_impl.cpp +++ b/src/input_common/sdl/sdl_impl.cpp @@ -18,16 +18,6 @@ #include <utility> #include <vector> -// Ignore -Wimplicit-fallthrough due to https://github.com/libsdl-org/SDL/issues/4307 -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wimplicit-fallthrough" -#endif -#include <SDL.h> -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - #include "common/logging/log.h" #include "common/math_util.h" #include "common/param_package.h" @@ -214,6 +204,40 @@ public: sdl_controller.reset(controller); } + bool IsJoyconLeft() const { + return std::strstr(GetControllerName().c_str(), "Joy-Con Left") != nullptr; + } + + bool IsJoyconRight() const { + return std::strstr(GetControllerName().c_str(), "Joy-Con Right") != nullptr; + } + + std::string GetControllerName() const { + if (sdl_controller) { + switch (SDL_GameControllerGetType(sdl_controller.get())) { + case SDL_CONTROLLER_TYPE_XBOX360: + return "XBox 360 Controller"; + case SDL_CONTROLLER_TYPE_XBOXONE: + return "XBox One Controller"; + default: + break; + } + const auto name = SDL_GameControllerName(sdl_controller.get()); + if (name) { + return name; + } + } + + if (sdl_joystick) { + const auto name = SDL_JoystickName(sdl_joystick.get()); + if (name) { + return name; + } + } + + return "Unknown"; + } + private: struct State { std::unordered_map<int, bool> buttons; @@ -858,23 +882,42 @@ SDLState::~SDLState() { std::vector<Common::ParamPackage> SDLState::GetInputDevices() { std::scoped_lock lock(joystick_map_mutex); std::vector<Common::ParamPackage> devices; + std::unordered_map<int, std::shared_ptr<SDLJoystick>> joycon_pairs; + for (const auto& [key, value] : joystick_map) { + for (const auto& joystick : value) { + if (!joystick->GetSDLJoystick()) { + continue; + } + std::string name = + fmt::format("{} {}", joystick->GetControllerName(), joystick->GetPort()); + devices.emplace_back(Common::ParamPackage{ + {"class", "sdl"}, + {"display", std::move(name)}, + {"guid", joystick->GetGUID()}, + {"port", std::to_string(joystick->GetPort())}, + }); + if (joystick->IsJoyconLeft()) { + joycon_pairs.insert_or_assign(joystick->GetPort(), joystick); + } + } + } + + // Add dual controllers for (const auto& [key, value] : joystick_map) { for (const auto& joystick : value) { - if (auto* const controller = joystick->GetSDLGameController()) { + if (joystick->IsJoyconRight()) { + if (!joycon_pairs.contains(joystick->GetPort())) { + continue; + } + const auto joystick2 = joycon_pairs.at(joystick->GetPort()); + std::string name = - fmt::format("{} {}", GetControllerName(controller), joystick->GetPort()); - devices.emplace_back(Common::ParamPackage{ - {"class", "sdl"}, - {"display", std::move(name)}, - {"guid", joystick->GetGUID()}, - {"port", std::to_string(joystick->GetPort())}, - }); - } else if (auto* const joy = joystick->GetSDLJoystick()) { - std::string name = fmt::format("{} {}", SDL_JoystickName(joy), joystick->GetPort()); + fmt::format("{} {}", "Nintendo Dual Joy-Con", joystick->GetPort()); devices.emplace_back(Common::ParamPackage{ {"class", "sdl"}, {"display", std::move(name)}, {"guid", joystick->GetGUID()}, + {"guid2", joystick2->GetGUID()}, {"port", std::to_string(joystick->GetPort())}, }); } @@ -883,17 +926,6 @@ std::vector<Common::ParamPackage> SDLState::GetInputDevices() { return devices; } -std::string SDLState::GetControllerName(SDL_GameController* controller) const { - switch (SDL_GameControllerGetType(controller)) { - case SDL_CONTROLLER_TYPE_XBOX360: - return "XBox 360 Controller"; - case SDL_CONTROLLER_TYPE_XBOXONE: - return "XBox One Controller"; - default: - return SDL_GameControllerName(controller); - } -} - namespace { Common::ParamPackage BuildAnalogParamPackageForButton(int port, std::string guid, s32 axis, float value = 0.1f) { @@ -1073,24 +1105,48 @@ ButtonMapping SDLState::GetButtonMappingForDevice(const Common::ParamPackage& pa return {}; } const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0)); + auto* controller = joystick->GetSDLGameController(); if (controller == nullptr) { return {}; } - const bool invert = - SDL_GameControllerGetType(controller) != SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO; - // This list is missing ZL/ZR since those are not considered buttons in SDL GameController. // We will add those afterwards // This list also excludes Screenshot since theres not really a mapping for that - using ButtonBindings = - std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerButton>, 17>; - const ButtonBindings switch_to_sdl_button{{ - {Settings::NativeButton::A, invert ? SDL_CONTROLLER_BUTTON_B : SDL_CONTROLLER_BUTTON_A}, - {Settings::NativeButton::B, invert ? SDL_CONTROLLER_BUTTON_A : SDL_CONTROLLER_BUTTON_B}, - {Settings::NativeButton::X, invert ? SDL_CONTROLLER_BUTTON_Y : SDL_CONTROLLER_BUTTON_X}, - {Settings::NativeButton::Y, invert ? SDL_CONTROLLER_BUTTON_X : SDL_CONTROLLER_BUTTON_Y}, + ButtonBindings switch_to_sdl_button; + + if (SDL_GameControllerGetType(controller) == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO) { + switch_to_sdl_button = GetNintendoButtonBinding(joystick); + } else { + switch_to_sdl_button = GetDefaultButtonBinding(); + } + + // Add the missing bindings for ZL/ZR + static constexpr ZButtonBindings switch_to_sdl_axis{{ + {Settings::NativeButton::ZL, SDL_CONTROLLER_AXIS_TRIGGERLEFT}, + {Settings::NativeButton::ZR, SDL_CONTROLLER_AXIS_TRIGGERRIGHT}, + }}; + + // Parameters contain two joysticks return dual + if (params.Has("guid2")) { + const auto joystick2 = GetSDLJoystickByGUID(params.Get("guid2", ""), params.Get("port", 0)); + + if (joystick2->GetSDLGameController() != nullptr) { + return GetDualControllerMapping(joystick, joystick2, switch_to_sdl_button, + switch_to_sdl_axis); + } + } + + return GetSingleControllerMapping(joystick, switch_to_sdl_button, switch_to_sdl_axis); +} + +ButtonBindings SDLState::GetDefaultButtonBinding() const { + return { + std::pair{Settings::NativeButton::A, SDL_CONTROLLER_BUTTON_B}, + {Settings::NativeButton::B, SDL_CONTROLLER_BUTTON_A}, + {Settings::NativeButton::X, SDL_CONTROLLER_BUTTON_Y}, + {Settings::NativeButton::Y, SDL_CONTROLLER_BUTTON_X}, {Settings::NativeButton::LStick, SDL_CONTROLLER_BUTTON_LEFTSTICK}, {Settings::NativeButton::RStick, SDL_CONTROLLER_BUTTON_RIGHTSTICK}, {Settings::NativeButton::L, SDL_CONTROLLER_BUTTON_LEFTSHOULDER}, @@ -1104,18 +1160,51 @@ ButtonMapping SDLState::GetButtonMappingForDevice(const Common::ParamPackage& pa {Settings::NativeButton::SL, SDL_CONTROLLER_BUTTON_LEFTSHOULDER}, {Settings::NativeButton::SR, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER}, {Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE}, - }}; + }; +} - // Add the missing bindings for ZL/ZR - using ZBindings = - std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerAxis>, 2>; - static constexpr ZBindings switch_to_sdl_axis{{ - {Settings::NativeButton::ZL, SDL_CONTROLLER_AXIS_TRIGGERLEFT}, - {Settings::NativeButton::ZR, SDL_CONTROLLER_AXIS_TRIGGERRIGHT}, - }}; +ButtonBindings SDLState::GetNintendoButtonBinding( + const std::shared_ptr<SDLJoystick>& joystick) const { + // Default SL/SR mapping for pro controllers + auto sl_button = SDL_CONTROLLER_BUTTON_LEFTSHOULDER; + auto sr_button = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER; + + if (joystick->IsJoyconLeft()) { + sl_button = SDL_CONTROLLER_BUTTON_PADDLE2; + sr_button = SDL_CONTROLLER_BUTTON_PADDLE4; + } + if (joystick->IsJoyconRight()) { + sl_button = SDL_CONTROLLER_BUTTON_PADDLE3; + sr_button = SDL_CONTROLLER_BUTTON_PADDLE1; + } + return { + std::pair{Settings::NativeButton::A, SDL_CONTROLLER_BUTTON_A}, + {Settings::NativeButton::B, SDL_CONTROLLER_BUTTON_B}, + {Settings::NativeButton::X, SDL_CONTROLLER_BUTTON_X}, + {Settings::NativeButton::Y, SDL_CONTROLLER_BUTTON_Y}, + {Settings::NativeButton::LStick, SDL_CONTROLLER_BUTTON_LEFTSTICK}, + {Settings::NativeButton::RStick, SDL_CONTROLLER_BUTTON_RIGHTSTICK}, + {Settings::NativeButton::L, SDL_CONTROLLER_BUTTON_LEFTSHOULDER}, + {Settings::NativeButton::R, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER}, + {Settings::NativeButton::Plus, SDL_CONTROLLER_BUTTON_START}, + {Settings::NativeButton::Minus, SDL_CONTROLLER_BUTTON_BACK}, + {Settings::NativeButton::DLeft, SDL_CONTROLLER_BUTTON_DPAD_LEFT}, + {Settings::NativeButton::DUp, SDL_CONTROLLER_BUTTON_DPAD_UP}, + {Settings::NativeButton::DRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT}, + {Settings::NativeButton::DDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN}, + {Settings::NativeButton::SL, sl_button}, + {Settings::NativeButton::SR, sr_button}, + {Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE}, + }; +} + +ButtonMapping SDLState::GetSingleControllerMapping( + const std::shared_ptr<SDLJoystick>& joystick, const ButtonBindings& switch_to_sdl_button, + const ZButtonBindings& switch_to_sdl_axis) const { ButtonMapping mapping; mapping.reserve(switch_to_sdl_button.size() + switch_to_sdl_axis.size()); + auto* controller = joystick->GetSDLGameController(); for (const auto& [switch_button, sdl_button] : switch_to_sdl_button) { const auto& binding = SDL_GameControllerGetBindForButton(controller, sdl_button); @@ -1133,11 +1222,68 @@ ButtonMapping SDLState::GetButtonMappingForDevice(const Common::ParamPackage& pa return mapping; } +ButtonMapping SDLState::GetDualControllerMapping(const std::shared_ptr<SDLJoystick>& joystick, + const std::shared_ptr<SDLJoystick>& joystick2, + const ButtonBindings& switch_to_sdl_button, + const ZButtonBindings& switch_to_sdl_axis) const { + ButtonMapping mapping; + mapping.reserve(switch_to_sdl_button.size() + switch_to_sdl_axis.size()); + auto* controller = joystick->GetSDLGameController(); + auto* controller2 = joystick2->GetSDLGameController(); + + for (const auto& [switch_button, sdl_button] : switch_to_sdl_button) { + if (IsButtonOnLeftSide(switch_button)) { + const auto& binding = SDL_GameControllerGetBindForButton(controller2, sdl_button); + mapping.insert_or_assign( + switch_button, + BuildParamPackageForBinding(joystick2->GetPort(), joystick2->GetGUID(), binding)); + continue; + } + const auto& binding = SDL_GameControllerGetBindForButton(controller, sdl_button); + mapping.insert_or_assign( + switch_button, + BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding)); + } + for (const auto& [switch_button, sdl_axis] : switch_to_sdl_axis) { + if (IsButtonOnLeftSide(switch_button)) { + const auto& binding = SDL_GameControllerGetBindForAxis(controller2, sdl_axis); + mapping.insert_or_assign( + switch_button, + BuildParamPackageForBinding(joystick2->GetPort(), joystick2->GetGUID(), binding)); + continue; + } + const auto& binding = SDL_GameControllerGetBindForAxis(controller, sdl_axis); + mapping.insert_or_assign( + switch_button, + BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding)); + } + + return mapping; +} + +bool SDLState::IsButtonOnLeftSide(Settings::NativeButton::Values button) const { + switch (button) { + case Settings::NativeButton::DDown: + case Settings::NativeButton::DLeft: + case Settings::NativeButton::DRight: + case Settings::NativeButton::DUp: + case Settings::NativeButton::L: + case Settings::NativeButton::LStick: + case Settings::NativeButton::Minus: + case Settings::NativeButton::Screenshot: + case Settings::NativeButton::ZL: + return true; + default: + return false; + } +} + AnalogMapping SDLState::GetAnalogMappingForDevice(const Common::ParamPackage& params) { if (!params.Has("guid") || !params.Has("port")) { return {}; } const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0)); + const auto joystick2 = GetSDLJoystickByGUID(params.Get("guid2", ""), params.Get("port", 0)); auto* controller = joystick->GetSDLGameController(); if (controller == nullptr) { return {}; @@ -1148,10 +1294,17 @@ AnalogMapping SDLState::GetAnalogMappingForDevice(const Common::ParamPackage& pa SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX); const auto& binding_left_y = SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY); - mapping.insert_or_assign(Settings::NativeAnalog::LStick, - BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(), - binding_left_x.value.axis, - binding_left_y.value.axis)); + if (params.Has("guid2")) { + mapping.insert_or_assign( + Settings::NativeAnalog::LStick, + BuildParamPackageForAnalog(joystick2->GetPort(), joystick2->GetGUID(), + binding_left_x.value.axis, binding_left_y.value.axis)); + } else { + mapping.insert_or_assign( + Settings::NativeAnalog::LStick, + BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(), + binding_left_x.value.axis, binding_left_y.value.axis)); + } const auto& binding_right_x = SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX); const auto& binding_right_y = @@ -1168,20 +1321,32 @@ MotionMapping SDLState::GetMotionMappingForDevice(const Common::ParamPackage& pa return {}; } const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0)); + const auto joystick2 = GetSDLJoystickByGUID(params.Get("guid2", ""), params.Get("port", 0)); auto* controller = joystick->GetSDLGameController(); if (controller == nullptr) { return {}; } + MotionMapping mapping = {}; joystick->EnableMotion(); - if (!joystick->HasGyro() && !joystick->HasAccel()) { - return {}; + if (joystick->HasGyro() || joystick->HasAccel()) { + mapping.insert_or_assign(Settings::NativeMotion::MotionRight, + BuildMotionParam(joystick->GetPort(), joystick->GetGUID())); + } + if (params.Has("guid2")) { + joystick2->EnableMotion(); + if (joystick2->HasGyro() || joystick2->HasAccel()) { + mapping.insert_or_assign(Settings::NativeMotion::MotionLeft, + BuildMotionParam(joystick2->GetPort(), joystick2->GetGUID())); + } + } else { + if (joystick->HasGyro() || joystick->HasAccel()) { + mapping.insert_or_assign(Settings::NativeMotion::MotionLeft, + BuildMotionParam(joystick->GetPort(), joystick->GetGUID())); + } } - MotionMapping mapping = {}; - mapping.insert_or_assign(Settings::NativeMotion::MotionLeft, - BuildMotionParam(joystick->GetPort(), joystick->GetGUID())); return mapping; } namespace Polling { diff --git a/src/input_common/sdl/sdl_impl.h b/src/input_common/sdl/sdl_impl.h index 121e019132..b77afcbd8c 100644 --- a/src/input_common/sdl/sdl_impl.h +++ b/src/input_common/sdl/sdl_impl.h @@ -9,6 +9,17 @@ #include <mutex> #include <thread> #include <unordered_map> + +// Ignore -Wimplicit-fallthrough due to https://github.com/libsdl-org/SDL/issues/4307 +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wimplicit-fallthrough" +#endif +#include <SDL.h> +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + #include "common/common_types.h" #include "common/threadsafe_queue.h" #include "input_common/sdl/sdl.h" @@ -18,6 +29,11 @@ using SDL_GameController = struct _SDL_GameController; using SDL_Joystick = struct _SDL_Joystick; using SDL_JoystickID = s32; +using ButtonBindings = + std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerButton>, 17>; +using ZButtonBindings = + std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerAxis>, 2>; + namespace InputCommon::SDL { class SDLAnalogFactory; @@ -66,8 +82,25 @@ private: /// Needs to be called before SDL_QuitSubSystem. void CloseJoysticks(); - /// Returns a custom name for specific controllers because the default name is not correct - std::string GetControllerName(SDL_GameController* controller) const; + /// Returns the default button bindings list for generic controllers + ButtonBindings GetDefaultButtonBinding() const; + + /// Returns the default button bindings list for nintendo controllers + ButtonBindings GetNintendoButtonBinding(const std::shared_ptr<SDLJoystick>& joystick) const; + + /// Returns the button mappings from a single controller + ButtonMapping GetSingleControllerMapping(const std::shared_ptr<SDLJoystick>& joystick, + const ButtonBindings& switch_to_sdl_button, + const ZButtonBindings& switch_to_sdl_axis) const; + + /// Returns the button mappings from two different controllers + ButtonMapping GetDualControllerMapping(const std::shared_ptr<SDLJoystick>& joystick, + const std::shared_ptr<SDLJoystick>& joystick2, + const ButtonBindings& switch_to_sdl_button, + const ZButtonBindings& switch_to_sdl_axis) const; + + /// Returns true if the button is on the left joycon + bool IsButtonOnLeftSide(Settings::NativeButton::Values button) const; // Set to true if SDL supports game controller subsystem bool has_gamecontroller = false; diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp index fa37aa79af..5edd06ebc5 100644 --- a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp +++ b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp @@ -53,6 +53,18 @@ struct Range { UNREACHABLE_MSG("Invalid memory usage={}", usage); return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; } + +constexpr VkExportMemoryAllocateInfo EXPORT_ALLOCATE_INFO{ + .sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO, + .pNext = nullptr, +#ifdef _WIN32 + .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT, +#elif __unix__ + .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT, +#else + .handleTypes = 0, +#endif +}; } // Anonymous namespace class MemoryAllocation { @@ -131,7 +143,7 @@ public: /// Returns whether this allocation is compatible with the arguments. [[nodiscard]] bool IsCompatible(VkMemoryPropertyFlags flags, u32 type_mask) const { - return (flags & property_flags) && (type_mask & shifted_memory_type) != 0; + return (flags & property_flags) == property_flags && (type_mask & shifted_memory_type) != 0; } private: @@ -217,14 +229,18 @@ MemoryAllocator::~MemoryAllocator() = default; MemoryCommit MemoryAllocator::Commit(const VkMemoryRequirements& requirements, MemoryUsage usage) { // Find the fastest memory flags we can afford with the current requirements - const VkMemoryPropertyFlags flags = MemoryPropertyFlags(requirements.memoryTypeBits, usage); + const u32 type_mask = requirements.memoryTypeBits; + const VkMemoryPropertyFlags usage_flags = MemoryUsagePropertyFlags(usage); + const VkMemoryPropertyFlags flags = MemoryPropertyFlags(type_mask, usage_flags); if (std::optional<MemoryCommit> commit = TryCommit(requirements, flags)) { return std::move(*commit); } // Commit has failed, allocate more memory. - // TODO(Rodrigo): Handle out of memory situations in some way like flushing to guest memory. - AllocMemory(flags, requirements.memoryTypeBits, AllocationChunkSize(requirements.size)); - + const u64 chunk_size = AllocationChunkSize(requirements.size); + if (!TryAllocMemory(flags, type_mask, chunk_size)) { + // TODO(Rodrigo): Handle out of memory situations in some way like flushing to guest memory. + throw vk::Exception(VK_ERROR_OUT_OF_DEVICE_MEMORY); + } // Commit again, this time it won't fail since there's a fresh allocation above. // If it does, there's a bug. return TryCommit(requirements, flags).value(); @@ -242,26 +258,25 @@ MemoryCommit MemoryAllocator::Commit(const vk::Image& image, MemoryUsage usage) return commit; } -void MemoryAllocator::AllocMemory(VkMemoryPropertyFlags flags, u32 type_mask, u64 size) { +bool MemoryAllocator::TryAllocMemory(VkMemoryPropertyFlags flags, u32 type_mask, u64 size) { const u32 type = FindType(flags, type_mask).value(); - const VkExportMemoryAllocateInfo export_allocate_info{ - .sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO, - .pNext = nullptr, -#ifdef _WIN32 - .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT, -#elif __unix__ - .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT, -#else - .handleTypes = 0, -#endif - }; - vk::DeviceMemory memory = device.GetLogical().AllocateMemory({ + vk::DeviceMemory memory = device.GetLogical().TryAllocateMemory({ .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, - .pNext = export_allocations ? &export_allocate_info : nullptr, + .pNext = export_allocations ? &EXPORT_ALLOCATE_INFO : nullptr, .allocationSize = size, .memoryTypeIndex = type, }); + if (!memory) { + if ((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0) { + // Try to allocate non device local memory + return TryAllocMemory(flags & ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, type_mask, size); + } else { + // RIP + return false; + } + } allocations.push_back(std::make_unique<MemoryAllocation>(std::move(memory), flags, size, type)); + return true; } std::optional<MemoryCommit> MemoryAllocator::TryCommit(const VkMemoryRequirements& requirements, @@ -274,24 +289,24 @@ std::optional<MemoryCommit> MemoryAllocator::TryCommit(const VkMemoryRequirement return commit; } } + if ((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0) { + // Look for non device local commits on failure + return TryCommit(requirements, flags & ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + } return std::nullopt; } -VkMemoryPropertyFlags MemoryAllocator::MemoryPropertyFlags(u32 type_mask, MemoryUsage usage) const { - return MemoryPropertyFlags(type_mask, MemoryUsagePropertyFlags(usage)); -} - VkMemoryPropertyFlags MemoryAllocator::MemoryPropertyFlags(u32 type_mask, VkMemoryPropertyFlags flags) const { if (FindType(flags, type_mask)) { // Found a memory type with those requirements return flags; } - if (flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) { + if ((flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) != 0) { // Remove host cached bit in case it's not supported return MemoryPropertyFlags(type_mask, flags & ~VK_MEMORY_PROPERTY_HOST_CACHED_BIT); } - if (flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) { + if ((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0) { // Remove device local, if it's not supported by the requested resource return MemoryPropertyFlags(type_mask, flags & ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); } @@ -302,7 +317,7 @@ VkMemoryPropertyFlags MemoryAllocator::MemoryPropertyFlags(u32 type_mask, std::optional<u32> MemoryAllocator::FindType(VkMemoryPropertyFlags flags, u32 type_mask) const { for (u32 type_index = 0; type_index < properties.memoryTypeCount; ++type_index) { const VkMemoryPropertyFlags type_flags = properties.memoryTypes[type_index].propertyFlags; - if ((type_mask & (1U << type_index)) && (type_flags & flags)) { + if ((type_mask & (1U << type_index)) != 0 && (type_flags & flags) == flags) { // The type matches in type and in the wanted properties. return type_index; } diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.h b/src/video_core/vulkan_common/vulkan_memory_allocator.h index d1ce294504..db12d02f40 100644 --- a/src/video_core/vulkan_common/vulkan_memory_allocator.h +++ b/src/video_core/vulkan_common/vulkan_memory_allocator.h @@ -101,16 +101,13 @@ public: MemoryCommit Commit(const vk::Image& image, MemoryUsage usage); private: - /// Allocates a chunk of memory. - void AllocMemory(VkMemoryPropertyFlags flags, u32 type_mask, u64 size); + /// Tries to allocate a chunk of memory. + bool TryAllocMemory(VkMemoryPropertyFlags flags, u32 type_mask, u64 size); /// Tries to allocate a memory commit. std::optional<MemoryCommit> TryCommit(const VkMemoryRequirements& requirements, VkMemoryPropertyFlags flags); - /// Returns the fastest compatible memory property flags from a wanted usage. - VkMemoryPropertyFlags MemoryPropertyFlags(u32 type_mask, MemoryUsage usage) const; - /// Returns the fastest compatible memory property flags from the wanted flags. VkMemoryPropertyFlags MemoryPropertyFlags(u32 type_mask, VkMemoryPropertyFlags flags) const; diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index eb58bfa5b9..552454acf2 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -16,7 +16,7 @@ namespace FS = Common::FS; -Config::Config(const std::string& config_name, ConfigType config_type) : type(config_type) { +Config::Config(std::string_view config_name, ConfigType config_type) : type(config_type) { global = config_type == ConfigType::GlobalConfig; Initialize(config_name); @@ -242,7 +242,7 @@ const std::array<UISettings::Shortcut, 17> Config::default_hotkeys{{ }}; // clang-format on -void Config::Initialize(const std::string& config_name) { +void Config::Initialize(std::string_view config_name) { const auto fs_config_loc = FS::GetYuzuPath(FS::YuzuPath::ConfigDir); const auto config_file = fmt::format("{}.ini", config_name); diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h index ce3355588b..114a2eaa77 100644 --- a/src/yuzu/configuration/config.h +++ b/src/yuzu/configuration/config.h @@ -22,7 +22,7 @@ public: InputProfile, }; - explicit Config(const std::string& config_name = "qt-config", + explicit Config(std::string_view config_name = "qt-config", ConfigType config_type = ConfigType::GlobalConfig); ~Config(); @@ -45,7 +45,7 @@ public: static const std::array<UISettings::Shortcut, 17> default_hotkeys; private: - void Initialize(const std::string& config_name); + void Initialize(std::string_view config_name); void ReadValues(); void ReadPlayerValue(std::size_t player_index); diff --git a/src/yuzu/configuration/configure_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp index d89f1ad4b8..7dfcf150c9 100644 --- a/src/yuzu/configuration/configure_per_game.cpp +++ b/src/yuzu/configuration/configure_per_game.cpp @@ -4,6 +4,7 @@ #include <algorithm> #include <memory> +#include <string> #include <utility> #include <QAbstractButton> @@ -17,6 +18,7 @@ #include <QTimer> #include <QTreeView> +#include "common/fs/path_util.h" #include "core/core.h" #include "core/file_sys/control_metadata.h" #include "core/file_sys/patch_manager.h" @@ -29,10 +31,11 @@ #include "yuzu/uisettings.h" #include "yuzu/util/util.h" -ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id) +ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id, std::string_view file_name) : QDialog(parent), ui(std::make_unique<Ui::ConfigurePerGame>()), title_id(title_id) { - game_config = std::make_unique<Config>(fmt::format("{:016X}", title_id), - Config::ConfigType::PerGameConfig); + const auto config_file_name = + title_id == 0 ? Common::FS::GetFilename(file_name) : fmt::format("{:016X}", title_id); + game_config = std::make_unique<Config>(config_file_name, Config::ConfigType::PerGameConfig); Settings::SetConfiguringGlobal(false); diff --git a/src/yuzu/configuration/configure_per_game.h b/src/yuzu/configuration/configure_per_game.h index f6e6ab7c49..dc6b68763c 100644 --- a/src/yuzu/configuration/configure_per_game.h +++ b/src/yuzu/configuration/configure_per_game.h @@ -5,6 +5,7 @@ #pragma once #include <memory> +#include <string> #include <vector> #include <QDialog> @@ -27,7 +28,7 @@ class ConfigurePerGame : public QDialog { Q_OBJECT public: - explicit ConfigurePerGame(QWidget* parent, u64 title_id); + explicit ConfigurePerGame(QWidget* parent, u64 title_id, std::string_view file_name); ~ConfigurePerGame() override; /// Save all button configurations to settings file diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index 63cf82f7d6..aa3bd5e349 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -561,11 +561,11 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri connect(remove_dlc, &QAction::triggered, [this, program_id]() { emit RemoveInstalledEntryRequested(program_id, InstalledEntryType::AddOnContent); }); - connect(remove_shader_cache, &QAction::triggered, [this, program_id]() { - emit RemoveFileRequested(program_id, GameListRemoveTarget::ShaderCache); + connect(remove_shader_cache, &QAction::triggered, [this, program_id, path]() { + emit RemoveFileRequested(program_id, GameListRemoveTarget::ShaderCache, path); }); - connect(remove_custom_config, &QAction::triggered, [this, program_id]() { - emit RemoveFileRequested(program_id, GameListRemoveTarget::CustomConfiguration); + connect(remove_custom_config, &QAction::triggered, [this, program_id, path]() { + emit RemoveFileRequested(program_id, GameListRemoveTarget::CustomConfiguration, path); }); connect(dump_romfs, &QAction::triggered, [this, program_id, path]() { emit DumpRomFSRequested(program_id, path); }); diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h index 9c0a1a4823..2867f66532 100644 --- a/src/yuzu/game_list.h +++ b/src/yuzu/game_list.h @@ -88,7 +88,8 @@ signals: const std::string& game_path); void OpenTransferableShaderCacheRequested(u64 program_id); void RemoveInstalledEntryRequested(u64 program_id, InstalledEntryType type); - void RemoveFileRequested(u64 program_id, GameListRemoveTarget target); + void RemoveFileRequested(u64 program_id, GameListRemoveTarget target, + std::string_view game_path); void DumpRomFSRequested(u64 program_id, const std::string& game_path); void CopyTIDRequested(u64 program_id); void NavigateToGamedbEntryRequested(u64 program_id, diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 0f0e228b07..dd8dd32333 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -1334,7 +1334,10 @@ void GMainWindow::BootGame(const QString& filename, std::size_t program_index) { if (!(loader == nullptr || loader->ReadProgramId(title_id) != Loader::ResultStatus::Success)) { // Load per game settings - Config per_game_config(fmt::format("{:016X}", title_id), Config::ConfigType::PerGameConfig); + const auto config_file_name = title_id == 0 + ? Common::FS::GetFilename(filename.toStdString()) + : fmt::format("{:016X}", title_id); + Config per_game_config(config_file_name, Config::ConfigType::PerGameConfig); } ConfigureVibration::SetAllVibrationDevices(); @@ -1795,7 +1798,8 @@ void GMainWindow::RemoveAddOnContent(u64 program_id, const QString& entry_type) tr("Successfully removed %1 installed DLC.").arg(count)); } -void GMainWindow::OnGameListRemoveFile(u64 program_id, GameListRemoveTarget target) { +void GMainWindow::OnGameListRemoveFile(u64 program_id, GameListRemoveTarget target, + std::string_view game_path) { const QString question = [this, target] { switch (target) { case GameListRemoveTarget::ShaderCache: @@ -1817,7 +1821,7 @@ void GMainWindow::OnGameListRemoveFile(u64 program_id, GameListRemoveTarget targ RemoveTransferableShaderCache(program_id); break; case GameListRemoveTarget::CustomConfiguration: - RemoveCustomConfiguration(program_id); + RemoveCustomConfiguration(program_id, game_path); break; } } @@ -1842,9 +1846,12 @@ void GMainWindow::RemoveTransferableShaderCache(u64 program_id) { } } -void GMainWindow::RemoveCustomConfiguration(u64 program_id) { - const auto custom_config_file_path = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir) / - "custom" / fmt::format("{:016X}.ini", program_id); +void GMainWindow::RemoveCustomConfiguration(u64 program_id, std::string_view game_path) { + const auto config_file_name = program_id == 0 + ? fmt::format("{:s}.ini", Common::FS::GetFilename(game_path)) + : fmt::format("{:016X}.ini", program_id); + const auto custom_config_file_path = + Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir) / "custom" / config_file_name; if (!Common::FS::Exists(custom_config_file_path)) { QMessageBox::warning(this, tr("Error Removing Custom Configuration"), @@ -2635,7 +2642,7 @@ void GMainWindow::OpenPerGameConfiguration(u64 title_id, const std::string& file const auto v_file = Core::GetGameFileFromPath(vfs, file_name); const auto& system = Core::System::GetInstance(); - ConfigurePerGame dialog(this, title_id); + ConfigurePerGame dialog(this, title_id, file_name); dialog.LoadFromFile(v_file); const auto result = dialog.exec(); diff --git a/src/yuzu/main.h b/src/yuzu/main.h index b3a5033cee..135681d419 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -236,7 +236,8 @@ private slots: const std::string& game_path); void OnTransferableShaderCacheOpenFile(u64 program_id); void OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryType type); - void OnGameListRemoveFile(u64 program_id, GameListRemoveTarget target); + void OnGameListRemoveFile(u64 program_id, GameListRemoveTarget target, + std::string_view game_path); void OnGameListDumpRomFS(u64 program_id, const std::string& game_path); void OnGameListCopyTID(u64 program_id); void OnGameListNavigateToGamedbEntry(u64 program_id, @@ -275,7 +276,7 @@ private: void RemoveUpdateContent(u64 program_id, const QString& entry_type); void RemoveAddOnContent(u64 program_id, const QString& entry_type); void RemoveTransferableShaderCache(u64 program_id); - void RemoveCustomConfiguration(u64 program_id); + void RemoveCustomConfiguration(u64 program_id, std::string_view game_path); std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id); InstallResult InstallNSPXCI(const QString& filename); InstallResult InstallNCA(const QString& filename); |