mirror of
https://github.com/moonlight-stream/moonlight-qt.git
synced 2025-07-03 00:06:09 +00:00
Implement a Wayland Vsync source using frame callbacks
This commit is contained in:
parent
394f28339e
commit
e3d51fd7f7
@ -103,7 +103,7 @@ unix:!macx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
packagesExist(wayland-client) {
|
packagesExist(wayland-client) {
|
||||||
DEFINES += HAS_WAYLAND
|
CONFIG += wayland
|
||||||
PKGCONFIG += wayland-client
|
PKGCONFIG += wayland-client
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -348,6 +348,13 @@ embedded {
|
|||||||
|
|
||||||
DEFINES += EMBEDDED_BUILD
|
DEFINES += EMBEDDED_BUILD
|
||||||
}
|
}
|
||||||
|
wayland {
|
||||||
|
message(Wayland extensions enabled)
|
||||||
|
|
||||||
|
DEFINES += HAS_WAYLAND
|
||||||
|
SOURCES += streaming/video/ffmpeg-renderers/pacer/waylandvsyncsource.cpp
|
||||||
|
HEADERS += streaming/video/ffmpeg-renderers/pacer/waylandvsyncsource.h
|
||||||
|
}
|
||||||
|
|
||||||
RESOURCES += \
|
RESOURCES += \
|
||||||
resources.qrc \
|
resources.qrc \
|
||||||
|
@ -10,6 +10,12 @@
|
|||||||
#include "dxvsyncsource.h"
|
#include "dxvsyncsource.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAS_WAYLAND
|
||||||
|
#include "waylandvsyncsource.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <SDL_syswm.h>
|
||||||
|
|
||||||
// Limit the number of queued frames to prevent excessive memory consumption
|
// Limit the number of queued frames to prevent excessive memory consumption
|
||||||
// if the V-Sync source or renderer is blocked for a while. It's important
|
// if the V-Sync source or renderer is blocked for a while. It's important
|
||||||
// that the sum of all queued frames between both pacing and rendering queues
|
// that the sum of all queued frames between both pacing and rendering queues
|
||||||
@ -154,8 +160,6 @@ 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);
|
|
||||||
|
|
||||||
m_FrameQueueLock.lock();
|
m_FrameQueueLock.lock();
|
||||||
|
|
||||||
// If the queue length history entries are large, be strict
|
// If the queue length history entries are large, be strict
|
||||||
@ -196,7 +200,7 @@ void Pacer::vsyncCallback(int timeUntilNextVsyncMillis)
|
|||||||
|
|
||||||
if (m_PacingQueue.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_PacingQueueNotEmpty.wait(&m_FrameQueueLock, timeUntilNextVsyncMillis - TIMER_SLACK_MS)) {
|
if (!m_PacingQueueNotEmpty.wait(&m_FrameQueueLock, SDL_max(timeUntilNextVsyncMillis, TIMER_SLACK_MS) - TIMER_SLACK_MS)) {
|
||||||
// Wait timed out - unlock and bail
|
// Wait timed out - unlock and bail
|
||||||
m_FrameQueueLock.unlock();
|
m_FrameQueueLock.unlock();
|
||||||
return;
|
return;
|
||||||
@ -218,16 +222,37 @@ bool Pacer::initialize(SDL_Window* window, int maxVideoFps, bool enablePacing)
|
|||||||
"Frame pacing active: target %d Hz with %d FPS stream",
|
"Frame pacing active: target %d Hz with %d FPS stream",
|
||||||
m_DisplayFps, m_MaxVideoFps);
|
m_DisplayFps, m_MaxVideoFps);
|
||||||
|
|
||||||
#if defined(Q_OS_WIN32)
|
SDL_SysWMinfo info;
|
||||||
|
SDL_VERSION(&info.version);
|
||||||
|
if (!SDL_GetWindowWMInfo(window, &info)) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
|
"SDL_GetWindowWMInfo() failed: %s",
|
||||||
|
SDL_GetError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (info.subsystem) {
|
||||||
|
#ifdef Q_OS_WIN32
|
||||||
|
case SDL_SYSWM_WINDOWS:
|
||||||
// Don't use D3DKMTWaitForVerticalBlankEvent() on Windows 7, because
|
// Don't use D3DKMTWaitForVerticalBlankEvent() on Windows 7, because
|
||||||
// it blocks during other concurrent DX operations (like actually rendering).
|
// it blocks during other concurrent DX operations (like actually rendering).
|
||||||
if (IsWindows8OrGreater()) {
|
if (IsWindows8OrGreater()) {
|
||||||
m_VsyncSource = new DxVsyncSource(this);
|
m_VsyncSource = new DxVsyncSource(this);
|
||||||
}
|
}
|
||||||
#else
|
break;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(SDL_VIDEO_DRIVER_WAYLAND) && defined(HAS_WAYLAND)
|
||||||
|
case SDL_SYSWM_WAYLAND:
|
||||||
|
m_VsyncSource = new WaylandVsyncSource(this);
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
default:
|
||||||
// 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.
|
||||||
#endif
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
SDL_assert(m_VsyncSource != nullptr || !(m_RendererAttributes & RENDERER_ATTRIBUTE_FORCE_PACING));
|
SDL_assert(m_VsyncSource != nullptr || !(m_RendererAttributes & RENDERER_ATTRIBUTE_FORCE_PACING));
|
||||||
|
|
||||||
|
@ -0,0 +1,77 @@
|
|||||||
|
#include "waylandvsyncsource.h"
|
||||||
|
|
||||||
|
#include <SDL_syswm.h>
|
||||||
|
|
||||||
|
const struct wl_callback_listener WaylandVsyncSource::s_FrameListener = {
|
||||||
|
.done = WaylandVsyncSource::frameDone,
|
||||||
|
};
|
||||||
|
|
||||||
|
WaylandVsyncSource::WaylandVsyncSource(Pacer* pacer)
|
||||||
|
: m_Pacer(pacer),
|
||||||
|
m_Display(nullptr),
|
||||||
|
m_Surface(nullptr),
|
||||||
|
m_Callback(nullptr),
|
||||||
|
m_LastFrameTime(0)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
WaylandVsyncSource::~WaylandVsyncSource()
|
||||||
|
{
|
||||||
|
if (m_Callback != nullptr) {
|
||||||
|
wl_callback_destroy(m_Callback);
|
||||||
|
wl_display_roundtrip(m_Display);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WaylandVsyncSource::initialize(SDL_Window* window, int displayFps)
|
||||||
|
{
|
||||||
|
SDL_SysWMinfo info;
|
||||||
|
|
||||||
|
SDL_VERSION(&info.version);
|
||||||
|
|
||||||
|
if (!SDL_GetWindowWMInfo(window, &info)) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
|
"SDL_GetWindowWMInfo() failed: %s",
|
||||||
|
SDL_GetError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pacer shoud not create us for non-Wayland windows
|
||||||
|
SDL_assert(info.subsystem == SDL_SYSWM_WAYLAND);
|
||||||
|
|
||||||
|
m_DisplayFps = displayFps;
|
||||||
|
m_Display = info.info.wl.display;
|
||||||
|
m_Surface = info.info.wl.surface;
|
||||||
|
|
||||||
|
// Enqueue our first frame callback
|
||||||
|
m_Callback = wl_surface_frame(m_Surface);
|
||||||
|
wl_callback_add_listener(m_Callback, &s_FrameListener, this);
|
||||||
|
wl_surface_commit(m_Surface);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WaylandVsyncSource::frameDone(void* data, struct wl_callback* oldCb, uint32_t time)
|
||||||
|
{
|
||||||
|
auto me = (WaylandVsyncSource*)data;
|
||||||
|
|
||||||
|
// Free this callback
|
||||||
|
SDL_assert(oldCb == me->m_Callback);
|
||||||
|
wl_callback_destroy(oldCb);
|
||||||
|
|
||||||
|
// Register for another callback before invoking Pacer to ensure we don't miss
|
||||||
|
// a frame callback if Pacer takes too long.
|
||||||
|
me->m_Callback = wl_surface_frame(me->m_Surface);
|
||||||
|
wl_callback_add_listener(me->m_Callback, &s_FrameListener, data);
|
||||||
|
wl_surface_commit(me->m_Surface);
|
||||||
|
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;
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "pacer.h"
|
||||||
|
|
||||||
|
#include <wayland-client-core.h>
|
||||||
|
#include <wayland-client-protocol.h>
|
||||||
|
|
||||||
|
class WaylandVsyncSource : public IVsyncSource
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
WaylandVsyncSource(Pacer* pacer);
|
||||||
|
|
||||||
|
virtual ~WaylandVsyncSource();
|
||||||
|
|
||||||
|
virtual bool initialize(SDL_Window* window, int displayFps);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void frameDone(void* data, struct wl_callback* oldCb, uint32_t time);
|
||||||
|
|
||||||
|
static const struct wl_callback_listener s_FrameListener;
|
||||||
|
|
||||||
|
Pacer* m_Pacer;
|
||||||
|
int m_DisplayFps;
|
||||||
|
wl_display* m_Display;
|
||||||
|
wl_surface* m_Surface;
|
||||||
|
wl_callback* m_Callback;
|
||||||
|
uint32_t m_LastFrameTime;
|
||||||
|
};
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user