diff options
| author | 2020-02-24 22:04:12 -0400 | |
|---|---|---|
| committer | 2020-06-27 11:35:06 -0400 | |
| commit | e31425df3877636c098ec7426ebd2067920715cb (patch) | |
| tree | 5c0fc518a4ebb8413c491b43a9fdd99450c7bd80 /src | |
| parent | Merge pull request #3396 from FernandoS27/prometheus-1 (diff) | |
| download | yuzu-e31425df3877636c098ec7426ebd2067920715cb.tar.gz yuzu-e31425df3877636c098ec7426ebd2067920715cb.tar.xz yuzu-e31425df3877636c098ec7426ebd2067920715cb.zip | |
General: Recover Prometheus project from harddrive failure
This commit: Implements CPU Interrupts, Replaces Cycle Timing for Host
Timing, Reworks the Kernel's Scheduler, Introduce Idle State and
Suspended State, Recreates the bootmanager, Initializes Multicore
system.
Diffstat (limited to 'src')
57 files changed, 1341 insertions, 816 deletions
diff --git a/src/common/thread.cpp b/src/common/thread.cpp index 0cd2d10bf..c9684aed9 100644 --- a/src/common/thread.cpp +++ b/src/common/thread.cpp | |||
| @@ -70,6 +70,12 @@ void SetCurrentThreadName(const char* name) { | |||
| 70 | } | 70 | } |
| 71 | #endif | 71 | #endif |
| 72 | 72 | ||
| 73 | #if defined(_WIN32) | ||
| 74 | void SetCurrentThreadName(const char* name) { | ||
| 75 | // Do Nothing on MingW | ||
| 76 | } | ||
| 77 | #endif | ||
| 78 | |||
| 73 | #endif | 79 | #endif |
| 74 | 80 | ||
| 75 | } // namespace Common | 81 | } // namespace Common |
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index efbad628f..552094ddb 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -7,6 +7,8 @@ endif() | |||
| 7 | add_library(core STATIC | 7 | add_library(core STATIC |
| 8 | arm/arm_interface.h | 8 | arm/arm_interface.h |
| 9 | arm/arm_interface.cpp | 9 | arm/arm_interface.cpp |
| 10 | arm/cpu_interrupt_handler.cpp | ||
| 11 | arm/cpu_interrupt_handler.h | ||
| 10 | arm/exclusive_monitor.cpp | 12 | arm/exclusive_monitor.cpp |
| 11 | arm/exclusive_monitor.h | 13 | arm/exclusive_monitor.h |
| 12 | arm/unicorn/arm_unicorn.cpp | 14 | arm/unicorn/arm_unicorn.cpp |
| @@ -547,8 +549,6 @@ add_library(core STATIC | |||
| 547 | hle/service/vi/vi_u.h | 549 | hle/service/vi/vi_u.h |
| 548 | hle/service/wlan/wlan.cpp | 550 | hle/service/wlan/wlan.cpp |
| 549 | hle/service/wlan/wlan.h | 551 | hle/service/wlan/wlan.h |
| 550 | host_timing.cpp | ||
| 551 | host_timing.h | ||
| 552 | loader/deconstructed_rom_directory.cpp | 552 | loader/deconstructed_rom_directory.cpp |
| 553 | loader/deconstructed_rom_directory.h | 553 | loader/deconstructed_rom_directory.h |
| 554 | loader/elf.cpp | 554 | loader/elf.cpp |
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index cb2e640e2..87a1c29cc 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h | |||
| @@ -18,11 +18,13 @@ enum class VMAPermission : u8; | |||
| 18 | 18 | ||
| 19 | namespace Core { | 19 | namespace Core { |
| 20 | class System; | 20 | class System; |
| 21 | class CPUInterruptHandler; | ||
| 21 | 22 | ||
| 22 | /// Generic ARMv8 CPU interface | 23 | /// Generic ARMv8 CPU interface |
| 23 | class ARM_Interface : NonCopyable { | 24 | class ARM_Interface : NonCopyable { |
| 24 | public: | 25 | public: |
| 25 | explicit ARM_Interface(System& system_) : system{system_} {} | 26 | explicit ARM_Interface(System& system_, CPUInterruptHandler& interrupt_handler) |
| 27 | : system{system_}, interrupt_handler{interrupt_handler} {} | ||
| 26 | virtual ~ARM_Interface() = default; | 28 | virtual ~ARM_Interface() = default; |
| 27 | 29 | ||
| 28 | struct ThreadContext32 { | 30 | struct ThreadContext32 { |
| @@ -175,6 +177,7 @@ public: | |||
| 175 | protected: | 177 | protected: |
| 176 | /// System context that this ARM interface is running under. | 178 | /// System context that this ARM interface is running under. |
| 177 | System& system; | 179 | System& system; |
| 180 | CPUInterruptHandler& interrupt_handler; | ||
| 178 | }; | 181 | }; |
| 179 | 182 | ||
| 180 | } // namespace Core | 183 | } // namespace Core |
diff --git a/src/core/arm/cpu_interrupt_handler.cpp b/src/core/arm/cpu_interrupt_handler.cpp new file mode 100644 index 000000000..2f1a1a269 --- /dev/null +++ b/src/core/arm/cpu_interrupt_handler.cpp | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/thread.h" | ||
| 8 | #include "core/arm/cpu_interrupt_handler.h" | ||
| 9 | |||
| 10 | namespace Core { | ||
| 11 | |||
| 12 | CPUInterruptHandler::CPUInterruptHandler() : is_interrupted{} { | ||
| 13 | interrupt_event = std::make_unique<Common::Event>(); | ||
| 14 | } | ||
| 15 | |||
| 16 | CPUInterruptHandler::~CPUInterruptHandler() = default; | ||
| 17 | |||
| 18 | void CPUInterruptHandler::SetInterrupt(bool is_interrupted_) { | ||
| 19 | if (is_interrupted_) { | ||
| 20 | interrupt_event->Set(); | ||
| 21 | } | ||
| 22 | this->is_interrupted = is_interrupted_; | ||
| 23 | } | ||
| 24 | |||
| 25 | void CPUInterruptHandler::AwaitInterrupt() { | ||
| 26 | interrupt_event->Wait(); | ||
| 27 | } | ||
| 28 | |||
| 29 | } // namespace Core | ||
diff --git a/src/core/arm/cpu_interrupt_handler.h b/src/core/arm/cpu_interrupt_handler.h new file mode 100644 index 000000000..91c31a271 --- /dev/null +++ b/src/core/arm/cpu_interrupt_handler.h | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | |||
| 9 | namespace Common { | ||
| 10 | class Event; | ||
| 11 | } | ||
| 12 | |||
| 13 | namespace Core { | ||
| 14 | |||
| 15 | class CPUInterruptHandler { | ||
| 16 | public: | ||
| 17 | CPUInterruptHandler(); | ||
| 18 | ~CPUInterruptHandler(); | ||
| 19 | |||
| 20 | CPUInterruptHandler(const CPUInterruptHandler&) = delete; | ||
| 21 | CPUInterruptHandler& operator=(const CPUInterruptHandler&) = delete; | ||
| 22 | |||
| 23 | CPUInterruptHandler(CPUInterruptHandler&&) = default; | ||
| 24 | CPUInterruptHandler& operator=(CPUInterruptHandler&&) = default; | ||
| 25 | |||
| 26 | constexpr bool IsInterrupted() const { | ||
| 27 | return is_interrupted; | ||
| 28 | } | ||
| 29 | |||
| 30 | void SetInterrupt(bool is_interrupted); | ||
| 31 | |||
| 32 | void AwaitInterrupt(); | ||
| 33 | |||
| 34 | private: | ||
| 35 | bool is_interrupted{}; | ||
| 36 | std::unique_ptr<Common::Event> interrupt_event; | ||
| 37 | }; | ||
| 38 | |||
| 39 | } // namespace Core | ||
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index 4c8663d03..0b7aa6a69 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp | |||
| @@ -114,9 +114,9 @@ void ARM_Dynarmic_32::Step() { | |||
| 114 | jit->Step(); | 114 | jit->Step(); |
| 115 | } | 115 | } |
| 116 | 116 | ||
| 117 | ARM_Dynarmic_32::ARM_Dynarmic_32(System& system, ExclusiveMonitor& exclusive_monitor, | 117 | ARM_Dynarmic_32::ARM_Dynarmic_32(System& system, CPUInterruptHandler& interrupt_handler, |
| 118 | std::size_t core_index) | 118 | ExclusiveMonitor& exclusive_monitor, std::size_t core_index) |
| 119 | : ARM_Interface{system}, cb(std::make_unique<DynarmicCallbacks32>(*this)), | 119 | : ARM_Interface{system, interrupt_handler}, cb(std::make_unique<DynarmicCallbacks32>(*this)), |
| 120 | cp15(std::make_shared<DynarmicCP15>(*this)), core_index{core_index}, | 120 | cp15(std::make_shared<DynarmicCP15>(*this)), core_index{core_index}, |
| 121 | exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {} | 121 | exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {} |
| 122 | 122 | ||
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h index e5b92d7bb..1e7e17e64 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.h +++ b/src/core/arm/dynarmic/arm_dynarmic_32.h | |||
| @@ -21,6 +21,7 @@ class Memory; | |||
| 21 | 21 | ||
| 22 | namespace Core { | 22 | namespace Core { |
| 23 | 23 | ||
| 24 | class CPUInterruptHandler; | ||
| 24 | class DynarmicCallbacks32; | 25 | class DynarmicCallbacks32; |
| 25 | class DynarmicCP15; | 26 | class DynarmicCP15; |
| 26 | class DynarmicExclusiveMonitor; | 27 | class DynarmicExclusiveMonitor; |
| @@ -28,7 +29,8 @@ class System; | |||
| 28 | 29 | ||
| 29 | class ARM_Dynarmic_32 final : public ARM_Interface { | 30 | class ARM_Dynarmic_32 final : public ARM_Interface { |
| 30 | public: | 31 | public: |
| 31 | ARM_Dynarmic_32(System& system, ExclusiveMonitor& exclusive_monitor, std::size_t core_index); | 32 | ARM_Dynarmic_32(System& system, CPUInterruptHandler& interrupt_handler, |
| 33 | ExclusiveMonitor& exclusive_monitor, std::size_t core_index); | ||
| 32 | ~ARM_Dynarmic_32() override; | 34 | ~ARM_Dynarmic_32() override; |
| 33 | 35 | ||
| 34 | void SetPC(u64 pc) override; | 36 | void SetPC(u64 pc) override; |
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index 5f5e36d94..5e316ffd4 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include "common/logging/log.h" | 9 | #include "common/logging/log.h" |
| 10 | #include "common/microprofile.h" | 10 | #include "common/microprofile.h" |
| 11 | #include "common/page_table.h" | 11 | #include "common/page_table.h" |
| 12 | #include "core/arm/cpu_interrupt_handler.h" | ||
| 12 | #include "core/arm/dynarmic/arm_dynarmic_64.h" | 13 | #include "core/arm/dynarmic/arm_dynarmic_64.h" |
| 13 | #include "core/core.h" | 14 | #include "core/core.h" |
| 14 | #include "core/core_manager.h" | 15 | #include "core/core_manager.h" |
| @@ -108,23 +109,16 @@ public: | |||
| 108 | } | 109 | } |
| 109 | 110 | ||
| 110 | void AddTicks(u64 ticks) override { | 111 | void AddTicks(u64 ticks) override { |
| 111 | // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a | 112 | /// We are using host timing, NOP |
| 112 | // rough approximation of the amount of executed ticks in the system, it may be thrown off | ||
| 113 | // if not all cores are doing a similar amount of work. Instead of doing this, we should | ||
| 114 | // device a way so that timing is consistent across all cores without increasing the ticks 4 | ||
| 115 | // times. | ||
| 116 | u64 amortized_ticks = (ticks - num_interpreted_instructions) / Core::NUM_CPU_CORES; | ||
| 117 | // Always execute at least one tick. | ||
| 118 | amortized_ticks = std::max<u64>(amortized_ticks, 1); | ||
| 119 | |||
| 120 | parent.system.CoreTiming().AddTicks(amortized_ticks); | ||
| 121 | num_interpreted_instructions = 0; | ||
| 122 | } | 113 | } |
| 123 | u64 GetTicksRemaining() override { | 114 | u64 GetTicksRemaining() override { |
| 124 | return std::max(parent.system.CoreTiming().GetDowncount(), s64{0}); | 115 | if (!parent.interrupt_handler.IsInterrupted()) { |
| 116 | return 1000ULL; | ||
| 117 | } | ||
| 118 | return 0ULL; | ||
| 125 | } | 119 | } |
| 126 | u64 GetCNTPCT() override { | 120 | u64 GetCNTPCT() override { |
| 127 | return Timing::CpuCyclesToClockCycles(parent.system.CoreTiming().GetTicks()); | 121 | return parent.system.CoreTiming().GetClockTicks(); |
| 128 | } | 122 | } |
| 129 | 123 | ||
| 130 | ARM_Dynarmic_64& parent; | 124 | ARM_Dynarmic_64& parent; |
| @@ -183,10 +177,10 @@ void ARM_Dynarmic_64::Step() { | |||
| 183 | cb->InterpreterFallback(jit->GetPC(), 1); | 177 | cb->InterpreterFallback(jit->GetPC(), 1); |
| 184 | } | 178 | } |
| 185 | 179 | ||
| 186 | ARM_Dynarmic_64::ARM_Dynarmic_64(System& system, ExclusiveMonitor& exclusive_monitor, | 180 | ARM_Dynarmic_64::ARM_Dynarmic_64(System& system, CPUInterruptHandler& interrupt_handler, |
| 187 | std::size_t core_index) | 181 | ExclusiveMonitor& exclusive_monitor, std::size_t core_index) |
| 188 | : ARM_Interface{system}, cb(std::make_unique<DynarmicCallbacks64>(*this)), | 182 | : ARM_Interface{system, interrupt_handler}, cb(std::make_unique<DynarmicCallbacks64>(*this)), |
| 189 | inner_unicorn{system, ARM_Unicorn::Arch::AArch64}, core_index{core_index}, | 183 | inner_unicorn{system, interrupt_handler, ARM_Unicorn::Arch::AArch64}, core_index{core_index}, |
| 190 | exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {} | 184 | exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {} |
| 191 | 185 | ||
| 192 | ARM_Dynarmic_64::~ARM_Dynarmic_64() = default; | 186 | ARM_Dynarmic_64::~ARM_Dynarmic_64() = default; |
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h index 647cecaf0..9e94b58c2 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.h +++ b/src/core/arm/dynarmic/arm_dynarmic_64.h | |||
| @@ -22,12 +22,14 @@ class Memory; | |||
| 22 | namespace Core { | 22 | namespace Core { |
| 23 | 23 | ||
| 24 | class DynarmicCallbacks64; | 24 | class DynarmicCallbacks64; |
| 25 | class CPUInterruptHandler; | ||
| 25 | class DynarmicExclusiveMonitor; | 26 | class DynarmicExclusiveMonitor; |
| 26 | class System; | 27 | class System; |
| 27 | 28 | ||
| 28 | class ARM_Dynarmic_64 final : public ARM_Interface { | 29 | class ARM_Dynarmic_64 final : public ARM_Interface { |
| 29 | public: | 30 | public: |
| 30 | ARM_Dynarmic_64(System& system, ExclusiveMonitor& exclusive_monitor, std::size_t core_index); | 31 | ARM_Dynarmic_64(System& system, CPUInterruptHandler& interrupt_handler, |
| 32 | ExclusiveMonitor& exclusive_monitor, std::size_t core_index); | ||
| 31 | ~ARM_Dynarmic_64() override; | 33 | ~ARM_Dynarmic_64() override; |
| 32 | 34 | ||
| 33 | void SetPC(u64 pc) override; | 35 | void SetPC(u64 pc) override; |
diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp index e40e9626a..0393fe641 100644 --- a/src/core/arm/unicorn/arm_unicorn.cpp +++ b/src/core/arm/unicorn/arm_unicorn.cpp | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | #include <unicorn/arm64.h> | 6 | #include <unicorn/arm64.h> |
| 7 | #include "common/assert.h" | 7 | #include "common/assert.h" |
| 8 | #include "common/microprofile.h" | 8 | #include "common/microprofile.h" |
| 9 | #include "core/arm/cpu_interrupt_handler.h" | ||
| 9 | #include "core/arm/unicorn/arm_unicorn.h" | 10 | #include "core/arm/unicorn/arm_unicorn.h" |
| 10 | #include "core/core.h" | 11 | #include "core/core.h" |
| 11 | #include "core/core_timing.h" | 12 | #include "core/core_timing.h" |
| @@ -62,7 +63,8 @@ static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int si | |||
| 62 | return false; | 63 | return false; |
| 63 | } | 64 | } |
| 64 | 65 | ||
| 65 | ARM_Unicorn::ARM_Unicorn(System& system, Arch architecture) : ARM_Interface{system} { | 66 | ARM_Unicorn::ARM_Unicorn(System& system, CPUInterruptHandler& interrupt_handler, Arch architecture) |
| 67 | : ARM_Interface{system, interrupt_handler} { | ||
| 66 | const auto arch = architecture == Arch::AArch32 ? UC_ARCH_ARM : UC_ARCH_ARM64; | 68 | const auto arch = architecture == Arch::AArch32 ? UC_ARCH_ARM : UC_ARCH_ARM64; |
| 67 | CHECKED(uc_open(arch, UC_MODE_ARM, &uc)); | 69 | CHECKED(uc_open(arch, UC_MODE_ARM, &uc)); |
| 68 | 70 | ||
| @@ -160,8 +162,12 @@ void ARM_Unicorn::Run() { | |||
| 160 | if (GDBStub::IsServerEnabled()) { | 162 | if (GDBStub::IsServerEnabled()) { |
| 161 | ExecuteInstructions(std::max(4000000U, 0U)); | 163 | ExecuteInstructions(std::max(4000000U, 0U)); |
| 162 | } else { | 164 | } else { |
| 163 | ExecuteInstructions( | 165 | while (true) { |
| 164 | std::max(std::size_t(system.CoreTiming().GetDowncount()), std::size_t{0})); | 166 | if (interrupt_handler.IsInterrupted()) { |
| 167 | return; | ||
| 168 | } | ||
| 169 | ExecuteInstructions(10); | ||
| 170 | } | ||
| 165 | } | 171 | } |
| 166 | } | 172 | } |
| 167 | 173 | ||
| @@ -183,8 +189,6 @@ void ARM_Unicorn::ExecuteInstructions(std::size_t num_instructions) { | |||
| 183 | UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC, page_buffer.data())); | 189 | UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC, page_buffer.data())); |
| 184 | CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions)); | 190 | CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions)); |
| 185 | CHECKED(uc_mem_unmap(uc, map_addr, page_buffer.size())); | 191 | CHECKED(uc_mem_unmap(uc, map_addr, page_buffer.size())); |
| 186 | |||
| 187 | system.CoreTiming().AddTicks(num_instructions); | ||
| 188 | if (GDBStub::IsServerEnabled()) { | 192 | if (GDBStub::IsServerEnabled()) { |
| 189 | if (last_bkpt_hit && last_bkpt.type == GDBStub::BreakpointType::Execute) { | 193 | if (last_bkpt_hit && last_bkpt.type == GDBStub::BreakpointType::Execute) { |
| 190 | uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address); | 194 | uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address); |
diff --git a/src/core/arm/unicorn/arm_unicorn.h b/src/core/arm/unicorn/arm_unicorn.h index 725c65085..0a4c087cd 100644 --- a/src/core/arm/unicorn/arm_unicorn.h +++ b/src/core/arm/unicorn/arm_unicorn.h | |||
| @@ -11,6 +11,7 @@ | |||
| 11 | 11 | ||
| 12 | namespace Core { | 12 | namespace Core { |
| 13 | 13 | ||
| 14 | class CPUInterruptHandler; | ||
| 14 | class System; | 15 | class System; |
| 15 | 16 | ||
| 16 | class ARM_Unicorn final : public ARM_Interface { | 17 | class ARM_Unicorn final : public ARM_Interface { |
| @@ -20,7 +21,7 @@ public: | |||
| 20 | AArch64, // 64-bit ARM | 21 | AArch64, // 64-bit ARM |
| 21 | }; | 22 | }; |
| 22 | 23 | ||
| 23 | explicit ARM_Unicorn(System& system, Arch architecture); | 24 | explicit ARM_Unicorn(System& system, CPUInterruptHandler& interrupt_handler, Arch architecture); |
| 24 | ~ARM_Unicorn() override; | 25 | ~ARM_Unicorn() override; |
| 25 | 26 | ||
| 26 | void SetPC(u64 pc) override; | 27 | void SetPC(u64 pc) override; |
diff --git a/src/core/core.cpp b/src/core/core.cpp index f9f8a3000..e8936b09d 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp | |||
| @@ -11,7 +11,6 @@ | |||
| 11 | #include "common/string_util.h" | 11 | #include "common/string_util.h" |
| 12 | #include "core/arm/exclusive_monitor.h" | 12 | #include "core/arm/exclusive_monitor.h" |
| 13 | #include "core/core.h" | 13 | #include "core/core.h" |
| 14 | #include "core/core_manager.h" | ||
| 15 | #include "core/core_timing.h" | 14 | #include "core/core_timing.h" |
| 16 | #include "core/cpu_manager.h" | 15 | #include "core/cpu_manager.h" |
| 17 | #include "core/device_memory.h" | 16 | #include "core/device_memory.h" |
| @@ -117,23 +116,30 @@ struct System::Impl { | |||
| 117 | : kernel{system}, fs_controller{system}, memory{system}, | 116 | : kernel{system}, fs_controller{system}, memory{system}, |
| 118 | cpu_manager{system}, reporter{system}, applet_manager{system} {} | 117 | cpu_manager{system}, reporter{system}, applet_manager{system} {} |
| 119 | 118 | ||
| 120 | CoreManager& CurrentCoreManager() { | ||
| 121 | return cpu_manager.GetCurrentCoreManager(); | ||
| 122 | } | ||
| 123 | |||
| 124 | Kernel::PhysicalCore& CurrentPhysicalCore() { | 119 | Kernel::PhysicalCore& CurrentPhysicalCore() { |
| 125 | const auto index = cpu_manager.GetActiveCoreIndex(); | 120 | return kernel.CurrentPhysicalCore(); |
| 126 | return kernel.PhysicalCore(index); | ||
| 127 | } | 121 | } |
| 128 | 122 | ||
| 129 | Kernel::PhysicalCore& GetPhysicalCore(std::size_t index) { | 123 | Kernel::PhysicalCore& GetPhysicalCore(std::size_t index) { |
| 130 | return kernel.PhysicalCore(index); | 124 | return kernel.PhysicalCore(index); |
| 131 | } | 125 | } |
| 132 | 126 | ||
| 133 | ResultStatus RunLoop(bool tight_loop) { | 127 | ResultStatus Run() { |
| 134 | status = ResultStatus::Success; | 128 | status = ResultStatus::Success; |
| 135 | 129 | ||
| 136 | cpu_manager.RunLoop(tight_loop); | 130 | kernel.Suspend(false); |
| 131 | core_timing.SyncPause(false); | ||
| 132 | cpu_manager.Pause(false); | ||
| 133 | |||
| 134 | return status; | ||
| 135 | } | ||
| 136 | |||
| 137 | ResultStatus Pause() { | ||
| 138 | status = ResultStatus::Success; | ||
| 139 | |||
| 140 | kernel.Suspend(true); | ||
| 141 | core_timing.SyncPause(true); | ||
| 142 | cpu_manager.Pause(true); | ||
| 137 | 143 | ||
| 138 | return status; | 144 | return status; |
| 139 | } | 145 | } |
| @@ -143,7 +149,7 @@ struct System::Impl { | |||
| 143 | 149 | ||
| 144 | device_memory = std::make_unique<Core::DeviceMemory>(system); | 150 | device_memory = std::make_unique<Core::DeviceMemory>(system); |
| 145 | 151 | ||
| 146 | core_timing.Initialize(); | 152 | core_timing.Initialize([&system]() { system.RegisterHostThread(); }); |
| 147 | kernel.Initialize(); | 153 | kernel.Initialize(); |
| 148 | cpu_manager.Initialize(); | 154 | cpu_manager.Initialize(); |
| 149 | 155 | ||
| @@ -387,20 +393,24 @@ struct System::Impl { | |||
| 387 | System::System() : impl{std::make_unique<Impl>(*this)} {} | 393 | System::System() : impl{std::make_unique<Impl>(*this)} {} |
| 388 | System::~System() = default; | 394 | System::~System() = default; |
| 389 | 395 | ||
| 390 | CoreManager& System::CurrentCoreManager() { | 396 | CpuManager& System::GetCpuManager() { |
| 391 | return impl->CurrentCoreManager(); | 397 | return impl->cpu_manager; |
| 398 | } | ||
| 399 | |||
| 400 | const CpuManager& System::GetCpuManager() const { | ||
| 401 | return impl->cpu_manager; | ||
| 392 | } | 402 | } |
| 393 | 403 | ||
| 394 | const CoreManager& System::CurrentCoreManager() const { | 404 | System::ResultStatus System::Run() { |
| 395 | return impl->CurrentCoreManager(); | 405 | return impl->Run(); |
| 396 | } | 406 | } |
| 397 | 407 | ||
| 398 | System::ResultStatus System::RunLoop(bool tight_loop) { | 408 | System::ResultStatus System::Pause() { |
| 399 | return impl->RunLoop(tight_loop); | 409 | return impl->Pause(); |
| 400 | } | 410 | } |
| 401 | 411 | ||
| 402 | System::ResultStatus System::SingleStep() { | 412 | System::ResultStatus System::SingleStep() { |
| 403 | return RunLoop(false); | 413 | return ResultStatus::Success; |
| 404 | } | 414 | } |
| 405 | 415 | ||
| 406 | void System::InvalidateCpuInstructionCaches() { | 416 | void System::InvalidateCpuInstructionCaches() { |
| @@ -444,7 +454,9 @@ const ARM_Interface& System::CurrentArmInterface() const { | |||
| 444 | } | 454 | } |
| 445 | 455 | ||
| 446 | std::size_t System::CurrentCoreIndex() const { | 456 | std::size_t System::CurrentCoreIndex() const { |
| 447 | return impl->cpu_manager.GetActiveCoreIndex(); | 457 | std::size_t core = impl->kernel.GetCurrentHostThreadID(); |
| 458 | ASSERT(core < Core::Hardware::NUM_CPU_CORES); | ||
| 459 | return core; | ||
| 448 | } | 460 | } |
| 449 | 461 | ||
| 450 | Kernel::Scheduler& System::CurrentScheduler() { | 462 | Kernel::Scheduler& System::CurrentScheduler() { |
| @@ -497,15 +509,6 @@ const ARM_Interface& System::ArmInterface(std::size_t core_index) const { | |||
| 497 | return impl->GetPhysicalCore(core_index).ArmInterface(); | 509 | return impl->GetPhysicalCore(core_index).ArmInterface(); |
| 498 | } | 510 | } |
| 499 | 511 | ||
| 500 | CoreManager& System::GetCoreManager(std::size_t core_index) { | ||
| 501 | return impl->cpu_manager.GetCoreManager(core_index); | ||
| 502 | } | ||
| 503 | |||
| 504 | const CoreManager& System::GetCoreManager(std::size_t core_index) const { | ||
| 505 | ASSERT(core_index < NUM_CPU_CORES); | ||
| 506 | return impl->cpu_manager.GetCoreManager(core_index); | ||
| 507 | } | ||
| 508 | |||
| 509 | ExclusiveMonitor& System::Monitor() { | 512 | ExclusiveMonitor& System::Monitor() { |
| 510 | return impl->kernel.GetExclusiveMonitor(); | 513 | return impl->kernel.GetExclusiveMonitor(); |
| 511 | } | 514 | } |
diff --git a/src/core/core.h b/src/core/core.h index acc53d6a1..7f170fc54 100644 --- a/src/core/core.h +++ b/src/core/core.h | |||
| @@ -90,7 +90,7 @@ class InterruptManager; | |||
| 90 | namespace Core { | 90 | namespace Core { |
| 91 | 91 | ||
| 92 | class ARM_Interface; | 92 | class ARM_Interface; |
| 93 | class CoreManager; | 93 | class CpuManager; |
| 94 | class DeviceMemory; | 94 | class DeviceMemory; |
| 95 | class ExclusiveMonitor; | 95 | class ExclusiveMonitor; |
| 96 | class FrameLimiter; | 96 | class FrameLimiter; |
| @@ -136,16 +136,18 @@ public: | |||
| 136 | }; | 136 | }; |
| 137 | 137 | ||
| 138 | /** | 138 | /** |
| 139 | * Run the core CPU loop | 139 | * Run the OS and Application |
| 140 | * This function runs the core for the specified number of CPU instructions before trying to | 140 | * This function will start emulation and run the competent devices |
| 141 | * update hardware. This is much faster than SingleStep (and should be equivalent), as the CPU | 141 | */ |
| 142 | * is not required to do a full dispatch with each instruction. NOTE: the number of instructions | 142 | ResultStatus Run(); |
| 143 | * requested is not guaranteed to run, as this will be interrupted preemptively if a hardware | 143 | |
| 144 | * update is requested (e.g. on a thread switch). | 144 | /** |
| 145 | * @param tight_loop If false, the CPU single-steps. | 145 | * Pause the OS and Application |
| 146 | * @return Result status, indicating whether or not the operation succeeded. | 146 | * This function will pause emulation and stop the competent devices |
| 147 | */ | 147 | */ |
| 148 | ResultStatus RunLoop(bool tight_loop = true); | 148 | ResultStatus Pause(); |
| 149 | |||
| 150 | |||
| 149 | 151 | ||
| 150 | /** | 152 | /** |
| 151 | * Step the CPU one instruction | 153 | * Step the CPU one instruction |
| @@ -215,11 +217,9 @@ public: | |||
| 215 | /// Gets a const reference to an ARM interface from the CPU core with the specified index | 217 | /// Gets a const reference to an ARM interface from the CPU core with the specified index |
| 216 | const ARM_Interface& ArmInterface(std::size_t core_index) const; | 218 | const ARM_Interface& ArmInterface(std::size_t core_index) const; |
| 217 | 219 | ||
| 218 | /// Gets a CPU interface to the CPU core with the specified index | 220 | CpuManager& GetCpuManager(); |
| 219 | CoreManager& GetCoreManager(std::size_t core_index); | ||
| 220 | 221 | ||
| 221 | /// Gets a CPU interface to the CPU core with the specified index | 222 | const CpuManager& GetCpuManager() const; |
| 222 | const CoreManager& GetCoreManager(std::size_t core_index) const; | ||
| 223 | 223 | ||
| 224 | /// Gets a reference to the exclusive monitor | 224 | /// Gets a reference to the exclusive monitor |
| 225 | ExclusiveMonitor& Monitor(); | 225 | ExclusiveMonitor& Monitor(); |
| @@ -373,12 +373,6 @@ public: | |||
| 373 | private: | 373 | private: |
| 374 | System(); | 374 | System(); |
| 375 | 375 | ||
| 376 | /// Returns the currently running CPU core | ||
| 377 | CoreManager& CurrentCoreManager(); | ||
| 378 | |||
| 379 | /// Returns the currently running CPU core | ||
| 380 | const CoreManager& CurrentCoreManager() const; | ||
| 381 | |||
| 382 | /** | 376 | /** |
| 383 | * Initialize the emulated system. | 377 | * Initialize the emulated system. |
| 384 | * @param emu_window Reference to the host-system window used for video output and keyboard | 378 | * @param emu_window Reference to the host-system window used for video output and keyboard |
diff --git a/src/core/core_manager.cpp b/src/core/core_manager.cpp index b6b797c80..45f0bb547 100644 --- a/src/core/core_manager.cpp +++ b/src/core/core_manager.cpp | |||
| @@ -34,7 +34,6 @@ void CoreManager::RunLoop(bool tight_loop) { | |||
| 34 | // instead advance to the next event and try to yield to the next thread | 34 | // instead advance to the next event and try to yield to the next thread |
| 35 | if (Kernel::GetCurrentThread() == nullptr) { | 35 | if (Kernel::GetCurrentThread() == nullptr) { |
| 36 | LOG_TRACE(Core, "Core-{} idling", core_index); | 36 | LOG_TRACE(Core, "Core-{} idling", core_index); |
| 37 | core_timing.Idle(); | ||
| 38 | } else { | 37 | } else { |
| 39 | if (tight_loop) { | 38 | if (tight_loop) { |
| 40 | physical_core.Run(); | 39 | physical_core.Run(); |
| @@ -42,7 +41,6 @@ void CoreManager::RunLoop(bool tight_loop) { | |||
| 42 | physical_core.Step(); | 41 | physical_core.Step(); |
| 43 | } | 42 | } |
| 44 | } | 43 | } |
| 45 | core_timing.Advance(); | ||
| 46 | 44 | ||
| 47 | Reschedule(); | 45 | Reschedule(); |
| 48 | } | 46 | } |
| @@ -59,7 +57,7 @@ void CoreManager::Reschedule() { | |||
| 59 | // Lock the global kernel mutex when we manipulate the HLE state | 57 | // Lock the global kernel mutex when we manipulate the HLE state |
| 60 | std::lock_guard lock(HLE::g_hle_lock); | 58 | std::lock_guard lock(HLE::g_hle_lock); |
| 61 | 59 | ||
| 62 | global_scheduler.SelectThread(core_index); | 60 | // global_scheduler.SelectThread(core_index); |
| 63 | 61 | ||
| 64 | physical_core.Scheduler().TryDoContextSwitch(); | 62 | physical_core.Scheduler().TryDoContextSwitch(); |
| 65 | } | 63 | } |
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index 46d4178c4..a3ce69790 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2008 Dolphin Emulator Project / 2017 Citra Emulator Project | 1 | // Copyright 2020 yuzu Emulator Project |
| 2 | // Licensed under GPLv2+ | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "core/core_timing.h" | 5 | #include "core/core_timing.h" |
| @@ -10,20 +10,16 @@ | |||
| 10 | #include <tuple> | 10 | #include <tuple> |
| 11 | 11 | ||
| 12 | #include "common/assert.h" | 12 | #include "common/assert.h" |
| 13 | #include "common/thread.h" | ||
| 14 | #include "core/core_timing_util.h" | 13 | #include "core/core_timing_util.h" |
| 15 | #include "core/hardware_properties.h" | ||
| 16 | 14 | ||
| 17 | namespace Core::Timing { | 15 | namespace Core::Timing { |
| 18 | 16 | ||
| 19 | constexpr int MAX_SLICE_LENGTH = 10000; | ||
| 20 | |||
| 21 | std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callback) { | 17 | std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callback) { |
| 22 | return std::make_shared<EventType>(std::move(callback), std::move(name)); | 18 | return std::make_shared<EventType>(std::move(callback), std::move(name)); |
| 23 | } | 19 | } |
| 24 | 20 | ||
| 25 | struct CoreTiming::Event { | 21 | struct CoreTiming::Event { |
| 26 | s64 time; | 22 | u64 time; |
| 27 | u64 fifo_order; | 23 | u64 fifo_order; |
| 28 | u64 userdata; | 24 | u64 userdata; |
| 29 | std::weak_ptr<EventType> type; | 25 | std::weak_ptr<EventType> type; |
| @@ -39,51 +35,74 @@ struct CoreTiming::Event { | |||
| 39 | } | 35 | } |
| 40 | }; | 36 | }; |
| 41 | 37 | ||
| 42 | CoreTiming::CoreTiming() = default; | 38 | CoreTiming::CoreTiming() { |
| 43 | CoreTiming::~CoreTiming() = default; | 39 | clock = |
| 40 | Common::CreateBestMatchingClock(Core::Hardware::BASE_CLOCK_RATE, Core::Hardware::CNTFREQ); | ||
| 41 | } | ||
| 44 | 42 | ||
| 45 | void CoreTiming::Initialize() { | 43 | CoreTiming::~CoreTiming() = default; |
| 46 | downcounts.fill(MAX_SLICE_LENGTH); | ||
| 47 | time_slice.fill(MAX_SLICE_LENGTH); | ||
| 48 | slice_length = MAX_SLICE_LENGTH; | ||
| 49 | global_timer = 0; | ||
| 50 | idled_cycles = 0; | ||
| 51 | current_context = 0; | ||
| 52 | 44 | ||
| 53 | // The time between CoreTiming being initialized and the first call to Advance() is considered | 45 | void CoreTiming::ThreadEntry(CoreTiming& instance) { |
| 54 | // the slice boundary between slice -1 and slice 0. Dispatcher loops must call Advance() before | 46 | std::string name = "yuzu:HostTiming"; |
| 55 | // executing the first cycle of each slice to prepare the slice length and downcount for | 47 | Common::SetCurrentThreadName(name.c_str()); |
| 56 | // that slice. | 48 | instance.on_thread_init(); |
| 57 | is_global_timer_sane = true; | 49 | instance.ThreadLoop(); |
| 50 | } | ||
| 58 | 51 | ||
| 52 | void CoreTiming::Initialize(std::function<void(void)>&& on_thread_init_) { | ||
| 53 | on_thread_init = std::move(on_thread_init_); | ||
| 59 | event_fifo_id = 0; | 54 | event_fifo_id = 0; |
| 60 | |||
| 61 | const auto empty_timed_callback = [](u64, s64) {}; | 55 | const auto empty_timed_callback = [](u64, s64) {}; |
| 62 | ev_lost = CreateEvent("_lost_event", empty_timed_callback); | 56 | ev_lost = CreateEvent("_lost_event", empty_timed_callback); |
| 57 | timer_thread = std::make_unique<std::thread>(ThreadEntry, std::ref(*this)); | ||
| 63 | } | 58 | } |
| 64 | 59 | ||
| 65 | void CoreTiming::Shutdown() { | 60 | void CoreTiming::Shutdown() { |
| 61 | paused = true; | ||
| 62 | shutting_down = true; | ||
| 63 | event.Set(); | ||
| 64 | timer_thread->join(); | ||
| 66 | ClearPendingEvents(); | 65 | ClearPendingEvents(); |
| 66 | timer_thread.reset(); | ||
| 67 | has_started = false; | ||
| 67 | } | 68 | } |
| 68 | 69 | ||
| 69 | void CoreTiming::ScheduleEvent(s64 cycles_into_future, const std::shared_ptr<EventType>& event_type, | 70 | void CoreTiming::Pause(bool is_paused) { |
| 70 | u64 userdata) { | 71 | paused = is_paused; |
| 71 | std::lock_guard guard{inner_mutex}; | 72 | } |
| 72 | const s64 timeout = GetTicks() + cycles_into_future; | ||
| 73 | 73 | ||
| 74 | // If this event needs to be scheduled before the next advance(), force one early | 74 | void CoreTiming::SyncPause(bool is_paused) { |
| 75 | if (!is_global_timer_sane) { | 75 | if (is_paused == paused && paused_set == paused) { |
| 76 | ForceExceptionCheck(cycles_into_future); | 76 | return; |
| 77 | } | 77 | } |
| 78 | Pause(is_paused); | ||
| 79 | event.Set(); | ||
| 80 | while (paused_set != is_paused) | ||
| 81 | ; | ||
| 82 | } | ||
| 83 | |||
| 84 | bool CoreTiming::IsRunning() const { | ||
| 85 | return !paused_set; | ||
| 86 | } | ||
| 87 | |||
| 88 | bool CoreTiming::HasPendingEvents() const { | ||
| 89 | return !(wait_set && event_queue.empty()); | ||
| 90 | } | ||
| 91 | |||
| 92 | void CoreTiming::ScheduleEvent(s64 ns_into_future, const std::shared_ptr<EventType>& event_type, | ||
| 93 | u64 userdata) { | ||
| 94 | basic_lock.lock(); | ||
| 95 | const u64 timeout = static_cast<u64>(GetGlobalTimeNs().count() + ns_into_future); | ||
| 78 | 96 | ||
| 79 | event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type}); | 97 | event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type}); |
| 80 | 98 | ||
| 81 | std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); | 99 | std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); |
| 100 | basic_lock.unlock(); | ||
| 101 | event.Set(); | ||
| 82 | } | 102 | } |
| 83 | 103 | ||
| 84 | void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, u64 userdata) { | 104 | void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, u64 userdata) { |
| 85 | std::lock_guard guard{inner_mutex}; | 105 | basic_lock.lock(); |
| 86 | |||
| 87 | const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { | 106 | const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { |
| 88 | return e.type.lock().get() == event_type.get() && e.userdata == userdata; | 107 | return e.type.lock().get() == event_type.get() && e.userdata == userdata; |
| 89 | }); | 108 | }); |
| @@ -93,23 +112,23 @@ void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, u | |||
| 93 | event_queue.erase(itr, event_queue.end()); | 112 | event_queue.erase(itr, event_queue.end()); |
| 94 | std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>()); | 113 | std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>()); |
| 95 | } | 114 | } |
| 115 | basic_lock.unlock(); | ||
| 96 | } | 116 | } |
| 97 | 117 | ||
| 98 | u64 CoreTiming::GetTicks() const { | 118 | void CoreTiming::AddTicks(std::size_t core_index, u64 ticks) { |
| 99 | u64 ticks = static_cast<u64>(global_timer); | 119 | ticks_count[core_index] += ticks; |
| 100 | if (!is_global_timer_sane) { | 120 | } |
| 101 | ticks += accumulated_ticks; | 121 | |
| 102 | } | 122 | void CoreTiming::ResetTicks(std::size_t core_index) { |
| 103 | return ticks; | 123 | ticks_count[core_index] = 0; |
| 104 | } | 124 | } |
| 105 | 125 | ||
| 106 | u64 CoreTiming::GetIdleTicks() const { | 126 | u64 CoreTiming::GetCPUTicks() const { |
| 107 | return static_cast<u64>(idled_cycles); | 127 | return clock->GetCPUCycles(); |
| 108 | } | 128 | } |
| 109 | 129 | ||
| 110 | void CoreTiming::AddTicks(u64 ticks) { | 130 | u64 CoreTiming::GetClockTicks() const { |
| 111 | accumulated_ticks += ticks; | 131 | return clock->GetClockCycles(); |
| 112 | downcounts[current_context] -= static_cast<s64>(ticks); | ||
| 113 | } | 132 | } |
| 114 | 133 | ||
| 115 | void CoreTiming::ClearPendingEvents() { | 134 | void CoreTiming::ClearPendingEvents() { |
| @@ -117,7 +136,7 @@ void CoreTiming::ClearPendingEvents() { | |||
| 117 | } | 136 | } |
| 118 | 137 | ||
| 119 | void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) { | 138 | void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) { |
| 120 | std::lock_guard guard{inner_mutex}; | 139 | basic_lock.lock(); |
| 121 | 140 | ||
| 122 | const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { | 141 | const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { |
| 123 | return e.type.lock().get() == event_type.get(); | 142 | return e.type.lock().get() == event_type.get(); |
| @@ -128,99 +147,64 @@ void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) { | |||
| 128 | event_queue.erase(itr, event_queue.end()); | 147 | event_queue.erase(itr, event_queue.end()); |
| 129 | std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>()); | 148 | std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>()); |
| 130 | } | 149 | } |
| 150 | basic_lock.unlock(); | ||
| 131 | } | 151 | } |
| 132 | 152 | ||
| 133 | void CoreTiming::ForceExceptionCheck(s64 cycles) { | 153 | std::optional<u64> CoreTiming::Advance() { |
| 134 | cycles = std::max<s64>(0, cycles); | 154 | advance_lock.lock(); |
| 135 | if (downcounts[current_context] <= cycles) { | 155 | basic_lock.lock(); |
| 136 | return; | 156 | global_timer = GetGlobalTimeNs().count(); |
| 137 | } | ||
| 138 | |||
| 139 | // downcount is always (much) smaller than MAX_INT so we can safely cast cycles to an int | ||
| 140 | // here. Account for cycles already executed by adjusting the g.slice_length | ||
| 141 | downcounts[current_context] = static_cast<int>(cycles); | ||
| 142 | } | ||
| 143 | |||
| 144 | std::optional<u64> CoreTiming::NextAvailableCore(const s64 needed_ticks) const { | ||
| 145 | const u64 original_context = current_context; | ||
| 146 | u64 next_context = (original_context + 1) % num_cpu_cores; | ||
| 147 | while (next_context != original_context) { | ||
| 148 | if (time_slice[next_context] >= needed_ticks) { | ||
| 149 | return {next_context}; | ||
| 150 | } else if (time_slice[next_context] >= 0) { | ||
| 151 | return std::nullopt; | ||
| 152 | } | ||
| 153 | next_context = (next_context + 1) % num_cpu_cores; | ||
| 154 | } | ||
| 155 | return std::nullopt; | ||
| 156 | } | ||
| 157 | |||
| 158 | void CoreTiming::Advance() { | ||
| 159 | std::unique_lock<std::mutex> guard(inner_mutex); | ||
| 160 | |||
| 161 | const u64 cycles_executed = accumulated_ticks; | ||
| 162 | time_slice[current_context] = std::max<s64>(0, time_slice[current_context] - accumulated_ticks); | ||
| 163 | global_timer += cycles_executed; | ||
| 164 | |||
| 165 | is_global_timer_sane = true; | ||
| 166 | 157 | ||
| 167 | while (!event_queue.empty() && event_queue.front().time <= global_timer) { | 158 | while (!event_queue.empty() && event_queue.front().time <= global_timer) { |
| 168 | Event evt = std::move(event_queue.front()); | 159 | Event evt = std::move(event_queue.front()); |
| 169 | std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>()); | 160 | std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>()); |
| 170 | event_queue.pop_back(); | 161 | event_queue.pop_back(); |
| 171 | inner_mutex.unlock(); | 162 | basic_lock.unlock(); |
| 172 | 163 | ||
| 173 | if (auto event_type{evt.type.lock()}) { | 164 | if (auto event_type{evt.type.lock()}) { |
| 174 | event_type->callback(evt.userdata, global_timer - evt.time); | 165 | event_type->callback(evt.userdata, global_timer - evt.time); |
| 175 | } | 166 | } |
| 176 | 167 | ||
| 177 | inner_mutex.lock(); | 168 | basic_lock.lock(); |
| 178 | } | 169 | } |
| 179 | 170 | ||
| 180 | is_global_timer_sane = false; | ||
| 181 | |||
| 182 | // Still events left (scheduled in the future) | ||
| 183 | if (!event_queue.empty()) { | 171 | if (!event_queue.empty()) { |
| 184 | const s64 needed_ticks = | 172 | const u64 next_time = event_queue.front().time - global_timer; |
| 185 | std::min<s64>(event_queue.front().time - global_timer, MAX_SLICE_LENGTH); | 173 | basic_lock.unlock(); |
| 186 | const auto next_core = NextAvailableCore(needed_ticks); | 174 | advance_lock.unlock(); |
| 187 | if (next_core) { | 175 | return next_time; |
| 188 | downcounts[*next_core] = needed_ticks; | 176 | } else { |
| 189 | } | 177 | basic_lock.unlock(); |
| 178 | advance_lock.unlock(); | ||
| 179 | return std::nullopt; | ||
| 190 | } | 180 | } |
| 191 | |||
| 192 | accumulated_ticks = 0; | ||
| 193 | |||
| 194 | downcounts[current_context] = time_slice[current_context]; | ||
| 195 | } | 181 | } |
| 196 | 182 | ||
| 197 | void CoreTiming::ResetRun() { | 183 | void CoreTiming::ThreadLoop() { |
| 198 | downcounts.fill(MAX_SLICE_LENGTH); | 184 | has_started = true; |
| 199 | time_slice.fill(MAX_SLICE_LENGTH); | 185 | while (!shutting_down) { |
| 200 | current_context = 0; | 186 | while (!paused) { |
| 201 | // Still events left (scheduled in the future) | 187 | paused_set = false; |
| 202 | if (!event_queue.empty()) { | 188 | const auto next_time = Advance(); |
| 203 | const s64 needed_ticks = | 189 | if (next_time) { |
| 204 | std::min<s64>(event_queue.front().time - global_timer, MAX_SLICE_LENGTH); | 190 | std::chrono::nanoseconds next_time_ns = std::chrono::nanoseconds(*next_time); |
| 205 | downcounts[current_context] = needed_ticks; | 191 | event.WaitFor(next_time_ns); |
| 192 | } else { | ||
| 193 | wait_set = true; | ||
| 194 | event.Wait(); | ||
| 195 | } | ||
| 196 | wait_set = false; | ||
| 197 | } | ||
| 198 | paused_set = true; | ||
| 206 | } | 199 | } |
| 207 | |||
| 208 | is_global_timer_sane = false; | ||
| 209 | accumulated_ticks = 0; | ||
| 210 | } | 200 | } |
| 211 | 201 | ||
| 212 | void CoreTiming::Idle() { | 202 | std::chrono::nanoseconds CoreTiming::GetGlobalTimeNs() const { |
| 213 | accumulated_ticks += downcounts[current_context]; | 203 | return clock->GetTimeNS(); |
| 214 | idled_cycles += downcounts[current_context]; | ||
| 215 | downcounts[current_context] = 0; | ||
| 216 | } | 204 | } |
| 217 | 205 | ||
| 218 | std::chrono::microseconds CoreTiming::GetGlobalTimeUs() const { | 206 | std::chrono::microseconds CoreTiming::GetGlobalTimeUs() const { |
| 219 | return std::chrono::microseconds{GetTicks() * 1000000 / Hardware::BASE_CLOCK_RATE}; | 207 | return clock->GetTimeUS(); |
| 220 | } | ||
| 221 | |||
| 222 | s64 CoreTiming::GetDowncount() const { | ||
| 223 | return downcounts[current_context]; | ||
| 224 | } | 208 | } |
| 225 | 209 | ||
| 226 | } // namespace Core::Timing | 210 | } // namespace Core::Timing |
diff --git a/src/core/core_timing.h b/src/core/core_timing.h index d50f4eb8a..707c8ef0c 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h | |||
| @@ -1,19 +1,25 @@ | |||
| 1 | // Copyright 2008 Dolphin Emulator Project / 2017 Citra Emulator Project | 1 | // Copyright 2020 yuzu Emulator Project |
| 2 | // Licensed under GPLv2+ | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <atomic> | ||
| 7 | #include <chrono> | 8 | #include <chrono> |
| 8 | #include <functional> | 9 | #include <functional> |
| 9 | #include <memory> | 10 | #include <memory> |
| 10 | #include <mutex> | 11 | #include <mutex> |
| 11 | #include <optional> | 12 | #include <optional> |
| 12 | #include <string> | 13 | #include <string> |
| 14 | #include <thread> | ||
| 13 | #include <vector> | 15 | #include <vector> |
| 14 | 16 | ||
| 15 | #include "common/common_types.h" | 17 | #include "common/common_types.h" |
| 18 | #include "common/spin_lock.h" | ||
| 19 | #include "common/thread.h" | ||
| 16 | #include "common/threadsafe_queue.h" | 20 | #include "common/threadsafe_queue.h" |
| 21 | #include "common/wall_clock.h" | ||
| 22 | #include "core/hardware_properties.h" | ||
| 17 | 23 | ||
| 18 | namespace Core::Timing { | 24 | namespace Core::Timing { |
| 19 | 25 | ||
| @@ -56,58 +62,55 @@ public: | |||
| 56 | 62 | ||
| 57 | /// CoreTiming begins at the boundary of timing slice -1. An initial call to Advance() is | 63 | /// CoreTiming begins at the boundary of timing slice -1. An initial call to Advance() is |
| 58 | /// required to end slice - 1 and start slice 0 before the first cycle of code is executed. | 64 | /// required to end slice - 1 and start slice 0 before the first cycle of code is executed. |
| 59 | void Initialize(); | 65 | void Initialize(std::function<void(void)>&& on_thread_init_); |
| 60 | 66 | ||
| 61 | /// Tears down all timing related functionality. | 67 | /// Tears down all timing related functionality. |
| 62 | void Shutdown(); | 68 | void Shutdown(); |
| 63 | 69 | ||
| 64 | /// After the first Advance, the slice lengths and the downcount will be reduced whenever an | 70 | /// Pauses/Unpauses the execution of the timer thread. |
| 65 | /// event is scheduled earlier than the current values. | 71 | void Pause(bool is_paused); |
| 66 | /// | ||
| 67 | /// Scheduling from a callback will not update the downcount until the Advance() completes. | ||
| 68 | void ScheduleEvent(s64 cycles_into_future, const std::shared_ptr<EventType>& event_type, | ||
| 69 | u64 userdata = 0); | ||
| 70 | 72 | ||
| 71 | void UnscheduleEvent(const std::shared_ptr<EventType>& event_type, u64 userdata); | 73 | /// Pauses/Unpauses the execution of the timer thread and waits until paused. |
| 74 | void SyncPause(bool is_paused); | ||
| 72 | 75 | ||
| 73 | /// We only permit one event of each type in the queue at a time. | 76 | /// Checks if core timing is running. |
| 74 | void RemoveEvent(const std::shared_ptr<EventType>& event_type); | 77 | bool IsRunning() const; |
| 75 | 78 | ||
| 76 | void ForceExceptionCheck(s64 cycles); | 79 | /// Checks if the timer thread has started. |
| 80 | bool HasStarted() const { | ||
| 81 | return has_started; | ||
| 82 | } | ||
| 77 | 83 | ||
| 78 | /// This should only be called from the emu thread, if you are calling it any other thread, | 84 | /// Checks if there are any pending time events. |
| 79 | /// you are doing something evil | 85 | bool HasPendingEvents() const; |
| 80 | u64 GetTicks() const; | ||
| 81 | 86 | ||
| 82 | u64 GetIdleTicks() const; | 87 | /// Schedules an event in core timing |
| 88 | void ScheduleEvent(s64 ns_into_future, const std::shared_ptr<EventType>& event_type, | ||
| 89 | u64 userdata = 0); | ||
| 83 | 90 | ||
| 84 | void AddTicks(u64 ticks); | 91 | void UnscheduleEvent(const std::shared_ptr<EventType>& event_type, u64 userdata); |
| 85 | 92 | ||
| 86 | /// Advance must be called at the beginning of dispatcher loops, not the end. Advance() ends | 93 | /// We only permit one event of each type in the queue at a time. |
| 87 | /// the previous timing slice and begins the next one, you must Advance from the previous | 94 | void RemoveEvent(const std::shared_ptr<EventType>& event_type); |
| 88 | /// slice to the current one before executing any cycles. CoreTiming starts in slice -1 so an | ||
| 89 | /// Advance() is required to initialize the slice length before the first cycle of emulated | ||
| 90 | /// instructions is executed. | ||
| 91 | void Advance(); | ||
| 92 | 95 | ||
| 93 | /// Pretend that the main CPU has executed enough cycles to reach the next event. | 96 | void AddTicks(std::size_t core_index, u64 ticks); |
| 94 | void Idle(); | ||
| 95 | 97 | ||
| 96 | std::chrono::microseconds GetGlobalTimeUs() const; | 98 | void ResetTicks(std::size_t core_index); |
| 97 | 99 | ||
| 98 | void ResetRun(); | 100 | /// Returns current time in emulated CPU cycles |
| 101 | u64 GetCPUTicks() const; | ||
| 99 | 102 | ||
| 100 | s64 GetDowncount() const; | 103 | /// Returns current time in emulated in Clock cycles |
| 104 | u64 GetClockTicks() const; | ||
| 101 | 105 | ||
| 102 | void SwitchContext(u64 new_context) { | 106 | /// Returns current time in microseconds. |
| 103 | current_context = new_context; | 107 | std::chrono::microseconds GetGlobalTimeUs() const; |
| 104 | } | ||
| 105 | 108 | ||
| 106 | bool CanCurrentContextRun() const { | 109 | /// Returns current time in nanoseconds. |
| 107 | return time_slice[current_context] > 0; | 110 | std::chrono::nanoseconds GetGlobalTimeNs() const; |
| 108 | } | ||
| 109 | 111 | ||
| 110 | std::optional<u64> NextAvailableCore(const s64 needed_ticks) const; | 112 | /// Checks for events manually and returns time in nanoseconds for next event, threadsafe. |
| 113 | std::optional<u64> Advance(); | ||
| 111 | 114 | ||
| 112 | private: | 115 | private: |
| 113 | struct Event; | 116 | struct Event; |
| @@ -115,21 +118,14 @@ private: | |||
| 115 | /// Clear all pending events. This should ONLY be done on exit. | 118 | /// Clear all pending events. This should ONLY be done on exit. |
| 116 | void ClearPendingEvents(); | 119 | void ClearPendingEvents(); |
| 117 | 120 | ||
| 118 | static constexpr u64 num_cpu_cores = 4; | 121 | static void ThreadEntry(CoreTiming& instance); |
| 122 | void ThreadLoop(); | ||
| 123 | |||
| 124 | std::unique_ptr<Common::WallClock> clock; | ||
| 119 | 125 | ||
| 120 | s64 global_timer = 0; | 126 | u64 global_timer = 0; |
| 121 | s64 idled_cycles = 0; | ||
| 122 | s64 slice_length = 0; | ||
| 123 | u64 accumulated_ticks = 0; | ||
| 124 | std::array<s64, num_cpu_cores> downcounts{}; | ||
| 125 | // Slice of time assigned to each core per run. | ||
| 126 | std::array<s64, num_cpu_cores> time_slice{}; | ||
| 127 | u64 current_context = 0; | ||
| 128 | 127 | ||
| 129 | // Are we in a function that has been called from Advance() | 128 | std::chrono::nanoseconds start_point; |
| 130 | // If events are scheduled from a function that gets called from Advance(), | ||
| 131 | // don't change slice_length and downcount. | ||
| 132 | bool is_global_timer_sane = false; | ||
| 133 | 129 | ||
| 134 | // The queue is a min-heap using std::make_heap/push_heap/pop_heap. | 130 | // The queue is a min-heap using std::make_heap/push_heap/pop_heap. |
| 135 | // We don't use std::priority_queue because we need to be able to serialize, unserialize and | 131 | // We don't use std::priority_queue because we need to be able to serialize, unserialize and |
| @@ -139,8 +135,18 @@ private: | |||
| 139 | u64 event_fifo_id = 0; | 135 | u64 event_fifo_id = 0; |
| 140 | 136 | ||
| 141 | std::shared_ptr<EventType> ev_lost; | 137 | std::shared_ptr<EventType> ev_lost; |
| 142 | 138 | Common::Event event{}; | |
| 143 | std::mutex inner_mutex; | 139 | Common::SpinLock basic_lock{}; |
| 140 | Common::SpinLock advance_lock{}; | ||
| 141 | std::unique_ptr<std::thread> timer_thread; | ||
| 142 | std::atomic<bool> paused{}; | ||
| 143 | std::atomic<bool> paused_set{}; | ||
| 144 | std::atomic<bool> wait_set{}; | ||
| 145 | std::atomic<bool> shutting_down{}; | ||
| 146 | std::atomic<bool> has_started{}; | ||
| 147 | std::function<void(void)> on_thread_init{}; | ||
| 148 | |||
| 149 | std::array<std::atomic<u64>, Core::Hardware::NUM_CPU_CORES> ticks_count{}; | ||
| 144 | }; | 150 | }; |
| 145 | 151 | ||
| 146 | /// Creates a core timing event with the given name and callback. | 152 | /// 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 70ddbdcca..494850992 100644 --- a/src/core/cpu_manager.cpp +++ b/src/core/cpu_manager.cpp | |||
| @@ -2,80 +2,192 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/fiber.h" | ||
| 6 | #include "common/thread.h" | ||
| 5 | #include "core/arm/exclusive_monitor.h" | 7 | #include "core/arm/exclusive_monitor.h" |
| 6 | #include "core/core.h" | 8 | #include "core/core.h" |
| 7 | #include "core/core_manager.h" | ||
| 8 | #include "core/core_timing.h" | 9 | #include "core/core_timing.h" |
| 9 | #include "core/cpu_manager.h" | 10 | #include "core/cpu_manager.h" |
| 10 | #include "core/gdbstub/gdbstub.h" | 11 | #include "core/gdbstub/gdbstub.h" |
| 12 | #include "core/hle/kernel/kernel.h" | ||
| 13 | #include "core/hle/kernel/physical_core.h" | ||
| 14 | #include "core/hle/kernel/scheduler.h" | ||
| 15 | #include "core/hle/kernel/thread.h" | ||
| 11 | 16 | ||
| 12 | namespace Core { | 17 | namespace Core { |
| 13 | 18 | ||
| 14 | CpuManager::CpuManager(System& system) : system{system} {} | 19 | CpuManager::CpuManager(System& system) : system{system} {} |
| 15 | CpuManager::~CpuManager() = default; | 20 | CpuManager::~CpuManager() = default; |
| 16 | 21 | ||
| 22 | void CpuManager::ThreadStart(CpuManager& cpu_manager, std::size_t core) { | ||
| 23 | cpu_manager.RunThread(core); | ||
| 24 | } | ||
| 25 | |||
| 17 | void CpuManager::Initialize() { | 26 | void CpuManager::Initialize() { |
| 18 | for (std::size_t index = 0; index < core_managers.size(); ++index) { | 27 | running_mode = true; |
| 19 | core_managers[index] = std::make_unique<CoreManager>(system, index); | 28 | for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { |
| 29 | core_data[core].host_thread = | ||
| 30 | std::make_unique<std::thread>(ThreadStart, std::ref(*this), core); | ||
| 20 | } | 31 | } |
| 21 | } | 32 | } |
| 22 | 33 | ||
| 23 | void CpuManager::Shutdown() { | 34 | void CpuManager::Shutdown() { |
| 24 | for (auto& cpu_core : core_managers) { | 35 | running_mode = false; |
| 25 | cpu_core.reset(); | 36 | Pause(false); |
| 37 | for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 38 | core_data[core].host_thread->join(); | ||
| 26 | } | 39 | } |
| 27 | } | 40 | } |
| 28 | 41 | ||
| 29 | CoreManager& CpuManager::GetCoreManager(std::size_t index) { | 42 | void CpuManager::GuestThreadFunction(void* cpu_manager_) { |
| 30 | return *core_managers.at(index); | 43 | CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_); |
| 44 | cpu_manager->RunGuestThread(); | ||
| 31 | } | 45 | } |
| 32 | 46 | ||
| 33 | const CoreManager& CpuManager::GetCoreManager(std::size_t index) const { | 47 | void CpuManager::IdleThreadFunction(void* cpu_manager_) { |
| 34 | return *core_managers.at(index); | 48 | CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_); |
| 49 | cpu_manager->RunIdleThread(); | ||
| 35 | } | 50 | } |
| 36 | 51 | ||
| 37 | CoreManager& CpuManager::GetCurrentCoreManager() { | 52 | void CpuManager::SuspendThreadFunction(void* cpu_manager_) { |
| 38 | // Otherwise, use single-threaded mode active_core variable | 53 | CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_); |
| 39 | return *core_managers[active_core]; | 54 | cpu_manager->RunSuspendThread(); |
| 40 | } | 55 | } |
| 41 | 56 | ||
| 42 | const CoreManager& CpuManager::GetCurrentCoreManager() const { | 57 | std::function<void(void*)> CpuManager::GetGuestThreadStartFunc() { |
| 43 | // Otherwise, use single-threaded mode active_core variable | 58 | return std::function<void(void*)>(GuestThreadFunction); |
| 44 | return *core_managers[active_core]; | ||
| 45 | } | 59 | } |
| 46 | 60 | ||
| 47 | void CpuManager::RunLoop(bool tight_loop) { | 61 | std::function<void(void*)> CpuManager::GetIdleThreadStartFunc() { |
| 48 | if (GDBStub::IsServerEnabled()) { | 62 | return std::function<void(void*)>(IdleThreadFunction); |
| 49 | GDBStub::HandlePacket(); | 63 | } |
| 50 | 64 | ||
| 51 | // If the loop is halted and we want to step, use a tiny (1) number of instructions to | 65 | std::function<void(void*)> CpuManager::GetSuspendThreadStartFunc() { |
| 52 | // execute. Otherwise, get out of the loop function. | 66 | return std::function<void(void*)>(SuspendThreadFunction); |
| 53 | if (GDBStub::GetCpuHaltFlag()) { | 67 | } |
| 54 | if (GDBStub::GetCpuStepFlag()) { | 68 | |
| 55 | tight_loop = false; | 69 | void* CpuManager::GetStartFuncParamater() { |
| 56 | } else { | 70 | return static_cast<void*>(this); |
| 57 | return; | 71 | } |
| 58 | } | 72 | |
| 59 | } | 73 | void CpuManager::RunGuestThread() { |
| 74 | auto& kernel = system.Kernel(); | ||
| 75 | { | ||
| 76 | auto& sched = kernel.CurrentScheduler(); | ||
| 77 | sched.OnThreadStart(); | ||
| 78 | } | ||
| 79 | while (true) { | ||
| 80 | auto& physical_core = kernel.CurrentPhysicalCore(); | ||
| 81 | LOG_CRITICAL(Core_ARM, "Running Guest Thread"); | ||
| 82 | physical_core.Idle(); | ||
| 83 | LOG_CRITICAL(Core_ARM, "Leaving Guest Thread"); | ||
| 84 | // physical_core.Run(); | ||
| 85 | auto& scheduler = physical_core.Scheduler(); | ||
| 86 | scheduler.TryDoContextSwitch(); | ||
| 60 | } | 87 | } |
| 88 | } | ||
| 61 | 89 | ||
| 62 | auto& core_timing = system.CoreTiming(); | 90 | void CpuManager::RunIdleThread() { |
| 63 | core_timing.ResetRun(); | 91 | auto& kernel = system.Kernel(); |
| 64 | bool keep_running{}; | 92 | while (true) { |
| 65 | do { | 93 | auto& physical_core = kernel.CurrentPhysicalCore(); |
| 66 | keep_running = false; | 94 | LOG_CRITICAL(Core_ARM, "Running Idle Thread"); |
| 67 | for (active_core = 0; active_core < NUM_CPU_CORES; ++active_core) { | 95 | physical_core.Idle(); |
| 68 | core_timing.SwitchContext(active_core); | 96 | auto& scheduler = physical_core.Scheduler(); |
| 69 | if (core_timing.CanCurrentContextRun()) { | 97 | scheduler.TryDoContextSwitch(); |
| 70 | core_managers[active_core]->RunLoop(tight_loop); | 98 | } |
| 99 | } | ||
| 100 | |||
| 101 | void CpuManager::RunSuspendThread() { | ||
| 102 | LOG_CRITICAL(Core_ARM, "Suspending Thread Entered"); | ||
| 103 | auto& kernel = system.Kernel(); | ||
| 104 | { | ||
| 105 | auto& sched = kernel.CurrentScheduler(); | ||
| 106 | sched.OnThreadStart(); | ||
| 107 | } | ||
| 108 | while (true) { | ||
| 109 | auto core = kernel.GetCurrentHostThreadID(); | ||
| 110 | auto& scheduler = kernel.CurrentScheduler(); | ||
| 111 | Kernel::Thread* current_thread = scheduler.GetCurrentThread(); | ||
| 112 | LOG_CRITICAL(Core_ARM, "Suspending Core {}", core); | ||
| 113 | Common::Fiber::YieldTo(current_thread->GetHostContext(), core_data[core].host_context); | ||
| 114 | LOG_CRITICAL(Core_ARM, "Unsuspending Core {}", core); | ||
| 115 | ASSERT(scheduler.ContextSwitchPending()); | ||
| 116 | ASSERT(core == kernel.GetCurrentHostThreadID()); | ||
| 117 | scheduler.TryDoContextSwitch(); | ||
| 118 | } | ||
| 119 | } | ||
| 120 | |||
| 121 | void CpuManager::Pause(bool paused) { | ||
| 122 | if (!paused) { | ||
| 123 | bool all_not_barrier = false; | ||
| 124 | while (!all_not_barrier) { | ||
| 125 | all_not_barrier = true; | ||
| 126 | for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 127 | all_not_barrier &= | ||
| 128 | !core_data[core].is_running.load() && core_data[core].initialized.load(); | ||
| 71 | } | 129 | } |
| 72 | keep_running |= core_timing.CanCurrentContextRun(); | ||
| 73 | } | 130 | } |
| 74 | } while (keep_running); | 131 | for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { |
| 132 | core_data[core].enter_barrier->Set(); | ||
| 133 | } | ||
| 134 | if (paused_state.load()) { | ||
| 135 | bool all_barrier = false; | ||
| 136 | while (!all_barrier) { | ||
| 137 | all_barrier = true; | ||
| 138 | for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 139 | all_barrier &= | ||
| 140 | core_data[core].is_paused.load() && core_data[core].initialized.load(); | ||
| 141 | } | ||
| 142 | } | ||
| 143 | for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 144 | core_data[core].exit_barrier->Set(); | ||
| 145 | } | ||
| 146 | } | ||
| 147 | } else { | ||
| 148 | /// Wait until all cores are paused. | ||
| 149 | bool all_barrier = false; | ||
| 150 | while (!all_barrier) { | ||
| 151 | all_barrier = true; | ||
| 152 | for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 153 | all_barrier &= | ||
| 154 | core_data[core].is_paused.load() && core_data[core].initialized.load(); | ||
| 155 | } | ||
| 156 | } | ||
| 157 | /// Don't release the barrier | ||
| 158 | } | ||
| 159 | paused_state = paused; | ||
| 160 | } | ||
| 75 | 161 | ||
| 76 | if (GDBStub::IsServerEnabled()) { | 162 | void CpuManager::RunThread(std::size_t core) { |
| 77 | GDBStub::SetCpuStepFlag(false); | 163 | /// Initialization |
| 164 | system.RegisterCoreThread(core); | ||
| 165 | std::string name = "yuzu:CoreHostThread_" + std::to_string(core); | ||
| 166 | Common::SetCurrentThreadName(name.c_str()); | ||
| 167 | auto& data = core_data[core]; | ||
| 168 | data.enter_barrier = std::make_unique<Common::Event>(); | ||
| 169 | data.exit_barrier = std::make_unique<Common::Event>(); | ||
| 170 | data.host_context = Common::Fiber::ThreadToFiber(); | ||
| 171 | data.is_running = false; | ||
| 172 | data.initialized = true; | ||
| 173 | /// Running | ||
| 174 | while (running_mode) { | ||
| 175 | data.is_running = false; | ||
| 176 | data.enter_barrier->Wait(); | ||
| 177 | auto& scheduler = system.Kernel().CurrentScheduler(); | ||
| 178 | Kernel::Thread* current_thread = scheduler.GetCurrentThread(); | ||
| 179 | data.is_running = true; | ||
| 180 | Common::Fiber::YieldTo(data.host_context, current_thread->GetHostContext()); | ||
| 181 | data.is_running = false; | ||
| 182 | data.is_paused = true; | ||
| 183 | data.exit_barrier->Wait(); | ||
| 184 | data.is_paused = false; | ||
| 78 | } | 185 | } |
| 186 | /// Time to cleanup | ||
| 187 | data.host_context->Exit(); | ||
| 188 | data.enter_barrier.reset(); | ||
| 189 | data.exit_barrier.reset(); | ||
| 190 | data.initialized = false; | ||
| 79 | } | 191 | } |
| 80 | 192 | ||
| 81 | } // namespace Core | 193 | } // namespace Core |
diff --git a/src/core/cpu_manager.h b/src/core/cpu_manager.h index 97554d1bb..8103ae857 100644 --- a/src/core/cpu_manager.h +++ b/src/core/cpu_manager.h | |||
| @@ -5,12 +5,18 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <functional> | ||
| 8 | #include <memory> | 9 | #include <memory> |
| 10 | #include <thread> | ||
| 9 | #include "core/hardware_properties.h" | 11 | #include "core/hardware_properties.h" |
| 10 | 12 | ||
| 13 | namespace Common { | ||
| 14 | class Event; | ||
| 15 | class Fiber; | ||
| 16 | } // namespace Common | ||
| 17 | |||
| 11 | namespace Core { | 18 | namespace Core { |
| 12 | 19 | ||
| 13 | class CoreManager; | ||
| 14 | class System; | 20 | class System; |
| 15 | 21 | ||
| 16 | class CpuManager { | 22 | class CpuManager { |
| @@ -27,21 +33,40 @@ public: | |||
| 27 | void Initialize(); | 33 | void Initialize(); |
| 28 | void Shutdown(); | 34 | void Shutdown(); |
| 29 | 35 | ||
| 30 | CoreManager& GetCoreManager(std::size_t index); | 36 | void Pause(bool paused); |
| 31 | const CoreManager& GetCoreManager(std::size_t index) const; | 37 | |
| 38 | std::function<void(void*)> GetGuestThreadStartFunc(); | ||
| 39 | std::function<void(void*)> GetIdleThreadStartFunc(); | ||
| 40 | std::function<void(void*)> GetSuspendThreadStartFunc(); | ||
| 41 | void* GetStartFuncParamater(); | ||
| 32 | 42 | ||
| 33 | CoreManager& GetCurrentCoreManager(); | 43 | private: |
| 34 | const CoreManager& GetCurrentCoreManager() const; | 44 | static void GuestThreadFunction(void* cpu_manager); |
| 45 | static void IdleThreadFunction(void* cpu_manager); | ||
| 46 | static void SuspendThreadFunction(void* cpu_manager); | ||
| 35 | 47 | ||
| 36 | std::size_t GetActiveCoreIndex() const { | 48 | void RunGuestThread(); |
| 37 | return active_core; | 49 | void RunIdleThread(); |
| 38 | } | 50 | void RunSuspendThread(); |
| 39 | 51 | ||
| 40 | void RunLoop(bool tight_loop); | 52 | static void ThreadStart(CpuManager& cpu_manager, std::size_t core); |
| 41 | 53 | ||
| 42 | private: | 54 | void RunThread(std::size_t core); |
| 43 | std::array<std::unique_ptr<CoreManager>, Hardware::NUM_CPU_CORES> core_managers; | 55 | |
| 44 | std::size_t active_core{}; ///< Active core, only used in single thread mode | 56 | struct CoreData { |
| 57 | std::shared_ptr<Common::Fiber> host_context; | ||
| 58 | std::unique_ptr<Common::Event> enter_barrier; | ||
| 59 | std::unique_ptr<Common::Event> exit_barrier; | ||
| 60 | std::atomic<bool> is_running; | ||
| 61 | std::atomic<bool> is_paused; | ||
| 62 | std::atomic<bool> initialized; | ||
| 63 | std::unique_ptr<std::thread> host_thread; | ||
| 64 | }; | ||
| 65 | |||
| 66 | std::atomic<bool> running_mode{}; | ||
| 67 | std::atomic<bool> paused_state{}; | ||
| 68 | |||
| 69 | std::array<CoreData, Core::Hardware::NUM_CPU_CORES> core_data{}; | ||
| 45 | 70 | ||
| 46 | System& system; | 71 | System& system; |
| 47 | }; | 72 | }; |
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 7655382fa..ba051a7d8 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp | |||
| @@ -13,11 +13,13 @@ | |||
| 13 | 13 | ||
| 14 | #include "common/assert.h" | 14 | #include "common/assert.h" |
| 15 | #include "common/logging/log.h" | 15 | #include "common/logging/log.h" |
| 16 | #include "common/thread.h" | ||
| 16 | #include "core/arm/arm_interface.h" | 17 | #include "core/arm/arm_interface.h" |
| 17 | #include "core/arm/exclusive_monitor.h" | 18 | #include "core/arm/exclusive_monitor.h" |
| 18 | #include "core/core.h" | 19 | #include "core/core.h" |
| 19 | #include "core/core_timing.h" | 20 | #include "core/core_timing.h" |
| 20 | #include "core/core_timing_util.h" | 21 | #include "core/core_timing_util.h" |
| 22 | #include "core/cpu_manager.h" | ||
| 21 | #include "core/device_memory.h" | 23 | #include "core/device_memory.h" |
| 22 | #include "core/hardware_properties.h" | 24 | #include "core/hardware_properties.h" |
| 23 | #include "core/hle/kernel/client_port.h" | 25 | #include "core/hle/kernel/client_port.h" |
| @@ -117,7 +119,9 @@ struct KernelCore::Impl { | |||
| 117 | InitializeSystemResourceLimit(kernel); | 119 | InitializeSystemResourceLimit(kernel); |
| 118 | InitializeMemoryLayout(); | 120 | InitializeMemoryLayout(); |
| 119 | InitializeThreads(); | 121 | InitializeThreads(); |
| 120 | InitializePreemption(); | 122 | InitializePreemption(kernel); |
| 123 | InitializeSchedulers(); | ||
| 124 | InitializeSuspendThreads(); | ||
| 121 | } | 125 | } |
| 122 | 126 | ||
| 123 | void Shutdown() { | 127 | void Shutdown() { |
| @@ -155,6 +159,12 @@ struct KernelCore::Impl { | |||
| 155 | } | 159 | } |
| 156 | } | 160 | } |
| 157 | 161 | ||
| 162 | void InitializeSchedulers() { | ||
| 163 | for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { | ||
| 164 | cores[i].Scheduler().Initialize(); | ||
| 165 | } | ||
| 166 | } | ||
| 167 | |||
| 158 | // Creates the default system resource limit | 168 | // Creates the default system resource limit |
| 159 | void InitializeSystemResourceLimit(KernelCore& kernel) { | 169 | void InitializeSystemResourceLimit(KernelCore& kernel) { |
| 160 | system_resource_limit = ResourceLimit::Create(kernel); | 170 | system_resource_limit = ResourceLimit::Create(kernel); |
| @@ -178,10 +188,13 @@ struct KernelCore::Impl { | |||
| 178 | Core::Timing::CreateEvent("ThreadWakeupCallback", ThreadWakeupCallback); | 188 | Core::Timing::CreateEvent("ThreadWakeupCallback", ThreadWakeupCallback); |
| 179 | } | 189 | } |
| 180 | 190 | ||
| 181 | void InitializePreemption() { | 191 | void InitializePreemption(KernelCore& kernel) { |
| 182 | preemption_event = | 192 | preemption_event = Core::Timing::CreateEvent( |
| 183 | Core::Timing::CreateEvent("PreemptionCallback", [this](u64 userdata, s64 cycles_late) { | 193 | "PreemptionCallback", [this, &kernel](u64 userdata, s64 cycles_late) { |
| 184 | global_scheduler.PreemptThreads(); | 194 | { |
| 195 | SchedulerLock lock(kernel); | ||
| 196 | global_scheduler.PreemptThreads(); | ||
| 197 | } | ||
| 185 | s64 time_interval = Core::Timing::msToCycles(std::chrono::milliseconds(10)); | 198 | s64 time_interval = Core::Timing::msToCycles(std::chrono::milliseconds(10)); |
| 186 | system.CoreTiming().ScheduleEvent(time_interval, preemption_event); | 199 | system.CoreTiming().ScheduleEvent(time_interval, preemption_event); |
| 187 | }); | 200 | }); |
| @@ -190,6 +203,20 @@ struct KernelCore::Impl { | |||
| 190 | system.CoreTiming().ScheduleEvent(time_interval, preemption_event); | 203 | system.CoreTiming().ScheduleEvent(time_interval, preemption_event); |
| 191 | } | 204 | } |
| 192 | 205 | ||
| 206 | void InitializeSuspendThreads() { | ||
| 207 | for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { | ||
| 208 | std::string name = "Suspend Thread Id:" + std::to_string(i); | ||
| 209 | std::function<void(void*)> init_func = | ||
| 210 | system.GetCpuManager().GetSuspendThreadStartFunc(); | ||
| 211 | void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater(); | ||
| 212 | ThreadType type = | ||
| 213 | static_cast<ThreadType>(THREADTYPE_KERNEL | THREADTYPE_HLE | THREADTYPE_SUSPEND); | ||
| 214 | auto thread_res = Thread::Create(system, type, name, 0, 0, 0, static_cast<u32>(i), 0, | ||
| 215 | nullptr, std::move(init_func), init_func_parameter); | ||
| 216 | suspend_threads[i] = std::move(thread_res).Unwrap(); | ||
| 217 | } | ||
| 218 | } | ||
| 219 | |||
| 193 | void MakeCurrentProcess(Process* process) { | 220 | void MakeCurrentProcess(Process* process) { |
| 194 | current_process = process; | 221 | current_process = process; |
| 195 | 222 | ||
| @@ -201,7 +228,10 @@ struct KernelCore::Impl { | |||
| 201 | core.SetIs64Bit(process->Is64BitProcess()); | 228 | core.SetIs64Bit(process->Is64BitProcess()); |
| 202 | } | 229 | } |
| 203 | 230 | ||
| 204 | system.Memory().SetCurrentPageTable(*process); | 231 | u32 core_id = GetCurrentHostThreadID(); |
| 232 | if (core_id < Core::Hardware::NUM_CPU_CORES) { | ||
| 233 | system.Memory().SetCurrentPageTable(*process, core_id); | ||
| 234 | } | ||
| 205 | } | 235 | } |
| 206 | 236 | ||
| 207 | void RegisterCoreThread(std::size_t core_id) { | 237 | void RegisterCoreThread(std::size_t core_id) { |
| @@ -219,7 +249,9 @@ struct KernelCore::Impl { | |||
| 219 | std::unique_lock lock{register_thread_mutex}; | 249 | std::unique_lock lock{register_thread_mutex}; |
| 220 | const std::thread::id this_id = std::this_thread::get_id(); | 250 | const std::thread::id this_id = std::this_thread::get_id(); |
| 221 | const auto it = host_thread_ids.find(this_id); | 251 | const auto it = host_thread_ids.find(this_id); |
| 222 | ASSERT(it == host_thread_ids.end()); | 252 | if (it != host_thread_ids.end()) { |
| 253 | return; | ||
| 254 | } | ||
| 223 | host_thread_ids[this_id] = registered_thread_ids++; | 255 | host_thread_ids[this_id] = registered_thread_ids++; |
| 224 | } | 256 | } |
| 225 | 257 | ||
| @@ -343,6 +375,8 @@ struct KernelCore::Impl { | |||
| 343 | std::shared_ptr<Kernel::SharedMemory> irs_shared_mem; | 375 | std::shared_ptr<Kernel::SharedMemory> irs_shared_mem; |
| 344 | std::shared_ptr<Kernel::SharedMemory> time_shared_mem; | 376 | std::shared_ptr<Kernel::SharedMemory> time_shared_mem; |
| 345 | 377 | ||
| 378 | std::array<std::shared_ptr<Thread>, Core::Hardware::NUM_CPU_CORES> suspend_threads{}; | ||
| 379 | |||
| 346 | // System context | 380 | // System context |
| 347 | Core::System& system; | 381 | Core::System& system; |
| 348 | }; | 382 | }; |
| @@ -412,6 +446,26 @@ const Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) const { | |||
| 412 | return impl->cores[id]; | 446 | return impl->cores[id]; |
| 413 | } | 447 | } |
| 414 | 448 | ||
| 449 | Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() { | ||
| 450 | u32 core_id = impl->GetCurrentHostThreadID(); | ||
| 451 | ASSERT(core_id < Core::Hardware::NUM_CPU_CORES); | ||
| 452 | return impl->cores[core_id]; | ||
| 453 | } | ||
| 454 | |||
| 455 | const Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() const { | ||
| 456 | u32 core_id = impl->GetCurrentHostThreadID(); | ||
| 457 | ASSERT(core_id < Core::Hardware::NUM_CPU_CORES); | ||
| 458 | return impl->cores[core_id]; | ||
| 459 | } | ||
| 460 | |||
| 461 | Kernel::Scheduler& KernelCore::CurrentScheduler() { | ||
| 462 | return CurrentPhysicalCore().Scheduler(); | ||
| 463 | } | ||
| 464 | |||
| 465 | const Kernel::Scheduler& KernelCore::CurrentScheduler() const { | ||
| 466 | return CurrentPhysicalCore().Scheduler(); | ||
| 467 | } | ||
| 468 | |||
| 415 | Kernel::Synchronization& KernelCore::Synchronization() { | 469 | Kernel::Synchronization& KernelCore::Synchronization() { |
| 416 | return impl->synchronization; | 470 | return impl->synchronization; |
| 417 | } | 471 | } |
| @@ -557,4 +611,20 @@ const Kernel::SharedMemory& KernelCore::GetTimeSharedMem() const { | |||
| 557 | return *impl->time_shared_mem; | 611 | return *impl->time_shared_mem; |
| 558 | } | 612 | } |
| 559 | 613 | ||
| 614 | void KernelCore::Suspend(bool in_suspention) { | ||
| 615 | const bool should_suspend = exception_exited || in_suspention; | ||
| 616 | { | ||
| 617 | SchedulerLock lock(*this); | ||
| 618 | ThreadStatus status = should_suspend ? ThreadStatus::Ready : ThreadStatus::WaitSleep; | ||
| 619 | for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { | ||
| 620 | impl->suspend_threads[i]->SetStatus(status); | ||
| 621 | } | ||
| 622 | } | ||
| 623 | } | ||
| 624 | |||
| 625 | void KernelCore::ExceptionalExit() { | ||
| 626 | exception_exited = true; | ||
| 627 | Suspend(true); | ||
| 628 | } | ||
| 629 | |||
| 560 | } // namespace Kernel | 630 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 83de1f542..5d32a8329 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h | |||
| @@ -110,6 +110,18 @@ public: | |||
| 110 | /// Gets the an instance of the respective physical CPU core. | 110 | /// Gets the an instance of the respective physical CPU core. |
| 111 | const Kernel::PhysicalCore& PhysicalCore(std::size_t id) const; | 111 | const Kernel::PhysicalCore& PhysicalCore(std::size_t id) const; |
| 112 | 112 | ||
| 113 | /// Gets the sole instance of the Scheduler at the current running core. | ||
| 114 | Kernel::Scheduler& CurrentScheduler(); | ||
| 115 | |||
| 116 | /// Gets the sole instance of the Scheduler at the current running core. | ||
| 117 | const Kernel::Scheduler& CurrentScheduler() const; | ||
| 118 | |||
| 119 | /// Gets the an instance of the current physical CPU core. | ||
| 120 | Kernel::PhysicalCore& CurrentPhysicalCore(); | ||
| 121 | |||
| 122 | /// Gets the an instance of the current physical CPU core. | ||
| 123 | const Kernel::PhysicalCore& CurrentPhysicalCore() const; | ||
| 124 | |||
| 113 | /// Gets the an instance of the Synchronization Interface. | 125 | /// Gets the an instance of the Synchronization Interface. |
| 114 | Kernel::Synchronization& Synchronization(); | 126 | Kernel::Synchronization& Synchronization(); |
| 115 | 127 | ||
| @@ -191,6 +203,12 @@ public: | |||
| 191 | /// Gets the shared memory object for Time services. | 203 | /// Gets the shared memory object for Time services. |
| 192 | const Kernel::SharedMemory& GetTimeSharedMem() const; | 204 | const Kernel::SharedMemory& GetTimeSharedMem() const; |
| 193 | 205 | ||
| 206 | /// Suspend/unsuspend the OS. | ||
| 207 | void Suspend(bool in_suspention); | ||
| 208 | |||
| 209 | /// Exceptional exit the OS. | ||
| 210 | void ExceptionalExit(); | ||
| 211 | |||
| 194 | private: | 212 | private: |
| 195 | friend class Object; | 213 | friend class Object; |
| 196 | friend class Process; | 214 | friend class Process; |
| @@ -219,6 +237,7 @@ private: | |||
| 219 | 237 | ||
| 220 | struct Impl; | 238 | struct Impl; |
| 221 | std::unique_ptr<Impl> impl; | 239 | std::unique_ptr<Impl> impl; |
| 240 | bool exception_exited{}; | ||
| 222 | }; | 241 | }; |
| 223 | 242 | ||
| 224 | } // namespace Kernel | 243 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/physical_core.cpp b/src/core/hle/kernel/physical_core.cpp index a15011076..69202540b 100644 --- a/src/core/hle/kernel/physical_core.cpp +++ b/src/core/hle/kernel/physical_core.cpp | |||
| @@ -2,12 +2,15 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/assert.h" | ||
| 5 | #include "common/logging/log.h" | 6 | #include "common/logging/log.h" |
| 7 | #include "common/spin_lock.h" | ||
| 6 | #include "core/arm/arm_interface.h" | 8 | #include "core/arm/arm_interface.h" |
| 7 | #ifdef ARCHITECTURE_x86_64 | 9 | #ifdef ARCHITECTURE_x86_64 |
| 8 | #include "core/arm/dynarmic/arm_dynarmic_32.h" | 10 | #include "core/arm/dynarmic/arm_dynarmic_32.h" |
| 9 | #include "core/arm/dynarmic/arm_dynarmic_64.h" | 11 | #include "core/arm/dynarmic/arm_dynarmic_64.h" |
| 10 | #endif | 12 | #endif |
| 13 | #include "core/arm/cpu_interrupt_handler.h" | ||
| 11 | #include "core/arm/exclusive_monitor.h" | 14 | #include "core/arm/exclusive_monitor.h" |
| 12 | #include "core/arm/unicorn/arm_unicorn.h" | 15 | #include "core/arm/unicorn/arm_unicorn.h" |
| 13 | #include "core/core.h" | 16 | #include "core/core.h" |
| @@ -19,21 +22,23 @@ namespace Kernel { | |||
| 19 | 22 | ||
| 20 | PhysicalCore::PhysicalCore(Core::System& system, std::size_t id, | 23 | PhysicalCore::PhysicalCore(Core::System& system, std::size_t id, |
| 21 | Core::ExclusiveMonitor& exclusive_monitor) | 24 | Core::ExclusiveMonitor& exclusive_monitor) |
| 22 | : core_index{id} { | 25 | : interrupt_handler{}, core_index{id} { |
| 23 | #ifdef ARCHITECTURE_x86_64 | 26 | #ifdef ARCHITECTURE_x86_64 |
| 24 | arm_interface_32 = | 27 | arm_interface_32 = std::make_unique<Core::ARM_Dynarmic_32>(system, interrupt_handler, |
| 25 | std::make_unique<Core::ARM_Dynarmic_32>(system, exclusive_monitor, core_index); | 28 | exclusive_monitor, core_index); |
| 26 | arm_interface_64 = | 29 | arm_interface_64 = std::make_unique<Core::ARM_Dynarmic_64>(system, interrupt_handler, |
| 27 | std::make_unique<Core::ARM_Dynarmic_64>(system, exclusive_monitor, core_index); | 30 | exclusive_monitor, core_index); |
| 28 | |||
| 29 | #else | 31 | #else |
| 30 | using Core::ARM_Unicorn; | 32 | using Core::ARM_Unicorn; |
| 31 | arm_interface_32 = std::make_unique<ARM_Unicorn>(system, ARM_Unicorn::Arch::AArch32); | 33 | arm_interface_32 = |
| 32 | arm_interface_64 = std::make_unique<ARM_Unicorn>(system, ARM_Unicorn::Arch::AArch64); | 34 | std::make_unique<ARM_Unicorn>(system, interrupt_handler, ARM_Unicorn::Arch::AArch32); |
| 35 | arm_interface_64 = | ||
| 36 | std::make_unique<ARM_Unicorn>(system, interrupt_handler, ARM_Unicorn::Arch::AArch64); | ||
| 33 | LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available"); | 37 | LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available"); |
| 34 | #endif | 38 | #endif |
| 35 | 39 | ||
| 36 | scheduler = std::make_unique<Kernel::Scheduler>(system, core_index); | 40 | scheduler = std::make_unique<Kernel::Scheduler>(system, core_index); |
| 41 | guard = std::make_unique<Common::SpinLock>(); | ||
| 37 | } | 42 | } |
| 38 | 43 | ||
| 39 | PhysicalCore::~PhysicalCore() = default; | 44 | PhysicalCore::~PhysicalCore() = default; |
| @@ -47,6 +52,10 @@ void PhysicalCore::Step() { | |||
| 47 | arm_interface->Step(); | 52 | arm_interface->Step(); |
| 48 | } | 53 | } |
| 49 | 54 | ||
| 55 | void PhysicalCore::Idle() { | ||
| 56 | interrupt_handler.AwaitInterrupt(); | ||
| 57 | } | ||
| 58 | |||
| 50 | void PhysicalCore::Stop() { | 59 | void PhysicalCore::Stop() { |
| 51 | arm_interface->PrepareReschedule(); | 60 | arm_interface->PrepareReschedule(); |
| 52 | } | 61 | } |
| @@ -63,4 +72,16 @@ void PhysicalCore::SetIs64Bit(bool is_64_bit) { | |||
| 63 | } | 72 | } |
| 64 | } | 73 | } |
| 65 | 74 | ||
| 75 | void PhysicalCore::Interrupt() { | ||
| 76 | guard->lock(); | ||
| 77 | interrupt_handler.SetInterrupt(true); | ||
| 78 | guard->unlock(); | ||
| 79 | } | ||
| 80 | |||
| 81 | void PhysicalCore::ClearInterrupt() { | ||
| 82 | guard->lock(); | ||
| 83 | interrupt_handler.SetInterrupt(false); | ||
| 84 | guard->unlock(); | ||
| 85 | } | ||
| 86 | |||
| 66 | } // namespace Kernel | 87 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/physical_core.h b/src/core/hle/kernel/physical_core.h index 3269166be..c3da30b72 100644 --- a/src/core/hle/kernel/physical_core.h +++ b/src/core/hle/kernel/physical_core.h | |||
| @@ -7,6 +7,12 @@ | |||
| 7 | #include <cstddef> | 7 | #include <cstddef> |
| 8 | #include <memory> | 8 | #include <memory> |
| 9 | 9 | ||
| 10 | #include "core/arm/cpu_interrupt_handler.h" | ||
| 11 | |||
| 12 | namespace Common { | ||
| 13 | class SpinLock; | ||
| 14 | } | ||
| 15 | |||
| 10 | namespace Kernel { | 16 | namespace Kernel { |
| 11 | class Scheduler; | 17 | class Scheduler; |
| 12 | } // namespace Kernel | 18 | } // namespace Kernel |
| @@ -32,11 +38,24 @@ public: | |||
| 32 | 38 | ||
| 33 | /// Execute current jit state | 39 | /// Execute current jit state |
| 34 | void Run(); | 40 | void Run(); |
| 41 | /// Set this core in IdleState. | ||
| 42 | void Idle(); | ||
| 35 | /// Execute a single instruction in current jit. | 43 | /// Execute a single instruction in current jit. |
| 36 | void Step(); | 44 | void Step(); |
| 37 | /// Stop JIT execution/exit | 45 | /// Stop JIT execution/exit |
| 38 | void Stop(); | 46 | void Stop(); |
| 39 | 47 | ||
| 48 | /// Interrupt this physical core. | ||
| 49 | void Interrupt(); | ||
| 50 | |||
| 51 | /// Clear this core's interrupt | ||
| 52 | void ClearInterrupt(); | ||
| 53 | |||
| 54 | /// Check if this core is interrupted | ||
| 55 | bool IsInterrupted() const { | ||
| 56 | return interrupt_handler.IsInterrupted(); | ||
| 57 | } | ||
| 58 | |||
| 40 | // Shutdown this physical core. | 59 | // Shutdown this physical core. |
| 41 | void Shutdown(); | 60 | void Shutdown(); |
| 42 | 61 | ||
| @@ -71,11 +90,13 @@ public: | |||
| 71 | void SetIs64Bit(bool is_64_bit); | 90 | void SetIs64Bit(bool is_64_bit); |
| 72 | 91 | ||
| 73 | private: | 92 | private: |
| 93 | Core::CPUInterruptHandler interrupt_handler; | ||
| 74 | std::size_t core_index; | 94 | std::size_t core_index; |
| 75 | std::unique_ptr<Core::ARM_Interface> arm_interface_32; | 95 | std::unique_ptr<Core::ARM_Interface> arm_interface_32; |
| 76 | std::unique_ptr<Core::ARM_Interface> arm_interface_64; | 96 | std::unique_ptr<Core::ARM_Interface> arm_interface_64; |
| 77 | std::unique_ptr<Kernel::Scheduler> scheduler; | 97 | std::unique_ptr<Kernel::Scheduler> scheduler; |
| 78 | Core::ARM_Interface* arm_interface{}; | 98 | Core::ARM_Interface* arm_interface{}; |
| 99 | std::unique_ptr<Common::SpinLock> guard; | ||
| 79 | }; | 100 | }; |
| 80 | 101 | ||
| 81 | } // namespace Kernel | 102 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index c4c5199b1..7e26a54f4 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp | |||
| @@ -30,14 +30,15 @@ namespace { | |||
| 30 | /** | 30 | /** |
| 31 | * Sets up the primary application thread | 31 | * Sets up the primary application thread |
| 32 | * | 32 | * |
| 33 | * @param system The system instance to create the main thread under. | ||
| 33 | * @param owner_process The parent process for the main thread | 34 | * @param owner_process The parent process for the main thread |
| 34 | * @param kernel The kernel instance to create the main thread under. | ||
| 35 | * @param priority The priority to give the main thread | 35 | * @param priority The priority to give the main thread |
| 36 | */ | 36 | */ |
| 37 | void SetupMainThread(Process& owner_process, KernelCore& kernel, u32 priority, VAddr stack_top) { | 37 | void SetupMainThread(Core::System& system, Process& owner_process, u32 priority, VAddr stack_top) { |
| 38 | const VAddr entry_point = owner_process.PageTable().GetCodeRegionStart(); | 38 | const VAddr entry_point = owner_process.PageTable().GetCodeRegionStart(); |
| 39 | auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0, | 39 | ThreadType type = THREADTYPE_USER; |
| 40 | owner_process.GetIdealCore(), stack_top, owner_process); | 40 | auto thread_res = Thread::Create(system, type, "main", entry_point, priority, 0, |
| 41 | owner_process.GetIdealCore(), stack_top, &owner_process); | ||
| 41 | 42 | ||
| 42 | std::shared_ptr<Thread> thread = std::move(thread_res).Unwrap(); | 43 | std::shared_ptr<Thread> thread = std::move(thread_res).Unwrap(); |
| 43 | 44 | ||
| @@ -48,8 +49,12 @@ void SetupMainThread(Process& owner_process, KernelCore& kernel, u32 priority, V | |||
| 48 | thread->GetContext32().cpu_registers[1] = thread_handle; | 49 | thread->GetContext32().cpu_registers[1] = thread_handle; |
| 49 | thread->GetContext64().cpu_registers[1] = thread_handle; | 50 | thread->GetContext64().cpu_registers[1] = thread_handle; |
| 50 | 51 | ||
| 52 | auto& kernel = system.Kernel(); | ||
| 51 | // Threads by default are dormant, wake up the main thread so it runs when the scheduler fires | 53 | // Threads by default are dormant, wake up the main thread so it runs when the scheduler fires |
| 52 | thread->ResumeFromWait(); | 54 | { |
| 55 | SchedulerLock lock{kernel}; | ||
| 56 | thread->SetStatus(ThreadStatus::Ready); | ||
| 57 | } | ||
| 53 | } | 58 | } |
| 54 | } // Anonymous namespace | 59 | } // Anonymous namespace |
| 55 | 60 | ||
| @@ -294,7 +299,7 @@ void Process::Run(s32 main_thread_priority, u64 stack_size) { | |||
| 294 | 299 | ||
| 295 | ChangeStatus(ProcessStatus::Running); | 300 | ChangeStatus(ProcessStatus::Running); |
| 296 | 301 | ||
| 297 | SetupMainThread(*this, kernel, main_thread_priority, main_thread_stack_top); | 302 | SetupMainThread(system, *this, main_thread_priority, main_thread_stack_top); |
| 298 | resource_limit->Reserve(ResourceType::Threads, 1); | 303 | resource_limit->Reserve(ResourceType::Threads, 1); |
| 299 | resource_limit->Reserve(ResourceType::PhysicalMemory, main_thread_stack_size); | 304 | resource_limit->Reserve(ResourceType::PhysicalMemory, main_thread_stack_size); |
| 300 | } | 305 | } |
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index 1140c72a3..5166020a0 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp | |||
| @@ -11,11 +11,15 @@ | |||
| 11 | #include <utility> | 11 | #include <utility> |
| 12 | 12 | ||
| 13 | #include "common/assert.h" | 13 | #include "common/assert.h" |
| 14 | #include "common/bit_util.h" | ||
| 15 | #include "common/fiber.h" | ||
| 14 | #include "common/logging/log.h" | 16 | #include "common/logging/log.h" |
| 15 | #include "core/arm/arm_interface.h" | 17 | #include "core/arm/arm_interface.h" |
| 16 | #include "core/core.h" | 18 | #include "core/core.h" |
| 17 | #include "core/core_timing.h" | 19 | #include "core/core_timing.h" |
| 20 | #include "core/cpu_manager.h" | ||
| 18 | #include "core/hle/kernel/kernel.h" | 21 | #include "core/hle/kernel/kernel.h" |
| 22 | #include "core/hle/kernel/physical_core.h" | ||
| 19 | #include "core/hle/kernel/process.h" | 23 | #include "core/hle/kernel/process.h" |
| 20 | #include "core/hle/kernel/scheduler.h" | 24 | #include "core/hle/kernel/scheduler.h" |
| 21 | #include "core/hle/kernel/time_manager.h" | 25 | #include "core/hle/kernel/time_manager.h" |
| @@ -27,78 +31,108 @@ GlobalScheduler::GlobalScheduler(KernelCore& kernel) : kernel{kernel} {} | |||
| 27 | GlobalScheduler::~GlobalScheduler() = default; | 31 | GlobalScheduler::~GlobalScheduler() = default; |
| 28 | 32 | ||
| 29 | void GlobalScheduler::AddThread(std::shared_ptr<Thread> thread) { | 33 | void GlobalScheduler::AddThread(std::shared_ptr<Thread> thread) { |
| 34 | global_list_guard.lock(); | ||
| 30 | thread_list.push_back(std::move(thread)); | 35 | thread_list.push_back(std::move(thread)); |
| 36 | global_list_guard.unlock(); | ||
| 31 | } | 37 | } |
| 32 | 38 | ||
| 33 | void GlobalScheduler::RemoveThread(std::shared_ptr<Thread> thread) { | 39 | void GlobalScheduler::RemoveThread(std::shared_ptr<Thread> thread) { |
| 40 | global_list_guard.lock(); | ||
| 34 | thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread), | 41 | thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread), |
| 35 | thread_list.end()); | 42 | thread_list.end()); |
| 43 | global_list_guard.unlock(); | ||
| 36 | } | 44 | } |
| 37 | 45 | ||
| 38 | void GlobalScheduler::UnloadThread(std::size_t core) { | 46 | u32 GlobalScheduler::SelectThreads() { |
| 39 | Scheduler& sched = kernel.Scheduler(core); | ||
| 40 | sched.UnloadThread(); | ||
| 41 | } | ||
| 42 | |||
| 43 | void GlobalScheduler::SelectThread(std::size_t core) { | ||
| 44 | const auto update_thread = [](Thread* thread, Scheduler& sched) { | 47 | const auto update_thread = [](Thread* thread, Scheduler& sched) { |
| 48 | sched.guard.lock(); | ||
| 45 | if (thread != sched.selected_thread.get()) { | 49 | if (thread != sched.selected_thread.get()) { |
| 46 | if (thread == nullptr) { | 50 | if (thread == nullptr) { |
| 47 | ++sched.idle_selection_count; | 51 | ++sched.idle_selection_count; |
| 48 | } | 52 | } |
| 49 | sched.selected_thread = SharedFrom(thread); | 53 | sched.selected_thread = SharedFrom(thread); |
| 50 | } | 54 | } |
| 51 | sched.is_context_switch_pending = sched.selected_thread != sched.current_thread; | 55 | const bool reschedule_pending = sched.selected_thread != sched.current_thread; |
| 56 | sched.is_context_switch_pending = reschedule_pending; | ||
| 52 | std::atomic_thread_fence(std::memory_order_seq_cst); | 57 | std::atomic_thread_fence(std::memory_order_seq_cst); |
| 58 | sched.guard.unlock(); | ||
| 59 | return reschedule_pending; | ||
| 53 | }; | 60 | }; |
| 54 | Scheduler& sched = kernel.Scheduler(core); | 61 | if (!is_reselection_pending.load()) { |
| 55 | Thread* current_thread = nullptr; | 62 | return 0; |
| 56 | // Step 1: Get top thread in schedule queue. | ||
| 57 | current_thread = scheduled_queue[core].empty() ? nullptr : scheduled_queue[core].front(); | ||
| 58 | if (current_thread) { | ||
| 59 | update_thread(current_thread, sched); | ||
| 60 | return; | ||
| 61 | } | 63 | } |
| 62 | // Step 2: Try selecting a suggested thread. | 64 | std::array<Thread*, Core::Hardware::NUM_CPU_CORES> top_threads{}; |
| 63 | Thread* winner = nullptr; | 65 | |
| 64 | std::set<s32> sug_cores; | 66 | u32 idle_cores{}; |
| 65 | for (auto thread : suggested_queue[core]) { | 67 | |
| 66 | s32 this_core = thread->GetProcessorID(); | 68 | // Step 1: Get top thread in schedule queue. |
| 67 | Thread* thread_on_core = nullptr; | 69 | for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { |
| 68 | if (this_core >= 0) { | 70 | Thread* top_thread = |
| 69 | thread_on_core = scheduled_queue[this_core].front(); | 71 | scheduled_queue[core].empty() ? nullptr : scheduled_queue[core].front(); |
| 70 | } | 72 | if (top_thread != nullptr) { |
| 71 | if (this_core < 0 || thread != thread_on_core) { | 73 | // TODO(Blinkhawk): Implement Thread Pinning |
| 72 | winner = thread; | 74 | } else { |
| 73 | break; | 75 | idle_cores |= (1ul << core); |
| 74 | } | 76 | } |
| 75 | sug_cores.insert(this_core); | 77 | top_threads[core] = top_thread; |
| 76 | } | 78 | } |
| 77 | // if we got a suggested thread, select it, else do a second pass. | 79 | |
| 78 | if (winner && winner->GetPriority() > 2) { | 80 | while (idle_cores != 0) { |
| 79 | if (winner->IsRunning()) { | 81 | u32 core_id = Common::CountTrailingZeroes32(idle_cores); |
| 80 | UnloadThread(static_cast<u32>(winner->GetProcessorID())); | 82 | |
| 83 | if (!suggested_queue[core_id].empty()) { | ||
| 84 | std::array<s32, Core::Hardware::NUM_CPU_CORES> migration_candidates{}; | ||
| 85 | std::size_t num_candidates = 0; | ||
| 86 | auto iter = suggested_queue[core_id].begin(); | ||
| 87 | Thread* suggested = nullptr; | ||
| 88 | // Step 2: Try selecting a suggested thread. | ||
| 89 | while (iter != suggested_queue[core_id].end()) { | ||
| 90 | suggested = *iter; | ||
| 91 | iter++; | ||
| 92 | s32 suggested_core_id = suggested->GetProcessorID(); | ||
| 93 | Thread* top_thread = | ||
| 94 | suggested_core_id > 0 ? top_threads[suggested_core_id] : nullptr; | ||
| 95 | if (top_thread != suggested) { | ||
| 96 | if (top_thread != nullptr && | ||
| 97 | top_thread->GetPriority() < THREADPRIO_MAX_CORE_MIGRATION) { | ||
| 98 | suggested = nullptr; | ||
| 99 | break; | ||
| 100 | // There's a too high thread to do core migration, cancel | ||
| 101 | } | ||
| 102 | TransferToCore(suggested->GetPriority(), static_cast<s32>(core_id), suggested); | ||
| 103 | break; | ||
| 104 | } | ||
| 105 | migration_candidates[num_candidates++] = suggested_core_id; | ||
| 106 | } | ||
| 107 | // Step 3: Select a suggested thread from another core | ||
| 108 | if (suggested == nullptr) { | ||
| 109 | for (std::size_t i = 0; i < num_candidates; i++) { | ||
| 110 | s32 candidate_core = migration_candidates[i]; | ||
| 111 | suggested = top_threads[candidate_core]; | ||
| 112 | auto it = scheduled_queue[candidate_core].begin(); | ||
| 113 | it++; | ||
| 114 | Thread* next = it != scheduled_queue[candidate_core].end() ? *it : nullptr; | ||
| 115 | if (next != nullptr) { | ||
| 116 | TransferToCore(suggested->GetPriority(), static_cast<s32>(core_id), | ||
| 117 | suggested); | ||
| 118 | top_threads[candidate_core] = next; | ||
| 119 | break; | ||
| 120 | } | ||
| 121 | } | ||
| 122 | } | ||
| 123 | top_threads[core_id] = suggested; | ||
| 81 | } | 124 | } |
| 82 | TransferToCore(winner->GetPriority(), static_cast<s32>(core), winner); | 125 | |
| 83 | update_thread(winner, sched); | 126 | idle_cores &= ~(1ul << core_id); |
| 84 | return; | ||
| 85 | } | 127 | } |
| 86 | // Step 3: Select a suggested thread from another core | 128 | u32 cores_needing_context_switch{}; |
| 87 | for (auto& src_core : sug_cores) { | 129 | for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { |
| 88 | auto it = scheduled_queue[src_core].begin(); | 130 | Scheduler& sched = kernel.Scheduler(core); |
| 89 | it++; | 131 | if (update_thread(top_threads[core], sched)) { |
| 90 | if (it != scheduled_queue[src_core].end()) { | 132 | cores_needing_context_switch |= (1ul << core); |
| 91 | Thread* thread_on_core = scheduled_queue[src_core].front(); | ||
| 92 | Thread* to_change = *it; | ||
| 93 | if (thread_on_core->IsRunning() || to_change->IsRunning()) { | ||
| 94 | UnloadThread(static_cast<u32>(src_core)); | ||
| 95 | } | ||
| 96 | TransferToCore(thread_on_core->GetPriority(), static_cast<s32>(core), thread_on_core); | ||
| 97 | current_thread = thread_on_core; | ||
| 98 | break; | ||
| 99 | } | 133 | } |
| 100 | } | 134 | } |
| 101 | update_thread(current_thread, sched); | 135 | return cores_needing_context_switch; |
| 102 | } | 136 | } |
| 103 | 137 | ||
| 104 | bool GlobalScheduler::YieldThread(Thread* yielding_thread) { | 138 | bool GlobalScheduler::YieldThread(Thread* yielding_thread) { |
| @@ -153,9 +187,6 @@ bool GlobalScheduler::YieldThreadAndBalanceLoad(Thread* yielding_thread) { | |||
| 153 | 187 | ||
| 154 | if (winner != nullptr) { | 188 | if (winner != nullptr) { |
| 155 | if (winner != yielding_thread) { | 189 | if (winner != yielding_thread) { |
| 156 | if (winner->IsRunning()) { | ||
| 157 | UnloadThread(static_cast<u32>(winner->GetProcessorID())); | ||
| 158 | } | ||
| 159 | TransferToCore(winner->GetPriority(), s32(core_id), winner); | 190 | TransferToCore(winner->GetPriority(), s32(core_id), winner); |
| 160 | } | 191 | } |
| 161 | } else { | 192 | } else { |
| @@ -195,9 +226,6 @@ bool GlobalScheduler::YieldThreadAndWaitForLoadBalancing(Thread* yielding_thread | |||
| 195 | } | 226 | } |
| 196 | if (winner != nullptr) { | 227 | if (winner != nullptr) { |
| 197 | if (winner != yielding_thread) { | 228 | if (winner != yielding_thread) { |
| 198 | if (winner->IsRunning()) { | ||
| 199 | UnloadThread(static_cast<u32>(winner->GetProcessorID())); | ||
| 200 | } | ||
| 201 | TransferToCore(winner->GetPriority(), static_cast<s32>(core_id), winner); | 229 | TransferToCore(winner->GetPriority(), static_cast<s32>(core_id), winner); |
| 202 | } | 230 | } |
| 203 | } else { | 231 | } else { |
| @@ -213,7 +241,9 @@ void GlobalScheduler::PreemptThreads() { | |||
| 213 | const u32 priority = preemption_priorities[core_id]; | 241 | const u32 priority = preemption_priorities[core_id]; |
| 214 | 242 | ||
| 215 | if (scheduled_queue[core_id].size(priority) > 0) { | 243 | if (scheduled_queue[core_id].size(priority) > 0) { |
| 216 | scheduled_queue[core_id].front(priority)->IncrementYieldCount(); | 244 | if (scheduled_queue[core_id].size(priority) > 1) { |
| 245 | scheduled_queue[core_id].front(priority)->IncrementYieldCount(); | ||
| 246 | } | ||
| 217 | scheduled_queue[core_id].yield(priority); | 247 | scheduled_queue[core_id].yield(priority); |
| 218 | if (scheduled_queue[core_id].size(priority) > 1) { | 248 | if (scheduled_queue[core_id].size(priority) > 1) { |
| 219 | scheduled_queue[core_id].front(priority)->IncrementYieldCount(); | 249 | scheduled_queue[core_id].front(priority)->IncrementYieldCount(); |
| @@ -247,9 +277,6 @@ void GlobalScheduler::PreemptThreads() { | |||
| 247 | } | 277 | } |
| 248 | 278 | ||
| 249 | if (winner != nullptr) { | 279 | if (winner != nullptr) { |
| 250 | if (winner->IsRunning()) { | ||
| 251 | UnloadThread(static_cast<u32>(winner->GetProcessorID())); | ||
| 252 | } | ||
| 253 | TransferToCore(winner->GetPriority(), s32(core_id), winner); | 280 | TransferToCore(winner->GetPriority(), s32(core_id), winner); |
| 254 | current_thread = | 281 | current_thread = |
| 255 | winner->GetPriority() <= current_thread->GetPriority() ? winner : current_thread; | 282 | winner->GetPriority() <= current_thread->GetPriority() ? winner : current_thread; |
| @@ -280,9 +307,6 @@ void GlobalScheduler::PreemptThreads() { | |||
| 280 | } | 307 | } |
| 281 | 308 | ||
| 282 | if (winner != nullptr) { | 309 | if (winner != nullptr) { |
| 283 | if (winner->IsRunning()) { | ||
| 284 | UnloadThread(static_cast<u32>(winner->GetProcessorID())); | ||
| 285 | } | ||
| 286 | TransferToCore(winner->GetPriority(), s32(core_id), winner); | 310 | TransferToCore(winner->GetPriority(), s32(core_id), winner); |
| 287 | current_thread = winner; | 311 | current_thread = winner; |
| 288 | } | 312 | } |
| @@ -292,6 +316,28 @@ void GlobalScheduler::PreemptThreads() { | |||
| 292 | } | 316 | } |
| 293 | } | 317 | } |
| 294 | 318 | ||
| 319 | void GlobalScheduler::EnableInterruptAndSchedule(u32 cores_pending_reschedule, | ||
| 320 | Core::EmuThreadHandle global_thread) { | ||
| 321 | u32 current_core = global_thread.host_handle; | ||
| 322 | bool must_context_switch = global_thread.guest_handle != InvalidHandle && | ||
| 323 | (current_core < Core::Hardware::NUM_CPU_CORES); | ||
| 324 | while (cores_pending_reschedule != 0) { | ||
| 325 | u32 core = Common::CountTrailingZeroes32(cores_pending_reschedule); | ||
| 326 | ASSERT(core < Core::Hardware::NUM_CPU_CORES); | ||
| 327 | if (!must_context_switch || core != current_core) { | ||
| 328 | auto& phys_core = kernel.PhysicalCore(core); | ||
| 329 | phys_core.Interrupt(); | ||
| 330 | } else { | ||
| 331 | must_context_switch = true; | ||
| 332 | } | ||
| 333 | cores_pending_reschedule &= ~(1ul << core); | ||
| 334 | } | ||
| 335 | if (must_context_switch) { | ||
| 336 | auto& core_scheduler = kernel.CurrentScheduler(); | ||
| 337 | core_scheduler.TryDoContextSwitch(); | ||
| 338 | } | ||
| 339 | } | ||
| 340 | |||
| 295 | void GlobalScheduler::Suggest(u32 priority, std::size_t core, Thread* thread) { | 341 | void GlobalScheduler::Suggest(u32 priority, std::size_t core, Thread* thread) { |
| 296 | suggested_queue[core].add(thread, priority); | 342 | suggested_queue[core].add(thread, priority); |
| 297 | } | 343 | } |
| @@ -349,6 +395,108 @@ bool GlobalScheduler::AskForReselectionOrMarkRedundant(Thread* current_thread, | |||
| 349 | } | 395 | } |
| 350 | } | 396 | } |
| 351 | 397 | ||
| 398 | void GlobalScheduler::AdjustSchedulingOnStatus(Thread* thread, u32 old_flags) { | ||
| 399 | if (old_flags == thread->scheduling_state) { | ||
| 400 | return; | ||
| 401 | } | ||
| 402 | |||
| 403 | if (static_cast<ThreadSchedStatus>(old_flags & static_cast<u32>(ThreadSchedMasks::LowMask)) == | ||
| 404 | ThreadSchedStatus::Runnable) { | ||
| 405 | // In this case the thread was running, now it's pausing/exitting | ||
| 406 | if (thread->processor_id >= 0) { | ||
| 407 | Unschedule(thread->current_priority, static_cast<u32>(thread->processor_id), thread); | ||
| 408 | } | ||
| 409 | |||
| 410 | for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 411 | if (core != static_cast<u32>(thread->processor_id) && | ||
| 412 | ((thread->affinity_mask >> core) & 1) != 0) { | ||
| 413 | Unsuggest(thread->current_priority, core, thread); | ||
| 414 | } | ||
| 415 | } | ||
| 416 | } else if (thread->GetSchedulingStatus() == ThreadSchedStatus::Runnable) { | ||
| 417 | // The thread is now set to running from being stopped | ||
| 418 | if (thread->processor_id >= 0) { | ||
| 419 | Schedule(thread->current_priority, static_cast<u32>(thread->processor_id), thread); | ||
| 420 | } | ||
| 421 | |||
| 422 | for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 423 | if (core != static_cast<u32>(thread->processor_id) && | ||
| 424 | ((thread->affinity_mask >> core) & 1) != 0) { | ||
| 425 | Suggest(thread->current_priority, core, thread); | ||
| 426 | } | ||
| 427 | } | ||
| 428 | } | ||
| 429 | |||
| 430 | SetReselectionPending(); | ||
| 431 | } | ||
| 432 | |||
| 433 | void GlobalScheduler::AdjustSchedulingOnPriority(Thread* thread, u32 old_priority) { | ||
| 434 | if (thread->GetSchedulingStatus() != ThreadSchedStatus::Runnable) { | ||
| 435 | return; | ||
| 436 | } | ||
| 437 | if (thread->processor_id >= 0) { | ||
| 438 | Unschedule(old_priority, static_cast<u32>(thread->processor_id), thread); | ||
| 439 | } | ||
| 440 | |||
| 441 | for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 442 | if (core != static_cast<u32>(thread->processor_id) && | ||
| 443 | ((thread->affinity_mask >> core) & 1) != 0) { | ||
| 444 | Unsuggest(old_priority, core, thread); | ||
| 445 | } | ||
| 446 | } | ||
| 447 | |||
| 448 | if (thread->processor_id >= 0) { | ||
| 449 | // TODO(Blinkhawk): compare it with current thread running on current core, instead of | ||
| 450 | // checking running | ||
| 451 | if (thread->IsRunning()) { | ||
| 452 | SchedulePrepend(thread->current_priority, static_cast<u32>(thread->processor_id), | ||
| 453 | thread); | ||
| 454 | } else { | ||
| 455 | Schedule(thread->current_priority, static_cast<u32>(thread->processor_id), thread); | ||
| 456 | } | ||
| 457 | } | ||
| 458 | |||
| 459 | for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 460 | if (core != static_cast<u32>(thread->processor_id) && | ||
| 461 | ((thread->affinity_mask >> core) & 1) != 0) { | ||
| 462 | Suggest(thread->current_priority, core, thread); | ||
| 463 | } | ||
| 464 | } | ||
| 465 | thread->IncrementYieldCount(); | ||
| 466 | SetReselectionPending(); | ||
| 467 | } | ||
| 468 | |||
| 469 | void GlobalScheduler::AdjustSchedulingOnAffinity(Thread* thread, u64 old_affinity_mask, | ||
| 470 | s32 old_core) { | ||
| 471 | if (thread->GetSchedulingStatus() != ThreadSchedStatus::Runnable || | ||
| 472 | thread->current_priority >= THREADPRIO_COUNT) { | ||
| 473 | return; | ||
| 474 | } | ||
| 475 | |||
| 476 | for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 477 | if (((old_affinity_mask >> core) & 1) != 0) { | ||
| 478 | if (core == static_cast<u32>(old_core)) { | ||
| 479 | Unschedule(thread->current_priority, core, thread); | ||
| 480 | } else { | ||
| 481 | Unsuggest(thread->current_priority, core, thread); | ||
| 482 | } | ||
| 483 | } | ||
| 484 | } | ||
| 485 | |||
| 486 | for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 487 | if (((thread->affinity_mask >> core) & 1) != 0) { | ||
| 488 | if (core == static_cast<u32>(thread->processor_id)) { | ||
| 489 | Schedule(thread->current_priority, core, thread); | ||
| 490 | } else { | ||
| 491 | Suggest(thread->current_priority, core, thread); | ||
| 492 | } | ||
| 493 | } | ||
| 494 | } | ||
| 495 | |||
| 496 | thread->IncrementYieldCount(); | ||
| 497 | SetReselectionPending(); | ||
| 498 | } | ||
| 499 | |||
| 352 | void GlobalScheduler::Shutdown() { | 500 | void GlobalScheduler::Shutdown() { |
| 353 | for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | 501 | for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { |
| 354 | scheduled_queue[core].clear(); | 502 | scheduled_queue[core].clear(); |
| @@ -374,13 +522,12 @@ void GlobalScheduler::Unlock() { | |||
| 374 | ASSERT(scope_lock > 0); | 522 | ASSERT(scope_lock > 0); |
| 375 | return; | 523 | return; |
| 376 | } | 524 | } |
| 377 | for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { | 525 | u32 cores_pending_reschedule = SelectThreads(); |
| 378 | SelectThread(i); | 526 | Core::EmuThreadHandle leaving_thread = current_owner; |
| 379 | } | ||
| 380 | current_owner = Core::EmuThreadHandle::InvalidHandle(); | 527 | current_owner = Core::EmuThreadHandle::InvalidHandle(); |
| 381 | scope_lock = 1; | 528 | scope_lock = 1; |
| 382 | inner_lock.unlock(); | 529 | inner_lock.unlock(); |
| 383 | // TODO(Blinkhawk): Setup the interrupts and change context on current core. | 530 | EnableInterruptAndSchedule(cores_pending_reschedule, leaving_thread); |
| 384 | } | 531 | } |
| 385 | 532 | ||
| 386 | Scheduler::Scheduler(Core::System& system, std::size_t core_id) | 533 | Scheduler::Scheduler(Core::System& system, std::size_t core_id) |
| @@ -393,56 +540,83 @@ bool Scheduler::HaveReadyThreads() const { | |||
| 393 | } | 540 | } |
| 394 | 541 | ||
| 395 | Thread* Scheduler::GetCurrentThread() const { | 542 | Thread* Scheduler::GetCurrentThread() const { |
| 396 | return current_thread.get(); | 543 | if (current_thread) { |
| 544 | return current_thread.get(); | ||
| 545 | } | ||
| 546 | return idle_thread.get(); | ||
| 397 | } | 547 | } |
| 398 | 548 | ||
| 399 | Thread* Scheduler::GetSelectedThread() const { | 549 | Thread* Scheduler::GetSelectedThread() const { |
| 400 | return selected_thread.get(); | 550 | return selected_thread.get(); |
| 401 | } | 551 | } |
| 402 | 552 | ||
| 403 | void Scheduler::SelectThreads() { | ||
| 404 | system.GlobalScheduler().SelectThread(core_id); | ||
| 405 | } | ||
| 406 | |||
| 407 | u64 Scheduler::GetLastContextSwitchTicks() const { | 553 | u64 Scheduler::GetLastContextSwitchTicks() const { |
| 408 | return last_context_switch_time; | 554 | return last_context_switch_time; |
| 409 | } | 555 | } |
| 410 | 556 | ||
| 411 | void Scheduler::TryDoContextSwitch() { | 557 | void Scheduler::TryDoContextSwitch() { |
| 558 | auto& phys_core = system.Kernel().CurrentPhysicalCore(); | ||
| 559 | if (phys_core.IsInterrupted()) { | ||
| 560 | phys_core.ClearInterrupt(); | ||
| 561 | } | ||
| 562 | guard.lock(); | ||
| 412 | if (is_context_switch_pending) { | 563 | if (is_context_switch_pending) { |
| 413 | SwitchContext(); | 564 | SwitchContext(); |
| 565 | } else { | ||
| 566 | guard.unlock(); | ||
| 414 | } | 567 | } |
| 415 | } | 568 | } |
| 416 | 569 | ||
| 417 | void Scheduler::UnloadThread() { | 570 | void Scheduler::OnThreadStart() { |
| 418 | Thread* const previous_thread = GetCurrentThread(); | 571 | SwitchContextStep2(); |
| 419 | Process* const previous_process = system.Kernel().CurrentProcess(); | 572 | } |
| 420 | 573 | ||
| 421 | UpdateLastContextSwitchTime(previous_thread, previous_process); | 574 | void Scheduler::SwitchContextStep2() { |
| 575 | Thread* previous_thread = current_thread.get(); | ||
| 576 | Thread* new_thread = selected_thread.get(); | ||
| 422 | 577 | ||
| 423 | // Save context for previous thread | 578 | // Load context of new thread |
| 424 | if (previous_thread) { | 579 | Process* const previous_process = |
| 425 | system.ArmInterface(core_id).SaveContext(previous_thread->GetContext32()); | 580 | previous_thread != nullptr ? previous_thread->GetOwnerProcess() : nullptr; |
| 426 | system.ArmInterface(core_id).SaveContext(previous_thread->GetContext64()); | ||
| 427 | // Save the TPIDR_EL0 system register in case it was modified. | ||
| 428 | previous_thread->SetTPIDR_EL0(system.ArmInterface(core_id).GetTPIDR_EL0()); | ||
| 429 | 581 | ||
| 430 | if (previous_thread->GetStatus() == ThreadStatus::Running) { | 582 | if (new_thread) { |
| 431 | // This is only the case when a reschedule is triggered without the current thread | 583 | new_thread->context_guard.lock(); |
| 432 | // yielding execution (i.e. an event triggered, system core time-sliced, etc) | 584 | ASSERT_MSG(new_thread->GetProcessorID() == s32(this->core_id), |
| 433 | previous_thread->SetStatus(ThreadStatus::Ready); | 585 | "Thread must be assigned to this core."); |
| 586 | ASSERT_MSG(new_thread->GetStatus() == ThreadStatus::Ready, | ||
| 587 | "Thread must be ready to become running."); | ||
| 588 | |||
| 589 | // Cancel any outstanding wakeup events for this thread | ||
| 590 | current_thread = SharedFrom(new_thread); | ||
| 591 | new_thread->SetStatus(ThreadStatus::Running); | ||
| 592 | new_thread->SetIsRunning(true); | ||
| 593 | |||
| 594 | auto* const thread_owner_process = current_thread->GetOwnerProcess(); | ||
| 595 | if (previous_process != thread_owner_process && thread_owner_process != nullptr) { | ||
| 596 | system.Kernel().MakeCurrentProcess(thread_owner_process); | ||
| 434 | } | 597 | } |
| 435 | previous_thread->SetIsRunning(false); | 598 | if (!new_thread->IsHLEThread()) { |
| 599 | auto& cpu_core = system.ArmInterface(core_id); | ||
| 600 | cpu_core.LoadContext(new_thread->GetContext32()); | ||
| 601 | cpu_core.LoadContext(new_thread->GetContext64()); | ||
| 602 | cpu_core.SetTlsAddress(new_thread->GetTLSAddress()); | ||
| 603 | cpu_core.SetTPIDR_EL0(new_thread->GetTPIDR_EL0()); | ||
| 604 | } | ||
| 605 | } else { | ||
| 606 | current_thread = nullptr; | ||
| 607 | // Note: We do not reset the current process and current page table when idling because | ||
| 608 | // technically we haven't changed processes, our threads are just paused. | ||
| 436 | } | 609 | } |
| 437 | current_thread = nullptr; | 610 | guard.unlock(); |
| 438 | } | 611 | } |
| 439 | 612 | ||
| 440 | void Scheduler::SwitchContext() { | 613 | void Scheduler::SwitchContext() { |
| 441 | Thread* const previous_thread = GetCurrentThread(); | 614 | Thread* previous_thread = current_thread.get(); |
| 442 | Thread* const new_thread = GetSelectedThread(); | 615 | Thread* new_thread = selected_thread.get(); |
| 443 | 616 | ||
| 444 | is_context_switch_pending = false; | 617 | is_context_switch_pending = false; |
| 445 | if (new_thread == previous_thread) { | 618 | if (new_thread == previous_thread) { |
| 619 | guard.unlock(); | ||
| 446 | return; | 620 | return; |
| 447 | } | 621 | } |
| 448 | 622 | ||
| @@ -452,51 +626,44 @@ void Scheduler::SwitchContext() { | |||
| 452 | 626 | ||
| 453 | // Save context for previous thread | 627 | // Save context for previous thread |
| 454 | if (previous_thread) { | 628 | if (previous_thread) { |
| 455 | system.ArmInterface(core_id).SaveContext(previous_thread->GetContext32()); | 629 | if (!previous_thread->IsHLEThread()) { |
| 456 | system.ArmInterface(core_id).SaveContext(previous_thread->GetContext64()); | 630 | auto& cpu_core = system.ArmInterface(core_id); |
| 457 | // Save the TPIDR_EL0 system register in case it was modified. | 631 | cpu_core.SaveContext(previous_thread->GetContext32()); |
| 458 | previous_thread->SetTPIDR_EL0(system.ArmInterface(core_id).GetTPIDR_EL0()); | 632 | cpu_core.SaveContext(previous_thread->GetContext64()); |
| 633 | // Save the TPIDR_EL0 system register in case it was modified. | ||
| 634 | previous_thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0()); | ||
| 459 | 635 | ||
| 636 | } | ||
| 460 | if (previous_thread->GetStatus() == ThreadStatus::Running) { | 637 | if (previous_thread->GetStatus() == ThreadStatus::Running) { |
| 461 | // This is only the case when a reschedule is triggered without the current thread | ||
| 462 | // yielding execution (i.e. an event triggered, system core time-sliced, etc) | ||
| 463 | previous_thread->SetStatus(ThreadStatus::Ready); | 638 | previous_thread->SetStatus(ThreadStatus::Ready); |
| 464 | } | 639 | } |
| 465 | previous_thread->SetIsRunning(false); | 640 | previous_thread->SetIsRunning(false); |
| 641 | previous_thread->context_guard.unlock(); | ||
| 466 | } | 642 | } |
| 467 | 643 | ||
| 468 | // Load context of new thread | 644 | std::shared_ptr<Common::Fiber> old_context; |
| 469 | if (new_thread) { | 645 | if (previous_thread != nullptr) { |
| 470 | ASSERT_MSG(new_thread->GetProcessorID() == s32(this->core_id), | 646 | old_context = previous_thread->GetHostContext(); |
| 471 | "Thread must be assigned to this core."); | 647 | } else { |
| 472 | ASSERT_MSG(new_thread->GetStatus() == ThreadStatus::Ready, | 648 | old_context = idle_thread->GetHostContext(); |
| 473 | "Thread must be ready to become running."); | 649 | } |
| 474 | |||
| 475 | // Cancel any outstanding wakeup events for this thread | ||
| 476 | new_thread->CancelWakeupTimer(); | ||
| 477 | current_thread = SharedFrom(new_thread); | ||
| 478 | new_thread->SetStatus(ThreadStatus::Running); | ||
| 479 | new_thread->SetIsRunning(true); | ||
| 480 | |||
| 481 | auto* const thread_owner_process = current_thread->GetOwnerProcess(); | ||
| 482 | if (previous_process != thread_owner_process) { | ||
| 483 | system.Kernel().MakeCurrentProcess(thread_owner_process); | ||
| 484 | } | ||
| 485 | 650 | ||
| 486 | system.ArmInterface(core_id).LoadContext(new_thread->GetContext32()); | 651 | std::shared_ptr<Common::Fiber> next_context; |
| 487 | system.ArmInterface(core_id).LoadContext(new_thread->GetContext64()); | 652 | if (new_thread != nullptr) { |
| 488 | system.ArmInterface(core_id).SetTlsAddress(new_thread->GetTLSAddress()); | 653 | next_context = new_thread->GetHostContext(); |
| 489 | system.ArmInterface(core_id).SetTPIDR_EL0(new_thread->GetTPIDR_EL0()); | ||
| 490 | } else { | 654 | } else { |
| 491 | current_thread = nullptr; | 655 | next_context = idle_thread->GetHostContext(); |
| 492 | // Note: We do not reset the current process and current page table when idling because | ||
| 493 | // technically we haven't changed processes, our threads are just paused. | ||
| 494 | } | 656 | } |
| 657 | |||
| 658 | Common::Fiber::YieldTo(old_context, next_context); | ||
| 659 | /// When a thread wakes up, the scheduler may have changed to other in another core. | ||
| 660 | auto& next_scheduler = system.Kernel().CurrentScheduler(); | ||
| 661 | next_scheduler.SwitchContextStep2(); | ||
| 495 | } | 662 | } |
| 496 | 663 | ||
| 497 | void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) { | 664 | void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) { |
| 498 | const u64 prev_switch_ticks = last_context_switch_time; | 665 | const u64 prev_switch_ticks = last_context_switch_time; |
| 499 | const u64 most_recent_switch_ticks = system.CoreTiming().GetTicks(); | 666 | const u64 most_recent_switch_ticks = system.CoreTiming().GetCPUTicks(); |
| 500 | const u64 update_ticks = most_recent_switch_ticks - prev_switch_ticks; | 667 | const u64 update_ticks = most_recent_switch_ticks - prev_switch_ticks; |
| 501 | 668 | ||
| 502 | if (thread != nullptr) { | 669 | if (thread != nullptr) { |
| @@ -510,6 +677,16 @@ void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) { | |||
| 510 | last_context_switch_time = most_recent_switch_ticks; | 677 | last_context_switch_time = most_recent_switch_ticks; |
| 511 | } | 678 | } |
| 512 | 679 | ||
| 680 | void Scheduler::Initialize() { | ||
| 681 | std::string name = "Idle Thread Id:" + std::to_string(core_id); | ||
| 682 | std::function<void(void*)> init_func = system.GetCpuManager().GetIdleThreadStartFunc(); | ||
| 683 | void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater(); | ||
| 684 | ThreadType type = static_cast<ThreadType>(THREADTYPE_KERNEL | THREADTYPE_HLE | THREADTYPE_IDLE); | ||
| 685 | auto thread_res = Thread::Create(system, type, name, 0, 64, 0, static_cast<u32>(core_id), 0, | ||
| 686 | nullptr, std::move(init_func), init_func_parameter); | ||
| 687 | idle_thread = std::move(thread_res).Unwrap(); | ||
| 688 | } | ||
| 689 | |||
| 513 | void Scheduler::Shutdown() { | 690 | void Scheduler::Shutdown() { |
| 514 | current_thread = nullptr; | 691 | current_thread = nullptr; |
| 515 | selected_thread = nullptr; | 692 | selected_thread = nullptr; |
diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h index 07df33f9c..16655b03f 100644 --- a/src/core/hle/kernel/scheduler.h +++ b/src/core/hle/kernel/scheduler.h | |||
| @@ -11,6 +11,7 @@ | |||
| 11 | 11 | ||
| 12 | #include "common/common_types.h" | 12 | #include "common/common_types.h" |
| 13 | #include "common/multi_level_queue.h" | 13 | #include "common/multi_level_queue.h" |
| 14 | #include "common/spin_lock.h" | ||
| 14 | #include "core/hardware_properties.h" | 15 | #include "core/hardware_properties.h" |
| 15 | #include "core/hle/kernel/thread.h" | 16 | #include "core/hle/kernel/thread.h" |
| 16 | 17 | ||
| @@ -41,41 +42,17 @@ public: | |||
| 41 | return thread_list; | 42 | return thread_list; |
| 42 | } | 43 | } |
| 43 | 44 | ||
| 44 | /** | 45 | /// Notify the scheduler a thread's status has changed. |
| 45 | * Add a thread to the suggested queue of a cpu core. Suggested threads may be | 46 | void AdjustSchedulingOnStatus(Thread* thread, u32 old_flags); |
| 46 | * picked if no thread is scheduled to run on the core. | ||
| 47 | */ | ||
| 48 | void Suggest(u32 priority, std::size_t core, Thread* thread); | ||
| 49 | |||
| 50 | /** | ||
| 51 | * Remove a thread to the suggested queue of a cpu core. Suggested threads may be | ||
| 52 | * picked if no thread is scheduled to run on the core. | ||
| 53 | */ | ||
| 54 | void Unsuggest(u32 priority, std::size_t core, Thread* thread); | ||
| 55 | |||
| 56 | /** | ||
| 57 | * Add a thread to the scheduling queue of a cpu core. The thread is added at the | ||
| 58 | * back the queue in its priority level. | ||
| 59 | */ | ||
| 60 | void Schedule(u32 priority, std::size_t core, Thread* thread); | ||
| 61 | 47 | ||
| 62 | /** | 48 | /// Notify the scheduler a thread's priority has changed. |
| 63 | * Add a thread to the scheduling queue of a cpu core. The thread is added at the | 49 | void AdjustSchedulingOnPriority(Thread* thread, u32 old_priority); |
| 64 | * front the queue in its priority level. | ||
| 65 | */ | ||
| 66 | void SchedulePrepend(u32 priority, std::size_t core, Thread* thread); | ||
| 67 | 50 | ||
| 68 | /// Reschedule an already scheduled thread based on a new priority | 51 | /// Notify the scheduler a thread's core and/or affinity mask has changed. |
| 69 | void Reschedule(u32 priority, std::size_t core, Thread* thread); | 52 | void AdjustSchedulingOnAffinity(Thread* thread, u64 old_affinity_mask, s32 old_core); |
| 70 | |||
| 71 | /// Unschedules a thread. | ||
| 72 | void Unschedule(u32 priority, std::size_t core, Thread* thread); | ||
| 73 | |||
| 74 | /// Selects a core and forces it to unload its current thread's context | ||
| 75 | void UnloadThread(std::size_t core); | ||
| 76 | 53 | ||
| 77 | /** | 54 | /** |
| 78 | * Takes care of selecting the new scheduled thread in three steps: | 55 | * Takes care of selecting the new scheduled threads in three steps: |
| 79 | * | 56 | * |
| 80 | * 1. First a thread is selected from the top of the priority queue. If no thread | 57 | * 1. First a thread is selected from the top of the priority queue. If no thread |
| 81 | * is obtained then we move to step two, else we are done. | 58 | * is obtained then we move to step two, else we are done. |
| @@ -85,8 +62,10 @@ public: | |||
| 85 | * | 62 | * |
| 86 | * 3. Third is no suggested thread is found, we do a second pass and pick a running | 63 | * 3. Third is no suggested thread is found, we do a second pass and pick a running |
| 87 | * thread in another core and swap it with its current thread. | 64 | * thread in another core and swap it with its current thread. |
| 65 | * | ||
| 66 | * returns the cores needing scheduling. | ||
| 88 | */ | 67 | */ |
| 89 | void SelectThread(std::size_t core); | 68 | u32 SelectThreads(); |
| 90 | 69 | ||
| 91 | bool HaveReadyThreads(std::size_t core_id) const { | 70 | bool HaveReadyThreads(std::size_t core_id) const { |
| 92 | return !scheduled_queue[core_id].empty(); | 71 | return !scheduled_queue[core_id].empty(); |
| @@ -149,6 +128,39 @@ private: | |||
| 149 | /// Unlocks the scheduler, reselects threads, interrupts cores for rescheduling | 128 | /// Unlocks the scheduler, reselects threads, interrupts cores for rescheduling |
| 150 | /// and reschedules current core if needed. | 129 | /// and reschedules current core if needed. |
| 151 | void Unlock(); | 130 | void Unlock(); |
| 131 | |||
| 132 | void EnableInterruptAndSchedule(u32 cores_pending_reschedule, Core::EmuThreadHandle global_thread); | ||
| 133 | |||
| 134 | /** | ||
| 135 | * Add a thread to the suggested queue of a cpu core. Suggested threads may be | ||
| 136 | * picked if no thread is scheduled to run on the core. | ||
| 137 | */ | ||
| 138 | void Suggest(u32 priority, std::size_t core, Thread* thread); | ||
| 139 | |||
| 140 | /** | ||
| 141 | * Remove a thread to the suggested queue of a cpu core. Suggested threads may be | ||
| 142 | * picked if no thread is scheduled to run on the core. | ||
| 143 | */ | ||
| 144 | void Unsuggest(u32 priority, std::size_t core, Thread* thread); | ||
| 145 | |||
| 146 | /** | ||
| 147 | * Add a thread to the scheduling queue of a cpu core. The thread is added at the | ||
| 148 | * back the queue in its priority level. | ||
| 149 | */ | ||
| 150 | void Schedule(u32 priority, std::size_t core, Thread* thread); | ||
| 151 | |||
| 152 | /** | ||
| 153 | * Add a thread to the scheduling queue of a cpu core. The thread is added at the | ||
| 154 | * front the queue in its priority level. | ||
| 155 | */ | ||
| 156 | void SchedulePrepend(u32 priority, std::size_t core, Thread* thread); | ||
| 157 | |||
| 158 | /// Reschedule an already scheduled thread based on a new priority | ||
| 159 | void Reschedule(u32 priority, std::size_t core, Thread* thread); | ||
| 160 | |||
| 161 | /// Unschedules a thread. | ||
| 162 | void Unschedule(u32 priority, std::size_t core, Thread* thread); | ||
| 163 | |||
| 152 | /** | 164 | /** |
| 153 | * Transfers a thread into an specific core. If the destination_core is -1 | 165 | * Transfers a thread into an specific core. If the destination_core is -1 |
| 154 | * it will be unscheduled from its source code and added into its suggested | 166 | * it will be unscheduled from its source code and added into its suggested |
| @@ -174,6 +186,8 @@ private: | |||
| 174 | std::atomic<s64> scope_lock{}; | 186 | std::atomic<s64> scope_lock{}; |
| 175 | Core::EmuThreadHandle current_owner{Core::EmuThreadHandle::InvalidHandle()}; | 187 | Core::EmuThreadHandle current_owner{Core::EmuThreadHandle::InvalidHandle()}; |
| 176 | 188 | ||
| 189 | Common::SpinLock global_list_guard{}; | ||
| 190 | |||
| 177 | /// Lists all thread ids that aren't deleted/etc. | 191 | /// Lists all thread ids that aren't deleted/etc. |
| 178 | std::vector<std::shared_ptr<Thread>> thread_list; | 192 | std::vector<std::shared_ptr<Thread>> thread_list; |
| 179 | KernelCore& kernel; | 193 | KernelCore& kernel; |
| @@ -190,12 +204,6 @@ public: | |||
| 190 | /// Reschedules to the next available thread (call after current thread is suspended) | 204 | /// Reschedules to the next available thread (call after current thread is suspended) |
| 191 | void TryDoContextSwitch(); | 205 | void TryDoContextSwitch(); |
| 192 | 206 | ||
| 193 | /// Unloads currently running thread | ||
| 194 | void UnloadThread(); | ||
| 195 | |||
| 196 | /// Select the threads in top of the scheduling multilist. | ||
| 197 | void SelectThreads(); | ||
| 198 | |||
| 199 | /// Gets the current running thread | 207 | /// Gets the current running thread |
| 200 | Thread* GetCurrentThread() const; | 208 | Thread* GetCurrentThread() const; |
| 201 | 209 | ||
| @@ -209,15 +217,22 @@ public: | |||
| 209 | return is_context_switch_pending; | 217 | return is_context_switch_pending; |
| 210 | } | 218 | } |
| 211 | 219 | ||
| 220 | void Initialize(); | ||
| 221 | |||
| 212 | /// Shutdowns the scheduler. | 222 | /// Shutdowns the scheduler. |
| 213 | void Shutdown(); | 223 | void Shutdown(); |
| 214 | 224 | ||
| 225 | void OnThreadStart(); | ||
| 226 | |||
| 215 | private: | 227 | private: |
| 216 | friend class GlobalScheduler; | 228 | friend class GlobalScheduler; |
| 217 | 229 | ||
| 218 | /// Switches the CPU's active thread context to that of the specified thread | 230 | /// Switches the CPU's active thread context to that of the specified thread |
| 219 | void SwitchContext(); | 231 | void SwitchContext(); |
| 220 | 232 | ||
| 233 | /// When a thread wakes up, it must run this through it's new scheduler | ||
| 234 | void SwitchContextStep2(); | ||
| 235 | |||
| 221 | /** | 236 | /** |
| 222 | * Called on every context switch to update the internal timestamp | 237 | * Called on every context switch to update the internal timestamp |
| 223 | * This also updates the running time ticks for the given thread and | 238 | * This also updates the running time ticks for the given thread and |
| @@ -233,12 +248,15 @@ private: | |||
| 233 | 248 | ||
| 234 | std::shared_ptr<Thread> current_thread = nullptr; | 249 | std::shared_ptr<Thread> current_thread = nullptr; |
| 235 | std::shared_ptr<Thread> selected_thread = nullptr; | 250 | std::shared_ptr<Thread> selected_thread = nullptr; |
| 251 | std::shared_ptr<Thread> idle_thread = nullptr; | ||
| 236 | 252 | ||
| 237 | Core::System& system; | 253 | Core::System& system; |
| 238 | u64 last_context_switch_time = 0; | 254 | u64 last_context_switch_time = 0; |
| 239 | u64 idle_selection_count = 0; | 255 | u64 idle_selection_count = 0; |
| 240 | const std::size_t core_id; | 256 | const std::size_t core_id; |
| 241 | 257 | ||
| 258 | Common::SpinLock guard{}; | ||
| 259 | |||
| 242 | bool is_context_switch_pending = false; | 260 | bool is_context_switch_pending = false; |
| 243 | }; | 261 | }; |
| 244 | 262 | ||
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 4ae4529f5..d7f0dcabd 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp | |||
| @@ -863,9 +863,9 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha | |||
| 863 | if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) { | 863 | if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) { |
| 864 | const u64 thread_ticks = current_thread->GetTotalCPUTimeTicks(); | 864 | const u64 thread_ticks = current_thread->GetTotalCPUTimeTicks(); |
| 865 | 865 | ||
| 866 | out_ticks = thread_ticks + (core_timing.GetTicks() - prev_ctx_ticks); | 866 | out_ticks = thread_ticks + (core_timing.GetCPUTicks() - prev_ctx_ticks); |
| 867 | } else if (same_thread && info_sub_id == system.CurrentCoreIndex()) { | 867 | } else if (same_thread && info_sub_id == system.CurrentCoreIndex()) { |
| 868 | out_ticks = core_timing.GetTicks() - prev_ctx_ticks; | 868 | out_ticks = core_timing.GetCPUTicks() - prev_ctx_ticks; |
| 869 | } | 869 | } |
| 870 | 870 | ||
| 871 | *result = out_ticks; | 871 | *result = out_ticks; |
| @@ -1428,9 +1428,10 @@ static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr e | |||
| 1428 | 1428 | ||
| 1429 | ASSERT(kernel.CurrentProcess()->GetResourceLimit()->Reserve(ResourceType::Threads, 1)); | 1429 | ASSERT(kernel.CurrentProcess()->GetResourceLimit()->Reserve(ResourceType::Threads, 1)); |
| 1430 | 1430 | ||
| 1431 | ThreadType type = THREADTYPE_USER; | ||
| 1431 | CASCADE_RESULT(std::shared_ptr<Thread> thread, | 1432 | CASCADE_RESULT(std::shared_ptr<Thread> thread, |
| 1432 | Thread::Create(kernel, "", entry_point, priority, arg, processor_id, stack_top, | 1433 | Thread::Create(system, type, "", entry_point, priority, arg, processor_id, stack_top, |
| 1433 | *current_process)); | 1434 | current_process)); |
| 1434 | 1435 | ||
| 1435 | const auto new_thread_handle = current_process->GetHandleTable().Create(thread); | 1436 | const auto new_thread_handle = current_process->GetHandleTable().Create(thread); |
| 1436 | if (new_thread_handle.Failed()) { | 1437 | if (new_thread_handle.Failed()) { |
| @@ -1513,13 +1514,6 @@ static void SleepThread(Core::System& system, s64 nanoseconds) { | |||
| 1513 | } else { | 1514 | } else { |
| 1514 | current_thread->Sleep(nanoseconds); | 1515 | current_thread->Sleep(nanoseconds); |
| 1515 | } | 1516 | } |
| 1516 | |||
| 1517 | if (is_redundant) { | ||
| 1518 | // If it's redundant, the core is pretty much idle. Some games keep idling | ||
| 1519 | // a core while it's doing nothing, we advance timing to avoid costly continuous | ||
| 1520 | // calls. | ||
| 1521 | system.CoreTiming().AddTicks(2000); | ||
| 1522 | } | ||
| 1523 | system.PrepareReschedule(current_thread->GetProcessorID()); | 1517 | system.PrepareReschedule(current_thread->GetProcessorID()); |
| 1524 | } | 1518 | } |
| 1525 | 1519 | ||
| @@ -1725,10 +1719,7 @@ static u64 GetSystemTick(Core::System& system) { | |||
| 1725 | auto& core_timing = system.CoreTiming(); | 1719 | auto& core_timing = system.CoreTiming(); |
| 1726 | 1720 | ||
| 1727 | // Returns the value of cntpct_el0 (https://switchbrew.org/wiki/SVC#svcGetSystemTick) | 1721 | // Returns the value of cntpct_el0 (https://switchbrew.org/wiki/SVC#svcGetSystemTick) |
| 1728 | const u64 result{Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks())}; | 1722 | const u64 result{system.CoreTiming().GetClockTicks()}; |
| 1729 | |||
| 1730 | // Advance time to defeat dumb games that busy-wait for the frame to end. | ||
| 1731 | core_timing.AddTicks(400); | ||
| 1732 | 1723 | ||
| 1733 | return result; | 1724 | return result; |
| 1734 | } | 1725 | } |
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index db7f379ac..8cb3593db 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp | |||
| @@ -9,12 +9,14 @@ | |||
| 9 | 9 | ||
| 10 | #include "common/assert.h" | 10 | #include "common/assert.h" |
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | #include "common/fiber.h" | ||
| 12 | #include "common/logging/log.h" | 13 | #include "common/logging/log.h" |
| 13 | #include "common/thread_queue_list.h" | 14 | #include "common/thread_queue_list.h" |
| 14 | #include "core/arm/arm_interface.h" | 15 | #include "core/arm/arm_interface.h" |
| 15 | #include "core/core.h" | 16 | #include "core/core.h" |
| 16 | #include "core/core_timing.h" | 17 | #include "core/core_timing.h" |
| 17 | #include "core/core_timing_util.h" | 18 | #include "core/core_timing_util.h" |
| 19 | #include "core/cpu_manager.h" | ||
| 18 | #include "core/hardware_properties.h" | 20 | #include "core/hardware_properties.h" |
| 19 | #include "core/hle/kernel/errors.h" | 21 | #include "core/hle/kernel/errors.h" |
| 20 | #include "core/hle/kernel/handle_table.h" | 22 | #include "core/hle/kernel/handle_table.h" |
| @@ -23,6 +25,7 @@ | |||
| 23 | #include "core/hle/kernel/process.h" | 25 | #include "core/hle/kernel/process.h" |
| 24 | #include "core/hle/kernel/scheduler.h" | 26 | #include "core/hle/kernel/scheduler.h" |
| 25 | #include "core/hle/kernel/thread.h" | 27 | #include "core/hle/kernel/thread.h" |
| 28 | #include "core/hle/kernel/time_manager.h" | ||
| 26 | #include "core/hle/result.h" | 29 | #include "core/hle/result.h" |
| 27 | #include "core/memory.h" | 30 | #include "core/memory.h" |
| 28 | 31 | ||
| @@ -44,6 +47,7 @@ Thread::Thread(KernelCore& kernel) : SynchronizationObject{kernel} {} | |||
| 44 | Thread::~Thread() = default; | 47 | Thread::~Thread() = default; |
| 45 | 48 | ||
| 46 | void Thread::Stop() { | 49 | void Thread::Stop() { |
| 50 | SchedulerLock lock(kernel); | ||
| 47 | // Cancel any outstanding wakeup events for this thread | 51 | // Cancel any outstanding wakeup events for this thread |
| 48 | Core::System::GetInstance().CoreTiming().UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(), | 52 | Core::System::GetInstance().CoreTiming().UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(), |
| 49 | global_handle); | 53 | global_handle); |
| @@ -71,9 +75,8 @@ void Thread::WakeAfterDelay(s64 nanoseconds) { | |||
| 71 | 75 | ||
| 72 | // This function might be called from any thread so we have to be cautious and use the | 76 | // This function might be called from any thread so we have to be cautious and use the |
| 73 | // thread-safe version of ScheduleEvent. | 77 | // thread-safe version of ScheduleEvent. |
| 74 | const s64 cycles = Core::Timing::nsToCycles(std::chrono::nanoseconds{nanoseconds}); | ||
| 75 | Core::System::GetInstance().CoreTiming().ScheduleEvent( | 78 | Core::System::GetInstance().CoreTiming().ScheduleEvent( |
| 76 | cycles, kernel.ThreadWakeupCallbackEventType(), global_handle); | 79 | nanoseconds, kernel.ThreadWakeupCallbackEventType(), global_handle); |
| 77 | } | 80 | } |
| 78 | 81 | ||
| 79 | void Thread::CancelWakeupTimer() { | 82 | void Thread::CancelWakeupTimer() { |
| @@ -125,6 +128,16 @@ void Thread::ResumeFromWait() { | |||
| 125 | SetStatus(ThreadStatus::Ready); | 128 | SetStatus(ThreadStatus::Ready); |
| 126 | } | 129 | } |
| 127 | 130 | ||
| 131 | void Thread::OnWakeUp() { | ||
| 132 | SchedulerLock lock(kernel); | ||
| 133 | if (activity == ThreadActivity::Paused) { | ||
| 134 | SetStatus(ThreadStatus::Paused); | ||
| 135 | return; | ||
| 136 | } | ||
| 137 | |||
| 138 | SetStatus(ThreadStatus::Ready); | ||
| 139 | } | ||
| 140 | |||
| 128 | void Thread::CancelWait() { | 141 | void Thread::CancelWait() { |
| 129 | if (GetSchedulingStatus() != ThreadSchedStatus::Paused) { | 142 | if (GetSchedulingStatus() != ThreadSchedStatus::Paused) { |
| 130 | is_sync_cancelled = true; | 143 | is_sync_cancelled = true; |
| @@ -153,12 +166,29 @@ static void ResetThreadContext64(Core::ARM_Interface::ThreadContext64& context, | |||
| 153 | context.fpcr = 0; | 166 | context.fpcr = 0; |
| 154 | } | 167 | } |
| 155 | 168 | ||
| 156 | ResultVal<std::shared_ptr<Thread>> Thread::Create(KernelCore& kernel, std::string name, | 169 | std::shared_ptr<Common::Fiber> Thread::GetHostContext() const { |
| 157 | VAddr entry_point, u32 priority, u64 arg, | 170 | return host_context; |
| 158 | s32 processor_id, VAddr stack_top, | 171 | } |
| 159 | Process& owner_process) { | 172 | |
| 173 | ResultVal<std::shared_ptr<Thread>> Thread::Create(Core::System& system, ThreadType type_flags, | ||
| 174 | std::string name, VAddr entry_point, u32 priority, | ||
| 175 | u64 arg, s32 processor_id, VAddr stack_top, | ||
| 176 | Process* owner_process) { | ||
| 177 | std::function<void(void*)> init_func = system.GetCpuManager().GetGuestThreadStartFunc(); | ||
| 178 | void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater(); | ||
| 179 | return Create(system, type_flags, name, entry_point, priority, arg, processor_id, stack_top, | ||
| 180 | owner_process, std::move(init_func), init_func_parameter); | ||
| 181 | } | ||
| 182 | |||
| 183 | ResultVal<std::shared_ptr<Thread>> Thread::Create(Core::System& system, ThreadType type_flags, | ||
| 184 | std::string name, VAddr entry_point, u32 priority, | ||
| 185 | u64 arg, s32 processor_id, VAddr stack_top, | ||
| 186 | Process* owner_process, | ||
| 187 | std::function<void(void*)>&& thread_start_func, | ||
| 188 | void* thread_start_parameter) { | ||
| 189 | auto& kernel = system.Kernel(); | ||
| 160 | // Check if priority is in ranged. Lowest priority -> highest priority id. | 190 | // Check if priority is in ranged. Lowest priority -> highest priority id. |
| 161 | if (priority > THREADPRIO_LOWEST) { | 191 | if (priority > THREADPRIO_LOWEST && (type_flags & THREADTYPE_IDLE == 0)) { |
| 162 | LOG_ERROR(Kernel_SVC, "Invalid thread priority: {}", priority); | 192 | LOG_ERROR(Kernel_SVC, "Invalid thread priority: {}", priority); |
| 163 | return ERR_INVALID_THREAD_PRIORITY; | 193 | return ERR_INVALID_THREAD_PRIORITY; |
| 164 | } | 194 | } |
| @@ -168,11 +198,12 @@ ResultVal<std::shared_ptr<Thread>> Thread::Create(KernelCore& kernel, std::strin | |||
| 168 | return ERR_INVALID_PROCESSOR_ID; | 198 | return ERR_INVALID_PROCESSOR_ID; |
| 169 | } | 199 | } |
| 170 | 200 | ||
| 171 | auto& system = Core::System::GetInstance(); | 201 | if (owner_process) { |
| 172 | if (!system.Memory().IsValidVirtualAddress(owner_process, entry_point)) { | 202 | if (!system.Memory().IsValidVirtualAddress(*owner_process, entry_point)) { |
| 173 | LOG_ERROR(Kernel_SVC, "(name={}): invalid entry {:016X}", name, entry_point); | 203 | LOG_ERROR(Kernel_SVC, "(name={}): invalid entry {:016X}", name, entry_point); |
| 174 | // TODO (bunnei): Find the correct error code to use here | 204 | // TODO (bunnei): Find the correct error code to use here |
| 175 | return RESULT_UNKNOWN; | 205 | return RESULT_UNKNOWN; |
| 206 | } | ||
| 176 | } | 207 | } |
| 177 | 208 | ||
| 178 | std::shared_ptr<Thread> thread = std::make_shared<Thread>(kernel); | 209 | std::shared_ptr<Thread> thread = std::make_shared<Thread>(kernel); |
| @@ -183,7 +214,7 @@ ResultVal<std::shared_ptr<Thread>> Thread::Create(KernelCore& kernel, std::strin | |||
| 183 | thread->stack_top = stack_top; | 214 | thread->stack_top = stack_top; |
| 184 | thread->tpidr_el0 = 0; | 215 | thread->tpidr_el0 = 0; |
| 185 | thread->nominal_priority = thread->current_priority = priority; | 216 | thread->nominal_priority = thread->current_priority = priority; |
| 186 | thread->last_running_ticks = system.CoreTiming().GetTicks(); | 217 | thread->last_running_ticks = 0; |
| 187 | thread->processor_id = processor_id; | 218 | thread->processor_id = processor_id; |
| 188 | thread->ideal_core = processor_id; | 219 | thread->ideal_core = processor_id; |
| 189 | thread->affinity_mask = 1ULL << processor_id; | 220 | thread->affinity_mask = 1ULL << processor_id; |
| @@ -193,16 +224,27 @@ ResultVal<std::shared_ptr<Thread>> Thread::Create(KernelCore& kernel, std::strin | |||
| 193 | thread->wait_handle = 0; | 224 | thread->wait_handle = 0; |
| 194 | thread->name = std::move(name); | 225 | thread->name = std::move(name); |
| 195 | thread->global_handle = kernel.GlobalHandleTable().Create(thread).Unwrap(); | 226 | thread->global_handle = kernel.GlobalHandleTable().Create(thread).Unwrap(); |
| 196 | thread->owner_process = &owner_process; | 227 | thread->owner_process = owner_process; |
| 197 | auto& scheduler = kernel.GlobalScheduler(); | 228 | thread->type = type_flags; |
| 198 | scheduler.AddThread(thread); | 229 | if ((type_flags & THREADTYPE_IDLE) == 0) { |
| 199 | thread->tls_address = thread->owner_process->CreateTLSRegion(); | 230 | auto& scheduler = kernel.GlobalScheduler(); |
| 200 | 231 | scheduler.AddThread(thread); | |
| 201 | thread->owner_process->RegisterThread(thread.get()); | 232 | } |
| 202 | 233 | if (owner_process) { | |
| 203 | ResetThreadContext32(thread->context_32, static_cast<u32>(stack_top), | 234 | thread->tls_address = thread->owner_process->CreateTLSRegion(); |
| 204 | static_cast<u32>(entry_point), static_cast<u32>(arg)); | 235 | thread->owner_process->RegisterThread(thread.get()); |
| 205 | ResetThreadContext64(thread->context_64, stack_top, entry_point, arg); | 236 | } else { |
| 237 | thread->tls_address = 0; | ||
| 238 | } | ||
| 239 | // TODO(peachum): move to ScheduleThread() when scheduler is added so selected core is used | ||
| 240 | // to initialize the context | ||
| 241 | if ((type_flags & THREADTYPE_HLE) == 0) { | ||
| 242 | ResetThreadContext32(thread->context_32, static_cast<u32>(stack_top), | ||
| 243 | static_cast<u32>(entry_point), static_cast<u32>(arg)); | ||
| 244 | ResetThreadContext64(thread->context_64, stack_top, entry_point, arg); | ||
| 245 | } | ||
| 246 | thread->host_context = | ||
| 247 | std::make_shared<Common::Fiber>(std::move(thread_start_func), thread_start_parameter); | ||
| 206 | 248 | ||
| 207 | return MakeResult<std::shared_ptr<Thread>>(std::move(thread)); | 249 | return MakeResult<std::shared_ptr<Thread>>(std::move(thread)); |
| 208 | } | 250 | } |
| @@ -258,7 +300,7 @@ void Thread::SetStatus(ThreadStatus new_status) { | |||
| 258 | } | 300 | } |
| 259 | 301 | ||
| 260 | if (status == ThreadStatus::Running) { | 302 | if (status == ThreadStatus::Running) { |
| 261 | last_running_ticks = Core::System::GetInstance().CoreTiming().GetTicks(); | 303 | last_running_ticks = Core::System::GetInstance().CoreTiming().GetCPUTicks(); |
| 262 | } | 304 | } |
| 263 | 305 | ||
| 264 | status = new_status; | 306 | status = new_status; |
| @@ -375,38 +417,55 @@ void Thread::SetActivity(ThreadActivity value) { | |||
| 375 | } | 417 | } |
| 376 | 418 | ||
| 377 | void Thread::Sleep(s64 nanoseconds) { | 419 | void Thread::Sleep(s64 nanoseconds) { |
| 378 | // Sleep current thread and check for next thread to schedule | 420 | Handle event_handle{}; |
| 379 | SetStatus(ThreadStatus::WaitSleep); | 421 | { |
| 422 | SchedulerLockAndSleep lock(kernel, event_handle, this, nanoseconds); | ||
| 423 | SetStatus(ThreadStatus::WaitSleep); | ||
| 424 | } | ||
| 380 | 425 | ||
| 381 | // Create an event to wake the thread up after the specified nanosecond delay has passed | 426 | if (event_handle != InvalidHandle) { |
| 382 | WakeAfterDelay(nanoseconds); | 427 | auto& time_manager = kernel.TimeManager(); |
| 428 | time_manager.UnscheduleTimeEvent(event_handle); | ||
| 429 | } | ||
| 383 | } | 430 | } |
| 384 | 431 | ||
| 385 | bool Thread::YieldSimple() { | 432 | bool Thread::YieldSimple() { |
| 386 | auto& scheduler = kernel.GlobalScheduler(); | 433 | bool result{}; |
| 387 | return scheduler.YieldThread(this); | 434 | { |
| 435 | SchedulerLock lock(kernel); | ||
| 436 | result = kernel.GlobalScheduler().YieldThread(this); | ||
| 437 | } | ||
| 438 | return result; | ||
| 388 | } | 439 | } |
| 389 | 440 | ||
| 390 | bool Thread::YieldAndBalanceLoad() { | 441 | bool Thread::YieldAndBalanceLoad() { |
| 391 | auto& scheduler = kernel.GlobalScheduler(); | 442 | bool result{}; |
| 392 | return scheduler.YieldThreadAndBalanceLoad(this); | 443 | { |
| 444 | SchedulerLock lock(kernel); | ||
| 445 | result = kernel.GlobalScheduler().YieldThreadAndBalanceLoad(this); | ||
| 446 | } | ||
| 447 | return result; | ||
| 393 | } | 448 | } |
| 394 | 449 | ||
| 395 | bool Thread::YieldAndWaitForLoadBalancing() { | 450 | bool Thread::YieldAndWaitForLoadBalancing() { |
| 396 | auto& scheduler = kernel.GlobalScheduler(); | 451 | bool result{}; |
| 397 | return scheduler.YieldThreadAndWaitForLoadBalancing(this); | 452 | { |
| 453 | SchedulerLock lock(kernel); | ||
| 454 | result = kernel.GlobalScheduler().YieldThreadAndWaitForLoadBalancing(this); | ||
| 455 | } | ||
| 456 | return result; | ||
| 398 | } | 457 | } |
| 399 | 458 | ||
| 400 | void Thread::SetSchedulingStatus(ThreadSchedStatus new_status) { | 459 | void Thread::SetSchedulingStatus(ThreadSchedStatus new_status) { |
| 401 | const u32 old_flags = scheduling_state; | 460 | const u32 old_flags = scheduling_state; |
| 402 | scheduling_state = (scheduling_state & static_cast<u32>(ThreadSchedMasks::HighMask)) | | 461 | scheduling_state = (scheduling_state & static_cast<u32>(ThreadSchedMasks::HighMask)) | |
| 403 | static_cast<u32>(new_status); | 462 | static_cast<u32>(new_status); |
| 404 | AdjustSchedulingOnStatus(old_flags); | 463 | kernel.GlobalScheduler().AdjustSchedulingOnStatus(this, old_flags); |
| 405 | } | 464 | } |
| 406 | 465 | ||
| 407 | void Thread::SetCurrentPriority(u32 new_priority) { | 466 | void Thread::SetCurrentPriority(u32 new_priority) { |
| 408 | const u32 old_priority = std::exchange(current_priority, new_priority); | 467 | const u32 old_priority = std::exchange(current_priority, new_priority); |
| 409 | AdjustSchedulingOnPriority(old_priority); | 468 | kernel.GlobalScheduler().AdjustSchedulingOnPriority(this, old_priority); |
| 410 | } | 469 | } |
| 411 | 470 | ||
| 412 | ResultCode Thread::SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask) { | 471 | ResultCode Thread::SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask) { |
| @@ -443,111 +502,12 @@ ResultCode Thread::SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask) { | |||
| 443 | processor_id = ideal_core; | 502 | processor_id = ideal_core; |
| 444 | } | 503 | } |
| 445 | } | 504 | } |
| 446 | AdjustSchedulingOnAffinity(old_affinity_mask, old_core); | 505 | kernel.GlobalScheduler().AdjustSchedulingOnAffinity(this, old_affinity_mask, old_core); |
| 447 | } | 506 | } |
| 448 | } | 507 | } |
| 449 | return RESULT_SUCCESS; | 508 | return RESULT_SUCCESS; |
| 450 | } | 509 | } |
| 451 | 510 | ||
| 452 | void Thread::AdjustSchedulingOnStatus(u32 old_flags) { | ||
| 453 | if (old_flags == scheduling_state) { | ||
| 454 | return; | ||
| 455 | } | ||
| 456 | |||
| 457 | auto& scheduler = kernel.GlobalScheduler(); | ||
| 458 | if (static_cast<ThreadSchedStatus>(old_flags & static_cast<u32>(ThreadSchedMasks::LowMask)) == | ||
| 459 | ThreadSchedStatus::Runnable) { | ||
| 460 | // In this case the thread was running, now it's pausing/exitting | ||
| 461 | if (processor_id >= 0) { | ||
| 462 | scheduler.Unschedule(current_priority, static_cast<u32>(processor_id), this); | ||
| 463 | } | ||
| 464 | |||
| 465 | for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 466 | if (core != static_cast<u32>(processor_id) && ((affinity_mask >> core) & 1) != 0) { | ||
| 467 | scheduler.Unsuggest(current_priority, core, this); | ||
| 468 | } | ||
| 469 | } | ||
| 470 | } else if (GetSchedulingStatus() == ThreadSchedStatus::Runnable) { | ||
| 471 | // The thread is now set to running from being stopped | ||
| 472 | if (processor_id >= 0) { | ||
| 473 | scheduler.Schedule(current_priority, static_cast<u32>(processor_id), this); | ||
| 474 | } | ||
| 475 | |||
| 476 | for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 477 | if (core != static_cast<u32>(processor_id) && ((affinity_mask >> core) & 1) != 0) { | ||
| 478 | scheduler.Suggest(current_priority, core, this); | ||
| 479 | } | ||
| 480 | } | ||
| 481 | } | ||
| 482 | |||
| 483 | scheduler.SetReselectionPending(); | ||
| 484 | } | ||
| 485 | |||
| 486 | void Thread::AdjustSchedulingOnPriority(u32 old_priority) { | ||
| 487 | if (GetSchedulingStatus() != ThreadSchedStatus::Runnable) { | ||
| 488 | return; | ||
| 489 | } | ||
| 490 | auto& scheduler = kernel.GlobalScheduler(); | ||
| 491 | if (processor_id >= 0) { | ||
| 492 | scheduler.Unschedule(old_priority, static_cast<u32>(processor_id), this); | ||
| 493 | } | ||
| 494 | |||
| 495 | for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 496 | if (core != static_cast<u32>(processor_id) && ((affinity_mask >> core) & 1) != 0) { | ||
| 497 | scheduler.Unsuggest(old_priority, core, this); | ||
| 498 | } | ||
| 499 | } | ||
| 500 | |||
| 501 | // Add thread to the new priority queues. | ||
| 502 | Thread* current_thread = GetCurrentThread(); | ||
| 503 | |||
| 504 | if (processor_id >= 0) { | ||
| 505 | if (current_thread == this) { | ||
| 506 | scheduler.SchedulePrepend(current_priority, static_cast<u32>(processor_id), this); | ||
| 507 | } else { | ||
| 508 | scheduler.Schedule(current_priority, static_cast<u32>(processor_id), this); | ||
| 509 | } | ||
| 510 | } | ||
| 511 | |||
| 512 | for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 513 | if (core != static_cast<u32>(processor_id) && ((affinity_mask >> core) & 1) != 0) { | ||
| 514 | scheduler.Suggest(current_priority, core, this); | ||
| 515 | } | ||
| 516 | } | ||
| 517 | |||
| 518 | scheduler.SetReselectionPending(); | ||
| 519 | } | ||
| 520 | |||
| 521 | void Thread::AdjustSchedulingOnAffinity(u64 old_affinity_mask, s32 old_core) { | ||
| 522 | auto& scheduler = kernel.GlobalScheduler(); | ||
| 523 | if (GetSchedulingStatus() != ThreadSchedStatus::Runnable || | ||
| 524 | current_priority >= THREADPRIO_COUNT) { | ||
| 525 | return; | ||
| 526 | } | ||
| 527 | |||
| 528 | for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 529 | if (((old_affinity_mask >> core) & 1) != 0) { | ||
| 530 | if (core == static_cast<u32>(old_core)) { | ||
| 531 | scheduler.Unschedule(current_priority, core, this); | ||
| 532 | } else { | ||
| 533 | scheduler.Unsuggest(current_priority, core, this); | ||
| 534 | } | ||
| 535 | } | ||
| 536 | } | ||
| 537 | |||
| 538 | for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 539 | if (((affinity_mask >> core) & 1) != 0) { | ||
| 540 | if (core == static_cast<u32>(processor_id)) { | ||
| 541 | scheduler.Schedule(current_priority, core, this); | ||
| 542 | } else { | ||
| 543 | scheduler.Suggest(current_priority, core, this); | ||
| 544 | } | ||
| 545 | } | ||
| 546 | } | ||
| 547 | |||
| 548 | scheduler.SetReselectionPending(); | ||
| 549 | } | ||
| 550 | |||
| 551 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 511 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 552 | 512 | ||
| 553 | /** | 513 | /** |
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 23fdef8a4..33d340b47 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h | |||
| @@ -9,23 +9,42 @@ | |||
| 9 | #include <vector> | 9 | #include <vector> |
| 10 | 10 | ||
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | #include "common/spin_lock.h" | ||
| 12 | #include "core/arm/arm_interface.h" | 13 | #include "core/arm/arm_interface.h" |
| 13 | #include "core/hle/kernel/object.h" | 14 | #include "core/hle/kernel/object.h" |
| 14 | #include "core/hle/kernel/synchronization_object.h" | 15 | #include "core/hle/kernel/synchronization_object.h" |
| 15 | #include "core/hle/result.h" | 16 | #include "core/hle/result.h" |
| 16 | 17 | ||
| 18 | namespace Common { | ||
| 19 | class Fiber; | ||
| 20 | } | ||
| 21 | |||
| 22 | namespace Core { | ||
| 23 | class System; | ||
| 24 | } | ||
| 25 | |||
| 17 | namespace Kernel { | 26 | namespace Kernel { |
| 18 | 27 | ||
| 28 | class GlobalScheduler; | ||
| 19 | class KernelCore; | 29 | class KernelCore; |
| 20 | class Process; | 30 | class Process; |
| 21 | class Scheduler; | 31 | class Scheduler; |
| 22 | 32 | ||
| 23 | enum ThreadPriority : u32 { | 33 | enum ThreadPriority : u32 { |
| 24 | THREADPRIO_HIGHEST = 0, ///< Highest thread priority | 34 | THREADPRIO_HIGHEST = 0, ///< Highest thread priority |
| 25 | THREADPRIO_USERLAND_MAX = 24, ///< Highest thread priority for userland apps | 35 | THREADPRIO_MAX_CORE_MIGRATION = 2, ///< Highest priority for a core migration |
| 26 | THREADPRIO_DEFAULT = 44, ///< Default thread priority for userland apps | 36 | THREADPRIO_USERLAND_MAX = 24, ///< Highest thread priority for userland apps |
| 27 | THREADPRIO_LOWEST = 63, ///< Lowest thread priority | 37 | THREADPRIO_DEFAULT = 44, ///< Default thread priority for userland apps |
| 28 | THREADPRIO_COUNT = 64, ///< Total number of possible thread priorities. | 38 | THREADPRIO_LOWEST = 63, ///< Lowest thread priority |
| 39 | THREADPRIO_COUNT = 64, ///< Total number of possible thread priorities. | ||
| 40 | }; | ||
| 41 | |||
| 42 | enum ThreadType : u32 { | ||
| 43 | THREADTYPE_USER = 0x1, | ||
| 44 | THREADTYPE_KERNEL = 0x2, | ||
| 45 | THREADTYPE_HLE = 0x4, | ||
| 46 | THREADTYPE_IDLE = 0x8, | ||
| 47 | THREADTYPE_SUSPEND = 0x10, | ||
| 29 | }; | 48 | }; |
| 30 | 49 | ||
| 31 | enum ThreadProcessorId : s32 { | 50 | enum ThreadProcessorId : s32 { |
| @@ -111,22 +130,43 @@ public: | |||
| 111 | std::function<bool(ThreadWakeupReason reason, std::shared_ptr<Thread> thread, | 130 | std::function<bool(ThreadWakeupReason reason, std::shared_ptr<Thread> thread, |
| 112 | std::shared_ptr<SynchronizationObject> object, std::size_t index)>; | 131 | std::shared_ptr<SynchronizationObject> object, std::size_t index)>; |
| 113 | 132 | ||
| 133 | /** | ||
| 134 | * Creates and returns a new thread. The new thread is immediately scheduled | ||
| 135 | * @param system The instance of the whole system | ||
| 136 | * @param name The friendly name desired for the thread | ||
| 137 | * @param entry_point The address at which the thread should start execution | ||
| 138 | * @param priority The thread's priority | ||
| 139 | * @param arg User data to pass to the thread | ||
| 140 | * @param processor_id The ID(s) of the processors on which the thread is desired to be run | ||
| 141 | * @param stack_top The address of the thread's stack top | ||
| 142 | * @param owner_process The parent process for the thread, if null, it's a kernel thread | ||
| 143 | * @return A shared pointer to the newly created thread | ||
| 144 | */ | ||
| 145 | static ResultVal<std::shared_ptr<Thread>> Create(Core::System& system, ThreadType type_flags, std::string name, | ||
| 146 | VAddr entry_point, u32 priority, u64 arg, | ||
| 147 | s32 processor_id, VAddr stack_top, | ||
| 148 | Process* owner_process); | ||
| 149 | |||
| 114 | /** | 150 | /** |
| 115 | * Creates and returns a new thread. The new thread is immediately scheduled | 151 | * Creates and returns a new thread. The new thread is immediately scheduled |
| 116 | * @param kernel The kernel instance this thread will be created under. | 152 | * @param system The instance of the whole system |
| 117 | * @param name The friendly name desired for the thread | 153 | * @param name The friendly name desired for the thread |
| 118 | * @param entry_point The address at which the thread should start execution | 154 | * @param entry_point The address at which the thread should start execution |
| 119 | * @param priority The thread's priority | 155 | * @param priority The thread's priority |
| 120 | * @param arg User data to pass to the thread | 156 | * @param arg User data to pass to the thread |
| 121 | * @param processor_id The ID(s) of the processors on which the thread is desired to be run | 157 | * @param processor_id The ID(s) of the processors on which the thread is desired to be run |
| 122 | * @param stack_top The address of the thread's stack top | 158 | * @param stack_top The address of the thread's stack top |
| 123 | * @param owner_process The parent process for the thread | 159 | * @param owner_process The parent process for the thread, if null, it's a kernel thread |
| 160 | * @param thread_start_func The function where the host context will start. | ||
| 161 | * @param thread_start_parameter The parameter which will passed to host context on init | ||
| 124 | * @return A shared pointer to the newly created thread | 162 | * @return A shared pointer to the newly created thread |
| 125 | */ | 163 | */ |
| 126 | static ResultVal<std::shared_ptr<Thread>> Create(KernelCore& kernel, std::string name, | 164 | static ResultVal<std::shared_ptr<Thread>> Create(Core::System& system, ThreadType type_flags, std::string name, |
| 127 | VAddr entry_point, u32 priority, u64 arg, | 165 | VAddr entry_point, u32 priority, u64 arg, |
| 128 | s32 processor_id, VAddr stack_top, | 166 | s32 processor_id, VAddr stack_top, |
| 129 | Process& owner_process); | 167 | Process* owner_process, |
| 168 | std::function<void(void*)>&& thread_start_func, | ||
| 169 | void* thread_start_parameter); | ||
| 130 | 170 | ||
| 131 | std::string GetName() const override { | 171 | std::string GetName() const override { |
| 132 | return name; | 172 | return name; |
| @@ -192,7 +232,9 @@ public: | |||
| 192 | } | 232 | } |
| 193 | 233 | ||
| 194 | /// Resumes a thread from waiting | 234 | /// Resumes a thread from waiting |
| 195 | void ResumeFromWait(); | 235 | void /* deprecated */ ResumeFromWait(); |
| 236 | |||
| 237 | void OnWakeUp(); | ||
| 196 | 238 | ||
| 197 | /// Cancels a waiting operation that this thread may or may not be within. | 239 | /// Cancels a waiting operation that this thread may or may not be within. |
| 198 | /// | 240 | /// |
| @@ -206,10 +248,10 @@ public: | |||
| 206 | * Schedules an event to wake up the specified thread after the specified delay | 248 | * Schedules an event to wake up the specified thread after the specified delay |
| 207 | * @param nanoseconds The time this thread will be allowed to sleep for | 249 | * @param nanoseconds The time this thread will be allowed to sleep for |
| 208 | */ | 250 | */ |
| 209 | void WakeAfterDelay(s64 nanoseconds); | 251 | void /* deprecated */ WakeAfterDelay(s64 nanoseconds); |
| 210 | 252 | ||
| 211 | /// Cancel any outstanding wakeup events for this thread | 253 | /// Cancel any outstanding wakeup events for this thread |
| 212 | void CancelWakeupTimer(); | 254 | void /* deprecated */ CancelWakeupTimer(); |
| 213 | 255 | ||
| 214 | /** | 256 | /** |
| 215 | * Sets the result after the thread awakens (from svcWaitSynchronization) | 257 | * Sets the result after the thread awakens (from svcWaitSynchronization) |
| @@ -290,6 +332,12 @@ public: | |||
| 290 | return context_64; | 332 | return context_64; |
| 291 | } | 333 | } |
| 292 | 334 | ||
| 335 | bool IsHLEThread() const { | ||
| 336 | return (type & THREADTYPE_HLE) != 0; | ||
| 337 | } | ||
| 338 | |||
| 339 | std::shared_ptr<Common::Fiber> GetHostContext() const; | ||
| 340 | |||
| 293 | ThreadStatus GetStatus() const { | 341 | ThreadStatus GetStatus() const { |
| 294 | return status; | 342 | return status; |
| 295 | } | 343 | } |
| @@ -467,16 +515,19 @@ public: | |||
| 467 | } | 515 | } |
| 468 | 516 | ||
| 469 | private: | 517 | private: |
| 518 | friend class GlobalScheduler; | ||
| 519 | friend class Scheduler; | ||
| 520 | |||
| 470 | void SetSchedulingStatus(ThreadSchedStatus new_status); | 521 | void SetSchedulingStatus(ThreadSchedStatus new_status); |
| 471 | void SetCurrentPriority(u32 new_priority); | 522 | void SetCurrentPriority(u32 new_priority); |
| 472 | ResultCode SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask); | 523 | ResultCode SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask); |
| 473 | 524 | ||
| 474 | void AdjustSchedulingOnStatus(u32 old_flags); | ||
| 475 | void AdjustSchedulingOnPriority(u32 old_priority); | ||
| 476 | void AdjustSchedulingOnAffinity(u64 old_affinity_mask, s32 old_core); | 525 | void AdjustSchedulingOnAffinity(u64 old_affinity_mask, s32 old_core); |
| 477 | 526 | ||
| 478 | ThreadContext32 context_32{}; | 527 | ThreadContext32 context_32{}; |
| 479 | ThreadContext64 context_64{}; | 528 | ThreadContext64 context_64{}; |
| 529 | Common::SpinLock context_guard{}; | ||
| 530 | std::shared_ptr<Common::Fiber> host_context{}; | ||
| 480 | 531 | ||
| 481 | u64 thread_id = 0; | 532 | u64 thread_id = 0; |
| 482 | 533 | ||
| @@ -485,6 +536,8 @@ private: | |||
| 485 | VAddr entry_point = 0; | 536 | VAddr entry_point = 0; |
| 486 | VAddr stack_top = 0; | 537 | VAddr stack_top = 0; |
| 487 | 538 | ||
| 539 | ThreadType type; | ||
| 540 | |||
| 488 | /// Nominal thread priority, as set by the emulated application. | 541 | /// Nominal thread priority, as set by the emulated application. |
| 489 | /// The nominal priority is the thread priority without priority | 542 | /// The nominal priority is the thread priority without priority |
| 490 | /// inheritance taken into account. | 543 | /// inheritance taken into account. |
diff --git a/src/core/hle/kernel/time_manager.cpp b/src/core/hle/kernel/time_manager.cpp index 21b290468..0b8f0d993 100644 --- a/src/core/hle/kernel/time_manager.cpp +++ b/src/core/hle/kernel/time_manager.cpp | |||
| @@ -19,7 +19,7 @@ TimeManager::TimeManager(Core::System& system) : system{system} { | |||
| 19 | Handle proper_handle = static_cast<Handle>(thread_handle); | 19 | Handle proper_handle = static_cast<Handle>(thread_handle); |
| 20 | std::shared_ptr<Thread> thread = | 20 | std::shared_ptr<Thread> thread = |
| 21 | this->system.Kernel().RetrieveThreadFromGlobalHandleTable(proper_handle); | 21 | this->system.Kernel().RetrieveThreadFromGlobalHandleTable(proper_handle); |
| 22 | thread->ResumeFromWait(); | 22 | thread->OnWakeUp(); |
| 23 | }); | 23 | }); |
| 24 | } | 24 | } |
| 25 | 25 | ||
diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp index 1f2131ec8..cb35919e9 100644 --- a/src/core/hle/service/hid/controllers/debug_pad.cpp +++ b/src/core/hle/service/hid/controllers/debug_pad.cpp | |||
| @@ -23,7 +23,7 @@ void Controller_DebugPad::OnRelease() {} | |||
| 23 | 23 | ||
| 24 | void Controller_DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, | 24 | void Controller_DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, |
| 25 | std::size_t size) { | 25 | std::size_t size) { |
| 26 | shared_memory.header.timestamp = core_timing.GetTicks(); | 26 | shared_memory.header.timestamp = core_timing.GetCPUTicks(); |
| 27 | shared_memory.header.total_entry_count = 17; | 27 | shared_memory.header.total_entry_count = 17; |
| 28 | 28 | ||
| 29 | if (!IsControllerActivated()) { | 29 | if (!IsControllerActivated()) { |
diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp index 6e990dd00..b7b7bfeae 100644 --- a/src/core/hle/service/hid/controllers/gesture.cpp +++ b/src/core/hle/service/hid/controllers/gesture.cpp | |||
| @@ -19,7 +19,7 @@ void Controller_Gesture::OnRelease() {} | |||
| 19 | 19 | ||
| 20 | void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, | 20 | void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, |
| 21 | std::size_t size) { | 21 | std::size_t size) { |
| 22 | shared_memory.header.timestamp = core_timing.GetTicks(); | 22 | shared_memory.header.timestamp = core_timing.GetCPUTicks(); |
| 23 | shared_memory.header.total_entry_count = 17; | 23 | shared_memory.header.total_entry_count = 17; |
| 24 | 24 | ||
| 25 | if (!IsControllerActivated()) { | 25 | if (!IsControllerActivated()) { |
diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp index 9a8d354ba..feae89525 100644 --- a/src/core/hle/service/hid/controllers/keyboard.cpp +++ b/src/core/hle/service/hid/controllers/keyboard.cpp | |||
| @@ -21,7 +21,7 @@ void Controller_Keyboard::OnRelease() {} | |||
| 21 | 21 | ||
| 22 | void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, | 22 | void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, |
| 23 | std::size_t size) { | 23 | std::size_t size) { |
| 24 | shared_memory.header.timestamp = core_timing.GetTicks(); | 24 | shared_memory.header.timestamp = core_timing.GetCPUTicks(); |
| 25 | shared_memory.header.total_entry_count = 17; | 25 | shared_memory.header.total_entry_count = 17; |
| 26 | 26 | ||
| 27 | if (!IsControllerActivated()) { | 27 | if (!IsControllerActivated()) { |
diff --git a/src/core/hle/service/hid/controllers/mouse.cpp b/src/core/hle/service/hid/controllers/mouse.cpp index 93d88ea50..ac40989c5 100644 --- a/src/core/hle/service/hid/controllers/mouse.cpp +++ b/src/core/hle/service/hid/controllers/mouse.cpp | |||
| @@ -19,7 +19,7 @@ void Controller_Mouse::OnRelease() {} | |||
| 19 | 19 | ||
| 20 | void Controller_Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, | 20 | void Controller_Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, |
| 21 | std::size_t size) { | 21 | std::size_t size) { |
| 22 | shared_memory.header.timestamp = core_timing.GetTicks(); | 22 | shared_memory.header.timestamp = core_timing.GetCPUTicks(); |
| 23 | shared_memory.header.total_entry_count = 17; | 23 | shared_memory.header.total_entry_count = 17; |
| 24 | 24 | ||
| 25 | if (!IsControllerActivated()) { | 25 | if (!IsControllerActivated()) { |
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index 6fbee7efa..ef67ad690 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp | |||
| @@ -328,7 +328,7 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* | |||
| 328 | const auto& last_entry = | 328 | const auto& last_entry = |
| 329 | main_controller->npad[main_controller->common.last_entry_index]; | 329 | main_controller->npad[main_controller->common.last_entry_index]; |
| 330 | 330 | ||
| 331 | main_controller->common.timestamp = core_timing.GetTicks(); | 331 | main_controller->common.timestamp = core_timing.GetCPUTicks(); |
| 332 | main_controller->common.last_entry_index = | 332 | main_controller->common.last_entry_index = |
| 333 | (main_controller->common.last_entry_index + 1) % 17; | 333 | (main_controller->common.last_entry_index + 1) % 17; |
| 334 | 334 | ||
diff --git a/src/core/hle/service/hid/controllers/stubbed.cpp b/src/core/hle/service/hid/controllers/stubbed.cpp index 9e527d176..e7483bfa2 100644 --- a/src/core/hle/service/hid/controllers/stubbed.cpp +++ b/src/core/hle/service/hid/controllers/stubbed.cpp | |||
| @@ -23,7 +23,7 @@ void Controller_Stubbed::OnUpdate(const Core::Timing::CoreTiming& core_timing, u | |||
| 23 | } | 23 | } |
| 24 | 24 | ||
| 25 | CommonHeader header{}; | 25 | CommonHeader header{}; |
| 26 | header.timestamp = core_timing.GetTicks(); | 26 | header.timestamp = core_timing.GetCPUTicks(); |
| 27 | header.total_entry_count = 17; | 27 | header.total_entry_count = 17; |
| 28 | header.entry_count = 0; | 28 | header.entry_count = 0; |
| 29 | header.last_entry_index = 0; | 29 | header.last_entry_index = 0; |
diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp index 1c6e55566..e326f8f5c 100644 --- a/src/core/hle/service/hid/controllers/touchscreen.cpp +++ b/src/core/hle/service/hid/controllers/touchscreen.cpp | |||
| @@ -22,7 +22,7 @@ void Controller_Touchscreen::OnRelease() {} | |||
| 22 | 22 | ||
| 23 | void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, | 23 | void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, |
| 24 | std::size_t size) { | 24 | std::size_t size) { |
| 25 | shared_memory.header.timestamp = core_timing.GetTicks(); | 25 | shared_memory.header.timestamp = core_timing.GetCPUTicks(); |
| 26 | shared_memory.header.total_entry_count = 17; | 26 | shared_memory.header.total_entry_count = 17; |
| 27 | 27 | ||
| 28 | if (!IsControllerActivated()) { | 28 | if (!IsControllerActivated()) { |
| @@ -49,7 +49,7 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin | |||
| 49 | touch_entry.diameter_x = Settings::values.touchscreen.diameter_x; | 49 | touch_entry.diameter_x = Settings::values.touchscreen.diameter_x; |
| 50 | touch_entry.diameter_y = Settings::values.touchscreen.diameter_y; | 50 | touch_entry.diameter_y = Settings::values.touchscreen.diameter_y; |
| 51 | touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle; | 51 | touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle; |
| 52 | const u64 tick = core_timing.GetTicks(); | 52 | const u64 tick = core_timing.GetCPUTicks(); |
| 53 | touch_entry.delta_time = tick - last_touch; | 53 | touch_entry.delta_time = tick - last_touch; |
| 54 | last_touch = tick; | 54 | last_touch = tick; |
| 55 | touch_entry.finger = Settings::values.touchscreen.finger; | 55 | touch_entry.finger = Settings::values.touchscreen.finger; |
diff --git a/src/core/hle/service/hid/controllers/xpad.cpp b/src/core/hle/service/hid/controllers/xpad.cpp index 27511b27b..2503ef241 100644 --- a/src/core/hle/service/hid/controllers/xpad.cpp +++ b/src/core/hle/service/hid/controllers/xpad.cpp | |||
| @@ -20,7 +20,7 @@ void Controller_XPad::OnRelease() {} | |||
| 20 | void Controller_XPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, | 20 | void Controller_XPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, |
| 21 | std::size_t size) { | 21 | std::size_t size) { |
| 22 | for (auto& xpad_entry : shared_memory.shared_memory_entries) { | 22 | for (auto& xpad_entry : shared_memory.shared_memory_entries) { |
| 23 | xpad_entry.header.timestamp = core_timing.GetTicks(); | 23 | xpad_entry.header.timestamp = core_timing.GetCPUTicks(); |
| 24 | xpad_entry.header.total_entry_count = 17; | 24 | xpad_entry.header.total_entry_count = 17; |
| 25 | 25 | ||
| 26 | if (!IsControllerActivated()) { | 26 | if (!IsControllerActivated()) { |
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 57d5edea7..e9020e0dc 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp | |||
| @@ -39,11 +39,9 @@ namespace Service::HID { | |||
| 39 | 39 | ||
| 40 | // Updating period for each HID device. | 40 | // Updating period for each HID device. |
| 41 | // TODO(ogniK): Find actual polling rate of hid | 41 | // TODO(ogniK): Find actual polling rate of hid |
| 42 | constexpr s64 pad_update_ticks = static_cast<s64>(Core::Hardware::BASE_CLOCK_RATE / 66); | 42 | constexpr s64 pad_update_ticks = static_cast<s64>(1000000000 / 66); |
| 43 | [[maybe_unused]] constexpr s64 accelerometer_update_ticks = | 43 | [[maybe_unused]] constexpr s64 accelerometer_update_ticks = static_cast<s64>(1000000000 / 100); |
| 44 | static_cast<s64>(Core::Hardware::BASE_CLOCK_RATE / 100); | 44 | [[maybe_unused]] constexpr s64 gyroscope_update_ticks = static_cast<s64>(1000000000 / 100); |
| 45 | [[maybe_unused]] constexpr s64 gyroscope_update_ticks = | ||
| 46 | static_cast<s64>(Core::Hardware::BASE_CLOCK_RATE / 100); | ||
| 47 | constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000; | 45 | constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000; |
| 48 | 46 | ||
| 49 | IAppletResource::IAppletResource(Core::System& system) | 47 | IAppletResource::IAppletResource(Core::System& system) |
| @@ -78,8 +76,8 @@ IAppletResource::IAppletResource(Core::System& system) | |||
| 78 | 76 | ||
| 79 | // Register update callbacks | 77 | // Register update callbacks |
| 80 | pad_update_event = | 78 | pad_update_event = |
| 81 | Core::Timing::CreateEvent("HID::UpdatePadCallback", [this](u64 userdata, s64 cycles_late) { | 79 | Core::Timing::CreateEvent("HID::UpdatePadCallback", [this](u64 userdata, s64 ns_late) { |
| 82 | UpdateControllers(userdata, cycles_late); | 80 | UpdateControllers(userdata, ns_late); |
| 83 | }); | 81 | }); |
| 84 | 82 | ||
| 85 | // TODO(shinyquagsire23): Other update callbacks? (accel, gyro?) | 83 | // TODO(shinyquagsire23): Other update callbacks? (accel, gyro?) |
| @@ -109,7 +107,7 @@ void IAppletResource::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) { | |||
| 109 | rb.PushCopyObjects(shared_mem); | 107 | rb.PushCopyObjects(shared_mem); |
| 110 | } | 108 | } |
| 111 | 109 | ||
| 112 | void IAppletResource::UpdateControllers(u64 userdata, s64 cycles_late) { | 110 | void IAppletResource::UpdateControllers(u64 userdata, s64 ns_late) { |
| 113 | auto& core_timing = system.CoreTiming(); | 111 | auto& core_timing = system.CoreTiming(); |
| 114 | 112 | ||
| 115 | const bool should_reload = Settings::values.is_device_reload_pending.exchange(false); | 113 | const bool should_reload = Settings::values.is_device_reload_pending.exchange(false); |
| @@ -120,7 +118,7 @@ void IAppletResource::UpdateControllers(u64 userdata, s64 cycles_late) { | |||
| 120 | controller->OnUpdate(core_timing, shared_mem->GetPointer(), SHARED_MEMORY_SIZE); | 118 | controller->OnUpdate(core_timing, shared_mem->GetPointer(), SHARED_MEMORY_SIZE); |
| 121 | } | 119 | } |
| 122 | 120 | ||
| 123 | core_timing.ScheduleEvent(pad_update_ticks - cycles_late, pad_update_event); | 121 | core_timing.ScheduleEvent(pad_update_ticks - ns_late, pad_update_event); |
| 124 | } | 122 | } |
| 125 | 123 | ||
| 126 | class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> { | 124 | class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> { |
diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp index 36ed6f7da..e82fd031b 100644 --- a/src/core/hle/service/hid/irs.cpp +++ b/src/core/hle/service/hid/irs.cpp | |||
| @@ -98,7 +98,7 @@ void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) { | |||
| 98 | 98 | ||
| 99 | IPC::ResponseBuilder rb{ctx, 5}; | 99 | IPC::ResponseBuilder rb{ctx, 5}; |
| 100 | rb.Push(RESULT_SUCCESS); | 100 | rb.Push(RESULT_SUCCESS); |
| 101 | rb.PushRaw<u64>(system.CoreTiming().GetTicks()); | 101 | rb.PushRaw<u64>(system.CoreTiming().GetCPUTicks()); |
| 102 | rb.PushRaw<u32>(0); | 102 | rb.PushRaw<u32>(0); |
| 103 | } | 103 | } |
| 104 | 104 | ||
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp index 0d913334e..fba89e7a6 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp | |||
| @@ -200,8 +200,7 @@ u32 nvhost_ctrl_gpu::GetGpuTime(const std::vector<u8>& input, std::vector<u8>& o | |||
| 200 | 200 | ||
| 201 | IoctlGetGpuTime params{}; | 201 | IoctlGetGpuTime params{}; |
| 202 | std::memcpy(¶ms, input.data(), input.size()); | 202 | std::memcpy(¶ms, input.data(), input.size()); |
| 203 | const auto ns = Core::Timing::CyclesToNs(system.CoreTiming().GetTicks()); | 203 | params.gpu_time = static_cast<u64_le>(system.CoreTiming().GetGlobalTimeNs().count()); |
| 204 | params.gpu_time = static_cast<u64_le>(ns.count()); | ||
| 205 | std::memcpy(output.data(), ¶ms, output.size()); | 204 | std::memcpy(output.data(), ¶ms, output.size()); |
| 206 | return 0; | 205 | return 0; |
| 207 | } | 206 | } |
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index 437bc5dee..aaf28995d 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp | |||
| @@ -27,8 +27,8 @@ | |||
| 27 | 27 | ||
| 28 | namespace Service::NVFlinger { | 28 | namespace Service::NVFlinger { |
| 29 | 29 | ||
| 30 | constexpr s64 frame_ticks = static_cast<s64>(Core::Hardware::BASE_CLOCK_RATE / 60); | 30 | constexpr s64 frame_ticks = static_cast<s64>(1000000000 / 60); |
| 31 | constexpr s64 frame_ticks_30fps = static_cast<s64>(Core::Hardware::BASE_CLOCK_RATE / 30); | 31 | constexpr s64 frame_ticks_30fps = static_cast<s64>(1000000000 / 30); |
| 32 | 32 | ||
| 33 | NVFlinger::NVFlinger(Core::System& system) : system(system) { | 33 | NVFlinger::NVFlinger(Core::System& system) : system(system) { |
| 34 | displays.emplace_back(0, "Default", system); | 34 | displays.emplace_back(0, "Default", system); |
| @@ -39,11 +39,10 @@ NVFlinger::NVFlinger(Core::System& system) : system(system) { | |||
| 39 | 39 | ||
| 40 | // Schedule the screen composition events | 40 | // Schedule the screen composition events |
| 41 | composition_event = | 41 | composition_event = |
| 42 | Core::Timing::CreateEvent("ScreenComposition", [this](u64 userdata, s64 cycles_late) { | 42 | Core::Timing::CreateEvent("ScreenComposition", [this](u64 userdata, s64 ns_late) { |
| 43 | Compose(); | 43 | Compose(); |
| 44 | const auto ticks = | 44 | const auto ticks = GetNextTicks(); |
| 45 | Settings::values.force_30fps_mode ? frame_ticks_30fps : GetNextTicks(); | 45 | this->system.CoreTiming().ScheduleEvent(std::max<s64>(0LL, ticks - ns_late), |
| 46 | this->system.CoreTiming().ScheduleEvent(std::max<s64>(0LL, ticks - cycles_late), | ||
| 47 | composition_event); | 46 | composition_event); |
| 48 | }); | 47 | }); |
| 49 | 48 | ||
| @@ -223,7 +222,7 @@ void NVFlinger::Compose() { | |||
| 223 | 222 | ||
| 224 | s64 NVFlinger::GetNextTicks() const { | 223 | s64 NVFlinger::GetNextTicks() const { |
| 225 | constexpr s64 max_hertz = 120LL; | 224 | constexpr s64 max_hertz = 120LL; |
| 226 | return (Core::Hardware::BASE_CLOCK_RATE * (1LL << swap_interval)) / max_hertz; | 225 | return (1000000000 * (1LL << swap_interval)) / max_hertz; |
| 227 | } | 226 | } |
| 228 | 227 | ||
| 229 | } // namespace Service::NVFlinger | 228 | } // namespace Service::NVFlinger |
diff --git a/src/core/hle/service/time/standard_steady_clock_core.cpp b/src/core/hle/service/time/standard_steady_clock_core.cpp index 1575f0b49..59a272f4a 100644 --- a/src/core/hle/service/time/standard_steady_clock_core.cpp +++ b/src/core/hle/service/time/standard_steady_clock_core.cpp | |||
| @@ -11,9 +11,8 @@ | |||
| 11 | namespace Service::Time::Clock { | 11 | namespace Service::Time::Clock { |
| 12 | 12 | ||
| 13 | TimeSpanType StandardSteadyClockCore::GetCurrentRawTimePoint(Core::System& system) { | 13 | TimeSpanType StandardSteadyClockCore::GetCurrentRawTimePoint(Core::System& system) { |
| 14 | const TimeSpanType ticks_time_span{TimeSpanType::FromTicks( | 14 | const TimeSpanType ticks_time_span{ |
| 15 | Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks()), | 15 | TimeSpanType::FromTicks(system.CoreTiming().GetClockTicks(), Core::Hardware::CNTFREQ)}; |
| 16 | Core::Hardware::CNTFREQ)}; | ||
| 17 | TimeSpanType raw_time_point{setup_value.nanoseconds + ticks_time_span.nanoseconds}; | 16 | TimeSpanType raw_time_point{setup_value.nanoseconds + ticks_time_span.nanoseconds}; |
| 18 | 17 | ||
| 19 | if (raw_time_point.nanoseconds < cached_raw_time_point.nanoseconds) { | 18 | if (raw_time_point.nanoseconds < cached_raw_time_point.nanoseconds) { |
diff --git a/src/core/hle/service/time/tick_based_steady_clock_core.cpp b/src/core/hle/service/time/tick_based_steady_clock_core.cpp index 44d5bc651..8baaa2a6a 100644 --- a/src/core/hle/service/time/tick_based_steady_clock_core.cpp +++ b/src/core/hle/service/time/tick_based_steady_clock_core.cpp | |||
| @@ -11,9 +11,8 @@ | |||
| 11 | namespace Service::Time::Clock { | 11 | namespace Service::Time::Clock { |
| 12 | 12 | ||
| 13 | SteadyClockTimePoint TickBasedSteadyClockCore::GetTimePoint(Core::System& system) { | 13 | SteadyClockTimePoint TickBasedSteadyClockCore::GetTimePoint(Core::System& system) { |
| 14 | const TimeSpanType ticks_time_span{TimeSpanType::FromTicks( | 14 | const TimeSpanType ticks_time_span{ |
| 15 | Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks()), | 15 | TimeSpanType::FromTicks(system.CoreTiming().GetClockTicks(), Core::Hardware::CNTFREQ)}; |
| 16 | Core::Hardware::CNTFREQ)}; | ||
| 17 | 16 | ||
| 18 | return {ticks_time_span.ToSeconds(), GetClockSourceId()}; | 17 | return {ticks_time_span.ToSeconds(), GetClockSourceId()}; |
| 19 | } | 18 | } |
diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp index 67f1bbcf3..4cf58a61a 100644 --- a/src/core/hle/service/time/time.cpp +++ b/src/core/hle/service/time/time.cpp | |||
| @@ -234,9 +234,8 @@ void Module::Interface::CalculateMonotonicSystemClockBaseTimePoint(Kernel::HLERe | |||
| 234 | const auto current_time_point{steady_clock_core.GetCurrentTimePoint(system)}; | 234 | const auto current_time_point{steady_clock_core.GetCurrentTimePoint(system)}; |
| 235 | 235 | ||
| 236 | if (current_time_point.clock_source_id == context.steady_time_point.clock_source_id) { | 236 | if (current_time_point.clock_source_id == context.steady_time_point.clock_source_id) { |
| 237 | const auto ticks{Clock::TimeSpanType::FromTicks( | 237 | const auto ticks{Clock::TimeSpanType::FromTicks(system.CoreTiming().GetClockTicks(), |
| 238 | Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks()), | 238 | Core::Hardware::CNTFREQ)}; |
| 239 | Core::Hardware::CNTFREQ)}; | ||
| 240 | const s64 base_time_point{context.offset + current_time_point.time_point - | 239 | const s64 base_time_point{context.offset + current_time_point.time_point - |
| 241 | ticks.ToSeconds()}; | 240 | ticks.ToSeconds()}; |
| 242 | IPC::ResponseBuilder rb{ctx, (sizeof(s64) / 4) + 2}; | 241 | IPC::ResponseBuilder rb{ctx, (sizeof(s64) / 4) + 2}; |
diff --git a/src/core/hle/service/time/time_sharedmemory.cpp b/src/core/hle/service/time/time_sharedmemory.cpp index 999ec1e51..e0ae9f874 100644 --- a/src/core/hle/service/time/time_sharedmemory.cpp +++ b/src/core/hle/service/time/time_sharedmemory.cpp | |||
| @@ -30,8 +30,7 @@ void SharedMemory::SetupStandardSteadyClock(Core::System& system, | |||
| 30 | const Common::UUID& clock_source_id, | 30 | const Common::UUID& clock_source_id, |
| 31 | Clock::TimeSpanType current_time_point) { | 31 | Clock::TimeSpanType current_time_point) { |
| 32 | const Clock::TimeSpanType ticks_time_span{Clock::TimeSpanType::FromTicks( | 32 | const Clock::TimeSpanType ticks_time_span{Clock::TimeSpanType::FromTicks( |
| 33 | Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks()), | 33 | system.CoreTiming().GetClockTicks(), Core::Hardware::CNTFREQ)}; |
| 34 | Core::Hardware::CNTFREQ)}; | ||
| 35 | const Clock::SteadyClockContext context{ | 34 | const Clock::SteadyClockContext context{ |
| 36 | static_cast<u64>(current_time_point.nanoseconds - ticks_time_span.nanoseconds), | 35 | static_cast<u64>(current_time_point.nanoseconds - ticks_time_span.nanoseconds), |
| 37 | clock_source_id}; | 36 | clock_source_id}; |
diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 9d87045a0..66634596d 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp | |||
| @@ -29,15 +29,12 @@ namespace Core::Memory { | |||
| 29 | struct Memory::Impl { | 29 | struct Memory::Impl { |
| 30 | explicit Impl(Core::System& system_) : system{system_} {} | 30 | explicit Impl(Core::System& system_) : system{system_} {} |
| 31 | 31 | ||
| 32 | void SetCurrentPageTable(Kernel::Process& process) { | 32 | void SetCurrentPageTable(Kernel::Process& process, u32 core_id) { |
| 33 | current_page_table = &process.PageTable().PageTableImpl(); | 33 | current_page_table = &process.PageTable().PageTableImpl(); |
| 34 | 34 | ||
| 35 | const std::size_t address_space_width = process.PageTable().GetAddressSpaceWidth(); | 35 | const std::size_t address_space_width = process.PageTable().GetAddressSpaceWidth(); |
| 36 | 36 | ||
| 37 | system.ArmInterface(0).PageTableChanged(*current_page_table, address_space_width); | 37 | system.ArmInterface(core_id).PageTableChanged(*current_page_table, address_space_width); |
| 38 | system.ArmInterface(1).PageTableChanged(*current_page_table, address_space_width); | ||
| 39 | system.ArmInterface(2).PageTableChanged(*current_page_table, address_space_width); | ||
| 40 | system.ArmInterface(3).PageTableChanged(*current_page_table, address_space_width); | ||
| 41 | } | 38 | } |
| 42 | 39 | ||
| 43 | void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, PAddr target) { | 40 | void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, PAddr target) { |
| @@ -689,8 +686,8 @@ struct Memory::Impl { | |||
| 689 | Memory::Memory(Core::System& system) : impl{std::make_unique<Impl>(system)} {} | 686 | Memory::Memory(Core::System& system) : impl{std::make_unique<Impl>(system)} {} |
| 690 | Memory::~Memory() = default; | 687 | Memory::~Memory() = default; |
| 691 | 688 | ||
| 692 | void Memory::SetCurrentPageTable(Kernel::Process& process) { | 689 | void Memory::SetCurrentPageTable(Kernel::Process& process, u32 core_id) { |
| 693 | impl->SetCurrentPageTable(process); | 690 | impl->SetCurrentPageTable(process, core_id); |
| 694 | } | 691 | } |
| 695 | 692 | ||
| 696 | void Memory::MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, PAddr target) { | 693 | void Memory::MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, PAddr target) { |
diff --git a/src/core/memory.h b/src/core/memory.h index 9292f3b0a..93f0c1d6c 100644 --- a/src/core/memory.h +++ b/src/core/memory.h | |||
| @@ -64,7 +64,7 @@ public: | |||
| 64 | * | 64 | * |
| 65 | * @param process The process to use the page table of. | 65 | * @param process The process to use the page table of. |
| 66 | */ | 66 | */ |
| 67 | void SetCurrentPageTable(Kernel::Process& process); | 67 | void SetCurrentPageTable(Kernel::Process& process, u32 core_id); |
| 68 | 68 | ||
| 69 | /** | 69 | /** |
| 70 | * Maps an allocated buffer onto a region of the emulated process address space. | 70 | * Maps an allocated buffer onto a region of the emulated process address space. |
diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp index b139e8465..53d27859b 100644 --- a/src/core/memory/cheat_engine.cpp +++ b/src/core/memory/cheat_engine.cpp | |||
| @@ -20,7 +20,7 @@ | |||
| 20 | 20 | ||
| 21 | namespace Core::Memory { | 21 | namespace Core::Memory { |
| 22 | 22 | ||
| 23 | constexpr s64 CHEAT_ENGINE_TICKS = static_cast<s64>(Core::Hardware::BASE_CLOCK_RATE / 12); | 23 | constexpr s64 CHEAT_ENGINE_TICKS = static_cast<s64>(1000000000 / 12); |
| 24 | constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF; | 24 | constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF; |
| 25 | 25 | ||
| 26 | StandardVmCallbacks::StandardVmCallbacks(Core::System& system, const CheatProcessMetadata& metadata) | 26 | StandardVmCallbacks::StandardVmCallbacks(Core::System& system, const CheatProcessMetadata& metadata) |
| @@ -190,7 +190,7 @@ CheatEngine::~CheatEngine() { | |||
| 190 | void CheatEngine::Initialize() { | 190 | void CheatEngine::Initialize() { |
| 191 | event = Core::Timing::CreateEvent( | 191 | event = Core::Timing::CreateEvent( |
| 192 | "CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id), | 192 | "CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id), |
| 193 | [this](u64 userdata, s64 cycles_late) { FrameCallback(userdata, cycles_late); }); | 193 | [this](u64 userdata, s64 ns_late) { FrameCallback(userdata, ns_late); }); |
| 194 | core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS, event); | 194 | core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS, event); |
| 195 | 195 | ||
| 196 | metadata.process_id = system.CurrentProcess()->GetProcessID(); | 196 | metadata.process_id = system.CurrentProcess()->GetProcessID(); |
| @@ -217,7 +217,7 @@ void CheatEngine::Reload(std::vector<CheatEntry> cheats) { | |||
| 217 | 217 | ||
| 218 | MICROPROFILE_DEFINE(Cheat_Engine, "Add-Ons", "Cheat Engine", MP_RGB(70, 200, 70)); | 218 | MICROPROFILE_DEFINE(Cheat_Engine, "Add-Ons", "Cheat Engine", MP_RGB(70, 200, 70)); |
| 219 | 219 | ||
| 220 | void CheatEngine::FrameCallback(u64 userdata, s64 cycles_late) { | 220 | void CheatEngine::FrameCallback(u64 userdata, s64 ns_late) { |
| 221 | if (is_pending_reload.exchange(false)) { | 221 | if (is_pending_reload.exchange(false)) { |
| 222 | vm.LoadProgram(cheats); | 222 | vm.LoadProgram(cheats); |
| 223 | } | 223 | } |
| @@ -230,7 +230,7 @@ void CheatEngine::FrameCallback(u64 userdata, s64 cycles_late) { | |||
| 230 | 230 | ||
| 231 | vm.Execute(metadata); | 231 | vm.Execute(metadata); |
| 232 | 232 | ||
| 233 | core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS - cycles_late, event); | 233 | core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS - ns_late, event); |
| 234 | } | 234 | } |
| 235 | 235 | ||
| 236 | } // namespace Core::Memory | 236 | } // namespace Core::Memory |
diff --git a/src/core/tools/freezer.cpp b/src/core/tools/freezer.cpp index b2c6c537e..8b0c50d11 100644 --- a/src/core/tools/freezer.cpp +++ b/src/core/tools/freezer.cpp | |||
| @@ -14,7 +14,7 @@ | |||
| 14 | namespace Tools { | 14 | namespace Tools { |
| 15 | namespace { | 15 | namespace { |
| 16 | 16 | ||
| 17 | constexpr s64 MEMORY_FREEZER_TICKS = static_cast<s64>(Core::Hardware::BASE_CLOCK_RATE / 60); | 17 | constexpr s64 MEMORY_FREEZER_TICKS = static_cast<s64>(1000000000 / 60); |
| 18 | 18 | ||
| 19 | u64 MemoryReadWidth(Core::Memory::Memory& memory, u32 width, VAddr addr) { | 19 | u64 MemoryReadWidth(Core::Memory::Memory& memory, u32 width, VAddr addr) { |
| 20 | switch (width) { | 20 | switch (width) { |
| @@ -57,7 +57,7 @@ Freezer::Freezer(Core::Timing::CoreTiming& core_timing_, Core::Memory::Memory& m | |||
| 57 | : core_timing{core_timing_}, memory{memory_} { | 57 | : core_timing{core_timing_}, memory{memory_} { |
| 58 | event = Core::Timing::CreateEvent( | 58 | event = Core::Timing::CreateEvent( |
| 59 | "MemoryFreezer::FrameCallback", | 59 | "MemoryFreezer::FrameCallback", |
| 60 | [this](u64 userdata, s64 cycles_late) { FrameCallback(userdata, cycles_late); }); | 60 | [this](u64 userdata, s64 ns_late) { FrameCallback(userdata, ns_late); }); |
| 61 | core_timing.ScheduleEvent(MEMORY_FREEZER_TICKS, event); | 61 | core_timing.ScheduleEvent(MEMORY_FREEZER_TICKS, event); |
| 62 | } | 62 | } |
| 63 | 63 | ||
| @@ -158,7 +158,7 @@ std::vector<Freezer::Entry> Freezer::GetEntries() const { | |||
| 158 | return entries; | 158 | return entries; |
| 159 | } | 159 | } |
| 160 | 160 | ||
| 161 | void Freezer::FrameCallback(u64 userdata, s64 cycles_late) { | 161 | void Freezer::FrameCallback(u64 userdata, s64 ns_late) { |
| 162 | if (!IsActive()) { | 162 | if (!IsActive()) { |
| 163 | LOG_DEBUG(Common_Memory, "Memory freezer has been deactivated, ending callback events."); | 163 | LOG_DEBUG(Common_Memory, "Memory freezer has been deactivated, ending callback events."); |
| 164 | return; | 164 | return; |
| @@ -173,7 +173,7 @@ void Freezer::FrameCallback(u64 userdata, s64 cycles_late) { | |||
| 173 | MemoryWriteWidth(memory, entry.width, entry.address, entry.value); | 173 | MemoryWriteWidth(memory, entry.width, entry.address, entry.value); |
| 174 | } | 174 | } |
| 175 | 175 | ||
| 176 | core_timing.ScheduleEvent(MEMORY_FREEZER_TICKS - cycles_late, event); | 176 | core_timing.ScheduleEvent(MEMORY_FREEZER_TICKS - ns_late, event); |
| 177 | } | 177 | } |
| 178 | 178 | ||
| 179 | void Freezer::FillEntryReads() { | 179 | void Freezer::FillEntryReads() { |
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index 3f750b51c..47ef30aa9 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt | |||
| @@ -8,7 +8,6 @@ add_executable(tests | |||
| 8 | core/arm/arm_test_common.cpp | 8 | core/arm/arm_test_common.cpp |
| 9 | core/arm/arm_test_common.h | 9 | core/arm/arm_test_common.h |
| 10 | core/core_timing.cpp | 10 | core/core_timing.cpp |
| 11 | core/host_timing.cpp | ||
| 12 | tests.cpp | 11 | tests.cpp |
| 13 | ) | 12 | ) |
| 14 | 13 | ||
diff --git a/src/tests/core/core_timing.cpp b/src/tests/core/core_timing.cpp index ff2d11cc8..795f3da09 100644 --- a/src/tests/core/core_timing.cpp +++ b/src/tests/core/core_timing.cpp | |||
| @@ -16,31 +16,30 @@ | |||
| 16 | 16 | ||
| 17 | namespace { | 17 | namespace { |
| 18 | // Numbers are chosen randomly to make sure the correct one is given. | 18 | // Numbers are chosen randomly to make sure the correct one is given. |
| 19 | constexpr std::array<u64, 5> CB_IDS{{42, 144, 93, 1026, UINT64_C(0xFFFF7FFFF7FFFF)}}; | 19 | static constexpr std::array<u64, 5> CB_IDS{{42, 144, 93, 1026, UINT64_C(0xFFFF7FFFF7FFFF)}}; |
| 20 | constexpr int MAX_SLICE_LENGTH = 10000; // Copied from CoreTiming internals | 20 | static constexpr int MAX_SLICE_LENGTH = 10000; // Copied from CoreTiming internals |
| 21 | static constexpr std::array<u64, 5> calls_order{{2, 0, 1, 4, 3}}; | ||
| 22 | static std::array<s64, 5> delays{}; | ||
| 21 | 23 | ||
| 22 | std::bitset<CB_IDS.size()> callbacks_ran_flags; | 24 | std::bitset<CB_IDS.size()> callbacks_ran_flags; |
| 23 | u64 expected_callback = 0; | 25 | u64 expected_callback = 0; |
| 24 | s64 lateness = 0; | 26 | s64 lateness = 0; |
| 25 | 27 | ||
| 26 | template <unsigned int IDX> | 28 | template <unsigned int IDX> |
| 27 | void CallbackTemplate(u64 userdata, s64 cycles_late) { | 29 | void HostCallbackTemplate(u64 userdata, s64 nanoseconds_late) { |
| 28 | static_assert(IDX < CB_IDS.size(), "IDX out of range"); | 30 | static_assert(IDX < CB_IDS.size(), "IDX out of range"); |
| 29 | callbacks_ran_flags.set(IDX); | 31 | callbacks_ran_flags.set(IDX); |
| 30 | REQUIRE(CB_IDS[IDX] == userdata); | 32 | REQUIRE(CB_IDS[IDX] == userdata); |
| 31 | REQUIRE(CB_IDS[IDX] == expected_callback); | 33 | REQUIRE(CB_IDS[IDX] == CB_IDS[calls_order[expected_callback]]); |
| 32 | REQUIRE(lateness == cycles_late); | 34 | delays[IDX] = nanoseconds_late; |
| 35 | ++expected_callback; | ||
| 33 | } | 36 | } |
| 34 | 37 | ||
| 35 | u64 callbacks_done = 0; | 38 | u64 callbacks_done = 0; |
| 36 | 39 | ||
| 37 | void EmptyCallback(u64 userdata, s64 cycles_late) { | ||
| 38 | ++callbacks_done; | ||
| 39 | } | ||
| 40 | |||
| 41 | struct ScopeInit final { | 40 | struct ScopeInit final { |
| 42 | ScopeInit() { | 41 | ScopeInit() { |
| 43 | core_timing.Initialize(); | 42 | core_timing.Initialize([]() {}); |
| 44 | } | 43 | } |
| 45 | ~ScopeInit() { | 44 | ~ScopeInit() { |
| 46 | core_timing.Shutdown(); | 45 | core_timing.Shutdown(); |
| @@ -49,110 +48,97 @@ struct ScopeInit final { | |||
| 49 | Core::Timing::CoreTiming core_timing; | 48 | Core::Timing::CoreTiming core_timing; |
| 50 | }; | 49 | }; |
| 51 | 50 | ||
| 52 | void AdvanceAndCheck(Core::Timing::CoreTiming& core_timing, u32 idx, u32 context = 0, | ||
| 53 | int expected_lateness = 0, int cpu_downcount = 0) { | ||
| 54 | callbacks_ran_flags = 0; | ||
| 55 | expected_callback = CB_IDS[idx]; | ||
| 56 | lateness = expected_lateness; | ||
| 57 | |||
| 58 | // Pretend we executed X cycles of instructions. | ||
| 59 | core_timing.SwitchContext(context); | ||
| 60 | core_timing.AddTicks(core_timing.GetDowncount() - cpu_downcount); | ||
| 61 | core_timing.Advance(); | ||
| 62 | core_timing.SwitchContext((context + 1) % 4); | ||
| 63 | |||
| 64 | REQUIRE(decltype(callbacks_ran_flags)().set(idx) == callbacks_ran_flags); | ||
| 65 | } | ||
| 66 | } // Anonymous namespace | ||
| 67 | |||
| 68 | TEST_CASE("CoreTiming[BasicOrder]", "[core]") { | 51 | TEST_CASE("CoreTiming[BasicOrder]", "[core]") { |
| 69 | ScopeInit guard; | 52 | ScopeInit guard; |
| 70 | auto& core_timing = guard.core_timing; | 53 | auto& core_timing = guard.core_timing; |
| 54 | std::vector<std::shared_ptr<Core::Timing::EventType>> events{ | ||
| 55 | Core::Timing::CreateEvent("callbackA", HostCallbackTemplate<0>), | ||
| 56 | Core::Timing::CreateEvent("callbackB", HostCallbackTemplate<1>), | ||
| 57 | Core::Timing::CreateEvent("callbackC", HostCallbackTemplate<2>), | ||
| 58 | Core::Timing::CreateEvent("callbackD", HostCallbackTemplate<3>), | ||
| 59 | Core::Timing::CreateEvent("callbackE", HostCallbackTemplate<4>), | ||
| 60 | }; | ||
| 61 | |||
| 62 | expected_callback = 0; | ||
| 63 | |||
| 64 | core_timing.SyncPause(true); | ||
| 65 | |||
| 66 | u64 one_micro = 1000U; | ||
| 67 | for (std::size_t i = 0; i < events.size(); i++) { | ||
| 68 | u64 order = calls_order[i]; | ||
| 69 | core_timing.ScheduleEvent(i * one_micro + 100U, events[order], CB_IDS[order]); | ||
| 70 | } | ||
| 71 | /// test pause | ||
| 72 | REQUIRE(callbacks_ran_flags.none()); | ||
| 71 | 73 | ||
| 72 | std::shared_ptr<Core::Timing::EventType> cb_a = | 74 | core_timing.Pause(false); // No need to sync |
| 73 | Core::Timing::CreateEvent("callbackA", CallbackTemplate<0>); | ||
| 74 | std::shared_ptr<Core::Timing::EventType> cb_b = | ||
| 75 | Core::Timing::CreateEvent("callbackB", CallbackTemplate<1>); | ||
| 76 | std::shared_ptr<Core::Timing::EventType> cb_c = | ||
| 77 | Core::Timing::CreateEvent("callbackC", CallbackTemplate<2>); | ||
| 78 | std::shared_ptr<Core::Timing::EventType> cb_d = | ||
| 79 | Core::Timing::CreateEvent("callbackD", CallbackTemplate<3>); | ||
| 80 | std::shared_ptr<Core::Timing::EventType> cb_e = | ||
| 81 | Core::Timing::CreateEvent("callbackE", CallbackTemplate<4>); | ||
| 82 | |||
| 83 | // Enter slice 0 | ||
| 84 | core_timing.ResetRun(); | ||
| 85 | |||
| 86 | // D -> B -> C -> A -> E | ||
| 87 | core_timing.SwitchContext(0); | ||
| 88 | core_timing.ScheduleEvent(1000, cb_a, CB_IDS[0]); | ||
| 89 | REQUIRE(1000 == core_timing.GetDowncount()); | ||
| 90 | core_timing.ScheduleEvent(500, cb_b, CB_IDS[1]); | ||
| 91 | REQUIRE(500 == core_timing.GetDowncount()); | ||
| 92 | core_timing.ScheduleEvent(800, cb_c, CB_IDS[2]); | ||
| 93 | REQUIRE(500 == core_timing.GetDowncount()); | ||
| 94 | core_timing.ScheduleEvent(100, cb_d, CB_IDS[3]); | ||
| 95 | REQUIRE(100 == core_timing.GetDowncount()); | ||
| 96 | core_timing.ScheduleEvent(1200, cb_e, CB_IDS[4]); | ||
| 97 | REQUIRE(100 == core_timing.GetDowncount()); | ||
| 98 | |||
| 99 | AdvanceAndCheck(core_timing, 3, 0); | ||
| 100 | AdvanceAndCheck(core_timing, 1, 1); | ||
| 101 | AdvanceAndCheck(core_timing, 2, 2); | ||
| 102 | AdvanceAndCheck(core_timing, 0, 3); | ||
| 103 | AdvanceAndCheck(core_timing, 4, 0); | ||
| 104 | } | ||
| 105 | |||
| 106 | TEST_CASE("CoreTiming[FairSharing]", "[core]") { | ||
| 107 | 75 | ||
| 108 | ScopeInit guard; | 76 | while (core_timing.HasPendingEvents()) |
| 109 | auto& core_timing = guard.core_timing; | 77 | ; |
| 110 | 78 | ||
| 111 | std::shared_ptr<Core::Timing::EventType> empty_callback = | 79 | REQUIRE(callbacks_ran_flags.all()); |
| 112 | Core::Timing::CreateEvent("empty_callback", EmptyCallback); | ||
| 113 | 80 | ||
| 114 | callbacks_done = 0; | 81 | for (std::size_t i = 0; i < delays.size(); i++) { |
| 115 | u64 MAX_CALLBACKS = 10; | 82 | const double delay = static_cast<double>(delays[i]); |
| 116 | for (std::size_t i = 0; i < 10; i++) { | 83 | const double micro = delay / 1000.0f; |
| 117 | core_timing.ScheduleEvent(i * 3333U, empty_callback, 0); | 84 | const double mili = micro / 1000.0f; |
| 85 | printf("HostTimer Pausing Delay[%zu]: %.3f %.6f\n", i, micro, mili); | ||
| 118 | } | 86 | } |
| 87 | } | ||
| 119 | 88 | ||
| 120 | const s64 advances = MAX_SLICE_LENGTH / 10; | 89 | #pragma optimize("", off) |
| 121 | core_timing.ResetRun(); | 90 | u64 TestTimerSpeed(Core::Timing::CoreTiming& core_timing) { |
| 122 | u64 current_time = core_timing.GetTicks(); | 91 | u64 start = core_timing.GetGlobalTimeNs().count(); |
| 123 | bool keep_running{}; | 92 | u64 placebo = 0; |
| 124 | do { | 93 | for (std::size_t i = 0; i < 1000; i++) { |
| 125 | keep_running = false; | 94 | placebo += core_timing.GetGlobalTimeNs().count(); |
| 126 | for (u32 active_core = 0; active_core < 4; ++active_core) { | 95 | } |
| 127 | core_timing.SwitchContext(active_core); | 96 | u64 end = core_timing.GetGlobalTimeNs().count(); |
| 128 | if (core_timing.CanCurrentContextRun()) { | 97 | return (end - start); |
| 129 | core_timing.AddTicks(std::min<s64>(advances, core_timing.GetDowncount())); | ||
| 130 | core_timing.Advance(); | ||
| 131 | } | ||
| 132 | keep_running |= core_timing.CanCurrentContextRun(); | ||
| 133 | } | ||
| 134 | } while (keep_running); | ||
| 135 | u64 current_time_2 = core_timing.GetTicks(); | ||
| 136 | |||
| 137 | REQUIRE(MAX_CALLBACKS == callbacks_done); | ||
| 138 | REQUIRE(current_time_2 == current_time + MAX_SLICE_LENGTH * 4); | ||
| 139 | } | 98 | } |
| 99 | #pragma optimize("", on) | ||
| 140 | 100 | ||
| 141 | TEST_CASE("Core::Timing[PredictableLateness]", "[core]") { | 101 | TEST_CASE("CoreTiming[BasicOrderNoPausing]", "[core]") { |
| 142 | ScopeInit guard; | 102 | ScopeInit guard; |
| 143 | auto& core_timing = guard.core_timing; | 103 | auto& core_timing = guard.core_timing; |
| 104 | std::vector<std::shared_ptr<Core::Timing::EventType>> events{ | ||
| 105 | Core::Timing::CreateEvent("callbackA", HostCallbackTemplate<0>), | ||
| 106 | Core::Timing::CreateEvent("callbackB", HostCallbackTemplate<1>), | ||
| 107 | Core::Timing::CreateEvent("callbackC", HostCallbackTemplate<2>), | ||
| 108 | Core::Timing::CreateEvent("callbackD", HostCallbackTemplate<3>), | ||
| 109 | Core::Timing::CreateEvent("callbackE", HostCallbackTemplate<4>), | ||
| 110 | }; | ||
| 111 | |||
| 112 | core_timing.SyncPause(true); | ||
| 113 | core_timing.SyncPause(false); | ||
| 114 | |||
| 115 | expected_callback = 0; | ||
| 116 | |||
| 117 | u64 start = core_timing.GetGlobalTimeNs().count(); | ||
| 118 | u64 one_micro = 1000U; | ||
| 119 | for (std::size_t i = 0; i < events.size(); i++) { | ||
| 120 | u64 order = calls_order[i]; | ||
| 121 | core_timing.ScheduleEvent(i * one_micro + 100U, events[order], CB_IDS[order]); | ||
| 122 | } | ||
| 123 | u64 end = core_timing.GetGlobalTimeNs().count(); | ||
| 124 | const double scheduling_time = static_cast<double>(end - start); | ||
| 125 | const double timer_time = static_cast<double>(TestTimerSpeed(core_timing)); | ||
| 144 | 126 | ||
| 145 | std::shared_ptr<Core::Timing::EventType> cb_a = | 127 | while (core_timing.HasPendingEvents()) |
| 146 | Core::Timing::CreateEvent("callbackA", CallbackTemplate<0>); | 128 | ; |
| 147 | std::shared_ptr<Core::Timing::EventType> cb_b = | ||
| 148 | Core::Timing::CreateEvent("callbackB", CallbackTemplate<1>); | ||
| 149 | 129 | ||
| 150 | // Enter slice 0 | 130 | REQUIRE(callbacks_ran_flags.all()); |
| 151 | core_timing.ResetRun(); | ||
| 152 | 131 | ||
| 153 | core_timing.ScheduleEvent(100, cb_a, CB_IDS[0]); | 132 | for (std::size_t i = 0; i < delays.size(); i++) { |
| 154 | core_timing.ScheduleEvent(200, cb_b, CB_IDS[1]); | 133 | const double delay = static_cast<double>(delays[i]); |
| 134 | const double micro = delay / 1000.0f; | ||
| 135 | const double mili = micro / 1000.0f; | ||
| 136 | printf("HostTimer No Pausing Delay[%zu]: %.3f %.6f\n", i, micro, mili); | ||
| 137 | } | ||
| 155 | 138 | ||
| 156 | AdvanceAndCheck(core_timing, 0, 0, 10, -10); // (100 - 10) | 139 | const double micro = scheduling_time / 1000.0f; |
| 157 | AdvanceAndCheck(core_timing, 1, 1, 50, -50); | 140 | const double mili = micro / 1000.0f; |
| 141 | printf("HostTimer No Pausing Scheduling Time: %.3f %.6f\n", micro, mili); | ||
| 142 | printf("HostTimer No Pausing Timer Time: %.3f %.6f\n", timer_time / 1000.f, | ||
| 143 | timer_time / 1000000.f); | ||
| 158 | } | 144 | } |
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index 8eb017f65..482e49711 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp | |||
| @@ -2,6 +2,8 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <chrono> | ||
| 6 | |||
| 5 | #include "common/assert.h" | 7 | #include "common/assert.h" |
| 6 | #include "common/microprofile.h" | 8 | #include "common/microprofile.h" |
| 7 | #include "core/core.h" | 9 | #include "core/core.h" |
| @@ -154,8 +156,7 @@ u64 GPU::GetTicks() const { | |||
| 154 | constexpr u64 gpu_ticks_num = 384; | 156 | constexpr u64 gpu_ticks_num = 384; |
| 155 | constexpr u64 gpu_ticks_den = 625; | 157 | constexpr u64 gpu_ticks_den = 625; |
| 156 | 158 | ||
| 157 | const u64 cpu_ticks = system.CoreTiming().GetTicks(); | 159 | u64 nanoseconds = system.CoreTiming().GetGlobalTimeNs().count(); |
| 158 | u64 nanoseconds = Core::Timing::CyclesToNs(cpu_ticks).count(); | ||
| 159 | if (Settings::values.use_fast_gpu_time) { | 160 | if (Settings::values.use_fast_gpu_time) { |
| 160 | nanoseconds /= 256; | 161 | nanoseconds /= 256; |
| 161 | } | 162 | } |
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index bfeb16458..9ceb6c8d7 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp | |||
| @@ -52,6 +52,8 @@ void EmuThread::run() { | |||
| 52 | 52 | ||
| 53 | emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); | 53 | emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); |
| 54 | 54 | ||
| 55 | Core::System::GetInstance().RegisterHostThread(); | ||
| 56 | |||
| 55 | Core::System::GetInstance().Renderer().Rasterizer().LoadDiskResources( | 57 | Core::System::GetInstance().Renderer().Rasterizer().LoadDiskResources( |
| 56 | stop_run, [this](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) { | 58 | stop_run, [this](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) { |
| 57 | emit LoadProgress(stage, value, total); | 59 | emit LoadProgress(stage, value, total); |
| @@ -65,28 +67,30 @@ void EmuThread::run() { | |||
| 65 | bool was_active = false; | 67 | bool was_active = false; |
| 66 | while (!stop_run) { | 68 | while (!stop_run) { |
| 67 | if (running) { | 69 | if (running) { |
| 68 | if (!was_active) | 70 | if (was_active) { |
| 69 | emit DebugModeLeft(); | 71 | emit DebugModeLeft(); |
| 72 | } | ||
| 70 | 73 | ||
| 71 | Core::System::ResultStatus result = Core::System::GetInstance().RunLoop(); | 74 | running_guard = true; |
| 75 | Core::System::ResultStatus result = Core::System::GetInstance().Run(); | ||
| 72 | if (result != Core::System::ResultStatus::Success) { | 76 | if (result != Core::System::ResultStatus::Success) { |
| 77 | running_guard = false; | ||
| 73 | this->SetRunning(false); | 78 | this->SetRunning(false); |
| 74 | emit ErrorThrown(result, Core::System::GetInstance().GetStatusDetails()); | 79 | emit ErrorThrown(result, Core::System::GetInstance().GetStatusDetails()); |
| 75 | } | 80 | } |
| 81 | running_wait.Wait(); | ||
| 82 | result = Core::System::GetInstance().Pause(); | ||
| 83 | if (result != Core::System::ResultStatus::Success) { | ||
| 84 | running_guard = false; | ||
| 85 | this->SetRunning(false); | ||
| 86 | emit ErrorThrown(result, Core::System::GetInstance().GetStatusDetails()); | ||
| 87 | } | ||
| 88 | running_guard = false; | ||
| 76 | 89 | ||
| 77 | was_active = running || exec_step; | 90 | was_active = true; |
| 78 | if (!was_active && !stop_run) | ||
| 79 | emit DebugModeEntered(); | ||
| 80 | } else if (exec_step) { | ||
| 81 | if (!was_active) | ||
| 82 | emit DebugModeLeft(); | ||
| 83 | |||
| 84 | exec_step = false; | ||
| 85 | Core::System::GetInstance().SingleStep(); | ||
| 86 | emit DebugModeEntered(); | 91 | emit DebugModeEntered(); |
| 87 | yieldCurrentThread(); | 92 | } else if (exec_step) { |
| 88 | 93 | UNIMPLEMENTED(); | |
| 89 | was_active = false; | ||
| 90 | } else { | 94 | } else { |
| 91 | std::unique_lock lock{running_mutex}; | 95 | std::unique_lock lock{running_mutex}; |
| 92 | running_cv.wait(lock, [this] { return IsRunning() || exec_step || stop_run; }); | 96 | running_cv.wait(lock, [this] { return IsRunning() || exec_step || stop_run; }); |
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index 3626604ca..768568b3e 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h | |||
| @@ -59,6 +59,11 @@ public: | |||
| 59 | this->running = running; | 59 | this->running = running; |
| 60 | lock.unlock(); | 60 | lock.unlock(); |
| 61 | running_cv.notify_all(); | 61 | running_cv.notify_all(); |
| 62 | if (!running) { | ||
| 63 | running_wait.Set(); | ||
| 64 | /// Wait until effectively paused | ||
| 65 | while (running_guard); | ||
| 66 | } | ||
| 62 | } | 67 | } |
| 63 | 68 | ||
| 64 | /** | 69 | /** |
| @@ -84,6 +89,8 @@ private: | |||
| 84 | std::atomic_bool stop_run{false}; | 89 | std::atomic_bool stop_run{false}; |
| 85 | std::mutex running_mutex; | 90 | std::mutex running_mutex; |
| 86 | std::condition_variable running_cv; | 91 | std::condition_variable running_cv; |
| 92 | Common::Event running_wait{}; | ||
| 93 | std::atomic_bool running_guard{false}; | ||
| 87 | 94 | ||
| 88 | signals: | 95 | signals: |
| 89 | /** | 96 | /** |
diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp index c1ea25fb8..765908c5a 100644 --- a/src/yuzu/debugger/wait_tree.cpp +++ b/src/yuzu/debugger/wait_tree.cpp | |||
| @@ -59,8 +59,10 @@ std::vector<std::unique_ptr<WaitTreeThread>> WaitTreeItem::MakeThreadItemList() | |||
| 59 | std::size_t row = 0; | 59 | std::size_t row = 0; |
| 60 | auto add_threads = [&](const std::vector<std::shared_ptr<Kernel::Thread>>& threads) { | 60 | auto add_threads = [&](const std::vector<std::shared_ptr<Kernel::Thread>>& threads) { |
| 61 | for (std::size_t i = 0; i < threads.size(); ++i) { | 61 | for (std::size_t i = 0; i < threads.size(); ++i) { |
| 62 | item_list.push_back(std::make_unique<WaitTreeThread>(*threads[i])); | 62 | if (!threads[i]->IsHLEThread()) { |
| 63 | item_list.back()->row = row; | 63 | item_list.push_back(std::make_unique<WaitTreeThread>(*threads[i])); |
| 64 | item_list.back()->row = row; | ||
| 65 | } | ||
| 64 | ++row; | 66 | ++row; |
| 65 | } | 67 | } |
| 66 | }; | 68 | }; |
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index 4d2ea7e9e..1e5377840 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp | |||
| @@ -237,7 +237,7 @@ int main(int argc, char** argv) { | |||
| 237 | 237 | ||
| 238 | std::thread render_thread([&emu_window] { emu_window->Present(); }); | 238 | std::thread render_thread([&emu_window] { emu_window->Present(); }); |
| 239 | while (emu_window->IsOpen()) { | 239 | while (emu_window->IsOpen()) { |
| 240 | system.RunLoop(); | 240 | //system.RunLoop(); |
| 241 | } | 241 | } |
| 242 | render_thread.join(); | 242 | render_thread.join(); |
| 243 | 243 | ||
diff --git a/src/yuzu_tester/yuzu.cpp b/src/yuzu_tester/yuzu.cpp index 676e70ebd..1a45506d4 100644 --- a/src/yuzu_tester/yuzu.cpp +++ b/src/yuzu_tester/yuzu.cpp | |||
| @@ -256,7 +256,7 @@ int main(int argc, char** argv) { | |||
| 256 | system.Renderer().Rasterizer().LoadDiskResources(); | 256 | system.Renderer().Rasterizer().LoadDiskResources(); |
| 257 | 257 | ||
| 258 | while (!finished) { | 258 | while (!finished) { |
| 259 | system.RunLoop(); | 259 | //system.RunLoop(); |
| 260 | } | 260 | } |
| 261 | 261 | ||
| 262 | detached_tasks.WaitForAllTasks(); | 262 | detached_tasks.WaitForAllTasks(); |