From dd48a1f5858c5d4b4a91533c2302d1664a67effb Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Thu, 30 Jul 2020 00:47:17 +1000 Subject: [PATCH] System: Handle large event timing overshoots better Usually a result of DMA cycle stealing. Instead of adding all time, add min(all_events.downcount) at a time. 1.5% performance improvement, but fixes desyncs between the SPU and CD-ROM. --- src/core/system.cpp | 92 +++++++++++++++++++++++---------------------- 1 file changed, 48 insertions(+), 44 deletions(-) diff --git a/src/core/system.cpp b/src/core/system.cpp index 1199f7bdf..e08fc875f 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -1090,52 +1090,56 @@ void System::RunEvents() { DebugAssert(!m_running_events && !m_events.empty()); - const TickCount pending_ticks = m_cpu->GetPendingTicks(); - m_global_tick_counter += static_cast(pending_ticks); - m_cpu->ResetPendingTicks(); - - TickCount time = static_cast(m_global_tick_counter - m_last_event_run_time); m_running_events = true; + + TickCount pending_ticks = (m_global_tick_counter + m_cpu->GetPendingTicks()) - m_last_event_run_time; + m_cpu->ResetPendingTicks(); + while (pending_ticks > 0) + { + const TickCount time = std::min(pending_ticks, m_events[0]->m_downcount); + m_global_tick_counter += static_cast(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 : m_events) + { + evt->m_downcount -= time; + evt->m_time_since_last_run += time; + } + + // Now we can actually run the callbacks. + while (m_events.front()->GetDowncount() <= 0) + { + TimingEvent* evt = m_events.front(); + const TickCount ticks_late = -evt->m_downcount; + std::pop_heap(m_events.begin(), m_events.end(), CompareEvents); + + // Factor late time into the time for the next invocation. + 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 (m_events_need_sorting) + { + // Another event may have been changed by this event, or the interval/downcount changed. + std::make_heap(m_events.begin(), m_events.end(), CompareEvents); + m_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(m_events.begin(), m_events.end(), CompareEvents); + } + } + } + m_last_event_run_time = m_global_tick_counter; - - // Apply downcount to all events. - // This will result in a negative downcount for those events which are late. - for (TimingEvent* evt : m_events) - { - evt->m_downcount -= time; - evt->m_time_since_last_run += time; - } - - // Now we can actually run the callbacks. - while (m_events.front()->GetDowncount() <= 0) - { - TimingEvent* evt = m_events.front(); - const TickCount ticks_late = -evt->m_downcount; - std::pop_heap(m_events.begin(), m_events.end(), CompareEvents); - - // Factor late time into the time for the next invocation. - 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 (m_events_need_sorting) - { - // Another event may have been changed by this event, or the interval/downcount changed. - std::make_heap(m_events.begin(), m_events.end(), CompareEvents); - m_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(m_events.begin(), m_events.end(), CompareEvents); - } - } - m_running_events = false; m_cpu->SetDowncount(m_events.front()->GetDowncount()); }