WIP: Global times instead of downcounts

This commit is contained in:
Connor McLaughlin 2020-10-14 22:45:56 +10:00
parent ec032bfb15
commit 865f48e537
4 changed files with 66 additions and 87 deletions

View File

@ -50,8 +50,8 @@ union CacheControl
struct State struct State
{ {
// ticks the CPU has executed // ticks the CPU has executed
TickCount pending_ticks = 0; u32 pending_ticks = 0;
TickCount downcount = 0; u32 downcount = 0;
Registers regs = {}; Registers regs = {};
Cop0Registers cop0_regs = {}; Cop0Registers cop0_regs = {};
@ -98,9 +98,9 @@ void Execute();
ALWAYS_INLINE Registers& GetRegs() { return g_state.regs; } ALWAYS_INLINE Registers& GetRegs() { return g_state.regs; }
ALWAYS_INLINE TickCount GetPendingTicks() { return g_state.pending_ticks; } ALWAYS_INLINE u32 GetPendingTicks() { return g_state.pending_ticks; }
ALWAYS_INLINE void ResetPendingTicks() { g_state.pending_ticks = 0; } ALWAYS_INLINE void ResetPendingTicks() { g_state.pending_ticks = 0; }
ALWAYS_INLINE void AddPendingTicks(TickCount ticks) { g_state.pending_ticks += ticks; } ALWAYS_INLINE void AddPendingTicks(u32 ticks) { g_state.pending_ticks += ticks; }
// state helpers // state helpers
ALWAYS_INLINE bool InUserMode() { return g_state.cop0_regs.sr.KUc; } ALWAYS_INLINE bool InUserMode() { return g_state.cop0_regs.sr.KUc; }

View File

@ -2,8 +2,8 @@
#include "types.h" #include "types.h"
static constexpr u32 SAVE_STATE_MAGIC = 0x43435544; static constexpr u32 SAVE_STATE_MAGIC = 0x43435544;
static constexpr u32 SAVE_STATE_VERSION = 43; static constexpr u32 SAVE_STATE_VERSION = 44;
static constexpr u32 SAVE_STATE_MINIMUM_VERSION = 42; static constexpr u32 SAVE_STATE_MINIMUM_VERSION = 44;
#pragma pack(push, 4) #pragma pack(push, 4)
struct SAVE_STATE_HEADER struct SAVE_STATE_HEADER

View File

@ -13,11 +13,10 @@ static TimingEvent* s_active_events_tail;
static TimingEvent* s_current_event = nullptr; static TimingEvent* s_current_event = nullptr;
static u32 s_active_event_count = 0; static u32 s_active_event_count = 0;
static u64 s_global_tick_counter = 0; static u64 s_global_tick_counter = 0;
static u64 s_last_event_run_time = 0;
u64 GetGlobalTickCounter() u64 GetGlobalTickCounter()
{ {
return s_global_tick_counter; return s_global_tick_counter + CPU::GetPendingTicks();
} }
void Initialize() void Initialize()
@ -27,8 +26,14 @@ void Initialize()
void Reset() void Reset()
{ {
const u64 old_ts = s_global_tick_counter;
s_global_tick_counter = 0; s_global_tick_counter = 0;
s_last_event_run_time = 0;
for (TimingEvent* event = s_active_events_head; event; event = event->next)
{
event->m_next_run_time = event->m_next_run_time - old_ts + s_global_tick_counter;
event->m_last_run_time = old_ts - event->m_last_run_time + s_global_tick_counter;
}
} }
void Shutdown() void Shutdown()
@ -50,18 +55,22 @@ std::unique_ptr<TimingEvent> CreateTimingEvent(std::string name, TickCount perio
void UpdateCPUDowncount() void UpdateCPUDowncount()
{ {
if (!CPU::g_state.frame_done) if (!CPU::g_state.frame_done)
CPU::g_state.downcount = s_active_events_head->GetDowncount(); {
const u64 gtc = GetGlobalTickCounter();
const u64 next_event_run_time = s_active_events_head->m_next_run_time;
CPU::g_state.downcount = (next_event_run_time > gtc) ? static_cast<u32>(next_event_run_time - gtc) : 0u;
}
} }
static void SortEvent(TimingEvent* event) static void SortEvent(TimingEvent* event)
{ {
const TickCount event_downcount = event->m_downcount; const u64 event_downcount = event->m_next_run_time;
if (event->prev && event->prev->m_downcount > event_downcount) if (event->prev && event->prev->m_next_run_time > event_downcount)
{ {
// move backwards // move backwards
TimingEvent* current = event->prev; TimingEvent* current = event->prev;
while (current && current->m_downcount > event_downcount) while (current && current->m_next_run_time > event_downcount)
current = current->prev; current = current->prev;
// unlink // unlink
@ -97,11 +106,11 @@ static void SortEvent(TimingEvent* event)
UpdateCPUDowncount(); UpdateCPUDowncount();
} }
} }
else if (event->next && event_downcount > event->next->m_downcount) else if (event->next && event_downcount > event->next->m_next_run_time)
{ {
// move forwards // move forwards
TimingEvent* current = event->next; TimingEvent* current = event->next;
while (current && event_downcount > current->m_downcount) while (current && event_downcount > current->m_next_run_time)
current = current->next; current = current->next;
// unlink // unlink
@ -146,7 +155,7 @@ static void AddActiveEvent(TimingEvent* event)
TimingEvent* current = nullptr; TimingEvent* current = nullptr;
TimingEvent* next = s_active_events_head; TimingEvent* next = s_active_events_head;
while (next && event->m_downcount > next->m_downcount) while (next && event->m_next_run_time > next->m_next_run_time)
{ {
current = next; current = next;
next = next->next; next = next->next;
@ -255,35 +264,28 @@ void RunEvents()
{ {
DebugAssert(!s_current_event); DebugAssert(!s_current_event);
TickCount pending_ticks = u32 pending_ticks = CPU::GetPendingTicks();
static_cast<TickCount>((s_global_tick_counter + static_cast<u32>(CPU::GetPendingTicks())) - s_last_event_run_time);
CPU::ResetPendingTicks(); CPU::ResetPendingTicks();
while (pending_ticks > 0) while (pending_ticks > 0)
{ {
const TickCount time = std::min(pending_ticks, s_active_events_head->GetDowncount()); const u32 time =
s_global_tick_counter += static_cast<u32>(time); std::min(pending_ticks, static_cast<u32>(s_active_events_head->m_next_run_time - s_global_tick_counter));
s_global_tick_counter += time;
pending_ticks -= time; pending_ticks -= time;
// Apply downcount to all events.
// This will result in a negative downcount for those events which are late.
for (TimingEvent* event = s_active_events_head; event; event = event->next)
{
event->m_downcount -= time;
event->m_time_since_last_run += time;
}
// Now we can actually run the callbacks. // Now we can actually run the callbacks.
while (s_active_events_head->m_downcount <= 0) const u64 gtc = s_global_tick_counter;
while (gtc >= s_active_events_head->m_next_run_time)
{ {
// move it to the end, since that'll likely be its new position // move it to the end, since that'll likely be its new position
TimingEvent* event = s_active_events_head; TimingEvent* event = s_active_events_head;
s_current_event = event; s_current_event = event;
// Factor late time into the time for the next invocation. // Factor late time into the time for the next invocation.
const TickCount ticks_late = -event->m_downcount; const TickCount ticks_late = static_cast<TickCount>(gtc - event->m_next_run_time);
const TickCount ticks_to_execute = event->m_time_since_last_run; const TickCount ticks_to_execute = static_cast<TickCount>(gtc - event->m_last_run_time);
event->m_downcount += event->m_interval; event->m_next_run_time = gtc + static_cast<u32>(event->m_interval);
event->m_time_since_last_run = 0; event->m_last_run_time = gtc;
// The cycles_late is only an indicator, it doesn't modify the cycles to execute. // The cycles_late is only an indicator, it doesn't modify the cycles to execute.
event->m_callback(ticks_to_execute, ticks_late); event->m_callback(ticks_to_execute, ticks_late);
@ -292,23 +294,13 @@ void RunEvents()
} }
} }
s_last_event_run_time = s_global_tick_counter;
s_current_event = nullptr; s_current_event = nullptr;
UpdateCPUDowncount(); UpdateCPUDowncount();
} }
bool DoState(StateWrapper& sw) bool DoState(StateWrapper& sw)
{ {
if (sw.IsReading() && sw.GetVersion() < 43) sw.Do(&s_global_tick_counter);
{
u32 global_tick_counter32;
sw.Do(&global_tick_counter32);
s_global_tick_counter = ZeroExtend64(global_tick_counter32);
}
else
{
sw.Do(&s_global_tick_counter);
}
if (sw.IsReading()) if (sw.IsReading())
{ {
@ -320,10 +312,11 @@ bool DoState(StateWrapper& sw)
for (u32 i = 0; i < event_count; i++) for (u32 i = 0; i < event_count; i++)
{ {
std::string event_name; std::string event_name;
TickCount downcount, time_since_last_run, period, interval; u64 next_run_time, last_run_time;
TickCount period, interval;
sw.Do(&event_name); sw.Do(&event_name);
sw.Do(&downcount); sw.Do(&next_run_time);
sw.Do(&time_since_last_run); sw.Do(&last_run_time);
sw.Do(&period); sw.Do(&period);
sw.Do(&interval); sw.Do(&interval);
if (sw.HasError()) if (sw.HasError())
@ -337,23 +330,12 @@ bool DoState(StateWrapper& sw)
} }
// Using reschedule is safe here since we call sort afterwards. // Using reschedule is safe here since we call sort afterwards.
event->m_downcount = downcount; event->m_next_run_time = next_run_time;
event->m_time_since_last_run = time_since_last_run; event->m_last_run_time = last_run_time;
event->m_period = period; event->m_period = period;
event->m_interval = interval; event->m_interval = interval;
} }
if (sw.GetVersion() < 43)
{
u32 last_event_run_time32;
sw.Do(&last_event_run_time32);
s_last_event_run_time = ZeroExtend64(last_event_run_time32);
}
else
{
sw.Do(&s_last_event_run_time);
}
Log_DevPrintf("Loaded %u events from save state.", event_count); Log_DevPrintf("Loaded %u events from save state.", event_count);
SortEvents(); SortEvents();
} }
@ -365,14 +347,12 @@ bool DoState(StateWrapper& sw)
for (TimingEvent* event = s_active_events_head; event; event = event->next) for (TimingEvent* event = s_active_events_head; event; event = event->next)
{ {
sw.Do(&event->m_name); sw.Do(&event->m_name);
sw.Do(&event->m_downcount); sw.Do(&event->m_next_run_time);
sw.Do(&event->m_time_since_last_run); sw.Do(&event->m_last_run_time);
sw.Do(&event->m_period); sw.Do(&event->m_period);
sw.Do(&event->m_interval); sw.Do(&event->m_interval);
} }
sw.Do(&s_last_event_run_time);
Log_DevPrintf("Wrote %u events to save state.", s_active_event_count); Log_DevPrintf("Wrote %u events to save state.", s_active_event_count);
} }
@ -382,9 +362,11 @@ bool DoState(StateWrapper& sw)
} // namespace TimingEvents } // namespace TimingEvents
TimingEvent::TimingEvent(std::string name, TickCount period, TickCount interval, TimingEventCallback callback) 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_period(period), m_interval(interval), m_callback(std::move(callback)), m_name(std::move(name)), m_active(false)
m_callback(std::move(callback)), m_name(std::move(name)), m_active(false)
{ {
const u64 gtc = TimingEvents::GetGlobalTickCounter();
m_next_run_time = gtc + static_cast<u32>(interval);
m_last_run_time = gtc;
} }
TimingEvent::~TimingEvent() TimingEvent::~TimingEvent()
@ -395,23 +377,24 @@ TimingEvent::~TimingEvent()
TickCount TimingEvent::GetTicksSinceLastExecution() const TickCount TimingEvent::GetTicksSinceLastExecution() const
{ {
return CPU::GetPendingTicks() + m_time_since_last_run; return static_cast<TickCount>(TimingEvents::GetGlobalTickCounter() - m_last_run_time);
} }
TickCount TimingEvent::GetTicksUntilNextExecution() const TickCount TimingEvent::GetTicksUntilNextExecution() const
{ {
return std::max(m_downcount - CPU::GetPendingTicks(), static_cast<TickCount>(0)); const u64 gtc = TimingEvents::GetGlobalTickCounter();
return (gtc >= m_next_run_time) ? 0 : static_cast<TickCount>(m_next_run_time - gtc);
} }
void TimingEvent::Schedule(TickCount ticks) void TimingEvent::Schedule(TickCount ticks)
{ {
const TickCount pending_ticks = CPU::GetPendingTicks(); const u64 gtc = TimingEvents::GetGlobalTickCounter();
m_downcount = pending_ticks + ticks; m_next_run_time = gtc + static_cast<u32>(ticks);
if (!m_active) if (!m_active)
{ {
// Event is going active, so we want it to only execute ticks from the current timestamp. // Event is going active, so we want it to only execute ticks from the current timestamp.
m_time_since_last_run = -pending_ticks; m_last_run_time = gtc;
m_active = true; m_active = true;
TimingEvents::AddActiveEvent(this); TimingEvents::AddActiveEvent(this);
} }
@ -442,8 +425,9 @@ void TimingEvent::Reset()
if (!m_active) if (!m_active)
return; return;
m_downcount = m_interval; const u64 gtc = TimingEvents::GetGlobalTickCounter();
m_time_since_last_run = 0; m_next_run_time = gtc + static_cast<u32>(m_interval);
m_last_run_time = 0;
if (TimingEvents::s_current_event != this) if (TimingEvents::s_current_event != this)
TimingEvents::SortEvent(this); TimingEvents::SortEvent(this);
} }
@ -453,13 +437,13 @@ void TimingEvent::InvokeEarly(bool force /* = false */)
if (!m_active) if (!m_active)
return; return;
const TickCount pending_ticks = CPU::GetPendingTicks(); const u64 gtc = TimingEvents::GetGlobalTickCounter();
const TickCount ticks_to_execute = m_time_since_last_run + pending_ticks; const TickCount ticks_to_execute = (gtc >= m_last_run_time) ? static_cast<TickCount>(gtc - m_last_run_time) : 0;
if (!force && ticks_to_execute < m_period) if (!force && ticks_to_execute < m_period)
return; return;
m_downcount = pending_ticks + m_interval; m_next_run_time = gtc + static_cast<u32>(m_interval);
m_time_since_last_run -= ticks_to_execute; m_last_run_time = gtc;
m_callback(ticks_to_execute, 0); m_callback(ticks_to_execute, 0);
// Since we've changed the downcount, we need to re-sort the events. // Since we've changed the downcount, we need to re-sort the events.
@ -473,9 +457,9 @@ void TimingEvent::Activate()
return; return;
// leave the downcount intact // leave the downcount intact
const TickCount pending_ticks = CPU::GetPendingTicks(); const u64 gtc = TimingEvents::GetGlobalTickCounter();
m_downcount += pending_ticks; m_next_run_time = gtc + static_cast<u32>(m_interval);
m_time_since_last_run -= pending_ticks; m_last_run_time = gtc;
m_active = true; m_active = true;
TimingEvents::AddActiveEvent(this); TimingEvents::AddActiveEvent(this);
@ -486,10 +470,6 @@ void TimingEvent::Deactivate()
if (!m_active) if (!m_active)
return; return;
const TickCount pending_ticks = CPU::GetPendingTicks();
m_downcount -= pending_ticks;
m_time_since_last_run += pending_ticks;
m_active = false; m_active = false;
TimingEvents::RemoveActiveEvent(this); TimingEvents::RemoveActiveEvent(this);
} }

View File

@ -23,7 +23,6 @@ public:
// Returns the number of ticks between each event. // Returns the number of ticks between each event.
ALWAYS_INLINE TickCount GetPeriod() const { return m_period; } ALWAYS_INLINE TickCount GetPeriod() const { return m_period; }
ALWAYS_INLINE TickCount GetInterval() const { return m_interval; } ALWAYS_INLINE TickCount GetInterval() const { return m_interval; }
ALWAYS_INLINE TickCount GetDowncount() const { return m_downcount; }
// Includes pending time. // Includes pending time.
TickCount GetTicksSinceLastExecution() const; TickCount GetTicksSinceLastExecution() const;
@ -59,8 +58,8 @@ public:
TimingEvent* prev = nullptr; TimingEvent* prev = nullptr;
TimingEvent* next = nullptr; TimingEvent* next = nullptr;
TickCount m_downcount; u64 m_next_run_time;
TickCount m_time_since_last_run; u64 m_last_run_time;
TickCount m_period; TickCount m_period;
TickCount m_interval; TickCount m_interval;