TimingEvents: Swap to 64-bit global ticks

This commit is contained in:
Stenzek 2024-04-21 14:49:57 +10:00
parent 7d80cabf9f
commit 1546b28f46
No known key found for this signature in database
7 changed files with 110 additions and 122 deletions

View File

@ -316,7 +316,7 @@ static ModeRegister s_mode = {};
static u8 s_interrupt_enable_register = INTERRUPT_REGISTER_MASK;
static u8 s_interrupt_flag_register = 0;
static u8 s_pending_async_interrupt = 0;
static u32 s_last_interrupt_time = 0;
static GlobalTicks s_last_interrupt_time = 0;
static CDImage::Position s_setloc_position = {};
static CDImage::LBA s_requested_lba{};
@ -324,7 +324,7 @@ static CDImage::LBA s_current_lba{}; // this is the hold position
static CDImage::LBA s_seek_start_lba{};
static CDImage::LBA s_seek_end_lba{};
static CDImage::LBA s_physical_lba{}; // current position of the disc with respect to time
static u32 s_physical_lba_update_tick = 0;
static GlobalTicks s_physical_lba_update_tick = 0;
static u32 s_physical_lba_update_carry = 0;
static bool s_setloc_pending = false;
static bool s_read_after_seek = false;
@ -484,7 +484,7 @@ void CDROM::Reset()
s_mode.read_raw_sector = true;
s_interrupt_enable_register = INTERRUPT_REGISTER_MASK;
s_interrupt_flag_register = 0;
s_last_interrupt_time = System::GetGlobalTickCounter() - MINIMUM_INTERRUPT_DELAY;
s_last_interrupt_time = TimingEvents::GetGlobalTickCounter() - MINIMUM_INTERRUPT_DELAY;
ClearAsyncInterrupt();
s_setloc_position = {};
s_seek_start_lba = 0;
@ -605,14 +605,14 @@ bool CDROM::DoState(StateWrapper& sw)
sw.Do(&s_interrupt_enable_register);
sw.Do(&s_interrupt_flag_register);
sw.DoEx(&s_last_interrupt_time, 57, System::GetGlobalTickCounter() - MINIMUM_INTERRUPT_DELAY);
//sw.DoEx(&s_last_interrupt_time, 57, TimingEvents::GetGlobalTickCounter() - MINIMUM_INTERRUPT_DELAY);// TODO: FIXME
sw.Do(&s_pending_async_interrupt);
sw.DoPOD(&s_setloc_position);
sw.Do(&s_current_lba);
sw.Do(&s_seek_start_lba);
sw.Do(&s_seek_end_lba);
sw.DoEx(&s_physical_lba, 49, s_current_lba);
sw.DoEx(&s_physical_lba_update_tick, 49, static_cast<u32>(0));
//sw.DoEx(&s_physical_lba_update_tick, 49, static_cast<u32>(0)); // TODO FIXME
sw.DoEx(&s_physical_lba_update_carry, 54, static_cast<u32>(0));
sw.Do(&s_setloc_pending);
sw.Do(&s_read_after_seek);
@ -1097,7 +1097,7 @@ bool CDROM::HasPendingAsyncInterrupt()
void CDROM::SetInterrupt(Interrupt interrupt)
{
s_interrupt_flag_register = static_cast<u8>(interrupt);
s_last_interrupt_time = System::GetGlobalTickCounter();
s_last_interrupt_time = TimingEvents::GetGlobalTickCounter();
UpdateInterruptRequest();
}
@ -1138,7 +1138,7 @@ void CDROM::QueueDeliverAsyncInterrupt()
return;
// underflows here are okay
const u32 diff = System::GetGlobalTickCounter() - s_last_interrupt_time;
const GlobalTicks diff = TimingEvents::GetGlobalTickCounter() - s_last_interrupt_time;
if (diff >= MINIMUM_INTERRUPT_DELAY)
{
DeliverAsyncInterrupt(nullptr, 0, 0);
@ -2442,13 +2442,13 @@ void CDROM::UpdatePositionWhileSeeking()
s_current_lba = current_lba;
s_physical_lba = current_lba;
s_physical_lba_update_tick = System::GetGlobalTickCounter();
s_physical_lba_update_tick = TimingEvents::GetGlobalTickCounter();
s_physical_lba_update_carry = 0;
}
void CDROM::UpdatePhysicalPosition(bool update_logical)
{
const u32 ticks = System::GetGlobalTickCounter();
const u32 ticks = TimingEvents::GetGlobalTickCounter();
if (IsSeeking() || IsReadingOrPlaying() || !IsMotorOn())
{
// If we're seeking+reading the first sector (no stat bits set), we need to return the set/current lba, not the last
@ -2539,7 +2539,7 @@ void CDROM::SetHoldPosition(CDImage::LBA lba, bool update_subq)
s_current_lba = lba;
s_physical_lba = lba;
s_physical_lba_update_tick = System::GetGlobalTickCounter();
s_physical_lba_update_tick = TimingEvents::GetGlobalTickCounter();
s_physical_lba_update_carry = 0;
}
@ -2607,7 +2607,7 @@ bool CDROM::CompleteSeek()
}
s_physical_lba = s_current_lba;
s_physical_lba_update_tick = System::GetGlobalTickCounter();
s_physical_lba_update_tick = TimingEvents::GetGlobalTickCounter();
s_physical_lba_update_carry = 0;
return seek_okay;
}
@ -2783,7 +2783,7 @@ void CDROM::DoSectorRead()
s_current_lba = s_reader.GetLastReadSector();
s_physical_lba = s_current_lba;
s_physical_lba_update_tick = System::GetGlobalTickCounter();
s_physical_lba_update_tick = TimingEvents::GetGlobalTickCounter();
s_physical_lba_update_carry = 0;
s_secondary_status.SetReadingBits(s_drive_state == DriveState::Playing);

