TimingEvents: Remove pointer indirection

Probably should move this to one big array for locality.
This commit is contained in:
Stenzek 2024-07-19 22:56:37 +10:00
parent 56dd9878e1
commit 4f7ddfaae6
No known key found for this signature in database
16 changed files with 210 additions and 224 deletions

View File

@ -299,10 +299,12 @@ static void CreateFileMap();
static void CreateFileMap(IsoReader& iso, std::string_view dir); static void CreateFileMap(IsoReader& iso, std::string_view dir);
static const std::string* LookupFileMap(u32 lba, u32* start_lba, u32* end_lba); static const std::string* LookupFileMap(u32 lba, u32* start_lba, u32* end_lba);
static std::unique_ptr<TimingEvent> s_command_event; static TimingEvent s_command_event{"CDROM Command Event", 1, 1, &CDROM::ExecuteCommand, nullptr};
static std::unique_ptr<TimingEvent> s_command_second_response_event; static TimingEvent s_command_second_response_event{"CDROM Command Second Response Event", 1, 1,
static std::unique_ptr<TimingEvent> s_async_interrupt_event; &CDROM::ExecuteCommandSecondResponse, nullptr};
static std::unique_ptr<TimingEvent> s_drive_event; static TimingEvent s_async_interrupt_event{"CDROM Async Interrupt Event", INTERRUPT_DELAY_CYCLES, 1,
&CDROM::DeliverAsyncInterrupt, nullptr};
static TimingEvent s_drive_event{"CDROM Drive Event", 1, 1, &CDROM::ExecuteDrive, nullptr};
static Command s_command = Command::None; static Command s_command = Command::None;
static Command s_command_second_response = Command::None; static Command s_command_second_response = Command::None;
@ -443,14 +445,6 @@ static std::array<CommandInfo, 255> s_command_info = {{
void CDROM::Initialize() void CDROM::Initialize()
{ {
s_command_event =
TimingEvents::CreateTimingEvent("CDROM Command Event", 1, 1, &CDROM::ExecuteCommand, nullptr, false);
s_command_second_response_event = TimingEvents::CreateTimingEvent(
"CDROM Command Second Response Event", 1, 1, &CDROM::ExecuteCommandSecondResponse, nullptr, false);
s_async_interrupt_event = TimingEvents::CreateTimingEvent("CDROM Async Interrupt Event", INTERRUPT_DELAY_CYCLES, 1,
&CDROM::DeliverAsyncInterrupt, nullptr, false);
s_drive_event = TimingEvents::CreateTimingEvent("CDROM Drive Event", 1, 1, &CDROM::ExecuteDrive, nullptr, false);
if (g_settings.cdrom_readahead_sectors > 0) if (g_settings.cdrom_readahead_sectors > 0)
s_reader.StartThread(g_settings.cdrom_readahead_sectors); s_reader.StartThread(g_settings.cdrom_readahead_sectors);
@ -463,10 +457,10 @@ void CDROM::Shutdown()
s_file_map_created = false; s_file_map_created = false;
s_show_current_file = false; s_show_current_file = false;
s_drive_event.reset(); s_drive_event.Deactivate();
s_async_interrupt_event.reset(); s_async_interrupt_event.Deactivate();
s_command_second_response_event.reset(); s_command_second_response_event.Deactivate();
s_command_event.reset(); s_command_event.Deactivate();
s_reader.StopThread(); s_reader.StopThread();
s_reader.RemoveMedia(); s_reader.RemoveMedia();
} }
@ -474,7 +468,7 @@ void CDROM::Shutdown()
void CDROM::Reset() void CDROM::Reset()
{ {
s_command = Command::None; s_command = Command::None;
s_command_event->Deactivate(); s_command_event.Deactivate();
ClearCommandSecondResponse(); ClearCommandSecondResponse();
ClearDriveState(); ClearDriveState();
s_status.bits = 0; s_status.bits = 0;
@ -569,7 +563,7 @@ void CDROM::SoftReset(TickCount ticks_late)
if (s_current_lba != 0) if (s_current_lba != 0)
{ {
s_drive_state = DriveState::SeekingImplicit; s_drive_state = DriveState::SeekingImplicit;
s_drive_event->SetIntervalAndSchedule(total_ticks); s_drive_event.SetIntervalAndSchedule(total_ticks);
s_requested_lba = 0; s_requested_lba = 0;
s_reader.QueueReadSector(s_requested_lba); s_reader.QueueReadSector(s_requested_lba);
s_seek_start_lba = s_current_lba; s_seek_start_lba = s_current_lba;
@ -578,7 +572,7 @@ void CDROM::SoftReset(TickCount ticks_late)
else else
{ {
s_drive_state = DriveState::ChangingSpeedOrTOCRead; s_drive_state = DriveState::ChangingSpeedOrTOCRead;
s_drive_event->Schedule(total_ticks); s_drive_event.Schedule(total_ticks);
} }
} }
} }
@ -697,10 +691,10 @@ bool CDROM::DoState(StateWrapper& sw)
if (s_reader.HasMedia()) if (s_reader.HasMedia())
s_reader.QueueReadSector(s_requested_lba); s_reader.QueueReadSector(s_requested_lba);
UpdateCommandEvent(); UpdateCommandEvent();
s_drive_event->SetState(!IsDriveIdle()); s_drive_event.SetState(!IsDriveIdle());
// Time will get fixed up later. // Time will get fixed up later.
s_command_second_response_event->SetState(s_command_second_response != Command::None); s_command_second_response_event.SetState(s_command_second_response != Command::None);
} }
return !sw.HasError(); return !sw.HasError();
@ -824,7 +818,7 @@ std::unique_ptr<CDImage> CDROM::RemoveMedia(bool for_disc_swap)
ClearDriveState(); ClearDriveState();
ClearCommandSecondResponse(); ClearCommandSecondResponse();
s_command = Command::None; s_command = Command::None;
s_command_event->Deactivate(); s_command_event.Deactivate();
// The console sends an interrupt when the shell is opened regardless of whether a command was executing. // The console sends an interrupt when the shell is opened regardless of whether a command was executing.
ClearAsyncInterrupt(); ClearAsyncInterrupt();
@ -834,7 +828,7 @@ std::unique_ptr<CDImage> CDROM::RemoveMedia(bool for_disc_swap)
if (for_disc_swap) if (for_disc_swap)
{ {
s_drive_state = DriveState::ShellOpening; s_drive_state = DriveState::ShellOpening;
s_drive_event->SetIntervalAndSchedule(stop_ticks); s_drive_event.SetIntervalAndSchedule(stop_ticks);
} }
return image; return image;
@ -890,7 +884,7 @@ void CDROM::CPUClockChanged()
{ {
// reschedule the disc read event // reschedule the disc read event
if (IsReadingOrPlaying()) if (IsReadingOrPlaying())
s_drive_event->SetInterval(GetTicksForRead()); s_drive_event.SetInterval(GetTicksForRead());
} }
u8 CDROM::ReadRegister(u32 offset) u8 CDROM::ReadRegister(u32 offset)
@ -1115,7 +1109,7 @@ void CDROM::WriteRegister(u32 offset, u8 value)
sizeof(s_cd_audio_volume_matrix)) != 0)) sizeof(s_cd_audio_volume_matrix)) != 0))
{ {
if (HasPendingDiscEvent()) if (HasPendingDiscEvent())
s_drive_event->InvokeEarly(); s_drive_event.InvokeEarly();
SPU::GeneratePendingSamples(); SPU::GeneratePendingSamples();
} }
@ -1210,7 +1204,7 @@ void CDROM::SetAsyncInterrupt(Interrupt interrupt)
void CDROM::ClearAsyncInterrupt() void CDROM::ClearAsyncInterrupt()
{ {
s_pending_async_interrupt = 0; s_pending_async_interrupt = 0;
s_async_interrupt_event->Deactivate(); s_async_interrupt_event.Deactivate();
s_async_response_fifo.Clear(); s_async_response_fifo.Clear();
} }
@ -1235,7 +1229,7 @@ void CDROM::QueueDeliverAsyncInterrupt()
{ {
DEV_LOG("Delaying async interrupt {} because it's been {} cycles since last interrupt", s_pending_async_interrupt, DEV_LOG("Delaying async interrupt {} because it's been {} cycles since last interrupt", s_pending_async_interrupt,
diff); diff);
s_async_interrupt_event->Schedule(INTERRUPT_DELAY_CYCLES); s_async_interrupt_event.Schedule(INTERRUPT_DELAY_CYCLES);
} }
} }
@ -1244,12 +1238,12 @@ void CDROM::DeliverAsyncInterrupt(void*, TickCount ticks, TickCount ticks_late)
if (HasPendingInterrupt()) if (HasPendingInterrupt())
{ {
// This shouldn't really happen, because we should block command execution.. but just in case. // This shouldn't really happen, because we should block command execution.. but just in case.
if (!s_async_interrupt_event->IsActive()) if (!s_async_interrupt_event.IsActive())
s_async_interrupt_event->Schedule(INTERRUPT_DELAY_CYCLES); s_async_interrupt_event.Schedule(INTERRUPT_DELAY_CYCLES);
} }
else else
{ {
s_async_interrupt_event->Deactivate(); s_async_interrupt_event.Deactivate();
Assert(s_pending_async_interrupt != 0 && !HasPendingInterrupt()); Assert(s_pending_async_interrupt != 0 && !HasPendingInterrupt());
DEBUG_LOG("Delivering async interrupt {}", s_pending_async_interrupt); DEBUG_LOG("Delivering async interrupt {}", s_pending_async_interrupt);
@ -1308,7 +1302,7 @@ void CDROM::UpdateInterruptRequest()
bool CDROM::HasPendingDiscEvent() bool CDROM::HasPendingDiscEvent()
{ {
return (s_drive_event->IsActive() && s_drive_event->GetTicksUntilNextExecution() <= 0); return (s_drive_event.IsActive() && s_drive_event.GetTicksUntilNextExecution() <= 0);
} }
TickCount CDROM::GetAckDelayForCommand(Command command) TickCount CDROM::GetAckDelayForCommand(Command command)
@ -1336,7 +1330,7 @@ TickCount CDROM::GetTicksForIDRead()
{ {
TickCount ticks = ID_READ_TICKS; TickCount ticks = ID_READ_TICKS;
if (s_drive_state == DriveState::SpinningUp) if (s_drive_state == DriveState::SpinningUp)
ticks += s_drive_event->GetTicksUntilNextExecution(); ticks += s_drive_event.GetTicksUntilNextExecution();
return ticks; return ticks;
} }
@ -1376,7 +1370,7 @@ TickCount CDROM::GetTicksForSeek(CDImage::LBA new_lba, bool ignore_speed_change)
if (!IsMotorOn()) if (!IsMotorOn())
{ {
ticks += ticks +=
(s_drive_state == DriveState::SpinningUp) ? s_drive_event->GetTicksUntilNextExecution() : GetTicksForSpinUp(); (s_drive_state == DriveState::SpinningUp) ? s_drive_event.GetTicksUntilNextExecution() : GetTicksForSpinUp();
if (s_drive_state == DriveState::ShellOpening || s_drive_state == DriveState::SpinningUp) if (s_drive_state == DriveState::ShellOpening || s_drive_state == DriveState::SpinningUp)
ClearDriveState(); ClearDriveState();
} }
@ -1416,7 +1410,7 @@ TickCount CDROM::GetTicksForSeek(CDImage::LBA new_lba, bool ignore_speed_change)
if (s_drive_state == DriveState::ChangingSpeedOrTOCRead && !ignore_speed_change) if (s_drive_state == DriveState::ChangingSpeedOrTOCRead && !ignore_speed_change)
{ {
// we're still reading the TOC, so add that time in // we're still reading the TOC, so add that time in
const TickCount remaining_change_ticks = s_drive_event->GetTicksUntilNextExecution(); const TickCount remaining_change_ticks = s_drive_event.GetTicksUntilNextExecution();
ticks += remaining_change_ticks; ticks += remaining_change_ticks;
DEV_LOG("Seek time for {} LBAs: {} ({:.3f} ms) ({} for speed change/implicit TOC read)", lba_diff, ticks, DEV_LOG("Seek time for {} LBAs: {} ({:.3f} ms) ({} for speed change/implicit TOC read)", lba_diff, ticks,
@ -1492,16 +1486,16 @@ void CDROM::BeginCommand(Command command)
s_command_info[static_cast<u8>(command)].name); s_command_info[static_cast<u8>(command)].name);
// subtract the currently-elapsed ack ticks from the new command // subtract the currently-elapsed ack ticks from the new command
if (s_command_event->IsActive()) if (s_command_event.IsActive())
{ {
const TickCount elapsed_ticks = s_command_event->GetInterval() - s_command_event->GetTicksUntilNextExecution(); const TickCount elapsed_ticks = s_command_event.GetInterval() - s_command_event.GetTicksUntilNextExecution();
ack_delay = std::max(ack_delay - elapsed_ticks, 1); ack_delay = std::max(ack_delay - elapsed_ticks, 1);
s_command_event->Deactivate(); s_command_event.Deactivate();
} }
} }
s_command = command; s_command = command;
s_command_event->SetIntervalAndSchedule(ack_delay); s_command_event.SetIntervalAndSchedule(ack_delay);
UpdateCommandEvent(); UpdateCommandEvent();
UpdateStatusRegister(); UpdateStatusRegister();
} }
@ -1511,7 +1505,7 @@ void CDROM::EndCommand()
s_param_fifo.Clear(); s_param_fifo.Clear();
s_command = Command::None; s_command = Command::None;
s_command_event->Deactivate(); s_command_event.Deactivate();
UpdateStatusRegister(); UpdateStatusRegister();
} }
@ -1633,7 +1627,7 @@ void CDROM::ExecuteCommand(void*, TickCount ticks, TickCount ticks_late)
if (s_drive_state == DriveState::ChangingSpeedOrTOCRead) if (s_drive_state == DriveState::ChangingSpeedOrTOCRead)
{ {
// cancel the speed change if it's less than a quarter complete // cancel the speed change if it's less than a quarter complete
if (s_drive_event->GetTicksUntilNextExecution() >= (GetTicksForSpeedChange() / 4)) if (s_drive_event.GetTicksUntilNextExecution() >= (GetTicksForSpeedChange() / 4))
{ {
DEV_LOG("Cancelling speed change event"); DEV_LOG("Cancelling speed change event");
ClearDriveState(); ClearDriveState();
@ -1648,19 +1642,19 @@ void CDROM::ExecuteCommand(void*, TickCount ticks, TickCount ticks_late)
DEV_LOG("Drive is {}, delaying event by {} ticks for speed change to {}-speed", DEV_LOG("Drive is {}, delaying event by {} ticks for speed change to {}-speed",
s_drive_state_names[static_cast<u8>(s_drive_state)], change_ticks, s_drive_state_names[static_cast<u8>(s_drive_state)], change_ticks,
s_mode.double_speed ? "double" : "single"); s_mode.double_speed ? "double" : "single");
s_drive_event->Delay(change_ticks); s_drive_event.Delay(change_ticks);
if (IsReadingOrPlaying()) if (IsReadingOrPlaying())
{ {
WARNING_LOG("Speed change while reading/playing, reads will be temporarily delayed."); WARNING_LOG("Speed change while reading/playing, reads will be temporarily delayed.");
s_drive_event->SetInterval(GetTicksForRead()); s_drive_event.SetInterval(GetTicksForRead());
} }
} }
else else
{ {
DEV_LOG("Drive is idle, speed change takes {} ticks", change_ticks); DEV_LOG("Drive is idle, speed change takes {} ticks", change_ticks);
s_drive_state = DriveState::ChangingSpeedOrTOCRead; s_drive_state = DriveState::ChangingSpeedOrTOCRead;
s_drive_event->Schedule(change_ticks); s_drive_event.Schedule(change_ticks);
} }
} }
} }
@ -1736,7 +1730,7 @@ void CDROM::ExecuteCommand(void*, TickCount ticks, TickCount ticks_late)
s_async_command_parameter = session; s_async_command_parameter = session;
s_drive_state = DriveState::ChangingSession; s_drive_state = DriveState::ChangingSession;
s_drive_event->Schedule(GetTicksForTOCRead()); s_drive_event.Schedule(GetTicksForTOCRead());
} }
EndCommand(); EndCommand();
@ -1870,7 +1864,7 @@ void CDROM::ExecuteCommand(void*, TickCount ticks, TickCount ticks_late)
{ {
// Stop reading. // Stop reading.
s_drive_state = DriveState::Idle; s_drive_state = DriveState::Idle;
s_drive_event->Deactivate(); s_drive_event.Deactivate();
s_secondary_status.ClearActiveBits(); s_secondary_status.ClearActiveBits();
} }
@ -2119,7 +2113,7 @@ void CDROM::ExecuteCommand(void*, TickCount ticks, TickCount ticks_late)
// According to nocash this doesn't clear the parameter FIFO. // According to nocash this doesn't clear the parameter FIFO.
s_command = Command::None; s_command = Command::None;
s_command_event->Deactivate(); s_command_event.Deactivate();
UpdateStatusRegister(); UpdateStatusRegister();
} }
break; break;
@ -2283,14 +2277,14 @@ void CDROM::ExecuteCommandSecondResponse(void*, TickCount ticks, TickCount ticks
} }
s_command_second_response = Command::None; s_command_second_response = Command::None;
s_command_second_response_event->Deactivate(); s_command_second_response_event.Deactivate();
} }
void CDROM::QueueCommandSecondResponse(Command command, TickCount ticks) void CDROM::QueueCommandSecondResponse(Command command, TickCount ticks)
{ {
ClearCommandSecondResponse(); ClearCommandSecondResponse();
s_command_second_response = command; s_command_second_response = command;
s_command_second_response_event->Schedule(ticks); s_command_second_response_event.Schedule(ticks);
} }
void CDROM::ClearCommandSecondResponse() void CDROM::ClearCommandSecondResponse()
@ -2301,7 +2295,7 @@ void CDROM::ClearCommandSecondResponse()
s_command_info[static_cast<u16>(s_command_second_response)].name); s_command_info[static_cast<u16>(s_command_second_response)].name);
} }
s_command_second_response_event->Deactivate(); s_command_second_response_event.Deactivate();
s_command_second_response = Command::None; s_command_second_response = Command::None;
} }
@ -2311,12 +2305,12 @@ void CDROM::UpdateCommandEvent()
// so deactivate it until the interrupt is acknowledged // so deactivate it until the interrupt is acknowledged
if (!HasPendingCommand() || HasPendingInterrupt() || HasPendingAsyncInterrupt()) if (!HasPendingCommand() || HasPendingInterrupt() || HasPendingAsyncInterrupt())
{ {
s_command_event->Deactivate(); s_command_event.Deactivate();
return; return;
} }
else if (HasPendingCommand()) else if (HasPendingCommand())
{ {
s_command_event->Activate(); s_command_event.Activate();
} }
} }
@ -2395,7 +2389,7 @@ void CDROM::ExecuteDrive(void*, TickCount ticks, TickCount ticks_late)
void CDROM::ClearDriveState() void CDROM::ClearDriveState()
{ {
s_drive_state = DriveState::Idle; s_drive_state = DriveState::Idle;
s_drive_event->Deactivate(); s_drive_event.Deactivate();
} }
void CDROM::BeginReading(TickCount ticks_late /* = 0 */, bool after_seek /* = false */) void CDROM::BeginReading(TickCount ticks_late /* = 0 */, bool after_seek /* = false */)
@ -2411,7 +2405,7 @@ void CDROM::BeginReading(TickCount ticks_late /* = 0 */, bool after_seek /* = fa
if (IsSeeking()) if (IsSeeking())
{ {
DEV_LOG("Read command while seeking, scheduling read after seek {} -> {} finishes in {} ticks", s_seek_start_lba, DEV_LOG("Read command while seeking, scheduling read after seek {} -> {} finishes in {} ticks", s_seek_start_lba,
s_seek_end_lba, s_drive_event->GetTicksUntilNextExecution()); s_seek_end_lba, s_drive_event.GetTicksUntilNextExecution());
// Implicit seeks won't trigger the read, so swap it for a logical. // Implicit seeks won't trigger the read, so swap it for a logical.
if (s_drive_state == DriveState::SeekingImplicit) if (s_drive_state == DriveState::SeekingImplicit)
@ -2433,8 +2427,8 @@ void CDROM::BeginReading(TickCount ticks_late /* = 0 */, bool after_seek /* = fa
ResetAudioDecoder(); ResetAudioDecoder();
s_drive_state = DriveState::Reading; s_drive_state = DriveState::Reading;
s_drive_event->SetInterval(ticks); s_drive_event.SetInterval(ticks);
s_drive_event->Schedule(first_sector_ticks); s_drive_event.Schedule(first_sector_ticks);
s_requested_lba = s_current_lba; s_requested_lba = s_current_lba;
s_reader.QueueReadSector(s_requested_lba); s_reader.QueueReadSector(s_requested_lba);
@ -2476,8 +2470,8 @@ void CDROM::BeginPlaying(u8 track, TickCount ticks_late /* = 0 */, bool after_se
ResetAudioDecoder(); ResetAudioDecoder();
s_drive_state = DriveState::Playing; s_drive_state = DriveState::Playing;
s_drive_event->SetInterval(ticks); s_drive_event.SetInterval(ticks);
s_drive_event->Schedule(first_sector_ticks); s_drive_event.Schedule(first_sector_ticks);
s_requested_lba = s_current_lba; s_requested_lba = s_current_lba;
s_reader.QueueReadSector(s_requested_lba); s_reader.QueueReadSector(s_requested_lba);
@ -2509,7 +2503,7 @@ void CDROM::BeginSeeking(bool logical, bool read_after_seek, bool play_after_see
s_last_sector_header_valid = false; s_last_sector_header_valid = false;
s_drive_state = logical ? DriveState::SeekingLogical : DriveState::SeekingPhysical; s_drive_state = logical ? DriveState::SeekingLogical : DriveState::SeekingPhysical;
s_drive_event->SetIntervalAndSchedule(seek_time); s_drive_event.SetIntervalAndSchedule(seek_time);
s_seek_start_lba = s_current_lba; s_seek_start_lba = s_current_lba;
s_seek_end_lba = seek_lba; s_seek_end_lba = seek_lba;
@ -2521,8 +2515,8 @@ void CDROM::UpdatePositionWhileSeeking()
{ {
DebugAssert(IsSeeking()); DebugAssert(IsSeeking());
const float completed_frac = 1.0f - std::min(static_cast<float>(s_drive_event->GetTicksUntilNextExecution()) / const float completed_frac = 1.0f - std::min(static_cast<float>(s_drive_event.GetTicksUntilNextExecution()) /
static_cast<float>(s_drive_event->GetInterval()), static_cast<float>(s_drive_event.GetInterval()),
1.0f); 1.0f);
CDImage::LBA current_lba; CDImage::LBA current_lba;
@ -2804,7 +2798,7 @@ void CDROM::DoSpinUpComplete()
{ {
DEBUG_LOG("Spinup complete"); DEBUG_LOG("Spinup complete");
s_drive_state = DriveState::Idle; s_drive_state = DriveState::Idle;
s_drive_event->Deactivate(); s_drive_event.Deactivate();
s_secondary_status.ClearActiveBits(); s_secondary_status.ClearActiveBits();
s_secondary_status.motor_on = true; s_secondary_status.motor_on = true;
} }
@ -2813,7 +2807,7 @@ void CDROM::DoSpeedChangeOrImplicitTOCReadComplete()
{ {
DEBUG_LOG("Speed change/implicit TOC read complete"); DEBUG_LOG("Speed change/implicit TOC read complete");
s_drive_state = DriveState::Idle; s_drive_state = DriveState::Idle;
s_drive_event->Deactivate(); s_drive_event.Deactivate();
} }
void CDROM::DoIDRead() void CDROM::DoIDRead()
@ -2878,7 +2872,7 @@ void CDROM::StartMotor()
DEV_LOG("Starting motor"); DEV_LOG("Starting motor");
s_drive_state = DriveState::SpinningUp; s_drive_state = DriveState::SpinningUp;
s_drive_event->Schedule(GetTicksForSpinUp()); s_drive_event.Schedule(GetTicksForSpinUp());
} }
void CDROM::StopMotor() void CDROM::StopMotor()
@ -3387,7 +3381,7 @@ void CDROM::CheckForSectorBufferReadComplete()
DEV_LOG("Sending additional INT1 for missed sector in buffer {}", s_current_write_sector_buffer); DEV_LOG("Sending additional INT1 for missed sector in buffer {}", s_current_write_sector_buffer);
s_async_response_fifo.Push(s_secondary_status.bits); s_async_response_fifo.Push(s_secondary_status.bits);
s_pending_async_interrupt = static_cast<u8>(Interrupt::DataReady); s_pending_async_interrupt = static_cast<u8>(Interrupt::DataReady);
s_async_interrupt_event->Schedule(INTERRUPT_DELAY_CYCLES); s_async_interrupt_event.Schedule(INTERRUPT_DELAY_CYCLES);
} }
} }
@ -3669,7 +3663,7 @@ void CDROM::DrawDebugWindow()
{ {
ImGui::TextColored(active_color, "Command: %s (0x%02X) (%d ticks remaining)", ImGui::TextColored(active_color, "Command: %s (0x%02X) (%d ticks remaining)",
s_command_info[static_cast<u8>(s_command)].name, static_cast<u8>(s_command), s_command_info[static_cast<u8>(s_command)].name, static_cast<u8>(s_command),
s_command_event->IsActive() ? s_command_event->GetTicksUntilNextExecution() : 0); s_command_event.IsActive() ? s_command_event.GetTicksUntilNextExecution() : 0);
} }
else else
{ {
@ -3684,7 +3678,7 @@ void CDROM::DrawDebugWindow()
{ {
ImGui::TextColored(active_color, "Drive: %s (%d ticks remaining)", ImGui::TextColored(active_color, "Drive: %s (%d ticks remaining)",
s_drive_state_names[static_cast<u8>(s_drive_state)], s_drive_state_names[static_cast<u8>(s_drive_state)],
s_drive_event->IsActive() ? s_drive_event->GetTicksUntilNextExecution() : 0); s_drive_event.IsActive() ? s_drive_event.GetTicksUntilNextExecution() : 0);
} }
ImGui::Text("Interrupt Enable Register: 0x%02X", s_interrupt_enable_register); ImGui::Text("Interrupt Enable Register: 0x%02X", s_interrupt_enable_register);

View File

@ -204,7 +204,7 @@ struct DMAState
TickCount halt_ticks = 100; TickCount halt_ticks = 100;
std::vector<u32> transfer_buffer; std::vector<u32> transfer_buffer;
std::unique_ptr<TimingEvent> unhalt_event; TimingEvent unhalt_event{"DMA Transfer Unhalt", 1, 1, &DMA::UnhaltTransfer, nullptr};
TickCount halt_ticks_remaining = 0; TickCount halt_ticks_remaining = 0;
std::array<ChannelState, NUM_CHANNELS> channels; std::array<ChannelState, NUM_CHANNELS> channels;
@ -243,22 +243,20 @@ void DMA::Initialize()
{ {
s_state.max_slice_ticks = g_settings.dma_max_slice_ticks; s_state.max_slice_ticks = g_settings.dma_max_slice_ticks;
s_state.halt_ticks = g_settings.dma_halt_ticks; s_state.halt_ticks = g_settings.dma_halt_ticks;
s_state.unhalt_event.SetInterval(s_state.max_slice_ticks);
s_state.unhalt_event = TimingEvents::CreateTimingEvent("DMA Transfer Unhalt", 1, s_state.max_slice_ticks,
&DMA::UnhaltTransfer, nullptr, false);
Reset(); Reset();
} }
void DMA::Shutdown() void DMA::Shutdown()
{ {
ClearState(); ClearState();
s_state.unhalt_event.reset(); s_state.unhalt_event.Deactivate();
} }
void DMA::Reset() void DMA::Reset()
{ {
ClearState(); ClearState();
s_state.unhalt_event->Deactivate(); s_state.unhalt_event.Deactivate();
} }
void DMA::ClearState() void DMA::ClearState()
@ -297,9 +295,9 @@ bool DMA::DoState(StateWrapper& sw)
if (sw.IsReading()) if (sw.IsReading())
{ {
if (s_state.halt_ticks_remaining > 0) if (s_state.halt_ticks_remaining > 0)
s_state.unhalt_event->SetIntervalAndSchedule(s_state.halt_ticks_remaining); s_state.unhalt_event.SetIntervalAndSchedule(s_state.halt_ticks_remaining);
else else
s_state.unhalt_event->Deactivate(); s_state.unhalt_event.Deactivate();
} }
return !sw.HasError(); return !sw.HasError();
@ -502,7 +500,7 @@ ALWAYS_INLINE_RELEASE bool DMA::CanTransferChannel(Channel channel, bool ignore_
bool DMA::IsTransferHalted() bool DMA::IsTransferHalted()
{ {
return s_state.unhalt_event->IsActive(); return s_state.unhalt_event.IsActive();
} }
void DMA::UpdateIRQ() void DMA::UpdateIRQ()
@ -733,7 +731,7 @@ bool DMA::TransferChannel()
if (cs.request) if (cs.request)
{ {
// we got halted // we got halted
if (!s_state.unhalt_event->IsActive()) if (!s_state.unhalt_event.IsActive())
HaltTransfer(s_state.halt_ticks); HaltTransfer(s_state.halt_ticks);
return false; return false;
@ -757,18 +755,18 @@ void DMA::HaltTransfer(TickCount duration)
{ {
s_state.halt_ticks_remaining += duration; s_state.halt_ticks_remaining += duration;
DEBUG_LOG("Halting DMA for {} ticks", s_state.halt_ticks_remaining); DEBUG_LOG("Halting DMA for {} ticks", s_state.halt_ticks_remaining);
if (s_state.unhalt_event->IsActive()) if (s_state.unhalt_event.IsActive())
return; return;
DebugAssert(!s_state.unhalt_event->IsActive()); DebugAssert(!s_state.unhalt_event.IsActive());
s_state.unhalt_event->SetIntervalAndSchedule(s_state.halt_ticks_remaining); s_state.unhalt_event.SetIntervalAndSchedule(s_state.halt_ticks_remaining);
} }
void DMA::UnhaltTransfer(void*, TickCount ticks, TickCount ticks_late) void DMA::UnhaltTransfer(void*, TickCount ticks, TickCount ticks_late)
{ {
DEBUG_LOG("Resuming DMA after {} ticks, {} ticks late", ticks, -(s_state.halt_ticks_remaining - ticks)); DEBUG_LOG("Resuming DMA after {} ticks, {} ticks late", ticks, -(s_state.halt_ticks_remaining - ticks));
s_state.halt_ticks_remaining -= ticks; s_state.halt_ticks_remaining -= ticks;
s_state.unhalt_event->Deactivate(); s_state.unhalt_event.Deactivate();
// TODO: Use channel priority. But doing it in ascending order is probably good enough. // TODO: Use channel priority. But doing it in ascending order is probably good enough.
// Main thing is that OTC happens after GPU, because otherwise it'll wipe out the LL. // Main thing is that OTC happens after GPU, because otherwise it'll wipe out the LL.

View File

@ -60,12 +60,22 @@ static u32 s_active_gpu_cycles_frames = 0;
static constexpr GPUTexture::Format DISPLAY_INTERNAL_POSTFX_FORMAT = GPUTexture::Format::RGBA8; static constexpr GPUTexture::Format DISPLAY_INTERNAL_POSTFX_FORMAT = GPUTexture::Format::RGBA8;
GPU::GPU() GPU::GPU()
: m_crtc_tick_event(
"GPU CRTC Tick", 1, 1,
[](void* param, TickCount ticks, TickCount ticks_late) { static_cast<GPU*>(param)->CRTCTickEvent(ticks); }, this),
m_command_tick_event(
"GPU Command Tick", 1, 1,
[](void* param, TickCount ticks, TickCount ticks_late) { static_cast<GPU*>(param)->CommandTickEvent(ticks); },
this)
{ {
ResetStatistics(); ResetStatistics();
} }
GPU::~GPU() GPU::~GPU()
{ {
m_command_tick_event.Deactivate();
m_crtc_tick_event.Deactivate();
JoinScreenshotThreads(); JoinScreenshotThreads();
DestroyDeinterlaceTextures(); DestroyDeinterlaceTextures();
g_gpu_device->RecycleTexture(std::move(m_chroma_smoothing_texture)); g_gpu_device->RecycleTexture(std::move(m_chroma_smoothing_texture));
@ -78,14 +88,7 @@ bool GPU::Initialize()
{ {
m_force_progressive_scan = g_settings.gpu_disable_interlacing; m_force_progressive_scan = g_settings.gpu_disable_interlacing;
m_force_ntsc_timings = g_settings.gpu_force_ntsc_timings; m_force_ntsc_timings = g_settings.gpu_force_ntsc_timings;
m_crtc_tick_event = TimingEvents::CreateTimingEvent( m_crtc_tick_event.Activate();
"GPU CRTC Tick", 1, 1,
[](void* param, TickCount ticks, TickCount ticks_late) { static_cast<GPU*>(param)->CRTCTickEvent(ticks); }, this,
true);
m_command_tick_event = TimingEvents::CreateTimingEvent(
"GPU Command Tick", 1, 1,
[](void* param, TickCount ticks, TickCount ticks_late) { static_cast<GPU*>(param)->CommandTickEvent(ticks); }, this,
true);
m_fifo_size = g_settings.gpu_fifo_size; m_fifo_size = g_settings.gpu_fifo_size;
m_max_run_ahead = g_settings.gpu_max_run_ahead; m_max_run_ahead = g_settings.gpu_max_run_ahead;
m_console_is_pal = System::IsPALRegion(); m_console_is_pal = System::IsPALRegion();
@ -187,8 +190,8 @@ void GPU::Reset(bool clear_vram)
m_blitter_state = BlitterState::Idle; m_blitter_state = BlitterState::Idle;
// Force event to reschedule itself. // Force event to reschedule itself.
m_crtc_tick_event->Deactivate(); m_crtc_tick_event.Deactivate();
m_command_tick_event->Deactivate(); m_command_tick_event.Deactivate();
SoftReset(); SoftReset();
UpdateDisplay(); UpdateDisplay();
@ -455,7 +458,7 @@ u32 GPU::ReadRegister(u32 offset)
if (IsCRTCScanlinePending()) if (IsCRTCScanlinePending())
SynchronizeCRTC(); SynchronizeCRTC();
if (IsCommandCompletionPending()) if (IsCommandCompletionPending())
m_command_tick_event->InvokeEarly(); m_command_tick_event.InvokeEarly();
return m_GPUSTAT.bits; return m_GPUSTAT.bits;
} }
@ -547,7 +550,7 @@ void GPU::AddCommandTicks(TickCount ticks)
void GPU::SynchronizeCRTC() void GPU::SynchronizeCRTC()
{ {
m_crtc_tick_event->InvokeEarly(); m_crtc_tick_event.InvokeEarly();
} }
float GPU::ComputeHorizontalFrequency() const float GPU::ComputeHorizontalFrequency() const
@ -833,17 +836,17 @@ void GPU::UpdateCRTCDisplayParameters()
TickCount GPU::GetPendingCRTCTicks() const TickCount GPU::GetPendingCRTCTicks() const
{ {
const TickCount pending_sysclk_ticks = m_crtc_tick_event->GetTicksSinceLastExecution(); const TickCount pending_sysclk_ticks = m_crtc_tick_event.GetTicksSinceLastExecution();
TickCount fractional_ticks = m_crtc_state.fractional_ticks; TickCount fractional_ticks = m_crtc_state.fractional_ticks;
return SystemTicksToCRTCTicks(pending_sysclk_ticks, &fractional_ticks); return SystemTicksToCRTCTicks(pending_sysclk_ticks, &fractional_ticks);
} }
TickCount GPU::GetPendingCommandTicks() const TickCount GPU::GetPendingCommandTicks() const
{ {
if (!m_command_tick_event->IsActive()) if (!m_command_tick_event.IsActive())
return 0; return 0;
return SystemTicksToGPUTicks(m_command_tick_event->GetTicksSinceLastExecution()); return SystemTicksToGPUTicks(m_command_tick_event.GetTicksSinceLastExecution());
} }
void GPU::UpdateCRTCTickEvent() void GPU::UpdateCRTCTickEvent()
@ -900,7 +903,7 @@ void GPU::UpdateCRTCTickEvent()
ticks_until_event = std::min(ticks_until_event, ticks_until_hblank_start_or_end); ticks_until_event = std::min(ticks_until_event, ticks_until_hblank_start_or_end);
} }
m_crtc_tick_event->Schedule(CRTCTicksToSystemTicks(ticks_until_event, m_crtc_state.fractional_ticks)); m_crtc_tick_event.Schedule(CRTCTicksToSystemTicks(ticks_until_event, m_crtc_state.fractional_ticks));
} }
bool GPU::IsCRTCScanlinePending() const bool GPU::IsCRTCScanlinePending() const
@ -1080,11 +1083,11 @@ void GPU::UpdateCommandTickEvent()
if (m_pending_command_ticks <= 0) if (m_pending_command_ticks <= 0)
{ {
m_pending_command_ticks = 0; m_pending_command_ticks = 0;
m_command_tick_event->Deactivate(); m_command_tick_event.Deactivate();
} }
else else
{ {
m_command_tick_event->SetIntervalAndSchedule(GPUTicksToSystemTicks(m_pending_command_ticks)); m_command_tick_event.SetIntervalAndSchedule(GPUTicksToSystemTicks(m_pending_command_ticks));
} }
} }
@ -1209,7 +1212,7 @@ void GPU::WriteGP1(u32 value)
case 0x00: // Reset GPU case 0x00: // Reset GPU
{ {
DEBUG_LOG("GP1 reset GPU"); DEBUG_LOG("GP1 reset GPU");
m_command_tick_event->InvokeEarly(); m_command_tick_event.InvokeEarly();
SynchronizeCRTC(); SynchronizeCRTC();
SoftReset(); SoftReset();
} }
@ -1218,7 +1221,7 @@ void GPU::WriteGP1(u32 value)
case 0x01: // Clear FIFO case 0x01: // Clear FIFO
{ {
DEBUG_LOG("GP1 clear FIFO"); DEBUG_LOG("GP1 clear FIFO");
m_command_tick_event->InvokeEarly(); m_command_tick_event.InvokeEarly();
SynchronizeCRTC(); SynchronizeCRTC();
// flush partial writes // flush partial writes
@ -1232,7 +1235,7 @@ void GPU::WriteGP1(u32 value)
m_blit_buffer.clear(); m_blit_buffer.clear();
m_blit_remaining_words = 0; m_blit_remaining_words = 0;
m_pending_command_ticks = 0; m_pending_command_ticks = 0;
m_command_tick_event->Deactivate(); m_command_tick_event.Deactivate();
UpdateDMARequest(); UpdateDMARequest();
UpdateGPUIdle(); UpdateGPUIdle();
} }
@ -1350,7 +1353,7 @@ void GPU::WriteGP1(u32 value)
{ {
// Have to be careful when setting this because Synchronize() can modify GPUSTAT. // Have to be careful when setting this because Synchronize() can modify GPUSTAT.
static constexpr u32 SET_MASK = UINT32_C(0b00000000011111110100000000000000); static constexpr u32 SET_MASK = UINT32_C(0b00000000011111110100000000000000);
m_command_tick_event->InvokeEarly(); m_command_tick_event.InvokeEarly();
SynchronizeCRTC(); SynchronizeCRTC();
m_GPUSTAT.bits = (m_GPUSTAT.bits & ~SET_MASK) | (new_GPUSTAT.bits & SET_MASK); m_GPUSTAT.bits = (m_GPUSTAT.bits & ~SET_MASK) | (new_GPUSTAT.bits & SET_MASK);
UpdateCRTCConfig(); UpdateCRTCConfig();

View File

@ -5,6 +5,7 @@
#include "gpu_types.h" #include "gpu_types.h"
#include "timers.h" #include "timers.h"
#include "types.h" #include "types.h"
#include "timing_event.h"
#include "util/gpu_texture.h" #include "util/gpu_texture.h"
@ -29,7 +30,6 @@ class GPUTexture;
class GPUPipeline; class GPUPipeline;
struct Settings; struct Settings;
class TimingEvent;
namespace Threading { namespace Threading {
class Thread; class Thread;
@ -370,8 +370,8 @@ protected:
AddCommandTicks(std::max(drawn_width, drawn_height)); AddCommandTicks(std::max(drawn_width, drawn_height));
} }
std::unique_ptr<TimingEvent> m_crtc_tick_event; TimingEvent m_crtc_tick_event;
std::unique_ptr<TimingEvent> m_command_tick_event; TimingEvent m_command_tick_event;
union GPUSTAT union GPUSTAT
{ {

View File

@ -28,16 +28,21 @@ static u32 s_irq_current_line;
#endif #endif
static constexpr std::array<u8, static_cast<size_t>(Justifier::Binding::ButtonCount)> s_button_indices = {{15, 3, 14}}; static constexpr std::array<u8, static_cast<size_t>(Justifier::Binding::ButtonCount)> s_button_indices = {{15, 3, 14}};
static constexpr std::array<const char*, NUM_CONTROLLER_AND_CARD_PORTS> s_event_names = {
{"Justifier IRQ P0", "Justifier IRQ P1", "Justifier IRQ P2", "Justifier IRQ P3", "Justifier IRQ P4",
"Justifier IRQ P5", "Justifier IRQ P6", "Justifier IRQ P7"}};
Justifier::Justifier(u32 index) : Controller(index) Justifier::Justifier(u32 index)
: Controller(index), m_irq_event(
s_event_names[index], 1, 1,
[](void* param, TickCount, TickCount) { static_cast<Justifier*>(param)->IRQEvent(); }, this)
{ {
m_irq_event = TimingEvents::CreateTimingEvent(
"Justifier IRQ", 1, 1, [](void* param, TickCount, TickCount) { static_cast<Justifier*>(param)->IRQEvent(); }, this,
false);
} }
Justifier::~Justifier() Justifier::~Justifier()
{ {
m_irq_event.Deactivate();
if (!m_cursor_path.empty()) if (!m_cursor_path.empty())
{ {
const u32 cursor_index = GetSoftwarePointerIndex(); const u32 cursor_index = GetSoftwarePointerIndex();
@ -245,7 +250,7 @@ void Justifier::UpdatePosition()
void Justifier::UpdateIRQEvent() void Justifier::UpdateIRQEvent()
{ {
// TODO: Avoid deactivate and event sort. // TODO: Avoid deactivate and event sort.
m_irq_event->Deactivate(); m_irq_event.Deactivate();
if (!m_position_valid) if (!m_position_valid)
return; return;
@ -261,7 +266,7 @@ void Justifier::UpdateIRQEvent()
const TickCount ticks_until_pos = g_gpu->GetSystemTicksUntilTicksAndLine(m_irq_tick, target_line); const TickCount ticks_until_pos = g_gpu->GetSystemTicksUntilTicksAndLine(m_irq_tick, target_line);
DEBUG_LOG("Triggering IRQ in {} ticks @ tick {} line {}", ticks_until_pos, m_irq_tick, target_line); DEBUG_LOG("Triggering IRQ in {} ticks @ tick {} line {}", ticks_until_pos, m_irq_tick, target_line);
m_irq_event->Schedule(ticks_until_pos); m_irq_event.Schedule(ticks_until_pos);
} }
void Justifier::IRQEvent() void Justifier::IRQEvent()

View File

@ -2,7 +2,10 @@
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
#pragma once #pragma once
#include "controller.h" #include "controller.h"
#include "timing_event.h"
#include <memory> #include <memory>
#include <optional> #include <optional>
#include <string_view> #include <string_view>
@ -77,8 +80,6 @@ private:
static constexpr u8 DEFAULT_OFFSCREEN_TRIGGER_FRAMES = 5; static constexpr u8 DEFAULT_OFFSCREEN_TRIGGER_FRAMES = 5;
static constexpr u8 DEFAULT_OFFSCREEN_RELEASE_FRAMES = 5; static constexpr u8 DEFAULT_OFFSCREEN_RELEASE_FRAMES = 5;
std::unique_ptr<TimingEvent> m_irq_event;
s8 m_first_line_offset = 0; s8 m_first_line_offset = 0;
s8 m_last_line_offset = 0; s8 m_last_line_offset = 0;
s16 m_tick_offset = 0; s16 m_tick_offset = 0;
@ -98,6 +99,8 @@ private:
TransferState m_transfer_state = TransferState::Idle; TransferState m_transfer_state = TransferState::Idle;
TimingEvent m_irq_event;
bool m_has_relative_binds = false; bool m_has_relative_binds = false;
float m_relative_pos[4] = {}; float m_relative_pos[4] = {};

View File

@ -151,7 +151,7 @@ struct MDECState
u16 current_q_scale = 0; u16 current_q_scale = 0;
alignas(16) std::array<u32, 256> block_rgb{}; alignas(16) std::array<u32, 256> block_rgb{};
std::unique_ptr<TimingEvent> block_copy_out_event; TimingEvent block_copy_out_event{"MDEC Block Copy Out", 1, 1, &MDEC::CopyOutBlock, nullptr};
u32 total_blocks_decoded = 0; u32 total_blocks_decoded = 0;
}; };
@ -162,20 +162,18 @@ ALIGN_TO_CACHE_LINE static MDECState s_state;
void MDEC::Initialize() void MDEC::Initialize()
{ {
s_state.block_copy_out_event =
TimingEvents::CreateTimingEvent("MDEC Block Copy Out", 1, 1, &MDEC::CopyOutBlock, nullptr, false);
s_state.total_blocks_decoded = 0; s_state.total_blocks_decoded = 0;
Reset(); Reset();
} }
void MDEC::Shutdown() void MDEC::Shutdown()
{ {
s_state.block_copy_out_event.reset(); s_state.block_copy_out_event.Deactivate();
} }
void MDEC::Reset() void MDEC::Reset()
{ {
s_state.block_copy_out_event->Deactivate(); s_state.block_copy_out_event.Deactivate();
SoftReset(); SoftReset();
} }
@ -211,7 +209,7 @@ bool MDEC::DoState(StateWrapper& sw)
bool block_copy_out_pending = HasPendingBlockCopyOut(); bool block_copy_out_pending = HasPendingBlockCopyOut();
sw.Do(&block_copy_out_pending); sw.Do(&block_copy_out_pending);
if (sw.IsReading()) if (sw.IsReading())
s_state.block_copy_out_event->SetState(block_copy_out_pending); s_state.block_copy_out_event.SetState(block_copy_out_pending);
return !sw.HasError(); return !sw.HasError();
} }
@ -304,7 +302,7 @@ void MDEC::DMAWrite(const u32* words, u32 word_count)
bool MDEC::HasPendingBlockCopyOut() bool MDEC::HasPendingBlockCopyOut()
{ {
return s_state.block_copy_out_event->IsActive(); return s_state.block_copy_out_event.IsActive();
} }
void MDEC::SoftReset() void MDEC::SoftReset()
@ -319,7 +317,7 @@ void MDEC::SoftReset()
s_state.current_block = 0; s_state.current_block = 0;
s_state.current_coefficient = 64; s_state.current_coefficient = 64;
s_state.current_q_scale = 0; s_state.current_q_scale = 0;
s_state.block_copy_out_event->Deactivate(); s_state.block_copy_out_event.Deactivate();
UpdateStatus(); UpdateStatus();
} }
@ -358,7 +356,7 @@ u32 MDEC::ReadDataRegister()
if (HasPendingBlockCopyOut()) if (HasPendingBlockCopyOut())
{ {
DEV_LOG("MDEC data out FIFO empty on read - stalling CPU"); DEV_LOG("MDEC data out FIFO empty on read - stalling CPU");
CPU::AddPendingTicks(s_state.block_copy_out_event->GetTicksUntilNextExecution()); CPU::AddPendingTicks(s_state.block_copy_out_event.GetTicksUntilNextExecution());
} }
else else
{ {
@ -617,13 +615,13 @@ void MDEC::ScheduleBlockCopyOut(TickCount ticks)
DebugAssert(!HasPendingBlockCopyOut()); DebugAssert(!HasPendingBlockCopyOut());
DEBUG_LOG("Scheduling block copy out in {} ticks", ticks); DEBUG_LOG("Scheduling block copy out in {} ticks", ticks);
s_state.block_copy_out_event->SetIntervalAndSchedule(ticks); s_state.block_copy_out_event.SetIntervalAndSchedule(ticks);
} }
void MDEC::CopyOutBlock(void* param, TickCount ticks, TickCount ticks_late) void MDEC::CopyOutBlock(void* param, TickCount ticks, TickCount ticks_late)
{ {
Assert(s_state.state == State::WritingMacroblock); Assert(s_state.state == State::WritingMacroblock);
s_state.block_copy_out_event->Deactivate(); s_state.block_copy_out_event.Deactivate();
switch (s_state.status.data_output_depth) switch (s_state.status.data_output_depth)
{ {

View File

@ -22,13 +22,12 @@
Log_SetChannel(MemoryCard); Log_SetChannel(MemoryCard);
MemoryCard::MemoryCard() MemoryCard::MemoryCard()
: m_save_event(
"Memory Card Host Flush", GetSaveDelayInTicks(), GetSaveDelayInTicks(),
[](void* param, TickCount ticks, TickCount ticks_late) { static_cast<MemoryCard*>(param)->SaveIfChanged(true); },
this)
{ {
m_FLAG.no_write_yet = true; m_FLAG.no_write_yet = true;
m_save_event = TimingEvents::CreateTimingEvent(
"Memory Card Host Flush", GetSaveDelayInTicks(), GetSaveDelayInTicks(),
[](void* param, TickCount ticks, TickCount ticks_late) { static_cast<MemoryCard*>(param)->SaveIfChanged(true); },
this, false);
} }
MemoryCard::~MemoryCard() MemoryCard::~MemoryCard()
@ -285,7 +284,7 @@ bool MemoryCard::Transfer(const u8 data_in, u8* data_out)
bool MemoryCard::IsOrWasRecentlyWriting() const bool MemoryCard::IsOrWasRecentlyWriting() const
{ {
return (m_state == State::WriteData || m_save_event->IsActive()); return (m_state == State::WriteData || m_save_event.IsActive());
} }
std::unique_ptr<MemoryCard> MemoryCard::Create() std::unique_ptr<MemoryCard> MemoryCard::Create()
@ -324,7 +323,7 @@ bool MemoryCard::LoadFromFile()
bool MemoryCard::SaveIfChanged(bool display_osd_message) bool MemoryCard::SaveIfChanged(bool display_osd_message)
{ {
m_save_event->Deactivate(); m_save_event.Deactivate();
if (!m_changed) if (!m_changed)
return true; return true;
@ -369,9 +368,9 @@ bool MemoryCard::SaveIfChanged(bool display_osd_message)
void MemoryCard::QueueFileSave() void MemoryCard::QueueFileSave()
{ {
// skip if the event is already pending, or we don't have a backing file // skip if the event is already pending, or we don't have a backing file
if (m_save_event->IsActive() || m_filename.empty()) if (m_save_event.IsActive() || m_filename.empty())
return; return;
// save in one second, that should be long enough for everything to finish writing // save in one second, that should be long enough for everything to finish writing
m_save_event->Schedule(GetSaveDelayInTicks()); m_save_event.Schedule(GetSaveDelayInTicks());
} }

View File

@ -1,17 +1,19 @@
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com> // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
#pragma once #pragma once
#include "common/bitfield.h"
#include "controller.h" #include "controller.h"
#include "memory_card_image.h" #include "memory_card_image.h"
#include "timing_event.h"
#include "common/bitfield.h"
#include <array> #include <array>
#include <memory> #include <memory>
#include <string> #include <string>
#include <string_view> #include <string_view>
class TimingEvent;
class MemoryCard final class MemoryCard final
{ {
public: public:
@ -97,8 +99,6 @@ private:
bool SaveIfChanged(bool display_osd_message); bool SaveIfChanged(bool display_osd_message);
void QueueFileSave(); void QueueFileSave();
std::unique_ptr<TimingEvent> m_save_event;
State m_state = State::Idle; State m_state = State::Idle;
FLAG m_FLAG = {}; FLAG m_FLAG = {};
u16 m_address = 0; u16 m_address = 0;
@ -107,7 +107,8 @@ private:
u8 m_last_byte = 0; u8 m_last_byte = 0;
bool m_changed = false; bool m_changed = false;
MemoryCardImage::DataArray m_data{}; TimingEvent m_save_event;
std::string m_filename; std::string m_filename;
MemoryCardImage::DataArray m_data{};
}; };

View File

@ -117,7 +117,7 @@ static std::array<std::unique_ptr<MemoryCard>, NUM_CONTROLLER_AND_CARD_PORTS> s_
static std::array<Multitap, NUM_MULTITAPS> s_multitaps; static std::array<Multitap, NUM_MULTITAPS> s_multitaps;
static std::unique_ptr<TimingEvent> s_transfer_event; static TimingEvent s_transfer_event{"Pad Serial Transfer", 1, 1, &Pad::TransferEvent, nullptr};
static State s_state = State::Idle; static State s_state = State::Idle;
static JOY_CTRL s_JOY_CTRL = {}; static JOY_CTRL s_JOY_CTRL = {};
@ -140,7 +140,6 @@ static std::unique_ptr<MemoryCard> s_dummy_card;
void Pad::Initialize() void Pad::Initialize()
{ {
s_transfer_event = TimingEvents::CreateTimingEvent("Pad Serial Transfer", 1, 1, &Pad::TransferEvent, nullptr, false);
Reset(); Reset();
} }
@ -148,7 +147,7 @@ void Pad::Shutdown()
{ {
s_memory_card_backup.reset(); s_memory_card_backup.reset();
s_transfer_event.reset(); s_transfer_event.Deactivate();
for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++) for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++)
{ {
@ -533,7 +532,7 @@ bool Pad::DoState(StateWrapper& sw, bool is_memory_state)
sw.Do(&s_transmit_buffer_full); sw.Do(&s_transmit_buffer_full);
if (sw.IsReading() && IsTransmitting()) if (sw.IsReading() && IsTransmitting())
s_transfer_event->Activate(); s_transfer_event.Activate();
return !sw.HasError(); return !sw.HasError();
} }
@ -581,7 +580,7 @@ u32 Pad::ReadRegister(u32 offset)
case 0x00: // JOY_DATA case 0x00: // JOY_DATA
{ {
if (IsTransmitting()) if (IsTransmitting())
s_transfer_event->InvokeEarly(); s_transfer_event.InvokeEarly();
const u8 value = s_receive_buffer_full ? s_receive_buffer : 0xFF; const u8 value = s_receive_buffer_full ? s_receive_buffer : 0xFF;
DEBUG_LOG("JOY_DATA (R) -> 0x{:02X}{}", value, s_receive_buffer_full ? "" : "(EMPTY)"); DEBUG_LOG("JOY_DATA (R) -> 0x{:02X}{}", value, s_receive_buffer_full ? "" : "(EMPTY)");
@ -595,7 +594,7 @@ u32 Pad::ReadRegister(u32 offset)
case 0x04: // JOY_STAT case 0x04: // JOY_STAT
{ {
if (IsTransmitting()) if (IsTransmitting())
s_transfer_event->InvokeEarly(); s_transfer_event.InvokeEarly();
const u32 bits = s_JOY_STAT.bits; const u32 bits = s_JOY_STAT.bits;
s_JOY_STAT.ACKINPUT = false; s_JOY_STAT.ACKINPUT = false;
@ -763,7 +762,7 @@ void Pad::BeginTransfer()
// until after (4) and (5) have been completed. // until after (4) and (5) have been completed.
s_state = State::Transmitting; s_state = State::Transmitting;
s_transfer_event->SetPeriodAndSchedule(GetTransferTicks()); s_transfer_event.SetPeriodAndSchedule(GetTransferTicks());
} }
void Pad::DoTransfer(TickCount ticks_late) void Pad::DoTransfer(TickCount ticks_late)
@ -882,7 +881,7 @@ void Pad::DoTransfer(TickCount ticks_late)
const TickCount ack_timer = GetACKTicks(memcard_transfer); const TickCount ack_timer = GetACKTicks(memcard_transfer);
DEBUG_LOG("Delaying ACK for {} ticks", ack_timer); DEBUG_LOG("Delaying ACK for {} ticks", ack_timer);
s_state = State::WaitingForACK; s_state = State::WaitingForACK;
s_transfer_event->SetPeriodAndSchedule(ack_timer); s_transfer_event.SetPeriodAndSchedule(ack_timer);
} }
UpdateJoyStat(); UpdateJoyStat();
@ -912,7 +911,7 @@ void Pad::EndTransfer()
DEBUG_LOG("Ending transfer"); DEBUG_LOG("Ending transfer");
s_state = State::Idle; s_state = State::Idle;
s_transfer_event->Deactivate(); s_transfer_event.Deactivate();
} }
void Pad::ResetDeviceTransferState() void Pad::ResetDeviceTransferState()

View File

@ -355,8 +355,9 @@ static void CreateOutputStream();
namespace { namespace {
struct SPUState struct SPUState
{ {
std::unique_ptr<TimingEvent> s_tick_event; TimingEvent transfer_event{"SPU Transfer", TRANSFER_TICKS_PER_HALFWORD, TRANSFER_TICKS_PER_HALFWORD,
std::unique_ptr<TimingEvent> transfer_event; &SPU::ExecuteTransfer, nullptr};
TimingEvent tick_event{"SPU Sample", SYSCLK_TICKS_PER_SPU_TICK, SYSCLK_TICKS_PER_SPU_TICK, &SPU::Execute, nullptr};
TickCount ticks_carry = 0; TickCount ticks_carry = 0;
TickCount cpu_ticks_per_spu_tick = 0; TickCount cpu_ticks_per_spu_tick = 0;
@ -426,10 +427,8 @@ void SPU::Initialize()
// (X * D) / N / 768 -> (X * D) / (N * 768) // (X * D) / N / 768 -> (X * D) / (N * 768)
s_state.cpu_ticks_per_spu_tick = System::ScaleTicksToOverclock(SYSCLK_TICKS_PER_SPU_TICK); s_state.cpu_ticks_per_spu_tick = System::ScaleTicksToOverclock(SYSCLK_TICKS_PER_SPU_TICK);
s_state.cpu_tick_divider = static_cast<TickCount>(g_settings.cpu_overclock_numerator * SYSCLK_TICKS_PER_SPU_TICK); s_state.cpu_tick_divider = static_cast<TickCount>(g_settings.cpu_overclock_numerator * SYSCLK_TICKS_PER_SPU_TICK);
s_state.s_tick_event = TimingEvents::CreateTimingEvent("SPU Sample", s_state.cpu_ticks_per_spu_tick, s_state.tick_event.SetInterval(s_state.cpu_ticks_per_spu_tick);
s_state.cpu_ticks_per_spu_tick, &SPU::Execute, nullptr, false); s_state.tick_event.SetPeriod(s_state.cpu_ticks_per_spu_tick);
s_state.transfer_event = TimingEvents::CreateTimingEvent(
"SPU Transfer", TRANSFER_TICKS_PER_HALFWORD, TRANSFER_TICKS_PER_HALFWORD, &SPU::ExecuteTransfer, nullptr, false);
s_state.null_audio_stream = AudioStream::CreateNullStream(SAMPLE_RATE, g_settings.audio_stream_parameters.buffer_ms); s_state.null_audio_stream = AudioStream::CreateNullStream(SAMPLE_RATE, g_settings.audio_stream_parameters.buffer_ms);
CreateOutputStream(); CreateOutputStream();
@ -482,8 +481,8 @@ void SPU::CPUClockChanged()
void SPU::Shutdown() void SPU::Shutdown()
{ {
StopDumpingAudio(); StopDumpingAudio();
s_state.s_tick_event.reset(); s_state.tick_event.Deactivate();
s_state.transfer_event.reset(); s_state.transfer_event.Deactivate();
s_state.audio_stream.reset(); s_state.audio_stream.reset();
} }
@ -539,8 +538,8 @@ void SPU::Reset()
v.ignore_loop_address = false; v.ignore_loop_address = false;
} }
s_state.s_tick_event->Deactivate(); s_state.tick_event.Deactivate();
s_state.transfer_event->Deactivate(); s_state.transfer_event.Deactivate();
s_state.transfer_fifo.Clear(); s_state.transfer_fifo.Clear();
s_ram.fill(0); s_ram.fill(0);
UpdateEventInterval(); UpdateEventInterval();
@ -885,7 +884,7 @@ void SPU::WriteRegister(u32 offset, u16 value)
case 0x1F801DA6 - SPU_BASE: case 0x1F801DA6 - SPU_BASE:
{ {
DEBUG_LOG("SPU transfer address register <- 0x{:04X}", value); DEBUG_LOG("SPU transfer address register <- 0x{:04X}", value);
s_state.transfer_event->InvokeEarly(); s_state.transfer_event.InvokeEarly();
s_state.transfer_address_reg = value; s_state.transfer_address_reg = value;
s_state.transfer_address = ZeroExtend32(value) * 8; s_state.transfer_address = ZeroExtend32(value) * 8;
if (IsRAMIRQTriggerable() && CheckRAMIRQ(s_state.transfer_address)) if (IsRAMIRQTriggerable() && CheckRAMIRQ(s_state.transfer_address))
@ -1283,14 +1282,14 @@ void SPU::ExecuteTransfer(void* param, TickCount ticks, TickCount ticks_late)
if (s_state.transfer_fifo.IsFull()) if (s_state.transfer_fifo.IsFull())
{ {
s_state.SPUSTAT.transfer_busy = false; s_state.SPUSTAT.transfer_busy = false;
s_state.transfer_event->Deactivate(); s_state.transfer_event.Deactivate();
return; return;
} }
s_state.SPUSTAT.transfer_busy = true; s_state.SPUSTAT.transfer_busy = true;
const TickCount ticks_until_complete = const TickCount ticks_until_complete =
TickCount(s_state.transfer_fifo.GetSpace() * u32(TRANSFER_TICKS_PER_HALFWORD)) + ((ticks < 0) ? -ticks : 0); TickCount(s_state.transfer_fifo.GetSpace() * u32(TRANSFER_TICKS_PER_HALFWORD)) + ((ticks < 0) ? -ticks : 0);
s_state.transfer_event->Schedule(ticks_until_complete); s_state.transfer_event.Schedule(ticks_until_complete);
} }
else else
{ {
@ -1307,14 +1306,14 @@ void SPU::ExecuteTransfer(void* param, TickCount ticks, TickCount ticks_late)
if (s_state.transfer_fifo.IsEmpty()) if (s_state.transfer_fifo.IsEmpty())
{ {
s_state.SPUSTAT.transfer_busy = false; s_state.SPUSTAT.transfer_busy = false;
s_state.transfer_event->Deactivate(); s_state.transfer_event.Deactivate();
return; return;
} }
s_state.SPUSTAT.transfer_busy = true; s_state.SPUSTAT.transfer_busy = true;
const TickCount ticks_until_complete = const TickCount ticks_until_complete =
TickCount(s_state.transfer_fifo.GetSize() * u32(TRANSFER_TICKS_PER_HALFWORD)) + ((ticks < 0) ? -ticks : 0); TickCount(s_state.transfer_fifo.GetSize() * u32(TRANSFER_TICKS_PER_HALFWORD)) + ((ticks < 0) ? -ticks : 0);
s_state.transfer_event->Schedule(ticks_until_complete); s_state.transfer_event.Schedule(ticks_until_complete);
} }
} }
@ -1343,26 +1342,26 @@ void SPU::UpdateTransferEvent()
const RAMTransferMode mode = s_state.SPUCNT.ram_transfer_mode; const RAMTransferMode mode = s_state.SPUCNT.ram_transfer_mode;
if (mode == RAMTransferMode::Stopped) if (mode == RAMTransferMode::Stopped)
{ {
s_state.transfer_event->Deactivate(); s_state.transfer_event.Deactivate();
} }
else if (mode == RAMTransferMode::DMARead) else if (mode == RAMTransferMode::DMARead)
{ {
// transfer event fills the fifo // transfer event fills the fifo
if (s_state.transfer_fifo.IsFull()) if (s_state.transfer_fifo.IsFull())
s_state.transfer_event->Deactivate(); s_state.transfer_event.Deactivate();
else if (!s_state.transfer_event->IsActive()) else if (!s_state.transfer_event.IsActive())
s_state.transfer_event->Schedule(TickCount(s_state.transfer_fifo.GetSpace() * u32(TRANSFER_TICKS_PER_HALFWORD))); s_state.transfer_event.Schedule(TickCount(s_state.transfer_fifo.GetSpace() * u32(TRANSFER_TICKS_PER_HALFWORD)));
} }
else else
{ {
// transfer event copies from fifo to ram // transfer event copies from fifo to ram
if (s_state.transfer_fifo.IsEmpty()) if (s_state.transfer_fifo.IsEmpty())
s_state.transfer_event->Deactivate(); s_state.transfer_event.Deactivate();
else if (!s_state.transfer_event->IsActive()) else if (!s_state.transfer_event.IsActive())
s_state.transfer_event->Schedule(TickCount(s_state.transfer_fifo.GetSize() * u32(TRANSFER_TICKS_PER_HALFWORD))); s_state.transfer_event.Schedule(TickCount(s_state.transfer_fifo.GetSize() * u32(TRANSFER_TICKS_PER_HALFWORD)));
} }
s_state.SPUSTAT.transfer_busy = s_state.transfer_event->IsActive(); s_state.SPUSTAT.transfer_busy = s_state.transfer_event.IsActive();
} }
void SPU::UpdateDMARequest() void SPU::UpdateDMARequest()
@ -1480,10 +1479,10 @@ void SPU::DMAWrite(const u32* words, u32 word_count)
void SPU::GeneratePendingSamples() void SPU::GeneratePendingSamples()
{ {
if (s_state.transfer_event->IsActive()) if (s_state.transfer_event.IsActive())
s_state.transfer_event->InvokeEarly(); s_state.transfer_event.InvokeEarly();
const TickCount ticks_pending = s_state.s_tick_event->GetTicksSinceLastExecution(); const TickCount ticks_pending = s_state.tick_event.GetTicksSinceLastExecution();
TickCount frames_to_execute; TickCount frames_to_execute;
if (g_settings.cpu_overclock_active) if (g_settings.cpu_overclock_active)
{ {
@ -1494,11 +1493,11 @@ void SPU::GeneratePendingSamples()
else else
{ {
frames_to_execute = frames_to_execute =
(s_state.s_tick_event->GetTicksSinceLastExecution() + s_state.ticks_carry) / SYSCLK_TICKS_PER_SPU_TICK; (s_state.tick_event.GetTicksSinceLastExecution() + s_state.ticks_carry) / SYSCLK_TICKS_PER_SPU_TICK;
} }
const bool force_exec = (frames_to_execute > 0); const bool force_exec = (frames_to_execute > 0);
s_state.s_tick_event->InvokeEarly(force_exec); s_state.tick_event.InvokeEarly(force_exec);
} }
bool SPU::IsDumpingAudio() bool SPU::IsDumpingAudio()
@ -2446,18 +2445,18 @@ void SPU::UpdateEventInterval()
// TODO: Make this predict how long until the interrupt will be hit instead... // TODO: Make this predict how long until the interrupt will be hit instead...
const u32 interval = (s_state.SPUCNT.enable && s_state.SPUCNT.irq9_enable) ? 1 : max_slice_frames; const u32 interval = (s_state.SPUCNT.enable && s_state.SPUCNT.irq9_enable) ? 1 : max_slice_frames;
const TickCount interval_ticks = static_cast<TickCount>(interval) * s_state.cpu_ticks_per_spu_tick; const TickCount interval_ticks = static_cast<TickCount>(interval) * s_state.cpu_ticks_per_spu_tick;
if (s_state.s_tick_event->IsActive() && s_state.s_tick_event->GetInterval() == interval_ticks) if (s_state.tick_event.IsActive() && s_state.tick_event.GetInterval() == interval_ticks)
return; return;
// Ensure all pending ticks have been executed, since we won't get them back after rescheduling. // Ensure all pending ticks have been executed, since we won't get them back after rescheduling.
s_state.s_tick_event->InvokeEarly(true); s_state.tick_event.InvokeEarly(true);
s_state.s_tick_event->SetInterval(interval_ticks); s_state.tick_event.SetInterval(interval_ticks);
TickCount downcount = interval_ticks; TickCount downcount = interval_ticks;
if (!g_settings.cpu_overclock_active) if (!g_settings.cpu_overclock_active)
downcount -= s_state.ticks_carry; downcount -= s_state.ticks_carry;
s_state.s_tick_event->Schedule(downcount); s_state.tick_event.Schedule(downcount);
} }
void SPU::DrawDebugStateWindow() void SPU::DrawDebugStateWindow()
@ -2534,7 +2533,7 @@ void SPU::DrawDebugStateWindow()
ImGui::Text("Transfer FIFO: "); ImGui::Text("Transfer FIFO: ");
ImGui::SameLine(offsets[0]); ImGui::SameLine(offsets[0]);
ImGui::TextColored(s_state.transfer_event->IsActive() ? active_color : inactive_color, "%u halfwords (%u bytes)", ImGui::TextColored(s_state.transfer_event.IsActive() ? active_color : inactive_color, "%u halfwords (%u bytes)",
s_state.transfer_fifo.GetSize(), s_state.transfer_fifo.GetSize() * 2); s_state.transfer_fifo.GetSize(), s_state.transfer_fifo.GetSize() * 2);
} }

View File

@ -75,7 +75,7 @@ static void UpdateSysClkEvent();
namespace { namespace {
struct TimersState struct TimersState
{ {
std::unique_ptr<TimingEvent> sysclk_event; TimingEvent sysclk_event{ "Timer SysClk Interrupt", 1, 1, &Timers::AddSysClkTicks, nullptr };
std::array<CounterState, NUM_TIMERS> counters{}; std::array<CounterState, NUM_TIMERS> counters{};
TickCount sysclk_ticks_carry = 0; // 0 unless overclocking is enabled TickCount sysclk_ticks_carry = 0; // 0 unless overclocking is enabled
@ -88,14 +88,12 @@ ALIGN_TO_CACHE_LINE static TimersState s_state;
void Timers::Initialize() void Timers::Initialize()
{ {
s_state.sysclk_event =
TimingEvents::CreateTimingEvent("Timer SysClk Interrupt", 1, 1, &Timers::AddSysClkTicks, nullptr, false);
Reset(); Reset();
} }
void Timers::Shutdown() void Timers::Shutdown()
{ {
s_state.sysclk_event.reset(); s_state.sysclk_event.Deactivate();
} }
void Timers::Reset() void Timers::Reset()
@ -112,7 +110,7 @@ void Timers::Reset()
cs.irq_done = false; cs.irq_done = false;
} }
s_state.sysclk_event->Deactivate(); s_state.sysclk_event.Deactivate();
s_state.sysclk_ticks_carry = 0; s_state.sysclk_ticks_carry = 0;
s_state.sysclk_div_8_carry = 0; s_state.sysclk_div_8_carry = 0;
UpdateSysClkEvent(); UpdateSysClkEvent();
@ -176,7 +174,7 @@ void Timers::SetGate(u32 timer, bool state)
// Because the gate prevents counting in or outside of the gate, we need a correct counter. // Because the gate prevents counting in or outside of the gate, we need a correct counter.
// For reset, we _can_ skip it, until the gate clears. // For reset, we _can_ skip it, until the gate clears.
if (!cs.use_external_clock && (cs.mode.sync_mode != SyncMode::ResetOnGateEnd || !state)) if (!cs.use_external_clock && (cs.mode.sync_mode != SyncMode::ResetOnGateEnd || !state))
s_state.sysclk_event->InvokeEarly(); s_state.sysclk_event.InvokeEarly();
switch (cs.mode.sync_mode) switch (cs.mode.sync_mode)
{ {
@ -321,7 +319,7 @@ u32 Timers::ReadRegister(u32 offset)
g_gpu->SynchronizeCRTC(); g_gpu->SynchronizeCRTC();
} }
s_state.sysclk_event->InvokeEarly(); s_state.sysclk_event.InvokeEarly();
return cs.counter; return cs.counter;
} }
@ -335,7 +333,7 @@ u32 Timers::ReadRegister(u32 offset)
g_gpu->SynchronizeCRTC(); g_gpu->SynchronizeCRTC();
} }
s_state.sysclk_event->InvokeEarly(); s_state.sysclk_event.InvokeEarly();
const u32 bits = cs.mode.bits; const u32 bits = cs.mode.bits;
cs.mode.reached_overflow = false; cs.mode.reached_overflow = false;
@ -371,7 +369,7 @@ void Timers::WriteRegister(u32 offset, u32 value)
g_gpu->SynchronizeCRTC(); g_gpu->SynchronizeCRTC();
} }
s_state.sysclk_event->InvokeEarly(); s_state.sysclk_event.InvokeEarly();
// Strictly speaking these IRQ checks should probably happen on the next tick. // Strictly speaking these IRQ checks should probably happen on the next tick.
switch (port_offset) switch (port_offset)
@ -488,7 +486,7 @@ TickCount Timers::GetTicksUntilNextInterrupt()
void Timers::UpdateSysClkEvent() void Timers::UpdateSysClkEvent()
{ {
s_state.sysclk_event->Schedule(GetTicksUntilNextInterrupt()); s_state.sysclk_event.Schedule(GetTicksUntilNextInterrupt());
} }
void Timers::DrawDebugStateWindow() void Timers::DrawDebugStateWindow()

View File

@ -60,18 +60,6 @@ void TimingEvents::Shutdown()
Assert(s_state.active_event_count == 0); Assert(s_state.active_event_count == 0);
} }
std::unique_ptr<TimingEvent> TimingEvents::CreateTimingEvent(std::string name, TickCount period, TickCount interval,
TimingEventCallback callback, void* callback_param,
bool activate)
{
std::unique_ptr<TimingEvent> event =
std::make_unique<TimingEvent>(std::move(name), period, interval, callback, callback_param);
if (activate)
event->Activate();
return event;
}
void TimingEvents::UpdateCPUDowncount() void TimingEvents::UpdateCPUDowncount()
{ {
const u32 event_downcount = s_state.active_events_head->GetDowncount(); const u32 event_downcount = s_state.active_events_head->GetDowncount();
@ -377,7 +365,7 @@ bool TimingEvents::DoState(StateWrapper& sw)
for (u32 i = 0; i < event_count; i++) for (u32 i = 0; i < event_count; i++)
{ {
std::string event_name; TinyString event_name;
TickCount downcount, time_since_last_run, period, interval; TickCount downcount, time_since_last_run, period, interval;
sw.Do(&event_name); sw.Do(&event_name);
sw.Do(&downcount); sw.Do(&downcount);
@ -430,17 +418,16 @@ bool TimingEvents::DoState(StateWrapper& sw)
return !sw.HasError(); return !sw.HasError();
} }
TimingEvent::TimingEvent(std::string name, TickCount period, TickCount interval, TimingEventCallback callback, TimingEvent::TimingEvent(const std::string_view name, TickCount period, TickCount interval,
void* callback_param) 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_downcount(interval), m_time_since_last_run(0),
m_period(period), m_interval(interval), m_name(std::move(name)) m_period(period), m_interval(interval), m_name(name)
{ {
} }
TimingEvent::~TimingEvent() TimingEvent::~TimingEvent()
{ {
if (m_active) DebugAssert(!m_active);
TimingEvents::RemoveActiveEvent(this);
} }
TickCount TimingEvent::GetTicksSinceLastExecution() const TickCount TimingEvent::GetTicksSinceLastExecution() const

View File

@ -1,14 +1,12 @@
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com> // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
#pragma once #pragma once
#include <functional>
#include <memory>
#include <string>
#include <vector>
#include "types.h" #include "types.h"
#include <string_view>
class StateWrapper; class StateWrapper;
// Event callback type. Second parameter is the number of cycles the event was executed "late". // Event callback type. Second parameter is the number of cycles the event was executed "late".
@ -17,11 +15,11 @@ using TimingEventCallback = void (*)(void* param, TickCount ticks, TickCount tic
class TimingEvent class TimingEvent
{ {
public: public:
TimingEvent(std::string name, TickCount period, TickCount interval, TimingEventCallback callback, TimingEvent(const std::string_view name, TickCount period, TickCount interval, TimingEventCallback callback,
void* callback_param); void* callback_param);
~TimingEvent(); ~TimingEvent();
ALWAYS_INLINE const std::string& GetName() const { return m_name; } ALWAYS_INLINE const std::string_view GetName() const { return m_name; }
ALWAYS_INLINE bool IsActive() const { return m_active; } ALWAYS_INLINE bool IsActive() const { return m_active; }
// Returns the number of ticks between each event. // Returns the number of ticks between each event.
@ -75,7 +73,7 @@ public:
TickCount m_interval; TickCount m_interval;
bool m_active = false; bool m_active = false;
std::string m_name; std::string_view m_name;
}; };
namespace TimingEvents { namespace TimingEvents {
@ -87,11 +85,6 @@ void Initialize();
void Reset(); void Reset();
void Shutdown(); 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 DoState(StateWrapper& sw);
bool IsRunningEvents(); bool IsRunningEvents();

View File

@ -77,6 +77,14 @@ void StateWrapper::Do(SmallStringBase* value_ptr)
value_ptr->update_size(); value_ptr->update_size();
} }
void StateWrapper::Do(std::string_view* value_ptr)
{
Assert(m_mode == Mode::Write);
u32 length = static_cast<u32>(value_ptr->length());
Do(&length);
DoBytes(const_cast<char*>(value_ptr->data()), length);
}
bool StateWrapper::DoMarker(const char* marker) bool StateWrapper::DoMarker(const char* marker)
{ {
SmallString file_value(marker); SmallString file_value(marker);

View File

@ -108,6 +108,7 @@ public:
void Do(bool* value_ptr); void Do(bool* value_ptr);
void Do(std::string* value_ptr); void Do(std::string* value_ptr);
void Do(std::string_view* value_ptr);
void Do(SmallStringBase* value_ptr); void Do(SmallStringBase* value_ptr);
template<typename T, size_t N> template<typename T, size_t N>