From d4665e8b22ef8442c0bb03c1ee66fbbdfd313188 Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Sat, 15 Aug 2020 14:56:20 +1000 Subject: [PATCH] GPU: Tweaks to ODE handling Fixes Team Buddies and The Next Tetris. --- src/core/gpu.cpp | 18 ++++++++++++------ src/core/gpu.h | 6 ++++++ src/core/system.cpp | 10 +++------- src/core/timing_event.cpp | 6 +++--- src/core/timing_event.h | 2 +- 5 files changed, 25 insertions(+), 17 deletions(-) diff --git a/src/core/gpu.cpp b/src/core/gpu.cpp index ca225673c..0ff029eee 100644 --- a/src/core/gpu.cpp +++ b/src/core/gpu.cpp @@ -162,6 +162,7 @@ bool GPU::DoState(StateWrapper& sw) sw.Do(&m_crtc_state.in_hblank); sw.Do(&m_crtc_state.in_vblank); sw.Do(&m_crtc_state.interlaced_field); + sw.Do(&m_crtc_state.interlaced_display_field); sw.Do(&m_crtc_state.active_line_lsb); sw.Do(&m_blitter_state); @@ -734,10 +735,10 @@ void GPU::CRTCTickEvent(TickCount ticks) System::FrameDone(); // switch fields early. this is needed so we draw to the correct one. - if (m_GPUSTAT.vertical_interlace) - m_crtc_state.interlaced_field ^= 1u; + if (m_GPUSTAT.InInterleaved480iMode()) + m_crtc_state.interlaced_display_field = m_crtc_state.interlaced_field ^ 1u; else - m_crtc_state.interlaced_field = 0; + m_crtc_state.interlaced_display_field = 0; } g_timers.SetGate(HBLANK_TIMER_INDEX, new_vblank); @@ -749,15 +750,20 @@ void GPU::CRTCTickEvent(TickCount ticks) { // start the new frame m_crtc_state.current_scanline = 0; + if (m_GPUSTAT.vertical_interlace) + m_crtc_state.interlaced_field ^= 1u; + else + m_crtc_state.interlaced_field = 0; } } // alternating even line bit in 240-line mode - if (m_GPUSTAT.vertical_interlace) + if (m_GPUSTAT.InInterleaved480iMode()) { m_crtc_state.active_line_lsb = - ConvertToBoolUnchecked((m_crtc_state.regs.Y + BoolToUInt32(m_crtc_state.interlaced_field)) & u32(1)); - m_GPUSTAT.display_line_lsb = m_crtc_state.active_line_lsb && !m_crtc_state.in_vblank; + Truncate8((m_crtc_state.regs.Y + BoolToUInt32(m_crtc_state.interlaced_display_field)) & u32(1)); + m_GPUSTAT.display_line_lsb = ConvertToBoolUnchecked( + (m_crtc_state.regs.Y + (BoolToUInt8(m_crtc_state.in_vblank) ^ m_crtc_state.interlaced_display_field)) & u32(1)); } else { diff --git a/src/core/gpu.h b/src/core/gpu.h index 239bcbbde..274265b34 100644 --- a/src/core/gpu.h +++ b/src/core/gpu.h @@ -513,6 +513,11 @@ protected: static constexpr u32 ACTIVE = (1 << 19) | (1 << 22); return ((bits & MASK) == ACTIVE); } + bool InInterleaved480iMode() const + { + static constexpr u32 ACTIVE = (1 << 19) | (1 << 22); + return ((bits & ACTIVE) == ACTIVE); + } // During transfer/render operations, if ((dst_pixel & mask_and) == 0) { pixel = src_pixel | mask_or } u16 GetMaskAND() const @@ -694,6 +699,7 @@ protected: bool in_vblank; u8 interlaced_field; // 0 = odd, 1 = even + u8 interlaced_display_field; u8 active_line_lsb; } m_crtc_state = {}; diff --git a/src/core/system.cpp b/src/core/system.cpp index 0327c08a0..afa88da9a 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -227,7 +227,7 @@ bool RecreateGPU(GPURenderer renderer) // save current state std::unique_ptr state_stream = ByteStream_CreateGrowableMemoryStream(); StateWrapper sw(state_stream.get(), StateWrapper::Mode::Write); - const bool state_valid = g_gpu->DoState(sw) && TimingEvents::DoState(sw, TimingEvents::GetGlobalTickCounter()); + const bool state_valid = g_gpu->DoState(sw) && TimingEvents::DoState(sw); if (!state_valid) Log_ErrorPrintf("Failed to save old GPU state when switching renderers"); @@ -247,7 +247,7 @@ bool RecreateGPU(GPURenderer renderer) sw.SetMode(StateWrapper::Mode::Read); g_gpu->RestoreGraphicsAPIState(); g_gpu->DoState(sw); - TimingEvents::DoState(sw, TimingEvents::GetGlobalTickCounter()); + TimingEvents::DoState(sw); g_gpu->ResetGraphicsAPIState(); } @@ -540,13 +540,9 @@ bool DoState(StateWrapper& sw) if (!sw.DoMarker("System")) return false; - // TODO: Move this into timing state - u32 global_tick_counter = TimingEvents::GetGlobalTickCounter(); - sw.Do(&s_region); sw.Do(&s_frame_number); sw.Do(&s_internal_frame_number); - sw.Do(&global_tick_counter); if (!sw.DoMarker("CPU") || !CPU::DoState(sw)) return false; @@ -584,7 +580,7 @@ bool DoState(StateWrapper& sw) if (!sw.DoMarker("SIO") || !g_sio.DoState(sw)) return false; - if (!sw.DoMarker("Events") || !TimingEvents::DoState(sw, global_tick_counter)) + if (!sw.DoMarker("Events") || !TimingEvents::DoState(sw)) return false; return !sw.HasError(); diff --git a/src/core/timing_event.cpp b/src/core/timing_event.cpp index 583f7e32c..42f7edfc9 100644 --- a/src/core/timing_event.cpp +++ b/src/core/timing_event.cpp @@ -172,12 +172,12 @@ void RunEvents() UpdateCPUDowncount(); } -bool DoState(StateWrapper& sw, u32 global_tick_counter) +bool DoState(StateWrapper& sw) { + sw.Do(&s_global_tick_counter); + if (sw.IsReading()) { - s_global_tick_counter = global_tick_counter; - // Load timestamps for the clock events. // Any oneshot events should be recreated by the load state method, so we can fix up their times here. u32 event_count = 0; diff --git a/src/core/timing_event.h b/src/core/timing_event.h index 200fbee41..0238b3402 100644 --- a/src/core/timing_event.h +++ b/src/core/timing_event.h @@ -79,7 +79,7 @@ std::unique_ptr CreateTimingEvent(std::string name, TickCount perio TimingEventCallback callback, bool activate); /// Serialization. -bool DoState(StateWrapper& sw, u32 global_tick_counter); +bool DoState(StateWrapper& sw); void RunEvents();