mirror of
https://github.com/moonlight-stream/moonlight-qt
synced 2024-12-16 22:23:07 +00:00
Refactor Pacer to handle both blocking and non-blocking VsyncSources
This commit is contained in:
parent
8e3e19a7f7
commit
6ae6218043
6 changed files with 170 additions and 129 deletions
|
@ -6,17 +6,18 @@
|
||||||
|
|
||||||
DxVsyncSource::DxVsyncSource(Pacer* pacer) :
|
DxVsyncSource::DxVsyncSource(Pacer* pacer) :
|
||||||
m_Pacer(pacer),
|
m_Pacer(pacer),
|
||||||
m_Thread(nullptr),
|
m_Gdi32Handle(nullptr),
|
||||||
m_Gdi32Handle(nullptr)
|
m_LastMonitor(nullptr)
|
||||||
{
|
{
|
||||||
SDL_AtomicSet(&m_Stopping, 0);
|
SDL_zero(m_WaitForVblankEventParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
DxVsyncSource::~DxVsyncSource()
|
DxVsyncSource::~DxVsyncSource()
|
||||||
{
|
{
|
||||||
if (m_Thread != nullptr) {
|
if (m_WaitForVblankEventParams.hAdapter != 0) {
|
||||||
SDL_AtomicSet(&m_Stopping, 1);
|
D3DKMT_CLOSEADAPTER closeAdapterParams = {};
|
||||||
SDL_WaitThread(m_Thread, nullptr);
|
closeAdapterParams.hAdapter = m_WaitForVblankEventParams.hAdapter;
|
||||||
|
m_D3DKMTCloseAdapter(&closeAdapterParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_Gdi32Handle != nullptr) {
|
if (m_Gdi32Handle != nullptr) {
|
||||||
|
@ -24,10 +25,8 @@ DxVsyncSource::~DxVsyncSource()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DxVsyncSource::initialize(SDL_Window* window, int displayFps)
|
bool DxVsyncSource::initialize(SDL_Window* window, int)
|
||||||
{
|
{
|
||||||
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,
|
||||||
|
@ -59,114 +58,89 @@ bool DxVsyncSource::initialize(SDL_Window* window, int displayFps)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_Window = info.info.win.window;
|
// Pacer should only create us on Win32
|
||||||
|
SDL_assert(info.subsystem == SDL_SYSWM_WINDOWS);
|
||||||
|
|
||||||
m_Thread = SDL_CreateThread(vsyncThread, "DXVsync", this);
|
m_Window = info.info.win.window;
|
||||||
if (m_Thread == nullptr) {
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
|
||||||
"Unable to create DX V-sync thread: %s",
|
|
||||||
SDL_GetError());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int DxVsyncSource::vsyncThread(void* context)
|
bool DxVsyncSource::isAsync()
|
||||||
{
|
{
|
||||||
DxVsyncSource* me = reinterpret_cast<DxVsyncSource*>(context);
|
// We wait in the context of the Pacer thread
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
#if SDL_VERSION_ATLEAST(2, 0, 9)
|
void DxVsyncSource::waitForVsync()
|
||||||
SDL_SetThreadPriority(SDL_THREAD_PRIORITY_TIME_CRITICAL);
|
{
|
||||||
#else
|
NTSTATUS status;
|
||||||
SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
D3DKMT_OPENADAPTERFROMHDC openAdapterParams = {};
|
// If the monitor has changed from last time, open the new adapter
|
||||||
HMONITOR lastMonitor = nullptr;
|
HMONITOR currentMonitor = MonitorFromWindow(m_Window, MONITOR_DEFAULTTONEAREST);
|
||||||
DEVMODEA monitorMode;
|
if (currentMonitor != m_LastMonitor) {
|
||||||
monitorMode.dmSize = sizeof(monitorMode);
|
MONITORINFOEXA monitorInfo = {};
|
||||||
|
monitorInfo.cbSize = sizeof(monitorInfo);
|
||||||
while (SDL_AtomicGet(&me->m_Stopping) == 0) {
|
if (!GetMonitorInfoA(currentMonitor, &monitorInfo)) {
|
||||||
D3DKMT_WAITFORVERTICALBLANKEVENT waitForVblankEventParams;
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
NTSTATUS status;
|
"GetMonitorInfo() failed: %d",
|
||||||
|
GetLastError());
|
||||||
// If the monitor has changed from last time, open the new adapter
|
return;
|
||||||
HMONITOR currentMonitor = MonitorFromWindow(me->m_Window, MONITOR_DEFAULTTONEAREST);
|
|
||||||
if (currentMonitor != lastMonitor) {
|
|
||||||
MONITORINFOEXA monitorInfo = {};
|
|
||||||
monitorInfo.cbSize = sizeof(monitorInfo);
|
|
||||||
if (!GetMonitorInfoA(currentMonitor, &monitorInfo)) {
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
|
||||||
"GetMonitorInfo() failed: %d",
|
|
||||||
GetLastError());
|
|
||||||
SDL_Delay(10);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!EnumDisplaySettingsA(monitorInfo.szDevice, ENUM_CURRENT_SETTINGS, &monitorMode)) {
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
|
||||||
"EnumDisplaySettings() failed: %d",
|
|
||||||
GetLastError());
|
|
||||||
SDL_Delay(10);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
|
|
||||||
"Monitor changed: %s %d Hz",
|
|
||||||
monitorInfo.szDevice,
|
|
||||||
monitorMode.dmDisplayFrequency);
|
|
||||||
|
|
||||||
if (openAdapterParams.hAdapter != 0) {
|
|
||||||
D3DKMT_CLOSEADAPTER closeAdapterParams = {};
|
|
||||||
closeAdapterParams.hAdapter = openAdapterParams.hAdapter;
|
|
||||||
me->m_D3DKMTCloseAdapter(&closeAdapterParams);
|
|
||||||
}
|
|
||||||
|
|
||||||
openAdapterParams.hDc = CreateDCA(nullptr, monitorInfo.szDevice, nullptr, nullptr);
|
|
||||||
if (!openAdapterParams.hDc) {
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
|
||||||
"CreateDC() failed: %d",
|
|
||||||
GetLastError());
|
|
||||||
SDL_Delay(10);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
status = me->m_D3DKMTOpenAdapterFromHdc(&openAdapterParams);
|
|
||||||
DeleteDC(openAdapterParams.hDc);
|
|
||||||
|
|
||||||
if (status != STATUS_SUCCESS) {
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
|
||||||
"D3DKMTOpenAdapterFromHdc() failed: %x",
|
|
||||||
status);
|
|
||||||
SDL_Delay(10);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
lastMonitor = currentMonitor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
waitForVblankEventParams.hAdapter = openAdapterParams.hAdapter;
|
DEVMODEA monitorMode;
|
||||||
waitForVblankEventParams.hDevice = 0;
|
monitorMode.dmSize = sizeof(monitorMode);
|
||||||
waitForVblankEventParams.VidPnSourceId = openAdapterParams.VidPnSourceId;
|
if (!EnumDisplaySettingsA(monitorInfo.szDevice, ENUM_CURRENT_SETTINGS, &monitorMode)) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
|
"EnumDisplaySettings() failed: %d",
|
||||||
|
GetLastError());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
|
"Monitor changed: %s %d Hz",
|
||||||
|
monitorInfo.szDevice,
|
||||||
|
monitorMode.dmDisplayFrequency);
|
||||||
|
|
||||||
|
// Close the old adapter
|
||||||
|
if (m_WaitForVblankEventParams.hAdapter != 0) {
|
||||||
|
D3DKMT_CLOSEADAPTER closeAdapterParams = {};
|
||||||
|
closeAdapterParams.hAdapter = m_WaitForVblankEventParams.hAdapter;
|
||||||
|
m_D3DKMTCloseAdapter(&closeAdapterParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
D3DKMT_OPENADAPTERFROMHDC openAdapterParams = {};
|
||||||
|
openAdapterParams.hDc = CreateDCA(nullptr, monitorInfo.szDevice, nullptr, nullptr);
|
||||||
|
if (!openAdapterParams.hDc) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
|
"CreateDC() failed: %d",
|
||||||
|
GetLastError());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open the new adapter
|
||||||
|
status = m_D3DKMTOpenAdapterFromHdc(&openAdapterParams);
|
||||||
|
DeleteDC(openAdapterParams.hDc);
|
||||||
|
|
||||||
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,
|
||||||
"D3DKMTWaitForVerticalBlankEvent() failed: %x",
|
"D3DKMTOpenAdapterFromHdc() failed: %x",
|
||||||
status);
|
status);
|
||||||
SDL_Delay(10);
|
return;
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
me->m_Pacer->vsyncCallback(1000 / me->m_DisplayFps);
|
m_WaitForVblankEventParams.hAdapter = openAdapterParams.hAdapter;
|
||||||
|
m_WaitForVblankEventParams.hDevice = 0;
|
||||||
|
m_WaitForVblankEventParams.VidPnSourceId = openAdapterParams.VidPnSourceId;
|
||||||
|
|
||||||
|
m_LastMonitor = currentMonitor;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (openAdapterParams.hAdapter != 0) {
|
status = m_D3DKMTWaitForVerticalBlankEvent(&m_WaitForVblankEventParams);
|
||||||
D3DKMT_CLOSEADAPTER closeAdapterParams = {};
|
if (status != STATUS_SUCCESS) {
|
||||||
closeAdapterParams.hAdapter = openAdapterParams.hAdapter;
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
me->m_D3DKMTCloseAdapter(&closeAdapterParams);
|
"D3DKMTWaitForVerticalBlankEvent() failed: %x",
|
||||||
|
status);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,17 +35,18 @@ public:
|
||||||
|
|
||||||
virtual ~DxVsyncSource();
|
virtual ~DxVsyncSource();
|
||||||
|
|
||||||
virtual bool initialize(SDL_Window* window, int displayFps);
|
virtual bool initialize(SDL_Window* window, int) override;
|
||||||
|
|
||||||
|
virtual bool isAsync() override;
|
||||||
|
|
||||||
|
virtual void waitForVsync() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static int vsyncThread(void* context);
|
|
||||||
|
|
||||||
Pacer* m_Pacer;
|
Pacer* m_Pacer;
|
||||||
SDL_Thread* m_Thread;
|
|
||||||
SDL_atomic_t m_Stopping;
|
|
||||||
HMODULE m_Gdi32Handle;
|
HMODULE m_Gdi32Handle;
|
||||||
HWND m_Window;
|
HWND m_Window;
|
||||||
int m_DisplayFps;
|
HMONITOR m_LastMonitor;
|
||||||
|
D3DKMT_WAITFORVERTICALBLANKEVENT m_WaitForVblankEventParams;
|
||||||
|
|
||||||
PFND3DKMTOPENADAPTERFROMHDC m_D3DKMTOpenAdapterFromHdc;
|
PFND3DKMTOPENADAPTERFROMHDC m_D3DKMTOpenAdapterFromHdc;
|
||||||
PFND3DKMTCLOSEADAPTER m_D3DKMTCloseAdapter;
|
PFND3DKMTCLOSEADAPTER m_D3DKMTCloseAdapter;
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
|
|
||||||
Pacer::Pacer(IFFmpegRenderer* renderer, PVIDEO_STATS videoStats) :
|
Pacer::Pacer(IFFmpegRenderer* renderer, PVIDEO_STATS videoStats) :
|
||||||
m_RenderThread(nullptr),
|
m_RenderThread(nullptr),
|
||||||
|
m_VsyncThread(nullptr),
|
||||||
m_Stopping(false),
|
m_Stopping(false),
|
||||||
m_VsyncSource(nullptr),
|
m_VsyncSource(nullptr),
|
||||||
m_VsyncRenderer(renderer),
|
m_VsyncRenderer(renderer),
|
||||||
|
@ -42,12 +43,19 @@ Pacer::Pacer(IFFmpegRenderer* renderer, PVIDEO_STATS videoStats) :
|
||||||
|
|
||||||
Pacer::~Pacer()
|
Pacer::~Pacer()
|
||||||
{
|
{
|
||||||
|
m_Stopping = true;
|
||||||
|
|
||||||
|
// Stop the V-sync thread
|
||||||
|
if (m_VsyncThread != nullptr) {
|
||||||
|
m_VsyncSignalled.wakeAll();
|
||||||
|
SDL_WaitThread(m_VsyncThread, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
// Stop V-sync callbacks
|
// Stop V-sync callbacks
|
||||||
delete m_VsyncSource;
|
delete m_VsyncSource;
|
||||||
m_VsyncSource = nullptr;
|
m_VsyncSource = nullptr;
|
||||||
|
|
||||||
// Stop the render thread
|
// Stop the render thread
|
||||||
m_Stopping = true;
|
|
||||||
if (m_RenderThread != nullptr) {
|
if (m_RenderThread != nullptr) {
|
||||||
m_RenderQueueNotEmpty.wakeAll();
|
m_RenderQueueNotEmpty.wakeAll();
|
||||||
SDL_WaitThread(m_RenderThread, nullptr);
|
SDL_WaitThread(m_RenderThread, nullptr);
|
||||||
|
@ -89,6 +97,39 @@ void Pacer::renderOnMainThread()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Pacer::vsyncThread(void *context)
|
||||||
|
{
|
||||||
|
Pacer* me = reinterpret_cast<Pacer*>(context);
|
||||||
|
|
||||||
|
#if SDL_VERSION_ATLEAST(2, 0, 9)
|
||||||
|
SDL_SetThreadPriority(SDL_THREAD_PRIORITY_TIME_CRITICAL);
|
||||||
|
#else
|
||||||
|
SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool async = me->m_VsyncSource->isAsync();
|
||||||
|
while (!me->m_Stopping) {
|
||||||
|
if (async) {
|
||||||
|
// Wait for the VSync source to invoke signalVsync() or 100ms to elapse
|
||||||
|
me->m_FrameQueueLock.lock();
|
||||||
|
me->m_VsyncSignalled.wait(&me->m_FrameQueueLock, 100);
|
||||||
|
me->m_FrameQueueLock.unlock();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Let the VSync source wait in the context of our thread
|
||||||
|
me->m_VsyncSource->waitForVsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (me->m_Stopping) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
me->handleVsync(1000 / me->m_DisplayFps);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int Pacer::renderThread(void* context)
|
int Pacer::renderThread(void* context)
|
||||||
{
|
{
|
||||||
Pacer* me = reinterpret_cast<Pacer*>(context);
|
Pacer* me = reinterpret_cast<Pacer*>(context);
|
||||||
|
@ -153,7 +194,7 @@ void Pacer::enqueueFrameForRenderingAndUnlock(AVFrame *frame)
|
||||||
|
|
||||||
// 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(int timeUntilNextVsyncMillis)
|
void Pacer::handleVsync(int timeUntilNextVsyncMillis)
|
||||||
{
|
{
|
||||||
// Make sure initialize() has been called
|
// Make sure initialize() has been called
|
||||||
SDL_assert(m_MaxVideoFps != 0);
|
SDL_assert(m_MaxVideoFps != 0);
|
||||||
|
@ -217,7 +258,7 @@ bool Pacer::initialize(SDL_Window* window, int maxVideoFps, bool enablePacing)
|
||||||
|
|
||||||
if (enablePacing) {
|
if (enablePacing) {
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
"Frame pacing active: target %d Hz with %d FPS stream",
|
"Frame pacing: target %d Hz with %d FPS stream",
|
||||||
m_DisplayFps, m_MaxVideoFps);
|
m_DisplayFps, m_MaxVideoFps);
|
||||||
|
|
||||||
SDL_SysWMinfo info;
|
SDL_SysWMinfo info;
|
||||||
|
@ -255,7 +296,10 @@ bool Pacer::initialize(SDL_Window* window, int maxVideoFps, bool enablePacing)
|
||||||
SDL_assert(m_VsyncSource != nullptr || !(m_RendererAttributes & RENDERER_ATTRIBUTE_FORCE_PACING));
|
SDL_assert(m_VsyncSource != nullptr || !(m_RendererAttributes & RENDERER_ATTRIBUTE_FORCE_PACING));
|
||||||
|
|
||||||
if (m_VsyncSource != nullptr && !m_VsyncSource->initialize(window, m_DisplayFps)) {
|
if (m_VsyncSource != nullptr && !m_VsyncSource->initialize(window, m_DisplayFps)) {
|
||||||
return false;
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
|
"Vsync source failed to initialize. Frame pacing will not be available!");
|
||||||
|
delete m_VsyncSource;
|
||||||
|
m_VsyncSource = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -264,6 +308,10 @@ bool Pacer::initialize(SDL_Window* window, int maxVideoFps, bool enablePacing)
|
||||||
m_DisplayFps, m_MaxVideoFps);
|
m_DisplayFps, m_MaxVideoFps);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_VsyncSource != nullptr) {
|
||||||
|
m_VsyncThread = SDL_CreateThread(Pacer::vsyncThread, "PacerVsync", this);
|
||||||
|
}
|
||||||
|
|
||||||
if (m_VsyncRenderer->isRenderThreadSupported()) {
|
if (m_VsyncRenderer->isRenderThreadSupported()) {
|
||||||
m_RenderThread = SDL_CreateThread(Pacer::renderThread, "PacerRender", this);
|
m_RenderThread = SDL_CreateThread(Pacer::renderThread, "PacerRender", this);
|
||||||
}
|
}
|
||||||
|
@ -271,6 +319,11 @@ bool Pacer::initialize(SDL_Window* window, int maxVideoFps, bool enablePacing)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Pacer::signalVsync()
|
||||||
|
{
|
||||||
|
m_VsyncSignalled.wakeOne();
|
||||||
|
}
|
||||||
|
|
||||||
void Pacer::renderFrame(AVFrame* frame)
|
void Pacer::renderFrame(AVFrame* frame)
|
||||||
{
|
{
|
||||||
// Count time spent in Pacer's queues
|
// Count time spent in Pacer's queues
|
||||||
|
|
|
@ -11,6 +11,15 @@ class IVsyncSource {
|
||||||
public:
|
public:
|
||||||
virtual ~IVsyncSource() {}
|
virtual ~IVsyncSource() {}
|
||||||
virtual bool initialize(SDL_Window* window, int displayFps) = 0;
|
virtual bool initialize(SDL_Window* window, int displayFps) = 0;
|
||||||
|
|
||||||
|
// Asynchronous sources produce callbacks on their own, while synchronous
|
||||||
|
// sources require calls to waitForVsync().
|
||||||
|
virtual bool isAsync() = 0;
|
||||||
|
|
||||||
|
virtual void waitForVsync() {
|
||||||
|
// Synchronous sources must implement waitForVsync()!
|
||||||
|
SDL_assert(false);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class Pacer
|
class Pacer
|
||||||
|
@ -24,13 +33,17 @@ public:
|
||||||
|
|
||||||
bool initialize(SDL_Window* window, int maxVideoFps, bool enablePacing);
|
bool initialize(SDL_Window* window, int maxVideoFps, bool enablePacing);
|
||||||
|
|
||||||
void vsyncCallback(int timeUntilNextVsyncMillis);
|
void signalVsync();
|
||||||
|
|
||||||
void renderOnMainThread();
|
void renderOnMainThread();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
static int vsyncThread(void* context);
|
||||||
|
|
||||||
static int renderThread(void* context);
|
static int renderThread(void* context);
|
||||||
|
|
||||||
|
void handleVsync(int timeUntilNextVsyncMillis);
|
||||||
|
|
||||||
void enqueueFrameForRenderingAndUnlock(AVFrame* frame);
|
void enqueueFrameForRenderingAndUnlock(AVFrame* frame);
|
||||||
|
|
||||||
void renderFrame(AVFrame* frame);
|
void renderFrame(AVFrame* frame);
|
||||||
|
@ -44,7 +57,9 @@ private:
|
||||||
QMutex m_FrameQueueLock;
|
QMutex m_FrameQueueLock;
|
||||||
QWaitCondition m_RenderQueueNotEmpty;
|
QWaitCondition m_RenderQueueNotEmpty;
|
||||||
QWaitCondition m_PacingQueueNotEmpty;
|
QWaitCondition m_PacingQueueNotEmpty;
|
||||||
|
QWaitCondition m_VsyncSignalled;
|
||||||
SDL_Thread* m_RenderThread;
|
SDL_Thread* m_RenderThread;
|
||||||
|
SDL_Thread* m_VsyncThread;
|
||||||
bool m_Stopping;
|
bool m_Stopping;
|
||||||
|
|
||||||
IVsyncSource* m_VsyncSource;
|
IVsyncSource* m_VsyncSource;
|
||||||
|
|
|
@ -10,8 +10,7 @@ WaylandVsyncSource::WaylandVsyncSource(Pacer* pacer)
|
||||||
: m_Pacer(pacer),
|
: m_Pacer(pacer),
|
||||||
m_Display(nullptr),
|
m_Display(nullptr),
|
||||||
m_Surface(nullptr),
|
m_Surface(nullptr),
|
||||||
m_Callback(nullptr),
|
m_Callback(nullptr)
|
||||||
m_LastFrameTime(0)
|
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -24,7 +23,7 @@ WaylandVsyncSource::~WaylandVsyncSource()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WaylandVsyncSource::initialize(SDL_Window* window, int displayFps)
|
bool WaylandVsyncSource::initialize(SDL_Window* window, int)
|
||||||
{
|
{
|
||||||
SDL_SysWMinfo info;
|
SDL_SysWMinfo info;
|
||||||
|
|
||||||
|
@ -40,7 +39,6 @@ bool WaylandVsyncSource::initialize(SDL_Window* window, int displayFps)
|
||||||
// Pacer shoud not create us for non-Wayland windows
|
// Pacer shoud not create us for non-Wayland windows
|
||||||
SDL_assert(info.subsystem == SDL_SYSWM_WAYLAND);
|
SDL_assert(info.subsystem == SDL_SYSWM_WAYLAND);
|
||||||
|
|
||||||
m_DisplayFps = displayFps;
|
|
||||||
m_Display = info.info.wl.display;
|
m_Display = info.info.wl.display;
|
||||||
m_Surface = info.info.wl.surface;
|
m_Surface = info.info.wl.surface;
|
||||||
|
|
||||||
|
@ -52,7 +50,13 @@ bool WaylandVsyncSource::initialize(SDL_Window* window, int displayFps)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WaylandVsyncSource::frameDone(void* data, struct wl_callback* oldCb, uint32_t time)
|
bool WaylandVsyncSource::isAsync()
|
||||||
|
{
|
||||||
|
// Wayland frame callbacks are asynchronous
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WaylandVsyncSource::frameDone(void* data, struct wl_callback* oldCb, uint32_t)
|
||||||
{
|
{
|
||||||
auto me = (WaylandVsyncSource*)data;
|
auto me = (WaylandVsyncSource*)data;
|
||||||
|
|
||||||
|
@ -60,18 +64,12 @@ void WaylandVsyncSource::frameDone(void* data, struct wl_callback* oldCb, uint32
|
||||||
SDL_assert(oldCb == me->m_Callback);
|
SDL_assert(oldCb == me->m_Callback);
|
||||||
wl_callback_destroy(oldCb);
|
wl_callback_destroy(oldCb);
|
||||||
|
|
||||||
// Register for another callback before invoking Pacer to ensure we don't miss
|
// Wake the Pacer Vsync thread
|
||||||
// a frame callback if Pacer takes too long.
|
me->m_Pacer->signalVsync();
|
||||||
|
|
||||||
|
// Register for another callback
|
||||||
me->m_Callback = wl_surface_frame(me->m_Surface);
|
me->m_Callback = wl_surface_frame(me->m_Surface);
|
||||||
wl_callback_add_listener(me->m_Callback, &s_FrameListener, data);
|
wl_callback_add_listener(me->m_Callback, &s_FrameListener, data);
|
||||||
wl_surface_commit(me->m_Surface);
|
wl_surface_commit(me->m_Surface);
|
||||||
wl_display_flush(me->m_Display);
|
wl_display_flush(me->m_Display);
|
||||||
|
|
||||||
if (me->m_LastFrameTime != 0) {
|
|
||||||
// Assuming that the time until the next V-Sync will usually be equal to the
|
|
||||||
// time from last callback to this one but cap it at 2x the expected frame period.
|
|
||||||
me->m_Pacer->vsyncCallback(SDL_min(time - me->m_LastFrameTime, 2000 / me->m_DisplayFps));
|
|
||||||
}
|
|
||||||
|
|
||||||
me->m_LastFrameTime = time;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,9 @@ public:
|
||||||
|
|
||||||
virtual ~WaylandVsyncSource();
|
virtual ~WaylandVsyncSource();
|
||||||
|
|
||||||
virtual bool initialize(SDL_Window* window, int displayFps);
|
virtual bool initialize(SDL_Window* window, int displayFps) override;
|
||||||
|
|
||||||
|
virtual bool isAsync() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void frameDone(void* data, struct wl_callback* oldCb, uint32_t time);
|
static void frameDone(void* data, struct wl_callback* oldCb, uint32_t time);
|
||||||
|
@ -20,10 +22,8 @@ private:
|
||||||
static const struct wl_callback_listener s_FrameListener;
|
static const struct wl_callback_listener s_FrameListener;
|
||||||
|
|
||||||
Pacer* m_Pacer;
|
Pacer* m_Pacer;
|
||||||
int m_DisplayFps;
|
|
||||||
wl_display* m_Display;
|
wl_display* m_Display;
|
||||||
wl_surface* m_Surface;
|
wl_surface* m_Surface;
|
||||||
wl_callback* m_Callback;
|
wl_callback* m_Callback;
|
||||||
uint32_t m_LastFrameTime;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue