JIT optimizations and refactoring (#675)
* CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
This commit is contained in:
committed by
GitHub
parent
1f9fc6ab74
commit
b6f871d2b9
@ -1,34 +1,268 @@
|
||||
#include "timing_event.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/log.h"
|
||||
#include "common/state_wrapper.h"
|
||||
#include "cpu_core.h"
|
||||
#include "system.h"
|
||||
Log_SetChannel(TimingEvents);
|
||||
|
||||
TimingEvent::TimingEvent(System* system, std::string name, TickCount period, TickCount interval,
|
||||
TimingEventCallback callback)
|
||||
namespace TimingEvents {
|
||||
|
||||
static std::vector<TimingEvent*> s_events;
|
||||
static u32 s_global_tick_counter = 0;
|
||||
static u32 s_last_event_run_time = 0;
|
||||
static bool s_running_events = false;
|
||||
static bool s_events_need_sorting = false;
|
||||
|
||||
u32 GetGlobalTickCounter()
|
||||
{
|
||||
return s_global_tick_counter;
|
||||
}
|
||||
|
||||
void Initialize()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
void Reset()
|
||||
{
|
||||
s_global_tick_counter = 0;
|
||||
s_last_event_run_time = 0;
|
||||
}
|
||||
|
||||
void Shutdown()
|
||||
{
|
||||
Assert(s_events.empty());
|
||||
}
|
||||
|
||||
std::unique_ptr<TimingEvent> CreateTimingEvent(std::string name, TickCount period, TickCount interval,
|
||||
TimingEventCallback callback, bool activate)
|
||||
{
|
||||
std::unique_ptr<TimingEvent> event =
|
||||
std::make_unique<TimingEvent>(std::move(name), period, interval, std::move(callback));
|
||||
if (activate)
|
||||
event->Activate();
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
void UpdateCPUDowncount()
|
||||
{
|
||||
if (!CPU::g_state.frame_done)
|
||||
CPU::g_state.downcount = s_events[0]->GetDowncount();
|
||||
}
|
||||
|
||||
static bool CompareEvents(const TimingEvent* lhs, const TimingEvent* rhs)
|
||||
{
|
||||
return lhs->GetDowncount() > rhs->GetDowncount();
|
||||
}
|
||||
|
||||
static void AddActiveEvent(TimingEvent* event)
|
||||
{
|
||||
s_events.push_back(event);
|
||||
if (!s_running_events)
|
||||
{
|
||||
std::push_heap(s_events.begin(), s_events.end(), CompareEvents);
|
||||
UpdateCPUDowncount();
|
||||
}
|
||||
else
|
||||
{
|
||||
s_events_need_sorting = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void RemoveActiveEvent(TimingEvent* event)
|
||||
{
|
||||
auto iter = std::find_if(s_events.begin(), s_events.end(), [event](const auto& it) { return event == it; });
|
||||
if (iter == s_events.end())
|
||||
{
|
||||
Panic("Attempt to remove inactive event");
|
||||
return;
|
||||
}
|
||||
|
||||
s_events.erase(iter);
|
||||
if (!s_running_events)
|
||||
{
|
||||
std::make_heap(s_events.begin(), s_events.end(), CompareEvents);
|
||||
if (!s_events.empty())
|
||||
UpdateCPUDowncount();
|
||||
}
|
||||
else
|
||||
{
|
||||
s_events_need_sorting = true;
|
||||
}
|
||||
}
|
||||
|
||||
static TimingEvent* FindActiveEvent(const char* name)
|
||||
{
|
||||
auto iter =
|
||||
std::find_if(s_events.begin(), s_events.end(), [&name](auto& ev) { return ev->GetName().compare(name) == 0; });
|
||||
|
||||
return (iter != s_events.end()) ? *iter : nullptr;
|
||||
}
|
||||
|
||||
static void SortEvents()
|
||||
{
|
||||
if (!s_running_events)
|
||||
{
|
||||
std::make_heap(s_events.begin(), s_events.end(), CompareEvents);
|
||||
UpdateCPUDowncount();
|
||||
}
|
||||
else
|
||||
{
|
||||
s_events_need_sorting = true;
|
||||
}
|
||||
}
|
||||
|
||||
void RunEvents()
|
||||
{
|
||||
DebugAssert(!s_running_events && !s_events.empty());
|
||||
|
||||
s_running_events = true;
|
||||
|
||||
TickCount pending_ticks = (s_global_tick_counter + CPU::GetPendingTicks()) - s_last_event_run_time;
|
||||
CPU::ResetPendingTicks();
|
||||
while (pending_ticks > 0)
|
||||
{
|
||||
const TickCount time = std::min(pending_ticks, s_events[0]->GetDowncount());
|
||||
s_global_tick_counter += static_cast<u32>(time);
|
||||
pending_ticks -= time;
|
||||
|
||||
// Apply downcount to all events.
|
||||
// This will result in a negative downcount for those events which are late.
|
||||
for (TimingEvent* evt : s_events)
|
||||
{
|
||||
evt->m_downcount -= time;
|
||||
evt->m_time_since_last_run += time;
|
||||
}
|
||||
|
||||
// Now we can actually run the callbacks.
|
||||
while (s_events.front()->m_downcount <= 0)
|
||||
{
|
||||
TimingEvent* evt = s_events.front();
|
||||
std::pop_heap(s_events.begin(), s_events.end(), CompareEvents);
|
||||
|
||||
// Factor late time into the time for the next invocation.
|
||||
const TickCount ticks_late = -evt->m_downcount;
|
||||
const TickCount ticks_to_execute = evt->m_time_since_last_run;
|
||||
evt->m_downcount += evt->m_interval;
|
||||
evt->m_time_since_last_run = 0;
|
||||
|
||||
// The cycles_late is only an indicator, it doesn't modify the cycles to execute.
|
||||
evt->m_callback(ticks_to_execute, ticks_late);
|
||||
|
||||
// Place it in the appropriate position in the queue.
|
||||
if (s_events_need_sorting)
|
||||
{
|
||||
// Another event may have been changed by this event, or the interval/downcount changed.
|
||||
std::make_heap(s_events.begin(), s_events.end(), CompareEvents);
|
||||
s_events_need_sorting = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Keep the event list in a heap. The event we just serviced will be in the last place,
|
||||
// so we can use push_here instead of make_heap, which should be faster.
|
||||
std::push_heap(s_events.begin(), s_events.end(), CompareEvents);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s_last_event_run_time = s_global_tick_counter;
|
||||
s_running_events = false;
|
||||
UpdateCPUDowncount();
|
||||
}
|
||||
|
||||
bool DoState(StateWrapper& sw, u32 global_tick_counter)
|
||||
{
|
||||
if (sw.IsReading())
|
||||
{
|
||||
s_global_tick_counter = global_tick_counter;
|
||||
|
||||
// Load timestamps for the clock events.
|
||||
// Any oneshot events should be recreated by the load state method, so we can fix up their times here.
|
||||
u32 event_count = 0;
|
||||
sw.Do(&event_count);
|
||||
|
||||
for (u32 i = 0; i < event_count; i++)
|
||||
{
|
||||
std::string event_name;
|
||||
TickCount downcount, time_since_last_run, period, interval;
|
||||
sw.Do(&event_name);
|
||||
sw.Do(&downcount);
|
||||
sw.Do(&time_since_last_run);
|
||||
sw.Do(&period);
|
||||
sw.Do(&interval);
|
||||
if (sw.HasError())
|
||||
return false;
|
||||
|
||||
TimingEvent* event = FindActiveEvent(event_name.c_str());
|
||||
if (!event)
|
||||
{
|
||||
Log_WarningPrintf("Save state has event '%s', but couldn't find this event when loading.", event_name.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Using reschedule is safe here since we call sort afterwards.
|
||||
event->m_downcount = downcount;
|
||||
event->m_time_since_last_run = time_since_last_run;
|
||||
event->m_period = period;
|
||||
event->m_interval = interval;
|
||||
}
|
||||
|
||||
sw.Do(&s_last_event_run_time);
|
||||
|
||||
Log_DevPrintf("Loaded %u events from save state.", event_count);
|
||||
SortEvents();
|
||||
}
|
||||
else
|
||||
{
|
||||
u32 event_count = static_cast<u32>(s_events.size());
|
||||
sw.Do(&event_count);
|
||||
|
||||
for (TimingEvent* evt : s_events)
|
||||
{
|
||||
sw.Do(&evt->m_name);
|
||||
sw.Do(&evt->m_downcount);
|
||||
sw.Do(&evt->m_time_since_last_run);
|
||||
sw.Do(&evt->m_period);
|
||||
sw.Do(&evt->m_interval);
|
||||
}
|
||||
|
||||
sw.Do(&s_last_event_run_time);
|
||||
|
||||
Log_DevPrintf("Wrote %u events to save state.", event_count);
|
||||
}
|
||||
|
||||
return !sw.HasError();
|
||||
}
|
||||
|
||||
} // namespace TimingEvents
|
||||
|
||||
TimingEvent::TimingEvent(std::string name, TickCount period, TickCount interval, TimingEventCallback callback)
|
||||
: m_downcount(interval), m_time_since_last_run(0), m_period(period), m_interval(interval),
|
||||
m_callback(std::move(callback)), m_system(system), m_name(std::move(name)), m_active(false)
|
||||
m_callback(std::move(callback)), m_name(std::move(name)), m_active(false)
|
||||
{
|
||||
}
|
||||
|
||||
TimingEvent::~TimingEvent()
|
||||
{
|
||||
if (m_active)
|
||||
m_system->RemoveActiveEvent(this);
|
||||
TimingEvents::RemoveActiveEvent(this);
|
||||
}
|
||||
|
||||
TickCount TimingEvent::GetTicksSinceLastExecution() const
|
||||
{
|
||||
return m_system->m_cpu->GetPendingTicks() + m_time_since_last_run;
|
||||
return CPU::GetPendingTicks() + m_time_since_last_run;
|
||||
}
|
||||
|
||||
TickCount TimingEvent::GetTicksUntilNextExecution() const
|
||||
{
|
||||
return std::max(m_downcount - m_system->m_cpu->GetPendingTicks(), static_cast<TickCount>(0));
|
||||
return std::max(m_downcount - CPU::GetPendingTicks(), static_cast<TickCount>(0));
|
||||
}
|
||||
|
||||
void TimingEvent::Schedule(TickCount ticks)
|
||||
{
|
||||
const TickCount pending_ticks = m_system->m_cpu->GetPendingTicks();
|
||||
const TickCount pending_ticks = CPU::GetPendingTicks();
|
||||
m_downcount = pending_ticks + ticks;
|
||||
|
||||
if (!m_active)
|
||||
@ -36,13 +270,13 @@ void TimingEvent::Schedule(TickCount ticks)
|
||||
// Event is going active, so we want it to only execute ticks from the current timestamp.
|
||||
m_time_since_last_run = -pending_ticks;
|
||||
m_active = true;
|
||||
m_system->AddActiveEvent(this);
|
||||
TimingEvents::AddActiveEvent(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Event is already active, so we leave the time since last run alone, and just modify the downcount.
|
||||
// If this is a call from an IO handler for example, re-sort the event queue.
|
||||
m_system->SortEvents();
|
||||
TimingEvents::SortEvents();
|
||||
}
|
||||
}
|
||||
|
||||
@ -66,7 +300,7 @@ void TimingEvent::Reset()
|
||||
|
||||
m_downcount = m_interval;
|
||||
m_time_since_last_run = 0;
|
||||
m_system->SortEvents();
|
||||
TimingEvents::SortEvents();
|
||||
}
|
||||
|
||||
void TimingEvent::InvokeEarly(bool force /* = false */)
|
||||
@ -74,7 +308,7 @@ void TimingEvent::InvokeEarly(bool force /* = false */)
|
||||
if (!m_active)
|
||||
return;
|
||||
|
||||
const TickCount pending_ticks = m_system->m_cpu->GetPendingTicks();
|
||||
const TickCount pending_ticks = CPU::GetPendingTicks();
|
||||
const TickCount ticks_to_execute = m_time_since_last_run + pending_ticks;
|
||||
if (!force && ticks_to_execute < m_period)
|
||||
return;
|
||||
@ -84,7 +318,7 @@ void TimingEvent::InvokeEarly(bool force /* = false */)
|
||||
m_callback(ticks_to_execute, 0);
|
||||
|
||||
// Since we've changed the downcount, we need to re-sort the events.
|
||||
m_system->SortEvents();
|
||||
TimingEvents::SortEvents();
|
||||
}
|
||||
|
||||
void TimingEvent::Activate()
|
||||
@ -93,12 +327,12 @@ void TimingEvent::Activate()
|
||||
return;
|
||||
|
||||
// leave the downcount intact
|
||||
const TickCount pending_ticks = m_system->m_cpu->GetPendingTicks();
|
||||
const TickCount pending_ticks = CPU::GetPendingTicks();
|
||||
m_downcount += pending_ticks;
|
||||
m_time_since_last_run -= pending_ticks;
|
||||
|
||||
m_active = true;
|
||||
m_system->AddActiveEvent(this);
|
||||
TimingEvents::AddActiveEvent(this);
|
||||
}
|
||||
|
||||
void TimingEvent::Deactivate()
|
||||
@ -106,10 +340,10 @@ void TimingEvent::Deactivate()
|
||||
if (!m_active)
|
||||
return;
|
||||
|
||||
const TickCount pending_ticks = m_system->m_cpu->GetPendingTicks();
|
||||
const TickCount pending_ticks = CPU::GetPendingTicks();
|
||||
m_downcount -= pending_ticks;
|
||||
m_time_since_last_run += pending_ticks;
|
||||
|
||||
m_active = false;
|
||||
m_system->RemoveActiveEvent(this);
|
||||
TimingEvents::RemoveActiveEvent(this);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user