mirror of
https://github.com/moonlight-stream/moonlight-qt.git
synced 2026-06-15 21:22:40 +00:00
Allow Pacer to wait for frames up until a few ms before v-sync for better smoothness and lower latency
This commit is contained in:
@@ -34,13 +34,15 @@ DisplayLinkVsyncSource::displayLinkOutputCallback(
|
|||||||
// interval, even if many ms prior, we can safely use the current host time
|
// interval, even if many ms prior, we can safely use the current host time
|
||||||
// and get a consistent callback for each v-sync. This reduces video latency
|
// and get a consistent callback for each v-sync. This reduces video latency
|
||||||
// by at least 1 frame vs. rendering with the actual vsyncTime.
|
// by at least 1 frame vs. rendering with the actual vsyncTime.
|
||||||
me->m_Pacer->vsyncCallback();
|
me->m_Pacer->vsyncCallback(500 / m_DisplayFps);
|
||||||
|
|
||||||
return kCVReturnSuccess;
|
return kCVReturnSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DisplayLinkVsyncSource::initialize(SDL_Window*)
|
bool DisplayLinkVsyncSource::initialize(SDL_Window*, int displayFps)
|
||||||
{
|
{
|
||||||
|
m_DisplayFps = displayFps;
|
||||||
|
|
||||||
CVDisplayLinkCreateWithActiveCGDisplays(&m_DisplayLink);
|
CVDisplayLinkCreateWithActiveCGDisplays(&m_DisplayLink);
|
||||||
CVDisplayLinkSetOutputCallback(m_DisplayLink, displayLinkOutputCallback, this);
|
CVDisplayLinkSetOutputCallback(m_DisplayLink, displayLinkOutputCallback, this);
|
||||||
CVDisplayLinkStart(m_DisplayLink);
|
CVDisplayLinkStart(m_DisplayLink);
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ public:
|
|||||||
|
|
||||||
virtual ~DisplayLinkVsyncSource();
|
virtual ~DisplayLinkVsyncSource();
|
||||||
|
|
||||||
virtual bool initialize(SDL_Window* window);
|
virtual bool initialize(SDL_Window* window, int displayFps);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static
|
static
|
||||||
@@ -26,5 +26,6 @@ private:
|
|||||||
|
|
||||||
Pacer* m_Pacer;
|
Pacer* m_Pacer;
|
||||||
CVDisplayLinkRef m_DisplayLink;
|
CVDisplayLinkRef m_DisplayLink;
|
||||||
|
int m_DisplayFps;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -24,8 +24,10 @@ DxVsyncSource::~DxVsyncSource()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DxVsyncSource::initialize(SDL_Window* window)
|
bool DxVsyncSource::initialize(SDL_Window* window, int displayFps)
|
||||||
{
|
{
|
||||||
|
m_DisplayFps = displayFps;
|
||||||
|
|
||||||
m_Gdi32Handle = LoadLibraryA("gdi32.dll");
|
m_Gdi32Handle = LoadLibraryA("gdi32.dll");
|
||||||
if (m_Gdi32Handle == nullptr) {
|
if (m_Gdi32Handle == nullptr) {
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
@@ -144,8 +146,6 @@ int DxVsyncSource::vsyncThread(void* context)
|
|||||||
waitForVblankEventParams.hDevice = 0;
|
waitForVblankEventParams.hDevice = 0;
|
||||||
waitForVblankEventParams.VidPnSourceId = openAdapterParams.VidPnSourceId;
|
waitForVblankEventParams.VidPnSourceId = openAdapterParams.VidPnSourceId;
|
||||||
|
|
||||||
me->m_Pacer->vsyncCallback();
|
|
||||||
|
|
||||||
status = me->m_D3DKMTWaitForVerticalBlankEvent(&waitForVblankEventParams);
|
status = me->m_D3DKMTWaitForVerticalBlankEvent(&waitForVblankEventParams);
|
||||||
if (status != STATUS_SUCCESS) {
|
if (status != STATUS_SUCCESS) {
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
@@ -154,6 +154,8 @@ int DxVsyncSource::vsyncThread(void* context)
|
|||||||
SDL_Delay(10);
|
SDL_Delay(10);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
me->m_Pacer->vsyncCallback(1000 / me->m_DisplayFps);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (openAdapterParams.hAdapter != 0) {
|
if (openAdapterParams.hAdapter != 0) {
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ public:
|
|||||||
|
|
||||||
virtual ~DxVsyncSource();
|
virtual ~DxVsyncSource();
|
||||||
|
|
||||||
virtual bool initialize(SDL_Window* window);
|
virtual bool initialize(SDL_Window* window, int displayFps);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static int vsyncThread(void* context);
|
static int vsyncThread(void* context);
|
||||||
@@ -45,6 +45,7 @@ private:
|
|||||||
SDL_atomic_t m_Stopping;
|
SDL_atomic_t m_Stopping;
|
||||||
HMODULE m_Gdi32Handle;
|
HMODULE m_Gdi32Handle;
|
||||||
HWND m_Window;
|
HWND m_Window;
|
||||||
|
int m_DisplayFps;
|
||||||
|
|
||||||
PFND3DKMTOPENADAPTERFROMHDC m_D3DKMTOpenAdapterFromHdc;
|
PFND3DKMTOPENADAPTERFROMHDC m_D3DKMTOpenAdapterFromHdc;
|
||||||
PFND3DKMTCLOSEADAPTER m_D3DKMTCloseAdapter;
|
PFND3DKMTCLOSEADAPTER m_D3DKMTCloseAdapter;
|
||||||
|
|||||||
@@ -15,8 +15,9 @@ NullThreadedVsyncSource::~NullThreadedVsyncSource()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NullThreadedVsyncSource::initialize(SDL_Window*)
|
bool NullThreadedVsyncSource::initialize(SDL_Window*, int displayFps)
|
||||||
{
|
{
|
||||||
|
m_DisplayFps = displayFps;
|
||||||
m_Thread = SDL_CreateThread(vsyncThread, "Null Vsync Thread", this);
|
m_Thread = SDL_CreateThread(vsyncThread, "Null Vsync Thread", this);
|
||||||
if (m_Thread == nullptr) {
|
if (m_Thread == nullptr) {
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
@@ -35,7 +36,7 @@ int NullThreadedVsyncSource::vsyncThread(void* context)
|
|||||||
SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH);
|
SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH);
|
||||||
|
|
||||||
while (SDL_AtomicGet(&me->m_Stopping) == 0) {
|
while (SDL_AtomicGet(&me->m_Stopping) == 0) {
|
||||||
me->m_Pacer->vsyncCallback();
|
me->m_Pacer->vsyncCallback(1000 / me->m_DisplayFps);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ public:
|
|||||||
|
|
||||||
virtual ~NullThreadedVsyncSource();
|
virtual ~NullThreadedVsyncSource();
|
||||||
|
|
||||||
virtual bool initialize(SDL_Window* window);
|
virtual bool initialize(SDL_Window* window, int displayFps);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static int vsyncThread(void* context);
|
static int vsyncThread(void* context);
|
||||||
@@ -17,4 +17,5 @@ private:
|
|||||||
Pacer* m_Pacer;
|
Pacer* m_Pacer;
|
||||||
SDL_Thread* m_Thread;
|
SDL_Thread* m_Thread;
|
||||||
SDL_atomic_t m_Stopping;
|
SDL_atomic_t m_Stopping;
|
||||||
|
int m_DisplayFps;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#include "pacer.h"
|
#include "pacer.h"
|
||||||
|
|
||||||
|
#include "nullthreadedvsyncsource.h"
|
||||||
|
|
||||||
#ifdef Q_OS_DARWIN
|
#ifdef Q_OS_DARWIN
|
||||||
#include "displaylinkvsyncsource.h"
|
#include "displaylinkvsyncsource.h"
|
||||||
#endif
|
#endif
|
||||||
@@ -10,6 +12,13 @@
|
|||||||
|
|
||||||
#define FRAME_HISTORY_ENTRIES 8
|
#define FRAME_HISTORY_ENTRIES 8
|
||||||
|
|
||||||
|
// We may be woken up slightly late so don't go all the way
|
||||||
|
// up to the next V-sync since we may accidentally step into
|
||||||
|
// the next V-sync period. It also takes some amount of time
|
||||||
|
// to do the render itself, so we can't render right before
|
||||||
|
// V-sync happens.
|
||||||
|
#define TIMER_SLACK_MS 3
|
||||||
|
|
||||||
Pacer::Pacer(IFFmpegRenderer* renderer) :
|
Pacer::Pacer(IFFmpegRenderer* renderer) :
|
||||||
m_FrameQueueLock(0),
|
m_FrameQueueLock(0),
|
||||||
m_VsyncSource(nullptr),
|
m_VsyncSource(nullptr),
|
||||||
@@ -34,11 +43,15 @@ Pacer::~Pacer()
|
|||||||
|
|
||||||
// Called in an arbitrary thread by the IVsyncSource on V-sync
|
// Called in an arbitrary thread by the IVsyncSource on V-sync
|
||||||
// or an event synchronized with V-sync
|
// or an event synchronized with V-sync
|
||||||
void Pacer::vsyncCallback()
|
void Pacer::vsyncCallback(int timeUntilNextVsyncMillis)
|
||||||
{
|
{
|
||||||
// Make sure initialize() has been called
|
// Make sure initialize() has been called
|
||||||
SDL_assert(m_MaxVideoFps != 0);
|
SDL_assert(m_MaxVideoFps != 0);
|
||||||
|
|
||||||
|
SDL_assert(timeUntilNextVsyncMillis >= TIMER_SLACK_MS);
|
||||||
|
|
||||||
|
Uint32 vsyncCallbackStartTime = SDL_GetTicks();
|
||||||
|
|
||||||
SDL_AtomicLock(&m_FrameQueueLock);
|
SDL_AtomicLock(&m_FrameQueueLock);
|
||||||
|
|
||||||
// If the queue length history entries are large, be strict
|
// If the queue length history entries are large, be strict
|
||||||
@@ -71,13 +84,27 @@ void Pacer::vsyncCallback()
|
|||||||
av_frame_free(&frame);
|
av_frame_free(&frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (m_FrameQueue.isEmpty()) {
|
if (m_FrameQueue.isEmpty()) {
|
||||||
SDL_AtomicUnlock(&m_FrameQueueLock);
|
SDL_AtomicUnlock(&m_FrameQueueLock);
|
||||||
|
|
||||||
|
while (!SDL_TICKS_PASSED(SDL_GetTicks(),
|
||||||
|
vsyncCallbackStartTime + timeUntilNextVsyncMillis - TIMER_SLACK_MS)) {
|
||||||
|
SDL_Delay(1);
|
||||||
|
|
||||||
|
SDL_AtomicLock(&m_FrameQueueLock);
|
||||||
|
if (!m_FrameQueue.isEmpty()) {
|
||||||
|
// Don't release the lock
|
||||||
|
goto RenderNextFrame;
|
||||||
|
}
|
||||||
|
SDL_AtomicUnlock(&m_FrameQueueLock);
|
||||||
|
}
|
||||||
|
|
||||||
// Nothing to render at this time
|
// Nothing to render at this time
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RenderNextFrame:
|
||||||
// Grab the first frame
|
// Grab the first frame
|
||||||
AVFrame* frame = m_FrameQueue.dequeue();
|
AVFrame* frame = m_FrameQueue.dequeue();
|
||||||
SDL_AtomicUnlock(&m_FrameQueueLock);
|
SDL_AtomicUnlock(&m_FrameQueueLock);
|
||||||
@@ -122,7 +149,7 @@ bool Pacer::initialize(SDL_Window* window, int maxVideoFps)
|
|||||||
// immediately like they used to.
|
// immediately like they used to.
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (m_VsyncSource != nullptr && !m_VsyncSource->initialize(window)) {
|
if (m_VsyncSource != nullptr && !m_VsyncSource->initialize(window, m_DisplayFps)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
class IVsyncSource {
|
class IVsyncSource {
|
||||||
public:
|
public:
|
||||||
virtual ~IVsyncSource() {}
|
virtual ~IVsyncSource() {}
|
||||||
virtual bool initialize(SDL_Window* window) = 0;
|
virtual bool initialize(SDL_Window* window, int displayFps) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Pacer
|
class Pacer
|
||||||
@@ -21,7 +21,7 @@ public:
|
|||||||
|
|
||||||
bool initialize(SDL_Window* window, int maxVideoFps);
|
bool initialize(SDL_Window* window, int maxVideoFps);
|
||||||
|
|
||||||
void vsyncCallback();
|
void vsyncCallback(int timeUntilNextVsyncMillis);
|
||||||
|
|
||||||
bool isUsingFrameQueue();
|
bool isUsingFrameQueue();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user