From 4f7ddfaae625d9f6f56f9ee87e25ca3c1a3983af Mon Sep 17 00:00:00 2001 From: Stenzek Date: Fri, 19 Jul 2024 22:56:37 +1000 Subject: [PATCH] TimingEvents: Remove pointer indirection Probably should move this to one big array for locality. --- src/core/cdrom.cpp | 126 ++++++++++++++++++------------------- src/core/dma.cpp | 26 ++++---- src/core/gpu.cpp | 47 +++++++------- src/core/gpu.h | 6 +- src/core/justifier.cpp | 17 +++-- src/core/justifier.h | 7 ++- src/core/mdec.cpp | 20 +++--- src/core/memory_card.cpp | 17 +++-- src/core/memory_card.h | 17 ++--- src/core/pad.cpp | 17 +++-- src/core/spu.cpp | 65 ++++++++++--------- src/core/timers.cpp | 18 +++--- src/core/timing_event.cpp | 23 ++----- src/core/timing_event.h | 19 ++---- src/util/state_wrapper.cpp | 8 +++ src/util/state_wrapper.h | 1 + 16 files changed, 210 insertions(+), 224 deletions(-) diff --git a/src/core/cdrom.cpp b/src/core/cdrom.cpp index e223c4063..29545d805 100644 --- a/src/core/cdrom.cpp +++ b/src/core/cdrom.cpp @@ -299,10 +299,12 @@ static void CreateFileMap(); static void CreateFileMap(IsoReader& iso, std::string_view dir); static const std::string* LookupFileMap(u32 lba, u32* start_lba, u32* end_lba); -static std::unique_ptr s_command_event; -static std::unique_ptr s_command_second_response_event; -static std::unique_ptr s_async_interrupt_event; -static std::unique_ptr s_drive_event; +static TimingEvent s_command_event{"CDROM Command Event", 1, 1, &CDROM::ExecuteCommand, nullptr}; +static TimingEvent s_command_second_response_event{"CDROM Command Second Response Event", 1, 1, + &CDROM::ExecuteCommandSecondResponse, nullptr}; +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_second_response = Command::None; @@ -443,14 +445,6 @@ static std::array s_command_info = {{ 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) s_reader.StartThread(g_settings.cdrom_readahead_sectors); @@ -463,10 +457,10 @@ void CDROM::Shutdown() s_file_map_created = false; s_show_current_file = false; - s_drive_event.reset(); - s_async_interrupt_event.reset(); - s_command_second_response_event.reset(); - s_command_event.reset(); + s_drive_event.Deactivate(); + s_async_interrupt_event.Deactivate(); + s_command_second_response_event.Deactivate(); + s_command_event.Deactivate(); s_reader.StopThread(); s_reader.RemoveMedia(); } @@ -474,7 +468,7 @@ void CDROM::Shutdown() void CDROM::Reset() { s_command = Command::None; - s_command_event->Deactivate(); + s_command_event.Deactivate(); ClearCommandSecondResponse(); ClearDriveState(); s_status.bits = 0; @@ -569,7 +563,7 @@ void CDROM::SoftReset(TickCount ticks_late) if (s_current_lba != 0) { s_drive_state = DriveState::SeekingImplicit; - s_drive_event->SetIntervalAndSchedule(total_ticks); + s_drive_event.SetIntervalAndSchedule(total_ticks); s_requested_lba = 0; s_reader.QueueReadSector(s_requested_lba); s_seek_start_lba = s_current_lba; @@ -578,7 +572,7 @@ void CDROM::SoftReset(TickCount ticks_late) else { 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()) s_reader.QueueReadSector(s_requested_lba); UpdateCommandEvent(); - s_drive_event->SetState(!IsDriveIdle()); + s_drive_event.SetState(!IsDriveIdle()); // 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(); @@ -824,7 +818,7 @@ std::unique_ptr CDROM::RemoveMedia(bool for_disc_swap) ClearDriveState(); ClearCommandSecondResponse(); 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. ClearAsyncInterrupt(); @@ -834,7 +828,7 @@ std::unique_ptr CDROM::RemoveMedia(bool for_disc_swap) if (for_disc_swap) { s_drive_state = DriveState::ShellOpening; - s_drive_event->SetIntervalAndSchedule(stop_ticks); + s_drive_event.SetIntervalAndSchedule(stop_ticks); } return image; @@ -890,7 +884,7 @@ void CDROM::CPUClockChanged() { // reschedule the disc read event if (IsReadingOrPlaying()) - s_drive_event->SetInterval(GetTicksForRead()); + s_drive_event.SetInterval(GetTicksForRead()); } u8 CDROM::ReadRegister(u32 offset) @@ -1115,7 +1109,7 @@ void CDROM::WriteRegister(u32 offset, u8 value) sizeof(s_cd_audio_volume_matrix)) != 0)) { if (HasPendingDiscEvent()) - s_drive_event->InvokeEarly(); + s_drive_event.InvokeEarly(); SPU::GeneratePendingSamples(); } @@ -1210,7 +1204,7 @@ void CDROM::SetAsyncInterrupt(Interrupt interrupt) void CDROM::ClearAsyncInterrupt() { s_pending_async_interrupt = 0; - s_async_interrupt_event->Deactivate(); + s_async_interrupt_event.Deactivate(); 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, 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()) { // This shouldn't really happen, because we should block command execution.. but just in case. - if (!s_async_interrupt_event->IsActive()) - s_async_interrupt_event->Schedule(INTERRUPT_DELAY_CYCLES); + if (!s_async_interrupt_event.IsActive()) + s_async_interrupt_event.Schedule(INTERRUPT_DELAY_CYCLES); } else { - s_async_interrupt_event->Deactivate(); + s_async_interrupt_event.Deactivate(); Assert(s_pending_async_interrupt != 0 && !HasPendingInterrupt()); DEBUG_LOG("Delivering async interrupt {}", s_pending_async_interrupt); @@ -1308,7 +1302,7 @@ void CDROM::UpdateInterruptRequest() 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) @@ -1336,7 +1330,7 @@ TickCount CDROM::GetTicksForIDRead() { TickCount ticks = ID_READ_TICKS; if (s_drive_state == DriveState::SpinningUp) - ticks += s_drive_event->GetTicksUntilNextExecution(); + ticks += s_drive_event.GetTicksUntilNextExecution(); return ticks; } @@ -1376,7 +1370,7 @@ TickCount CDROM::GetTicksForSeek(CDImage::LBA new_lba, bool ignore_speed_change) if (!IsMotorOn()) { 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) 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) { // 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; 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(command)].name); // 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); - s_command_event->Deactivate(); + s_command_event.Deactivate(); } } s_command = command; - s_command_event->SetIntervalAndSchedule(ack_delay); + s_command_event.SetIntervalAndSchedule(ack_delay); UpdateCommandEvent(); UpdateStatusRegister(); } @@ -1511,7 +1505,7 @@ void CDROM::EndCommand() s_param_fifo.Clear(); s_command = Command::None; - s_command_event->Deactivate(); + s_command_event.Deactivate(); UpdateStatusRegister(); } @@ -1633,7 +1627,7 @@ void CDROM::ExecuteCommand(void*, TickCount ticks, TickCount ticks_late) if (s_drive_state == DriveState::ChangingSpeedOrTOCRead) { // 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"); 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", s_drive_state_names[static_cast(s_drive_state)], change_ticks, s_mode.double_speed ? "double" : "single"); - s_drive_event->Delay(change_ticks); + s_drive_event.Delay(change_ticks); if (IsReadingOrPlaying()) { WARNING_LOG("Speed change while reading/playing, reads will be temporarily delayed."); - s_drive_event->SetInterval(GetTicksForRead()); + s_drive_event.SetInterval(GetTicksForRead()); } } else { DEV_LOG("Drive is idle, speed change takes {} ticks", change_ticks); 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_drive_state = DriveState::ChangingSession; - s_drive_event->Schedule(GetTicksForTOCRead()); + s_drive_event.Schedule(GetTicksForTOCRead()); } EndCommand(); @@ -1870,7 +1864,7 @@ void CDROM::ExecuteCommand(void*, TickCount ticks, TickCount ticks_late) { // Stop reading. s_drive_state = DriveState::Idle; - s_drive_event->Deactivate(); + s_drive_event.Deactivate(); 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. s_command = Command::None; - s_command_event->Deactivate(); + s_command_event.Deactivate(); UpdateStatusRegister(); } break; @@ -2283,14 +2277,14 @@ void CDROM::ExecuteCommandSecondResponse(void*, TickCount ticks, TickCount ticks } 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) { ClearCommandSecondResponse(); s_command_second_response = command; - s_command_second_response_event->Schedule(ticks); + s_command_second_response_event.Schedule(ticks); } void CDROM::ClearCommandSecondResponse() @@ -2301,7 +2295,7 @@ void CDROM::ClearCommandSecondResponse() s_command_info[static_cast(s_command_second_response)].name); } - s_command_second_response_event->Deactivate(); + s_command_second_response_event.Deactivate(); s_command_second_response = Command::None; } @@ -2311,12 +2305,12 @@ void CDROM::UpdateCommandEvent() // so deactivate it until the interrupt is acknowledged if (!HasPendingCommand() || HasPendingInterrupt() || HasPendingAsyncInterrupt()) { - s_command_event->Deactivate(); + s_command_event.Deactivate(); return; } 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() { s_drive_state = DriveState::Idle; - s_drive_event->Deactivate(); + s_drive_event.Deactivate(); } 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()) { 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. if (s_drive_state == DriveState::SeekingImplicit) @@ -2433,8 +2427,8 @@ void CDROM::BeginReading(TickCount ticks_late /* = 0 */, bool after_seek /* = fa ResetAudioDecoder(); s_drive_state = DriveState::Reading; - s_drive_event->SetInterval(ticks); - s_drive_event->Schedule(first_sector_ticks); + s_drive_event.SetInterval(ticks); + s_drive_event.Schedule(first_sector_ticks); s_requested_lba = s_current_lba; s_reader.QueueReadSector(s_requested_lba); @@ -2476,8 +2470,8 @@ void CDROM::BeginPlaying(u8 track, TickCount ticks_late /* = 0 */, bool after_se ResetAudioDecoder(); s_drive_state = DriveState::Playing; - s_drive_event->SetInterval(ticks); - s_drive_event->Schedule(first_sector_ticks); + s_drive_event.SetInterval(ticks); + s_drive_event.Schedule(first_sector_ticks); s_requested_lba = s_current_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_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_end_lba = seek_lba; @@ -2521,8 +2515,8 @@ void CDROM::UpdatePositionWhileSeeking() { DebugAssert(IsSeeking()); - const float completed_frac = 1.0f - std::min(static_cast(s_drive_event->GetTicksUntilNextExecution()) / - static_cast(s_drive_event->GetInterval()), + const float completed_frac = 1.0f - std::min(static_cast(s_drive_event.GetTicksUntilNextExecution()) / + static_cast(s_drive_event.GetInterval()), 1.0f); CDImage::LBA current_lba; @@ -2804,7 +2798,7 @@ void CDROM::DoSpinUpComplete() { DEBUG_LOG("Spinup complete"); s_drive_state = DriveState::Idle; - s_drive_event->Deactivate(); + s_drive_event.Deactivate(); s_secondary_status.ClearActiveBits(); s_secondary_status.motor_on = true; } @@ -2813,7 +2807,7 @@ void CDROM::DoSpeedChangeOrImplicitTOCReadComplete() { DEBUG_LOG("Speed change/implicit TOC read complete"); s_drive_state = DriveState::Idle; - s_drive_event->Deactivate(); + s_drive_event.Deactivate(); } void CDROM::DoIDRead() @@ -2878,7 +2872,7 @@ void CDROM::StartMotor() DEV_LOG("Starting motor"); s_drive_state = DriveState::SpinningUp; - s_drive_event->Schedule(GetTicksForSpinUp()); + s_drive_event.Schedule(GetTicksForSpinUp()); } 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); s_async_response_fifo.Push(s_secondary_status.bits); s_pending_async_interrupt = static_cast(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)", s_command_info[static_cast(s_command)].name, static_cast(s_command), - s_command_event->IsActive() ? s_command_event->GetTicksUntilNextExecution() : 0); + s_command_event.IsActive() ? s_command_event.GetTicksUntilNextExecution() : 0); } else { @@ -3684,7 +3678,7 @@ void CDROM::DrawDebugWindow() { ImGui::TextColored(active_color, "Drive: %s (%d ticks remaining)", s_drive_state_names[static_cast(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); diff --git a/src/core/dma.cpp b/src/core/dma.cpp index ecc336945..dd01f0ee7 100644 --- a/src/core/dma.cpp +++ b/src/core/dma.cpp @@ -204,7 +204,7 @@ struct DMAState TickCount halt_ticks = 100; std::vector transfer_buffer; - std::unique_ptr unhalt_event; + TimingEvent unhalt_event{"DMA Transfer Unhalt", 1, 1, &DMA::UnhaltTransfer, nullptr}; TickCount halt_ticks_remaining = 0; std::array channels; @@ -243,22 +243,20 @@ void DMA::Initialize() { s_state.max_slice_ticks = g_settings.dma_max_slice_ticks; s_state.halt_ticks = g_settings.dma_halt_ticks; - - s_state.unhalt_event = TimingEvents::CreateTimingEvent("DMA Transfer Unhalt", 1, s_state.max_slice_ticks, - &DMA::UnhaltTransfer, nullptr, false); + s_state.unhalt_event.SetInterval(s_state.max_slice_ticks); Reset(); } void DMA::Shutdown() { ClearState(); - s_state.unhalt_event.reset(); + s_state.unhalt_event.Deactivate(); } void DMA::Reset() { ClearState(); - s_state.unhalt_event->Deactivate(); + s_state.unhalt_event.Deactivate(); } void DMA::ClearState() @@ -297,9 +295,9 @@ bool DMA::DoState(StateWrapper& sw) if (sw.IsReading()) { 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 - s_state.unhalt_event->Deactivate(); + s_state.unhalt_event.Deactivate(); } return !sw.HasError(); @@ -502,7 +500,7 @@ ALWAYS_INLINE_RELEASE bool DMA::CanTransferChannel(Channel channel, bool ignore_ bool DMA::IsTransferHalted() { - return s_state.unhalt_event->IsActive(); + return s_state.unhalt_event.IsActive(); } void DMA::UpdateIRQ() @@ -733,7 +731,7 @@ bool DMA::TransferChannel() if (cs.request) { // we got halted - if (!s_state.unhalt_event->IsActive()) + if (!s_state.unhalt_event.IsActive()) HaltTransfer(s_state.halt_ticks); return false; @@ -757,18 +755,18 @@ void DMA::HaltTransfer(TickCount duration) { s_state.halt_ticks_remaining += duration; DEBUG_LOG("Halting DMA for {} ticks", s_state.halt_ticks_remaining); - if (s_state.unhalt_event->IsActive()) + if (s_state.unhalt_event.IsActive()) return; - DebugAssert(!s_state.unhalt_event->IsActive()); - s_state.unhalt_event->SetIntervalAndSchedule(s_state.halt_ticks_remaining); + DebugAssert(!s_state.unhalt_event.IsActive()); + s_state.unhalt_event.SetIntervalAndSchedule(s_state.halt_ticks_remaining); } void DMA::UnhaltTransfer(void*, TickCount ticks, TickCount ticks_late) { DEBUG_LOG("Resuming DMA after {} ticks, {} ticks late", 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. // Main thing is that OTC happens after GPU, because otherwise it'll wipe out the LL. diff --git a/src/core/gpu.cpp b/src/core/gpu.cpp index 57f33bc34..189581be2 100644 --- a/src/core/gpu.cpp +++ b/src/core/gpu.cpp @@ -60,12 +60,22 @@ static u32 s_active_gpu_cycles_frames = 0; static constexpr GPUTexture::Format DISPLAY_INTERNAL_POSTFX_FORMAT = GPUTexture::Format::RGBA8; GPU::GPU() + : m_crtc_tick_event( + "GPU CRTC Tick", 1, 1, + [](void* param, TickCount ticks, TickCount ticks_late) { static_cast(param)->CRTCTickEvent(ticks); }, this), + m_command_tick_event( + "GPU Command Tick", 1, 1, + [](void* param, TickCount ticks, TickCount ticks_late) { static_cast(param)->CommandTickEvent(ticks); }, + this) { ResetStatistics(); } GPU::~GPU() { + m_command_tick_event.Deactivate(); + m_crtc_tick_event.Deactivate(); + JoinScreenshotThreads(); DestroyDeinterlaceTextures(); 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_ntsc_timings = g_settings.gpu_force_ntsc_timings; - m_crtc_tick_event = TimingEvents::CreateTimingEvent( - "GPU CRTC Tick", 1, 1, - [](void* param, TickCount ticks, TickCount ticks_late) { static_cast(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(param)->CommandTickEvent(ticks); }, this, - true); + m_crtc_tick_event.Activate(); m_fifo_size = g_settings.gpu_fifo_size; m_max_run_ahead = g_settings.gpu_max_run_ahead; m_console_is_pal = System::IsPALRegion(); @@ -187,8 +190,8 @@ void GPU::Reset(bool clear_vram) m_blitter_state = BlitterState::Idle; // Force event to reschedule itself. - m_crtc_tick_event->Deactivate(); - m_command_tick_event->Deactivate(); + m_crtc_tick_event.Deactivate(); + m_command_tick_event.Deactivate(); SoftReset(); UpdateDisplay(); @@ -455,7 +458,7 @@ u32 GPU::ReadRegister(u32 offset) if (IsCRTCScanlinePending()) SynchronizeCRTC(); if (IsCommandCompletionPending()) - m_command_tick_event->InvokeEarly(); + m_command_tick_event.InvokeEarly(); return m_GPUSTAT.bits; } @@ -547,7 +550,7 @@ void GPU::AddCommandTicks(TickCount ticks) void GPU::SynchronizeCRTC() { - m_crtc_tick_event->InvokeEarly(); + m_crtc_tick_event.InvokeEarly(); } float GPU::ComputeHorizontalFrequency() const @@ -833,17 +836,17 @@ void GPU::UpdateCRTCDisplayParameters() 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; return SystemTicksToCRTCTicks(pending_sysclk_ticks, &fractional_ticks); } TickCount GPU::GetPendingCommandTicks() const { - if (!m_command_tick_event->IsActive()) + if (!m_command_tick_event.IsActive()) return 0; - return SystemTicksToGPUTicks(m_command_tick_event->GetTicksSinceLastExecution()); + return SystemTicksToGPUTicks(m_command_tick_event.GetTicksSinceLastExecution()); } void GPU::UpdateCRTCTickEvent() @@ -900,7 +903,7 @@ void GPU::UpdateCRTCTickEvent() 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 @@ -1080,11 +1083,11 @@ void GPU::UpdateCommandTickEvent() if (m_pending_command_ticks <= 0) { m_pending_command_ticks = 0; - m_command_tick_event->Deactivate(); + m_command_tick_event.Deactivate(); } 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 { DEBUG_LOG("GP1 reset GPU"); - m_command_tick_event->InvokeEarly(); + m_command_tick_event.InvokeEarly(); SynchronizeCRTC(); SoftReset(); } @@ -1218,7 +1221,7 @@ void GPU::WriteGP1(u32 value) case 0x01: // Clear FIFO { DEBUG_LOG("GP1 clear FIFO"); - m_command_tick_event->InvokeEarly(); + m_command_tick_event.InvokeEarly(); SynchronizeCRTC(); // flush partial writes @@ -1232,7 +1235,7 @@ void GPU::WriteGP1(u32 value) m_blit_buffer.clear(); m_blit_remaining_words = 0; m_pending_command_ticks = 0; - m_command_tick_event->Deactivate(); + m_command_tick_event.Deactivate(); UpdateDMARequest(); UpdateGPUIdle(); } @@ -1350,7 +1353,7 @@ void GPU::WriteGP1(u32 value) { // Have to be careful when setting this because Synchronize() can modify GPUSTAT. static constexpr u32 SET_MASK = UINT32_C(0b00000000011111110100000000000000); - m_command_tick_event->InvokeEarly(); + m_command_tick_event.InvokeEarly(); SynchronizeCRTC(); m_GPUSTAT.bits = (m_GPUSTAT.bits & ~SET_MASK) | (new_GPUSTAT.bits & SET_MASK); UpdateCRTCConfig(); diff --git a/src/core/gpu.h b/src/core/gpu.h index 019138b28..42ec451c1 100644 --- a/src/core/gpu.h +++ b/src/core/gpu.h @@ -5,6 +5,7 @@ #include "gpu_types.h" #include "timers.h" #include "types.h" +#include "timing_event.h" #include "util/gpu_texture.h" @@ -29,7 +30,6 @@ class GPUTexture; class GPUPipeline; struct Settings; -class TimingEvent; namespace Threading { class Thread; @@ -370,8 +370,8 @@ protected: AddCommandTicks(std::max(drawn_width, drawn_height)); } - std::unique_ptr m_crtc_tick_event; - std::unique_ptr m_command_tick_event; + TimingEvent m_crtc_tick_event; + TimingEvent m_command_tick_event; union GPUSTAT { diff --git a/src/core/justifier.cpp b/src/core/justifier.cpp index 72df27b00..d623ad43b 100644 --- a/src/core/justifier.cpp +++ b/src/core/justifier.cpp @@ -28,16 +28,21 @@ static u32 s_irq_current_line; #endif static constexpr std::array(Justifier::Binding::ButtonCount)> s_button_indices = {{15, 3, 14}}; +static constexpr std::array 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(param)->IRQEvent(); }, this) { - m_irq_event = TimingEvents::CreateTimingEvent( - "Justifier IRQ", 1, 1, [](void* param, TickCount, TickCount) { static_cast(param)->IRQEvent(); }, this, - false); } Justifier::~Justifier() { + m_irq_event.Deactivate(); + if (!m_cursor_path.empty()) { const u32 cursor_index = GetSoftwarePointerIndex(); @@ -245,7 +250,7 @@ void Justifier::UpdatePosition() void Justifier::UpdateIRQEvent() { // TODO: Avoid deactivate and event sort. - m_irq_event->Deactivate(); + m_irq_event.Deactivate(); if (!m_position_valid) return; @@ -261,7 +266,7 @@ void Justifier::UpdateIRQEvent() 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); - m_irq_event->Schedule(ticks_until_pos); + m_irq_event.Schedule(ticks_until_pos); } void Justifier::IRQEvent() diff --git a/src/core/justifier.h b/src/core/justifier.h index ec19db955..51895bdcc 100644 --- a/src/core/justifier.h +++ b/src/core/justifier.h @@ -2,7 +2,10 @@ // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) #pragma once + #include "controller.h" +#include "timing_event.h" + #include #include #include @@ -77,8 +80,6 @@ private: static constexpr u8 DEFAULT_OFFSCREEN_TRIGGER_FRAMES = 5; static constexpr u8 DEFAULT_OFFSCREEN_RELEASE_FRAMES = 5; - std::unique_ptr m_irq_event; - s8 m_first_line_offset = 0; s8 m_last_line_offset = 0; s16 m_tick_offset = 0; @@ -98,6 +99,8 @@ private: TransferState m_transfer_state = TransferState::Idle; + TimingEvent m_irq_event; + bool m_has_relative_binds = false; float m_relative_pos[4] = {}; diff --git a/src/core/mdec.cpp b/src/core/mdec.cpp index 937c8301c..4605157a5 100644 --- a/src/core/mdec.cpp +++ b/src/core/mdec.cpp @@ -151,7 +151,7 @@ struct MDECState u16 current_q_scale = 0; alignas(16) std::array block_rgb{}; - std::unique_ptr block_copy_out_event; + TimingEvent block_copy_out_event{"MDEC Block Copy Out", 1, 1, &MDEC::CopyOutBlock, nullptr}; u32 total_blocks_decoded = 0; }; @@ -162,20 +162,18 @@ ALIGN_TO_CACHE_LINE static MDECState s_state; 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; Reset(); } void MDEC::Shutdown() { - s_state.block_copy_out_event.reset(); + s_state.block_copy_out_event.Deactivate(); } void MDEC::Reset() { - s_state.block_copy_out_event->Deactivate(); + s_state.block_copy_out_event.Deactivate(); SoftReset(); } @@ -211,7 +209,7 @@ bool MDEC::DoState(StateWrapper& sw) bool block_copy_out_pending = HasPendingBlockCopyOut(); sw.Do(&block_copy_out_pending); 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(); } @@ -304,7 +302,7 @@ void MDEC::DMAWrite(const u32* words, u32 word_count) bool MDEC::HasPendingBlockCopyOut() { - return s_state.block_copy_out_event->IsActive(); + return s_state.block_copy_out_event.IsActive(); } void MDEC::SoftReset() @@ -319,7 +317,7 @@ void MDEC::SoftReset() s_state.current_block = 0; s_state.current_coefficient = 64; s_state.current_q_scale = 0; - s_state.block_copy_out_event->Deactivate(); + s_state.block_copy_out_event.Deactivate(); UpdateStatus(); } @@ -358,7 +356,7 @@ u32 MDEC::ReadDataRegister() if (HasPendingBlockCopyOut()) { 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 { @@ -617,13 +615,13 @@ void MDEC::ScheduleBlockCopyOut(TickCount ticks) DebugAssert(!HasPendingBlockCopyOut()); 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) { 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) { diff --git a/src/core/memory_card.cpp b/src/core/memory_card.cpp index c99451e25..e6dd2b007 100644 --- a/src/core/memory_card.cpp +++ b/src/core/memory_card.cpp @@ -22,13 +22,12 @@ Log_SetChannel(MemoryCard); MemoryCard::MemoryCard() + : m_save_event( + "Memory Card Host Flush", GetSaveDelayInTicks(), GetSaveDelayInTicks(), + [](void* param, TickCount ticks, TickCount ticks_late) { static_cast(param)->SaveIfChanged(true); }, + this) { 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(param)->SaveIfChanged(true); }, - this, false); } MemoryCard::~MemoryCard() @@ -285,7 +284,7 @@ bool MemoryCard::Transfer(const u8 data_in, u8* data_out) 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::Create() @@ -324,7 +323,7 @@ bool MemoryCard::LoadFromFile() bool MemoryCard::SaveIfChanged(bool display_osd_message) { - m_save_event->Deactivate(); + m_save_event.Deactivate(); if (!m_changed) return true; @@ -369,9 +368,9 @@ bool MemoryCard::SaveIfChanged(bool display_osd_message) void MemoryCard::QueueFileSave() { // 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; // save in one second, that should be long enough for everything to finish writing - m_save_event->Schedule(GetSaveDelayInTicks()); + m_save_event.Schedule(GetSaveDelayInTicks()); } diff --git a/src/core/memory_card.h b/src/core/memory_card.h index 41403219b..80faf3085 100644 --- a/src/core/memory_card.h +++ b/src/core/memory_card.h @@ -1,17 +1,19 @@ -// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin +// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) #pragma once -#include "common/bitfield.h" + #include "controller.h" #include "memory_card_image.h" +#include "timing_event.h" + +#include "common/bitfield.h" + #include #include #include #include -class TimingEvent; - class MemoryCard final { public: @@ -97,8 +99,6 @@ private: bool SaveIfChanged(bool display_osd_message); void QueueFileSave(); - std::unique_ptr m_save_event; - State m_state = State::Idle; FLAG m_FLAG = {}; u16 m_address = 0; @@ -107,7 +107,8 @@ private: u8 m_last_byte = 0; bool m_changed = false; - MemoryCardImage::DataArray m_data{}; - + TimingEvent m_save_event; std::string m_filename; + + MemoryCardImage::DataArray m_data{}; }; diff --git a/src/core/pad.cpp b/src/core/pad.cpp index 300064ce8..ef8b89e52 100644 --- a/src/core/pad.cpp +++ b/src/core/pad.cpp @@ -117,7 +117,7 @@ static std::array, NUM_CONTROLLER_AND_CARD_PORTS> s_ static std::array s_multitaps; -static std::unique_ptr s_transfer_event; +static TimingEvent s_transfer_event{"Pad Serial Transfer", 1, 1, &Pad::TransferEvent, nullptr}; static State s_state = State::Idle; static JOY_CTRL s_JOY_CTRL = {}; @@ -140,7 +140,6 @@ static std::unique_ptr s_dummy_card; void Pad::Initialize() { - s_transfer_event = TimingEvents::CreateTimingEvent("Pad Serial Transfer", 1, 1, &Pad::TransferEvent, nullptr, false); Reset(); } @@ -148,7 +147,7 @@ void Pad::Shutdown() { s_memory_card_backup.reset(); - s_transfer_event.reset(); + s_transfer_event.Deactivate(); 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); if (sw.IsReading() && IsTransmitting()) - s_transfer_event->Activate(); + s_transfer_event.Activate(); return !sw.HasError(); } @@ -581,7 +580,7 @@ u32 Pad::ReadRegister(u32 offset) case 0x00: // JOY_DATA { if (IsTransmitting()) - s_transfer_event->InvokeEarly(); + s_transfer_event.InvokeEarly(); const u8 value = s_receive_buffer_full ? s_receive_buffer : 0xFF; 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 { if (IsTransmitting()) - s_transfer_event->InvokeEarly(); + s_transfer_event.InvokeEarly(); const u32 bits = s_JOY_STAT.bits; s_JOY_STAT.ACKINPUT = false; @@ -763,7 +762,7 @@ void Pad::BeginTransfer() // until after (4) and (5) have been completed. s_state = State::Transmitting; - s_transfer_event->SetPeriodAndSchedule(GetTransferTicks()); + s_transfer_event.SetPeriodAndSchedule(GetTransferTicks()); } void Pad::DoTransfer(TickCount ticks_late) @@ -882,7 +881,7 @@ void Pad::DoTransfer(TickCount ticks_late) const TickCount ack_timer = GetACKTicks(memcard_transfer); DEBUG_LOG("Delaying ACK for {} ticks", ack_timer); s_state = State::WaitingForACK; - s_transfer_event->SetPeriodAndSchedule(ack_timer); + s_transfer_event.SetPeriodAndSchedule(ack_timer); } UpdateJoyStat(); @@ -912,7 +911,7 @@ void Pad::EndTransfer() DEBUG_LOG("Ending transfer"); s_state = State::Idle; - s_transfer_event->Deactivate(); + s_transfer_event.Deactivate(); } void Pad::ResetDeviceTransferState() diff --git a/src/core/spu.cpp b/src/core/spu.cpp index d6b50483a..29bed3992 100644 --- a/src/core/spu.cpp +++ b/src/core/spu.cpp @@ -355,8 +355,9 @@ static void CreateOutputStream(); namespace { struct SPUState { - std::unique_ptr s_tick_event; - std::unique_ptr transfer_event; + TimingEvent transfer_event{"SPU Transfer", TRANSFER_TICKS_PER_HALFWORD, TRANSFER_TICKS_PER_HALFWORD, + &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 cpu_ticks_per_spu_tick = 0; @@ -426,10 +427,8 @@ void SPU::Initialize() // (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_tick_divider = static_cast(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.cpu_ticks_per_spu_tick, &SPU::Execute, nullptr, false); - s_state.transfer_event = TimingEvents::CreateTimingEvent( - "SPU Transfer", TRANSFER_TICKS_PER_HALFWORD, TRANSFER_TICKS_PER_HALFWORD, &SPU::ExecuteTransfer, nullptr, false); + s_state.tick_event.SetInterval(s_state.cpu_ticks_per_spu_tick); + s_state.tick_event.SetPeriod(s_state.cpu_ticks_per_spu_tick); s_state.null_audio_stream = AudioStream::CreateNullStream(SAMPLE_RATE, g_settings.audio_stream_parameters.buffer_ms); CreateOutputStream(); @@ -482,8 +481,8 @@ void SPU::CPUClockChanged() void SPU::Shutdown() { StopDumpingAudio(); - s_state.s_tick_event.reset(); - s_state.transfer_event.reset(); + s_state.tick_event.Deactivate(); + s_state.transfer_event.Deactivate(); s_state.audio_stream.reset(); } @@ -539,8 +538,8 @@ void SPU::Reset() v.ignore_loop_address = false; } - s_state.s_tick_event->Deactivate(); - s_state.transfer_event->Deactivate(); + s_state.tick_event.Deactivate(); + s_state.transfer_event.Deactivate(); s_state.transfer_fifo.Clear(); s_ram.fill(0); UpdateEventInterval(); @@ -885,7 +884,7 @@ void SPU::WriteRegister(u32 offset, u16 value) case 0x1F801DA6 - SPU_BASE: { 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 = ZeroExtend32(value) * 8; 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()) { s_state.SPUSTAT.transfer_busy = false; - s_state.transfer_event->Deactivate(); + s_state.transfer_event.Deactivate(); return; } s_state.SPUSTAT.transfer_busy = true; const TickCount ticks_until_complete = 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 { @@ -1307,14 +1306,14 @@ void SPU::ExecuteTransfer(void* param, TickCount ticks, TickCount ticks_late) if (s_state.transfer_fifo.IsEmpty()) { s_state.SPUSTAT.transfer_busy = false; - s_state.transfer_event->Deactivate(); + s_state.transfer_event.Deactivate(); return; } s_state.SPUSTAT.transfer_busy = true; const TickCount ticks_until_complete = 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; if (mode == RAMTransferMode::Stopped) { - s_state.transfer_event->Deactivate(); + s_state.transfer_event.Deactivate(); } else if (mode == RAMTransferMode::DMARead) { // transfer event fills the fifo if (s_state.transfer_fifo.IsFull()) - s_state.transfer_event->Deactivate(); - 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.Deactivate(); + else if (!s_state.transfer_event.IsActive()) + s_state.transfer_event.Schedule(TickCount(s_state.transfer_fifo.GetSpace() * u32(TRANSFER_TICKS_PER_HALFWORD))); } else { // transfer event copies from fifo to ram if (s_state.transfer_fifo.IsEmpty()) - s_state.transfer_event->Deactivate(); - 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.Deactivate(); + 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.SPUSTAT.transfer_busy = s_state.transfer_event->IsActive(); + s_state.SPUSTAT.transfer_busy = s_state.transfer_event.IsActive(); } void SPU::UpdateDMARequest() @@ -1480,10 +1479,10 @@ void SPU::DMAWrite(const u32* words, u32 word_count) void SPU::GeneratePendingSamples() { - if (s_state.transfer_event->IsActive()) - s_state.transfer_event->InvokeEarly(); + if (s_state.transfer_event.IsActive()) + 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; if (g_settings.cpu_overclock_active) { @@ -1494,11 +1493,11 @@ void SPU::GeneratePendingSamples() else { 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); - s_state.s_tick_event->InvokeEarly(force_exec); + s_state.tick_event.InvokeEarly(force_exec); } bool SPU::IsDumpingAudio() @@ -2446,18 +2445,18 @@ void SPU::UpdateEventInterval() // 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 TickCount interval_ticks = static_cast(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; // 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.s_tick_event->SetInterval(interval_ticks); + s_state.tick_event.InvokeEarly(true); + s_state.tick_event.SetInterval(interval_ticks); TickCount downcount = interval_ticks; if (!g_settings.cpu_overclock_active) downcount -= s_state.ticks_carry; - s_state.s_tick_event->Schedule(downcount); + s_state.tick_event.Schedule(downcount); } void SPU::DrawDebugStateWindow() @@ -2534,7 +2533,7 @@ void SPU::DrawDebugStateWindow() ImGui::Text("Transfer FIFO: "); 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); } diff --git a/src/core/timers.cpp b/src/core/timers.cpp index b1e7ae4ae..3dc0ff66d 100644 --- a/src/core/timers.cpp +++ b/src/core/timers.cpp @@ -75,7 +75,7 @@ static void UpdateSysClkEvent(); namespace { struct TimersState { - std::unique_ptr sysclk_event; + TimingEvent sysclk_event{ "Timer SysClk Interrupt", 1, 1, &Timers::AddSysClkTicks, nullptr }; std::array counters{}; 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() { - s_state.sysclk_event = - TimingEvents::CreateTimingEvent("Timer SysClk Interrupt", 1, 1, &Timers::AddSysClkTicks, nullptr, false); Reset(); } void Timers::Shutdown() { - s_state.sysclk_event.reset(); + s_state.sysclk_event.Deactivate(); } void Timers::Reset() @@ -112,7 +110,7 @@ void Timers::Reset() cs.irq_done = false; } - s_state.sysclk_event->Deactivate(); + s_state.sysclk_event.Deactivate(); s_state.sysclk_ticks_carry = 0; s_state.sysclk_div_8_carry = 0; 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. // For reset, we _can_ skip it, until the gate clears. 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) { @@ -321,7 +319,7 @@ u32 Timers::ReadRegister(u32 offset) g_gpu->SynchronizeCRTC(); } - s_state.sysclk_event->InvokeEarly(); + s_state.sysclk_event.InvokeEarly(); return cs.counter; } @@ -335,7 +333,7 @@ u32 Timers::ReadRegister(u32 offset) g_gpu->SynchronizeCRTC(); } - s_state.sysclk_event->InvokeEarly(); + s_state.sysclk_event.InvokeEarly(); const u32 bits = cs.mode.bits; cs.mode.reached_overflow = false; @@ -371,7 +369,7 @@ void Timers::WriteRegister(u32 offset, u32 value) 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. switch (port_offset) @@ -488,7 +486,7 @@ TickCount Timers::GetTicksUntilNextInterrupt() void Timers::UpdateSysClkEvent() { - s_state.sysclk_event->Schedule(GetTicksUntilNextInterrupt()); + s_state.sysclk_event.Schedule(GetTicksUntilNextInterrupt()); } void Timers::DrawDebugStateWindow() diff --git a/src/core/timing_event.cpp b/src/core/timing_event.cpp index 2baab9a59..542dea66c 100644 --- a/src/core/timing_event.cpp +++ b/src/core/timing_event.cpp @@ -60,18 +60,6 @@ void TimingEvents::Shutdown() Assert(s_state.active_event_count == 0); } -std::unique_ptr TimingEvents::CreateTimingEvent(std::string name, TickCount period, TickCount interval, - TimingEventCallback callback, void* callback_param, - bool activate) -{ - std::unique_ptr event = - std::make_unique(std::move(name), period, interval, callback, callback_param); - if (activate) - event->Activate(); - - return event; -} - void TimingEvents::UpdateCPUDowncount() { 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++) { - std::string event_name; + TinyString event_name; TickCount downcount, time_since_last_run, period, interval; sw.Do(&event_name); sw.Do(&downcount); @@ -430,17 +418,16 @@ bool TimingEvents::DoState(StateWrapper& sw) return !sw.HasError(); } -TimingEvent::TimingEvent(std::string name, TickCount period, TickCount interval, TimingEventCallback callback, - void* callback_param) +TimingEvent::TimingEvent(const std::string_view name, TickCount period, TickCount interval, + TimingEventCallback callback, void* callback_param) : m_callback(callback), m_callback_param(callback_param), m_downcount(interval), m_time_since_last_run(0), - m_period(period), m_interval(interval), m_name(std::move(name)) + m_period(period), m_interval(interval), m_name(name) { } TimingEvent::~TimingEvent() { - if (m_active) - TimingEvents::RemoveActiveEvent(this); + DebugAssert(!m_active); } TickCount TimingEvent::GetTicksSinceLastExecution() const diff --git a/src/core/timing_event.h b/src/core/timing_event.h index 01733ba51..4e3b9f116 100644 --- a/src/core/timing_event.h +++ b/src/core/timing_event.h @@ -1,14 +1,12 @@ -// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin +// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) #pragma once -#include -#include -#include -#include #include "types.h" +#include + class StateWrapper; // 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 { 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); ~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; } // Returns the number of ticks between each event. @@ -75,7 +73,7 @@ public: TickCount m_interval; bool m_active = false; - std::string m_name; + std::string_view m_name; }; namespace TimingEvents { @@ -87,11 +85,6 @@ void Initialize(); void Reset(); void Shutdown(); -/// Creates a new event. -std::unique_ptr CreateTimingEvent(std::string name, TickCount period, TickCount interval, - TimingEventCallback callback, void* callback_param, bool activate); - -/// Serialization. bool DoState(StateWrapper& sw); bool IsRunningEvents(); diff --git a/src/util/state_wrapper.cpp b/src/util/state_wrapper.cpp index dba2fb9c5..f221c75db 100644 --- a/src/util/state_wrapper.cpp +++ b/src/util/state_wrapper.cpp @@ -77,6 +77,14 @@ void StateWrapper::Do(SmallStringBase* value_ptr) value_ptr->update_size(); } +void StateWrapper::Do(std::string_view* value_ptr) +{ + Assert(m_mode == Mode::Write); + u32 length = static_cast(value_ptr->length()); + Do(&length); + DoBytes(const_cast(value_ptr->data()), length); +} + bool StateWrapper::DoMarker(const char* marker) { SmallString file_value(marker); diff --git a/src/util/state_wrapper.h b/src/util/state_wrapper.h index 2a473262b..91812de66 100644 --- a/src/util/state_wrapper.h +++ b/src/util/state_wrapper.h @@ -108,6 +108,7 @@ public: void Do(bool* value_ptr); void Do(std::string* value_ptr); + void Do(std::string_view* value_ptr); void Do(SmallStringBase* value_ptr); template