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 const std::string* LookupFileMap(u32 lba, u32* start_lba, u32* end_lba);
static std::unique_ptr<TimingEvent> s_command_event;
static std::unique_ptr<TimingEvent> s_command_second_response_event;
static std::unique_ptr<TimingEvent> s_async_interrupt_event;
static std::unique_ptr<TimingEvent> 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<CommandInfo, 255> 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<CDImage> 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<CDImage> 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<u8>(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<u8>(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<u16>(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<float>(s_drive_event->GetTicksUntilNextExecution()) /
static_cast<float>(s_drive_event->GetInterval()),
const float completed_frac = 1.0f - std::min(static_cast<float>(s_drive_event.GetTicksUntilNextExecution()) /
static_cast<float>(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<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)",
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
{
@ -3684,7 +3678,7 @@ void CDROM::DrawDebugWindow()
{
ImGui::TextColored(active_color, "Drive: %s (%d ticks remaining)",
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);

View File

@ -204,7 +204,7 @@ struct DMAState
TickCount halt_ticks = 100;
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;
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.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.

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;
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();
}
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<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_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();

View File

@ -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<TimingEvent> m_crtc_tick_event;
std::unique_ptr<TimingEvent> m_command_tick_event;
TimingEvent m_crtc_tick_event;
TimingEvent m_command_tick_event;
union GPUSTAT
{

View File

@ -28,16 +28,21 @@ static u32 s_irq_current_line;
#endif
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()
{
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()

View File

@ -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 <memory>
#include <optional>
#include <string_view>
@ -77,8 +80,6 @@ private:
static constexpr u8 DEFAULT_OFFSCREEN_TRIGGER_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_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] = {};

View File

@ -151,7 +151,7 @@ struct MDECState
u16 current_q_scale = 0;
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;
};
@ -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)
{

View File

@ -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<MemoryCard*>(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<MemoryCard*>(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> 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());
}

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)
#pragma once
#include "common/bitfield.h"
#include "controller.h"
#include "memory_card_image.h"
#include "timing_event.h"
#include "common/bitfield.h"
#include <array>
#include <memory>
#include <string>
#include <string_view>
class TimingEvent;
class MemoryCard final
{
public:
@ -97,8 +99,6 @@ private:
bool SaveIfChanged(bool display_osd_message);
void QueueFileSave();
std::unique_ptr<TimingEvent> 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{};
};

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::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 JOY_CTRL s_JOY_CTRL = {};
@ -140,7 +140,6 @@ static std::unique_ptr<MemoryCard> 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()

View File

@ -355,8 +355,9 @@ static void CreateOutputStream();
namespace {
struct SPUState
{
std::unique_ptr<TimingEvent> s_tick_event;
std::unique_ptr<TimingEvent> 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<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.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<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;
// 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);
}

View File

@ -75,7 +75,7 @@ static void UpdateSysClkEvent();
namespace {
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{};
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()

View File

@ -60,18 +60,6 @@ void TimingEvents::Shutdown()
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()
{
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

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)
#pragma once
#include <functional>
#include <memory>
#include <string>
#include <vector>
#include "types.h"
#include <string_view>
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<TimingEvent> CreateTimingEvent(std::string name, TickCount period, TickCount interval,
TimingEventCallback callback, void* callback_param, bool activate);
/// Serialization.
bool DoState(StateWrapper& sw);
bool IsRunningEvents();

View File

@ -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<u32>(value_ptr->length());
Do(&length);
DoBytes(const_cast<char*>(value_ptr->data()), length);
}
bool StateWrapper::DoMarker(const char* marker)
{
SmallString file_value(marker);

View File

@ -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<typename T, size_t N>