Merge pull request #8730 from JosJuice/frame-advance-duplicate-frame
Core: Skip duplicate frames when using frame advance
This commit is contained in:
commit
935b12d785
|
@ -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<bool> s_stop_frame_step;
|
||||
|
||||
#ifdef USE_MEMORYWATCHER
|
||||
static std::unique_ptr<MemoryWatcher> s_memory_watcher;
|
||||
|
@ -858,18 +860,27 @@ void VideoThrottle()
|
|||
|
||||
// --- Callbacks for backends / engine ---
|
||||
|
||||
// Should be called from GPU thread when a frame is drawn
|
||||
void Callback_VideoCopiedToXFB(bool video_update)
|
||||
// Called from Renderer::Swap (GPU thread) when a new (non-duplicate)
|
||||
// frame is presented to the host screen
|
||||
void Callback_FramePresented()
|
||||
{
|
||||
if (video_update)
|
||||
s_drawn_frame++;
|
||||
s_stop_frame_step.store(true);
|
||||
}
|
||||
|
||||
// Called at field boundaries in `VideoInterface::Update()`
|
||||
void FrameUpdate()
|
||||
// Called from VideoInterface::Update (CPU thread) at emulated field boundaries
|
||||
void Callback_NewField()
|
||||
{
|
||||
Movie::FrameUpdate();
|
||||
if (s_frame_step)
|
||||
{
|
||||
// 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();
|
||||
|
@ -877,6 +888,7 @@ void FrameUpdate()
|
|||
s_on_state_changed_callback(Core::GetState());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateTitle()
|
||||
{
|
||||
|
@ -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);
|
||||
|
|
|
@ -25,8 +25,8 @@ namespace Core
|
|||
bool GetIsThrottlerTempDisabled();
|
||||
void SetIsThrottlerTempDisabled(bool disable);
|
||||
|
||||
void Callback_VideoCopiedToXFB(bool video_update);
|
||||
void FrameUpdate();
|
||||
void Callback_FramePresented();
|
||||
void Callback_NewField();
|
||||
|
||||
enum class State
|
||||
{
|
||||
|
|
|
@ -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,32 +817,10 @@ 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
|
||||
|
||||
if (s_half_line_count == 0 || s_half_line_count == GetHalfLinesPerEvenField())
|
||||
Core::FrameUpdate();
|
||||
|
||||
// If an SI poll is scheduled to happen on this half-line, do it!
|
||||
|
||||
if (s_half_line_of_next_si_poll == s_half_line_count)
|
||||
{
|
||||
Core::UpdateInputGate(!SConfig::GetInstance().m_BackgroundInput);
|
||||
SerialInterface::UpdateDevices();
|
||||
s_half_line_of_next_si_poll += 2 * SerialInterface::GetPollXLines();
|
||||
}
|
||||
|
||||
// If this half-line is at the actual boundary of either field, schedule an SI poll to happen
|
||||
// some number of half-lines in the future
|
||||
|
||||
if (s_half_line_count == 0)
|
||||
{
|
||||
s_half_line_of_next_si_poll = num_half_lines_for_si_poll; // first results start at vsync
|
||||
}
|
||||
if (s_half_line_count == GetHalfLinesPerEvenField())
|
||||
{
|
||||
s_half_line_of_next_si_poll = GetHalfLinesPerEvenField() + num_half_lines_for_si_poll;
|
||||
}
|
||||
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
|
||||
|
@ -864,6 +843,33 @@ void Update(u64 ticks)
|
|||
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();
|
||||
|
||||
// If an SI poll is scheduled to happen on this half-line, do it!
|
||||
|
||||
if (s_half_line_of_next_si_poll == s_half_line_count)
|
||||
{
|
||||
Core::UpdateInputGate(!SConfig::GetInstance().m_BackgroundInput);
|
||||
SerialInterface::UpdateDevices();
|
||||
s_half_line_of_next_si_poll += 2 * SerialInterface::GetPollXLines();
|
||||
}
|
||||
|
||||
// If this half-line is at the actual boundary of either field, schedule an SI poll to happen
|
||||
// some number of half-lines in the future
|
||||
|
||||
if (s_half_line_count == 0)
|
||||
{
|
||||
s_half_line_of_next_si_poll = num_half_lines_for_si_poll; // first results start at vsync
|
||||
}
|
||||
if (s_half_line_count == GetHalfLinesPerEvenField())
|
||||
{
|
||||
s_half_line_of_next_si_poll = GetHalfLinesPerEvenField() + num_half_lines_for_si_poll;
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
|
|
|
@ -97,6 +97,12 @@ void AsyncRequests::PushEvent(const AsyncRequests::Event& event, bool blocking)
|
|||
}
|
||||
}
|
||||
|
||||
void AsyncRequests::WaitForEmptyQueue()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
m_cond.wait(lock, [this] { return m_queue.empty(); });
|
||||
}
|
||||
|
||||
void AsyncRequests::SetEnable(bool enable)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
|
|
|
@ -82,6 +82,7 @@ public:
|
|||
PullEventsInternal();
|
||||
}
|
||||
void PushEvent(const Event& event, bool blocking = false);
|
||||
void WaitForEmptyQueue();
|
||||
void SetEnable(bool enable);
|
||||
void SetPassthrough(bool enable);
|
||||
|
||||
|
|
|
@ -1309,7 +1309,7 @@ void Renderer::Swap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u6
|
|||
{
|
||||
// Remove stale EFB/XFB copies.
|
||||
g_texture_cache->Cleanup(m_frame_count);
|
||||
Core::Callback_VideoCopiedToXFB(true);
|
||||
Core::Callback_FramePresented();
|
||||
}
|
||||
|
||||
// Handle any config changes, this gets propogated to the backend.
|
||||
|
|
Loading…
Reference in New Issue