View File

@ -888,7 +888,7 @@ void CPU::CodeCache::LogCurrentState()
"tick=%u dc=%u/%u pc=%08X at=%08X v0=%08X v1=%08X a0=%08X a1=%08X a2=%08X a3=%08X t0=%08X t1=%08X t2=%08X t3=%08X "
"t4=%08X t5=%08X t6=%08X t7=%08X s0=%08X s1=%08X s2=%08X s3=%08X s4=%08X s5=%08X s6=%08X s7=%08X t8=%08X t9=%08X "
"k0=%08X k1=%08X gp=%08X sp=%08X fp=%08X ra=%08X hi=%08X lo=%08X ldr=%s ldv=%08X cause=%08X sr=%08X gte=%08X\n",
System::GetGlobalTickCounter(), g_state.pending_ticks, g_state.downcount, g_state.pc, regs.at, regs.v0, regs.v1,
TimingEvents::GetGlobalTickCounter(), g_state.pending_ticks, g_state.downcount, g_state.pc, regs.at, regs.v0, regs.v1,
regs.a0, regs.a1, regs.a2, regs.a3, regs.t0, regs.t1, regs.t2, regs.t3, regs.t4, regs.t5, regs.t6, regs.t7, regs.s0,
regs.s1, regs.s2, regs.s3, regs.s4, regs.s5, regs.s6, regs.s7, regs.t8, regs.t9, regs.k0, regs.k1, regs.gp, regs.sp,
regs.fp, regs.ra, regs.hi, regs.lo,

View File

