diff --git a/src/core/gpu.cpp b/src/core/gpu.cpp index cd97e8ffd..6b09d453a 100644 --- a/src/core/gpu.cpp +++ b/src/core/gpu.cpp @@ -35,6 +35,8 @@ bool GPU::Initialize(HostDisplay* host_display, System* system, DMA* dma, Interr m_system->CreateTimingEvent("GPU Tick", 1, 1, std::bind(&GPU::Execute, this, std::placeholders::_1), true); m_fifo_size = system->GetSettings().gpu_fifo_size; m_max_run_ahead = system->GetSettings().gpu_max_run_ahead; + m_console_is_pal = system->IsPALRegion(); + UpdateCRTCConfig(); return true; } @@ -46,9 +48,10 @@ void GPU::UpdateSettings() m_fifo_size = settings.gpu_fifo_size; m_max_run_ahead = settings.gpu_max_run_ahead; - if (m_force_ntsc_timings != settings.gpu_force_ntsc_timings) + if (m_force_ntsc_timings != settings.gpu_force_ntsc_timings || m_console_is_pal != m_system->IsPALRegion()) { m_force_ntsc_timings = settings.gpu_force_ntsc_timings; + m_console_is_pal = m_system->IsPALRegion(); UpdateCRTCConfig(); } @@ -129,6 +132,7 @@ bool GPU::DoState(StateWrapper& sw) sw.Do(&m_drawing_offset.y); sw.Do(&m_drawing_offset.x); + sw.Do(&m_console_is_pal); sw.Do(&m_set_texture_disable_mask); sw.Do(&m_crtc_state.regs.display_address_start); @@ -353,6 +357,42 @@ void GPU::DMAWrite(const u32* words, u32 word_count) } } +/** + * NTSC GPU clock 53.693175 MHz + * PAL GPU clock 53.203425 MHz + * courtesy of @ggrtk + * + * NTSC - sysclk * 715909 / 451584 + * PAL - sysclk * 709379 / 451584 + */ + +TickCount GPU::GPUTicksToSystemTicks(TickCount gpu_ticks, TickCount fractional_ticks) const +{ + // convert to master clock, rounding up as we want to overshoot not undershoot + if (!m_console_is_pal) + return static_cast((u64(gpu_ticks) * u64(451584) + fractional_ticks + u64(715908)) / u64(715909)); + else + return static_cast((u64(gpu_ticks) * u64(451584) + fractional_ticks + u64(709378)) / u64(709379)); +} + +TickCount GPU::SystemTicksToGPUTicks(TickCount sysclk_ticks, TickCount* fractional_ticks) const +{ + if (!m_console_is_pal) + { + const u64 mul = u64(sysclk_ticks) * u64(715909) + u64(*fractional_ticks); + const TickCount ticks = static_cast(mul / u64(451584)); + *fractional_ticks = static_cast(mul % u64(451584)); + return ticks; + } + else + { + const u64 mul = u64(sysclk_ticks) * u64(709379) + u64(*fractional_ticks); + const TickCount ticks = static_cast(mul / u64(451584)); + *fractional_ticks = static_cast(mul % u64(451584)); + return ticks; + } +} + void GPU::AddCommandTicks(TickCount ticks) { if (m_command_ticks != 0) @@ -364,7 +404,7 @@ void GPU::AddCommandTicks(TickCount ticks) m_command_ticks = GetPendingGPUTicks() + ticks; // reschedule GPU tick event if it would execute later than this command finishes - const TickCount sysclk_ticks = GPUTicksToSystemTicks(ticks); + const TickCount sysclk_ticks = GPUTicksToSystemTicks(ticks, 0); if (m_tick_event->GetTicksUntilNextExecution() > sysclk_ticks) m_tick_event->Schedule(sysclk_ticks); } @@ -374,6 +414,23 @@ void GPU::Synchronize() m_tick_event->InvokeEarly(); } +float GPU::ComputeHorizontalFrequency() const +{ + const CRTCState& cs = m_crtc_state; + TickCount fractional_ticks = 0; + return static_cast(static_cast(SystemTicksToGPUTicks(MASTER_CLOCK, &fractional_ticks)) / + static_cast(cs.horizontal_total)); +} + +float GPU::ComputeVerticalFrequency() const +{ + const CRTCState& cs = m_crtc_state; + const TickCount ticks_per_frame = cs.horizontal_total * cs.vertical_total; + TickCount fractional_ticks = 0; + return static_cast(static_cast(SystemTicksToGPUTicks(MASTER_CLOCK, &fractional_ticks)) / + static_cast(ticks_per_frame)); +} + void GPU::UpdateCRTCConfig() { static constexpr std::array dot_clock_dividers = {{10, 8, 5, 4, 7, 7, 7, 7}}; @@ -420,10 +477,7 @@ void GPU::UpdateCRTCConfig() cs.current_tick_in_scanline %= NTSC_TICKS_PER_LINE; } - const TickCount ticks_per_frame = cs.horizontal_total * cs.vertical_total; - const float vertical_frequency = - static_cast(static_cast((u64(MASTER_CLOCK) * 11) / 7) / static_cast(ticks_per_frame)); - m_system->SetThrottleFrequency(vertical_frequency); + m_system->SetThrottleFrequency(ComputeVerticalFrequency()); UpdateCRTCDisplayParameters(); UpdateSliceTicks(); @@ -571,7 +625,8 @@ void GPU::UpdateCRTCDisplayParameters() TickCount GPU::GetPendingGPUTicks() const { const TickCount pending_sysclk_ticks = m_tick_event->GetTicksSinceLastExecution(); - return ((pending_sysclk_ticks * 11) + m_crtc_state.fractional_ticks) / 7; + TickCount fractional_ticks = m_crtc_state.fractional_ticks; + return SystemTicksToGPUTicks(pending_sysclk_ticks, &fractional_ticks); } void GPU::UpdateSliceTicks() @@ -595,7 +650,8 @@ void GPU::UpdateSliceTicks() #endif m_tick_event->Schedule( - GPUTicksToSystemTicks((m_command_ticks > 0) ? std::min(m_command_ticks, ticks_until_event) : ticks_until_event)); + GPUTicksToSystemTicks((m_command_ticks > 0) ? std::min(m_command_ticks, ticks_until_event) : ticks_until_event, + m_crtc_state.fractional_ticks)); } bool GPU::IsRasterScanlinePending() const @@ -614,9 +670,7 @@ void GPU::Execute(TickCount ticks) { // convert cpu/master clock to GPU ticks, accounting for partial cycles because of the non-integer divider { - const TickCount ticks_mul_11 = (ticks * 11) + m_crtc_state.fractional_ticks; - const TickCount gpu_ticks = ticks_mul_11 / 7; - m_crtc_state.fractional_ticks = ticks_mul_11 % 7; + const TickCount gpu_ticks = SystemTicksToGPUTicks(ticks, &m_crtc_state.fractional_ticks); m_crtc_state.current_tick_in_scanline += gpu_ticks; // handle blits @@ -1326,6 +1380,10 @@ void GPU::DrawDebugStateWindow() if (ImGui::CollapsingHeader("CRTC", ImGuiTreeNodeFlags_DefaultOpen)) { const auto& cs = m_crtc_state; + ImGui::Text("Clock: %s", (m_console_is_pal ? (m_GPUSTAT.pal_mode ? "PAL-on-PAL" : "NTSC-on-PAL") : + (m_GPUSTAT.pal_mode ? "PAL-on-NTSC" : "NTSC-on-NTSC"))); + ImGui::Text("Horizontal Frequency: %.3f KHz", ComputeHorizontalFrequency() / 1000.0f); + ImGui::Text("Vertical Frequency: %.3f Hz", ComputeVerticalFrequency()); ImGui::Text("Dot Clock Divider: %u", cs.dot_clock_divider); ImGui::Text("Vertical Interlace: %s (%s field)", m_GPUSTAT.vertical_interlace ? "Yes" : "No", m_crtc_state.interlaced_field ? "odd" : "even"); diff --git a/src/core/gpu.h b/src/core/gpu.h index 5cc83a751..423bdbc93 100644 --- a/src/core/gpu.h +++ b/src/core/gpu.h @@ -170,11 +170,8 @@ public: bool ConvertScreenCoordinatesToBeamTicksAndLines(s32 window_x, s32 window_y, u32* out_tick, u32* out_line) const; protected: - static constexpr TickCount GPUTicksToSystemTicks(TickCount gpu_ticks) - { - // convert to master clock, rounding up as we want to overshoot not undershoot - return static_cast((static_cast(gpu_ticks) * 7u + 10u) / 11u); - } + TickCount GPUTicksToSystemTicks(TickCount gpu_ticks, TickCount fractional_ticks) const; + TickCount SystemTicksToGPUTicks(TickCount sysclk_ticks, TickCount* fractional_ticks) const; // Helper/format conversion functions. static constexpr u8 Convert5To8(u8 x5) { return (x5 << 3) | (x5 & 7); } @@ -326,6 +323,8 @@ protected: void SoftReset(); // Sets dots per scanline + float ComputeHorizontalFrequency() const; + float ComputeVerticalFrequency() const; void UpdateCRTCConfig(); void UpdateCRTCDisplayParameters(); @@ -572,6 +571,7 @@ protected: s32 y; } m_drawing_offset = {}; + bool m_console_is_pal = false; bool m_set_texture_disable_mask = false; bool m_drawing_area_changed = false; bool m_force_progressive_scan = false; diff --git a/src/core/save_state_version.h b/src/core/save_state_version.h index 2d5f06b30..b7cfb1c5b 100644 --- a/src/core/save_state_version.h +++ b/src/core/save_state_version.h @@ -2,7 +2,7 @@ #include "types.h" static constexpr u32 SAVE_STATE_MAGIC = 0x43435544; -static constexpr u32 SAVE_STATE_VERSION = 36; +static constexpr u32 SAVE_STATE_VERSION = 37; #pragma pack(push, 4) struct SAVE_STATE_HEADER diff --git a/src/core/system.cpp b/src/core/system.cpp index 294f3df79..cb5b328b7 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -260,6 +260,7 @@ void System::InitializeComponents() m_timers->Initialize(this, m_interrupt_controller.get(), m_gpu.get()); m_spu->Initialize(this, m_dma.get(), m_interrupt_controller.get()); m_mdec->Initialize(this, m_dma.get()); + m_gpu->UpdateSettings(); UpdateThrottlePeriod(); }