diff options
34 files changed, 583 insertions, 139 deletions
diff --git a/.ci/yuzu-mainline-step2.yml b/.ci/yuzu-mainline-step2.yml index a90041d28..3159ce3ed 100644 --- a/.ci/yuzu-mainline-step2.yml +++ b/.ci/yuzu-mainline-step2.yml @@ -19,6 +19,7 @@ stages: displayName: 'build' jobs: - job: build + timeoutInMinutes: 120 displayName: 'standard' pool: vmImage: ubuntu-latest @@ -43,6 +44,7 @@ stages: displayName: 'build-windows' jobs: - job: build + timeoutInMinutes: 120 displayName: 'msvc' pool: vmImage: windows-2019 @@ -65,4 +67,4 @@ stages: - job: github displayName: 'github' steps: - - template: ./templates/release-github.yml
\ No newline at end of file + - template: ./templates/release-github.yml diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index c7b899131..5c99c00f5 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -517,6 +517,8 @@ add_library(core STATIC hle/service/psc/psc.h hle/service/ptm/psm.cpp hle/service/ptm/psm.h + hle/service/kernel_helpers.cpp + hle/service/kernel_helpers.h hle/service/service.cpp hle/service/service.h hle/service/set/set.cpp diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index 28ed6265a..ca68fc325 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -58,6 +58,9 @@ bool SessionRequestManager::HasSessionRequestHandler(const HLERequestContext& co void SessionRequestHandler::ClientConnected(KServerSession* session) { session->ClientConnected(shared_from_this()); + + // Ensure our server session is tracked globally. + kernel.RegisterServerSession(session); } void SessionRequestHandler::ClientDisconnected(KServerSession* session) { diff --git a/src/core/hle/kernel/k_auto_object.cpp b/src/core/hle/kernel/k_auto_object.cpp index dbe237f09..c99a9ebb7 100644 --- a/src/core/hle/kernel/k_auto_object.cpp +++ b/src/core/hle/kernel/k_auto_object.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include "core/hle/kernel/k_auto_object.h" +#include "core/hle/kernel/kernel.h" namespace Kernel { @@ -11,4 +12,12 @@ KAutoObject* KAutoObject::Create(KAutoObject* obj) { return obj; } +void KAutoObject::RegisterWithKernel() { + kernel.RegisterKernelObject(this); +} + +void KAutoObject::UnregisterWithKernel() { + kernel.UnregisterKernelObject(this); +} + } // namespace Kernel diff --git a/src/core/hle/kernel/k_auto_object.h b/src/core/hle/kernel/k_auto_object.h index 88a052f65..e4fcdbc67 100644 --- a/src/core/hle/kernel/k_auto_object.h +++ b/src/core/hle/kernel/k_auto_object.h @@ -85,8 +85,12 @@ private: KERNEL_AUTOOBJECT_TRAITS(KAutoObject, KAutoObject); public: - explicit KAutoObject(KernelCore& kernel_) : kernel(kernel_) {} - virtual ~KAutoObject() = default; + explicit KAutoObject(KernelCore& kernel_) : kernel(kernel_) { + RegisterWithKernel(); + } + virtual ~KAutoObject() { + UnregisterWithKernel(); + } static KAutoObject* Create(KAutoObject* ptr); @@ -166,6 +170,10 @@ public: } } +private: + void RegisterWithKernel(); + void UnregisterWithKernel(); + protected: KernelCore& kernel; std::string name; diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp index d1bd98051..8ead1a769 100644 --- a/src/core/hle/kernel/k_process.cpp +++ b/src/core/hle/kernel/k_process.cpp @@ -10,6 +10,7 @@ #include "common/alignment.h" #include "common/assert.h" #include "common/logging/log.h" +#include "common/scope_exit.h" #include "common/settings.h" #include "core/core.h" #include "core/device_memory.h" @@ -43,6 +44,8 @@ void SetupMainThread(Core::System& system, KProcess& owner_process, u32 priority ASSERT(owner_process.GetResourceLimit()->Reserve(LimitableResource::Threads, 1)); KThread* thread = KThread::Create(system.Kernel()); + SCOPE_EXIT({ thread->Close(); }); + ASSERT(KThread::InitializeUserThread(system, thread, entry_point, 0, stack_top, priority, owner_process.GetIdealCoreId(), &owner_process) .IsSuccess()); @@ -162,7 +165,7 @@ void KProcess::DecrementThreadCount() { ASSERT(num_threads > 0); if (const auto count = --num_threads; count == 0) { - UNIMPLEMENTED_MSG("Process termination is not implemented!"); + LOG_WARNING(Kernel, "Process termination is not fully implemented."); } } @@ -406,6 +409,9 @@ void KProcess::Finalize() { resource_limit->Close(); } + // Finalize the handle table and close any open handles. + handle_table.Finalize(); + // Perform inherited finalization. KAutoObjectWithSlabHeapAndContainer<KProcess, KSynchronizationObject>::Finalize(); } diff --git a/src/core/hle/kernel/k_server_session.cpp b/src/core/hle/kernel/k_server_session.cpp index 5c3c13ce6..b9f24475c 100644 --- a/src/core/hle/kernel/k_server_session.cpp +++ b/src/core/hle/kernel/k_server_session.cpp @@ -28,7 +28,10 @@ namespace Kernel { KServerSession::KServerSession(KernelCore& kernel_) : KSynchronizationObject{kernel_} {} -KServerSession::~KServerSession() {} +KServerSession::~KServerSession() { + // Ensure that the global list tracking server sessions does not hold on to a reference. + kernel.UnregisterServerSession(this); +} void KServerSession::Initialize(KSession* parent_session_, std::string&& name_, std::shared_ptr<SessionRequestManager> manager_) { diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 64bd0c494..92fbc5532 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -61,6 +61,7 @@ struct KernelCore::Impl { void Initialize(KernelCore& kernel) { global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel); global_handle_table = std::make_unique<Kernel::KHandleTable>(kernel); + global_handle_table->Initialize(KHandleTable::MaxTableSize); is_phantom_mode_for_singlecore = false; @@ -90,9 +91,39 @@ struct KernelCore::Impl { } void Shutdown() { + // Shutdown all processes. + if (current_process) { + current_process->Finalize(); + current_process->Close(); + current_process = nullptr; + } process_list.clear(); - // Ensures all service threads gracefully shutdown + // Close all open server ports. + std::unordered_set<KServerPort*> server_ports_; + { + std::lock_guard lk(server_ports_lock); + server_ports_ = server_ports; + server_ports.clear(); + } + for (auto* server_port : server_ports_) { + server_port->Close(); + } + // Close all open server sessions. + std::unordered_set<KServerSession*> server_sessions_; + { + std::lock_guard lk(server_sessions_lock); + server_sessions_ = server_sessions; + server_sessions.clear(); + } + for (auto* server_session : server_sessions_) { + server_session->Close(); + } + + // Ensure that the object list container is finalized and properly shutdown. + object_list_container.Finalize(); + + // Ensures all service threads gracefully shutdown. service_threads.clear(); next_object_id = 0; @@ -111,11 +142,7 @@ struct KernelCore::Impl { cores.clear(); - if (current_process) { - current_process->Close(); - current_process = nullptr; - } - + global_handle_table->Finalize(); global_handle_table.reset(); preemption_event = nullptr; @@ -142,6 +169,16 @@ struct KernelCore::Impl { // Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others next_host_thread_id = Core::Hardware::NUM_CPU_CORES; + + // Track kernel objects that were not freed on shutdown + { + std::lock_guard lk(registered_objects_lock); + if (registered_objects.size()) { + LOG_WARNING(Kernel, "{} kernel objects were dangling on shutdown!", + registered_objects.size()); + registered_objects.clear(); + } + } } void InitializePhysicalCores() { @@ -630,6 +667,21 @@ struct KernelCore::Impl { user_slab_heap_size); } + KClientPort* CreateNamedServicePort(std::string name) { + auto search = service_interface_factory.find(name); + if (search == service_interface_factory.end()) { + UNIMPLEMENTED(); + return {}; + } + + KClientPort* port = &search->second(system.ServiceManager(), system); + { + std::lock_guard lk(server_ports_lock); + server_ports.insert(&port->GetParent()->GetServerPort()); + } + return port; + } + std::atomic<u32> next_object_id{0}; std::atomic<u64> next_kernel_process_id{KProcess::InitialKIPIDMin}; std::atomic<u64> next_user_process_id{KProcess::ProcessIDMin}; @@ -656,6 +708,12 @@ struct KernelCore::Impl { /// the ConnectToPort SVC. std::unordered_map<std::string, ServiceInterfaceFactory> service_interface_factory; NamedPortTable named_ports; + std::unordered_set<KServerPort*> server_ports; + std::unordered_set<KServerSession*> server_sessions; + std::unordered_set<KAutoObject*> registered_objects; + std::mutex server_ports_lock; + std::mutex server_sessions_lock; + std::mutex registered_objects_lock; std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor; std::vector<Kernel::PhysicalCore> cores; @@ -844,12 +902,27 @@ void KernelCore::RegisterNamedService(std::string name, ServiceInterfaceFactory& } KClientPort* KernelCore::CreateNamedServicePort(std::string name) { - auto search = impl->service_interface_factory.find(name); - if (search == impl->service_interface_factory.end()) { - UNIMPLEMENTED(); - return {}; - } - return &search->second(impl->system.ServiceManager(), impl->system); + return impl->CreateNamedServicePort(std::move(name)); +} + +void KernelCore::RegisterServerSession(KServerSession* server_session) { + std::lock_guard lk(impl->server_sessions_lock); + impl->server_sessions.insert(server_session); +} + +void KernelCore::UnregisterServerSession(KServerSession* server_session) { + std::lock_guard lk(impl->server_sessions_lock); + impl->server_sessions.erase(server_session); +} + +void KernelCore::RegisterKernelObject(KAutoObject* object) { + std::lock_guard lk(impl->registered_objects_lock); + impl->registered_objects.insert(object); +} + +void KernelCore::UnregisterKernelObject(KAutoObject* object) { + std::lock_guard lk(impl->registered_objects_lock); + impl->registered_objects.erase(object); } bool KernelCore::IsValidNamedPort(NamedPortTable::const_iterator port) const { diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 2d01e1ae0..3a6db0b1c 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -45,6 +45,7 @@ class KPort; class KProcess; class KResourceLimit; class KScheduler; +class KServerSession; class KSession; class KSharedMemory; class KThread; @@ -185,6 +186,22 @@ public: /// Opens a port to a service previously registered with RegisterNamedService. KClientPort* CreateNamedServicePort(std::string name); + /// Registers a server session with the gobal emulation state, to be freed on shutdown. This is + /// necessary because we do not emulate processes for HLE sessions. + void RegisterServerSession(KServerSession* server_session); + + /// Unregisters a server session previously registered with RegisterServerSession when it was + /// destroyed during the current emulation session. + void UnregisterServerSession(KServerSession* server_session); + + /// Registers all kernel objects with the global emulation state, this is purely for tracking + /// leaks after emulation has been shutdown. + void RegisterKernelObject(KAutoObject* object); + + /// Unregisters a kernel object previously registered with RegisterKernelObject when it was + /// destroyed during the current emulation session. + void UnregisterKernelObject(KAutoObject* object); + /// Determines whether or not the given port is a valid named port. bool IsValidNamedPort(NamedPortTable::const_iterator port) const; diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 8339e11a0..2eb532472 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -298,6 +298,7 @@ static ResultCode ConnectToNamedPort(Core::System& system, Handle* out, VAddr po // Create a session. KClientSession* session{}; R_TRY(port->CreateSession(std::addressof(session))); + port->Close(); // Register the session in the table, close the extra reference. handle_table.Register(*out, session); @@ -1439,11 +1440,6 @@ static void ExitProcess(Core::System& system) { LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID()); ASSERT_MSG(current_process->GetStatus() == ProcessStatus::Running, "Process has already exited"); - - current_process->PrepareForTermination(); - - // Kill the current thread - system.Kernel().CurrentScheduler()->GetCurrentThread()->Exit(); } static void ExitProcess32(Core::System& system) { diff --git a/src/core/hle/service/am/applets/applet_controller.cpp b/src/core/hle/service/am/applets/applet_controller.cpp index 12682effe..2721679c1 100644 --- a/src/core/hle/service/am/applets/applet_controller.cpp +++ b/src/core/hle/service/am/applets/applet_controller.cpp @@ -87,6 +87,10 @@ void Controller::Initialize() { case sizeof(ControllerUpdateFirmwareArg): controller_private_arg.mode = ControllerSupportMode::ShowControllerFirmwareUpdate; break; + case sizeof(ControllerKeyRemappingArg): + controller_private_arg.mode = + ControllerSupportMode::ShowControllerKeyRemappingForSystem; + break; default: UNIMPLEMENTED_MSG("Unknown ControllerPrivateArg mode={} with arg_size={}", controller_private_arg.mode, controller_private_arg.arg_size); @@ -99,7 +103,9 @@ void Controller::Initialize() { // This is always 0 (Application) except with ShowControllerFirmwareUpdateForSystem. if (controller_private_arg.caller >= ControllerSupportCaller::MaxControllerSupportCaller) { if (controller_private_arg.flag_1 && - controller_private_arg.mode == ControllerSupportMode::ShowControllerFirmwareUpdate) { + (controller_private_arg.mode == ControllerSupportMode::ShowControllerFirmwareUpdate || + controller_private_arg.mode == + ControllerSupportMode::ShowControllerKeyRemappingForSystem)) { controller_private_arg.caller = ControllerSupportCaller::System; } else { controller_private_arg.caller = ControllerSupportCaller::Application; @@ -121,6 +127,7 @@ void Controller::Initialize() { std::memcpy(&controller_user_arg_old, user_arg.data(), user_arg.size()); break; case ControllerAppletVersion::Version7: + case ControllerAppletVersion::Version8: ASSERT(user_arg.size() == sizeof(ControllerSupportArgNew)); std::memcpy(&controller_user_arg_new, user_arg.data(), user_arg.size()); break; @@ -143,6 +150,16 @@ void Controller::Initialize() { std::memcpy(&controller_update_arg, update_arg.data(), update_arg.size()); break; } + case ControllerSupportMode::ShowControllerKeyRemappingForSystem: { + const auto remapping_arg_storage = broker.PopNormalDataToApplet(); + ASSERT(remapping_arg_storage != nullptr); + + const auto& remapping_arg = remapping_arg_storage->GetData(); + ASSERT(remapping_arg.size() == sizeof(ControllerKeyRemappingArg)); + + std::memcpy(&controller_key_remapping_arg, remapping_arg.data(), remapping_arg.size()); + break; + } default: { UNIMPLEMENTED_MSG("Unimplemented ControllerSupportMode={}", controller_private_arg.mode); break; @@ -179,6 +196,7 @@ void Controller::Execute() { std::vector<ExplainText>(controller_user_arg_old.explain_text.begin(), controller_user_arg_old.explain_text.end())); case ControllerAppletVersion::Version7: + case ControllerAppletVersion::Version8: default: return ConvertToFrontendParameters( controller_private_arg, controller_user_arg_new.header, @@ -210,6 +228,7 @@ void Controller::Execute() { } case ControllerSupportMode::ShowControllerStrapGuide: case ControllerSupportMode::ShowControllerFirmwareUpdate: + case ControllerSupportMode::ShowControllerKeyRemappingForSystem: UNIMPLEMENTED_MSG("ControllerSupportMode={} is not implemented", controller_private_arg.mode); ConfigurationComplete(); diff --git a/src/core/hle/service/am/applets/applet_controller.h b/src/core/hle/service/am/applets/applet_controller.h index 20617e91f..0a34c4fc0 100644 --- a/src/core/hle/service/am/applets/applet_controller.h +++ b/src/core/hle/service/am/applets/applet_controller.h @@ -25,13 +25,15 @@ enum class ControllerAppletVersion : u32_le { Version3 = 0x3, // 1.0.0 - 2.3.0 Version4 = 0x4, // 3.0.0 - 5.1.0 Version5 = 0x5, // 6.0.0 - 7.0.1 - Version7 = 0x7, // 8.0.0+ + Version7 = 0x7, // 8.0.0 - 10.2.0 + Version8 = 0x8, // 11.0.0+ }; enum class ControllerSupportMode : u8 { ShowControllerSupport, ShowControllerStrapGuide, ShowControllerFirmwareUpdate, + ShowControllerKeyRemappingForSystem, MaxControllerSupportMode, }; @@ -78,7 +80,7 @@ struct ControllerSupportArgOld { static_assert(sizeof(ControllerSupportArgOld) == 0x21C, "ControllerSupportArgOld has incorrect size."); -// LibraryAppletVersion 0x7 +// LibraryAppletVersion 0x7, 0x8 struct ControllerSupportArgNew { ControllerSupportArgHeader header{}; std::array<IdentificationColor, 8> identification_colors{}; @@ -95,6 +97,14 @@ struct ControllerUpdateFirmwareArg { static_assert(sizeof(ControllerUpdateFirmwareArg) == 0x4, "ControllerUpdateFirmwareArg has incorrect size."); +struct ControllerKeyRemappingArg { + u64 unknown{}; + u32 unknown_2{}; + INSERT_PADDING_WORDS(1); +}; +static_assert(sizeof(ControllerKeyRemappingArg) == 0x10, + "ControllerKeyRemappingArg has incorrect size."); + struct ControllerSupportResultInfo { s8 player_count{}; INSERT_PADDING_BYTES(3); @@ -128,6 +138,7 @@ private: ControllerSupportArgOld controller_user_arg_old; ControllerSupportArgNew controller_user_arg_new; ControllerUpdateFirmwareArg controller_update_arg; + ControllerKeyRemappingArg controller_key_remapping_arg; bool complete{false}; ResultCode status{ResultSuccess}; bool is_single_mode{false}; diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index 6ce1360e3..b7f551e40 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -18,6 +18,7 @@ #include "core/hle/kernel/k_writable_event.h" #include "core/hle/kernel/kernel.h" #include "core/hle/service/hid/controllers/npad.h" +#include "core/hle/service/kernel_helpers.h" namespace Service::HID { constexpr s32 HID_JOYSTICK_MAX = 0x7fff; @@ -147,7 +148,9 @@ bool Controller_NPad::IsDeviceHandleValid(const DeviceHandle& device_handle) { device_handle.device_index < DeviceIndex::MaxDeviceIndex; } -Controller_NPad::Controller_NPad(Core::System& system_) : ControllerBase{system_} { +Controller_NPad::Controller_NPad(Core::System& system_, + KernelHelpers::ServiceContext& service_context_) + : ControllerBase{system_}, service_context{service_context_} { latest_vibration_values.fill({DEFAULT_VIBRATION_VALUE, DEFAULT_VIBRATION_VALUE}); } @@ -251,10 +254,9 @@ void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) { } void Controller_NPad::OnInit() { - auto& kernel = system.Kernel(); for (std::size_t i = 0; i < styleset_changed_events.size(); ++i) { - styleset_changed_events[i] = Kernel::KEvent::Create(kernel); - styleset_changed_events[i]->Initialize(fmt::format("npad:NpadStyleSetChanged_{}", i)); + styleset_changed_events[i] = + service_context.CreateEvent(fmt::format("npad:NpadStyleSetChanged_{}", i)); } if (!IsControllerActivated()) { @@ -344,8 +346,7 @@ void Controller_NPad::OnRelease() { } for (std::size_t i = 0; i < styleset_changed_events.size(); ++i) { - styleset_changed_events[i]->Close(); - styleset_changed_events[i] = nullptr; + service_context.CloseEvent(styleset_changed_events[i]); } } diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h index 1409d82a2..4fcc6f93a 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h @@ -20,6 +20,10 @@ class KEvent; class KReadableEvent; } // namespace Kernel +namespace Service::KernelHelpers { +class ServiceContext; +} + namespace Service::HID { constexpr u32 NPAD_HANDHELD = 32; @@ -27,7 +31,8 @@ constexpr u32 NPAD_UNKNOWN = 16; // TODO(ogniK): What is this? class Controller_NPad final : public ControllerBase { public: - explicit Controller_NPad(Core::System& system_); + explicit Controller_NPad(Core::System& system_, + KernelHelpers::ServiceContext& service_context_); ~Controller_NPad() override; // Called when the controller is initialized @@ -566,6 +571,7 @@ private: std::array<std::unique_ptr<Input::MotionDevice>, Settings::NativeMotion::NUM_MOTIONS_HID>, 10>; + KernelHelpers::ServiceContext& service_context; std::mutex mutex; ButtonArray buttons; StickArray sticks; diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index d68b023d0..b8b80570d 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -46,8 +46,9 @@ constexpr auto pad_update_ns = std::chrono::nanoseconds{1000 * 1000}; // constexpr auto motion_update_ns = std::chrono::nanoseconds{15 * 1000 * 1000}; // (15ms, 66.666Hz) constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000; -IAppletResource::IAppletResource(Core::System& system_) - : ServiceFramework{system_, "IAppletResource"} { +IAppletResource::IAppletResource(Core::System& system_, + KernelHelpers::ServiceContext& service_context_) + : ServiceFramework{system_, "IAppletResource"}, service_context{service_context_} { static const FunctionInfo functions[] = { {0, &IAppletResource::GetSharedMemoryHandle, "GetSharedMemoryHandle"}, }; @@ -63,7 +64,7 @@ IAppletResource::IAppletResource(Core::System& system_) MakeController<Controller_Stubbed>(HidController::CaptureButton); MakeController<Controller_Stubbed>(HidController::InputDetector); MakeController<Controller_Stubbed>(HidController::UniquePad); - MakeController<Controller_NPad>(HidController::NPad); + MakeControllerWithServiceContext<Controller_NPad>(HidController::NPad); MakeController<Controller_Gesture>(HidController::Gesture); MakeController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor); @@ -191,13 +192,14 @@ private: std::shared_ptr<IAppletResource> Hid::GetAppletResource() { if (applet_resource == nullptr) { - applet_resource = std::make_shared<IAppletResource>(system); + applet_resource = std::make_shared<IAppletResource>(system, service_context); } return applet_resource; } -Hid::Hid(Core::System& system_) : ServiceFramework{system_, "hid"} { +Hid::Hid(Core::System& system_) + : ServiceFramework{system_, "hid"}, service_context{system_, service_name} { // clang-format off static const FunctionInfo functions[] = { {0, &Hid::CreateAppletResource, "CreateAppletResource"}, @@ -347,7 +349,7 @@ void Hid::CreateAppletResource(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); if (applet_resource == nullptr) { - applet_resource = std::make_shared<IAppletResource>(system); + applet_resource = std::make_shared<IAppletResource>(system, service_context); } IPC::ResponseBuilder rb{ctx, 2, 0, 1}; diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index 83fc2ea1d..9c5c7f252 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h @@ -7,6 +7,7 @@ #include <chrono> #include "core/hle/service/hid/controllers/controller_base.h" +#include "core/hle/service/kernel_helpers.h" #include "core/hle/service/service.h" namespace Core::Timing { @@ -39,7 +40,8 @@ enum class HidController : std::size_t { class IAppletResource final : public ServiceFramework<IAppletResource> { public: - explicit IAppletResource(Core::System& system_); + explicit IAppletResource(Core::System& system_, + KernelHelpers::ServiceContext& service_context_); ~IAppletResource() override; void ActivateController(HidController controller); @@ -60,11 +62,18 @@ private: void MakeController(HidController controller) { controllers[static_cast<std::size_t>(controller)] = std::make_unique<T>(system); } + template <typename T> + void MakeControllerWithServiceContext(HidController controller) { + controllers[static_cast<std::size_t>(controller)] = + std::make_unique<T>(system, service_context); + } void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx); void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); + KernelHelpers::ServiceContext& service_context; + std::shared_ptr<Core::Timing::EventType> pad_update_event; std::shared_ptr<Core::Timing::EventType> motion_update_event; @@ -176,6 +185,8 @@ private: static_assert(sizeof(VibrationDeviceInfo) == 0x8, "VibrationDeviceInfo has incorrect size."); std::shared_ptr<IAppletResource> applet_resource; + + KernelHelpers::ServiceContext service_context; }; /// Reload input devices. Used when input configuration changed diff --git a/src/core/hle/service/kernel_helpers.cpp b/src/core/hle/service/kernel_helpers.cpp new file mode 100644 index 000000000..62f4cdfb2 --- /dev/null +++ b/src/core/hle/service/kernel_helpers.cpp @@ -0,0 +1,62 @@ +// Copyright 2021 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/core.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/kernel/k_process.h" +#include "core/hle/kernel/k_readable_event.h" +#include "core/hle/kernel/k_resource_limit.h" +#include "core/hle/kernel/k_scoped_resource_reservation.h" +#include "core/hle/kernel/k_writable_event.h" +#include "core/hle/service/kernel_helpers.h" + +namespace Service::KernelHelpers { + +ServiceContext::ServiceContext(Core::System& system_, std::string name_) + : kernel(system_.Kernel()) { + process = Kernel::KProcess::Create(kernel); + ASSERT(Kernel::KProcess::Initialize(process, system_, std::move(name_), + Kernel::KProcess::ProcessType::Userland) + .IsSuccess()); +} + +ServiceContext::~ServiceContext() { + process->Close(); + process = nullptr; +} + +Kernel::KEvent* ServiceContext::CreateEvent(std::string&& name) { + // Reserve a new event from the process resource limit + Kernel::KScopedResourceReservation event_reservation(process, + Kernel::LimitableResource::Events); + if (!event_reservation.Succeeded()) { + LOG_CRITICAL(Service, "Resource limit reached!"); + return {}; + } + + // Create a new event. + auto* event = Kernel::KEvent::Create(kernel); + if (!event) { + LOG_CRITICAL(Service, "Unable to create event!"); + return {}; + } + + // Initialize the event. + event->Initialize(std::move(name)); + + // Commit the thread reservation. + event_reservation.Commit(); + + // Register the event. + Kernel::KEvent::Register(kernel, event); + + return event; +} + +void ServiceContext::CloseEvent(Kernel::KEvent* event) { + event->GetReadableEvent().Close(); + event->GetWritableEvent().Close(); +} + +} // namespace Service::KernelHelpers diff --git a/src/core/hle/service/kernel_helpers.h b/src/core/hle/service/kernel_helpers.h new file mode 100644 index 000000000..4f3e95f67 --- /dev/null +++ b/src/core/hle/service/kernel_helpers.h @@ -0,0 +1,35 @@ +// Copyright 2021 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <string> + +namespace Core { +class System; +} + +namespace Kernel { +class KernelCore; +class KEvent; +class KProcess; +} // namespace Kernel + +namespace Service::KernelHelpers { + +class ServiceContext { +public: + ServiceContext(Core::System& system_, std::string name_); + ~ServiceContext(); + + Kernel::KEvent* CreateEvent(std::string&& name); + + void CloseEvent(Kernel::KEvent* event); + +private: + Kernel::KernelCore& kernel; + Kernel::KProcess* process{}; +}; + +} // namespace Service::KernelHelpers diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp index 03992af5e..ff405099a 100644 --- a/src/core/hle/service/nvdrv/nvdrv.cpp +++ b/src/core/hle/service/nvdrv/nvdrv.cpp @@ -39,11 +39,11 @@ void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger nvflinger.SetNVDrvInstance(module_); } -Module::Module(Core::System& system) : syncpoint_manager{system.GPU()} { - auto& kernel = system.Kernel(); +Module::Module(Core::System& system) + : syncpoint_manager{system.GPU()}, service_context{system, "nvdrv"} { for (u32 i = 0; i < MaxNvEvents; i++) { - events_interface.events[i].event = Kernel::KEvent::Create(kernel); - events_interface.events[i].event->Initialize(fmt::format("NVDRV::NvEvent_{}", i)); + events_interface.events[i].event = + service_context.CreateEvent(fmt::format("NVDRV::NvEvent_{}", i)); events_interface.status[i] = EventState::Free; events_interface.registered[i] = false; } @@ -65,8 +65,7 @@ Module::Module(Core::System& system) : syncpoint_manager{system.GPU()} { Module::~Module() { for (u32 i = 0; i < MaxNvEvents; i++) { - events_interface.events[i].event->Close(); - events_interface.events[i].event = nullptr; + service_context.CloseEvent(events_interface.events[i].event); } } diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h index a43ceb7ae..e2a1dde5b 100644 --- a/src/core/hle/service/nvdrv/nvdrv.h +++ b/src/core/hle/service/nvdrv/nvdrv.h @@ -9,6 +9,7 @@ #include <vector> #include "common/common_types.h" +#include "core/hle/service/kernel_helpers.h" #include "core/hle/service/nvdrv/nvdata.h" #include "core/hle/service/nvdrv/syncpoint_manager.h" #include "core/hle/service/service.h" @@ -154,6 +155,8 @@ private: std::unordered_map<std::string, std::shared_ptr<Devices::nvdevice>> devices; EventInterface events_interface; + + KernelHelpers::ServiceContext service_context; }; /// Registers all NVDRV services with the specified service manager. diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index e6fba88b2..b3e50433b 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -104,23 +104,22 @@ ServiceFrameworkBase::~ServiceFrameworkBase() { void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager) { const auto guard = LockService(); - ASSERT(!port_installed); + ASSERT(!service_registered); - auto port = service_manager.RegisterService(service_name, max_sessions).Unwrap(); - port->SetSessionHandler(shared_from_this()); - port_installed = true; + service_manager.RegisterService(service_name, max_sessions, shared_from_this()); + service_registered = true; } Kernel::KClientPort& ServiceFrameworkBase::CreatePort() { const auto guard = LockService(); - ASSERT(!port_installed); + ASSERT(!service_registered); auto* port = Kernel::KPort::Create(kernel); port->Initialize(max_sessions, false, service_name); port->GetServerPort().SetSessionHandler(shared_from_this()); - port_installed = true; + service_registered = true; return port->GetClientPort(); } diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index e078ac176..c9d6b879d 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -96,6 +96,9 @@ protected: /// System context that the service operates under. Core::System& system; + /// Identifier string used to connect to the service. + std::string service_name; + private: template <typename T> friend class ServiceFramework; @@ -117,14 +120,12 @@ private: void RegisterHandlersBaseTipc(const FunctionInfoBase* functions, std::size_t n); void ReportUnimplementedFunction(Kernel::HLERequestContext& ctx, const FunctionInfoBase* info); - /// Identifier string used to connect to the service. - std::string service_name; /// Maximum number of concurrent sessions that this service can handle. u32 max_sessions; /// Flag to store if a port was already create/installed to detect multiple install attempts, /// which is not supported. - bool port_installed = false; + bool service_registered = false; /// Function used to safely up-cast pointers to the derived class before invoking a handler. InvokerFn* handler_invoker; diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp index 15034abed..ae4dc4a75 100644 --- a/src/core/hle/service/sm/sm.cpp +++ b/src/core/hle/service/sm/sm.cpp @@ -4,6 +4,7 @@ #include <tuple> #include "common/assert.h" +#include "common/scope_exit.h" #include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/k_client_port.h" @@ -40,17 +41,13 @@ static ResultCode ValidateServiceName(const std::string& name) { } Kernel::KClientPort& ServiceManager::InterfaceFactory(ServiceManager& self, Core::System& system) { - ASSERT(self.sm_interface.expired()); - - auto sm = std::make_shared<SM>(self, system); - self.sm_interface = sm; + self.sm_interface = std::make_shared<SM>(self, system); self.controller_interface = std::make_unique<Controller>(system); - - return sm->CreatePort(); + return self.sm_interface->CreatePort(); } -ResultVal<Kernel::KServerPort*> ServiceManager::RegisterService(std::string name, - u32 max_sessions) { +ResultCode ServiceManager::RegisterService(std::string name, u32 max_sessions, + Kernel::SessionRequestHandlerPtr handler) { CASCADE_CODE(ValidateServiceName(name)); @@ -59,12 +56,9 @@ ResultVal<Kernel::KServerPort*> ServiceManager::RegisterService(std::string name return ERR_ALREADY_REGISTERED; } - auto* port = Kernel::KPort::Create(kernel); - port->Initialize(max_sessions, false, name); + registered_services.emplace(std::move(name), handler); - registered_services.emplace(std::move(name), port); - - return MakeResult(&port->GetServerPort()); + return ResultSuccess; } ResultCode ServiceManager::UnregisterService(const std::string& name) { @@ -76,14 +70,11 @@ ResultCode ServiceManager::UnregisterService(const std::string& name) { return ERR_SERVICE_NOT_REGISTERED; } - iter->second->Close(); - registered_services.erase(iter); return ResultSuccess; } ResultVal<Kernel::KPort*> ServiceManager::GetServicePort(const std::string& name) { - CASCADE_CODE(ValidateServiceName(name)); auto it = registered_services.find(name); if (it == registered_services.end()) { @@ -91,10 +82,13 @@ ResultVal<Kernel::KPort*> ServiceManager::GetServicePort(const std::string& name return ERR_SERVICE_NOT_REGISTERED; } - return MakeResult(it->second); -} + auto* port = Kernel::KPort::Create(kernel); + port->Initialize(ServerSessionCountMax, false, name); + auto handler = it->second; + port->GetServerPort().SetSessionHandler(std::move(handler)); -SM::~SM() = default; + return MakeResult(port); +} /** * SM::Initialize service function @@ -156,11 +150,15 @@ ResultVal<Kernel::KClientSession*> SM::GetServiceImpl(Kernel::HLERequestContext& LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, port_result.Code().raw); return port_result.Code(); } - auto& port = port_result.Unwrap()->GetClientPort(); + auto& port = port_result.Unwrap(); + SCOPE_EXIT({ port->GetClientPort().Close(); }); + + server_ports.emplace_back(&port->GetServerPort()); // Create a new session. Kernel::KClientSession* session{}; - if (const auto result = port.CreateSession(std::addressof(session)); result.IsError()) { + if (const auto result = port->GetClientPort().CreateSession(std::addressof(session)); + result.IsError()) { LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, result.raw); return result; } @@ -180,20 +178,21 @@ void SM::RegisterService(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_SM, "called with name={}, max_session_count={}, is_light={}", name, max_session_count, is_light); - auto handle = service_manager.RegisterService(name, max_session_count); - if (handle.Failed()) { - LOG_ERROR(Service_SM, "failed to register service with error_code={:08X}", - handle.Code().raw); + if (const auto result = service_manager.RegisterService(name, max_session_count, nullptr); + result.IsError()) { + LOG_ERROR(Service_SM, "failed to register service with error_code={:08X}", result.raw); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(handle.Code()); + rb.Push(result); return; } - IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles}; - rb.Push(handle.Code()); + auto* port = Kernel::KPort::Create(kernel); + port->Initialize(ServerSessionCountMax, is_light, name); + SCOPE_EXIT({ port->GetClientPort().Close(); }); - auto server_port = handle.Unwrap(); - rb.PushMoveObjects(server_port); + IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles}; + rb.Push(ResultSuccess); + rb.PushMoveObjects(port->GetServerPort()); } void SM::UnregisterService(Kernel::HLERequestContext& ctx) { @@ -225,4 +224,10 @@ SM::SM(ServiceManager& service_manager_, Core::System& system_) }); } +SM::~SM() { + for (auto& server_port : server_ports) { + server_port->Close(); + } +} + } // namespace Service::SM diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h index ea37f11d4..068c78588 100644 --- a/src/core/hle/service/sm/sm.h +++ b/src/core/hle/service/sm/sm.h @@ -49,6 +49,7 @@ private: ServiceManager& service_manager; bool is_initialized{}; Kernel::KernelCore& kernel; + std::vector<Kernel::KServerPort*> server_ports; }; class ServiceManager { @@ -58,7 +59,8 @@ public: explicit ServiceManager(Kernel::KernelCore& kernel_); ~ServiceManager(); - ResultVal<Kernel::KServerPort*> RegisterService(std::string name, u32 max_sessions); + ResultCode RegisterService(std::string name, u32 max_sessions, + Kernel::SessionRequestHandlerPtr handler); ResultCode UnregisterService(const std::string& name); ResultVal<Kernel::KPort*> GetServicePort(const std::string& name); @@ -69,21 +71,17 @@ public: LOG_DEBUG(Service, "Can't find service: {}", service_name); return nullptr; } - auto* port = service->second; - if (port == nullptr) { - return nullptr; - } - return std::static_pointer_cast<T>(port->GetServerPort().GetSessionRequestHandler()); + return std::static_pointer_cast<T>(service->second); } void InvokeControlRequest(Kernel::HLERequestContext& context); private: - std::weak_ptr<SM> sm_interface; + std::shared_ptr<SM> sm_interface; std::unique_ptr<Controller> controller_interface; /// Map of registered services, retrieved using GetServicePort. - std::unordered_map<std::string, Kernel::KPort*> registered_services; + std::unordered_map<std::string, Kernel::SessionRequestHandlerPtr> registered_services; /// Kernel context Kernel::KernelCore& kernel; diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp index 1656b85fb..70a0ba09c 100644 --- a/src/input_common/sdl/sdl_impl.cpp +++ b/src/input_common/sdl/sdl_impl.cpp @@ -115,6 +115,41 @@ public: return state.buttons.at(button); } + bool ToggleButton(int button) { + std::lock_guard lock{mutex}; + + if (!state.toggle_buttons.contains(button) || !state.lock_buttons.contains(button)) { + state.toggle_buttons.insert_or_assign(button, false); + state.lock_buttons.insert_or_assign(button, false); + } + + const bool button_state = state.toggle_buttons.at(button); + const bool button_lock = state.lock_buttons.at(button); + + if (button_lock) { + return button_state; + } + + state.lock_buttons.insert_or_assign(button, true); + + if (button_state) { + state.toggle_buttons.insert_or_assign(button, false); + } else { + state.toggle_buttons.insert_or_assign(button, true); + } + + return !button_state; + } + + bool UnlockButton(int button) { + std::lock_guard lock{mutex}; + if (!state.toggle_buttons.contains(button)) { + return false; + } + state.lock_buttons.insert_or_assign(button, false); + return state.toggle_buttons.at(button); + } + void SetAxis(int axis, Sint16 value) { std::lock_guard lock{mutex}; state.axes.insert_or_assign(axis, value); @@ -241,6 +276,8 @@ public: private: struct State { std::unordered_map<int, bool> buttons; + std::unordered_map<int, bool> toggle_buttons{}; + std::unordered_map<int, bool> lock_buttons{}; std::unordered_map<int, Sint16> axes; std::unordered_map<int, Uint8> hats; } state; @@ -402,16 +439,25 @@ void SDLState::CloseJoysticks() { class SDLButton final : public Input::ButtonDevice { public: - explicit SDLButton(std::shared_ptr<SDLJoystick> joystick_, int button_) - : joystick(std::move(joystick_)), button(button_) {} + explicit SDLButton(std::shared_ptr<SDLJoystick> joystick_, int button_, bool toggle_) + : joystick(std::move(joystick_)), button(button_), toggle(toggle_) {} bool GetStatus() const override { - return joystick->GetButton(button); + const bool button_state = joystick->GetButton(button); + if (!toggle) { + return button_state; + } + + if (button_state) { + return joystick->ToggleButton(button); + } + return joystick->UnlockButton(button); } private: std::shared_ptr<SDLJoystick> joystick; int button; + bool toggle; }; class SDLDirectionButton final : public Input::ButtonDevice { @@ -635,6 +681,7 @@ public: std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override { const std::string guid = params.Get("guid", "0"); const int port = params.Get("port", 0); + const auto toggle = params.Get("toggle", false); auto joystick = state.GetSDLJoystickByGUID(guid, port); @@ -660,7 +707,8 @@ public: if (params.Has("axis")) { const int axis = params.Get("axis", 0); - const float threshold = params.Get("threshold", 0.5f); + // Convert range from (0.0, 1.0) to (-1.0, 1.0) + const float threshold = (params.Get("threshold", 0.5f) - 0.5f) * 2.0f; const std::string direction_name = params.Get("direction", ""); bool trigger_if_greater; if (direction_name == "+") { @@ -679,7 +727,7 @@ public: const int button = params.Get("button", 0); // This is necessary so accessing GetButton with button won't crash joystick->SetButton(button, false); - return std::make_unique<SDLButton>(joystick, button); + return std::make_unique<SDLButton>(joystick, button, toggle); } private: @@ -933,12 +981,11 @@ Common::ParamPackage BuildAnalogParamPackageForButton(int port, std::string guid params.Set("port", port); params.Set("guid", std::move(guid)); params.Set("axis", axis); + params.Set("threshold", "0.5"); if (value > 0) { params.Set("direction", "+"); - params.Set("threshold", "0.5"); } else { params.Set("direction", "-"); - params.Set("threshold", "-0.5"); } return params; } diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index fd01c902c..88ccf96f5 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -765,7 +765,7 @@ void TextureCacheRuntime::CopyImage(Image& dst, Image& src, dst_range.AddLayers(copy.dstSubresource); src_range.AddLayers(copy.srcSubresource); } - const std::array read_barriers{ + const std::array pre_barriers{ VkImageMemoryBarrier{ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, @@ -774,7 +774,7 @@ void TextureCacheRuntime::CopyImage(Image& dst, Image& src, VK_ACCESS_TRANSFER_WRITE_BIT, .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT, .oldLayout = VK_IMAGE_LAYOUT_GENERAL, - .newLayout = VK_IMAGE_LAYOUT_GENERAL, + .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .image = src_image, @@ -795,29 +795,43 @@ void TextureCacheRuntime::CopyImage(Image& dst, Image& src, .subresourceRange = dst_range.SubresourceRange(aspect_mask), }, }; - const VkImageMemoryBarrier write_barrier{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .pNext = nullptr, - .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, - .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT | - VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | - VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | - VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | - VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | - VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, - .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - .newLayout = VK_IMAGE_LAYOUT_GENERAL, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = dst_image, - .subresourceRange = dst_range.SubresourceRange(aspect_mask), + const std::array post_barriers{ + VkImageMemoryBarrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = 0, + .dstAccessMask = 0, + .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + .newLayout = VK_IMAGE_LAYOUT_GENERAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = src_image, + .subresourceRange = src_range.SubresourceRange(aspect_mask), + }, + VkImageMemoryBarrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT | + VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | + VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, + .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + .newLayout = VK_IMAGE_LAYOUT_GENERAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = dst_image, + .subresourceRange = dst_range.SubresourceRange(aspect_mask), + }, }; cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, - 0, {}, {}, read_barriers); - cmdbuf.CopyImage(src_image, VK_IMAGE_LAYOUT_GENERAL, dst_image, + 0, {}, {}, pre_barriers); + cmdbuf.CopyImage(src_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, vk_copies); cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, - 0, write_barrier); + 0, {}, {}, post_barriers); }); } diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index d5d624b96..6b9bd05f1 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -149,8 +149,9 @@ QString ButtonToText(const Common::ParamPackage& param) { if (param.Has("button")) { const QString button_str = QString::fromStdString(param.Get("button", "")); + const QString toggle = QString::fromStdString(param.Get("toggle", false) ? "~" : ""); - return QObject::tr("Button %1").arg(button_str); + return QObject::tr("%1Button %2").arg(toggle, button_str); } if (param.Has("motion")) { @@ -313,6 +314,24 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i buttons_param[button_id].Set("toggle", toggle_value); button_map[button_id]->setText(ButtonToText(buttons_param[button_id])); }); + if (buttons_param[button_id].Has("threshold")) { + context_menu.addAction(tr("Set threshold"), [&] { + const int button_threshold = static_cast<int>( + buttons_param[button_id].Get("threshold", 0.5f) * 100.0f); + const int new_threshold = QInputDialog::getInt( + this, tr("Set threshold"), tr("Choose a value between 0% and 100%"), + button_threshold, 0, 100); + buttons_param[button_id].Set("threshold", new_threshold / 100.0f); + + if (button_id == Settings::NativeButton::ZL) { + ui->sliderZLThreshold->setValue(new_threshold); + } + if (button_id == Settings::NativeButton::ZR) { + ui->sliderZRThreshold->setValue(new_threshold); + } + }); + } + context_menu.exec(button_map[button_id]->mapToGlobal(menu_location)); ui->controllerFrame->SetPlayerInput(player_index, buttons_param, analogs_param); }); @@ -341,6 +360,20 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i }); } + connect(ui->sliderZLThreshold, &QSlider::valueChanged, [=, this] { + if (buttons_param[Settings::NativeButton::ZL].Has("threshold")) { + const auto slider_value = ui->sliderZLThreshold->value(); + buttons_param[Settings::NativeButton::ZL].Set("threshold", slider_value / 100.0f); + } + }); + + connect(ui->sliderZRThreshold, &QSlider::valueChanged, [=, this] { + if (buttons_param[Settings::NativeButton::ZR].Has("threshold")) { + const auto slider_value = ui->sliderZRThreshold->value(); + buttons_param[Settings::NativeButton::ZR].Set("threshold", slider_value / 100.0f); + } + }); + for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) { for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) { auto* const analog_button = analog_map_buttons[analog_id][sub_button_id]; @@ -849,6 +882,18 @@ void ConfigureInputPlayer::UpdateUI() { button_map[button]->setText(ButtonToText(buttons_param[button])); } + if (buttons_param[Settings::NativeButton::ZL].Has("threshold")) { + const int button_threshold = static_cast<int>( + buttons_param[Settings::NativeButton::ZL].Get("threshold", 0.5f) * 100.0f); + ui->sliderZLThreshold->setValue(button_threshold); + } + + if (buttons_param[Settings::NativeButton::ZR].Has("threshold")) { + const int button_threshold = static_cast<int>( + buttons_param[Settings::NativeButton::ZR].Get("threshold", 0.5f) * 100.0f); + ui->sliderZRThreshold->setValue(button_threshold); + } + for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) { motion_map[motion_id]->setText(ButtonToText(motions_param[motion_id])); } diff --git a/src/yuzu/configuration/configure_input_player.ui b/src/yuzu/configuration/configure_input_player.ui index e76aa484f..e7433912b 100644 --- a/src/yuzu/configuration/configure_input_player.ui +++ b/src/yuzu/configuration/configure_input_player.ui @@ -1334,6 +1334,12 @@ </item> <item> <widget class="QGroupBox" name="buttonShoulderButtonsButtonZLGroup"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> <property name="title"> <string>ZL</string> </property> @@ -1378,6 +1384,22 @@ </property> </widget> </item> + <item> + <widget class="QSlider" name="sliderZLThreshold"> + <property name="maximumSize"> + <size> + <width>70</width> + <height>15</height> + </size> + </property> + <property name="maximum"> + <number>100</number> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> </layout> </widget> </item> @@ -1759,6 +1781,12 @@ </item> <item> <widget class="QGroupBox" name="buttonShoulderButtonsZRGroup"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> <property name="title"> <string>ZR</string> </property> @@ -1803,6 +1831,22 @@ </property> </widget> </item> + <item> + <widget class="QSlider" name="sliderZRThreshold"> + <property name="maximumSize"> + <size> + <width>70</width> + <height>15</height> + </size> + </property> + <property name="maximum"> + <number>100</number> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> </layout> </widget> </item> diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index b18056baf..3e22fee37 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp @@ -446,6 +446,7 @@ void Config::ReadValues() { ReadSetting("Renderer", Settings::values.renderer_debug); ReadSetting("Renderer", Settings::values.vulkan_device); + ReadSetting("Renderer", Settings::values.fullscreen_mode); ReadSetting("Renderer", Settings::values.aspect_ratio); ReadSetting("Renderer", Settings::values.max_anisotropy); ReadSetting("Renderer", Settings::values.use_frame_limit); diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index b362f10b4..88d33ecab 100644 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h @@ -224,6 +224,10 @@ debug = # Which Vulkan physical device to use (defaults to 0) vulkan_device = +# Whether to use fullscreen or borderless window mode +# 0 (Windows default): Borderless window, 1 (All other default): Exclusive fullscreen +fullscreen_mode = + # Aspect ratio # 0: Default (16:9), 1: Force 4:3, 2: Force 21:9, 3: Stretch to Window aspect_ratio = diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp index 896181f0b..353e51ea7 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp @@ -6,6 +6,7 @@ #include "common/logging/log.h" #include "common/scm_rev.h" +#include "common/settings.h" #include "core/core.h" #include "core/perf_stats.h" #include "input_common/keyboard.h" @@ -122,24 +123,37 @@ void EmuWindow_SDL2::OnResize() { } void EmuWindow_SDL2::Fullscreen() { - if (SDL_SetWindowFullscreen(render_window, SDL_WINDOW_FULLSCREEN) == 0) { - return; - } - - LOG_ERROR(Frontend, "Fullscreening failed: {}", SDL_GetError()); + switch (Settings::values.fullscreen_mode.GetValue()) { + case 1: // Exclusive fullscreen + // Set window size to render size before entering fullscreen -- SDL does not resize to + // display dimensions in this mode. + // TODO: Multiply the window size by resolution_factor (for both docked modes) + if (Settings::values.use_docked_mode) { + SDL_SetWindowSize(render_window, Layout::ScreenDocked::Width, + Layout::ScreenDocked::Height); + } - // Try a different fullscreening method - LOG_INFO(Frontend, "Attempting to use borderless fullscreen..."); - if (SDL_SetWindowFullscreen(render_window, SDL_WINDOW_FULLSCREEN_DESKTOP) == 0) { - return; - } + if (SDL_SetWindowFullscreen(render_window, SDL_WINDOW_FULLSCREEN) == 0) { + return; + } - LOG_ERROR(Frontend, "Borderless fullscreening failed: {}", SDL_GetError()); + LOG_ERROR(Frontend, "Fullscreening failed: {}", SDL_GetError()); + LOG_INFO(Frontend, "Attempting to use borderless fullscreen..."); + [[fallthrough]]; + case 0: // Borderless window + if (SDL_SetWindowFullscreen(render_window, SDL_WINDOW_FULLSCREEN_DESKTOP) == 0) { + return; + } - // Fallback algorithm: Maximise window. - // Works on all systems (unless something is seriously wrong), so no fallback for this one. - LOG_INFO(Frontend, "Falling back on a maximised window..."); - SDL_MaximizeWindow(render_window); + LOG_ERROR(Frontend, "Borderless fullscreening failed: {}", SDL_GetError()); + [[fallthrough]]; + default: + // Fallback algorithm: Maximise window. + // Works on all systems (unless something is seriously wrong), so no fallback for this one. + LOG_INFO(Frontend, "Falling back on a maximised window..."); + SDL_MaximizeWindow(render_window); + break; + } } void EmuWindow_SDL2::WaitEvent() { 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 152e56db8..d1473dbab 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp @@ -24,7 +24,7 @@ #include <SDL.h> #include <SDL_syswm.h> -EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem) +EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem, bool fullscreen) : EmuWindow_SDL2{input_subsystem} { const std::string window_title = fmt::format("yuzu {} | {}-{} (Vulkan)", Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc); @@ -42,6 +42,10 @@ EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsyste SetWindowIcon(); + if (fullscreen) { + Fullscreen(); + } + switch (wm.subsystem) { #ifdef SDL_VIDEO_DRIVER_WINDOWS case SDL_SYSWM_TYPE::SDL_SYSWM_WINDOWS: 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 bdfdc3c6f..de53844f0 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h @@ -19,7 +19,7 @@ class InputSubsystem; class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 { public: - explicit EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem); + explicit EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem, bool fullscreen); ~EmuWindow_SDL2_VK() override; std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index 9607cdcb1..ac4ea88d3 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp @@ -175,7 +175,7 @@ int main(int argc, char** argv) { emu_window = std::make_unique<EmuWindow_SDL2_GL>(&input_subsystem, fullscreen); break; case Settings::RendererBackend::Vulkan: - emu_window = std::make_unique<EmuWindow_SDL2_VK>(&input_subsystem); + emu_window = std::make_unique<EmuWindow_SDL2_VK>(&input_subsystem, fullscreen); break; } |