duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

timing_event.cpp (18142B)


      1 // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
      2 // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
      3 
      4 #include "timing_event.h"
      5 #include "cpu_core.h"
      6 #include "cpu_core_private.h"
      7 #include "system.h"
      8 
      9 #include "util/state_wrapper.h"
     10 
     11 #include "common/assert.h"
     12 #include "common/log.h"
     13 #include "common/thirdparty/SmallVector.h"
     14 
     15 Log_SetChannel(TimingEvents);
     16 
     17 namespace TimingEvents {
     18 
     19 static GlobalTicks GetTimestampForNewEvent();
     20 
     21 static void SortEvent(TimingEvent* event);
     22 static void AddActiveEvent(TimingEvent* event);
     23 static void RemoveActiveEvent(TimingEvent* event);
     24 static void SortEvents();
     25 static TimingEvent* FindActiveEvent(const std::string_view name);
     26 static void CommitGlobalTicks(const GlobalTicks new_global_ticks);
     27 
     28 namespace {
     29 struct TimingEventsState
     30 {
     31   TimingEvent* active_events_head = nullptr;
     32   TimingEvent* active_events_tail = nullptr;
     33   TimingEvent* current_event = nullptr;
     34   u32 active_event_count = 0;
     35   GlobalTicks current_event_next_run_time = 0;
     36   GlobalTicks global_tick_counter = 0;
     37   GlobalTicks event_run_tick_counter = 0;
     38 };
     39 } // namespace
     40 
     41 ALIGN_TO_CACHE_LINE static TimingEventsState s_state;
     42 
     43 } // namespace TimingEvents
     44 
     45 GlobalTicks TimingEvents::GetGlobalTickCounter()
     46 {
     47   return s_state.global_tick_counter;
     48 }
     49 
     50 GlobalTicks TimingEvents::GetTimestampForNewEvent()
     51 {
     52   // we want to schedule relative to the currently-being processed event, but if we haven't run events in a while, it
     53   // needs to include the pending time. so explicitly add the two.
     54   return s_state.global_tick_counter + CPU::GetPendingTicks();
     55 }
     56 
     57 GlobalTicks TimingEvents::GetEventRunTickCounter()
     58 {
     59   return s_state.event_run_tick_counter;
     60 }
     61 
     62 void TimingEvents::Initialize()
     63 {
     64   Reset();
     65 }
     66 
     67 void TimingEvents::Reset()
     68 {
     69   s_state.global_tick_counter = 0;
     70   s_state.event_run_tick_counter = 0;
     71 }
     72 
     73 void TimingEvents::Shutdown()
     74 {
     75   Assert(s_state.active_event_count == 0);
     76 }
     77 
     78 void TimingEvents::UpdateCPUDowncount()
     79 {
     80   DebugAssert(s_state.active_events_head->m_next_run_time >= s_state.global_tick_counter);
     81   const u32 event_downcount =
     82     static_cast<u32>(s_state.active_events_head->m_next_run_time - s_state.global_tick_counter);
     83   CPU::g_state.downcount = CPU::HasPendingInterrupt() ? 0 : event_downcount;
     84 }
     85 
     86 TimingEvent** TimingEvents::GetHeadEventPtr()
     87 {
     88   return &s_state.active_events_head;
     89 }
     90 
     91 void TimingEvents::SortEvent(TimingEvent* event)
     92 {
     93   const GlobalTicks event_runtime = event->m_next_run_time;
     94 
     95   if (event->prev && event->prev->m_next_run_time > event_runtime)
     96   {
     97     // move backwards
     98     TimingEvent* current = event->prev;
     99     while (current && current->m_next_run_time > event_runtime)
    100       current = current->prev;
    101 
    102     // unlink
    103     if (event->prev)
    104       event->prev->next = event->next;
    105     else
    106       s_state.active_events_head = event->next;
    107     if (event->next)
    108       event->next->prev = event->prev;
    109     else
    110       s_state.active_events_tail = event->prev;
    111 
    112     // insert after current
    113     if (current)
    114     {
    115       event->next = current->next;
    116       if (current->next)
    117         current->next->prev = event;
    118       else
    119         s_state.active_events_tail = event;
    120 
    121       event->prev = current;
    122       current->next = event;
    123     }
    124     else
    125     {
    126       // insert at front
    127       DebugAssert(s_state.active_events_head);
    128       s_state.active_events_head->prev = event;
    129       event->prev = nullptr;
    130       event->next = s_state.active_events_head;
    131       s_state.active_events_head = event;
    132       UpdateCPUDowncount();
    133     }
    134   }
    135   else if (event->next && event_runtime > event->next->m_next_run_time)
    136   {
    137     // move forwards
    138     TimingEvent* current = event->next;
    139     while (current && event_runtime > current->m_next_run_time)
    140       current = current->next;
    141 
    142     // unlink
    143     if (event->prev)
    144     {
    145       event->prev->next = event->next;
    146     }
    147     else
    148     {
    149       s_state.active_events_head = event->next;
    150       if (!s_state.current_event)
    151         UpdateCPUDowncount();
    152     }
    153     if (event->next)
    154       event->next->prev = event->prev;
    155     else
    156       s_state.active_events_tail = event->prev;
    157 
    158     // insert before current
    159     if (current)
    160     {
    161       event->next = current;
    162       event->prev = current->prev;
    163 
    164       if (current->prev)
    165       {
    166         current->prev->next = event;
    167       }
    168       else
    169       {
    170         s_state.active_events_head = event;
    171         if (!s_state.current_event)
    172           UpdateCPUDowncount();
    173       }
    174 
    175       current->prev = event;
    176     }
    177     else
    178     {
    179       // insert at back
    180       DebugAssert(s_state.active_events_tail);
    181       s_state.active_events_tail->next = event;
    182       event->next = nullptr;
    183       event->prev = s_state.active_events_tail;
    184       s_state.active_events_tail = event;
    185     }
    186   }
    187 }
    188 
    189 void TimingEvents::AddActiveEvent(TimingEvent* event)
    190 {
    191   DebugAssert(!event->prev && !event->next);
    192   s_state.active_event_count++;
    193 
    194   const GlobalTicks event_runtime = event->m_next_run_time;
    195   TimingEvent* current = nullptr;
    196   TimingEvent* next = s_state.active_events_head;
    197   while (next && event_runtime > next->m_next_run_time)
    198   {
    199     current = next;
    200     next = next->next;
    201   }
    202 
    203   if (!next)
    204   {
    205     // new tail
    206     event->prev = s_state.active_events_tail;
    207     if (s_state.active_events_tail)
    208     {
    209       s_state.active_events_tail->next = event;
    210       s_state.active_events_tail = event;
    211     }
    212     else
    213     {
    214       // first event
    215       s_state.active_events_tail = event;
    216       s_state.active_events_head = event;
    217       UpdateCPUDowncount();
    218     }
    219   }
    220   else if (!current)
    221   {
    222     // new head
    223     event->next = s_state.active_events_head;
    224     s_state.active_events_head->prev = event;
    225     s_state.active_events_head = event;
    226     UpdateCPUDowncount();
    227   }
    228   else
    229   {
    230     // inbetween current < event > next
    231     event->prev = current;
    232     event->next = next;
    233     current->next = event;
    234     next->prev = event;
    235   }
    236 }
    237 
    238 void TimingEvents::RemoveActiveEvent(TimingEvent* event)
    239 {
    240   DebugAssert(s_state.active_event_count > 0);
    241 
    242   if (event->next)
    243   {
    244     event->next->prev = event->prev;
    245   }
    246   else
    247   {
    248     s_state.active_events_tail = event->prev;
    249   }
    250 
    251   if (event->prev)
    252   {
    253     event->prev->next = event->next;
    254   }
    255   else
    256   {
    257     s_state.active_events_head = event->next;
    258     if (s_state.active_events_head && !s_state.current_event)
    259       UpdateCPUDowncount();
    260   }
    261 
    262   event->prev = nullptr;
    263   event->next = nullptr;
    264 
    265   s_state.active_event_count--;
    266 }
    267 
    268 void TimingEvents::SortEvents()
    269 {
    270   llvm::SmallVector<TimingEvent*, 16> events;
    271   events.reserve(s_state.active_event_count);
    272 
    273   TimingEvent* next = s_state.active_events_head;
    274   while (next)
    275   {
    276     TimingEvent* current = next;
    277     events.push_back(current);
    278     next = current->next;
    279     current->prev = nullptr;
    280     current->next = nullptr;
    281   }
    282 
    283   s_state.active_events_head = nullptr;
    284   s_state.active_events_tail = nullptr;
    285   s_state.active_event_count = 0;
    286 
    287   for (TimingEvent* event : events)
    288     AddActiveEvent(event);
    289 }
    290 
    291 static TimingEvent* TimingEvents::FindActiveEvent(const std::string_view name)
    292 {
    293   for (TimingEvent* event = s_state.active_events_head; event; event = event->next)
    294   {
    295     if (event->GetName() == name)
    296       return event;
    297   }
    298 
    299   return nullptr;
    300 }
    301 
    302 bool TimingEvents::IsRunningEvents()
    303 {
    304   return (s_state.current_event != nullptr);
    305 }
    306 
    307 void TimingEvents::CancelRunningEvent()
    308 {
    309   TimingEvent* const event = s_state.current_event;
    310   if (!event)
    311     return;
    312 
    313   // Might need to sort it, since we're bailing out.
    314   if (event->IsActive())
    315   {
    316     event->m_next_run_time = s_state.current_event_next_run_time;
    317     SortEvent(event);
    318   }
    319 
    320   s_state.current_event = nullptr;
    321 }
    322 
    323 ALWAYS_INLINE_RELEASE void TimingEvents::CommitGlobalTicks(const GlobalTicks new_global_ticks)
    324 {
    325   s_state.event_run_tick_counter = new_global_ticks;
    326 
    327   do
    328   {
    329     TimingEvent* event = s_state.active_events_head;
    330     s_state.global_tick_counter = std::min(new_global_ticks, event->m_next_run_time);
    331 
    332     // Now we can actually run the callbacks.
    333     while (s_state.global_tick_counter >= event->m_next_run_time)
    334     {
    335       s_state.current_event = event;
    336 
    337       // Factor late time into the time for the next invocation.
    338       const TickCount ticks_late = static_cast<TickCount>(s_state.global_tick_counter - event->m_next_run_time);
    339       const TickCount ticks_to_execute = static_cast<TickCount>(s_state.global_tick_counter - event->m_last_run_time);
    340 
    341       // Why don't we modify event->m_downcount directly? Because otherwise the event list won't be sorted.
    342       // Adding the interval may cause this event to have a greater downcount than the next, and a new event
    343       // may be inserted at the front, despite having a higher downcount than the next.
    344       s_state.current_event_next_run_time = event->m_next_run_time + static_cast<u32>(event->m_interval);
    345       event->m_last_run_time = s_state.global_tick_counter;
    346 
    347       // The cycles_late is only an indicator, it doesn't modify the cycles to execute.
    348       event->m_callback(event->m_callback_param, ticks_to_execute, ticks_late);
    349       if (event->m_active)
    350       {
    351         event->m_next_run_time = s_state.current_event_next_run_time;
    352         SortEvent(event);
    353       }
    354 
    355       event = s_state.active_events_head;
    356     }
    357   } while (new_global_ticks > s_state.global_tick_counter);
    358   s_state.current_event = nullptr;
    359 }
    360 
    361 void TimingEvents::RunEvents()
    362 {
    363   DebugAssert(!s_state.current_event);
    364   DebugAssert(CPU::GetPendingTicks() >= CPU::g_state.downcount);
    365 
    366   do
    367   {
    368     const GlobalTicks new_global_ticks =
    369       s_state.event_run_tick_counter + static_cast<GlobalTicks>(CPU::GetPendingTicks());
    370     if (new_global_ticks >= s_state.active_events_head->m_next_run_time)
    371     {
    372       CPU::ResetPendingTicks();
    373       CommitGlobalTicks(new_global_ticks);
    374     }
    375 
    376     if (CPU::HasPendingInterrupt())
    377       CPU::DispatchInterrupt();
    378 
    379     UpdateCPUDowncount();
    380   } while (CPU::GetPendingTicks() >= CPU::g_state.downcount);
    381 }
    382 
    383 void TimingEvents::CommitLeftoverTicks()
    384 {
    385 #ifdef _DEBUG
    386   if (s_state.event_run_tick_counter > s_state.global_tick_counter)
    387     DEV_LOG("Late-running {} ticks before execution", s_state.event_run_tick_counter - s_state.global_tick_counter);
    388 #endif
    389 
    390   CommitGlobalTicks(s_state.event_run_tick_counter);
    391 
    392   if (CPU::HasPendingInterrupt())
    393     CPU::DispatchInterrupt();
    394 
    395   UpdateCPUDowncount();
    396 }
    397 
    398 bool TimingEvents::DoState(StateWrapper& sw)
    399 {
    400   if (sw.GetVersion() < 71) [[unlikely]]
    401   {
    402     u32 old_global_tick_counter = 0;
    403     sw.Do(&old_global_tick_counter);
    404     s_state.global_tick_counter = static_cast<GlobalTicks>(old_global_tick_counter);
    405     s_state.event_run_tick_counter = s_state.global_tick_counter;
    406 
    407     // Load timestamps for the clock events.
    408     // Any oneshot events should be recreated by the load state method, so we can fix up their times here.
    409     u32 event_count = 0;
    410     sw.Do(&event_count);
    411 
    412     for (u32 i = 0; i < event_count; i++)
    413     {
    414       TinyString event_name;
    415       TickCount downcount, time_since_last_run, period, interval;
    416       sw.Do(&event_name);
    417       sw.Do(&downcount);
    418       sw.Do(&time_since_last_run);
    419       sw.Do(&period);
    420       sw.Do(&interval);
    421       if (sw.HasError())
    422         return false;
    423 
    424       TimingEvent* event = FindActiveEvent(event_name);
    425       if (!event)
    426       {
    427         WARNING_LOG("Save state has event '{}', but couldn't find this event when loading.", event_name);
    428         continue;
    429       }
    430 
    431       event->m_next_run_time = s_state.global_tick_counter + static_cast<u32>(downcount);
    432       event->m_last_run_time = s_state.global_tick_counter - static_cast<u32>(time_since_last_run);
    433       event->m_period = period;
    434       event->m_interval = interval;
    435     }
    436 
    437     if (sw.GetVersion() < 43) [[unlikely]]
    438     {
    439       u32 last_event_run_time = 0;
    440       sw.Do(&last_event_run_time);
    441     }
    442 
    443     DEBUG_LOG("Loaded {} events from save state.", event_count);
    444     s_state.current_event = nullptr;
    445 
    446     // Add pending ticks to the CPU, this'll happen if we saved state when we weren't paused.
    447     const TickCount pending_ticks =
    448       static_cast<TickCount>(s_state.event_run_tick_counter - s_state.global_tick_counter);
    449     DebugAssert(pending_ticks >= 0);
    450     CPU::AddPendingTicks(pending_ticks);
    451     SortEvents();
    452     UpdateCPUDowncount();
    453   }
    454   else
    455   {
    456     sw.Do(&s_state.global_tick_counter);
    457     sw.Do(&s_state.event_run_tick_counter);
    458 
    459     if (sw.IsReading())
    460     {
    461       // Load timestamps for the clock events.
    462       // Any oneshot events should be recreated by the load state method, so we can fix up their times here.
    463       u32 event_count = 0;
    464       sw.Do(&event_count);
    465 
    466       for (u32 i = 0; i < event_count; i++)
    467       {
    468         TinyString event_name;
    469         GlobalTicks next_run_time, last_run_time;
    470         TickCount period, interval;
    471         sw.Do(&event_name);
    472         sw.Do(&next_run_time);
    473         sw.Do(&last_run_time);
    474         sw.Do(&period);
    475         sw.Do(&interval);
    476         if (sw.HasError())
    477           return false;
    478 
    479         TimingEvent* event = FindActiveEvent(event_name);
    480         if (!event)
    481         {
    482           WARNING_LOG("Save state has event '{}', but couldn't find this event when loading.", event_name);
    483           continue;
    484         }
    485 
    486         event->m_next_run_time = next_run_time;
    487         event->m_last_run_time = last_run_time;
    488         event->m_period = period;
    489         event->m_interval = interval;
    490       }
    491 
    492       DEBUG_LOG("Loaded {} events from save state.", event_count);
    493 
    494       // Even if we're actually running an event, we don't want to set it to a new counter.
    495       s_state.current_event = nullptr;
    496 
    497       SortEvents();
    498       UpdateCPUDowncount();
    499     }
    500     else
    501     {
    502       sw.Do(&s_state.active_event_count);
    503 
    504       for (TimingEvent* event = s_state.active_events_head; event; event = event->next)
    505       {
    506         sw.Do(&event->m_name);
    507         GlobalTicks next_run_time =
    508           (s_state.current_event == event) ? s_state.current_event_next_run_time : event->m_next_run_time;
    509         sw.Do(&next_run_time);
    510         sw.Do(&event->m_last_run_time);
    511         sw.Do(&event->m_period);
    512         sw.Do(&event->m_interval);
    513       }
    514 
    515       DEBUG_LOG("Wrote {} events to save state.", s_state.active_event_count);
    516     }
    517   }
    518 
    519   return !sw.HasError();
    520 }
    521 
    522 TimingEvent::TimingEvent(const std::string_view name, TickCount period, TickCount interval,
    523                          TimingEventCallback callback, void* callback_param)
    524   : m_callback(callback), m_callback_param(callback_param), m_period(period), m_interval(interval), m_name(name)
    525 {
    526   const GlobalTicks ts = TimingEvents::GetTimestampForNewEvent();
    527   m_last_run_time = ts;
    528   m_next_run_time = ts + static_cast<u32>(interval);
    529 }
    530 
    531 TimingEvent::~TimingEvent()
    532 {
    533   DebugAssert(!m_active);
    534 }
    535 
    536 TickCount TimingEvent::GetTicksSinceLastExecution() const
    537 {
    538   // Can be negative if event A->B invoked B early while in the event loop.
    539   const GlobalTicks ts = TimingEvents::GetTimestampForNewEvent();
    540   return (ts >= m_last_run_time) ? static_cast<TickCount>(ts - m_last_run_time) : 0;
    541 }
    542 
    543 TickCount TimingEvent::GetTicksUntilNextExecution() const
    544 {
    545   const GlobalTicks ts = TimingEvents::GetTimestampForNewEvent();
    546   return (ts >= m_next_run_time) ? 0 : static_cast<TickCount>(m_next_run_time - ts);
    547 }
    548 
    549 void TimingEvent::Delay(TickCount ticks)
    550 {
    551   using namespace TimingEvents;
    552 
    553   if (!m_active)
    554   {
    555     Panic("Trying to delay an inactive event");
    556     return;
    557   }
    558 
    559   DebugAssert(TimingEvents::s_state.current_event != this);
    560 
    561   m_next_run_time += static_cast<u32>(ticks);
    562   SortEvent(this);
    563   if (s_state.active_events_head == this)
    564     UpdateCPUDowncount();
    565 }
    566 
    567 void TimingEvent::Schedule(TickCount ticks)
    568 {
    569   using namespace TimingEvents;
    570 
    571   const GlobalTicks ts = GetTimestampForNewEvent();
    572   const GlobalTicks next_run_time = ts + static_cast<u32>(ticks);
    573 
    574   // See note in RunEvents().
    575   s_state.current_event_next_run_time =
    576     (s_state.current_event == this) ? next_run_time : s_state.current_event_next_run_time;
    577 
    578   if (!m_active)
    579   {
    580     // Event is going active, so we want it to only execute ticks from the current timestamp.
    581     m_next_run_time = next_run_time;
    582     m_last_run_time = ts;
    583     m_active = true;
    584     AddActiveEvent(this);
    585   }
    586   else
    587   {
    588     // Event is already active, so we leave the time since last run alone, and just modify the downcount.
    589     // If this is a call from an IO handler for example, re-sort the event queue.
    590     if (s_state.current_event != this)
    591     {
    592       m_next_run_time = next_run_time;
    593       SortEvent(this);
    594       if (s_state.active_events_head == this)
    595         UpdateCPUDowncount();
    596     }
    597   }
    598 }
    599 
    600 void TimingEvent::SetIntervalAndSchedule(TickCount ticks)
    601 {
    602   DebugAssert(ticks > 0);
    603   SetInterval(ticks);
    604   Schedule(ticks);
    605 }
    606 
    607 void TimingEvent::SetPeriodAndSchedule(TickCount ticks)
    608 {
    609   SetPeriod(ticks);
    610   SetInterval(ticks);
    611   Schedule(ticks);
    612 }
    613 
    614 void TimingEvent::InvokeEarly(bool force /* = false */)
    615 {
    616   using namespace TimingEvents;
    617 
    618   if (!m_active)
    619     return;
    620 
    621   // Might happen due to other InvokeEarly()'s mid event loop.
    622   const GlobalTicks ts = GetTimestampForNewEvent();
    623   if (ts <= m_last_run_time)
    624     return;
    625 
    626   // Shouldn't be invoking early when we're the current event running.
    627   // TODO: Make DebugAssert instead.
    628   Assert(s_state.current_event != this);
    629 
    630   const TickCount ticks_to_execute = static_cast<TickCount>(ts - m_last_run_time);
    631   if (!force && ticks_to_execute < m_period)
    632     return;
    633 
    634   m_next_run_time = ts + static_cast<u32>(m_interval);
    635   m_last_run_time = ts;
    636 
    637   // Since we've changed the downcount, we need to re-sort the events.
    638   SortEvent(this);
    639   if (s_state.active_events_head == this)
    640     UpdateCPUDowncount();
    641 
    642   m_callback(m_callback_param, ticks_to_execute, 0);
    643 }
    644 
    645 void TimingEvent::Activate()
    646 {
    647   using namespace TimingEvents;
    648 
    649   if (m_active)
    650     return;
    651 
    652   const GlobalTicks ts = GetTimestampForNewEvent();
    653   const GlobalTicks next_run_time = ts + static_cast<u32>(m_interval);
    654   m_next_run_time = next_run_time;
    655   m_last_run_time = ts;
    656 
    657   s_state.current_event_next_run_time =
    658     (s_state.current_event == this) ? next_run_time : s_state.current_event_next_run_time;
    659 
    660   m_active = true;
    661   AddActiveEvent(this);
    662 }
    663 
    664 void TimingEvent::Deactivate()
    665 {
    666   using namespace TimingEvents;
    667 
    668   if (!m_active)
    669     return;
    670 
    671   m_active = false;
    672   RemoveActiveEvent(this);
    673 }