forked from ShuriZma/suyu
Merge pull request #2123 from lioncash/coretiming-global
core_timing: De-globalize core_timing facilities
This commit is contained in:
commit
99da6362c4
|
@ -26,14 +26,15 @@ static Stream::Format ChannelsToStreamFormat(u32 num_channels) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
StreamPtr AudioOut::OpenStream(u32 sample_rate, u32 num_channels, std::string&& name,
|
StreamPtr AudioOut::OpenStream(Core::Timing::CoreTiming& core_timing, u32 sample_rate,
|
||||||
|
u32 num_channels, std::string&& name,
|
||||||
Stream::ReleaseCallback&& release_callback) {
|
Stream::ReleaseCallback&& release_callback) {
|
||||||
if (!sink) {
|
if (!sink) {
|
||||||
sink = CreateSinkFromID(Settings::values.sink_id, Settings::values.audio_device_id);
|
sink = CreateSinkFromID(Settings::values.sink_id, Settings::values.audio_device_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::make_shared<Stream>(
|
return std::make_shared<Stream>(
|
||||||
sample_rate, ChannelsToStreamFormat(num_channels), std::move(release_callback),
|
core_timing, sample_rate, ChannelsToStreamFormat(num_channels), std::move(release_callback),
|
||||||
sink->AcquireSinkStream(sample_rate, num_channels, name), std::move(name));
|
sink->AcquireSinkStream(sample_rate, num_channels, name), std::move(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,10 @@
|
||||||
#include "audio_core/stream.h"
|
#include "audio_core/stream.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
|
||||||
|
namespace Core::Timing {
|
||||||
|
class CoreTiming;
|
||||||
|
}
|
||||||
|
|
||||||
namespace AudioCore {
|
namespace AudioCore {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -21,8 +25,8 @@ namespace AudioCore {
|
||||||
class AudioOut {
|
class AudioOut {
|
||||||
public:
|
public:
|
||||||
/// Opens a new audio stream
|
/// Opens a new audio stream
|
||||||
StreamPtr OpenStream(u32 sample_rate, u32 num_channels, std::string&& name,
|
StreamPtr OpenStream(Core::Timing::CoreTiming& core_timing, u32 sample_rate, u32 num_channels,
|
||||||
Stream::ReleaseCallback&& release_callback);
|
std::string&& name, Stream::ReleaseCallback&& release_callback);
|
||||||
|
|
||||||
/// Returns a vector of recently released buffers specified by tag for the specified stream
|
/// Returns a vector of recently released buffers specified by tag for the specified stream
|
||||||
std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(StreamPtr stream, std::size_t max_count);
|
std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(StreamPtr stream, std::size_t max_count);
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include "audio_core/codec.h"
|
#include "audio_core/codec.h"
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
|
#include "core/core.h"
|
||||||
#include "core/hle/kernel/writable_event.h"
|
#include "core/hle/kernel/writable_event.h"
|
||||||
#include "core/memory.h"
|
#include "core/memory.h"
|
||||||
|
|
||||||
|
@ -71,14 +72,14 @@ private:
|
||||||
EffectOutStatus out_status{};
|
EffectOutStatus out_status{};
|
||||||
EffectInStatus info{};
|
EffectInStatus info{};
|
||||||
};
|
};
|
||||||
AudioRenderer::AudioRenderer(AudioRendererParameter params,
|
AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, AudioRendererParameter params,
|
||||||
Kernel::SharedPtr<Kernel::WritableEvent> buffer_event)
|
Kernel::SharedPtr<Kernel::WritableEvent> buffer_event)
|
||||||
: worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count),
|
: worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count),
|
||||||
effects(params.effect_count) {
|
effects(params.effect_count) {
|
||||||
|
|
||||||
audio_out = std::make_unique<AudioCore::AudioOut>();
|
audio_out = std::make_unique<AudioCore::AudioOut>();
|
||||||
stream = audio_out->OpenStream(STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS, "AudioRenderer",
|
stream = audio_out->OpenStream(core_timing, STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS,
|
||||||
[=]() { buffer_event->Signal(); });
|
"AudioRenderer", [=]() { buffer_event->Signal(); });
|
||||||
audio_out->StartStream(stream);
|
audio_out->StartStream(stream);
|
||||||
|
|
||||||
QueueMixedBuffer(0);
|
QueueMixedBuffer(0);
|
||||||
|
|
|
@ -14,6 +14,10 @@
|
||||||
#include "common/swap.h"
|
#include "common/swap.h"
|
||||||
#include "core/hle/kernel/object.h"
|
#include "core/hle/kernel/object.h"
|
||||||
|
|
||||||
|
namespace Core::Timing {
|
||||||
|
class CoreTiming;
|
||||||
|
}
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
class WritableEvent;
|
class WritableEvent;
|
||||||
}
|
}
|
||||||
|
@ -208,7 +212,7 @@ static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size
|
||||||
|
|
||||||
class AudioRenderer {
|
class AudioRenderer {
|
||||||
public:
|
public:
|
||||||
AudioRenderer(AudioRendererParameter params,
|
AudioRenderer(Core::Timing::CoreTiming& core_timing, AudioRendererParameter params,
|
||||||
Kernel::SharedPtr<Kernel::WritableEvent> buffer_event);
|
Kernel::SharedPtr<Kernel::WritableEvent> buffer_event);
|
||||||
~AudioRenderer();
|
~AudioRenderer();
|
||||||
|
|
||||||
|
|
|
@ -32,12 +32,12 @@ u32 Stream::GetNumChannels() const {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream::Stream(u32 sample_rate, Format format, ReleaseCallback&& release_callback,
|
Stream::Stream(Core::Timing::CoreTiming& core_timing, u32 sample_rate, Format format,
|
||||||
SinkStream& sink_stream, std::string&& name_)
|
ReleaseCallback&& release_callback, SinkStream& sink_stream, std::string&& name_)
|
||||||
: sample_rate{sample_rate}, format{format}, release_callback{std::move(release_callback)},
|
: sample_rate{sample_rate}, format{format}, release_callback{std::move(release_callback)},
|
||||||
sink_stream{sink_stream}, name{std::move(name_)} {
|
sink_stream{sink_stream}, core_timing{core_timing}, name{std::move(name_)} {
|
||||||
|
|
||||||
release_event = Core::Timing::RegisterEvent(
|
release_event = core_timing.RegisterEvent(
|
||||||
name, [this](u64 userdata, int cycles_late) { ReleaseActiveBuffer(); });
|
name, [this](u64 userdata, int cycles_late) { ReleaseActiveBuffer(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,8 +99,7 @@ void Stream::PlayNextBuffer() {
|
||||||
|
|
||||||
sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples());
|
sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples());
|
||||||
|
|
||||||
Core::Timing::ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event,
|
core_timing.ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {});
|
||||||
{});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Stream::ReleaseActiveBuffer() {
|
void Stream::ReleaseActiveBuffer() {
|
||||||
|
|
|
@ -14,8 +14,9 @@
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
|
||||||
namespace Core::Timing {
|
namespace Core::Timing {
|
||||||
|
class CoreTiming;
|
||||||
struct EventType;
|
struct EventType;
|
||||||
}
|
} // namespace Core::Timing
|
||||||
|
|
||||||
namespace AudioCore {
|
namespace AudioCore {
|
||||||
|
|
||||||
|
@ -42,8 +43,8 @@ public:
|
||||||
/// Callback function type, used to change guest state on a buffer being released
|
/// Callback function type, used to change guest state on a buffer being released
|
||||||
using ReleaseCallback = std::function<void()>;
|
using ReleaseCallback = std::function<void()>;
|
||||||
|
|
||||||
Stream(u32 sample_rate, Format format, ReleaseCallback&& release_callback,
|
Stream(Core::Timing::CoreTiming& core_timing, u32 sample_rate, Format format,
|
||||||
SinkStream& sink_stream, std::string&& name_);
|
ReleaseCallback&& release_callback, SinkStream& sink_stream, std::string&& name_);
|
||||||
|
|
||||||
/// Plays the audio stream
|
/// Plays the audio stream
|
||||||
void Play();
|
void Play();
|
||||||
|
@ -100,6 +101,7 @@ private:
|
||||||
std::queue<BufferPtr> queued_buffers; ///< Buffers queued to be played in the stream
|
std::queue<BufferPtr> queued_buffers; ///< Buffers queued to be played in the stream
|
||||||
std::queue<BufferPtr> released_buffers; ///< Buffers recently released from the stream
|
std::queue<BufferPtr> released_buffers; ///< Buffers recently released from the stream
|
||||||
SinkStream& sink_stream; ///< Output sink for the stream
|
SinkStream& sink_stream; ///< Output sink for the stream
|
||||||
|
Core::Timing::CoreTiming& core_timing; ///< Core timing instance.
|
||||||
std::string name; ///< Name of the stream, must be unique
|
std::string name; ///< Name of the stream, must be unique
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -112,14 +112,14 @@ public:
|
||||||
// Always execute at least one tick.
|
// Always execute at least one tick.
|
||||||
amortized_ticks = std::max<u64>(amortized_ticks, 1);
|
amortized_ticks = std::max<u64>(amortized_ticks, 1);
|
||||||
|
|
||||||
Timing::AddTicks(amortized_ticks);
|
parent.core_timing.AddTicks(amortized_ticks);
|
||||||
num_interpreted_instructions = 0;
|
num_interpreted_instructions = 0;
|
||||||
}
|
}
|
||||||
u64 GetTicksRemaining() override {
|
u64 GetTicksRemaining() override {
|
||||||
return std::max(Timing::GetDowncount(), 0);
|
return std::max(parent.core_timing.GetDowncount(), 0);
|
||||||
}
|
}
|
||||||
u64 GetCNTPCT() override {
|
u64 GetCNTPCT() override {
|
||||||
return Timing::GetTicks();
|
return parent.core_timing.GetTicks();
|
||||||
}
|
}
|
||||||
|
|
||||||
ARM_Dynarmic& parent;
|
ARM_Dynarmic& parent;
|
||||||
|
@ -172,8 +172,10 @@ void ARM_Dynarmic::Step() {
|
||||||
cb->InterpreterFallback(jit->GetPC(), 1);
|
cb->InterpreterFallback(jit->GetPC(), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
ARM_Dynarmic::ARM_Dynarmic(ExclusiveMonitor& exclusive_monitor, std::size_t core_index)
|
ARM_Dynarmic::ARM_Dynarmic(Timing::CoreTiming& core_timing, ExclusiveMonitor& exclusive_monitor,
|
||||||
: cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), core_index{core_index},
|
std::size_t core_index)
|
||||||
|
: cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), inner_unicorn{core_timing},
|
||||||
|
core_index{core_index}, core_timing{core_timing},
|
||||||
exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {
|
exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {
|
||||||
ThreadContext ctx{};
|
ThreadContext ctx{};
|
||||||
inner_unicorn.SaveContext(ctx);
|
inner_unicorn.SaveContext(ctx);
|
||||||
|
|
|
@ -16,6 +16,10 @@ namespace Memory {
|
||||||
struct PageTable;
|
struct PageTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace Core::Timing {
|
||||||
|
class CoreTiming;
|
||||||
|
}
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
|
||||||
class ARM_Dynarmic_Callbacks;
|
class ARM_Dynarmic_Callbacks;
|
||||||
|
@ -23,7 +27,8 @@ class DynarmicExclusiveMonitor;
|
||||||
|
|
||||||
class ARM_Dynarmic final : public ARM_Interface {
|
class ARM_Dynarmic final : public ARM_Interface {
|
||||||
public:
|
public:
|
||||||
ARM_Dynarmic(ExclusiveMonitor& exclusive_monitor, std::size_t core_index);
|
ARM_Dynarmic(Timing::CoreTiming& core_timing, ExclusiveMonitor& exclusive_monitor,
|
||||||
|
std::size_t core_index);
|
||||||
~ARM_Dynarmic();
|
~ARM_Dynarmic();
|
||||||
|
|
||||||
void MapBackingMemory(VAddr address, std::size_t size, u8* memory,
|
void MapBackingMemory(VAddr address, std::size_t size, u8* memory,
|
||||||
|
@ -62,6 +67,7 @@ private:
|
||||||
ARM_Unicorn inner_unicorn;
|
ARM_Unicorn inner_unicorn;
|
||||||
|
|
||||||
std::size_t core_index;
|
std::size_t core_index;
|
||||||
|
Timing::CoreTiming& core_timing;
|
||||||
DynarmicExclusiveMonitor& exclusive_monitor;
|
DynarmicExclusiveMonitor& exclusive_monitor;
|
||||||
|
|
||||||
Memory::PageTable* current_page_table = nullptr;
|
Memory::PageTable* current_page_table = nullptr;
|
||||||
|
|
|
@ -72,7 +72,7 @@ static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int si
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
ARM_Unicorn::ARM_Unicorn() {
|
ARM_Unicorn::ARM_Unicorn(Timing::CoreTiming& core_timing) : core_timing{core_timing} {
|
||||||
CHECKED(uc_open(UC_ARCH_ARM64, UC_MODE_ARM, &uc));
|
CHECKED(uc_open(UC_ARCH_ARM64, UC_MODE_ARM, &uc));
|
||||||
|
|
||||||
auto fpv = 3 << 20;
|
auto fpv = 3 << 20;
|
||||||
|
@ -177,7 +177,7 @@ void ARM_Unicorn::Run() {
|
||||||
if (GDBStub::IsServerEnabled()) {
|
if (GDBStub::IsServerEnabled()) {
|
||||||
ExecuteInstructions(std::max(4000000, 0));
|
ExecuteInstructions(std::max(4000000, 0));
|
||||||
} else {
|
} else {
|
||||||
ExecuteInstructions(std::max(Timing::GetDowncount(), 0));
|
ExecuteInstructions(std::max(core_timing.GetDowncount(), 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,7 +190,7 @@ MICROPROFILE_DEFINE(ARM_Jit_Unicorn, "ARM JIT", "Unicorn", MP_RGB(255, 64, 64));
|
||||||
void ARM_Unicorn::ExecuteInstructions(int num_instructions) {
|
void ARM_Unicorn::ExecuteInstructions(int num_instructions) {
|
||||||
MICROPROFILE_SCOPE(ARM_Jit_Unicorn);
|
MICROPROFILE_SCOPE(ARM_Jit_Unicorn);
|
||||||
CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions));
|
CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions));
|
||||||
Timing::AddTicks(num_instructions);
|
core_timing.AddTicks(num_instructions);
|
||||||
if (GDBStub::IsServerEnabled()) {
|
if (GDBStub::IsServerEnabled()) {
|
||||||
if (last_bkpt_hit) {
|
if (last_bkpt_hit) {
|
||||||
uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address);
|
uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address);
|
||||||
|
|
|
@ -9,12 +9,17 @@
|
||||||
#include "core/arm/arm_interface.h"
|
#include "core/arm/arm_interface.h"
|
||||||
#include "core/gdbstub/gdbstub.h"
|
#include "core/gdbstub/gdbstub.h"
|
||||||
|
|
||||||
|
namespace Core::Timing {
|
||||||
|
class CoreTiming;
|
||||||
|
}
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
|
||||||
class ARM_Unicorn final : public ARM_Interface {
|
class ARM_Unicorn final : public ARM_Interface {
|
||||||
public:
|
public:
|
||||||
ARM_Unicorn();
|
explicit ARM_Unicorn(Timing::CoreTiming& core_timing);
|
||||||
~ARM_Unicorn();
|
~ARM_Unicorn();
|
||||||
|
|
||||||
void MapBackingMemory(VAddr address, std::size_t size, u8* memory,
|
void MapBackingMemory(VAddr address, std::size_t size, u8* memory,
|
||||||
Kernel::VMAPermission perms) override;
|
Kernel::VMAPermission perms) override;
|
||||||
void UnmapMemory(VAddr address, std::size_t size) override;
|
void UnmapMemory(VAddr address, std::size_t size) override;
|
||||||
|
@ -43,6 +48,7 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uc_engine* uc{};
|
uc_engine* uc{};
|
||||||
|
Timing::CoreTiming& core_timing;
|
||||||
GDBStub::BreakpointAddress last_bkpt{};
|
GDBStub::BreakpointAddress last_bkpt{};
|
||||||
bool last_bkpt_hit;
|
bool last_bkpt_hit;
|
||||||
};
|
};
|
||||||
|
|
|
@ -94,8 +94,8 @@ struct System::Impl {
|
||||||
ResultStatus Init(System& system, Frontend::EmuWindow& emu_window) {
|
ResultStatus Init(System& system, Frontend::EmuWindow& emu_window) {
|
||||||
LOG_DEBUG(HW_Memory, "initialized OK");
|
LOG_DEBUG(HW_Memory, "initialized OK");
|
||||||
|
|
||||||
Timing::Init();
|
core_timing.Initialize();
|
||||||
kernel.Initialize();
|
kernel.Initialize(core_timing);
|
||||||
|
|
||||||
const auto current_time = std::chrono::duration_cast<std::chrono::seconds>(
|
const auto current_time = std::chrono::duration_cast<std::chrono::seconds>(
|
||||||
std::chrono::system_clock::now().time_since_epoch());
|
std::chrono::system_clock::now().time_since_epoch());
|
||||||
|
@ -120,7 +120,7 @@ struct System::Impl {
|
||||||
telemetry_session = std::make_unique<Core::TelemetrySession>();
|
telemetry_session = std::make_unique<Core::TelemetrySession>();
|
||||||
service_manager = std::make_shared<Service::SM::ServiceManager>();
|
service_manager = std::make_shared<Service::SM::ServiceManager>();
|
||||||
|
|
||||||
Service::Init(service_manager, *virtual_filesystem);
|
Service::Init(service_manager, system, *virtual_filesystem);
|
||||||
GDBStub::Init();
|
GDBStub::Init();
|
||||||
|
|
||||||
renderer = VideoCore::CreateRenderer(emu_window, system);
|
renderer = VideoCore::CreateRenderer(emu_window, system);
|
||||||
|
@ -205,7 +205,7 @@ struct System::Impl {
|
||||||
|
|
||||||
// Shutdown kernel and core timing
|
// Shutdown kernel and core timing
|
||||||
kernel.Shutdown();
|
kernel.Shutdown();
|
||||||
Timing::Shutdown();
|
core_timing.Shutdown();
|
||||||
|
|
||||||
// Close app loader
|
// Close app loader
|
||||||
app_loader.reset();
|
app_loader.reset();
|
||||||
|
@ -232,9 +232,10 @@ struct System::Impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
PerfStatsResults GetAndResetPerfStats() {
|
PerfStatsResults GetAndResetPerfStats() {
|
||||||
return perf_stats.GetAndResetStats(Timing::GetGlobalTimeUs());
|
return perf_stats.GetAndResetStats(core_timing.GetGlobalTimeUs());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Timing::CoreTiming core_timing;
|
||||||
Kernel::KernelCore kernel;
|
Kernel::KernelCore kernel;
|
||||||
/// RealVfsFilesystem instance
|
/// RealVfsFilesystem instance
|
||||||
FileSys::VirtualFilesystem virtual_filesystem;
|
FileSys::VirtualFilesystem virtual_filesystem;
|
||||||
|
@ -396,6 +397,14 @@ const Kernel::KernelCore& System::Kernel() const {
|
||||||
return impl->kernel;
|
return impl->kernel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Timing::CoreTiming& System::CoreTiming() {
|
||||||
|
return impl->core_timing;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Timing::CoreTiming& System::CoreTiming() const {
|
||||||
|
return impl->core_timing;
|
||||||
|
}
|
||||||
|
|
||||||
Core::PerfStats& System::GetPerfStats() {
|
Core::PerfStats& System::GetPerfStats() {
|
||||||
return impl->perf_stats;
|
return impl->perf_stats;
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,10 @@ namespace VideoCore {
|
||||||
class RendererBase;
|
class RendererBase;
|
||||||
} // namespace VideoCore
|
} // namespace VideoCore
|
||||||
|
|
||||||
|
namespace Core::Timing {
|
||||||
|
class CoreTiming;
|
||||||
|
}
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
|
||||||
class ARM_Interface;
|
class ARM_Interface;
|
||||||
|
@ -205,6 +209,12 @@ public:
|
||||||
/// Provides a constant pointer to the current process.
|
/// Provides a constant pointer to the current process.
|
||||||
const Kernel::Process* CurrentProcess() const;
|
const Kernel::Process* CurrentProcess() const;
|
||||||
|
|
||||||
|
/// Provides a reference to the core timing instance.
|
||||||
|
Timing::CoreTiming& CoreTiming();
|
||||||
|
|
||||||
|
/// Provides a constant reference to the core timing instance.
|
||||||
|
const Timing::CoreTiming& CoreTiming() const;
|
||||||
|
|
||||||
/// Provides a reference to the kernel instance.
|
/// Provides a reference to the kernel instance.
|
||||||
Kernel::KernelCore& Kernel();
|
Kernel::KernelCore& Kernel();
|
||||||
|
|
||||||
|
|
|
@ -49,17 +49,18 @@ bool CpuBarrier::Rendezvous() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Cpu::Cpu(ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier, std::size_t core_index)
|
Cpu::Cpu(Timing::CoreTiming& core_timing, ExclusiveMonitor& exclusive_monitor,
|
||||||
: cpu_barrier{cpu_barrier}, core_index{core_index} {
|
CpuBarrier& cpu_barrier, std::size_t core_index)
|
||||||
|
: cpu_barrier{cpu_barrier}, core_timing{core_timing}, core_index{core_index} {
|
||||||
if (Settings::values.use_cpu_jit) {
|
if (Settings::values.use_cpu_jit) {
|
||||||
#ifdef ARCHITECTURE_x86_64
|
#ifdef ARCHITECTURE_x86_64
|
||||||
arm_interface = std::make_unique<ARM_Dynarmic>(exclusive_monitor, core_index);
|
arm_interface = std::make_unique<ARM_Dynarmic>(core_timing, exclusive_monitor, core_index);
|
||||||
#else
|
#else
|
||||||
arm_interface = std::make_unique<ARM_Unicorn>();
|
arm_interface = std::make_unique<ARM_Unicorn>();
|
||||||
LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
|
LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
arm_interface = std::make_unique<ARM_Unicorn>();
|
arm_interface = std::make_unique<ARM_Unicorn>(core_timing);
|
||||||
}
|
}
|
||||||
|
|
||||||
scheduler = std::make_unique<Kernel::Scheduler>(*arm_interface);
|
scheduler = std::make_unique<Kernel::Scheduler>(*arm_interface);
|
||||||
|
@ -93,14 +94,14 @@ void Cpu::RunLoop(bool tight_loop) {
|
||||||
|
|
||||||
if (IsMainCore()) {
|
if (IsMainCore()) {
|
||||||
// TODO(Subv): Only let CoreTiming idle if all 4 cores are idling.
|
// TODO(Subv): Only let CoreTiming idle if all 4 cores are idling.
|
||||||
Timing::Idle();
|
core_timing.Idle();
|
||||||
Timing::Advance();
|
core_timing.Advance();
|
||||||
}
|
}
|
||||||
|
|
||||||
PrepareReschedule();
|
PrepareReschedule();
|
||||||
} else {
|
} else {
|
||||||
if (IsMainCore()) {
|
if (IsMainCore()) {
|
||||||
Timing::Advance();
|
core_timing.Advance();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tight_loop) {
|
if (tight_loop) {
|
||||||
|
|
|
@ -15,6 +15,10 @@ namespace Kernel {
|
||||||
class Scheduler;
|
class Scheduler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace Core::Timing {
|
||||||
|
class CoreTiming;
|
||||||
|
}
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
|
||||||
class ARM_Interface;
|
class ARM_Interface;
|
||||||
|
@ -41,7 +45,8 @@ private:
|
||||||
|
|
||||||
class Cpu {
|
class Cpu {
|
||||||
public:
|
public:
|
||||||
Cpu(ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier, std::size_t core_index);
|
Cpu(Timing::CoreTiming& core_timing, ExclusiveMonitor& exclusive_monitor,
|
||||||
|
CpuBarrier& cpu_barrier, std::size_t core_index);
|
||||||
~Cpu();
|
~Cpu();
|
||||||
|
|
||||||
void RunLoop(bool tight_loop = true);
|
void RunLoop(bool tight_loop = true);
|
||||||
|
@ -82,6 +87,7 @@ private:
|
||||||
std::unique_ptr<ARM_Interface> arm_interface;
|
std::unique_ptr<ARM_Interface> arm_interface;
|
||||||
CpuBarrier& cpu_barrier;
|
CpuBarrier& cpu_barrier;
|
||||||
std::unique_ptr<Kernel::Scheduler> scheduler;
|
std::unique_ptr<Kernel::Scheduler> scheduler;
|
||||||
|
Timing::CoreTiming& core_timing;
|
||||||
|
|
||||||
std::atomic<bool> reschedule_pending = false;
|
std::atomic<bool> reschedule_pending = false;
|
||||||
std::size_t core_index;
|
std::size_t core_index;
|
||||||
|
|
|
@ -8,69 +8,60 @@
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include <unordered_map>
|
|
||||||
#include <vector>
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/thread.h"
|
#include "common/thread.h"
|
||||||
#include "common/threadsafe_queue.h"
|
|
||||||
#include "core/core_timing_util.h"
|
#include "core/core_timing_util.h"
|
||||||
|
|
||||||
namespace Core::Timing {
|
namespace Core::Timing {
|
||||||
|
|
||||||
static s64 global_timer;
|
constexpr int MAX_SLICE_LENGTH = 20000;
|
||||||
static int slice_length;
|
|
||||||
static int downcount;
|
|
||||||
|
|
||||||
struct EventType {
|
struct CoreTiming::Event {
|
||||||
TimedCallback callback;
|
|
||||||
const std::string* name;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Event {
|
|
||||||
s64 time;
|
s64 time;
|
||||||
u64 fifo_order;
|
u64 fifo_order;
|
||||||
u64 userdata;
|
u64 userdata;
|
||||||
const EventType* type;
|
const EventType* type;
|
||||||
};
|
|
||||||
|
|
||||||
// Sort by time, unless the times are the same, in which case sort by the order added to the queue
|
// Sort by time, unless the times are the same, in which case sort by
|
||||||
static bool operator>(const Event& left, const Event& right) {
|
// the order added to the queue
|
||||||
|
friend bool operator>(const Event& left, const Event& right) {
|
||||||
return std::tie(left.time, left.fifo_order) > std::tie(right.time, right.fifo_order);
|
return std::tie(left.time, left.fifo_order) > std::tie(right.time, right.fifo_order);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool operator<(const Event& left, const Event& right) {
|
friend bool operator<(const Event& left, const Event& right) {
|
||||||
return std::tie(left.time, left.fifo_order) < std::tie(right.time, right.fifo_order);
|
return std::tie(left.time, left.fifo_order) < std::tie(right.time, right.fifo_order);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// unordered_map stores each element separately as a linked list node so pointers to elements
|
CoreTiming::CoreTiming() = default;
|
||||||
// remain stable regardless of rehashes/resizing.
|
CoreTiming::~CoreTiming() = default;
|
||||||
static std::unordered_map<std::string, EventType> event_types;
|
|
||||||
|
|
||||||
// The queue is a min-heap using std::make_heap/push_heap/pop_heap.
|
void CoreTiming::Initialize() {
|
||||||
// We don't use std::priority_queue because we need to be able to serialize, unserialize and
|
downcount = MAX_SLICE_LENGTH;
|
||||||
// erase arbitrary events (RemoveEvent()) regardless of the queue order. These aren't accomodated
|
slice_length = MAX_SLICE_LENGTH;
|
||||||
// by the standard adaptor class.
|
global_timer = 0;
|
||||||
static std::vector<Event> event_queue;
|
idled_cycles = 0;
|
||||||
static u64 event_fifo_id;
|
|
||||||
// the queue for storing the events from other threads threadsafe until they will be added
|
|
||||||
// to the event_queue by the emu thread
|
|
||||||
static Common::MPSCQueue<Event> ts_queue;
|
|
||||||
|
|
||||||
// the queue for unscheduling the events from other threads threadsafe
|
// The time between CoreTiming being initialized and the first call to Advance() is considered
|
||||||
static Common::MPSCQueue<std::pair<const EventType*, u64>> unschedule_queue;
|
// the slice boundary between slice -1 and slice 0. Dispatcher loops must call Advance() before
|
||||||
|
// executing the first cycle of each slice to prepare the slice length and downcount for
|
||||||
|
// that slice.
|
||||||
|
is_global_timer_sane = true;
|
||||||
|
|
||||||
constexpr int MAX_SLICE_LENGTH = 20000;
|
event_fifo_id = 0;
|
||||||
|
|
||||||
static s64 idled_cycles;
|
const auto empty_timed_callback = [](u64, s64) {};
|
||||||
|
ev_lost = RegisterEvent("_lost_event", empty_timed_callback);
|
||||||
|
}
|
||||||
|
|
||||||
// Are we in a function that has been called from Advance()
|
void CoreTiming::Shutdown() {
|
||||||
// If events are sheduled from a function that gets called from Advance(),
|
MoveEvents();
|
||||||
// don't change slice_length and downcount.
|
ClearPendingEvents();
|
||||||
static bool is_global_timer_sane;
|
UnregisterAllEvents();
|
||||||
|
}
|
||||||
|
|
||||||
static EventType* ev_lost = nullptr;
|
EventType* CoreTiming::RegisterEvent(const std::string& name, TimedCallback callback) {
|
||||||
|
|
||||||
EventType* RegisterEvent(const std::string& name, TimedCallback callback) {
|
|
||||||
// check for existing type with same name.
|
// check for existing type with same name.
|
||||||
// we want event type names to remain unique so that we can use them for serialization.
|
// we want event type names to remain unique so that we can use them for serialization.
|
||||||
ASSERT_MSG(event_types.find(name) == event_types.end(),
|
ASSERT_MSG(event_types.find(name) == event_types.end(),
|
||||||
|
@ -84,73 +75,31 @@ EventType* RegisterEvent(const std::string& name, TimedCallback callback) {
|
||||||
return event_type;
|
return event_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
void UnregisterAllEvents() {
|
void CoreTiming::UnregisterAllEvents() {
|
||||||
ASSERT_MSG(event_queue.empty(), "Cannot unregister events with events pending");
|
ASSERT_MSG(event_queue.empty(), "Cannot unregister events with events pending");
|
||||||
event_types.clear();
|
event_types.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Init() {
|
void CoreTiming::ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata) {
|
||||||
downcount = MAX_SLICE_LENGTH;
|
|
||||||
slice_length = MAX_SLICE_LENGTH;
|
|
||||||
global_timer = 0;
|
|
||||||
idled_cycles = 0;
|
|
||||||
|
|
||||||
// The time between CoreTiming being intialized and the first call to Advance() is considered
|
|
||||||
// the slice boundary between slice -1 and slice 0. Dispatcher loops must call Advance() before
|
|
||||||
// executing the first cycle of each slice to prepare the slice length and downcount for
|
|
||||||
// that slice.
|
|
||||||
is_global_timer_sane = true;
|
|
||||||
|
|
||||||
event_fifo_id = 0;
|
|
||||||
|
|
||||||
const auto empty_timed_callback = [](u64, s64) {};
|
|
||||||
ev_lost = RegisterEvent("_lost_event", empty_timed_callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Shutdown() {
|
|
||||||
MoveEvents();
|
|
||||||
ClearPendingEvents();
|
|
||||||
UnregisterAllEvents();
|
|
||||||
}
|
|
||||||
|
|
||||||
// This should only be called from the CPU thread. If you are calling
|
|
||||||
// it from any other thread, you are doing something evil
|
|
||||||
u64 GetTicks() {
|
|
||||||
u64 ticks = static_cast<u64>(global_timer);
|
|
||||||
if (!is_global_timer_sane) {
|
|
||||||
ticks += slice_length - downcount;
|
|
||||||
}
|
|
||||||
return ticks;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddTicks(u64 ticks) {
|
|
||||||
downcount -= static_cast<int>(ticks);
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 GetIdleTicks() {
|
|
||||||
return static_cast<u64>(idled_cycles);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClearPendingEvents() {
|
|
||||||
event_queue.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata) {
|
|
||||||
ASSERT(event_type != nullptr);
|
ASSERT(event_type != nullptr);
|
||||||
s64 timeout = GetTicks() + cycles_into_future;
|
const s64 timeout = GetTicks() + cycles_into_future;
|
||||||
|
|
||||||
// If this event needs to be scheduled before the next advance(), force one early
|
// If this event needs to be scheduled before the next advance(), force one early
|
||||||
if (!is_global_timer_sane)
|
if (!is_global_timer_sane) {
|
||||||
ForceExceptionCheck(cycles_into_future);
|
ForceExceptionCheck(cycles_into_future);
|
||||||
|
}
|
||||||
|
|
||||||
event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type});
|
event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type});
|
||||||
std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
|
std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type, u64 userdata) {
|
void CoreTiming::ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type,
|
||||||
|
u64 userdata) {
|
||||||
ts_queue.Push(Event{global_timer + cycles_into_future, 0, userdata, event_type});
|
ts_queue.Push(Event{global_timer + cycles_into_future, 0, userdata, event_type});
|
||||||
}
|
}
|
||||||
|
|
||||||
void UnscheduleEvent(const EventType* event_type, u64 userdata) {
|
void CoreTiming::UnscheduleEvent(const EventType* event_type, u64 userdata) {
|
||||||
auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) {
|
const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) {
|
||||||
return e.type == event_type && e.userdata == userdata;
|
return e.type == event_type && e.userdata == userdata;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -161,12 +110,32 @@ void UnscheduleEvent(const EventType* event_type, u64 userdata) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UnscheduleEventThreadsafe(const EventType* event_type, u64 userdata) {
|
void CoreTiming::UnscheduleEventThreadsafe(const EventType* event_type, u64 userdata) {
|
||||||
unschedule_queue.Push(std::make_pair(event_type, userdata));
|
unschedule_queue.Push(std::make_pair(event_type, userdata));
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemoveEvent(const EventType* event_type) {
|
u64 CoreTiming::GetTicks() const {
|
||||||
auto itr = std::remove_if(event_queue.begin(), event_queue.end(),
|
u64 ticks = static_cast<u64>(global_timer);
|
||||||
|
if (!is_global_timer_sane) {
|
||||||
|
ticks += slice_length - downcount;
|
||||||
|
}
|
||||||
|
return ticks;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 CoreTiming::GetIdleTicks() const {
|
||||||
|
return static_cast<u64>(idled_cycles);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreTiming::AddTicks(u64 ticks) {
|
||||||
|
downcount -= static_cast<int>(ticks);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreTiming::ClearPendingEvents() {
|
||||||
|
event_queue.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreTiming::RemoveEvent(const EventType* event_type) {
|
||||||
|
const auto itr = std::remove_if(event_queue.begin(), event_queue.end(),
|
||||||
[&](const Event& e) { return e.type == event_type; });
|
[&](const Event& e) { return e.type == event_type; });
|
||||||
|
|
||||||
// Removing random items breaks the invariant so we have to re-establish it.
|
// Removing random items breaks the invariant so we have to re-establish it.
|
||||||
|
@ -176,22 +145,24 @@ void RemoveEvent(const EventType* event_type) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemoveNormalAndThreadsafeEvent(const EventType* event_type) {
|
void CoreTiming::RemoveNormalAndThreadsafeEvent(const EventType* event_type) {
|
||||||
MoveEvents();
|
MoveEvents();
|
||||||
RemoveEvent(event_type);
|
RemoveEvent(event_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ForceExceptionCheck(s64 cycles) {
|
void CoreTiming::ForceExceptionCheck(s64 cycles) {
|
||||||
cycles = std::max<s64>(0, cycles);
|
cycles = std::max<s64>(0, cycles);
|
||||||
if (downcount > cycles) {
|
if (downcount <= cycles) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// downcount is always (much) smaller than MAX_INT so we can safely cast cycles to an int
|
// downcount is always (much) smaller than MAX_INT so we can safely cast cycles to an int
|
||||||
// here. Account for cycles already executed by adjusting the g.slice_length
|
// here. Account for cycles already executed by adjusting the g.slice_length
|
||||||
slice_length -= downcount - static_cast<int>(cycles);
|
slice_length -= downcount - static_cast<int>(cycles);
|
||||||
downcount = static_cast<int>(cycles);
|
downcount = static_cast<int>(cycles);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void MoveEvents() {
|
void CoreTiming::MoveEvents() {
|
||||||
for (Event ev; ts_queue.Pop(ev);) {
|
for (Event ev; ts_queue.Pop(ev);) {
|
||||||
ev.fifo_order = event_fifo_id++;
|
ev.fifo_order = event_fifo_id++;
|
||||||
event_queue.emplace_back(std::move(ev));
|
event_queue.emplace_back(std::move(ev));
|
||||||
|
@ -199,13 +170,13 @@ void MoveEvents() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Advance() {
|
void CoreTiming::Advance() {
|
||||||
MoveEvents();
|
MoveEvents();
|
||||||
for (std::pair<const EventType*, u64> ev; unschedule_queue.Pop(ev);) {
|
for (std::pair<const EventType*, u64> ev; unschedule_queue.Pop(ev);) {
|
||||||
UnscheduleEvent(ev.first, ev.second);
|
UnscheduleEvent(ev.first, ev.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
int cycles_executed = slice_length - downcount;
|
const int cycles_executed = slice_length - downcount;
|
||||||
global_timer += cycles_executed;
|
global_timer += cycles_executed;
|
||||||
slice_length = MAX_SLICE_LENGTH;
|
slice_length = MAX_SLICE_LENGTH;
|
||||||
|
|
||||||
|
@ -229,16 +200,16 @@ void Advance() {
|
||||||
downcount = slice_length;
|
downcount = slice_length;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Idle() {
|
void CoreTiming::Idle() {
|
||||||
idled_cycles += downcount;
|
idled_cycles += downcount;
|
||||||
downcount = 0;
|
downcount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::chrono::microseconds GetGlobalTimeUs() {
|
std::chrono::microseconds CoreTiming::GetGlobalTimeUs() const {
|
||||||
return std::chrono::microseconds{GetTicks() * 1000000 / BASE_CLOCK_RATE};
|
return std::chrono::microseconds{GetTicks() * 1000000 / BASE_CLOCK_RATE};
|
||||||
}
|
}
|
||||||
|
|
||||||
int GetDowncount() {
|
int CoreTiming::GetDowncount() const {
|
||||||
return downcount;
|
return downcount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,27 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <functional>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "common/threadsafe_queue.h"
|
||||||
|
|
||||||
|
namespace Core::Timing {
|
||||||
|
|
||||||
|
/// A callback that may be scheduled for a particular core timing event.
|
||||||
|
using TimedCallback = std::function<void(u64 userdata, int cycles_late)>;
|
||||||
|
|
||||||
|
/// Contains the characteristics of a particular event.
|
||||||
|
struct EventType {
|
||||||
|
/// The event's callback function.
|
||||||
|
TimedCallback callback;
|
||||||
|
/// A pointer to the name of the event.
|
||||||
|
const std::string* name;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is a system to schedule events into the emulated machine's future. Time is measured
|
* This is a system to schedule events into the emulated machine's future. Time is measured
|
||||||
* in main CPU clock cycles.
|
* in main CPU clock cycles.
|
||||||
|
@ -16,53 +37,52 @@
|
||||||
* inside callback:
|
* inside callback:
|
||||||
* ScheduleEvent(periodInCycles - cyclesLate, callback, "whatever")
|
* ScheduleEvent(periodInCycles - cyclesLate, callback, "whatever")
|
||||||
*/
|
*/
|
||||||
|
class CoreTiming {
|
||||||
|
public:
|
||||||
|
CoreTiming();
|
||||||
|
~CoreTiming();
|
||||||
|
|
||||||
#include <chrono>
|
CoreTiming(const CoreTiming&) = delete;
|
||||||
#include <functional>
|
CoreTiming(CoreTiming&&) = delete;
|
||||||
#include <string>
|
|
||||||
#include "common/common_types.h"
|
|
||||||
|
|
||||||
namespace Core::Timing {
|
CoreTiming& operator=(const CoreTiming&) = delete;
|
||||||
|
CoreTiming& operator=(CoreTiming&&) = delete;
|
||||||
|
|
||||||
struct EventType;
|
/// CoreTiming begins at the boundary of timing slice -1. An initial call to Advance() is
|
||||||
|
/// required to end slice - 1 and start slice 0 before the first cycle of code is executed.
|
||||||
|
void Initialize();
|
||||||
|
|
||||||
using TimedCallback = std::function<void(u64 userdata, int cycles_late)>;
|
/// Tears down all timing related functionality.
|
||||||
|
|
||||||
/**
|
|
||||||
* CoreTiming begins at the boundary of timing slice -1. An initial call to Advance() is
|
|
||||||
* required to end slice -1 and start slice 0 before the first cycle of code is executed.
|
|
||||||
*/
|
|
||||||
void Init();
|
|
||||||
void Shutdown();
|
void Shutdown();
|
||||||
|
|
||||||
/**
|
/// Registers a core timing event with the given name and callback.
|
||||||
* This should only be called from the emu thread, if you are calling it any other thread, you are
|
///
|
||||||
* doing something evil
|
/// @param name The name of the core timing event to register.
|
||||||
*/
|
/// @param callback The callback to execute for the event.
|
||||||
u64 GetTicks();
|
///
|
||||||
u64 GetIdleTicks();
|
/// @returns An EventType instance representing the registered event.
|
||||||
void AddTicks(u64 ticks);
|
///
|
||||||
|
/// @pre The name of the event being registered must be unique among all
|
||||||
/**
|
/// registered events.
|
||||||
* Returns the event_type identifier. if name is not unique, it will assert.
|
///
|
||||||
*/
|
|
||||||
EventType* RegisterEvent(const std::string& name, TimedCallback callback);
|
EventType* RegisterEvent(const std::string& name, TimedCallback callback);
|
||||||
|
|
||||||
|
/// Unregisters all registered events thus far.
|
||||||
void UnregisterAllEvents();
|
void UnregisterAllEvents();
|
||||||
|
|
||||||
/**
|
/// After the first Advance, the slice lengths and the downcount will be reduced whenever an
|
||||||
* After the first Advance, the slice lengths and the downcount will be reduced whenever an event
|
/// event is scheduled earlier than the current values.
|
||||||
* is scheduled earlier than the current values.
|
///
|
||||||
* Scheduling from a callback will not update the downcount until the Advance() completes.
|
/// Scheduling from a callback will not update the downcount until the Advance() completes.
|
||||||
*/
|
|
||||||
void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata = 0);
|
void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata = 0);
|
||||||
|
|
||||||
/**
|
/// This is to be called when outside of hle threads, such as the graphics thread, wants to
|
||||||
* This is to be called when outside of hle threads, such as the graphics thread, wants to
|
/// schedule things to be executed on the main thread.
|
||||||
* schedule things to be executed on the main thread.
|
///
|
||||||
* Not that this doesn't change slice_length and thus events scheduled by this might be called
|
/// @note This doesn't change slice_length and thus events scheduled by this might be
|
||||||
* with a delay of up to MAX_SLICE_LENGTH
|
/// called with a delay of up to MAX_SLICE_LENGTH
|
||||||
*/
|
void ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type,
|
||||||
void ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type, u64 userdata);
|
u64 userdata = 0);
|
||||||
|
|
||||||
void UnscheduleEvent(const EventType* event_type, u64 userdata);
|
void UnscheduleEvent(const EventType* event_type, u64 userdata);
|
||||||
void UnscheduleEventThreadsafe(const EventType* event_type, u64 userdata);
|
void UnscheduleEventThreadsafe(const EventType* event_type, u64 userdata);
|
||||||
|
@ -71,25 +91,66 @@ void UnscheduleEventThreadsafe(const EventType* event_type, u64 userdata);
|
||||||
void RemoveEvent(const EventType* event_type);
|
void RemoveEvent(const EventType* event_type);
|
||||||
void RemoveNormalAndThreadsafeEvent(const EventType* event_type);
|
void RemoveNormalAndThreadsafeEvent(const EventType* event_type);
|
||||||
|
|
||||||
/** Advance must be called at the beginning of dispatcher loops, not the end. Advance() ends
|
void ForceExceptionCheck(s64 cycles);
|
||||||
* the previous timing slice and begins the next one, you must Advance from the previous
|
|
||||||
* slice to the current one before executing any cycles. CoreTiming starts in slice -1 so an
|
/// This should only be called from the emu thread, if you are calling it any other thread,
|
||||||
* Advance() is required to initialize the slice length before the first cycle of emulated
|
/// you are doing something evil
|
||||||
* instructions is executed.
|
u64 GetTicks() const;
|
||||||
*/
|
|
||||||
|
u64 GetIdleTicks() const;
|
||||||
|
|
||||||
|
void AddTicks(u64 ticks);
|
||||||
|
|
||||||
|
/// Advance must be called at the beginning of dispatcher loops, not the end. Advance() ends
|
||||||
|
/// the previous timing slice and begins the next one, you must Advance from the previous
|
||||||
|
/// slice to the current one before executing any cycles. CoreTiming starts in slice -1 so an
|
||||||
|
/// Advance() is required to initialize the slice length before the first cycle of emulated
|
||||||
|
/// instructions is executed.
|
||||||
void Advance();
|
void Advance();
|
||||||
void MoveEvents();
|
|
||||||
|
|
||||||
/// Pretend that the main CPU has executed enough cycles to reach the next event.
|
/// Pretend that the main CPU has executed enough cycles to reach the next event.
|
||||||
void Idle();
|
void Idle();
|
||||||
|
|
||||||
|
std::chrono::microseconds GetGlobalTimeUs() const;
|
||||||
|
|
||||||
|
int GetDowncount() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Event;
|
||||||
|
|
||||||
/// Clear all pending events. This should ONLY be done on exit.
|
/// Clear all pending events. This should ONLY be done on exit.
|
||||||
void ClearPendingEvents();
|
void ClearPendingEvents();
|
||||||
|
void MoveEvents();
|
||||||
|
|
||||||
void ForceExceptionCheck(s64 cycles);
|
s64 global_timer = 0;
|
||||||
|
s64 idled_cycles = 0;
|
||||||
|
int slice_length = 0;
|
||||||
|
int downcount = 0;
|
||||||
|
|
||||||
std::chrono::microseconds GetGlobalTimeUs();
|
// Are we in a function that has been called from Advance()
|
||||||
|
// If events are scheduled from a function that gets called from Advance(),
|
||||||
|
// don't change slice_length and downcount.
|
||||||
|
bool is_global_timer_sane = false;
|
||||||
|
|
||||||
int GetDowncount();
|
// The queue is a min-heap using std::make_heap/push_heap/pop_heap.
|
||||||
|
// We don't use std::priority_queue because we need to be able to serialize, unserialize and
|
||||||
|
// erase arbitrary events (RemoveEvent()) regardless of the queue order. These aren't
|
||||||
|
// accomodated by the standard adaptor class.
|
||||||
|
std::vector<Event> event_queue;
|
||||||
|
u64 event_fifo_id = 0;
|
||||||
|
|
||||||
|
// Stores each element separately as a linked list node so pointers to elements
|
||||||
|
// remain stable regardless of rehashes/resizing.
|
||||||
|
std::unordered_map<std::string, EventType> event_types;
|
||||||
|
|
||||||
|
// The queue for storing the events from other threads threadsafe until they will be added
|
||||||
|
// to the event_queue by the emu thread
|
||||||
|
Common::MPSCQueue<Event> ts_queue;
|
||||||
|
|
||||||
|
// The queue for unscheduling the events from other threads threadsafe
|
||||||
|
Common::MPSCQueue<std::pair<const EventType*, u64>> unschedule_queue;
|
||||||
|
|
||||||
|
EventType* ev_lost = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace Core::Timing
|
} // namespace Core::Timing
|
||||||
|
|
|
@ -27,7 +27,8 @@ void CpuCoreManager::Initialize(System& system) {
|
||||||
exclusive_monitor = Cpu::MakeExclusiveMonitor(cores.size());
|
exclusive_monitor = Cpu::MakeExclusiveMonitor(cores.size());
|
||||||
|
|
||||||
for (std::size_t index = 0; index < cores.size(); ++index) {
|
for (std::size_t index = 0; index < cores.size(); ++index) {
|
||||||
cores[index] = std::make_unique<Cpu>(*exclusive_monitor, *barrier, index);
|
cores[index] =
|
||||||
|
std::make_unique<Cpu>(system.CoreTiming(), *exclusive_monitor, *barrier, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create threads for CPU cores 1-3, and build thread_to_cpu map
|
// Create threads for CPU cores 1-3, and build thread_to_cpu map
|
||||||
|
|
|
@ -86,11 +86,11 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] int cycles_
|
||||||
}
|
}
|
||||||
|
|
||||||
struct KernelCore::Impl {
|
struct KernelCore::Impl {
|
||||||
void Initialize(KernelCore& kernel) {
|
void Initialize(KernelCore& kernel, Core::Timing::CoreTiming& core_timing) {
|
||||||
Shutdown();
|
Shutdown();
|
||||||
|
|
||||||
InitializeSystemResourceLimit(kernel);
|
InitializeSystemResourceLimit(kernel);
|
||||||
InitializeThreads();
|
InitializeThreads(core_timing);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Shutdown() {
|
void Shutdown() {
|
||||||
|
@ -122,9 +122,9 @@ struct KernelCore::Impl {
|
||||||
ASSERT(system_resource_limit->SetLimitValue(ResourceType::Sessions, 900).IsSuccess());
|
ASSERT(system_resource_limit->SetLimitValue(ResourceType::Sessions, 900).IsSuccess());
|
||||||
}
|
}
|
||||||
|
|
||||||
void InitializeThreads() {
|
void InitializeThreads(Core::Timing::CoreTiming& core_timing) {
|
||||||
thread_wakeup_event_type =
|
thread_wakeup_event_type =
|
||||||
Core::Timing::RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback);
|
core_timing.RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::atomic<u32> next_object_id{0};
|
std::atomic<u32> next_object_id{0};
|
||||||
|
@ -152,8 +152,8 @@ KernelCore::~KernelCore() {
|
||||||
Shutdown();
|
Shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
void KernelCore::Initialize() {
|
void KernelCore::Initialize(Core::Timing::CoreTiming& core_timing) {
|
||||||
impl->Initialize(*this);
|
impl->Initialize(*this, core_timing);
|
||||||
}
|
}
|
||||||
|
|
||||||
void KernelCore::Shutdown() {
|
void KernelCore::Shutdown() {
|
||||||
|
|
|
@ -12,8 +12,9 @@ template <typename T>
|
||||||
class ResultVal;
|
class ResultVal;
|
||||||
|
|
||||||
namespace Core::Timing {
|
namespace Core::Timing {
|
||||||
|
class CoreTiming;
|
||||||
struct EventType;
|
struct EventType;
|
||||||
}
|
} // namespace Core::Timing
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
|
@ -39,7 +40,11 @@ public:
|
||||||
KernelCore& operator=(KernelCore&&) = delete;
|
KernelCore& operator=(KernelCore&&) = delete;
|
||||||
|
|
||||||
/// Resets the kernel to a clean slate for use.
|
/// Resets the kernel to a clean slate for use.
|
||||||
void Initialize();
|
///
|
||||||
|
/// @param core_timing CoreTiming instance used to create any necessary
|
||||||
|
/// kernel-specific callback events.
|
||||||
|
///
|
||||||
|
void Initialize(Core::Timing::CoreTiming& core_timing);
|
||||||
|
|
||||||
/// Clears all resources in use by the kernel instance.
|
/// Clears all resources in use by the kernel instance.
|
||||||
void Shutdown();
|
void Shutdown();
|
||||||
|
|
|
@ -111,7 +111,7 @@ void Scheduler::SwitchContext(Thread* new_thread) {
|
||||||
|
|
||||||
void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) {
|
void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) {
|
||||||
const u64 prev_switch_ticks = last_context_switch_time;
|
const u64 prev_switch_ticks = last_context_switch_time;
|
||||||
const u64 most_recent_switch_ticks = Core::Timing::GetTicks();
|
const u64 most_recent_switch_ticks = Core::System::GetInstance().CoreTiming().GetTicks();
|
||||||
const u64 update_ticks = most_recent_switch_ticks - prev_switch_ticks;
|
const u64 update_ticks = most_recent_switch_ticks - prev_switch_ticks;
|
||||||
|
|
||||||
if (thread != nullptr) {
|
if (thread != nullptr) {
|
||||||
|
|
|
@ -918,6 +918,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& system = Core::System::GetInstance();
|
const auto& system = Core::System::GetInstance();
|
||||||
|
const auto& core_timing = system.CoreTiming();
|
||||||
const auto& scheduler = system.CurrentScheduler();
|
const auto& scheduler = system.CurrentScheduler();
|
||||||
const auto* const current_thread = scheduler.GetCurrentThread();
|
const auto* const current_thread = scheduler.GetCurrentThread();
|
||||||
const bool same_thread = current_thread == thread;
|
const bool same_thread = current_thread == thread;
|
||||||
|
@ -927,9 +928,9 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
|
||||||
if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) {
|
if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) {
|
||||||
const u64 thread_ticks = current_thread->GetTotalCPUTimeTicks();
|
const u64 thread_ticks = current_thread->GetTotalCPUTimeTicks();
|
||||||
|
|
||||||
out_ticks = thread_ticks + (Core::Timing::GetTicks() - prev_ctx_ticks);
|
out_ticks = thread_ticks + (core_timing.GetTicks() - prev_ctx_ticks);
|
||||||
} else if (same_thread && info_sub_id == system.CurrentCoreIndex()) {
|
} else if (same_thread && info_sub_id == system.CurrentCoreIndex()) {
|
||||||
out_ticks = Core::Timing::GetTicks() - prev_ctx_ticks;
|
out_ticks = core_timing.GetTicks() - prev_ctx_ticks;
|
||||||
}
|
}
|
||||||
|
|
||||||
*result = out_ticks;
|
*result = out_ticks;
|
||||||
|
@ -1546,10 +1547,11 @@ static ResultCode SignalToAddress(VAddr address, u32 type, s32 value, s32 num_to
|
||||||
static u64 GetSystemTick() {
|
static u64 GetSystemTick() {
|
||||||
LOG_TRACE(Kernel_SVC, "called");
|
LOG_TRACE(Kernel_SVC, "called");
|
||||||
|
|
||||||
const u64 result{Core::Timing::GetTicks()};
|
auto& core_timing = Core::System::GetInstance().CoreTiming();
|
||||||
|
const u64 result{core_timing.GetTicks()};
|
||||||
|
|
||||||
// Advance time to defeat dumb games that busy-wait for the frame to end.
|
// Advance time to defeat dumb games that busy-wait for the frame to end.
|
||||||
Core::Timing::AddTicks(400);
|
core_timing.AddTicks(400);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,8 @@ Thread::~Thread() = default;
|
||||||
|
|
||||||
void Thread::Stop() {
|
void Thread::Stop() {
|
||||||
// Cancel any outstanding wakeup events for this thread
|
// Cancel any outstanding wakeup events for this thread
|
||||||
Core::Timing::UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(), callback_handle);
|
Core::System::GetInstance().CoreTiming().UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(),
|
||||||
|
callback_handle);
|
||||||
kernel.ThreadWakeupCallbackHandleTable().Close(callback_handle);
|
kernel.ThreadWakeupCallbackHandleTable().Close(callback_handle);
|
||||||
callback_handle = 0;
|
callback_handle = 0;
|
||||||
|
|
||||||
|
@ -85,13 +86,14 @@ void Thread::WakeAfterDelay(s64 nanoseconds) {
|
||||||
|
|
||||||
// This function might be called from any thread so we have to be cautious and use the
|
// This function might be called from any thread so we have to be cautious and use the
|
||||||
// thread-safe version of ScheduleEvent.
|
// thread-safe version of ScheduleEvent.
|
||||||
Core::Timing::ScheduleEventThreadsafe(Core::Timing::nsToCycles(nanoseconds),
|
Core::System::GetInstance().CoreTiming().ScheduleEventThreadsafe(
|
||||||
kernel.ThreadWakeupCallbackEventType(), callback_handle);
|
Core::Timing::nsToCycles(nanoseconds), kernel.ThreadWakeupCallbackEventType(),
|
||||||
|
callback_handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Thread::CancelWakeupTimer() {
|
void Thread::CancelWakeupTimer() {
|
||||||
Core::Timing::UnscheduleEventThreadsafe(kernel.ThreadWakeupCallbackEventType(),
|
Core::System::GetInstance().CoreTiming().UnscheduleEventThreadsafe(
|
||||||
callback_handle);
|
kernel.ThreadWakeupCallbackEventType(), callback_handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::optional<s32> GetNextProcessorId(u64 mask) {
|
static std::optional<s32> GetNextProcessorId(u64 mask) {
|
||||||
|
@ -190,6 +192,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name
|
||||||
return ResultCode(-1);
|
return ResultCode(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto& system = Core::System::GetInstance();
|
||||||
SharedPtr<Thread> thread(new Thread(kernel));
|
SharedPtr<Thread> thread(new Thread(kernel));
|
||||||
|
|
||||||
thread->thread_id = kernel.CreateNewThreadID();
|
thread->thread_id = kernel.CreateNewThreadID();
|
||||||
|
@ -198,7 +201,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name
|
||||||
thread->stack_top = stack_top;
|
thread->stack_top = stack_top;
|
||||||
thread->tpidr_el0 = 0;
|
thread->tpidr_el0 = 0;
|
||||||
thread->nominal_priority = thread->current_priority = priority;
|
thread->nominal_priority = thread->current_priority = priority;
|
||||||
thread->last_running_ticks = Core::Timing::GetTicks();
|
thread->last_running_ticks = system.CoreTiming().GetTicks();
|
||||||
thread->processor_id = processor_id;
|
thread->processor_id = processor_id;
|
||||||
thread->ideal_core = processor_id;
|
thread->ideal_core = processor_id;
|
||||||
thread->affinity_mask = 1ULL << processor_id;
|
thread->affinity_mask = 1ULL << processor_id;
|
||||||
|
@ -209,7 +212,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name
|
||||||
thread->name = std::move(name);
|
thread->name = std::move(name);
|
||||||
thread->callback_handle = kernel.ThreadWakeupCallbackHandleTable().Create(thread).Unwrap();
|
thread->callback_handle = kernel.ThreadWakeupCallbackHandleTable().Create(thread).Unwrap();
|
||||||
thread->owner_process = &owner_process;
|
thread->owner_process = &owner_process;
|
||||||
thread->scheduler = &Core::System::GetInstance().Scheduler(processor_id);
|
thread->scheduler = &system.Scheduler(processor_id);
|
||||||
thread->scheduler->AddThread(thread, priority);
|
thread->scheduler->AddThread(thread, priority);
|
||||||
thread->tls_address = thread->owner_process->MarkNextAvailableTLSSlotAsUsed(*thread);
|
thread->tls_address = thread->owner_process->MarkNextAvailableTLSSlotAsUsed(*thread);
|
||||||
|
|
||||||
|
@ -258,7 +261,7 @@ void Thread::SetStatus(ThreadStatus new_status) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status == ThreadStatus::Running) {
|
if (status == ThreadStatus::Running) {
|
||||||
last_running_ticks = Core::Timing::GetTicks();
|
last_running_ticks = Core::System::GetInstance().CoreTiming().GetTicks();
|
||||||
}
|
}
|
||||||
|
|
||||||
status = new_status;
|
status = new_status;
|
||||||
|
|
|
@ -68,12 +68,12 @@ public:
|
||||||
RegisterHandlers(functions);
|
RegisterHandlers(functions);
|
||||||
|
|
||||||
// This is the event handle used to check if the audio buffer was released
|
// This is the event handle used to check if the audio buffer was released
|
||||||
auto& kernel = Core::System::GetInstance().Kernel();
|
auto& system = Core::System::GetInstance();
|
||||||
buffer_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky,
|
buffer_event = Kernel::WritableEvent::CreateEventPair(
|
||||||
"IAudioOutBufferReleased");
|
system.Kernel(), Kernel::ResetType::Sticky, "IAudioOutBufferReleased");
|
||||||
|
|
||||||
stream = audio_core.OpenStream(audio_params.sample_rate, audio_params.channel_count,
|
stream = audio_core.OpenStream(system.CoreTiming(), audio_params.sample_rate,
|
||||||
std::move(unique_name),
|
audio_params.channel_count, std::move(unique_name),
|
||||||
[=]() { buffer_event.writable->Signal(); });
|
[=]() { buffer_event.writable->Signal(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,10 +42,11 @@ public:
|
||||||
// clang-format on
|
// clang-format on
|
||||||
RegisterHandlers(functions);
|
RegisterHandlers(functions);
|
||||||
|
|
||||||
auto& kernel = Core::System::GetInstance().Kernel();
|
auto& system = Core::System::GetInstance();
|
||||||
system_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky,
|
system_event = Kernel::WritableEvent::CreateEventPair(
|
||||||
"IAudioRenderer:SystemEvent");
|
system.Kernel(), Kernel::ResetType::Sticky, "IAudioRenderer:SystemEvent");
|
||||||
renderer = std::make_unique<AudioCore::AudioRenderer>(audren_params, system_event.writable);
|
renderer = std::make_unique<AudioCore::AudioRenderer>(system.CoreTiming(), audren_params,
|
||||||
|
system_event.writable);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -7,6 +7,10 @@
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/swap.h"
|
#include "common/swap.h"
|
||||||
|
|
||||||
|
namespace Core::Timing {
|
||||||
|
class CoreTiming;
|
||||||
|
}
|
||||||
|
|
||||||
namespace Service::HID {
|
namespace Service::HID {
|
||||||
class ControllerBase {
|
class ControllerBase {
|
||||||
public:
|
public:
|
||||||
|
@ -20,7 +24,8 @@ public:
|
||||||
virtual void OnRelease() = 0;
|
virtual void OnRelease() = 0;
|
||||||
|
|
||||||
// When the controller is requesting an update for the shared memory
|
// When the controller is requesting an update for the shared memory
|
||||||
virtual void OnUpdate(u8* data, std::size_t size) = 0;
|
virtual void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
|
||||||
|
std::size_t size) = 0;
|
||||||
|
|
||||||
// Called when input devices should be loaded
|
// Called when input devices should be loaded
|
||||||
virtual void OnLoadInputDevices() = 0;
|
virtual void OnLoadInputDevices() = 0;
|
||||||
|
|
|
@ -21,8 +21,9 @@ void Controller_DebugPad::OnInit() {}
|
||||||
|
|
||||||
void Controller_DebugPad::OnRelease() {}
|
void Controller_DebugPad::OnRelease() {}
|
||||||
|
|
||||||
void Controller_DebugPad::OnUpdate(u8* data, std::size_t size) {
|
void Controller_DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
|
||||||
shared_memory.header.timestamp = Core::Timing::GetTicks();
|
std::size_t size) {
|
||||||
|
shared_memory.header.timestamp = core_timing.GetTicks();
|
||||||
shared_memory.header.total_entry_count = 17;
|
shared_memory.header.total_entry_count = 17;
|
||||||
|
|
||||||
if (!IsControllerActivated()) {
|
if (!IsControllerActivated()) {
|
||||||
|
|
|
@ -26,7 +26,7 @@ public:
|
||||||
void OnRelease() override;
|
void OnRelease() override;
|
||||||
|
|
||||||
// When the controller is requesting an update for the shared memory
|
// When the controller is requesting an update for the shared memory
|
||||||
void OnUpdate(u8* data, std::size_t size) override;
|
void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
|
||||||
|
|
||||||
// Called when input devices should be loaded
|
// Called when input devices should be loaded
|
||||||
void OnLoadInputDevices() override;
|
void OnLoadInputDevices() override;
|
||||||
|
|
|
@ -17,8 +17,9 @@ void Controller_Gesture::OnInit() {}
|
||||||
|
|
||||||
void Controller_Gesture::OnRelease() {}
|
void Controller_Gesture::OnRelease() {}
|
||||||
|
|
||||||
void Controller_Gesture::OnUpdate(u8* data, std::size_t size) {
|
void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
|
||||||
shared_memory.header.timestamp = Core::Timing::GetTicks();
|
std::size_t size) {
|
||||||
|
shared_memory.header.timestamp = core_timing.GetTicks();
|
||||||
shared_memory.header.total_entry_count = 17;
|
shared_memory.header.total_entry_count = 17;
|
||||||
|
|
||||||
if (!IsControllerActivated()) {
|
if (!IsControllerActivated()) {
|
||||||
|
|
|
@ -22,7 +22,7 @@ public:
|
||||||
void OnRelease() override;
|
void OnRelease() override;
|
||||||
|
|
||||||
// When the controller is requesting an update for the shared memory
|
// When the controller is requesting an update for the shared memory
|
||||||
void OnUpdate(u8* data, size_t size) override;
|
void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, size_t size) override;
|
||||||
|
|
||||||
// Called when input devices should be loaded
|
// Called when input devices should be loaded
|
||||||
void OnLoadInputDevices() override;
|
void OnLoadInputDevices() override;
|
||||||
|
|
|
@ -19,8 +19,9 @@ void Controller_Keyboard::OnInit() {}
|
||||||
|
|
||||||
void Controller_Keyboard::OnRelease() {}
|
void Controller_Keyboard::OnRelease() {}
|
||||||
|
|
||||||
void Controller_Keyboard::OnUpdate(u8* data, std::size_t size) {
|
void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
|
||||||
shared_memory.header.timestamp = Core::Timing::GetTicks();
|
std::size_t size) {
|
||||||
|
shared_memory.header.timestamp = core_timing.GetTicks();
|
||||||
shared_memory.header.total_entry_count = 17;
|
shared_memory.header.total_entry_count = 17;
|
||||||
|
|
||||||
if (!IsControllerActivated()) {
|
if (!IsControllerActivated()) {
|
||||||
|
|
|
@ -25,7 +25,7 @@ public:
|
||||||
void OnRelease() override;
|
void OnRelease() override;
|
||||||
|
|
||||||
// When the controller is requesting an update for the shared memory
|
// When the controller is requesting an update for the shared memory
|
||||||
void OnUpdate(u8* data, std::size_t size) override;
|
void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
|
||||||
|
|
||||||
// Called when input devices should be loaded
|
// Called when input devices should be loaded
|
||||||
void OnLoadInputDevices() override;
|
void OnLoadInputDevices() override;
|
||||||
|
|
|
@ -17,8 +17,9 @@ Controller_Mouse::~Controller_Mouse() = default;
|
||||||
void Controller_Mouse::OnInit() {}
|
void Controller_Mouse::OnInit() {}
|
||||||
void Controller_Mouse::OnRelease() {}
|
void Controller_Mouse::OnRelease() {}
|
||||||
|
|
||||||
void Controller_Mouse::OnUpdate(u8* data, std::size_t size) {
|
void Controller_Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
|
||||||
shared_memory.header.timestamp = Core::Timing::GetTicks();
|
std::size_t size) {
|
||||||
|
shared_memory.header.timestamp = core_timing.GetTicks();
|
||||||
shared_memory.header.total_entry_count = 17;
|
shared_memory.header.total_entry_count = 17;
|
||||||
|
|
||||||
if (!IsControllerActivated()) {
|
if (!IsControllerActivated()) {
|
||||||
|
|
|
@ -24,7 +24,7 @@ public:
|
||||||
void OnRelease() override;
|
void OnRelease() override;
|
||||||
|
|
||||||
// When the controller is requesting an update for the shared memory
|
// When the controller is requesting an update for the shared memory
|
||||||
void OnUpdate(u8* data, std::size_t size) override;
|
void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
|
||||||
|
|
||||||
// Called when input devices should be loaded
|
// Called when input devices should be loaded
|
||||||
void OnLoadInputDevices() override;
|
void OnLoadInputDevices() override;
|
||||||
|
|
|
@ -288,7 +288,8 @@ void Controller_NPad::RequestPadStateUpdate(u32 npad_id) {
|
||||||
rstick_entry.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
|
rstick_entry.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
|
void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
|
||||||
|
std::size_t data_len) {
|
||||||
if (!IsControllerActivated())
|
if (!IsControllerActivated())
|
||||||
return;
|
return;
|
||||||
for (std::size_t i = 0; i < shared_memory_entries.size(); i++) {
|
for (std::size_t i = 0; i < shared_memory_entries.size(); i++) {
|
||||||
|
@ -308,7 +309,7 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
|
||||||
const auto& last_entry =
|
const auto& last_entry =
|
||||||
main_controller->npad[main_controller->common.last_entry_index];
|
main_controller->npad[main_controller->common.last_entry_index];
|
||||||
|
|
||||||
main_controller->common.timestamp = Core::Timing::GetTicks();
|
main_controller->common.timestamp = core_timing.GetTicks();
|
||||||
main_controller->common.last_entry_index =
|
main_controller->common.last_entry_index =
|
||||||
(main_controller->common.last_entry_index + 1) % 17;
|
(main_controller->common.last_entry_index + 1) % 17;
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ public:
|
||||||
void OnRelease() override;
|
void OnRelease() override;
|
||||||
|
|
||||||
// When the controller is requesting an update for the shared memory
|
// When the controller is requesting an update for the shared memory
|
||||||
void OnUpdate(u8* data, std::size_t size) override;
|
void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
|
||||||
|
|
||||||
// Called when input devices should be loaded
|
// Called when input devices should be loaded
|
||||||
void OnLoadInputDevices() override;
|
void OnLoadInputDevices() override;
|
||||||
|
|
|
@ -16,13 +16,14 @@ void Controller_Stubbed::OnInit() {}
|
||||||
|
|
||||||
void Controller_Stubbed::OnRelease() {}
|
void Controller_Stubbed::OnRelease() {}
|
||||||
|
|
||||||
void Controller_Stubbed::OnUpdate(u8* data, std::size_t size) {
|
void Controller_Stubbed::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
|
||||||
|
std::size_t size) {
|
||||||
if (!smart_update) {
|
if (!smart_update) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
CommonHeader header{};
|
CommonHeader header{};
|
||||||
header.timestamp = Core::Timing::GetTicks();
|
header.timestamp = core_timing.GetTicks();
|
||||||
header.total_entry_count = 17;
|
header.total_entry_count = 17;
|
||||||
header.entry_count = 0;
|
header.entry_count = 0;
|
||||||
header.last_entry_index = 0;
|
header.last_entry_index = 0;
|
||||||
|
|
|
@ -20,7 +20,7 @@ public:
|
||||||
void OnRelease() override;
|
void OnRelease() override;
|
||||||
|
|
||||||
// When the controller is requesting an update for the shared memory
|
// When the controller is requesting an update for the shared memory
|
||||||
void OnUpdate(u8* data, std::size_t size) override;
|
void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
|
||||||
|
|
||||||
// Called when input devices should be loaded
|
// Called when input devices should be loaded
|
||||||
void OnLoadInputDevices() override;
|
void OnLoadInputDevices() override;
|
||||||
|
|
|
@ -20,8 +20,9 @@ void Controller_Touchscreen::OnInit() {}
|
||||||
|
|
||||||
void Controller_Touchscreen::OnRelease() {}
|
void Controller_Touchscreen::OnRelease() {}
|
||||||
|
|
||||||
void Controller_Touchscreen::OnUpdate(u8* data, std::size_t size) {
|
void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
|
||||||
shared_memory.header.timestamp = Core::Timing::GetTicks();
|
std::size_t size) {
|
||||||
|
shared_memory.header.timestamp = core_timing.GetTicks();
|
||||||
shared_memory.header.total_entry_count = 17;
|
shared_memory.header.total_entry_count = 17;
|
||||||
|
|
||||||
if (!IsControllerActivated()) {
|
if (!IsControllerActivated()) {
|
||||||
|
@ -48,7 +49,7 @@ void Controller_Touchscreen::OnUpdate(u8* data, std::size_t size) {
|
||||||
touch_entry.diameter_x = Settings::values.touchscreen.diameter_x;
|
touch_entry.diameter_x = Settings::values.touchscreen.diameter_x;
|
||||||
touch_entry.diameter_y = Settings::values.touchscreen.diameter_y;
|
touch_entry.diameter_y = Settings::values.touchscreen.diameter_y;
|
||||||
touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle;
|
touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle;
|
||||||
const u64 tick = Core::Timing::GetTicks();
|
const u64 tick = core_timing.GetTicks();
|
||||||
touch_entry.delta_time = tick - last_touch;
|
touch_entry.delta_time = tick - last_touch;
|
||||||
last_touch = tick;
|
last_touch = tick;
|
||||||
touch_entry.finger = Settings::values.touchscreen.finger;
|
touch_entry.finger = Settings::values.touchscreen.finger;
|
||||||
|
|
|
@ -24,7 +24,7 @@ public:
|
||||||
void OnRelease() override;
|
void OnRelease() override;
|
||||||
|
|
||||||
// When the controller is requesting an update for the shared memory
|
// When the controller is requesting an update for the shared memory
|
||||||
void OnUpdate(u8* data, std::size_t size) override;
|
void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
|
||||||
|
|
||||||
// Called when input devices should be loaded
|
// Called when input devices should be loaded
|
||||||
void OnLoadInputDevices() override;
|
void OnLoadInputDevices() override;
|
||||||
|
|
|
@ -17,9 +17,10 @@ void Controller_XPad::OnInit() {}
|
||||||
|
|
||||||
void Controller_XPad::OnRelease() {}
|
void Controller_XPad::OnRelease() {}
|
||||||
|
|
||||||
void Controller_XPad::OnUpdate(u8* data, std::size_t size) {
|
void Controller_XPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
|
||||||
|
std::size_t size) {
|
||||||
for (auto& xpad_entry : shared_memory.shared_memory_entries) {
|
for (auto& xpad_entry : shared_memory.shared_memory_entries) {
|
||||||
xpad_entry.header.timestamp = Core::Timing::GetTicks();
|
xpad_entry.header.timestamp = core_timing.GetTicks();
|
||||||
xpad_entry.header.total_entry_count = 17;
|
xpad_entry.header.total_entry_count = 17;
|
||||||
|
|
||||||
if (!IsControllerActivated()) {
|
if (!IsControllerActivated()) {
|
||||||
|
|
|
@ -22,7 +22,7 @@ public:
|
||||||
void OnRelease() override;
|
void OnRelease() override;
|
||||||
|
|
||||||
// When the controller is requesting an update for the shared memory
|
// When the controller is requesting an update for the shared memory
|
||||||
void OnUpdate(u8* data, std::size_t size) override;
|
void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
|
||||||
|
|
||||||
// Called when input devices should be loaded
|
// Called when input devices should be loaded
|
||||||
void OnLoadInputDevices() override;
|
void OnLoadInputDevices() override;
|
||||||
|
|
|
@ -73,13 +73,15 @@ IAppletResource::IAppletResource() : ServiceFramework("IAppletResource") {
|
||||||
GetController<Controller_Stubbed>(HidController::Unknown3).SetCommonHeaderOffset(0x5000);
|
GetController<Controller_Stubbed>(HidController::Unknown3).SetCommonHeaderOffset(0x5000);
|
||||||
|
|
||||||
// Register update callbacks
|
// Register update callbacks
|
||||||
pad_update_event = Core::Timing::RegisterEvent(
|
auto& core_timing = Core::System::GetInstance().CoreTiming();
|
||||||
"HID::UpdatePadCallback",
|
pad_update_event =
|
||||||
[this](u64 userdata, int cycles_late) { UpdateControllers(userdata, cycles_late); });
|
core_timing.RegisterEvent("HID::UpdatePadCallback", [this](u64 userdata, int cycles_late) {
|
||||||
|
UpdateControllers(userdata, cycles_late);
|
||||||
|
});
|
||||||
|
|
||||||
// TODO(shinyquagsire23): Other update callbacks? (accel, gyro?)
|
// TODO(shinyquagsire23): Other update callbacks? (accel, gyro?)
|
||||||
|
|
||||||
Core::Timing::ScheduleEvent(pad_update_ticks, pad_update_event);
|
core_timing.ScheduleEvent(pad_update_ticks, pad_update_event);
|
||||||
|
|
||||||
ReloadInputDevices();
|
ReloadInputDevices();
|
||||||
}
|
}
|
||||||
|
@ -93,7 +95,7 @@ void IAppletResource::DeactivateController(HidController controller) {
|
||||||
}
|
}
|
||||||
|
|
||||||
IAppletResource ::~IAppletResource() {
|
IAppletResource ::~IAppletResource() {
|
||||||
Core::Timing::UnscheduleEvent(pad_update_event, 0);
|
Core::System::GetInstance().CoreTiming().UnscheduleEvent(pad_update_event, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IAppletResource::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
|
void IAppletResource::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
|
||||||
|
@ -105,15 +107,17 @@ void IAppletResource::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void IAppletResource::UpdateControllers(u64 userdata, int cycles_late) {
|
void IAppletResource::UpdateControllers(u64 userdata, int cycles_late) {
|
||||||
|
auto& core_timing = Core::System::GetInstance().CoreTiming();
|
||||||
|
|
||||||
const bool should_reload = Settings::values.is_device_reload_pending.exchange(false);
|
const bool should_reload = Settings::values.is_device_reload_pending.exchange(false);
|
||||||
for (const auto& controller : controllers) {
|
for (const auto& controller : controllers) {
|
||||||
if (should_reload) {
|
if (should_reload) {
|
||||||
controller->OnLoadInputDevices();
|
controller->OnLoadInputDevices();
|
||||||
}
|
}
|
||||||
controller->OnUpdate(shared_mem->GetPointer(), SHARED_MEMORY_SIZE);
|
controller->OnUpdate(core_timing, shared_mem->GetPointer(), SHARED_MEMORY_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
Core::Timing::ScheduleEvent(pad_update_ticks - cycles_late, pad_update_event);
|
core_timing.ScheduleEvent(pad_update_ticks - cycles_late, pad_update_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> {
|
class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> {
|
||||||
|
|
|
@ -98,7 +98,7 @@ void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) {
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 5};
|
IPC::ResponseBuilder rb{ctx, 5};
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
rb.PushRaw<u64>(Core::Timing::GetTicks());
|
rb.PushRaw<u64>(Core::System::GetInstance().CoreTiming().GetTicks());
|
||||||
rb.PushRaw<u32>(0);
|
rb.PushRaw<u32>(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
|
#include "core/core.h"
|
||||||
#include "core/core_timing.h"
|
#include "core/core_timing.h"
|
||||||
#include "core/core_timing_util.h"
|
#include "core/core_timing_util.h"
|
||||||
#include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h"
|
#include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h"
|
||||||
|
@ -184,7 +185,7 @@ u32 nvhost_ctrl_gpu::GetGpuTime(const std::vector<u8>& input, std::vector<u8>& o
|
||||||
|
|
||||||
IoctlGetGpuTime params{};
|
IoctlGetGpuTime params{};
|
||||||
std::memcpy(¶ms, input.data(), input.size());
|
std::memcpy(¶ms, input.data(), input.size());
|
||||||
params.gpu_time = Core::Timing::cyclesToNs(Core::Timing::GetTicks());
|
params.gpu_time = Core::Timing::cyclesToNs(Core::System::GetInstance().CoreTiming().GetTicks());
|
||||||
std::memcpy(output.data(), ¶ms, output.size());
|
std::memcpy(output.data(), ¶ms, output.size());
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,19 +27,19 @@ namespace Service::NVFlinger {
|
||||||
constexpr std::size_t SCREEN_REFRESH_RATE = 60;
|
constexpr std::size_t SCREEN_REFRESH_RATE = 60;
|
||||||
constexpr u64 frame_ticks = static_cast<u64>(Core::Timing::BASE_CLOCK_RATE / SCREEN_REFRESH_RATE);
|
constexpr u64 frame_ticks = static_cast<u64>(Core::Timing::BASE_CLOCK_RATE / SCREEN_REFRESH_RATE);
|
||||||
|
|
||||||
NVFlinger::NVFlinger() {
|
NVFlinger::NVFlinger(Core::Timing::CoreTiming& core_timing) : core_timing{core_timing} {
|
||||||
// Schedule the screen composition events
|
// Schedule the screen composition events
|
||||||
composition_event =
|
composition_event =
|
||||||
Core::Timing::RegisterEvent("ScreenComposition", [this](u64 userdata, int cycles_late) {
|
core_timing.RegisterEvent("ScreenComposition", [this](u64 userdata, int cycles_late) {
|
||||||
Compose();
|
Compose();
|
||||||
Core::Timing::ScheduleEvent(frame_ticks - cycles_late, composition_event);
|
this->core_timing.ScheduleEvent(frame_ticks - cycles_late, composition_event);
|
||||||
});
|
});
|
||||||
|
|
||||||
Core::Timing::ScheduleEvent(frame_ticks, composition_event);
|
core_timing.ScheduleEvent(frame_ticks, composition_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
NVFlinger::~NVFlinger() {
|
NVFlinger::~NVFlinger() {
|
||||||
Core::Timing::UnscheduleEvent(composition_event, 0);
|
core_timing.UnscheduleEvent(composition_event, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) {
|
void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) {
|
||||||
|
|
|
@ -15,8 +15,9 @@
|
||||||
#include "core/hle/kernel/object.h"
|
#include "core/hle/kernel/object.h"
|
||||||
|
|
||||||
namespace Core::Timing {
|
namespace Core::Timing {
|
||||||
|
class CoreTiming;
|
||||||
struct EventType;
|
struct EventType;
|
||||||
}
|
} // namespace Core::Timing
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
class ReadableEvent;
|
class ReadableEvent;
|
||||||
|
@ -52,7 +53,7 @@ struct Display {
|
||||||
|
|
||||||
class NVFlinger final {
|
class NVFlinger final {
|
||||||
public:
|
public:
|
||||||
NVFlinger();
|
explicit NVFlinger(Core::Timing::CoreTiming& core_timing);
|
||||||
~NVFlinger();
|
~NVFlinger();
|
||||||
|
|
||||||
/// Sets the NVDrv module instance to use to send buffers to the GPU.
|
/// Sets the NVDrv module instance to use to send buffers to the GPU.
|
||||||
|
@ -117,6 +118,9 @@ private:
|
||||||
|
|
||||||
/// Event that handles screen composition.
|
/// Event that handles screen composition.
|
||||||
Core::Timing::EventType* composition_event;
|
Core::Timing::EventType* composition_event;
|
||||||
|
|
||||||
|
/// Core timing instance for registering/unregistering the composition event.
|
||||||
|
Core::Timing::CoreTiming& core_timing;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Service::NVFlinger
|
} // namespace Service::NVFlinger
|
||||||
|
|
|
@ -194,10 +194,11 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co
|
||||||
// Module interface
|
// Module interface
|
||||||
|
|
||||||
/// Initialize ServiceManager
|
/// Initialize ServiceManager
|
||||||
void Init(std::shared_ptr<SM::ServiceManager>& sm, FileSys::VfsFilesystem& vfs) {
|
void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system,
|
||||||
|
FileSys::VfsFilesystem& vfs) {
|
||||||
// NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it
|
// NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it
|
||||||
// here and pass it into the respective InstallInterfaces functions.
|
// here and pass it into the respective InstallInterfaces functions.
|
||||||
auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>();
|
auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>(system.CoreTiming());
|
||||||
|
|
||||||
SM::ServiceManager::InstallInterfaces(sm);
|
SM::ServiceManager::InstallInterfaces(sm);
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,14 @@
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Namespace Service
|
// Namespace Service
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
class System;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace FileSys {
|
||||||
|
class VfsFilesystem;
|
||||||
|
}
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
class ClientPort;
|
class ClientPort;
|
||||||
class ServerPort;
|
class ServerPort;
|
||||||
|
@ -21,10 +29,6 @@ class ServerSession;
|
||||||
class HLERequestContext;
|
class HLERequestContext;
|
||||||
} // namespace Kernel
|
} // namespace Kernel
|
||||||
|
|
||||||
namespace FileSys {
|
|
||||||
class VfsFilesystem;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace Service {
|
namespace Service {
|
||||||
|
|
||||||
namespace SM {
|
namespace SM {
|
||||||
|
@ -178,7 +182,8 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Initialize ServiceManager
|
/// Initialize ServiceManager
|
||||||
void Init(std::shared_ptr<SM::ServiceManager>& sm, FileSys::VfsFilesystem& vfs);
|
void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system,
|
||||||
|
FileSys::VfsFilesystem& vfs);
|
||||||
|
|
||||||
/// Shutdown ServiceManager
|
/// Shutdown ServiceManager
|
||||||
void Shutdown();
|
void Shutdown();
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
|
#include "core/core.h"
|
||||||
#include "core/core_timing.h"
|
#include "core/core_timing.h"
|
||||||
#include "core/core_timing_util.h"
|
#include "core/core_timing_util.h"
|
||||||
#include "core/hle/ipc_helpers.h"
|
#include "core/hle/ipc_helpers.h"
|
||||||
|
@ -106,8 +107,9 @@ private:
|
||||||
void GetCurrentTimePoint(Kernel::HLERequestContext& ctx) {
|
void GetCurrentTimePoint(Kernel::HLERequestContext& ctx) {
|
||||||
LOG_DEBUG(Service_Time, "called");
|
LOG_DEBUG(Service_Time, "called");
|
||||||
|
|
||||||
|
const auto& core_timing = Core::System::GetInstance().CoreTiming();
|
||||||
const SteadyClockTimePoint steady_clock_time_point{
|
const SteadyClockTimePoint steady_clock_time_point{
|
||||||
Core::Timing::cyclesToMs(Core::Timing::GetTicks()) / 1000};
|
Core::Timing::cyclesToMs(core_timing.GetTicks()) / 1000};
|
||||||
IPC::ResponseBuilder rb{ctx, (sizeof(SteadyClockTimePoint) / 4) + 2};
|
IPC::ResponseBuilder rb{ctx, (sizeof(SteadyClockTimePoint) / 4) + 2};
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
rb.PushRaw(steady_clock_time_point);
|
rb.PushRaw(steady_clock_time_point);
|
||||||
|
@ -281,8 +283,9 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto& core_timing = Core::System::GetInstance().CoreTiming();
|
||||||
const SteadyClockTimePoint steady_clock_time_point{
|
const SteadyClockTimePoint steady_clock_time_point{
|
||||||
Core::Timing::cyclesToMs(Core::Timing::GetTicks()) / 1000, {}};
|
Core::Timing::cyclesToMs(core_timing.GetTicks()) / 1000, {}};
|
||||||
|
|
||||||
CalendarTime calendar_time{};
|
CalendarTime calendar_time{};
|
||||||
calendar_time.year = tm->tm_year + 1900;
|
calendar_time.year = tm->tm_year + 1900;
|
||||||
|
|
|
@ -28,100 +28,103 @@ void CallbackTemplate(u64 userdata, s64 cycles_late) {
|
||||||
REQUIRE(lateness == cycles_late);
|
REQUIRE(lateness == cycles_late);
|
||||||
}
|
}
|
||||||
|
|
||||||
class ScopeInit final {
|
struct ScopeInit final {
|
||||||
public:
|
|
||||||
ScopeInit() {
|
ScopeInit() {
|
||||||
Core::Timing::Init();
|
core_timing.Initialize();
|
||||||
}
|
}
|
||||||
~ScopeInit() {
|
~ScopeInit() {
|
||||||
Core::Timing::Shutdown();
|
core_timing.Shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Core::Timing::CoreTiming core_timing;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void AdvanceAndCheck(u32 idx, int downcount, int expected_lateness = 0,
|
static void AdvanceAndCheck(Core::Timing::CoreTiming& core_timing, u32 idx, int downcount,
|
||||||
int cpu_downcount = 0) {
|
int expected_lateness = 0, int cpu_downcount = 0) {
|
||||||
callbacks_ran_flags = 0;
|
callbacks_ran_flags = 0;
|
||||||
expected_callback = CB_IDS[idx];
|
expected_callback = CB_IDS[idx];
|
||||||
lateness = expected_lateness;
|
lateness = expected_lateness;
|
||||||
|
|
||||||
// Pretend we executed X cycles of instructions.
|
// Pretend we executed X cycles of instructions.
|
||||||
Core::Timing::AddTicks(Core::Timing::GetDowncount() - cpu_downcount);
|
core_timing.AddTicks(core_timing.GetDowncount() - cpu_downcount);
|
||||||
Core::Timing::Advance();
|
core_timing.Advance();
|
||||||
|
|
||||||
REQUIRE(decltype(callbacks_ran_flags)().set(idx) == callbacks_ran_flags);
|
REQUIRE(decltype(callbacks_ran_flags)().set(idx) == callbacks_ran_flags);
|
||||||
REQUIRE(downcount == Core::Timing::GetDowncount());
|
REQUIRE(downcount == core_timing.GetDowncount());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("CoreTiming[BasicOrder]", "[core]") {
|
TEST_CASE("CoreTiming[BasicOrder]", "[core]") {
|
||||||
ScopeInit guard;
|
ScopeInit guard;
|
||||||
|
auto& core_timing = guard.core_timing;
|
||||||
|
|
||||||
Core::Timing::EventType* cb_a = Core::Timing::RegisterEvent("callbackA", CallbackTemplate<0>);
|
Core::Timing::EventType* cb_a = core_timing.RegisterEvent("callbackA", CallbackTemplate<0>);
|
||||||
Core::Timing::EventType* cb_b = Core::Timing::RegisterEvent("callbackB", CallbackTemplate<1>);
|
Core::Timing::EventType* cb_b = core_timing.RegisterEvent("callbackB", CallbackTemplate<1>);
|
||||||
Core::Timing::EventType* cb_c = Core::Timing::RegisterEvent("callbackC", CallbackTemplate<2>);
|
Core::Timing::EventType* cb_c = core_timing.RegisterEvent("callbackC", CallbackTemplate<2>);
|
||||||
Core::Timing::EventType* cb_d = Core::Timing::RegisterEvent("callbackD", CallbackTemplate<3>);
|
Core::Timing::EventType* cb_d = core_timing.RegisterEvent("callbackD", CallbackTemplate<3>);
|
||||||
Core::Timing::EventType* cb_e = Core::Timing::RegisterEvent("callbackE", CallbackTemplate<4>);
|
Core::Timing::EventType* cb_e = core_timing.RegisterEvent("callbackE", CallbackTemplate<4>);
|
||||||
|
|
||||||
// Enter slice 0
|
// Enter slice 0
|
||||||
Core::Timing::Advance();
|
core_timing.Advance();
|
||||||
|
|
||||||
// D -> B -> C -> A -> E
|
// D -> B -> C -> A -> E
|
||||||
Core::Timing::ScheduleEvent(1000, cb_a, CB_IDS[0]);
|
core_timing.ScheduleEvent(1000, cb_a, CB_IDS[0]);
|
||||||
REQUIRE(1000 == Core::Timing::GetDowncount());
|
REQUIRE(1000 == core_timing.GetDowncount());
|
||||||
Core::Timing::ScheduleEvent(500, cb_b, CB_IDS[1]);
|
core_timing.ScheduleEvent(500, cb_b, CB_IDS[1]);
|
||||||
REQUIRE(500 == Core::Timing::GetDowncount());
|
REQUIRE(500 == core_timing.GetDowncount());
|
||||||
Core::Timing::ScheduleEvent(800, cb_c, CB_IDS[2]);
|
core_timing.ScheduleEvent(800, cb_c, CB_IDS[2]);
|
||||||
REQUIRE(500 == Core::Timing::GetDowncount());
|
REQUIRE(500 == core_timing.GetDowncount());
|
||||||
Core::Timing::ScheduleEvent(100, cb_d, CB_IDS[3]);
|
core_timing.ScheduleEvent(100, cb_d, CB_IDS[3]);
|
||||||
REQUIRE(100 == Core::Timing::GetDowncount());
|
REQUIRE(100 == core_timing.GetDowncount());
|
||||||
Core::Timing::ScheduleEvent(1200, cb_e, CB_IDS[4]);
|
core_timing.ScheduleEvent(1200, cb_e, CB_IDS[4]);
|
||||||
REQUIRE(100 == Core::Timing::GetDowncount());
|
REQUIRE(100 == core_timing.GetDowncount());
|
||||||
|
|
||||||
AdvanceAndCheck(3, 400);
|
AdvanceAndCheck(core_timing, 3, 400);
|
||||||
AdvanceAndCheck(1, 300);
|
AdvanceAndCheck(core_timing, 1, 300);
|
||||||
AdvanceAndCheck(2, 200);
|
AdvanceAndCheck(core_timing, 2, 200);
|
||||||
AdvanceAndCheck(0, 200);
|
AdvanceAndCheck(core_timing, 0, 200);
|
||||||
AdvanceAndCheck(4, MAX_SLICE_LENGTH);
|
AdvanceAndCheck(core_timing, 4, MAX_SLICE_LENGTH);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("CoreTiming[Threadsave]", "[core]") {
|
TEST_CASE("CoreTiming[Threadsave]", "[core]") {
|
||||||
ScopeInit guard;
|
ScopeInit guard;
|
||||||
|
auto& core_timing = guard.core_timing;
|
||||||
|
|
||||||
Core::Timing::EventType* cb_a = Core::Timing::RegisterEvent("callbackA", CallbackTemplate<0>);
|
Core::Timing::EventType* cb_a = core_timing.RegisterEvent("callbackA", CallbackTemplate<0>);
|
||||||
Core::Timing::EventType* cb_b = Core::Timing::RegisterEvent("callbackB", CallbackTemplate<1>);
|
Core::Timing::EventType* cb_b = core_timing.RegisterEvent("callbackB", CallbackTemplate<1>);
|
||||||
Core::Timing::EventType* cb_c = Core::Timing::RegisterEvent("callbackC", CallbackTemplate<2>);
|
Core::Timing::EventType* cb_c = core_timing.RegisterEvent("callbackC", CallbackTemplate<2>);
|
||||||
Core::Timing::EventType* cb_d = Core::Timing::RegisterEvent("callbackD", CallbackTemplate<3>);
|
Core::Timing::EventType* cb_d = core_timing.RegisterEvent("callbackD", CallbackTemplate<3>);
|
||||||
Core::Timing::EventType* cb_e = Core::Timing::RegisterEvent("callbackE", CallbackTemplate<4>);
|
Core::Timing::EventType* cb_e = core_timing.RegisterEvent("callbackE", CallbackTemplate<4>);
|
||||||
|
|
||||||
// Enter slice 0
|
// Enter slice 0
|
||||||
Core::Timing::Advance();
|
core_timing.Advance();
|
||||||
|
|
||||||
// D -> B -> C -> A -> E
|
// D -> B -> C -> A -> E
|
||||||
Core::Timing::ScheduleEventThreadsafe(1000, cb_a, CB_IDS[0]);
|
core_timing.ScheduleEventThreadsafe(1000, cb_a, CB_IDS[0]);
|
||||||
// Manually force since ScheduleEventThreadsafe doesn't call it
|
// Manually force since ScheduleEventThreadsafe doesn't call it
|
||||||
Core::Timing::ForceExceptionCheck(1000);
|
core_timing.ForceExceptionCheck(1000);
|
||||||
REQUIRE(1000 == Core::Timing::GetDowncount());
|
REQUIRE(1000 == core_timing.GetDowncount());
|
||||||
Core::Timing::ScheduleEventThreadsafe(500, cb_b, CB_IDS[1]);
|
core_timing.ScheduleEventThreadsafe(500, cb_b, CB_IDS[1]);
|
||||||
// Manually force since ScheduleEventThreadsafe doesn't call it
|
// Manually force since ScheduleEventThreadsafe doesn't call it
|
||||||
Core::Timing::ForceExceptionCheck(500);
|
core_timing.ForceExceptionCheck(500);
|
||||||
REQUIRE(500 == Core::Timing::GetDowncount());
|
REQUIRE(500 == core_timing.GetDowncount());
|
||||||
Core::Timing::ScheduleEventThreadsafe(800, cb_c, CB_IDS[2]);
|
core_timing.ScheduleEventThreadsafe(800, cb_c, CB_IDS[2]);
|
||||||
// Manually force since ScheduleEventThreadsafe doesn't call it
|
// Manually force since ScheduleEventThreadsafe doesn't call it
|
||||||
Core::Timing::ForceExceptionCheck(800);
|
core_timing.ForceExceptionCheck(800);
|
||||||
REQUIRE(500 == Core::Timing::GetDowncount());
|
REQUIRE(500 == core_timing.GetDowncount());
|
||||||
Core::Timing::ScheduleEventThreadsafe(100, cb_d, CB_IDS[3]);
|
core_timing.ScheduleEventThreadsafe(100, cb_d, CB_IDS[3]);
|
||||||
// Manually force since ScheduleEventThreadsafe doesn't call it
|
// Manually force since ScheduleEventThreadsafe doesn't call it
|
||||||
Core::Timing::ForceExceptionCheck(100);
|
core_timing.ForceExceptionCheck(100);
|
||||||
REQUIRE(100 == Core::Timing::GetDowncount());
|
REQUIRE(100 == core_timing.GetDowncount());
|
||||||
Core::Timing::ScheduleEventThreadsafe(1200, cb_e, CB_IDS[4]);
|
core_timing.ScheduleEventThreadsafe(1200, cb_e, CB_IDS[4]);
|
||||||
// Manually force since ScheduleEventThreadsafe doesn't call it
|
// Manually force since ScheduleEventThreadsafe doesn't call it
|
||||||
Core::Timing::ForceExceptionCheck(1200);
|
core_timing.ForceExceptionCheck(1200);
|
||||||
REQUIRE(100 == Core::Timing::GetDowncount());
|
REQUIRE(100 == core_timing.GetDowncount());
|
||||||
|
|
||||||
AdvanceAndCheck(3, 400);
|
AdvanceAndCheck(core_timing, 3, 400);
|
||||||
AdvanceAndCheck(1, 300);
|
AdvanceAndCheck(core_timing, 1, 300);
|
||||||
AdvanceAndCheck(2, 200);
|
AdvanceAndCheck(core_timing, 2, 200);
|
||||||
AdvanceAndCheck(0, 200);
|
AdvanceAndCheck(core_timing, 0, 200);
|
||||||
AdvanceAndCheck(4, MAX_SLICE_LENGTH);
|
AdvanceAndCheck(core_timing, 4, MAX_SLICE_LENGTH);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace SharedSlotTest {
|
namespace SharedSlotTest {
|
||||||
|
@ -142,58 +145,61 @@ TEST_CASE("CoreTiming[SharedSlot]", "[core]") {
|
||||||
using namespace SharedSlotTest;
|
using namespace SharedSlotTest;
|
||||||
|
|
||||||
ScopeInit guard;
|
ScopeInit guard;
|
||||||
|
auto& core_timing = guard.core_timing;
|
||||||
|
|
||||||
Core::Timing::EventType* cb_a = Core::Timing::RegisterEvent("callbackA", FifoCallback<0>);
|
Core::Timing::EventType* cb_a = core_timing.RegisterEvent("callbackA", FifoCallback<0>);
|
||||||
Core::Timing::EventType* cb_b = Core::Timing::RegisterEvent("callbackB", FifoCallback<1>);
|
Core::Timing::EventType* cb_b = core_timing.RegisterEvent("callbackB", FifoCallback<1>);
|
||||||
Core::Timing::EventType* cb_c = Core::Timing::RegisterEvent("callbackC", FifoCallback<2>);
|
Core::Timing::EventType* cb_c = core_timing.RegisterEvent("callbackC", FifoCallback<2>);
|
||||||
Core::Timing::EventType* cb_d = Core::Timing::RegisterEvent("callbackD", FifoCallback<3>);
|
Core::Timing::EventType* cb_d = core_timing.RegisterEvent("callbackD", FifoCallback<3>);
|
||||||
Core::Timing::EventType* cb_e = Core::Timing::RegisterEvent("callbackE", FifoCallback<4>);
|
Core::Timing::EventType* cb_e = core_timing.RegisterEvent("callbackE", FifoCallback<4>);
|
||||||
|
|
||||||
Core::Timing::ScheduleEvent(1000, cb_a, CB_IDS[0]);
|
core_timing.ScheduleEvent(1000, cb_a, CB_IDS[0]);
|
||||||
Core::Timing::ScheduleEvent(1000, cb_b, CB_IDS[1]);
|
core_timing.ScheduleEvent(1000, cb_b, CB_IDS[1]);
|
||||||
Core::Timing::ScheduleEvent(1000, cb_c, CB_IDS[2]);
|
core_timing.ScheduleEvent(1000, cb_c, CB_IDS[2]);
|
||||||
Core::Timing::ScheduleEvent(1000, cb_d, CB_IDS[3]);
|
core_timing.ScheduleEvent(1000, cb_d, CB_IDS[3]);
|
||||||
Core::Timing::ScheduleEvent(1000, cb_e, CB_IDS[4]);
|
core_timing.ScheduleEvent(1000, cb_e, CB_IDS[4]);
|
||||||
|
|
||||||
// Enter slice 0
|
// Enter slice 0
|
||||||
Core::Timing::Advance();
|
core_timing.Advance();
|
||||||
REQUIRE(1000 == Core::Timing::GetDowncount());
|
REQUIRE(1000 == core_timing.GetDowncount());
|
||||||
|
|
||||||
callbacks_ran_flags = 0;
|
callbacks_ran_flags = 0;
|
||||||
counter = 0;
|
counter = 0;
|
||||||
lateness = 0;
|
lateness = 0;
|
||||||
Core::Timing::AddTicks(Core::Timing::GetDowncount());
|
core_timing.AddTicks(core_timing.GetDowncount());
|
||||||
Core::Timing::Advance();
|
core_timing.Advance();
|
||||||
REQUIRE(MAX_SLICE_LENGTH == Core::Timing::GetDowncount());
|
REQUIRE(MAX_SLICE_LENGTH == core_timing.GetDowncount());
|
||||||
REQUIRE(0x1FULL == callbacks_ran_flags.to_ullong());
|
REQUIRE(0x1FULL == callbacks_ran_flags.to_ullong());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Core::Timing[PredictableLateness]", "[core]") {
|
TEST_CASE("Core::Timing[PredictableLateness]", "[core]") {
|
||||||
ScopeInit guard;
|
ScopeInit guard;
|
||||||
|
auto& core_timing = guard.core_timing;
|
||||||
|
|
||||||
Core::Timing::EventType* cb_a = Core::Timing::RegisterEvent("callbackA", CallbackTemplate<0>);
|
Core::Timing::EventType* cb_a = core_timing.RegisterEvent("callbackA", CallbackTemplate<0>);
|
||||||
Core::Timing::EventType* cb_b = Core::Timing::RegisterEvent("callbackB", CallbackTemplate<1>);
|
Core::Timing::EventType* cb_b = core_timing.RegisterEvent("callbackB", CallbackTemplate<1>);
|
||||||
|
|
||||||
// Enter slice 0
|
// Enter slice 0
|
||||||
Core::Timing::Advance();
|
core_timing.Advance();
|
||||||
|
|
||||||
Core::Timing::ScheduleEvent(100, cb_a, CB_IDS[0]);
|
core_timing.ScheduleEvent(100, cb_a, CB_IDS[0]);
|
||||||
Core::Timing::ScheduleEvent(200, cb_b, CB_IDS[1]);
|
core_timing.ScheduleEvent(200, cb_b, CB_IDS[1]);
|
||||||
|
|
||||||
AdvanceAndCheck(0, 90, 10, -10); // (100 - 10)
|
AdvanceAndCheck(core_timing, 0, 90, 10, -10); // (100 - 10)
|
||||||
AdvanceAndCheck(1, MAX_SLICE_LENGTH, 50, -50);
|
AdvanceAndCheck(core_timing, 1, MAX_SLICE_LENGTH, 50, -50);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace ChainSchedulingTest {
|
namespace ChainSchedulingTest {
|
||||||
static int reschedules = 0;
|
static int reschedules = 0;
|
||||||
|
|
||||||
static void RescheduleCallback(u64 userdata, s64 cycles_late) {
|
static void RescheduleCallback(Core::Timing::CoreTiming& core_timing, u64 userdata,
|
||||||
|
s64 cycles_late) {
|
||||||
--reschedules;
|
--reschedules;
|
||||||
REQUIRE(reschedules >= 0);
|
REQUIRE(reschedules >= 0);
|
||||||
REQUIRE(lateness == cycles_late);
|
REQUIRE(lateness == cycles_late);
|
||||||
|
|
||||||
if (reschedules > 0) {
|
if (reschedules > 0) {
|
||||||
Core::Timing::ScheduleEvent(1000, reinterpret_cast<Core::Timing::EventType*>(userdata),
|
core_timing.ScheduleEvent(1000, reinterpret_cast<Core::Timing::EventType*>(userdata),
|
||||||
userdata);
|
userdata);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -203,36 +209,39 @@ TEST_CASE("CoreTiming[ChainScheduling]", "[core]") {
|
||||||
using namespace ChainSchedulingTest;
|
using namespace ChainSchedulingTest;
|
||||||
|
|
||||||
ScopeInit guard;
|
ScopeInit guard;
|
||||||
|
auto& core_timing = guard.core_timing;
|
||||||
|
|
||||||
Core::Timing::EventType* cb_a = Core::Timing::RegisterEvent("callbackA", CallbackTemplate<0>);
|
Core::Timing::EventType* cb_a = core_timing.RegisterEvent("callbackA", CallbackTemplate<0>);
|
||||||
Core::Timing::EventType* cb_b = Core::Timing::RegisterEvent("callbackB", CallbackTemplate<1>);
|
Core::Timing::EventType* cb_b = core_timing.RegisterEvent("callbackB", CallbackTemplate<1>);
|
||||||
Core::Timing::EventType* cb_c = Core::Timing::RegisterEvent("callbackC", CallbackTemplate<2>);
|
Core::Timing::EventType* cb_c = core_timing.RegisterEvent("callbackC", CallbackTemplate<2>);
|
||||||
Core::Timing::EventType* cb_rs =
|
Core::Timing::EventType* cb_rs = core_timing.RegisterEvent(
|
||||||
Core::Timing::RegisterEvent("callbackReschedule", RescheduleCallback);
|
"callbackReschedule", [&core_timing](u64 userdata, s64 cycles_late) {
|
||||||
|
RescheduleCallback(core_timing, userdata, cycles_late);
|
||||||
|
});
|
||||||
|
|
||||||
// Enter slice 0
|
// Enter slice 0
|
||||||
Core::Timing::Advance();
|
core_timing.Advance();
|
||||||
|
|
||||||
Core::Timing::ScheduleEvent(800, cb_a, CB_IDS[0]);
|
core_timing.ScheduleEvent(800, cb_a, CB_IDS[0]);
|
||||||
Core::Timing::ScheduleEvent(1000, cb_b, CB_IDS[1]);
|
core_timing.ScheduleEvent(1000, cb_b, CB_IDS[1]);
|
||||||
Core::Timing::ScheduleEvent(2200, cb_c, CB_IDS[2]);
|
core_timing.ScheduleEvent(2200, cb_c, CB_IDS[2]);
|
||||||
Core::Timing::ScheduleEvent(1000, cb_rs, reinterpret_cast<u64>(cb_rs));
|
core_timing.ScheduleEvent(1000, cb_rs, reinterpret_cast<u64>(cb_rs));
|
||||||
REQUIRE(800 == Core::Timing::GetDowncount());
|
REQUIRE(800 == core_timing.GetDowncount());
|
||||||
|
|
||||||
reschedules = 3;
|
reschedules = 3;
|
||||||
AdvanceAndCheck(0, 200); // cb_a
|
AdvanceAndCheck(core_timing, 0, 200); // cb_a
|
||||||
AdvanceAndCheck(1, 1000); // cb_b, cb_rs
|
AdvanceAndCheck(core_timing, 1, 1000); // cb_b, cb_rs
|
||||||
REQUIRE(2 == reschedules);
|
REQUIRE(2 == reschedules);
|
||||||
|
|
||||||
Core::Timing::AddTicks(Core::Timing::GetDowncount());
|
core_timing.AddTicks(core_timing.GetDowncount());
|
||||||
Core::Timing::Advance(); // cb_rs
|
core_timing.Advance(); // cb_rs
|
||||||
REQUIRE(1 == reschedules);
|
REQUIRE(1 == reschedules);
|
||||||
REQUIRE(200 == Core::Timing::GetDowncount());
|
REQUIRE(200 == core_timing.GetDowncount());
|
||||||
|
|
||||||
AdvanceAndCheck(2, 800); // cb_c
|
AdvanceAndCheck(core_timing, 2, 800); // cb_c
|
||||||
|
|
||||||
Core::Timing::AddTicks(Core::Timing::GetDowncount());
|
core_timing.AddTicks(core_timing.GetDowncount());
|
||||||
Core::Timing::Advance(); // cb_rs
|
core_timing.Advance(); // cb_rs
|
||||||
REQUIRE(0 == reschedules);
|
REQUIRE(0 == reschedules);
|
||||||
REQUIRE(MAX_SLICE_LENGTH == Core::Timing::GetDowncount());
|
REQUIRE(MAX_SLICE_LENGTH == core_timing.GetDowncount());
|
||||||
}
|
}
|
||||||
|
|
|
@ -317,7 +317,7 @@ void Maxwell3D::ProcessQueryGet() {
|
||||||
LongQueryResult query_result{};
|
LongQueryResult query_result{};
|
||||||
query_result.value = result;
|
query_result.value = result;
|
||||||
// TODO(Subv): Generate a real GPU timestamp and write it here instead of CoreTiming
|
// TODO(Subv): Generate a real GPU timestamp and write it here instead of CoreTiming
|
||||||
query_result.timestamp = Core::Timing::GetTicks();
|
query_result.timestamp = Core::System::GetInstance().CoreTiming().GetTicks();
|
||||||
Memory::WriteBlock(*address, &query_result, sizeof(query_result));
|
Memory::WriteBlock(*address, &query_result, sizeof(query_result));
|
||||||
}
|
}
|
||||||
dirty_flags.OnMemoryWrite();
|
dirty_flags.OnMemoryWrite();
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
|
#include "core/core.h"
|
||||||
#include "core/core_timing.h"
|
#include "core/core_timing.h"
|
||||||
#include "core/memory.h"
|
#include "core/memory.h"
|
||||||
#include "video_core/engines/fermi_2d.h"
|
#include "video_core/engines/fermi_2d.h"
|
||||||
|
@ -283,7 +284,7 @@ void GPU::ProcessSemaphoreTriggerMethod() {
|
||||||
block.sequence = regs.semaphore_sequence;
|
block.sequence = regs.semaphore_sequence;
|
||||||
// TODO(Kmather73): Generate a real GPU timestamp and write it here instead of
|
// TODO(Kmather73): Generate a real GPU timestamp and write it here instead of
|
||||||
// CoreTiming
|
// CoreTiming
|
||||||
block.timestamp = Core::Timing::GetTicks();
|
block.timestamp = Core::System::GetInstance().CoreTiming().GetTicks();
|
||||||
Memory::WriteBlock(*address, &block, sizeof(block));
|
Memory::WriteBlock(*address, &block, sizeof(block));
|
||||||
} else {
|
} else {
|
||||||
const auto address =
|
const auto address =
|
||||||
|
|
|
@ -137,7 +137,7 @@ void RendererOpenGL::SwapBuffers(
|
||||||
|
|
||||||
render_window.PollEvents();
|
render_window.PollEvents();
|
||||||
|
|
||||||
system.FrameLimiter().DoFrameLimiting(Core::Timing::GetGlobalTimeUs());
|
system.FrameLimiter().DoFrameLimiting(system.CoreTiming().GetGlobalTimeUs());
|
||||||
system.GetPerfStats().BeginSystemFrame();
|
system.GetPerfStats().BeginSystemFrame();
|
||||||
|
|
||||||
// Restore the rasterizer state
|
// Restore the rasterizer state
|
||||||
|
|
Loading…
Reference in New Issue