mirror of
https://github.com/moonlight-stream/moonlight-qt.git
synced 2025-07-18 10:32:38 +00:00
Use a separate render thread even with a V-Sync source
This commit is contained in:
parent
81c6202582
commit
f03d12b677
@ -8,10 +8,13 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef Q_OS_WIN32
|
#ifdef Q_OS_WIN32
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <VersionHelpers.h>
|
||||||
#include "dxvsyncsource.h"
|
#include "dxvsyncsource.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define FRAME_HISTORY_ENTRIES 8
|
#define PACING_HISTORY_ENTRIES 8
|
||||||
#define RENDER_TIME_HISTORY_ENTRIES 20
|
#define RENDER_TIME_HISTORY_ENTRIES 20
|
||||||
|
|
||||||
// We may be woken up slightly late so don't go all the way
|
// We may be woken up slightly late so don't go all the way
|
||||||
@ -42,13 +45,17 @@ Pacer::~Pacer()
|
|||||||
// Stop the render thread
|
// Stop the render thread
|
||||||
m_Stopping = true;
|
m_Stopping = true;
|
||||||
if (m_RenderThread != nullptr) {
|
if (m_RenderThread != nullptr) {
|
||||||
m_FrameQueueNotEmpty.wakeAll();
|
m_RenderQueueNotEmpty.wakeAll();
|
||||||
SDL_WaitThread(m_RenderThread, nullptr);
|
SDL_WaitThread(m_RenderThread, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete any remaining unconsumed frames
|
// Delete any remaining unconsumed frames
|
||||||
while (!m_FrameQueue.isEmpty()) {
|
while (!m_RenderQueue.isEmpty()) {
|
||||||
AVFrame* frame = m_FrameQueue.dequeue();
|
AVFrame* frame = m_RenderQueue.dequeue();
|
||||||
|
av_frame_free(&frame);
|
||||||
|
}
|
||||||
|
while (!m_PacingQueue.isEmpty()) {
|
||||||
|
AVFrame* frame = m_PacingQueue.dequeue();
|
||||||
av_frame_free(&frame);
|
av_frame_free(&frame);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -63,8 +70,8 @@ int Pacer::renderThread(void* context)
|
|||||||
me->m_FrameQueueLock.lock();
|
me->m_FrameQueueLock.lock();
|
||||||
|
|
||||||
// Wait for a frame to be ready to render
|
// Wait for a frame to be ready to render
|
||||||
while (!me->m_Stopping && me->m_FrameQueue.isEmpty()) {
|
while (!me->m_Stopping && me->m_RenderQueue.isEmpty()) {
|
||||||
me->m_FrameQueueNotEmpty.wait(&me->m_FrameQueueLock);
|
me->m_RenderQueueNotEmpty.wait(&me->m_FrameQueueLock);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (me->m_Stopping) {
|
if (me->m_Stopping) {
|
||||||
@ -76,7 +83,7 @@ int Pacer::renderThread(void* context)
|
|||||||
// Dequeue the most recent frame for rendering and free the others.
|
// Dequeue the most recent frame for rendering and free the others.
|
||||||
// NB: m_FrameQueueLock still held here!
|
// NB: m_FrameQueueLock still held here!
|
||||||
AVFrame* lastFrame = nullptr;
|
AVFrame* lastFrame = nullptr;
|
||||||
while (!me->m_FrameQueue.isEmpty()) {
|
while (!me->m_RenderQueue.isEmpty()) {
|
||||||
if (lastFrame != nullptr) {
|
if (lastFrame != nullptr) {
|
||||||
// Don't hold the frame queue lock across av_frame_free(),
|
// Don't hold the frame queue lock across av_frame_free(),
|
||||||
// since it could need to talk to the GPU driver. This is safe
|
// since it could need to talk to the GPU driver. This is safe
|
||||||
@ -88,7 +95,7 @@ int Pacer::renderThread(void* context)
|
|||||||
me->m_FrameQueueLock.lock();
|
me->m_FrameQueueLock.lock();
|
||||||
}
|
}
|
||||||
|
|
||||||
lastFrame = me->m_FrameQueue.dequeue();
|
lastFrame = me->m_RenderQueue.dequeue();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Release the frame queue lock before rendering
|
// Release the frame queue lock before rendering
|
||||||
@ -120,7 +127,7 @@ void Pacer::vsyncCallback(int timeUntilNextVsyncMillis)
|
|||||||
// frame history to drop frames only if consistently above the
|
// frame history to drop frames only if consistently above the
|
||||||
// one queued frame mark.
|
// one queued frame mark.
|
||||||
if (m_MaxVideoFps >= m_DisplayFps) {
|
if (m_MaxVideoFps >= m_DisplayFps) {
|
||||||
for (int queueHistoryEntry : m_FrameQueueHistory) {
|
for (int queueHistoryEntry : m_PacingQueueHistory) {
|
||||||
if (queueHistoryEntry <= 1) {
|
if (queueHistoryEntry <= 1) {
|
||||||
// Be lenient as long as the queue length
|
// Be lenient as long as the queue length
|
||||||
// resolves before the end of frame history
|
// resolves before the end of frame history
|
||||||
@ -129,16 +136,16 @@ void Pacer::vsyncCallback(int timeUntilNextVsyncMillis)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_FrameQueueHistory.count() == FRAME_HISTORY_ENTRIES) {
|
if (m_PacingQueueHistory.count() == PACING_HISTORY_ENTRIES) {
|
||||||
m_FrameQueueHistory.dequeue();
|
m_PacingQueueHistory.dequeue();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_FrameQueueHistory.enqueue(m_FrameQueue.count());
|
m_PacingQueueHistory.enqueue(m_PacingQueue.count());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Catch up if we're several frames ahead
|
// Catch up if we're several frames ahead
|
||||||
while (m_FrameQueue.count() > frameDropTarget) {
|
while (m_PacingQueue.count() > frameDropTarget) {
|
||||||
AVFrame* frame = m_FrameQueue.dequeue();
|
AVFrame* frame = m_PacingQueue.dequeue();
|
||||||
|
|
||||||
// Drop the lock while we call av_frame_free()
|
// Drop the lock while we call av_frame_free()
|
||||||
m_FrameQueueLock.unlock();
|
m_FrameQueueLock.unlock();
|
||||||
@ -147,9 +154,9 @@ void Pacer::vsyncCallback(int timeUntilNextVsyncMillis)
|
|||||||
m_FrameQueueLock.lock();
|
m_FrameQueueLock.lock();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_FrameQueue.isEmpty()) {
|
if (m_PacingQueue.isEmpty()) {
|
||||||
// Wait for a frame to arrive or our V-sync timeout to expire
|
// Wait for a frame to arrive or our V-sync timeout to expire
|
||||||
if (!m_FrameQueueNotEmpty.wait(&m_FrameQueueLock, timeUntilNextVsyncMillis - TIMER_SLACK_MS)) {
|
if (!m_PacingQueueNotEmpty.wait(&m_FrameQueueLock, timeUntilNextVsyncMillis - TIMER_SLACK_MS)) {
|
||||||
// Wait timed out - unlock and bail
|
// Wait timed out - unlock and bail
|
||||||
m_FrameQueueLock.unlock();
|
m_FrameQueueLock.unlock();
|
||||||
|
|
||||||
@ -159,11 +166,10 @@ void Pacer::vsyncCallback(int timeUntilNextVsyncMillis)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Grab the first frame
|
// Place the first frame on the render queue
|
||||||
AVFrame* frame = m_FrameQueue.dequeue();
|
m_RenderQueue.enqueue(m_PacingQueue.dequeue());
|
||||||
m_FrameQueueLock.unlock();
|
m_FrameQueueLock.unlock();
|
||||||
|
m_RenderQueueNotEmpty.wakeOne();
|
||||||
renderFrame(frame);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Pacer::initialize(SDL_Window* window, int maxVideoFps, bool enablePacing)
|
bool Pacer::initialize(SDL_Window* window, int maxVideoFps, bool enablePacing)
|
||||||
@ -179,7 +185,11 @@ bool Pacer::initialize(SDL_Window* window, int maxVideoFps, bool enablePacing)
|
|||||||
#if defined(Q_OS_DARWIN)
|
#if defined(Q_OS_DARWIN)
|
||||||
m_VsyncSource = DisplayLinkVsyncSourceFactory::createVsyncSource(this);
|
m_VsyncSource = DisplayLinkVsyncSourceFactory::createVsyncSource(this);
|
||||||
#elif defined(Q_OS_WIN32)
|
#elif defined(Q_OS_WIN32)
|
||||||
|
// Don't use D3DKMTWaitForVerticalBlankEvent() on Windows 7, because
|
||||||
|
// it blocks during other concurrent DX operations (like actually rendering).
|
||||||
|
if (IsWindows8OrGreater()) {
|
||||||
m_VsyncSource = new DxVsyncSource(this);
|
m_VsyncSource = new DxVsyncSource(this);
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
// Platforms without a VsyncSource will just render frames
|
// Platforms without a VsyncSource will just render frames
|
||||||
// immediately like they used to.
|
// immediately like they used to.
|
||||||
@ -195,9 +205,7 @@ bool Pacer::initialize(SDL_Window* window, int maxVideoFps, bool enablePacing)
|
|||||||
m_DisplayFps, m_MaxVideoFps);
|
m_DisplayFps, m_MaxVideoFps);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_VsyncSource == nullptr) {
|
|
||||||
m_RenderThread = SDL_CreateThread(Pacer::renderThread, "Pacer Render Thread", this);
|
m_RenderThread = SDL_CreateThread(Pacer::renderThread, "Pacer Render Thread", this);
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -261,7 +269,17 @@ void Pacer::submitFrame(AVFrame* frame)
|
|||||||
|
|
||||||
// Queue the frame and possibly wake up the render thread
|
// Queue the frame and possibly wake up the render thread
|
||||||
m_FrameQueueLock.lock();
|
m_FrameQueueLock.lock();
|
||||||
m_FrameQueue.enqueue(frame);
|
if (m_VsyncSource != nullptr) {
|
||||||
m_FrameQueueLock.unlock();
|
m_PacingQueue.enqueue(frame);
|
||||||
m_FrameQueueNotEmpty.wakeOne();
|
}
|
||||||
|
else {
|
||||||
|
m_RenderQueue.enqueue(frame);
|
||||||
|
}
|
||||||
|
m_FrameQueueLock.unlock();
|
||||||
|
if (m_VsyncSource != nullptr) {
|
||||||
|
m_PacingQueueNotEmpty.wakeOne();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_RenderQueueNotEmpty.wakeOne();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,11 +33,13 @@ private:
|
|||||||
|
|
||||||
void renderFrame(AVFrame* frame);
|
void renderFrame(AVFrame* frame);
|
||||||
|
|
||||||
QQueue<AVFrame*> m_FrameQueue;
|
QQueue<AVFrame*> m_RenderQueue;
|
||||||
QQueue<int> m_FrameQueueHistory;
|
QQueue<AVFrame*> m_PacingQueue;
|
||||||
|
QQueue<int> m_PacingQueueHistory;
|
||||||
QQueue<int> m_RenderTimeHistory;
|
QQueue<int> m_RenderTimeHistory;
|
||||||
QMutex m_FrameQueueLock;
|
QMutex m_FrameQueueLock;
|
||||||
QWaitCondition m_FrameQueueNotEmpty;
|
QWaitCondition m_RenderQueueNotEmpty;
|
||||||
|
QWaitCondition m_PacingQueueNotEmpty;
|
||||||
SDL_Thread* m_RenderThread;
|
SDL_Thread* m_RenderThread;
|
||||||
bool m_Stopping;
|
bool m_Stopping;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user