diff --git a/Source/Core/Core/Core.cpp b/Source/Core/Core/Core.cpp index 4644df5689..72b33dd37b 100644 --- a/Source/Core/Core/Core.cpp +++ b/Source/Core/Core/Core.cpp @@ -77,6 +77,7 @@ #include "InputCommon/ControllerInterface/ControllerInterface.h" #include "InputCommon/GCAdapter.h" +#include "VideoCommon/AsyncRequests.h" #include "VideoCommon/Fifo.h" #include "VideoCommon/OnScreenDisplay.h" #include "VideoCommon/RenderBase.h" @@ -104,6 +105,7 @@ static std::thread s_cpu_thread; static bool s_request_refresh_info = false; static bool s_is_throttler_temp_disabled = false; static bool s_frame_step = false; +static std::atomic s_stop_frame_step; #ifdef USE_MEMORYWATCHER static std::unique_ptr s_memory_watcher; @@ -863,18 +865,28 @@ void VideoThrottle() void Callback_FramePresented() { s_drawn_frame++; + s_stop_frame_step.store(true); } // Called from VideoInterface::Update (CPU thread) at emulated field boundaries void Callback_NewField() { - Movie::FrameUpdate(); if (s_frame_step) { - s_frame_step = false; - CPU::Break(); - if (s_on_state_changed_callback) - s_on_state_changed_callback(Core::GetState()); + // To ensure that s_stop_frame_step is up to date, wait for the GPU thread queue to empty, + // since it is may contain a swap event (which will call Callback_FramePresented). This hurts + // the performance a little, but luckily, performance matters less when using frame stepping. + AsyncRequests::GetInstance()->WaitForEmptyQueue(); + + // Only stop the frame stepping if a new frame was displayed + // (as opposed to the previous frame being displayed for another frame). + if (s_stop_frame_step.load()) + { + s_frame_step = false; + CPU::Break(); + if (s_on_state_changed_callback) + s_on_state_changed_callback(Core::GetState()); + } } } @@ -1045,6 +1057,7 @@ void DoFrameStep() if (GetState() == State::Paused) { // if already paused, frame advance for 1 frame + s_stop_frame_step = false; s_frame_step = true; RequestRefreshInfo(); SetState(State::Running); diff --git a/Source/Core/Core/HW/VideoInterface.cpp b/Source/Core/Core/HW/VideoInterface.cpp index b3a6ed90d0..b3a53eeb9c 100644 --- a/Source/Core/Core/HW/VideoInterface.cpp +++ b/Source/Core/Core/HW/VideoInterface.cpp @@ -24,6 +24,7 @@ #include "Core/HW/ProcessorInterface.h" #include "Core/HW/SI/SI.h" #include "Core/HW/SystemTimers.h" +#include "Core/Movie.h" #include "DiscIO/Enums.h" @@ -816,8 +817,34 @@ static void EndField() // Run when: When a frame is scanned (progressive/interlace) void Update(u64 ticks) { - // If this half-line is at a field boundary, potentially deal with frame-stepping - // and/or update movie state before dealing with anything else + // Movie's frame counter should be updated before actually rendering the frame, + // in case frame counter display is enabled + + Movie::FrameUpdate(); + + // If this half-line is at some boundary of the "active video lines" in either field, we either + // need to (a) send a request to the GPU thread to actually render the XFB, or (b) increment + // the number of frames we've actually drawn + + if (s_half_line_count == s_even_field_first_hl) + { + BeginField(FieldType::Even, ticks); + } + else if (s_half_line_count == s_odd_field_first_hl) + { + BeginField(FieldType::Odd, ticks); + } + else if (s_half_line_count == s_even_field_last_hl) + { + EndField(); + } + else if (s_half_line_count == s_odd_field_last_hl) + { + EndField(); + } + + // If this half-line is at a field boundary, deal with updating movie state before potentially + // dealing with SI polls, but after potentially sending a swap request to the GPU thread if (s_half_line_count == 0 || s_half_line_count == GetHalfLinesPerEvenField()) Core::Callback_NewField(); @@ -843,27 +870,6 @@ void Update(u64 ticks) s_half_line_of_next_si_poll = GetHalfLinesPerEvenField() + num_half_lines_for_si_poll; } - // If this half-line is at some boundary of the "active video lines" in either field, we either - // need to (a) send a request to the GPU thread to actually render the XFB, or (b) increment - // the number of frames we've actually drawn - - if (s_half_line_count == s_even_field_first_hl) - { - BeginField(FieldType::Even, ticks); - } - else if (s_half_line_count == s_odd_field_first_hl) - { - BeginField(FieldType::Odd, ticks); - } - else if (s_half_line_count == s_even_field_last_hl) - { - EndField(); - } - else if (s_half_line_count == s_odd_field_last_hl) - { - EndField(); - } - // Move to the next half-line and potentially roll-over the count to zero. If we've reached // the beginning of a new full-line, update the timer diff --git a/Source/Core/VideoCommon/AsyncRequests.cpp b/Source/Core/VideoCommon/AsyncRequests.cpp index a824d0020a..9a32b9ad48 100644 --- a/Source/Core/VideoCommon/AsyncRequests.cpp +++ b/Source/Core/VideoCommon/AsyncRequests.cpp @@ -97,6 +97,12 @@ void AsyncRequests::PushEvent(const AsyncRequests::Event& event, bool blocking) } } +void AsyncRequests::WaitForEmptyQueue() +{ + std::unique_lock lock(m_mutex); + m_cond.wait(lock, [this] { return m_queue.empty(); }); +} + void AsyncRequests::SetEnable(bool enable) { std::unique_lock lock(m_mutex); diff --git a/Source/Core/VideoCommon/AsyncRequests.h b/Source/Core/VideoCommon/AsyncRequests.h index dc81667586..f8f682af2c 100644 --- a/Source/Core/VideoCommon/AsyncRequests.h +++ b/Source/Core/VideoCommon/AsyncRequests.h @@ -82,6 +82,7 @@ public: PullEventsInternal(); } void PushEvent(const Event& event, bool blocking = false); + void WaitForEmptyQueue(); void SetEnable(bool enable); void SetPassthrough(bool enable);