diff options
37 files changed, 692 insertions, 325 deletions
| diff --git a/.ci/scripts/clang/docker.sh b/.ci/scripts/clang/docker.sh index 7d3ae4a1a..51769545e 100755 --- a/.ci/scripts/clang/docker.sh +++ b/.ci/scripts/clang/docker.sh @@ -11,6 +11,7 @@ ccache -s  mkdir build || true && cd build  cmake .. \        -DCMAKE_BUILD_TYPE=Release \ +      -DCMAKE_CXX_FLAGS="-march=x86-64-v2" \        -DCMAKE_CXX_COMPILER=/usr/lib/ccache/clang++ \        -DCMAKE_C_COMPILER=/usr/lib/ccache/clang \        -DCMAKE_INSTALL_PREFIX="/usr" \ diff --git a/.ci/scripts/linux/docker.sh b/.ci/scripts/linux/docker.sh index 35c4a4368..c8bc56c9a 100755 --- a/.ci/scripts/linux/docker.sh +++ b/.ci/scripts/linux/docker.sh @@ -12,6 +12,7 @@ mkdir build || true && cd build  cmake .. \        -DBoost_USE_STATIC_LIBS=ON \        -DCMAKE_BUILD_TYPE=Release \ +      -DCMAKE_CXX_FLAGS="-march=x86-64-v2" \        -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ \        -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc \        -DCMAKE_INSTALL_PREFIX="/usr" \ diff --git a/.ci/templates/build-msvc.yml b/.ci/templates/build-msvc.yml index ea405e5dc..c379dd757 100644 --- a/.ci/templates/build-msvc.yml +++ b/.ci/templates/build-msvc.yml @@ -9,7 +9,7 @@ parameters:  steps:  - script: choco install vulkan-sdk    displayName: 'Install vulkan-sdk' -- script: refreshenv && mkdir build && cd build && cmake -G "Visual Studio 17 2022" -A x64 -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DYUZU_TESTS=OFF -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON -DDISPLAY_VERSION=${{ parameters['version'] }} -DCMAKE_BUILD_TYPE=Release -DYUZU_CRASH_DUMPS=ON .. && cd .. +- script: refreshenv && mkdir build && cd build && cmake -E env CXXFLAGS="/Gw /GA /Gr /Ob2" cmake -G "Visual Studio 17 2022" -A x64 -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON -DCMAKE_POLICY_DEFAULT_CMP0069=NEW -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DYUZU_TESTS=OFF -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON -DDISPLAY_VERSION=${{ parameters['version'] }} -DCMAKE_BUILD_TYPE=Release -DYUZU_CRASH_DUMPS=ON .. && cd ..    displayName: 'Configure CMake'  - task: MSBuild@1    displayName: 'Build' diff --git a/CMakeLists.txt b/CMakeLists.txt index 47eddf99e..f71a8b3e3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -208,7 +208,7 @@ find_package(libusb 1.0.24)  find_package(lz4 REQUIRED)  find_package(nlohmann_json 3.8 REQUIRED)  find_package(Opus 1.3) -find_package(Vulkan 1.3.213) +find_package(Vulkan 1.3.238)  find_package(ZLIB 1.2 REQUIRED)  find_package(zstd 1.5 REQUIRED) diff --git a/externals/Vulkan-Headers b/externals/Vulkan-Headers -Subproject 2826791bed6a793f164bf534cd859968f13df8a +Subproject 00671c64ba5c488ade22ad572a0ef81d5e64c80 diff --git a/src/common/assert.h b/src/common/assert.h index 8c927fcc0..67e7e9375 100644 --- a/src/common/assert.h +++ b/src/common/assert.h @@ -69,7 +69,7 @@ void assert_fail_impl();  #define ASSERT_OR_EXECUTE(_a_, _b_)                                                                \      do {                                                                                           \          ASSERT(_a_);                                                                               \ -        if (!(_a_)) {                                                                              \ +        if (!(_a_)) [[unlikely]] {                                                                 \              _b_                                                                                    \          }                                                                                          \      } while (0) @@ -78,7 +78,7 @@ void assert_fail_impl();  #define ASSERT_OR_EXECUTE_MSG(_a_, _b_, ...)                                                       \      do {                                                                                           \          ASSERT_MSG(_a_, __VA_ARGS__);                                                              \ -        if (!(_a_)) {                                                                              \ +        if (!(_a_)) [[unlikely]] {                                                                 \              _b_                                                                                    \          }                                                                                          \      } while (0) diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index c6b5ac196..0252c8c31 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -201,6 +201,9 @@ add_library(core STATIC      hle/kernel/k_event_info.h      hle/kernel/k_handle_table.cpp      hle/kernel/k_handle_table.h +    hle/kernel/k_hardware_timer_base.h +    hle/kernel/k_hardware_timer.cpp +    hle/kernel/k_hardware_timer.h      hle/kernel/k_interrupt_manager.cpp      hle/kernel/k_interrupt_manager.h      hle/kernel/k_light_condition_variable.cpp @@ -268,6 +271,7 @@ add_library(core STATIC      hle/kernel/k_thread_local_page.h      hle/kernel/k_thread_queue.cpp      hle/kernel/k_thread_queue.h +    hle/kernel/k_timer_task.h      hle/kernel/k_trace.h      hle/kernel/k_transfer_memory.cpp      hle/kernel/k_transfer_memory.h @@ -290,8 +294,6 @@ add_library(core STATIC      hle/kernel/svc_common.h      hle/kernel/svc_types.h      hle/kernel/svc_wrap.h -    hle/kernel/time_manager.cpp -    hle/kernel/time_manager.h      hle/result.h      hle/service/acc/acc.cpp      hle/service/acc/acc.h diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp index 67969e938..f238d6ccd 100644 --- a/src/core/hid/emulated_controller.cpp +++ b/src/core/hid/emulated_controller.cpp @@ -145,6 +145,7 @@ void EmulatedController::LoadDevices() {      output_params[3].Set("output", true);      LoadTASParams(); +    LoadVirtualGamepadParams();      std::ranges::transform(button_params, button_devices.begin(), Common::Input::CreateInputDevice);      std::ranges::transform(stick_params, stick_devices.begin(), Common::Input::CreateInputDevice); @@ -163,6 +164,12 @@ void EmulatedController::LoadDevices() {                             Common::Input::CreateInputDevice);      std::ranges::transform(tas_stick_params, tas_stick_devices.begin(),                             Common::Input::CreateInputDevice); + +    // Initialize virtual gamepad devices +    std::ranges::transform(virtual_button_params, virtual_button_devices.begin(), +                           Common::Input::CreateInputDevice); +    std::ranges::transform(virtual_stick_params, virtual_stick_devices.begin(), +                           Common::Input::CreateInputDevice);  }  void EmulatedController::LoadTASParams() { @@ -205,6 +212,46 @@ void EmulatedController::LoadTASParams() {      tas_stick_params[Settings::NativeAnalog::RStick].Set("axis_y", 3);  } +void EmulatedController::LoadVirtualGamepadParams() { +    const auto player_index = NpadIdTypeToIndex(npad_id_type); +    Common::ParamPackage common_params{}; +    common_params.Set("engine", "virtual_gamepad"); +    common_params.Set("port", static_cast<int>(player_index)); +    for (auto& param : virtual_button_params) { +        param = common_params; +    } +    for (auto& param : virtual_stick_params) { +        param = common_params; +    } + +    // TODO(german77): Replace this with an input profile or something better +    virtual_button_params[Settings::NativeButton::A].Set("button", 0); +    virtual_button_params[Settings::NativeButton::B].Set("button", 1); +    virtual_button_params[Settings::NativeButton::X].Set("button", 2); +    virtual_button_params[Settings::NativeButton::Y].Set("button", 3); +    virtual_button_params[Settings::NativeButton::LStick].Set("button", 4); +    virtual_button_params[Settings::NativeButton::RStick].Set("button", 5); +    virtual_button_params[Settings::NativeButton::L].Set("button", 6); +    virtual_button_params[Settings::NativeButton::R].Set("button", 7); +    virtual_button_params[Settings::NativeButton::ZL].Set("button", 8); +    virtual_button_params[Settings::NativeButton::ZR].Set("button", 9); +    virtual_button_params[Settings::NativeButton::Plus].Set("button", 10); +    virtual_button_params[Settings::NativeButton::Minus].Set("button", 11); +    virtual_button_params[Settings::NativeButton::DLeft].Set("button", 12); +    virtual_button_params[Settings::NativeButton::DUp].Set("button", 13); +    virtual_button_params[Settings::NativeButton::DRight].Set("button", 14); +    virtual_button_params[Settings::NativeButton::DDown].Set("button", 15); +    virtual_button_params[Settings::NativeButton::SL].Set("button", 16); +    virtual_button_params[Settings::NativeButton::SR].Set("button", 17); +    virtual_button_params[Settings::NativeButton::Home].Set("button", 18); +    virtual_button_params[Settings::NativeButton::Screenshot].Set("button", 19); + +    virtual_stick_params[Settings::NativeAnalog::LStick].Set("axis_x", 0); +    virtual_stick_params[Settings::NativeAnalog::LStick].Set("axis_y", 1); +    virtual_stick_params[Settings::NativeAnalog::RStick].Set("axis_x", 2); +    virtual_stick_params[Settings::NativeAnalog::RStick].Set("axis_y", 3); +} +  void EmulatedController::ReloadInput() {      // If you load any device here add the equivalent to the UnloadInput() function      LoadDevices(); @@ -322,6 +369,35 @@ void EmulatedController::ReloadInput() {                  },          });      } + +    // Use a common UUID for Virtual Gamepad +    static constexpr Common::UUID VIRTUAL_UUID = Common::UUID{ +        {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; + +    // Register virtual devices. No need to force update +    for (std::size_t index = 0; index < virtual_button_devices.size(); ++index) { +        if (!virtual_button_devices[index]) { +            continue; +        } +        virtual_button_devices[index]->SetCallback({ +            .on_change = +                [this, index](const Common::Input::CallbackStatus& callback) { +                    SetButton(callback, index, VIRTUAL_UUID); +                }, +        }); +    } + +    for (std::size_t index = 0; index < virtual_stick_devices.size(); ++index) { +        if (!virtual_stick_devices[index]) { +            continue; +        } +        virtual_stick_devices[index]->SetCallback({ +            .on_change = +                [this, index](const Common::Input::CallbackStatus& callback) { +                    SetStick(callback, index, VIRTUAL_UUID); +                }, +        }); +    }  }  void EmulatedController::UnloadInput() { @@ -349,6 +425,12 @@ void EmulatedController::UnloadInput() {      for (auto& stick : tas_stick_devices) {          stick.reset();      } +    for (auto& button : virtual_button_devices) { +        button.reset(); +    } +    for (auto& stick : virtual_stick_devices) { +        stick.reset(); +    }      camera_devices.reset();      nfc_devices.reset();  } diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h index fa7a34278..a398543a6 100644 --- a/src/core/hid/emulated_controller.h +++ b/src/core/hid/emulated_controller.h @@ -385,6 +385,9 @@ private:      /// Set the params for TAS devices      void LoadTASParams(); +    /// Set the params for virtual pad devices +    void LoadVirtualGamepadParams(); +      /**       * @param use_temporary_value If true tmp_npad_type will be used       * @return true if the controller style is fullkey @@ -500,6 +503,12 @@ private:      ButtonDevices tas_button_devices;      StickDevices tas_stick_devices; +    // Virtual gamepad related variables +    ButtonParams virtual_button_params; +    StickParams virtual_stick_params; +    ButtonDevices virtual_button_devices; +    StickDevices virtual_stick_devices; +      mutable std::mutex mutex;      mutable std::mutex callback_mutex;      std::unordered_map<int, ControllerUpdateCallback> callback_list; diff --git a/src/core/hle/kernel/k_address_arbiter.cpp b/src/core/hle/kernel/k_address_arbiter.cpp index f85b11557..a442a3b98 100644 --- a/src/core/hle/kernel/k_address_arbiter.cpp +++ b/src/core/hle/kernel/k_address_arbiter.cpp @@ -10,7 +10,6 @@  #include "core/hle/kernel/k_thread_queue.h"  #include "core/hle/kernel/kernel.h"  #include "core/hle/kernel/svc_results.h" -#include "core/hle/kernel/time_manager.h"  #include "core/memory.h"  namespace Kernel { diff --git a/src/core/hle/kernel/k_hardware_timer.cpp b/src/core/hle/kernel/k_hardware_timer.cpp new file mode 100644 index 000000000..6bba79ea0 --- /dev/null +++ b/src/core/hle/kernel/k_hardware_timer.cpp @@ -0,0 +1,74 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/core_timing.h" +#include "core/hle/kernel/k_hardware_timer.h" +#include "core/hle/kernel/k_scheduler.h" + +namespace Kernel { + +void KHardwareTimer::Initialize() { +    // Create the timing callback to register with CoreTiming. +    m_event_type = Core::Timing::CreateEvent( +        "KHardwareTimer::Callback", [](std::uintptr_t timer_handle, s64, std::chrono::nanoseconds) { +            reinterpret_cast<KHardwareTimer*>(timer_handle)->DoTask(); +            return std::nullopt; +        }); +} + +void KHardwareTimer::Finalize() { +    this->DisableInterrupt(); +    m_event_type.reset(); +} + +void KHardwareTimer::DoTask() { +    // Handle the interrupt. +    { +        KScopedSchedulerLock slk{m_kernel}; +        KScopedSpinLock lk(this->GetLock()); + +        //! Ignore this event if needed. +        if (!this->GetInterruptEnabled()) { +            return; +        } + +        // Disable the timer interrupt while we handle this. +        this->DisableInterrupt(); + +        if (const s64 next_time = this->DoInterruptTaskImpl(GetTick()); +            0 < next_time && next_time <= m_wakeup_time) { +            // We have a next time, so we should set the time to interrupt and turn the interrupt +            // on. +            this->EnableInterrupt(next_time); +        } +    } + +    // Clear the timer interrupt. +    // Kernel::GetInterruptManager().ClearInterrupt(KInterruptName_NonSecurePhysicalTimer, +    //                                              GetCurrentCoreId()); +} + +void KHardwareTimer::EnableInterrupt(s64 wakeup_time) { +    this->DisableInterrupt(); + +    m_wakeup_time = wakeup_time; +    m_kernel.System().CoreTiming().ScheduleEvent(std::chrono::nanoseconds{m_wakeup_time}, +                                                 m_event_type, reinterpret_cast<uintptr_t>(this), +                                                 true); +} + +void KHardwareTimer::DisableInterrupt() { +    m_kernel.System().CoreTiming().UnscheduleEvent(m_event_type, reinterpret_cast<uintptr_t>(this)); +    m_wakeup_time = std::numeric_limits<s64>::max(); +} + +s64 KHardwareTimer::GetTick() const { +    return m_kernel.System().CoreTiming().GetGlobalTimeNs().count(); +} + +bool KHardwareTimer::GetInterruptEnabled() { +    return m_wakeup_time != std::numeric_limits<s64>::max(); +} + +} // namespace Kernel diff --git a/src/core/hle/kernel/k_hardware_timer.h b/src/core/hle/kernel/k_hardware_timer.h new file mode 100644 index 000000000..00bef6ea1 --- /dev/null +++ b/src/core/hle/kernel/k_hardware_timer.h @@ -0,0 +1,54 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/kernel/k_hardware_timer_base.h" + +namespace Core::Timing { +struct EventType; +} // namespace Core::Timing + +namespace Kernel { + +class KHardwareTimer : /* public KInterruptTask, */ public KHardwareTimerBase { +public: +    explicit KHardwareTimer(KernelCore& kernel) : KHardwareTimerBase{kernel} {} + +    // Public API. +    void Initialize(); +    void Finalize(); + +    s64 GetCount() const { +        return GetTick(); +    } + +    void RegisterTask(KTimerTask* task, s64 time_from_now) { +        this->RegisterAbsoluteTask(task, GetTick() + time_from_now); +    } + +    void RegisterAbsoluteTask(KTimerTask* task, s64 task_time) { +        KScopedDisableDispatch dd{m_kernel}; +        KScopedSpinLock lk{this->GetLock()}; + +        if (this->RegisterAbsoluteTaskImpl(task, task_time)) { +            if (task_time <= m_wakeup_time) { +                this->EnableInterrupt(task_time); +            } +        } +    } + +private: +    void EnableInterrupt(s64 wakeup_time); +    void DisableInterrupt(); +    bool GetInterruptEnabled(); +    s64 GetTick() const; +    void DoTask(); + +private: +    // Absolute time in nanoseconds +    s64 m_wakeup_time{std::numeric_limits<s64>::max()}; +    std::shared_ptr<Core::Timing::EventType> m_event_type{}; +}; + +} // namespace Kernel diff --git a/src/core/hle/kernel/k_hardware_timer_base.h b/src/core/hle/kernel/k_hardware_timer_base.h new file mode 100644 index 000000000..6318b35bd --- /dev/null +++ b/src/core/hle/kernel/k_hardware_timer_base.h @@ -0,0 +1,92 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/kernel/k_spin_lock.h" +#include "core/hle/kernel/k_thread.h" +#include "core/hle/kernel/k_timer_task.h" + +namespace Kernel { + +class KHardwareTimerBase { +public: +    explicit KHardwareTimerBase(KernelCore& kernel) : m_kernel{kernel} {} + +    void CancelTask(KTimerTask* task) { +        KScopedDisableDispatch dd{m_kernel}; +        KScopedSpinLock lk{m_lock}; + +        if (const s64 task_time = task->GetTime(); task_time > 0) { +            this->RemoveTaskFromTree(task); +        } +    } + +protected: +    KSpinLock& GetLock() { +        return m_lock; +    } + +    s64 DoInterruptTaskImpl(s64 cur_time) { +        // We want to handle all tasks, returning the next time that a task is scheduled. +        while (true) { +            // Get the next task. If there isn't one, return 0. +            KTimerTask* task = m_next_task; +            if (task == nullptr) { +                return 0; +            } + +            // If the task needs to be done in the future, do it in the future and not now. +            if (const s64 task_time = task->GetTime(); task_time > cur_time) { +                return task_time; +            } + +            // Remove the task from the tree of tasks, and update our next task. +            this->RemoveTaskFromTree(task); + +            // Handle the task. +            task->OnTimer(); +        } +    } + +    bool RegisterAbsoluteTaskImpl(KTimerTask* task, s64 task_time) { +        ASSERT(task_time > 0); + +        // Set the task's time, and insert it into our tree. +        task->SetTime(task_time); +        m_task_tree.insert(*task); + +        // Update our next task if relevant. +        if (m_next_task != nullptr && m_next_task->GetTime() <= task_time) { +            return false; +        } +        m_next_task = task; +        return true; +    } + +private: +    void RemoveTaskFromTree(KTimerTask* task) { +        // Erase from the tree. +        auto it = m_task_tree.erase(m_task_tree.iterator_to(*task)); + +        // Clear the task's scheduled time. +        task->SetTime(0); + +        // Update our next task if relevant. +        if (m_next_task == task) { +            m_next_task = (it != m_task_tree.end()) ? std::addressof(*it) : nullptr; +        } +    } + +protected: +    KernelCore& m_kernel; + +private: +    using TimerTaskTree = Common::IntrusiveRedBlackTreeBaseTraits<KTimerTask>::TreeType<KTimerTask>; + +    KSpinLock m_lock{}; +    TimerTaskTree m_task_tree{}; +    KTimerTask* m_next_task{}; +}; + +} // namespace Kernel diff --git a/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h b/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h index 76c095e69..76db65a4d 100644 --- a/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h +++ b/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h @@ -5,9 +5,9 @@  #include "common/common_types.h"  #include "core/hle/kernel/global_scheduler_context.h" +#include "core/hle/kernel/k_hardware_timer.h"  #include "core/hle/kernel/k_thread.h"  #include "core/hle/kernel/kernel.h" -#include "core/hle/kernel/time_manager.h"  namespace Kernel { @@ -22,7 +22,7 @@ public:      ~KScopedSchedulerLockAndSleep() {          // Register the sleep.          if (timeout_tick > 0) { -            kernel.TimeManager().ScheduleTimeEvent(thread, timeout_tick); +            kernel.HardwareTimer().RegisterTask(thread, timeout_tick);          }          // Unlock the scheduler. diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h index dc52b4ed3..7cd94a340 100644 --- a/src/core/hle/kernel/k_thread.h +++ b/src/core/hle/kernel/k_thread.h @@ -22,6 +22,7 @@  #include "core/hle/kernel/k_light_lock.h"  #include "core/hle/kernel/k_spin_lock.h"  #include "core/hle/kernel/k_synchronization_object.h" +#include "core/hle/kernel/k_timer_task.h"  #include "core/hle/kernel/k_worker_task.h"  #include "core/hle/kernel/slab_helpers.h"  #include "core/hle/kernel/svc_common.h" @@ -112,7 +113,8 @@ void SetCurrentThread(KernelCore& kernel, KThread* thread);  [[nodiscard]] s32 GetCurrentCoreId(KernelCore& kernel);  class KThread final : public KAutoObjectWithSlabHeapAndContainer<KThread, KWorkerTask>, -                      public boost::intrusive::list_base_hook<> { +                      public boost::intrusive::list_base_hook<>, +                      public KTimerTask {      KERNEL_AUTOOBJECT_TRAITS(KThread, KSynchronizationObject);  private: @@ -840,4 +842,8 @@ private:      KernelCore& kernel;  }; +inline void KTimerTask::OnTimer() { +    static_cast<KThread*>(this)->OnTimer(); +} +  } // namespace Kernel diff --git a/src/core/hle/kernel/k_thread_queue.cpp b/src/core/hle/kernel/k_thread_queue.cpp index 9f4e081ba..5f1dc97eb 100644 --- a/src/core/hle/kernel/k_thread_queue.cpp +++ b/src/core/hle/kernel/k_thread_queue.cpp @@ -1,9 +1,9 @@  // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project  // SPDX-License-Identifier: GPL-2.0-or-later +#include "core/hle/kernel/k_hardware_timer.h"  #include "core/hle/kernel/k_thread_queue.h"  #include "core/hle/kernel/kernel.h" -#include "core/hle/kernel/time_manager.h"  namespace Kernel { @@ -22,7 +22,7 @@ void KThreadQueue::EndWait(KThread* waiting_thread, Result wait_result) {      waiting_thread->ClearWaitQueue();      // Cancel the thread task. -    kernel.TimeManager().UnscheduleTimeEvent(waiting_thread); +    kernel.HardwareTimer().CancelTask(waiting_thread);  }  void KThreadQueue::CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) { @@ -37,7 +37,7 @@ void KThreadQueue::CancelWait(KThread* waiting_thread, Result wait_result, bool      // Cancel the thread task.      if (cancel_timer_task) { -        kernel.TimeManager().UnscheduleTimeEvent(waiting_thread); +        kernel.HardwareTimer().CancelTask(waiting_thread);      }  } diff --git a/src/core/hle/kernel/k_timer_task.h b/src/core/hle/kernel/k_timer_task.h new file mode 100644 index 000000000..66f0a5a90 --- /dev/null +++ b/src/core/hle/kernel/k_timer_task.h @@ -0,0 +1,40 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/intrusive_red_black_tree.h" + +namespace Kernel { + +class KTimerTask : public Common::IntrusiveRedBlackTreeBaseNode<KTimerTask> { +public: +    static constexpr int Compare(const KTimerTask& lhs, const KTimerTask& rhs) { +        if (lhs.GetTime() < rhs.GetTime()) { +            return -1; +        } else { +            return 1; +        } +    } + +    constexpr explicit KTimerTask() = default; + +    constexpr void SetTime(s64 t) { +        m_time = t; +    } + +    constexpr s64 GetTime() const { +        return m_time; +    } + +    // NOTE: This is virtual in Nintendo's kernel. Prior to 13.0.0, KWaitObject was also a +    // TimerTask; this is no longer the case. Since this is now KThread exclusive, we have +    // devirtualized (see inline declaration for this inside k_thread.h). +    void OnTimer(); + +private: +    // Absolute time in nanoseconds +    s64 m_time{}; +}; + +} // namespace Kernel diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 0eb74a422..b75bac5df 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -26,6 +26,7 @@  #include "core/hle/kernel/k_client_port.h"  #include "core/hle/kernel/k_dynamic_resource_manager.h"  #include "core/hle/kernel/k_handle_table.h" +#include "core/hle/kernel/k_hardware_timer.h"  #include "core/hle/kernel/k_memory_layout.h"  #include "core/hle/kernel/k_memory_manager.h"  #include "core/hle/kernel/k_page_buffer.h" @@ -39,7 +40,6 @@  #include "core/hle/kernel/kernel.h"  #include "core/hle/kernel/physical_core.h"  #include "core/hle/kernel/service_thread.h" -#include "core/hle/kernel/time_manager.h"  #include "core/hle/result.h"  #include "core/hle/service/sm/sm.h"  #include "core/memory.h" @@ -55,7 +55,7 @@ struct KernelCore::Impl {      static constexpr size_t ReservedDynamicPageCount = 64;      explicit Impl(Core::System& system_, KernelCore& kernel_) -        : time_manager{system_}, service_threads_manager{1, "ServiceThreadsManager"}, +        : service_threads_manager{1, "ServiceThreadsManager"},            service_thread_barrier{2}, system{system_} {}      void SetMulticore(bool is_multi) { @@ -63,6 +63,9 @@ struct KernelCore::Impl {      }      void Initialize(KernelCore& kernel) { +        hardware_timer = std::make_unique<Kernel::KHardwareTimer>(kernel); +        hardware_timer->Initialize(); +          global_object_list_container = std::make_unique<KAutoObjectWithListContainer>(kernel);          global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel);          global_handle_table = std::make_unique<Kernel::KHandleTable>(kernel); @@ -193,6 +196,9 @@ struct KernelCore::Impl {          // Ensure that the object list container is finalized and properly shutdown.          global_object_list_container->Finalize();          global_object_list_container.reset(); + +        hardware_timer->Finalize(); +        hardware_timer.reset();      }      void CloseServices() { @@ -832,7 +838,7 @@ struct KernelCore::Impl {      std::vector<KProcess*> process_list;      std::atomic<KProcess*> current_process{};      std::unique_ptr<Kernel::GlobalSchedulerContext> global_scheduler_context; -    Kernel::TimeManager time_manager; +    std::unique_ptr<Kernel::KHardwareTimer> hardware_timer;      Init::KSlabResourceCounts slab_resource_counts{};      KResourceLimit* system_resource_limit{}; @@ -1019,12 +1025,8 @@ Kernel::KScheduler* KernelCore::CurrentScheduler() {      return impl->schedulers[core_id].get();  } -Kernel::TimeManager& KernelCore::TimeManager() { -    return impl->time_manager; -} - -const Kernel::TimeManager& KernelCore::TimeManager() const { -    return impl->time_manager; +Kernel::KHardwareTimer& KernelCore::HardwareTimer() { +    return *impl->hardware_timer;  }  Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() { diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 2e22fe0f6..8d22f8d2c 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -39,6 +39,7 @@ class KDynamicPageManager;  class KEvent;  class KEventInfo;  class KHandleTable; +class KHardwareTimer;  class KLinkedListNode;  class KMemoryLayout;  class KMemoryManager; @@ -63,7 +64,6 @@ class KCodeMemory;  class PhysicalCore;  class ServiceThread;  class Synchronization; -class TimeManager;  using ServiceInterfaceFactory =      std::function<KClientPort&(Service::SM::ServiceManager&, Core::System&)>; @@ -175,11 +175,8 @@ public:      /// Gets the an instance of the current physical CPU core.      const Kernel::PhysicalCore& CurrentPhysicalCore() const; -    /// Gets the an instance of the TimeManager Interface. -    Kernel::TimeManager& TimeManager(); - -    /// Gets the an instance of the TimeManager Interface. -    const Kernel::TimeManager& TimeManager() const; +    /// Gets the an instance of the hardware timer. +    Kernel::KHardwareTimer& HardwareTimer();      /// Stops execution of 'id' core, in order to reschedule a new thread.      void PrepareReschedule(std::size_t id); diff --git a/src/core/hle/kernel/time_manager.cpp b/src/core/hle/kernel/time_manager.cpp deleted file mode 100644 index 5ee72c432..000000000 --- a/src/core/hle/kernel/time_manager.cpp +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "common/assert.h" -#include "core/core.h" -#include "core/core_timing.h" -#include "core/hle/kernel/k_scheduler.h" -#include "core/hle/kernel/k_thread.h" -#include "core/hle/kernel/time_manager.h" - -namespace Kernel { - -TimeManager::TimeManager(Core::System& system_) : system{system_} { -    time_manager_event_type = Core::Timing::CreateEvent( -        "Kernel::TimeManagerCallback", -        [this](std::uintptr_t thread_handle, s64 time, -               std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> { -            KThread* thread = reinterpret_cast<KThread*>(thread_handle); -            { -                KScopedSchedulerLock sl(system.Kernel()); -                thread->OnTimer(); -            } -            return std::nullopt; -        }); -} - -void TimeManager::ScheduleTimeEvent(KThread* thread, s64 nanoseconds) { -    std::scoped_lock lock{mutex}; -    if (nanoseconds > 0) { -        ASSERT(thread); -        ASSERT(thread->GetState() != ThreadState::Runnable); -        system.CoreTiming().ScheduleEvent(std::chrono::nanoseconds{nanoseconds}, -                                          time_manager_event_type, -                                          reinterpret_cast<uintptr_t>(thread)); -    } -} - -void TimeManager::UnscheduleTimeEvent(KThread* thread) { -    std::scoped_lock lock{mutex}; -    system.CoreTiming().UnscheduleEvent(time_manager_event_type, -                                        reinterpret_cast<uintptr_t>(thread)); -} - -} // namespace Kernel diff --git a/src/core/hle/kernel/time_manager.h b/src/core/hle/kernel/time_manager.h deleted file mode 100644 index 94d16b3b4..000000000 --- a/src/core/hle/kernel/time_manager.h +++ /dev/null @@ -1,41 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <memory> -#include <mutex> - -namespace Core { -class System; -} // namespace Core - -namespace Core::Timing { -struct EventType; -} // namespace Core::Timing - -namespace Kernel { - -class KThread; - -/** - * The `TimeManager` takes care of scheduling time events on threads and executes their TimeUp - * method when the event is triggered. - */ -class TimeManager { -public: -    explicit TimeManager(Core::System& system); - -    /// Schedule a time event on `timetask` thread that will expire in 'nanoseconds' -    void ScheduleTimeEvent(KThread* time_task, s64 nanoseconds); - -    /// Unschedule an existing time event -    void UnscheduleTimeEvent(KThread* thread); - -private: -    Core::System& system; -    std::shared_ptr<Core::Timing::EventType> time_manager_event_type; -    std::mutex mutex; -}; - -} // namespace Kernel diff --git a/src/core/hle/service/nfc/nfc_user.cpp b/src/core/hle/service/nfc/nfc_user.cpp index 4615697e2..89aa6b3f5 100644 --- a/src/core/hle/service/nfc/nfc_user.cpp +++ b/src/core/hle/service/nfc/nfc_user.cpp @@ -97,7 +97,7 @@ void IUser::IsNfcEnabled(Kernel::HLERequestContext& ctx) {  }  void IUser::ListDevices(Kernel::HLERequestContext& ctx) { -    LOG_INFO(Service_NFC, "called"); +    LOG_DEBUG(Service_NFC, "called");      if (state == State::NonInitialized) {          IPC::ResponseBuilder rb{ctx, 2}; diff --git a/src/core/hle/service/nfp/nfp_user.cpp b/src/core/hle/service/nfp/nfp_user.cpp index 49816b4c7..a4d3d1bc7 100644 --- a/src/core/hle/service/nfp/nfp_user.cpp +++ b/src/core/hle/service/nfp/nfp_user.cpp @@ -83,7 +83,7 @@ void IUser::Finalize(Kernel::HLERequestContext& ctx) {  }  void IUser::ListDevices(Kernel::HLERequestContext& ctx) { -    LOG_INFO(Service_NFP, "called"); +    LOG_DEBUG(Service_NFP, "called");      if (state == State::NonInitialized) {          IPC::ResponseBuilder rb{ctx, 2}; diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt index 7932aaab0..f24c89b04 100644 --- a/src/input_common/CMakeLists.txt +++ b/src/input_common/CMakeLists.txt @@ -20,6 +20,8 @@ add_library(input_common STATIC      drivers/udp_client.h      drivers/virtual_amiibo.cpp      drivers/virtual_amiibo.h +    drivers/virtual_gamepad.cpp +    drivers/virtual_gamepad.h      helpers/stick_from_buttons.cpp      helpers/stick_from_buttons.h      helpers/touch_from_buttons.cpp diff --git a/src/input_common/drivers/camera.h b/src/input_common/drivers/camera.h index 38fb1ae4c..ead3e0fde 100644 --- a/src/input_common/drivers/camera.h +++ b/src/input_common/drivers/camera.h @@ -25,6 +25,7 @@ public:      Common::Input::CameraError SetCameraFormat(const PadIdentifier& identifier_,                                                 Common::Input::CameraFormat camera_format) override; +private:      Common::Input::CameraStatus status{};  }; diff --git a/src/input_common/drivers/virtual_gamepad.cpp b/src/input_common/drivers/virtual_gamepad.cpp new file mode 100644 index 000000000..7db945aa6 --- /dev/null +++ b/src/input_common/drivers/virtual_gamepad.cpp @@ -0,0 +1,78 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "input_common/drivers/virtual_gamepad.h" + +namespace InputCommon { +constexpr std::size_t PlayerIndexCount = 10; + +VirtualGamepad::VirtualGamepad(std::string input_engine_) : InputEngine(std::move(input_engine_)) { +    for (std::size_t i = 0; i < PlayerIndexCount; i++) { +        PreSetController(GetIdentifier(i)); +    } +} + +void VirtualGamepad::SetButtonState(std::size_t player_index, int button_id, bool value) { +    if (player_index > PlayerIndexCount) { +        return; +    } +    const auto identifier = GetIdentifier(player_index); +    SetButton(identifier, button_id, value); +} + +void VirtualGamepad::SetButtonState(std::size_t player_index, VirtualButton button_id, bool value) { +    SetButtonState(player_index, static_cast<int>(button_id), value); +} + +void VirtualGamepad::SetStickPosition(std::size_t player_index, int axis_id, float x_value, +                                      float y_value) { +    if (player_index > PlayerIndexCount) { +        return; +    } +    const auto identifier = GetIdentifier(player_index); +    SetAxis(identifier, axis_id * 2, x_value); +    SetAxis(identifier, (axis_id * 2) + 1, y_value); +} + +void VirtualGamepad::SetStickPosition(std::size_t player_index, VirtualStick axis_id, float x_value, +                                      float y_value) { +    SetStickPosition(player_index, static_cast<int>(axis_id), x_value, y_value); +} + +void VirtualGamepad::ResetControllers() { +    for (std::size_t i = 0; i < PlayerIndexCount; i++) { +        SetStickPosition(i, VirtualStick::Left, 0.0f, 0.0f); +        SetStickPosition(i, VirtualStick::Right, 0.0f, 0.0f); + +        SetButtonState(i, VirtualButton::ButtonA, false); +        SetButtonState(i, VirtualButton::ButtonB, false); +        SetButtonState(i, VirtualButton::ButtonX, false); +        SetButtonState(i, VirtualButton::ButtonY, false); +        SetButtonState(i, VirtualButton::StickL, false); +        SetButtonState(i, VirtualButton::StickR, false); +        SetButtonState(i, VirtualButton::TriggerL, false); +        SetButtonState(i, VirtualButton::TriggerR, false); +        SetButtonState(i, VirtualButton::TriggerZL, false); +        SetButtonState(i, VirtualButton::TriggerZR, false); +        SetButtonState(i, VirtualButton::ButtonPlus, false); +        SetButtonState(i, VirtualButton::ButtonMinus, false); +        SetButtonState(i, VirtualButton::ButtonLeft, false); +        SetButtonState(i, VirtualButton::ButtonUp, false); +        SetButtonState(i, VirtualButton::ButtonRight, false); +        SetButtonState(i, VirtualButton::ButtonDown, false); +        SetButtonState(i, VirtualButton::ButtonSL, false); +        SetButtonState(i, VirtualButton::ButtonSR, false); +        SetButtonState(i, VirtualButton::ButtonHome, false); +        SetButtonState(i, VirtualButton::ButtonCapture, false); +    } +} + +PadIdentifier VirtualGamepad::GetIdentifier(std::size_t player_index) const { +    return { +        .guid = Common::UUID{}, +        .port = player_index, +        .pad = 0, +    }; +} + +} // namespace InputCommon diff --git a/src/input_common/drivers/virtual_gamepad.h b/src/input_common/drivers/virtual_gamepad.h new file mode 100644 index 000000000..3df91cc6f --- /dev/null +++ b/src/input_common/drivers/virtual_gamepad.h @@ -0,0 +1,73 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "input_common/input_engine.h" + +namespace InputCommon { + +/** + * A virtual controller that is always assigned to the game input + */ +class VirtualGamepad final : public InputEngine { +public: +    enum class VirtualButton { +        ButtonA, +        ButtonB, +        ButtonX, +        ButtonY, +        StickL, +        StickR, +        TriggerL, +        TriggerR, +        TriggerZL, +        TriggerZR, +        ButtonPlus, +        ButtonMinus, +        ButtonLeft, +        ButtonUp, +        ButtonRight, +        ButtonDown, +        ButtonSL, +        ButtonSR, +        ButtonHome, +        ButtonCapture, +    }; + +    enum class VirtualStick { +        Left = 0, +        Right = 1, +    }; + +    explicit VirtualGamepad(std::string input_engine_); + +    /** +     * Sets the status of all buttons bound with the key to pressed +     * @param player_index the player number that will take this action +     * @param button_id the id of the button +     * @param value indicates if the button is pressed or not +     */ +    void SetButtonState(std::size_t player_index, int button_id, bool value); +    void SetButtonState(std::size_t player_index, VirtualButton button_id, bool value); + +    /** +     * Sets the status of all buttons bound with the key to released +     * @param player_index the player number that will take this action +     * @param axis_id the id of the axis to move +     * @param x_value the position of the stick in the x axis +     * @param y_value the position of the stick in the y axis +     */ +    void SetStickPosition(std::size_t player_index, int axis_id, float x_value, float y_value); +    void SetStickPosition(std::size_t player_index, VirtualStick axis_id, float x_value, +                          float y_value); + +    /// Restores all inputs into the neutral position +    void ResetControllers(); + +private: +    /// Returns the correct identifier corresponding to the player index +    PadIdentifier GetIdentifier(std::size_t player_index) const; +}; + +} // namespace InputCommon diff --git a/src/input_common/input_mapping.cpp b/src/input_common/input_mapping.cpp index 0fa4b1ddb..edd5287c1 100644 --- a/src/input_common/input_mapping.cpp +++ b/src/input_common/input_mapping.cpp @@ -200,12 +200,6 @@ bool MappingFactory::IsDriverValid(const MappingData& data) const {          return false;      }      // The following drivers don't need to be mapped -    if (data.engine == "tas") { -        return false; -    } -    if (data.engine == "touch") { -        return false; -    }      if (data.engine == "touch_from_button") {          return false;      } diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp index 942a13535..86deb4c7c 100644 --- a/src/input_common/main.cpp +++ b/src/input_common/main.cpp @@ -12,6 +12,7 @@  #include "input_common/drivers/touch_screen.h"  #include "input_common/drivers/udp_client.h"  #include "input_common/drivers/virtual_amiibo.h" +#include "input_common/drivers/virtual_gamepad.h"  #include "input_common/helpers/stick_from_buttons.h"  #include "input_common/helpers/touch_from_buttons.h"  #include "input_common/input_engine.h" @@ -25,73 +26,33 @@  namespace InputCommon {  struct InputSubsystem::Impl { -    void Initialize() { -        mapping_factory = std::make_shared<MappingFactory>(); +    template <typename Engine> +    void RegisterEngine(std::string name, std::shared_ptr<Engine>& engine) {          MappingCallback mapping_callback{[this](const MappingData& data) { RegisterInput(data); }}; -        keyboard = std::make_shared<Keyboard>("keyboard"); -        keyboard->SetMappingCallback(mapping_callback); -        keyboard_factory = std::make_shared<InputFactory>(keyboard); -        keyboard_output_factory = std::make_shared<OutputFactory>(keyboard); -        Common::Input::RegisterInputFactory(keyboard->GetEngineName(), keyboard_factory); -        Common::Input::RegisterOutputFactory(keyboard->GetEngineName(), keyboard_output_factory); - -        mouse = std::make_shared<Mouse>("mouse"); -        mouse->SetMappingCallback(mapping_callback); -        mouse_factory = std::make_shared<InputFactory>(mouse); -        mouse_output_factory = std::make_shared<OutputFactory>(mouse); -        Common::Input::RegisterInputFactory(mouse->GetEngineName(), mouse_factory); -        Common::Input::RegisterOutputFactory(mouse->GetEngineName(), mouse_output_factory); - -        touch_screen = std::make_shared<TouchScreen>("touch"); -        touch_screen_factory = std::make_shared<InputFactory>(touch_screen); -        Common::Input::RegisterInputFactory(touch_screen->GetEngineName(), touch_screen_factory); - -        gcadapter = std::make_shared<GCAdapter>("gcpad"); -        gcadapter->SetMappingCallback(mapping_callback); -        gcadapter_input_factory = std::make_shared<InputFactory>(gcadapter); -        gcadapter_output_factory = std::make_shared<OutputFactory>(gcadapter); -        Common::Input::RegisterInputFactory(gcadapter->GetEngineName(), gcadapter_input_factory); -        Common::Input::RegisterOutputFactory(gcadapter->GetEngineName(), gcadapter_output_factory); - -        udp_client = std::make_shared<CemuhookUDP::UDPClient>("cemuhookudp"); -        udp_client->SetMappingCallback(mapping_callback); -        udp_client_input_factory = std::make_shared<InputFactory>(udp_client); -        udp_client_output_factory = std::make_shared<OutputFactory>(udp_client); -        Common::Input::RegisterInputFactory(udp_client->GetEngineName(), udp_client_input_factory); -        Common::Input::RegisterOutputFactory(udp_client->GetEngineName(), -                                             udp_client_output_factory); - -        tas_input = std::make_shared<TasInput::Tas>("tas"); -        tas_input->SetMappingCallback(mapping_callback); -        tas_input_factory = std::make_shared<InputFactory>(tas_input); -        tas_output_factory = std::make_shared<OutputFactory>(tas_input); -        Common::Input::RegisterInputFactory(tas_input->GetEngineName(), tas_input_factory); -        Common::Input::RegisterOutputFactory(tas_input->GetEngineName(), tas_output_factory); - -        camera = std::make_shared<Camera>("camera"); -        camera->SetMappingCallback(mapping_callback); -        camera_input_factory = std::make_shared<InputFactory>(camera); -        camera_output_factory = std::make_shared<OutputFactory>(camera); -        Common::Input::RegisterInputFactory(camera->GetEngineName(), camera_input_factory); -        Common::Input::RegisterOutputFactory(camera->GetEngineName(), camera_output_factory); - -        virtual_amiibo = std::make_shared<VirtualAmiibo>("virtual_amiibo"); -        virtual_amiibo->SetMappingCallback(mapping_callback); -        virtual_amiibo_input_factory = std::make_shared<InputFactory>(virtual_amiibo); -        virtual_amiibo_output_factory = std::make_shared<OutputFactory>(virtual_amiibo); -        Common::Input::RegisterInputFactory(virtual_amiibo->GetEngineName(), -                                            virtual_amiibo_input_factory); -        Common::Input::RegisterOutputFactory(virtual_amiibo->GetEngineName(), -                                             virtual_amiibo_output_factory); +        engine = std::make_shared<Engine>(name); +        engine->SetMappingCallback(mapping_callback); + +        std::shared_ptr<InputFactory> input_factory = std::make_shared<InputFactory>(engine); +        std::shared_ptr<OutputFactory> output_factory = std::make_shared<OutputFactory>(engine); +        Common::Input::RegisterInputFactory(engine->GetEngineName(), std::move(input_factory)); +        Common::Input::RegisterOutputFactory(engine->GetEngineName(), std::move(output_factory)); +    } + +    void Initialize() { +        mapping_factory = std::make_shared<MappingFactory>(); +        RegisterEngine("keyboard", keyboard); +        RegisterEngine("mouse", mouse); +        RegisterEngine("touch", touch_screen); +        RegisterEngine("gcpad", gcadapter); +        RegisterEngine("cemuhookudp", udp_client); +        RegisterEngine("tas", tas_input); +        RegisterEngine("camera", camera); +        RegisterEngine("virtual_amiibo", virtual_amiibo); +        RegisterEngine("virtual_gamepad", virtual_gamepad);  #ifdef HAVE_SDL2 -        sdl = std::make_shared<SDLDriver>("sdl"); -        sdl->SetMappingCallback(mapping_callback); -        sdl_input_factory = std::make_shared<InputFactory>(sdl); -        sdl_output_factory = std::make_shared<OutputFactory>(sdl); -        Common::Input::RegisterInputFactory(sdl->GetEngineName(), sdl_input_factory); -        Common::Input::RegisterOutputFactory(sdl->GetEngineName(), sdl_output_factory); +        RegisterEngine("sdl", sdl);  #endif          Common::Input::RegisterInputFactory("touch_from_button", @@ -100,42 +61,25 @@ struct InputSubsystem::Impl {                                              std::make_shared<StickFromButton>());      } -    void Shutdown() { -        Common::Input::UnregisterInputFactory(keyboard->GetEngineName()); -        Common::Input::UnregisterOutputFactory(keyboard->GetEngineName()); -        keyboard.reset(); - -        Common::Input::UnregisterInputFactory(mouse->GetEngineName()); -        Common::Input::UnregisterOutputFactory(mouse->GetEngineName()); -        mouse.reset(); - -        Common::Input::UnregisterInputFactory(touch_screen->GetEngineName()); -        touch_screen.reset(); - -        Common::Input::UnregisterInputFactory(gcadapter->GetEngineName()); -        Common::Input::UnregisterOutputFactory(gcadapter->GetEngineName()); -        gcadapter.reset(); - -        Common::Input::UnregisterInputFactory(udp_client->GetEngineName()); -        Common::Input::UnregisterOutputFactory(udp_client->GetEngineName()); -        udp_client.reset(); - -        Common::Input::UnregisterInputFactory(tas_input->GetEngineName()); -        Common::Input::UnregisterOutputFactory(tas_input->GetEngineName()); -        tas_input.reset(); - -        Common::Input::UnregisterInputFactory(camera->GetEngineName()); -        Common::Input::UnregisterOutputFactory(camera->GetEngineName()); -        camera.reset(); - -        Common::Input::UnregisterInputFactory(virtual_amiibo->GetEngineName()); -        Common::Input::UnregisterOutputFactory(virtual_amiibo->GetEngineName()); -        virtual_amiibo.reset(); +    template <typename Engine> +    void UnregisterEngine(std::shared_ptr<Engine>& engine) { +        Common::Input::UnregisterInputFactory(engine->GetEngineName()); +        Common::Input::UnregisterOutputFactory(engine->GetEngineName()); +        engine.reset(); +    } +    void Shutdown() { +        UnregisterEngine(keyboard); +        UnregisterEngine(mouse); +        UnregisterEngine(touch_screen); +        UnregisterEngine(gcadapter); +        UnregisterEngine(udp_client); +        UnregisterEngine(tas_input); +        UnregisterEngine(camera); +        UnregisterEngine(virtual_amiibo); +        UnregisterEngine(virtual_gamepad);  #ifdef HAVE_SDL2 -        Common::Input::UnregisterInputFactory(sdl->GetEngineName()); -        Common::Input::UnregisterOutputFactory(sdl->GetEngineName()); -        sdl.reset(); +        UnregisterEngine(sdl);  #endif          Common::Input::UnregisterInputFactory("touch_from_button"); @@ -163,117 +107,86 @@ struct InputSubsystem::Impl {          return devices;      } -    [[nodiscard]] AnalogMapping GetAnalogMappingForDevice( +    [[nodiscard]] std::shared_ptr<InputEngine> GetInputEngine(          const Common::ParamPackage& params) const {          if (!params.Has("engine") || params.Get("engine", "") == "any") { -            return {}; +            return nullptr;          }          const std::string engine = params.Get("engine", ""); +        if (engine == keyboard->GetEngineName()) { +            return keyboard; +        }          if (engine == mouse->GetEngineName()) { -            return mouse->GetAnalogMappingForDevice(params); +            return mouse;          }          if (engine == gcadapter->GetEngineName()) { -            return gcadapter->GetAnalogMappingForDevice(params); +            return gcadapter;          }          if (engine == udp_client->GetEngineName()) { -            return udp_client->GetAnalogMappingForDevice(params); -        } -        if (engine == tas_input->GetEngineName()) { -            return tas_input->GetAnalogMappingForDevice(params); +            return udp_client;          }  #ifdef HAVE_SDL2          if (engine == sdl->GetEngineName()) { -            return sdl->GetAnalogMappingForDevice(params); +            return sdl;          }  #endif -        return {}; +        return nullptr;      } -    [[nodiscard]] ButtonMapping GetButtonMappingForDevice( +    [[nodiscard]] AnalogMapping GetAnalogMappingForDevice(          const Common::ParamPackage& params) const { -        if (!params.Has("engine") || params.Get("engine", "") == "any") { +        const auto input_engine = GetInputEngine(params); + +        if (input_engine == nullptr) {              return {};          } -        const std::string engine = params.Get("engine", ""); -        if (engine == gcadapter->GetEngineName()) { -            return gcadapter->GetButtonMappingForDevice(params); -        } -        if (engine == udp_client->GetEngineName()) { -            return udp_client->GetButtonMappingForDevice(params); -        } -        if (engine == tas_input->GetEngineName()) { -            return tas_input->GetButtonMappingForDevice(params); -        } -#ifdef HAVE_SDL2 -        if (engine == sdl->GetEngineName()) { -            return sdl->GetButtonMappingForDevice(params); + +        return input_engine->GetAnalogMappingForDevice(params); +    } + +    [[nodiscard]] ButtonMapping GetButtonMappingForDevice( +        const Common::ParamPackage& params) const { +        const auto input_engine = GetInputEngine(params); + +        if (input_engine == nullptr) { +            return {};          } -#endif -        return {}; + +        return input_engine->GetButtonMappingForDevice(params);      }      [[nodiscard]] MotionMapping GetMotionMappingForDevice(          const Common::ParamPackage& params) const { -        if (!params.Has("engine") || params.Get("engine", "") == "any") { +        const auto input_engine = GetInputEngine(params); + +        if (input_engine == nullptr) {              return {};          } -        const std::string engine = params.Get("engine", ""); -        if (engine == udp_client->GetEngineName()) { -            return udp_client->GetMotionMappingForDevice(params); -        } -#ifdef HAVE_SDL2 -        if (engine == sdl->GetEngineName()) { -            return sdl->GetMotionMappingForDevice(params); -        } -#endif -        return {}; + +        return input_engine->GetMotionMappingForDevice(params);      }      Common::Input::ButtonNames GetButtonName(const Common::ParamPackage& params) const {          if (!params.Has("engine") || params.Get("engine", "") == "any") {              return Common::Input::ButtonNames::Undefined;          } -        const std::string engine = params.Get("engine", ""); -        if (engine == mouse->GetEngineName()) { -            return mouse->GetUIName(params); -        } -        if (engine == gcadapter->GetEngineName()) { -            return gcadapter->GetUIName(params); -        } -        if (engine == udp_client->GetEngineName()) { -            return udp_client->GetUIName(params); -        } -        if (engine == tas_input->GetEngineName()) { -            return tas_input->GetUIName(params); -        } -#ifdef HAVE_SDL2 -        if (engine == sdl->GetEngineName()) { -            return sdl->GetUIName(params); +        const auto input_engine = GetInputEngine(params); + +        if (input_engine == nullptr) { +            return Common::Input::ButtonNames::Invalid;          } -#endif -        return Common::Input::ButtonNames::Invalid; + +        return input_engine->GetUIName(params);      }      bool IsStickInverted(const Common::ParamPackage& params) { -        const std::string engine = params.Get("engine", ""); -        if (engine == mouse->GetEngineName()) { -            return mouse->IsStickInverted(params); -        } -        if (engine == gcadapter->GetEngineName()) { -            return gcadapter->IsStickInverted(params); -        } -        if (engine == udp_client->GetEngineName()) { -            return udp_client->IsStickInverted(params); -        } -        if (engine == tas_input->GetEngineName()) { -            return tas_input->IsStickInverted(params); -        } -#ifdef HAVE_SDL2 -        if (engine == sdl->GetEngineName()) { -            return sdl->IsStickInverted(params); +        const auto input_engine = GetInputEngine(params); + +        if (input_engine == nullptr) { +            return false;          } -#endif -        return false; + +        return input_engine->IsStickInverted(params);      }      bool IsController(const Common::ParamPackage& params) { @@ -290,6 +203,9 @@ struct InputSubsystem::Impl {          if (engine == tas_input->GetEngineName()) {              return true;          } +        if (engine == virtual_gamepad->GetEngineName()) { +            return true; +        }  #ifdef HAVE_SDL2          if (engine == sdl->GetEngineName()) {              return true; @@ -338,28 +254,10 @@ struct InputSubsystem::Impl {      std::shared_ptr<CemuhookUDP::UDPClient> udp_client;      std::shared_ptr<Camera> camera;      std::shared_ptr<VirtualAmiibo> virtual_amiibo; - -    std::shared_ptr<InputFactory> keyboard_factory; -    std::shared_ptr<InputFactory> mouse_factory; -    std::shared_ptr<InputFactory> gcadapter_input_factory; -    std::shared_ptr<InputFactory> touch_screen_factory; -    std::shared_ptr<InputFactory> udp_client_input_factory; -    std::shared_ptr<InputFactory> tas_input_factory; -    std::shared_ptr<InputFactory> camera_input_factory; -    std::shared_ptr<InputFactory> virtual_amiibo_input_factory; - -    std::shared_ptr<OutputFactory> keyboard_output_factory; -    std::shared_ptr<OutputFactory> mouse_output_factory; -    std::shared_ptr<OutputFactory> gcadapter_output_factory; -    std::shared_ptr<OutputFactory> udp_client_output_factory; -    std::shared_ptr<OutputFactory> tas_output_factory; -    std::shared_ptr<OutputFactory> camera_output_factory; -    std::shared_ptr<OutputFactory> virtual_amiibo_output_factory; +    std::shared_ptr<VirtualGamepad> virtual_gamepad;  #ifdef HAVE_SDL2      std::shared_ptr<SDLDriver> sdl; -    std::shared_ptr<InputFactory> sdl_input_factory; -    std::shared_ptr<OutputFactory> sdl_output_factory;  #endif  }; @@ -423,6 +321,14 @@ const VirtualAmiibo* InputSubsystem::GetVirtualAmiibo() const {      return impl->virtual_amiibo.get();  } +VirtualGamepad* InputSubsystem::GetVirtualGamepad() { +    return impl->virtual_gamepad.get(); +} + +const VirtualGamepad* InputSubsystem::GetVirtualGamepad() const { +    return impl->virtual_gamepad.get(); +} +  std::vector<Common::ParamPackage> InputSubsystem::GetInputDevices() const {      return impl->GetInputDevices();  } diff --git a/src/input_common/main.h b/src/input_common/main.h index 6218c37f6..1207d786c 100644 --- a/src/input_common/main.h +++ b/src/input_common/main.h @@ -34,6 +34,7 @@ class Keyboard;  class Mouse;  class TouchScreen;  class VirtualAmiibo; +class VirtualGamepad;  struct MappingData;  } // namespace InputCommon @@ -108,6 +109,12 @@ public:      /// Retrieves the underlying virtual amiibo input device.      [[nodiscard]] const VirtualAmiibo* GetVirtualAmiibo() const; +    /// Retrieves the underlying virtual gamepad input device. +    [[nodiscard]] VirtualGamepad* GetVirtualGamepad(); + +    /// Retrieves the underlying virtual gamepad input device. +    [[nodiscard]] const VirtualGamepad* GetVirtualGamepad() const; +      /**       * Returns all available input devices that this Factory can create a new device with.       * Each returned ParamPackage should have a `display` field used for display, a `engine` field diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp index 483b534a0..7dca7341c 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.cpp +++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp @@ -314,6 +314,18 @@ const char* ToString(VkResult result) noexcept {          return "VK_ERROR_VALIDATION_FAILED_EXT";      case VkResult::VK_ERROR_INVALID_SHADER_NV:          return "VK_ERROR_INVALID_SHADER_NV"; +    case VkResult::VK_ERROR_IMAGE_USAGE_NOT_SUPPORTED_KHR: +        return "VK_ERROR_IMAGE_USAGE_NOT_SUPPORTED_KHR"; +    case VkResult::VK_ERROR_VIDEO_PICTURE_LAYOUT_NOT_SUPPORTED_KHR: +        return "VK_ERROR_VIDEO_PICTURE_LAYOUT_NOT_SUPPORTED_KHR"; +    case VkResult::VK_ERROR_VIDEO_PROFILE_OPERATION_NOT_SUPPORTED_KHR: +        return "VK_ERROR_VIDEO_PROFILE_OPERATION_NOT_SUPPORTED_KHR"; +    case VkResult::VK_ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR: +        return "VK_ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR"; +    case VkResult::VK_ERROR_VIDEO_PROFILE_CODEC_NOT_SUPPORTED_KHR: +        return "VK_ERROR_VIDEO_PROFILE_CODEC_NOT_SUPPORTED_KHR"; +    case VkResult::VK_ERROR_VIDEO_STD_VERSION_NOT_SUPPORTED_KHR: +        return "VK_ERROR_VIDEO_STD_VERSION_NOT_SUPPORTED_KHR";      case VkResult::VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT:          return "VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT";      case VkResult::VK_ERROR_FRAGMENTATION_EXT: diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 40b3d91fc..13782869d 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -740,7 +740,9 @@ void GRenderWindow::InitializeCamera() {          return;      } -    camera_data.resize(CAMERA_WIDTH * CAMERA_HEIGHT); +    const auto camera_width = input_subsystem->GetCamera()->getImageWidth(); +    const auto camera_height = input_subsystem->GetCamera()->getImageHeight(); +    camera_data.resize(camera_width * camera_height);      camera_capture->setCaptureDestination(QCameraImageCapture::CaptureDestination::CaptureToBuffer);      connect(camera_capture.get(), &QCameraImageCapture::imageCaptured, this,              &GRenderWindow::OnCameraCapture); @@ -796,14 +798,22 @@ void GRenderWindow::RequestCameraCapture() {  }  void GRenderWindow::OnCameraCapture(int requestId, const QImage& img) { +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA      // TODO: Capture directly in the format and resolution needed +    const auto camera_width = input_subsystem->GetCamera()->getImageWidth(); +    const auto camera_height = input_subsystem->GetCamera()->getImageHeight();      const auto converted = -        img.scaled(CAMERA_WIDTH, CAMERA_HEIGHT, Qt::AspectRatioMode::IgnoreAspectRatio, +        img.scaled(static_cast<int>(camera_width), static_cast<int>(camera_height), +                   Qt::AspectRatioMode::IgnoreAspectRatio,                     Qt::TransformationMode::SmoothTransformation)              .mirrored(false, true); -    std::memcpy(camera_data.data(), converted.bits(), CAMERA_WIDTH * CAMERA_HEIGHT * sizeof(u32)); -    input_subsystem->GetCamera()->SetCameraData(CAMERA_WIDTH, CAMERA_HEIGHT, camera_data); +    if (camera_data.size() != camera_width * camera_height) { +        camera_data.resize(camera_width * camera_height); +    } +    std::memcpy(camera_data.data(), converted.bits(), camera_width * camera_height * sizeof(u32)); +    input_subsystem->GetCamera()->SetCameraData(camera_width, camera_height, camera_data);      pending_camera_snapshots = 0; +#endif  }  bool GRenderWindow::event(QEvent* event) { diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index 52867d628..1c2e76369 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h @@ -239,16 +239,14 @@ private:      bool first_frame = false;      InputCommon::TasInput::TasState last_tas_state; +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA      bool is_virtual_camera;      int pending_camera_snapshots; -#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA +    std::vector<u32> camera_data;      std::unique_ptr<QCamera> camera;      std::unique_ptr<QCameraImageCapture> camera_capture; -    static constexpr std::size_t CAMERA_WIDTH = 320; -    static constexpr std::size_t CAMERA_HEIGHT = 240; -    std::vector<u32> camera_data; -#endif      std::unique_ptr<QTimer> camera_timer; +#endif      Core::System& system; diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index a8d47a2f9..2ea4f367b 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -783,8 +783,6 @@ void Config::ReadSystemValues() {          }      } -    ReadBasicSetting(Settings::values.device_name); -      if (global) {          ReadBasicSetting(Settings::values.current_user);          Settings::values.current_user = std::clamp<int>(Settings::values.current_user.GetValue(), 0, @@ -797,6 +795,7 @@ void Config::ReadSystemValues() {          } else {              Settings::values.custom_rtc = std::nullopt;          } +        ReadBasicSetting(Settings::values.device_name);      }      ReadGlobalSetting(Settings::values.sound_index); @@ -1407,7 +1406,6 @@ void Config::SaveSystemValues() {                   Settings::values.rng_seed.UsingGlobal());      WriteSetting(QStringLiteral("rng_seed"), Settings::values.rng_seed.GetValue(global).value_or(0),                   0, Settings::values.rng_seed.UsingGlobal()); -    WriteBasicSetting(Settings::values.device_name);      if (global) {          WriteBasicSetting(Settings::values.current_user); @@ -1416,6 +1414,7 @@ void Config::SaveSystemValues() {                       false);          WriteSetting(QStringLiteral("custom_rtc"),                       QVariant::fromValue<long long>(Settings::values.custom_rtc.value_or(0)), 0); +        WriteBasicSetting(Settings::values.device_name);      }      WriteGlobalSetting(Settings::values.sound_index); diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index c6f285dc2..820f60e61 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -2662,6 +2662,9 @@ void GMainWindow::OnMenuInstallToNAND() {          return;      } +    // Save folder location of the first selected file +    UISettings::values.roms_path = QFileInfo(filenames[0]).path(); +      int remaining = filenames.size();      // This would only overflow above 2^43 bytes (8.796 TB) diff --git a/src/yuzu/startup_checks.cpp b/src/yuzu/startup_checks.cpp index 563818362..9f702fe95 100644 --- a/src/yuzu/startup_checks.cpp +++ b/src/yuzu/startup_checks.cpp @@ -186,7 +186,7 @@ pid_t SpawnChild(const char* arg0) {          return pid;      } else if (pid == 0) {          // child -        execl(arg0, arg0, nullptr); +        execlp(arg0, arg0, nullptr);          const int err = errno;          fmt::print(stderr, "execl failed with error {}\n", err);          _exit(0); diff --git a/src/yuzu/util/overlay_dialog.cpp b/src/yuzu/util/overlay_dialog.cpp index b27954512..3fa3d0afb 100644 --- a/src/yuzu/util/overlay_dialog.cpp +++ b/src/yuzu/util/overlay_dialog.cpp @@ -42,7 +42,7 @@ OverlayDialog::OverlayDialog(QWidget* parent, Core::System& system, const QStrin      MoveAndResizeWindow();      // TODO (Morph): Remove this when InputInterpreter no longer relies on the HID backend -    if (system.IsPoweredOn()) { +    if (system.IsPoweredOn() && !ui->buttonsDialog->isHidden()) {          input_interpreter = std::make_unique<InputInterpreter>(system);          StartInputThread(); @@ -83,6 +83,11 @@ void OverlayDialog::InitializeRegularTextDialog(const QString& title_text, const          ui->button_ok_label->setEnabled(false);      } +    if (ui->button_cancel->isHidden() && ui->button_ok_label->isHidden()) { +        ui->buttonsDialog->hide(); +        return; +    } +      connect(          ui->button_cancel, &QPushButton::clicked, this,          [this](bool) { @@ -130,6 +135,11 @@ void OverlayDialog::InitializeRichTextDialog(const QString& title_text, const QS          ui->button_ok_rich->setEnabled(false);      } +    if (ui->button_cancel_rich->isHidden() && ui->button_ok_rich->isHidden()) { +        ui->buttonsRichDialog->hide(); +        return; +    } +      connect(          ui->button_cancel_rich, &QPushButton::clicked, this,          [this](bool) { | 
