summaryrefslogtreecommitdiffstats
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/arm/arm_interface.cpp32
-rw-r--r--src/core/arm/arm_interface.h1
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.cpp72
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.h2
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp45
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.h2
-rw-r--r--src/core/core_timing.cpp203
-rw-r--r--src/core/core_timing.h48
-rw-r--r--src/core/cpu_manager.cpp55
-rw-r--r--src/core/cpu_manager.h21
-rw-r--r--src/core/debugger/gdbstub_arch.cpp8
-rw-r--r--src/core/hardware_interrupt_manager.cpp5
-rw-r--r--src/core/hid/hid_types.h1
-rw-r--r--src/core/hle/kernel/k_code_memory.h6
-rw-r--r--src/core/hle/kernel/k_process.cpp2
-rw-r--r--src/core/hle/kernel/k_scheduler.cpp7
-rw-r--r--src/core/hle/kernel/k_scheduler.h6
-rw-r--r--src/core/hle/kernel/k_thread.cpp34
-rw-r--r--src/core/hle/kernel/k_thread.h3
-rw-r--r--src/core/hle/kernel/kernel.cpp9
-rw-r--r--src/core/hle/kernel/time_manager.cpp20
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp38
-rw-r--r--src/core/hle/service/hid/controllers/npad.h3
-rw-r--r--src/core/hle/service/hid/errors.h3
-rw-r--r--src/core/hle/service/hid/hid.cpp116
-rw-r--r--src/core/hle/service/hid/hidbus.cpp16
-rw-r--r--src/core/hle/service/nvflinger/binder.h1
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp13
-rw-r--r--src/core/memory/cheat_engine.cpp8
-rw-r--r--src/core/tools/freezer.cpp4
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);
}