MTGS: Support 'running idle', i.e. redisplaying the current frame

This commit is contained in:
Connor McLaughlin 2022-05-15 18:19:03 +10:00 committed by refractionpcsx2
parent be3f120548
commit d466184a02
5 changed files with 51 additions and 1 deletions

View File

@ -26,6 +26,30 @@
// Semaphore Implementations // Semaphore Implementations
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
bool Threading::WorkSema::CheckForWork()
{
s32 value = m_state.load(std::memory_order_relaxed);
pxAssert(!IsDead(value));
// we want to switch to the running state, but preserve the waiting empty bit for RUNNING_N -> RUNNING_0
// otherwise, we clear the waiting flag (since we're notifying the waiter that we're empty below)
while (!m_state.compare_exchange_weak(value,
IsReadyForSleep(value) ? STATE_RUNNING_0 : (value & STATE_FLAG_WAITING_EMPTY),
std::memory_order_acq_rel, std::memory_order_relaxed))
{
}
// if we're not empty, we have work to do
if (!IsReadyForSleep(value))
return true;
// this means we're empty, so notify any waiters
if (value & STATE_FLAG_WAITING_EMPTY)
m_empty_sema.Post();
// no work to do
return false;
}
void Threading::WorkSema::WaitForWork() void Threading::WorkSema::WaitForWork()
{ {

View File

@ -211,6 +211,8 @@ namespace Threading
m_sema.Post(); m_sema.Post();
} }
/// Checks if there's any work in the queue
bool CheckForWork();
/// Wait for work to be added to the queue /// Wait for work to be added to the queue
void WaitForWork(); void WaitForWork();
/// Wait for work to be added to the queue, spinning for a bit before sleeping the thread /// Wait for work to be added to the queue, spinning for a bit before sleeping the thread

View File

@ -367,6 +367,7 @@ public:
Threading::ThreadHandle m_thread_handle; Threading::ThreadHandle m_thread_handle;
std::atomic_bool m_open_flag{false}; std::atomic_bool m_open_flag{false};
std::atomic_bool m_shutdown_flag{false}; std::atomic_bool m_shutdown_flag{false};
std::atomic_bool m_run_idle_flag{false};
Threading::KernelSemaphore m_open_or_close_done; Threading::KernelSemaphore m_open_or_close_done;
public: public:
@ -416,6 +417,7 @@ public:
void SetSoftwareRendering(bool software, bool display_message = true); void SetSoftwareRendering(bool software, bool display_message = true);
void ToggleSoftwareRendering(); void ToggleSoftwareRendering();
bool SaveMemorySnapshot(u32 width, u32 height, std::vector<u32>* pixels); bool SaveMemorySnapshot(u32 width, u32 height, std::vector<u32>* pixels);
void SetRunIdle(bool enabled);
protected: protected:
bool TryOpenGS(); bool TryOpenGS();

View File

@ -31,6 +31,8 @@
#ifndef PCSX2_CORE #ifndef PCSX2_CORE
#include "gui/Dialogs/ModalPopups.h" #include "gui/Dialogs/ModalPopups.h"
#else
#include "VMManager.h"
#endif #endif
// Uncomment this to enable profiling of the GS RingBufferCopy function. // Uncomment this to enable profiling of the GS RingBufferCopy function.
@ -296,9 +298,23 @@ void SysMtgsThread::MainLoop()
// is very optimized (only 1 instruction test in most cases), so no point in trying // is very optimized (only 1 instruction test in most cases), so no point in trying
// to avoid it. // to avoid it.
#ifdef PCSX2_CORE
if (m_run_idle_flag.load(std::memory_order_acquire) && VMManager::GetState() != VMState::Running)
{
if (!m_sem_event.CheckForWork())
GSPresentCurrentFrame();
}
else
{
mtvu_lock.unlock(); mtvu_lock.unlock();
m_sem_event.WaitForWork(); m_sem_event.WaitForWork();
mtvu_lock.lock(); mtvu_lock.lock();
}
#else
mtvu_lock.unlock();
m_sem_event.WaitForWork();
mtvu_lock.lock();
#endif
if (!m_open_flag.load(std::memory_order_acquire)) if (!m_open_flag.load(std::memory_order_acquire))
break; break;
@ -1005,3 +1021,9 @@ void SysMtgsThread::PresentCurrentFrame()
{ {
GSPresentCurrentFrame(); GSPresentCurrentFrame();
} }
void SysMtgsThread::SetRunIdle(bool enabled)
{
// NOTE: Should only be called on the GS thread.
m_run_idle_flag.store(enabled, std::memory_order_release);
}