@ -213,7 +213,7 @@ static System::FrameTimeHistory s_frame_time_history;
static u32 s_frame_time_history_pos = 0;
static u32 s_last_frame_number = 0;
static u32 s_last_internal_frame_number = 0;
static u32 s_last_global_tick_counter = 0;
static GlobalTicks s_last_global_tick_counter = 0;
static u64 s_last_cpu_time = 0;
static u64 s_last_sw_time = 0;
static u32 s_presents_since_last_update = 0;
@ -403,11 +403,6 @@ void System::UpdateOverclock()
UpdateThrottlePeriod();
}
u32 System::GetGlobalTickCounter()
{
return TimingEvents::GetGlobalTickCounter() + CPU::GetPendingTicks();
}
u32 System::GetFrameNumber()
{
return s_frame_number;
@ -2611,7 +2606,7 @@ void System::UpdatePerformanceCounters()
const u32 frames_run = s_frame_number - s_last_frame_number;
const float frames_runf = static_cast<float>(frames_run);
const u32 global_tick_counter = GetGlobalTickCounter();
const GlobalTicks global_tick_counter = TimingEvents::GetGlobalTickCounter();
// TODO: Make the math here less rubbish
const double pct_divider =
@ -2672,7 +2667,7 @@ void System::ResetPerformanceCounters()
{
s_last_frame_number = s_frame_number;
s_last_internal_frame_number = s_internal_frame_number;
s_last_global_tick_counter = GetGlobalTickCounter();
s_last_global_tick_counter = TimingEvents::GetGlobalTickCounter();
s_last_cpu_time = s_cpu_thread_handle ? s_cpu_thread_handle.GetCPUTime() : 0;
if (const Threading::Thread* sw_thread = g_gpu->GetSWThread(); sw_thread)
s_last_sw_time = sw_thread->GetCPUTime();

View File

@ -195,7 +195,6 @@ void UpdateOverclock();
/// direct execution to this executable.
bool InjectEXEFromBuffer(const void* buffer, u32 buffer_size, bool patch_loader = true);
u32 GetGlobalTickCounter();
u32 GetFrameNumber();
u32 GetInternalFrameNumber();
void IncrementInternalFrameNumber();

View File

@ -16,16 +16,16 @@ static TimingEvent* s_active_events_head;
static TimingEvent* s_active_events_tail;
static TimingEvent* s_current_event = nullptr;
static u32 s_active_event_count = 0;
static u32 s_global_tick_counter = 0;
static u32 s_event_run_tick_counter = 0;
static GlobalTicks s_global_tick_counter = 0;
static GlobalTicks s_event_run_tick_counter = 0;
static bool s_frame_done = false;
u32 GetGlobalTickCounter()
GlobalTicks GetGlobalTickCounter()
{
return s_global_tick_counter;
return s_global_tick_counter + CPU::GetPendingTicks();
}
u32 GetEventRunTickCounter()
GlobalTicks GetEventRunTickCounter()
{
return s_event_run_tick_counter;
}
@ -38,6 +38,7 @@ void Initialize()
void Reset()
{
s_global_tick_counter = 0;
s_event_run_tick_counter = 0;
}
void Shutdown()
@ -58,7 +59,8 @@ std::unique_ptr<TimingEvent> CreateTimingEvent(std::string name, TickCount perio
void UpdateCPUDowncount()
{
const u32 event_downcount = s_active_events_head->GetDowncount();
DebugAssert(s_active_events_head->GetNextRunTime() >= s_global_tick_counter);
const u32 event_downcount = static_cast<u32>(s_active_events_head->GetNextRunTime() - s_global_tick_counter);
CPU::g_state.downcount = CPU::HasPendingInterrupt() ? 0 : event_downcount;
}
@ -69,13 +71,13 @@ TimingEvent** GetHeadEventPtr()
static void SortEvent(TimingEvent* event)
{
const TickCount event_downcount = event->m_downcount;
const GlobalTicks event_runtime = event->GetNextRunTime();
if (event->prev && event->prev->m_downcount > event_downcount)
if (event->prev && event->prev->GetNextRunTime() > event_runtime)
{
// move backwards
TimingEvent* current = event->prev;
while (current && current->m_downcount > event_downcount)
while (current && current->GetNextRunTime() > event_runtime)
current = current->prev;
// unlink
@ -111,11 +113,11 @@ static void SortEvent(TimingEvent* event)
UpdateCPUDowncount();
}
}
else if (event->next && event_downcount > event->next->m_downcount)
else if (event->next && event_runtime > event->next->GetNextRunTime())
{
// move forwards
TimingEvent* current = event->next;
while (current && event_downcount > current->m_downcount)
while (current && event_runtime > current->GetNextRunTime())
current = current->next;
// unlink
@ -168,9 +170,10 @@ static void AddActiveEvent(TimingEvent* event)
DebugAssert(!event->prev && !event->next);
s_active_event_count++;
const GlobalTicks event_runtime = event->GetNextRunTime();
TimingEvent* current = nullptr;
TimingEvent* next = s_active_events_head;
while (next && event->m_downcount > next->m_downcount)
while (next && event_runtime > next->GetNextRunTime())
{
current = next;
next = next->next;
@ -231,7 +234,7 @@ static void RemoveActiveEvent(TimingEvent* event)
else
{
s_active_events_head = event->next;
if (s_active_events_head)
if (s_active_events_head && !s_current_event)
UpdateCPUDowncount();
}
@ -295,45 +298,37 @@ void RunEvents()
if (CPU::HasPendingInterrupt())
CPU::DispatchInterrupt();
TickCount pending_ticks = CPU::GetPendingTicks();
if (pending_ticks >= s_active_events_head->GetDowncount())
// TODO: Get rid of pending completely...
const GlobalTicks new_global_ticks = s_global_tick_counter + static_cast<GlobalTicks>(CPU::GetPendingTicks());
if (new_global_ticks >= s_active_events_head->m_next_run_time)
{
CPU::ResetPendingTicks();
s_event_run_tick_counter = s_global_tick_counter + static_cast<u32>(pending_ticks);
s_event_run_tick_counter = new_global_ticks; // TODO: Might be wrong... should move below?but then it'd ping-pong.
do
{
const TickCount time = std::min(pending_ticks, s_active_events_head->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* event = s_active_events_head; event; event = event->next)
{
event->m_downcount -= time;
event->m_time_since_last_run += time;
}
s_global_tick_counter = std::min(new_global_ticks, s_active_events_head->m_next_run_time);
// Now we can actually run the callbacks.
while (s_active_events_head->m_downcount <= 0)
TimingEvent* event;
while (s_global_tick_counter >= (event = s_active_events_head)->m_next_run_time)
{
// move it to the end, since that'll likely be its new position
TimingEvent* event = s_active_events_head;
s_current_event = event;
// Factor late time into the time for the next invocation.
const TickCount ticks_late = -event->m_downcount;
const TickCount ticks_to_execute = event->m_time_since_last_run;
event->m_downcount += event->m_interval;
event->m_time_since_last_run = 0;
const TickCount ticks_late =
static_cast<TickCount>(s_global_tick_counter - s_active_events_head->m_next_run_time);
const TickCount ticks_to_execute =
static_cast<TickCount>(s_global_tick_counter - s_active_events_head->m_last_run_time);
s_active_events_head->m_next_run_time += static_cast<GlobalTicks>(event->m_interval);
s_active_events_head->m_last_run_time = s_global_tick_counter;
// The cycles_late is only an indicator, it doesn't modify the cycles to execute.
event->m_callback(event->m_callback_param, ticks_to_execute, ticks_late);
if (event->m_active)
SortEvent(event);
}
} while (pending_ticks > 0);
} while (new_global_ticks > s_event_run_tick_counter);
s_current_event = nullptr;
}
@ -379,8 +374,9 @@ bool DoState(StateWrapper& sw)
}
// Using reschedule is safe here since we call sort afterwards.
event->m_downcount = downcount;
event->m_time_since_last_run = time_since_last_run;
Panic("Fixme");
//event->m_downcount = downcount;
//event->m_time_since_last_run = time_since_last_run;
event->m_period = period;
event->m_interval = interval;
}
@ -402,8 +398,8 @@ bool DoState(StateWrapper& sw)
for (TimingEvent* event = s_active_events_head; event; event = event->next)
{
sw.Do(&event->m_name);
sw.Do(&event->m_downcount);
sw.Do(&event->m_time_since_last_run);
//sw.Do(&event->m_downcount);
//sw.Do(&event->m_time_since_last_run);
sw.Do(&event->m_period);
sw.Do(&event->m_interval);
}
@ -418,7 +414,7 @@ bool DoState(StateWrapper& sw)
TimingEvent::TimingEvent(std::string name, TickCount period, TickCount interval, TimingEventCallback callback,
void* callback_param)
: m_callback(callback), m_callback_param(callback_param), m_downcount(interval), m_time_since_last_run(0),
: m_callback(callback), m_callback_param(callback_param), m_next_run_time(TimingEvents::GetGlobalTickCounter() + static_cast<GlobalTicks>(interval)), m_last_run_time(TimingEvents::GetGlobalTickCounter()),
m_period(period), m_interval(interval), m_name(std::move(name))
{
}
@ -429,16 +425,6 @@ TimingEvent::~TimingEvent()
TimingEvents::RemoveActiveEvent(this);
}
TickCount TimingEvent::GetTicksSinceLastExecution() const
{
return CPU::GetPendingTicks() + m_time_since_last_run;
}
TickCount TimingEvent::GetTicksUntilNextExecution() const
{
return std::max(m_downcount - CPU::GetPendingTicks(), static_cast<TickCount>(0));
}
void TimingEvent::Delay(TickCount ticks)
{
if (!m_active)
@ -447,7 +433,7 @@ void TimingEvent::Delay(TickCount ticks)
return;
}
m_downcount += ticks;
m_next_run_time += static_cast<GlobalTicks>(ticks);
DebugAssert(TimingEvents::s_current_event != this);
TimingEvents::SortEvent(this);
@ -457,13 +443,13 @@ void TimingEvent::Delay(TickCount ticks)
void TimingEvent::Schedule(TickCount ticks)
{
const TickCount pending_ticks = CPU::GetPendingTicks();
m_downcount = pending_ticks + ticks;
const GlobalTicks current_ticks = TimingEvents::GetGlobalTickCounter();
m_next_run_time = current_ticks + static_cast<GlobalTicks>(ticks);
if (!m_active)
{
// 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 = current_ticks;
m_active = true;
TimingEvents::AddActiveEvent(this);
}
@ -498,8 +484,9 @@ void TimingEvent::Reset()
if (!m_active)
return;
m_downcount = m_interval;
m_time_since_last_run = 0;
const GlobalTicks current_ticks = TimingEvents::GetGlobalTickCounter();
m_next_run_time = current_ticks + static_cast<GlobalTicks>(m_interval);
m_last_run_time = current_ticks;
if (TimingEvents::s_current_event != this)
{
TimingEvents::SortEvent(this);
@ -513,13 +500,15 @@ void TimingEvent::InvokeEarly(bool force /* = false */)
if (!m_active)
return;
const TickCount pending_ticks = CPU::GetPendingTicks();
const TickCount ticks_to_execute = m_time_since_last_run + pending_ticks;
const GlobalTicks current_ticks = TimingEvents::GetGlobalTickCounter();
DebugAssert(current_ticks >= m_last_run_time);
const TickCount ticks_to_execute = static_cast<TickCount>(current_ticks - m_last_run_time);
if ((!force && ticks_to_execute < m_period) || ticks_to_execute <= 0)
return;
m_downcount = pending_ticks + m_interval;
m_time_since_last_run -= ticks_to_execute;
m_next_run_time = current_ticks + static_cast<GlobalTicks>(m_interval);
m_last_run_time = current_ticks;
m_callback(m_callback_param, ticks_to_execute, 0);
// Since we've changed the downcount, we need to re-sort the events.
@ -534,10 +523,9 @@ void TimingEvent::Activate()
if (m_active)
return;
// leave the downcount intact
const TickCount pending_ticks = CPU::GetPendingTicks();
m_downcount += pending_ticks;
m_time_since_last_run -= pending_ticks;
const GlobalTicks current_ticks = TimingEvents::GetGlobalTickCounter();
m_next_run_time = current_ticks + static_cast<GlobalTicks>(m_interval);
m_last_run_time = current_ticks;
m_active = true;
TimingEvents::AddActiveEvent(this);
@ -548,10 +536,6 @@ void TimingEvent::Deactivate()
if (!m_active)
return;
const TickCount pending_ticks = CPU::GetPendingTicks();
m_downcount -= pending_ticks;
m_time_since_last_run += pending_ticks;
m_active = false;
TimingEvents::RemoveActiveEvent(this);
}

View File

@ -11,9 +11,37 @@
class StateWrapper;
class TimingEvent;
// Event callback type. Second parameter is the number of cycles the event was executed "late".
using TimingEventCallback = void (*)(void* param, TickCount ticks, TickCount ticks_late);
namespace TimingEvents {
GlobalTicks GetGlobalTickCounter();
GlobalTicks GetEventRunTickCounter();
void Initialize();
void Reset();
void Shutdown();
/// Creates a new event.
std::unique_ptr<TimingEvent> CreateTimingEvent(std::string name, TickCount period, TickCount interval,
TimingEventCallback callback, void* callback_param, bool activate);
/// Serialization.
bool DoState(StateWrapper& sw);
bool IsRunningEvents();
void SetFrameDone();
void RunEvents();
void UpdateCPUDowncount();
TimingEvent** GetHeadEventPtr();
} // namespace TimingEvents
class TimingEvent
{
public:
@ -27,11 +55,18 @@ public:
// Returns the number of ticks between each event.
ALWAYS_INLINE TickCount GetPeriod() const { return m_period; }
ALWAYS_INLINE TickCount GetInterval() const { return m_interval; }
ALWAYS_INLINE TickCount GetDowncount() const { return m_downcount; }
ALWAYS_INLINE GlobalTicks GetNextRunTime() const { return m_next_run_time; }
ALWAYS_INLINE GlobalTicks GetLastRunTime() const { return m_last_run_time; }
// Includes pending time.
TickCount GetTicksSinceLastExecution() const;
TickCount GetTicksUntilNextExecution() const;
ALWAYS_INLINE TickCount GetTicksSinceLastExecution() const
{
return static_cast<TickCount>(TimingEvents::GetGlobalTickCounter() - m_last_run_time);
}
ALWAYS_INLINE TickCount GetTicksUntilNextExecution() const
{
return static_cast<TickCount>(m_next_run_time - TimingEvents::GetGlobalTickCounter());
}
// Adds ticks to current execution.
void Delay(TickCount ticks);
@ -69,37 +104,11 @@ public:
TimingEventCallback m_callback;
void* m_callback_param;
TickCount m_downcount;
TickCount m_time_since_last_run;
GlobalTicks m_next_run_time;
GlobalTicks m_last_run_time;
TickCount m_period;
TickCount m_interval;
bool m_active = false;
std::string m_name;
};
namespace TimingEvents {
u32 GetGlobalTickCounter();
u32 GetEventRunTickCounter();
void Initialize();
void Reset();
void Shutdown();
/// Creates a new event.
std::unique_ptr<TimingEvent> CreateTimingEvent(std::string name, TickCount period, TickCount interval,
TimingEventCallback callback, void* callback_param, bool activate);
/// Serialization.
bool DoState(StateWrapper& sw);
bool IsRunningEvents();
void SetFrameDone();
void RunEvents();
void UpdateCPUDowncount();
TimingEvent** GetHeadEventPtr();
} // namespace TimingEvents

View File

@ -20,7 +20,8 @@ enum class MemoryAccessSize : u32
Word
};
using TickCount = s32;
using TickCount = s32; // TODO: Make u32
using GlobalTicks = u64;
enum class ConsoleRegion
{