diff options
Diffstat (limited to 'src/core')
30 files changed, 497 insertions, 287 deletions
diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp index 8e095cdcd..cef79b245 100644 --- a/src/core/arm/arm_interface.cpp +++ b/src/core/arm/arm_interface.cpp @@ -1,6 +1,10 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#ifndef _MSC_VER +#include <cxxabi.h> +#endif + #include <map> #include <optional> #include "common/bit_field.h" @@ -68,8 +72,19 @@ void ARM_Interface::SymbolicateBacktrace(Core::System& system, std::vector<Backt if (symbol_set != symbols.end()) { const auto symbol = Symbols::GetSymbolName(symbol_set->second, entry.offset); if (symbol.has_value()) { +#ifdef _MSC_VER // TODO(DarkLordZach): Add demangling of symbol names. entry.name = *symbol; +#else + int status{-1}; + char* demangled{abi::__cxa_demangle(symbol->c_str(), nullptr, nullptr, &status)}; + if (status == 0 && demangled != nullptr) { + entry.name = demangled; + std::free(demangled); + } else { + entry.name = *symbol; + } +#endif } } } @@ -119,16 +134,23 @@ void ARM_Interface::Run() { } system.ExitDynarmicProfile(); - // Notify the debugger and go to sleep if a breakpoint was hit. - if (Has(hr, breakpoint)) { + // Notify the debugger and go to sleep if a breakpoint was hit, + // or if the thread is unable to continue for any reason. + if (Has(hr, breakpoint) || Has(hr, no_execute)) { RewindBreakpointInstruction(); - system.GetDebugger().NotifyThreadStopped(current_thread); - current_thread->RequestSuspend(SuspendType::Debug); + if (system.DebuggerEnabled()) { + system.GetDebugger().NotifyThreadStopped(current_thread); + } + current_thread->RequestSuspend(Kernel::SuspendType::Debug); break; } + + // Notify the debugger and go to sleep if a watchpoint was hit. if (Has(hr, watchpoint)) { RewindBreakpointInstruction(); - system.GetDebugger().NotifyThreadWatchpoint(current_thread, *HaltedWatchpoint()); + if (system.DebuggerEnabled()) { + system.GetDebugger().NotifyThreadWatchpoint(current_thread, *HaltedWatchpoint()); + } current_thread->RequestSuspend(SuspendType::Debug); break; } diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index 4e431e27a..8a066ed91 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h @@ -204,6 +204,7 @@ public: static constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3; static constexpr Dynarmic::HaltReason breakpoint = Dynarmic::HaltReason::UserDefined4; static constexpr Dynarmic::HaltReason watchpoint = Dynarmic::HaltReason::UserDefined5; + static constexpr Dynarmic::HaltReason no_execute = Dynarmic::HaltReason::UserDefined6; protected: /// System context that this ARM interface is running under. diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index 8c90c8be0..1be5fe1c1 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp @@ -48,6 +48,12 @@ public: CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Read); return memory.Read64(vaddr); } + std::optional<u32> MemoryReadCode(u32 vaddr) override { + if (!memory.IsValidVirtualAddressRange(vaddr, sizeof(u32))) { + return std::nullopt; + } + return MemoryRead32(vaddr); + } void MemoryWrite8(u32 vaddr, u8 value) override { if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) { @@ -89,21 +95,28 @@ public: void InterpreterFallback(u32 pc, std::size_t num_instructions) override { parent.LogBacktrace(); - UNIMPLEMENTED_MSG("This should never happen, pc = {:08X}, code = {:08X}", pc, - MemoryReadCode(pc)); + LOG_ERROR(Core_ARM, + "Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc, + num_instructions, MemoryRead32(pc)); } void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override { - if (debugger_enabled) { - parent.SaveContext(parent.breakpoint_context); - parent.jit.load()->HaltExecution(ARM_Interface::breakpoint); + switch (exception) { + case Dynarmic::A32::Exception::NoExecuteFault: + LOG_CRITICAL(Core_ARM, "Cannot execute instruction at unmapped address {:#08x}", pc); + ReturnException(pc, ARM_Interface::no_execute); return; - } + default: + if (debugger_enabled) { + ReturnException(pc, ARM_Interface::breakpoint); + return; + } - parent.LogBacktrace(); - LOG_CRITICAL(Core_ARM, - "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})", - exception, pc, MemoryReadCode(pc), parent.IsInThumbMode()); + parent.LogBacktrace(); + LOG_CRITICAL(Core_ARM, + "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})", + exception, pc, MemoryRead32(pc), parent.IsInThumbMode()); + } } void CallSVC(u32 swi) override { @@ -141,15 +154,20 @@ public: const auto match{parent.MatchingWatchpoint(addr, size, type)}; if (match) { - parent.SaveContext(parent.breakpoint_context); - parent.jit.load()->HaltExecution(ARM_Interface::watchpoint); parent.halted_watchpoint = match; + ReturnException(parent.jit.load()->Regs()[15], ARM_Interface::watchpoint); return false; } return true; } + void ReturnException(u32 pc, Dynarmic::HaltReason hr) { + parent.SaveContext(parent.breakpoint_context); + parent.breakpoint_context.cpu_registers[15] = pc; + parent.jit.load()->HaltExecution(hr); + } + ARM_Dynarmic_32& parent; Core::Memory::Memory& memory; std::size_t num_interpreted_instructions{}; @@ -409,18 +427,38 @@ void ARM_Dynarmic_32::PageTableChanged(Common::PageTable& page_table, } std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktrace(Core::System& system, - u64 sp, u64 lr) { - // No way to get accurate stack traces in A32 yet - return {}; + u64 fp, u64 lr, u64 pc) { + std::vector<BacktraceEntry> out; + auto& memory = system.Memory(); + + out.push_back({"", 0, pc, 0, ""}); + + // fp (= r11) points to the last frame record. + // Frame records are two words long: + // fp+0 : pointer to previous frame record + // fp+4 : value of lr for frame + while (true) { + out.push_back({"", 0, lr, 0, ""}); + if (!fp || (fp % 4 != 0) || !memory.IsValidVirtualAddressRange(fp, 8)) { + break; + } + lr = memory.Read32(fp + 4); + fp = memory.Read32(fp); + } + + SymbolicateBacktrace(system, out); + + return out; } std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktraceFromContext( System& system, const ThreadContext32& ctx) { - return GetBacktrace(system, ctx.cpu_registers[13], ctx.cpu_registers[14]); + const auto& reg = ctx.cpu_registers; + return GetBacktrace(system, reg[11], reg[14], reg[15]); } std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktrace() const { - return GetBacktrace(system, GetReg(13), GetReg(14)); + return GetBacktrace(system, GetReg(11), GetReg(14), GetReg(15)); } } // namespace Core diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h index fcbe24f0c..346e9abf8 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.h +++ b/src/core/arm/dynarmic/arm_dynarmic_32.h @@ -78,7 +78,7 @@ protected: private: std::shared_ptr<Dynarmic::A32::Jit> MakeJit(Common::PageTable* page_table) const; - static std::vector<BacktraceEntry> GetBacktrace(Core::System& system, u64 sp, u64 lr); + static std::vector<BacktraceEntry> GetBacktrace(Core::System& system, u64 fp, u64 lr, u64 pc); using JitCacheKey = std::pair<Common::PageTable*, std::size_t>; using JitCacheType = diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index 4370ca294..c437f24b8 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp @@ -52,6 +52,12 @@ public: CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Read); return {memory.Read64(vaddr), memory.Read64(vaddr + 8)}; } + std::optional<u32> MemoryReadCode(u64 vaddr) override { + if (!memory.IsValidVirtualAddressRange(vaddr, sizeof(u32))) { + return std::nullopt; + } + return MemoryRead32(vaddr); + } void MemoryWrite8(u64 vaddr, u8 value) override { if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) { @@ -105,7 +111,7 @@ public: parent.LogBacktrace(); LOG_ERROR(Core_ARM, "Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc, - num_instructions, MemoryReadCode(pc)); + num_instructions, MemoryRead32(pc)); } void InstructionCacheOperationRaised(Dynarmic::A64::InstructionCacheOperation op, @@ -138,16 +144,19 @@ public: case Dynarmic::A64::Exception::SendEventLocal: case Dynarmic::A64::Exception::Yield: return; + case Dynarmic::A64::Exception::NoExecuteFault: + LOG_CRITICAL(Core_ARM, "Cannot execute instruction at unmapped address {:#016x}", pc); + ReturnException(pc, ARM_Interface::no_execute); + return; default: if (debugger_enabled) { - parent.SaveContext(parent.breakpoint_context); - parent.jit.load()->HaltExecution(ARM_Interface::breakpoint); + ReturnException(pc, ARM_Interface::breakpoint); return; } parent.LogBacktrace(); - ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})", - static_cast<std::size_t>(exception), pc, MemoryReadCode(pc)); + LOG_CRITICAL(Core_ARM, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})", + static_cast<std::size_t>(exception), pc, MemoryRead32(pc)); } } @@ -188,15 +197,20 @@ public: const auto match{parent.MatchingWatchpoint(addr, size, type)}; if (match) { - parent.SaveContext(parent.breakpoint_context); - parent.jit.load()->HaltExecution(ARM_Interface::watchpoint); parent.halted_watchpoint = match; + ReturnException(parent.jit.load()->GetPC(), ARM_Interface::watchpoint); return false; } return true; } + void ReturnException(u64 pc, Dynarmic::HaltReason hr) { + parent.SaveContext(parent.breakpoint_context); + parent.breakpoint_context.pc = pc; + parent.jit.load()->HaltExecution(hr); + } + ARM_Dynarmic_64& parent; Core::Memory::Memory& memory; u64 tpidrro_el0 = 0; @@ -480,22 +494,22 @@ void ARM_Dynarmic_64::PageTableChanged(Common::PageTable& page_table, } std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktrace(Core::System& system, - u64 fp, u64 lr) { + u64 fp, u64 lr, u64 pc) { std::vector<BacktraceEntry> out; auto& memory = system.Memory(); - // fp (= r29) points to the last frame record. - // Note that this is the frame record for the *previous* frame, not the current one. - // Note we need to subtract 4 from our last read to get the proper address + out.push_back({"", 0, pc, 0, ""}); + + // fp (= x29) points to the previous frame record. // Frame records are two words long: // fp+0 : pointer to previous frame record // fp+8 : value of lr for frame while (true) { out.push_back({"", 0, lr, 0, ""}); - if (!fp) { + if (!fp || (fp % 4 != 0) || !memory.IsValidVirtualAddressRange(fp, 16)) { break; } - lr = memory.Read64(fp + 8) - 4; + lr = memory.Read64(fp + 8); fp = memory.Read64(fp); } @@ -506,11 +520,12 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktrace(Core::S std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktraceFromContext( System& system, const ThreadContext64& ctx) { - return GetBacktrace(system, ctx.cpu_registers[29], ctx.cpu_registers[30]); + const auto& reg = ctx.cpu_registers; + return GetBacktrace(system, reg[29], reg[30], ctx.pc); } std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktrace() const { - return GetBacktrace(system, GetReg(29), GetReg(30)); + return GetBacktrace(system, GetReg(29), GetReg(30), GetPC()); } } // namespace Core diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h index 71dbaac5e..c77a83ad7 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.h +++ b/src/core/arm/dynarmic/arm_dynarmic_64.h @@ -73,7 +73,7 @@ private: std::shared_ptr<Dynarmic::A64::Jit> MakeJit(Common::PageTable* page_table, std::size_t address_space_bits) const; - static std::vector<BacktraceEntry> GetBacktrace(Core::System& system, u64 fp, u64 lr); + static std::vector<BacktraceEntry> GetBacktrace(Core::System& system, u64 fp, u64 lr, u64 pc); using JitCacheKey = std::pair<Common::PageTable*, std::size_t>; using JitCacheType = diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index 29e7dba9b..5425637f5 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -6,7 +6,9 @@ #include <string> #include <tuple> +#include "common/logging/log.h" #include "common/microprofile.h" +#include "common/thread.h" #include "core/core_timing.h" #include "core/core_timing_util.h" #include "core/hardware_properties.h" @@ -20,10 +22,11 @@ std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callbac } struct CoreTiming::Event { - u64 time; + s64 time; u64 fifo_order; std::uintptr_t user_data; std::weak_ptr<EventType> type; + s64 reschedule_time; // Sort by time, unless the times are the same, in which case sort by // the order added to the queue @@ -41,11 +44,11 @@ CoreTiming::CoreTiming() CoreTiming::~CoreTiming() = default; -void CoreTiming::ThreadEntry(CoreTiming& instance) { - constexpr char name[] = "yuzu:HostTiming"; - MicroProfileOnThreadCreate(name); - Common::SetCurrentThreadName(name); - Common::SetCurrentThreadPriority(Common::ThreadPriority::VeryHigh); +void CoreTiming::ThreadEntry(CoreTiming& instance, size_t id) { + const std::string name = "yuzu:HostTiming_" + std::to_string(id); + MicroProfileOnThreadCreate(name.c_str()); + Common::SetCurrentThreadName(name.c_str()); + Common::SetCurrentThreadPriority(Common::ThreadPriority::Critical); instance.on_thread_init(); instance.ThreadLoop(); MicroProfileOnThreadExit(); @@ -56,71 +59,131 @@ void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) { event_fifo_id = 0; shutting_down = false; ticks = 0; - const auto empty_timed_callback = [](std::uintptr_t, std::chrono::nanoseconds) {}; + const auto empty_timed_callback = [](std::uintptr_t, u64, std::chrono::nanoseconds) + -> std::optional<std::chrono::nanoseconds> { return std::nullopt; }; ev_lost = CreateEvent("_lost_event", empty_timed_callback); if (is_multicore) { - timer_thread = std::make_unique<std::thread>(ThreadEntry, std::ref(*this)); + worker_threads.emplace_back(ThreadEntry, std::ref(*this), 0); } } void CoreTiming::Shutdown() { - paused = true; + is_paused = true; shutting_down = true; - pause_event.Set(); - event.Set(); - if (timer_thread) { - timer_thread->join(); + std::atomic_thread_fence(std::memory_order_release); + + event_cv.notify_all(); + wait_pause_cv.notify_all(); + for (auto& thread : worker_threads) { + thread.join(); } + worker_threads.clear(); + pause_callbacks.clear(); ClearPendingEvents(); - timer_thread.reset(); has_started = false; } -void CoreTiming::Pause(bool is_paused) { - paused = is_paused; - pause_event.Set(); +void CoreTiming::Pause(bool is_paused_) { + std::unique_lock main_lock(event_mutex); + if (is_paused_ == paused_state.load(std::memory_order_relaxed)) { + return; + } + if (is_multicore) { + is_paused = is_paused_; + event_cv.notify_all(); + if (!is_paused_) { + wait_pause_cv.notify_all(); + } + } + paused_state.store(is_paused_, std::memory_order_relaxed); + + if (!is_paused_) { + pause_end_time = GetGlobalTimeNs().count(); + } + + for (auto& cb : pause_callbacks) { + cb(is_paused_); + } } -void CoreTiming::SyncPause(bool is_paused) { - if (is_paused == paused && paused_set == paused) { +void CoreTiming::SyncPause(bool is_paused_) { + std::unique_lock main_lock(event_mutex); + if (is_paused_ == paused_state.load(std::memory_order_relaxed)) { return; } - Pause(is_paused); - if (timer_thread) { - if (!is_paused) { - pause_event.Set(); + + if (is_multicore) { + is_paused = is_paused_; + event_cv.notify_all(); + if (!is_paused_) { + wait_pause_cv.notify_all(); + } + } + paused_state.store(is_paused_, std::memory_order_relaxed); + if (is_multicore) { + if (is_paused_) { + wait_signal_cv.wait(main_lock, [this] { return pause_count == worker_threads.size(); }); + } else { + wait_signal_cv.wait(main_lock, [this] { return pause_count == 0; }); } - event.Set(); - while (paused_set != is_paused) - ; + } + + if (!is_paused_) { + pause_end_time = GetGlobalTimeNs().count(); + } + + for (auto& cb : pause_callbacks) { + cb(is_paused_); } } bool CoreTiming::IsRunning() const { - return !paused_set; + return !paused_state.load(std::memory_order_acquire); } bool CoreTiming::HasPendingEvents() const { - return !(wait_set && event_queue.empty()); + std::unique_lock main_lock(event_mutex); + return !event_queue.empty() || pending_events.load(std::memory_order_relaxed) != 0; } void CoreTiming::ScheduleEvent(std::chrono::nanoseconds ns_into_future, const std::shared_ptr<EventType>& event_type, - std::uintptr_t user_data) { - { - std::scoped_lock scope{basic_lock}; - const u64 timeout = static_cast<u64>((GetGlobalTimeNs() + ns_into_future).count()); + std::uintptr_t user_data, bool absolute_time) { + + std::unique_lock main_lock(event_mutex); + const auto next_time{absolute_time ? ns_into_future : GetGlobalTimeNs() + ns_into_future}; - event_queue.emplace_back(Event{timeout, event_fifo_id++, user_data, event_type}); + event_queue.emplace_back(Event{next_time.count(), event_fifo_id++, user_data, event_type, 0}); + pending_events.fetch_add(1, std::memory_order_relaxed); - std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); + std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); + + if (is_multicore) { + event_cv.notify_one(); + } +} + +void CoreTiming::ScheduleLoopingEvent(std::chrono::nanoseconds start_time, + std::chrono::nanoseconds resched_time, + const std::shared_ptr<EventType>& event_type, + std::uintptr_t user_data, bool absolute_time) { + std::unique_lock main_lock(event_mutex); + const auto next_time{absolute_time ? start_time : GetGlobalTimeNs() + start_time}; + + event_queue.emplace_back( + Event{next_time.count(), event_fifo_id++, user_data, event_type, resched_time.count()}); + pending_events.fetch_add(1, std::memory_order_relaxed); + + std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); + + if (is_multicore) { + event_cv.notify_one(); } - event.Set(); } void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data) { - std::scoped_lock scope{basic_lock}; + std::unique_lock main_lock(event_mutex); const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { return e.type.lock().get() == event_type.get() && e.user_data == user_data; }); @@ -129,6 +192,7 @@ void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, if (itr != event_queue.end()) { event_queue.erase(itr, event_queue.end()); std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>()); + pending_events.fetch_sub(1, std::memory_order_relaxed); } } @@ -168,11 +232,12 @@ u64 CoreTiming::GetClockTicks() const { } void CoreTiming::ClearPendingEvents() { + std::unique_lock main_lock(event_mutex); event_queue.clear(); } void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) { - std::scoped_lock lock{basic_lock}; + std::unique_lock main_lock(event_mutex); const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { return e.type.lock().get() == event_type.get(); @@ -185,22 +250,48 @@ void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) { } } +void CoreTiming::RegisterPauseCallback(PauseCallback&& callback) { + std::unique_lock main_lock(event_mutex); + pause_callbacks.emplace_back(std::move(callback)); +} + std::optional<s64> CoreTiming::Advance() { - std::scoped_lock lock{advance_lock, basic_lock}; global_timer = GetGlobalTimeNs().count(); + std::unique_lock main_lock(event_mutex); while (!event_queue.empty() && event_queue.front().time <= global_timer) { Event evt = std::move(event_queue.front()); std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>()); event_queue.pop_back(); - basic_lock.unlock(); if (const auto event_type{evt.type.lock()}) { - event_type->callback( - evt.user_data, std::chrono::nanoseconds{static_cast<s64>(global_timer - evt.time)}); + event_mutex.unlock(); + + const auto new_schedule_time{event_type->callback( + evt.user_data, evt.time, + std::chrono::nanoseconds{GetGlobalTimeNs().count() - evt.time})}; + + event_mutex.lock(); + pending_events.fetch_sub(1, std::memory_order_relaxed); + + if (evt.reschedule_time != 0) { + // If this event was scheduled into a pause, its time now is going to be way behind. + // Re-set this event to continue from the end of the pause. + auto next_time{evt.time + evt.reschedule_time}; + if (evt.time < pause_end_time) { + next_time = pause_end_time + evt.reschedule_time; + } + + const auto next_schedule_time{new_schedule_time.has_value() + ? new_schedule_time.value().count() + : evt.reschedule_time}; + event_queue.emplace_back( + Event{next_time, event_fifo_id++, evt.user_data, evt.type, next_schedule_time}); + pending_events.fetch_add(1, std::memory_order_relaxed); + std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); + } } - basic_lock.lock(); global_timer = GetGlobalTimeNs().count(); } @@ -213,26 +304,34 @@ std::optional<s64> CoreTiming::Advance() { } void CoreTiming::ThreadLoop() { + const auto predicate = [this] { return !event_queue.empty() || is_paused; }; has_started = true; while (!shutting_down) { - while (!paused) { - paused_set = false; + while (!is_paused && !shutting_down) { const auto next_time = Advance(); if (next_time) { if (*next_time > 0) { std::chrono::nanoseconds next_time_ns = std::chrono::nanoseconds(*next_time); - event.WaitFor(next_time_ns); + std::unique_lock main_lock(event_mutex); + event_cv.wait_for(main_lock, next_time_ns, predicate); } } else { - wait_set = true; - event.Wait(); + std::unique_lock main_lock(event_mutex); + event_cv.wait(main_lock, predicate); } - wait_set = false; } - paused_set = true; - clock->Pause(true); - pause_event.Wait(); - clock->Pause(false); + std::unique_lock main_lock(event_mutex); + pause_count++; + if (pause_count == worker_threads.size()) { + clock->Pause(true); + wait_signal_cv.notify_all(); + } + wait_pause_cv.wait(main_lock, [this] { return !is_paused || shutting_down; }); + pause_count--; + if (pause_count == 0) { + clock->Pause(false); + wait_signal_cv.notify_all(); + } } } diff --git a/src/core/core_timing.h b/src/core/core_timing.h index d27773009..09b6ed81a 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h @@ -5,6 +5,7 @@ #include <atomic> #include <chrono> +#include <condition_variable> #include <functional> #include <memory> #include <mutex> @@ -14,14 +15,14 @@ #include <vector> #include "common/common_types.h" -#include "common/thread.h" #include "common/wall_clock.h" namespace Core::Timing { /// A callback that may be scheduled for a particular core timing event. -using TimedCallback = - std::function<void(std::uintptr_t user_data, std::chrono::nanoseconds ns_late)>; +using TimedCallback = std::function<std::optional<std::chrono::nanoseconds>( + std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late)>; +using PauseCallback = std::function<void(bool paused)>; /// Contains the characteristics of a particular event. struct EventType { @@ -93,7 +94,15 @@ public: /// Schedules an event in core timing void ScheduleEvent(std::chrono::nanoseconds ns_into_future, - const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data = 0); + const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data = 0, + bool absolute_time = false); + + /// Schedules an event which will automatically re-schedule itself with the given time, until + /// unscheduled + void ScheduleLoopingEvent(std::chrono::nanoseconds start_time, + std::chrono::nanoseconds resched_time, + const std::shared_ptr<EventType>& event_type, + std::uintptr_t user_data = 0, bool absolute_time = false); void UnscheduleEvent(const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data); @@ -125,18 +134,21 @@ public: /// Checks for events manually and returns time in nanoseconds for next event, threadsafe. std::optional<s64> Advance(); + /// Register a callback function to be called when coretiming pauses. + void RegisterPauseCallback(PauseCallback&& callback); + private: struct Event; /// Clear all pending events. This should ONLY be done on exit. void ClearPendingEvents(); - static void ThreadEntry(CoreTiming& instance); + static void ThreadEntry(CoreTiming& instance, size_t id); void ThreadLoop(); std::unique_ptr<Common::WallClock> clock; - u64 global_timer = 0; + s64 global_timer = 0; // The queue is a min-heap using std::make_heap/push_heap/pop_heap. // We don't use std::priority_queue because we need to be able to serialize, unserialize and @@ -144,25 +156,31 @@ private: // accomodated by the standard adaptor class. std::vector<Event> event_queue; u64 event_fifo_id = 0; + std::atomic<size_t> pending_events{}; std::shared_ptr<EventType> ev_lost; - Common::Event event{}; - Common::Event pause_event{}; - std::mutex basic_lock; - std::mutex advance_lock; - std::unique_ptr<std::thread> timer_thread; - std::atomic<bool> paused{}; - std::atomic<bool> paused_set{}; - std::atomic<bool> wait_set{}; - std::atomic<bool> shutting_down{}; std::atomic<bool> has_started{}; std::function<void()> on_thread_init{}; + std::vector<std::thread> worker_threads; + + std::condition_variable event_cv; + std::condition_variable wait_pause_cv; + std::condition_variable wait_signal_cv; + mutable std::mutex event_mutex; + + std::atomic<bool> paused_state{}; + bool is_paused{}; + bool shutting_down{}; bool is_multicore{}; + size_t pause_count{}; + s64 pause_end_time{}; /// Cycle timing u64 ticks{}; s64 downcount{}; + + std::vector<PauseCallback> pause_callbacks{}; }; /// Creates a core timing event with the given name and callback. diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp index fd6928105..37d3d83b9 100644 --- a/src/core/cpu_manager.cpp +++ b/src/core/cpu_manager.cpp @@ -41,51 +41,32 @@ void CpuManager::Shutdown() { } } -std::function<void(void*)> CpuManager::GetGuestThreadStartFunc() { - return GuestThreadFunction; -} - -std::function<void(void*)> CpuManager::GetIdleThreadStartFunc() { - return IdleThreadFunction; -} - -std::function<void(void*)> CpuManager::GetShutdownThreadStartFunc() { - return ShutdownThreadFunction; -} - -void CpuManager::GuestThreadFunction(void* cpu_manager_) { - CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_); - if (cpu_manager->is_multicore) { - cpu_manager->MultiCoreRunGuestThread(); +void CpuManager::GuestThreadFunction() { + if (is_multicore) { + MultiCoreRunGuestThread(); } else { - cpu_manager->SingleCoreRunGuestThread(); + SingleCoreRunGuestThread(); } } -void CpuManager::GuestRewindFunction(void* cpu_manager_) { - CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_); - if (cpu_manager->is_multicore) { - cpu_manager->MultiCoreRunGuestLoop(); +void CpuManager::GuestRewindFunction() { + if (is_multicore) { + MultiCoreRunGuestLoop(); } else { - cpu_manager->SingleCoreRunGuestLoop(); + SingleCoreRunGuestLoop(); } } -void CpuManager::IdleThreadFunction(void* cpu_manager_) { - CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_); - if (cpu_manager->is_multicore) { - cpu_manager->MultiCoreRunIdleThread(); +void CpuManager::IdleThreadFunction() { + if (is_multicore) { + MultiCoreRunIdleThread(); } else { - cpu_manager->SingleCoreRunIdleThread(); + SingleCoreRunIdleThread(); } } -void CpuManager::ShutdownThreadFunction(void* cpu_manager) { - static_cast<CpuManager*>(cpu_manager)->ShutdownThread(); -} - -void* CpuManager::GetStartFuncParameter() { - return this; +void CpuManager::ShutdownThreadFunction() { + ShutdownThread(); } /////////////////////////////////////////////////////////////////////////////// @@ -97,7 +78,7 @@ void CpuManager::MultiCoreRunGuestThread() { kernel.CurrentScheduler()->OnThreadStart(); auto* thread = kernel.CurrentScheduler()->GetSchedulerCurrentThread(); auto& host_context = thread->GetHostContext(); - host_context->SetRewindPoint(GuestRewindFunction, this); + host_context->SetRewindPoint([this] { GuestRewindFunction(); }); MultiCoreRunGuestLoop(); } @@ -134,7 +115,7 @@ void CpuManager::SingleCoreRunGuestThread() { kernel.CurrentScheduler()->OnThreadStart(); auto* thread = kernel.CurrentScheduler()->GetSchedulerCurrentThread(); auto& host_context = thread->GetHostContext(); - host_context->SetRewindPoint(GuestRewindFunction, this); + host_context->SetRewindPoint([this] { GuestRewindFunction(); }); SingleCoreRunGuestLoop(); } @@ -194,7 +175,9 @@ void CpuManager::PreemptSingleCore(bool from_running_enviroment) { { auto& scheduler = system.Kernel().Scheduler(current_core); scheduler.Reload(scheduler.GetSchedulerCurrentThread()); - idle_count = 0; + if (!scheduler.IsIdle()) { + idle_count = 0; + } } } diff --git a/src/core/cpu_manager.h b/src/core/cpu_manager.h index f0751fc58..76dc58ee1 100644 --- a/src/core/cpu_manager.h +++ b/src/core/cpu_manager.h @@ -50,10 +50,15 @@ public: void Initialize(); void Shutdown(); - static std::function<void(void*)> GetGuestThreadStartFunc(); - static std::function<void(void*)> GetIdleThreadStartFunc(); - static std::function<void(void*)> GetShutdownThreadStartFunc(); - void* GetStartFuncParameter(); + std::function<void()> GetGuestThreadStartFunc() { + return [this] { GuestThreadFunction(); }; + } + std::function<void()> GetIdleThreadStartFunc() { + return [this] { IdleThreadFunction(); }; + } + std::function<void()> GetShutdownThreadStartFunc() { + return [this] { ShutdownThreadFunction(); }; + } void PreemptSingleCore(bool from_running_enviroment = true); @@ -62,10 +67,10 @@ public: } private: - static void GuestThreadFunction(void* cpu_manager); - static void GuestRewindFunction(void* cpu_manager); - static void IdleThreadFunction(void* cpu_manager); - static void ShutdownThreadFunction(void* cpu_manager); + void GuestThreadFunction(); + void GuestRewindFunction(); + void IdleThreadFunction(); + void ShutdownThreadFunction(); void MultiCoreRunGuestThread(); void MultiCoreRunGuestLoop(); diff --git a/src/core/debugger/gdbstub_arch.cpp b/src/core/debugger/gdbstub_arch.cpp index 750c353b9..4bef09bd7 100644 --- a/src/core/debugger/gdbstub_arch.cpp +++ b/src/core/debugger/gdbstub_arch.cpp @@ -191,8 +191,10 @@ std::string GDBStubA64::RegRead(const Kernel::KThread* thread, size_t id) const const auto& gprs{context.cpu_registers}; const auto& fprs{context.vector_registers}; - if (id <= SP_REGISTER) { + if (id < SP_REGISTER) { return ValueToHex(gprs[id]); + } else if (id == SP_REGISTER) { + return ValueToHex(context.sp); } else if (id == PC_REGISTER) { return ValueToHex(context.pc); } else if (id == PSTATE_REGISTER) { @@ -215,8 +217,10 @@ void GDBStubA64::RegWrite(Kernel::KThread* thread, size_t id, std::string_view v auto& context{thread->GetContext64()}; - if (id <= SP_REGISTER) { + if (id < SP_REGISTER) { context.cpu_registers[id] = HexToValue<u64>(value); + } else if (id == SP_REGISTER) { + context.sp = HexToValue<u64>(value); } else if (id == PC_REGISTER) { context.pc = HexToValue<u64>(value); } else if (id == PSTATE_REGISTER) { diff --git a/src/core/hardware_interrupt_manager.cpp b/src/core/hardware_interrupt_manager.cpp index d2d968a76..d08cc3315 100644 --- a/src/core/hardware_interrupt_manager.cpp +++ b/src/core/hardware_interrupt_manager.cpp @@ -11,11 +11,14 @@ namespace Core::Hardware { InterruptManager::InterruptManager(Core::System& system_in) : system(system_in) { gpu_interrupt_event = Core::Timing::CreateEvent( - "GPUInterrupt", [this](std::uintptr_t message, std::chrono::nanoseconds) { + "GPUInterrupt", + [this](std::uintptr_t message, u64 time, + std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> { auto nvdrv = system.ServiceManager().GetService<Service::Nvidia::NVDRV>("nvdrv"); const u32 syncpt = static_cast<u32>(message >> 32); const u32 value = static_cast<u32>(message); nvdrv->SignalGPUInterruptSyncpt(syncpt, value); + return std::nullopt; }); } diff --git a/src/core/hid/hid_types.h b/src/core/hid/hid_types.h index 9f76f9bcb..e49223016 100644 --- a/src/core/hid/hid_types.h +++ b/src/core/hid/hid_types.h @@ -272,6 +272,7 @@ enum class VibrationDeviceType : u32 { Unknown = 0, LinearResonantActuator = 1, GcErm = 2, + N64 = 3, }; // This is nn::hid::VibrationGcErmCommand diff --git a/src/core/hle/kernel/k_code_memory.h b/src/core/hle/kernel/k_code_memory.h index 2410f74a3..2e7e1436a 100644 --- a/src/core/hle/kernel/k_code_memory.h +++ b/src/core/hle/kernel/k_code_memory.h @@ -30,19 +30,19 @@ public: explicit KCodeMemory(KernelCore& kernel_); Result Initialize(Core::DeviceMemory& device_memory, VAddr address, size_t size); - void Finalize(); + void Finalize() override; Result Map(VAddr address, size_t size); Result Unmap(VAddr address, size_t size); Result MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm); Result UnmapFromOwner(VAddr address, size_t size); - bool IsInitialized() const { + bool IsInitialized() const override { return m_is_initialized; } static void PostDestroy([[maybe_unused]] uintptr_t arg) {} - KProcess* GetOwner() const { + KProcess* GetOwner() const override { return m_owner; } VAddr GetSourceAddress() const { diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp index 183c693e3..b662788b3 100644 --- a/src/core/hle/kernel/k_process.cpp +++ b/src/core/hle/kernel/k_process.cpp @@ -161,7 +161,7 @@ bool KProcess::ReleaseUserException(KThread* thread) { std::addressof(num_waiters), reinterpret_cast<uintptr_t>(std::addressof(exception_thread))); next != nullptr) { - next->SetState(ThreadState::Runnable); + next->EndWait(ResultSuccess); } KScheduler::SetSchedulerUpdateNeeded(kernel); diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp index d586b3f5c..d599d2bcb 100644 --- a/src/core/hle/kernel/k_scheduler.cpp +++ b/src/core/hle/kernel/k_scheduler.cpp @@ -622,7 +622,7 @@ void KScheduler::YieldToAnyThread(KernelCore& kernel) { } KScheduler::KScheduler(Core::System& system_, s32 core_id_) : system{system_}, core_id{core_id_} { - switch_fiber = std::make_shared<Common::Fiber>(OnSwitch, this); + switch_fiber = std::make_shared<Common::Fiber>([this] { SwitchToCurrent(); }); state.needs_scheduling.store(true); state.interrupt_task_thread_runnable = false; state.should_count_idle = false; @@ -778,11 +778,6 @@ void KScheduler::ScheduleImpl() { next_scheduler.SwitchContextStep2(); } -void KScheduler::OnSwitch(void* this_scheduler) { - KScheduler* sched = static_cast<KScheduler*>(this_scheduler); - sched->SwitchToCurrent(); -} - void KScheduler::SwitchToCurrent() { while (true) { { diff --git a/src/core/hle/kernel/k_scheduler.h b/src/core/hle/kernel/k_scheduler.h index 3f90656ee..6a4760eca 100644 --- a/src/core/hle/kernel/k_scheduler.h +++ b/src/core/hle/kernel/k_scheduler.h @@ -55,6 +55,11 @@ public: return idle_thread; } + /// Returns true if the scheduler is idle + [[nodiscard]] bool IsIdle() const { + return GetSchedulerCurrentThread() == idle_thread; + } + /// Gets the timestamp for the last context switch in ticks. [[nodiscard]] u64 GetLastContextSwitchTicks() const; @@ -165,7 +170,6 @@ private: */ void UpdateLastContextSwitchTime(KThread* thread, KProcess* process); - static void OnSwitch(void* this_scheduler); void SwitchToCurrent(); KThread* prev_thread{}; diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp index 8d7faa662..90de86770 100644 --- a/src/core/hle/kernel/k_thread.cpp +++ b/src/core/hle/kernel/k_thread.cpp @@ -246,14 +246,12 @@ Result KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_stack Result KThread::InitializeThread(KThread* thread, KThreadFunction func, uintptr_t arg, VAddr user_stack_top, s32 prio, s32 core, KProcess* owner, - ThreadType type, std::function<void(void*)>&& init_func, - void* init_func_parameter) { + ThreadType type, std::function<void()>&& init_func) { // Initialize the thread. R_TRY(thread->Initialize(func, arg, user_stack_top, prio, core, owner, type)); // Initialize emulation parameters. - thread->host_context = - std::make_shared<Common::Fiber>(std::move(init_func), init_func_parameter); + thread->host_context = std::make_shared<Common::Fiber>(std::move(init_func)); thread->is_single_core = !Settings::values.use_multi_core.GetValue(); return ResultSuccess; @@ -265,15 +263,13 @@ Result KThread::InitializeDummyThread(KThread* thread) { Result KThread::InitializeIdleThread(Core::System& system, KThread* thread, s32 virt_core) { return InitializeThread(thread, {}, {}, {}, IdleThreadPriority, virt_core, {}, ThreadType::Main, - Core::CpuManager::GetIdleThreadStartFunc(), - system.GetCpuManager().GetStartFuncParameter()); + system.GetCpuManager().GetIdleThreadStartFunc()); } Result KThread::InitializeHighPriorityThread(Core::System& system, KThread* thread, KThreadFunction func, uintptr_t arg, s32 virt_core) { return InitializeThread(thread, func, arg, {}, {}, virt_core, nullptr, ThreadType::HighPriority, - Core::CpuManager::GetShutdownThreadStartFunc(), - system.GetCpuManager().GetStartFuncParameter()); + system.GetCpuManager().GetShutdownThreadStartFunc()); } Result KThread::InitializeUserThread(Core::System& system, KThread* thread, KThreadFunction func, @@ -281,8 +277,7 @@ Result KThread::InitializeUserThread(Core::System& system, KThread* thread, KThr KProcess* owner) { system.Kernel().GlobalSchedulerContext().AddThread(thread); return InitializeThread(thread, func, arg, user_stack_top, prio, virt_core, owner, - ThreadType::User, Core::CpuManager::GetGuestThreadStartFunc(), - system.GetCpuManager().GetStartFuncParameter()); + ThreadType::User, system.GetCpuManager().GetGuestThreadStartFunc()); } void KThread::PostDestroy(uintptr_t arg) { @@ -313,14 +308,20 @@ void KThread::Finalize() { auto it = waiter_list.begin(); while (it != waiter_list.end()) { - // Clear the lock owner - it->SetLockOwner(nullptr); + // Get the thread. + KThread* const waiter = std::addressof(*it); + + // The thread shouldn't be a kernel waiter. + ASSERT(!IsKernelAddressKey(waiter->GetAddressKey())); + + // Clear the lock owner. + waiter->SetLockOwner(nullptr); // Erase the waiter from our list. it = waiter_list.erase(it); // Cancel the thread's wait. - it->CancelWait(ResultInvalidState, true); + waiter->CancelWait(ResultInvalidState, true); } } @@ -485,9 +486,7 @@ void KThread::Unpin() { // Resume any threads that began waiting on us while we were pinned. for (auto it = pinned_waiter_list.begin(); it != pinned_waiter_list.end(); ++it) { - if (it->GetState() == ThreadState::Waiting) { - it->SetState(ThreadState::Runnable); - } + it->EndWait(ResultSuccess); } } @@ -882,6 +881,7 @@ void KThread::AddWaiterImpl(KThread* thread) { // Keep track of how many kernel waiters we have. if (IsKernelAddressKey(thread->GetAddressKey())) { ASSERT((num_kernel_waiters++) >= 0); + KScheduler::SetSchedulerUpdateNeeded(kernel); } // Insert the waiter. @@ -895,6 +895,7 @@ void KThread::RemoveWaiterImpl(KThread* thread) { // Keep track of how many kernel waiters we have. if (IsKernelAddressKey(thread->GetAddressKey())) { ASSERT((num_kernel_waiters--) > 0); + KScheduler::SetSchedulerUpdateNeeded(kernel); } // Remove the waiter. @@ -970,6 +971,7 @@ KThread* KThread::RemoveWaiterByKey(s32* out_num_waiters, VAddr key) { // Keep track of how many kernel waiters we have. if (IsKernelAddressKey(thread->GetAddressKey())) { ASSERT((num_kernel_waiters--) > 0); + KScheduler::SetSchedulerUpdateNeeded(kernel); } it = waiter_list.erase(it); diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h index 94c4cd1c8..28cd7ecb0 100644 --- a/src/core/hle/kernel/k_thread.h +++ b/src/core/hle/kernel/k_thread.h @@ -729,8 +729,7 @@ private: [[nodiscard]] static Result InitializeThread(KThread* thread, KThreadFunction func, uintptr_t arg, VAddr user_stack_top, s32 prio, s32 core, KProcess* owner, ThreadType type, - std::function<void(void*)>&& init_func, - void* init_func_parameter); + std::function<void()>&& init_func); static void RestorePriority(KernelCore& kernel_ctx, KThread* thread); diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 0009193be..7307cf262 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -234,17 +234,18 @@ struct KernelCore::Impl { void InitializePreemption(KernelCore& kernel) { preemption_event = Core::Timing::CreateEvent( - "PreemptionCallback", [this, &kernel](std::uintptr_t, std::chrono::nanoseconds) { + "PreemptionCallback", + [this, &kernel](std::uintptr_t, s64 time, + std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> { { KScopedSchedulerLock lock(kernel); global_scheduler_context->PreemptThreads(); } - const auto time_interval = std::chrono::nanoseconds{std::chrono::milliseconds(10)}; - system.CoreTiming().ScheduleEvent(time_interval, preemption_event); + return std::nullopt; }); const auto time_interval = std::chrono::nanoseconds{std::chrono::milliseconds(10)}; - system.CoreTiming().ScheduleEvent(time_interval, preemption_event); + system.CoreTiming().ScheduleLoopingEvent(time_interval, time_interval, preemption_event); } void InitializeShutdownThreads() { diff --git a/src/core/hle/kernel/time_manager.cpp b/src/core/hle/kernel/time_manager.cpp index 2724c3782..5ee72c432 100644 --- a/src/core/hle/kernel/time_manager.cpp +++ b/src/core/hle/kernel/time_manager.cpp @@ -11,15 +11,17 @@ namespace Kernel { TimeManager::TimeManager(Core::System& system_) : system{system_} { - time_manager_event_type = - Core::Timing::CreateEvent("Kernel::TimeManagerCallback", - [this](std::uintptr_t thread_handle, std::chrono::nanoseconds) { - KThread* thread = reinterpret_cast<KThread*>(thread_handle); - { - KScopedSchedulerLock sl(system.Kernel()); - thread->OnTimer(); - } - }); + 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) { diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index c08b0a5dc..3c28dee76 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -49,28 +49,41 @@ bool Controller_NPad::IsNpadIdValid(Core::HID::NpadIdType npad_id) { } } -bool Controller_NPad::IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle) { +Result Controller_NPad::IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle) { const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id)); const bool npad_type = device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType; const bool device_index = device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex; - return npad_id && npad_type && device_index; + + if (!npad_type) { + return VibrationInvalidStyleIndex; + } + if (!npad_id) { + return VibrationInvalidNpadId; + } + if (!device_index) { + return VibrationDeviceIndexOutOfRange; + } + + return ResultSuccess; } Result Controller_NPad::VerifyValidSixAxisSensorHandle( const Core::HID::SixAxisSensorHandle& device_handle) { const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id)); + const bool device_index = device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex; + const bool npad_type = device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType; + if (!npad_id) { return InvalidNpadId; } - const bool device_index = device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex; if (!device_index) { return NpadDeviceIndexOutOfRange; } // This doesn't get validated on nnsdk - const bool npad_type = device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType; if (!npad_type) { return NpadInvalidHandle; } + return ResultSuccess; } @@ -705,6 +718,11 @@ Controller_NPad::NpadJoyHoldType Controller_NPad::GetHoldType() const { } void Controller_NPad::SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode) { + if (activation_mode >= NpadHandheldActivationMode::MaxActivationMode) { + ASSERT_MSG(false, "Activation mode should be always None, Single or Dual"); + return; + } + handheld_activation_mode = activation_mode; } @@ -820,11 +838,11 @@ bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, const auto now = steady_clock::now(); - // Filter out non-zero vibrations that are within 10ms of each other. + // Filter out non-zero vibrations that are within 15ms of each other. if ((vibration_value.low_amplitude != 0.0f || vibration_value.high_amplitude != 0.0f) && duration_cast<milliseconds>( now - controller.vibration[device_index].last_vibration_timepoint) < - milliseconds(10)) { + milliseconds(15)) { return false; } @@ -840,7 +858,7 @@ bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, void Controller_NPad::VibrateController( const Core::HID::VibrationDeviceHandle& vibration_device_handle, const Core::HID::VibrationValue& vibration_value) { - if (!IsDeviceHandleValid(vibration_device_handle)) { + if (IsDeviceHandleValid(vibration_device_handle).IsError()) { return; } @@ -903,7 +921,7 @@ void Controller_NPad::VibrateControllers( Core::HID::VibrationValue Controller_NPad::GetLastVibration( const Core::HID::VibrationDeviceHandle& vibration_device_handle) const { - if (!IsDeviceHandleValid(vibration_device_handle)) { + if (IsDeviceHandleValid(vibration_device_handle).IsError()) { return {}; } @@ -914,7 +932,7 @@ Core::HID::VibrationValue Controller_NPad::GetLastVibration( void Controller_NPad::InitializeVibrationDevice( const Core::HID::VibrationDeviceHandle& vibration_device_handle) { - if (!IsDeviceHandleValid(vibration_device_handle)) { + if (IsDeviceHandleValid(vibration_device_handle).IsError()) { return; } @@ -941,7 +959,7 @@ void Controller_NPad::SetPermitVibrationSession(bool permit_vibration_session) { bool Controller_NPad::IsVibrationDeviceMounted( const Core::HID::VibrationDeviceHandle& vibration_device_handle) const { - if (!IsDeviceHandleValid(vibration_device_handle)) { + if (IsDeviceHandleValid(vibration_device_handle).IsError()) { return false; } diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h index 8b54724ed..1a589cca2 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h @@ -81,6 +81,7 @@ public: Dual = 0, Single = 1, None = 2, + MaxActivationMode = 3, }; // This is nn::hid::NpadCommunicationMode @@ -196,7 +197,7 @@ public: Core::HID::NpadButton GetAndResetPressState(); static bool IsNpadIdValid(Core::HID::NpadIdType npad_id); - static bool IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle); + static Result IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle); static Result VerifyValidSixAxisSensorHandle( const Core::HID::SixAxisSensorHandle& device_handle); diff --git a/src/core/hle/service/hid/errors.h b/src/core/hle/service/hid/errors.h index 615c23b84..46282f42e 100644 --- a/src/core/hle/service/hid/errors.h +++ b/src/core/hle/service/hid/errors.h @@ -9,6 +9,9 @@ namespace Service::HID { constexpr Result NpadInvalidHandle{ErrorModule::HID, 100}; constexpr Result NpadDeviceIndexOutOfRange{ErrorModule::HID, 107}; +constexpr Result VibrationInvalidStyleIndex{ErrorModule::HID, 122}; +constexpr Result VibrationInvalidNpadId{ErrorModule::HID, 123}; +constexpr Result VibrationDeviceIndexOutOfRange{ErrorModule::HID, 124}; constexpr Result InvalidSixAxisFusionRange{ErrorModule::HID, 423}; constexpr Result NpadIsDualJoycon{ErrorModule::HID, 601}; constexpr Result NpadIsSameType{ErrorModule::HID, 602}; diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index dc5d0366d..89bb12442 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -74,26 +74,34 @@ IAppletResource::IAppletResource(Core::System& system_, // Register update callbacks pad_update_event = Core::Timing::CreateEvent( "HID::UpdatePadCallback", - [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { + [this](std::uintptr_t user_data, s64 time, + std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { const auto guard = LockService(); UpdateControllers(user_data, ns_late); + return std::nullopt; }); mouse_keyboard_update_event = Core::Timing::CreateEvent( "HID::UpdateMouseKeyboardCallback", - [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { + [this](std::uintptr_t user_data, s64 time, + std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { const auto guard = LockService(); UpdateMouseKeyboard(user_data, ns_late); + return std::nullopt; }); motion_update_event = Core::Timing::CreateEvent( "HID::UpdateMotionCallback", - [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { + [this](std::uintptr_t user_data, s64 time, + std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { const auto guard = LockService(); UpdateMotion(user_data, ns_late); + return std::nullopt; }); - system.CoreTiming().ScheduleEvent(pad_update_ns, pad_update_event); - system.CoreTiming().ScheduleEvent(mouse_keyboard_update_ns, mouse_keyboard_update_event); - system.CoreTiming().ScheduleEvent(motion_update_ns, motion_update_event); + system.CoreTiming().ScheduleLoopingEvent(pad_update_ns, pad_update_ns, pad_update_event); + system.CoreTiming().ScheduleLoopingEvent(mouse_keyboard_update_ns, mouse_keyboard_update_ns, + mouse_keyboard_update_event); + system.CoreTiming().ScheduleLoopingEvent(motion_update_ns, motion_update_ns, + motion_update_event); system.HIDCore().ReloadInputDevices(); } @@ -135,13 +143,6 @@ void IAppletResource::UpdateControllers(std::uintptr_t user_data, } controller->OnUpdate(core_timing); } - - // If ns_late is higher than the update rate ignore the delay - if (ns_late > pad_update_ns) { - ns_late = {}; - } - - core_timing.ScheduleEvent(pad_update_ns - ns_late, pad_update_event); } void IAppletResource::UpdateMouseKeyboard(std::uintptr_t user_data, @@ -150,26 +151,12 @@ void IAppletResource::UpdateMouseKeyboard(std::uintptr_t user_data, controllers[static_cast<size_t>(HidController::Mouse)]->OnUpdate(core_timing); controllers[static_cast<size_t>(HidController::Keyboard)]->OnUpdate(core_timing); - - // If ns_late is higher than the update rate ignore the delay - if (ns_late > mouse_keyboard_update_ns) { - ns_late = {}; - } - - core_timing.ScheduleEvent(mouse_keyboard_update_ns - ns_late, mouse_keyboard_update_event); } void IAppletResource::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { auto& core_timing = system.CoreTiming(); controllers[static_cast<size_t>(HidController::NPad)]->OnMotionUpdate(core_timing); - - // If ns_late is higher than the update rate ignore the delay - if (ns_late > motion_update_ns) { - ns_late = {}; - } - - core_timing.ScheduleEvent(motion_update_ns - ns_late, motion_update_event); } class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> { @@ -778,7 +765,7 @@ void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) { bool is_at_rest{}; auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); - const auto result = controller.IsSixAxisSensorAtRest(parameters.sixaxis_handle, is_at_rest); + controller.IsSixAxisSensorAtRest(parameters.sixaxis_handle, is_at_rest); LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", @@ -786,7 +773,7 @@ void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) { parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(result); + rb.Push(ResultSuccess); rb.Push(is_at_rest); } @@ -803,8 +790,8 @@ void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& c bool is_firmware_available{}; auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); - const auto result = controller.IsFirmwareUpdateAvailableForSixAxisSensor( - parameters.sixaxis_handle, is_firmware_available); + controller.IsFirmwareUpdateAvailableForSixAxisSensor(parameters.sixaxis_handle, + is_firmware_available); LOG_WARNING( Service_HID, @@ -813,7 +800,7 @@ void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& c parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(result); + rb.Push(ResultSuccess); rb.Push(is_firmware_available); } @@ -1083,13 +1070,13 @@ void Hid::DisconnectNpad(Kernel::HLERequestContext& ctx) { const auto parameters{rp.PopRaw<Parameters>()}; auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); - const auto result = controller.DisconnectNpad(parameters.npad_id); + controller.DisconnectNpad(parameters.npad_id); LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); + rb.Push(ResultSuccess); } void Hid::GetPlayerLedPattern(Kernel::HLERequestContext& ctx) { @@ -1165,15 +1152,14 @@ void Hid::SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx const auto parameters{rp.PopRaw<Parameters>()}; auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); - const auto result = - controller.SetNpadMode(parameters.npad_id, Controller_NPad::NpadJoyDeviceType::Left, - Controller_NPad::NpadJoyAssignmentMode::Single); + controller.SetNpadMode(parameters.npad_id, Controller_NPad::NpadJoyDeviceType::Left, + Controller_NPad::NpadJoyAssignmentMode::Single); LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); + rb.Push(ResultSuccess); } void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) { @@ -1189,15 +1175,15 @@ void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) { const auto parameters{rp.PopRaw<Parameters>()}; auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); - const auto result = controller.SetNpadMode(parameters.npad_id, parameters.npad_joy_device_type, - Controller_NPad::NpadJoyAssignmentMode::Single); + controller.SetNpadMode(parameters.npad_id, parameters.npad_joy_device_type, + Controller_NPad::NpadJoyAssignmentMode::Single); LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}", parameters.npad_id, parameters.applet_resource_user_id, parameters.npad_joy_device_type); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); + rb.Push(ResultSuccess); } void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) { @@ -1212,14 +1198,13 @@ void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) { const auto parameters{rp.PopRaw<Parameters>()}; auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); - const auto result = controller.SetNpadMode(parameters.npad_id, {}, - Controller_NPad::NpadJoyAssignmentMode::Dual); + controller.SetNpadMode(parameters.npad_id, {}, Controller_NPad::NpadJoyAssignmentMode::Dual); LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); + rb.Push(ResultSuccess); } void Hid::MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) { @@ -1412,8 +1397,11 @@ void Hid::ClearNpadCaptureButtonAssignment(Kernel::HLERequestContext& ctx) { void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()}; + const auto& controller = + GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); Core::HID::VibrationDeviceInfo vibration_device_info; + bool check_device_index = false; switch (vibration_device_handle.npad_type) { case Core::HID::NpadStyleIndex::ProController: @@ -1421,34 +1409,46 @@ void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) { case Core::HID::NpadStyleIndex::JoyconDual: case Core::HID::NpadStyleIndex::JoyconLeft: case Core::HID::NpadStyleIndex::JoyconRight: - default: vibration_device_info.type = Core::HID::VibrationDeviceType::LinearResonantActuator; + check_device_index = true; break; case Core::HID::NpadStyleIndex::GameCube: vibration_device_info.type = Core::HID::VibrationDeviceType::GcErm; break; - case Core::HID::NpadStyleIndex::Pokeball: + case Core::HID::NpadStyleIndex::N64: + vibration_device_info.type = Core::HID::VibrationDeviceType::N64; + break; + default: vibration_device_info.type = Core::HID::VibrationDeviceType::Unknown; break; } - switch (vibration_device_handle.device_index) { - case Core::HID::DeviceIndex::Left: - vibration_device_info.position = Core::HID::VibrationDevicePosition::Left; - break; - case Core::HID::DeviceIndex::Right: - vibration_device_info.position = Core::HID::VibrationDevicePosition::Right; - break; - case Core::HID::DeviceIndex::None: - default: - ASSERT_MSG(false, "DeviceIndex should never be None!"); - vibration_device_info.position = Core::HID::VibrationDevicePosition::None; - break; + vibration_device_info.position = Core::HID::VibrationDevicePosition::None; + if (check_device_index) { + switch (vibration_device_handle.device_index) { + case Core::HID::DeviceIndex::Left: + vibration_device_info.position = Core::HID::VibrationDevicePosition::Left; + break; + case Core::HID::DeviceIndex::Right: + vibration_device_info.position = Core::HID::VibrationDevicePosition::Right; + break; + case Core::HID::DeviceIndex::None: + default: + ASSERT_MSG(false, "DeviceIndex should never be None!"); + break; + } } LOG_DEBUG(Service_HID, "called, vibration_device_type={}, vibration_device_position={}", vibration_device_info.type, vibration_device_info.position); + const auto result = controller.IsDeviceHandleValid(vibration_device_handle); + if (result.IsError()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); + return; + } + IPC::ResponseBuilder rb{ctx, 4}; rb.Push(ResultSuccess); rb.PushRaw(vibration_device_info); diff --git a/src/core/hle/service/hid/hidbus.cpp b/src/core/hle/service/hid/hidbus.cpp index fa6153b4c..e5e50845f 100644 --- a/src/core/hle/service/hid/hidbus.cpp +++ b/src/core/hle/service/hid/hidbus.cpp @@ -50,12 +50,15 @@ HidBus::HidBus(Core::System& system_) // Register update callbacks hidbus_update_event = Core::Timing::CreateEvent( "Hidbus::UpdateCallback", - [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { + [this](std::uintptr_t user_data, s64 time, + std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { const auto guard = LockService(); UpdateHidbus(user_data, ns_late); + return std::nullopt; }); - system_.CoreTiming().ScheduleEvent(hidbus_update_ns, hidbus_update_event); + system_.CoreTiming().ScheduleLoopingEvent(hidbus_update_ns, hidbus_update_ns, + hidbus_update_event); } HidBus::~HidBus() { @@ -63,8 +66,6 @@ HidBus::~HidBus() { } void HidBus::UpdateHidbus(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { - auto& core_timing = system.CoreTiming(); - if (is_hidbus_enabled) { for (std::size_t i = 0; i < devices.size(); ++i) { if (!devices[i].is_device_initializated) { @@ -82,13 +83,6 @@ void HidBus::UpdateHidbus(std::uintptr_t user_data, std::chrono::nanoseconds ns_ sizeof(HidbusStatusManagerEntry)); } } - - // If ns_late is higher than the update rate ignore the delay - if (ns_late > hidbus_update_ns) { - ns_late = {}; - } - - core_timing.ScheduleEvent(hidbus_update_ns - ns_late, hidbus_update_event); } std::optional<std::size_t> HidBus::GetDeviceIndexFromHandle(BusHandle handle) const { diff --git a/src/core/hle/service/nvflinger/binder.h b/src/core/hle/service/nvflinger/binder.h index 21aaa40cd..157333ff8 100644 --- a/src/core/hle/service/nvflinger/binder.h +++ b/src/core/hle/service/nvflinger/binder.h @@ -34,6 +34,7 @@ enum class TransactionId { class IBinder { public: + virtual ~IBinder() = default; virtual void Transact(Kernel::HLERequestContext& ctx, android::TransactionId code, u32 flags) = 0; virtual Kernel::KReadableEvent& GetNativeHandle() = 0; diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index 2b2985a2d..5f69c8c2c 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp @@ -67,21 +67,20 @@ NVFlinger::NVFlinger(Core::System& system_, HosBinderDriverServer& hos_binder_dr // Schedule the screen composition events composition_event = Core::Timing::CreateEvent( - "ScreenComposition", [this](std::uintptr_t, std::chrono::nanoseconds ns_late) { + "ScreenComposition", + [this](std::uintptr_t, s64 time, + std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { const auto lock_guard = Lock(); Compose(); - const auto ticks = std::chrono::nanoseconds{GetNextTicks()}; - const auto ticks_delta = ticks - ns_late; - const auto future_ns = std::max(std::chrono::nanoseconds::zero(), ticks_delta); - - this->system.CoreTiming().ScheduleEvent(future_ns, composition_event); + return std::max(std::chrono::nanoseconds::zero(), + std::chrono::nanoseconds(GetNextTicks()) - ns_late); }); if (system.IsMulticore()) { vsync_thread = std::jthread([this](std::stop_token token) { SplitVSync(token); }); } else { - system.CoreTiming().ScheduleEvent(frame_ns, composition_event); + system.CoreTiming().ScheduleLoopingEvent(frame_ns, frame_ns, composition_event); } } diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp index 5f71f0ff5..ffdbacc18 100644 --- a/src/core/memory/cheat_engine.cpp +++ b/src/core/memory/cheat_engine.cpp @@ -184,10 +184,12 @@ CheatEngine::~CheatEngine() { void CheatEngine::Initialize() { event = Core::Timing::CreateEvent( "CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id), - [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { + [this](std::uintptr_t user_data, s64 time, + std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { FrameCallback(user_data, ns_late); + return std::nullopt; }); - core_timing.ScheduleEvent(CHEAT_ENGINE_NS, event); + core_timing.ScheduleLoopingEvent(CHEAT_ENGINE_NS, CHEAT_ENGINE_NS, event); metadata.process_id = system.CurrentProcess()->GetProcessID(); metadata.title_id = system.GetCurrentProcessProgramID(); @@ -237,8 +239,6 @@ void CheatEngine::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late MICROPROFILE_SCOPE(Cheat_Engine); vm.Execute(metadata); - - core_timing.ScheduleEvent(CHEAT_ENGINE_NS - ns_late, event); } } // namespace Core::Memory diff --git a/src/core/tools/freezer.cpp b/src/core/tools/freezer.cpp index 5cc99fbe4..98ebbbf32 100644 --- a/src/core/tools/freezer.cpp +++ b/src/core/tools/freezer.cpp @@ -53,8 +53,10 @@ Freezer::Freezer(Core::Timing::CoreTiming& core_timing_, Core::Memory::Memory& m : core_timing{core_timing_}, memory{memory_} { event = Core::Timing::CreateEvent( "MemoryFreezer::FrameCallback", - [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { + [this](std::uintptr_t user_data, s64 time, + std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { FrameCallback(user_data, ns_late); + return std::nullopt; }); core_timing.ScheduleEvent(memory_freezer_ns, event); } |