mirror of
https://github.com/moonlight-stream/moonlight-qt.git
synced 2025-07-03 16:25:54 +00:00
Improve unsupported FPS options and performance
This commit is contained in:
parent
64a08f0533
commit
c9a7c15f98
@ -178,17 +178,28 @@ ScrollView {
|
|||||||
fpsListModel.append({"text": "30 FPS", "video_fps": "30"})
|
fpsListModel.append({"text": "30 FPS", "video_fps": "30"})
|
||||||
fpsListModel.append({"text": "60 FPS", "video_fps": "60"})
|
fpsListModel.append({"text": "60 FPS", "video_fps": "60"})
|
||||||
|
|
||||||
|
// Add unsupported FPS values that come before the display max FPS
|
||||||
|
if (prefs.unsupportedFps) {
|
||||||
|
if (max_fps > 90) {
|
||||||
|
fpsListModel.append({"text": "90 FPS (Unsupported)", "video_fps": "90"})
|
||||||
|
}
|
||||||
|
if (max_fps > 120) {
|
||||||
|
fpsListModel.append({"text": "120 FPS (Unsupported)", "video_fps": "120"})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Use 64 as the cutoff for adding a separate option to
|
// Use 64 as the cutoff for adding a separate option to
|
||||||
// handle wonky displays that report just over 60 Hz.
|
// handle wonky displays that report just over 60 Hz.
|
||||||
if (max_fps > 64) {
|
if (max_fps > 64) {
|
||||||
fpsListModel.append({"text": max_fps+" FPS", "video_fps": ""+max_fps})
|
fpsListModel.append({"text": max_fps+" FPS", "video_fps": ""+max_fps})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add unsupported FPS values that come after the display max FPS
|
||||||
if (prefs.unsupportedFps) {
|
if (prefs.unsupportedFps) {
|
||||||
if (max_fps !== 90) {
|
if (max_fps < 90) {
|
||||||
fpsListModel.append({"text": "90 FPS (Unsupported)", "video_fps": "90"})
|
fpsListModel.append({"text": "90 FPS (Unsupported)", "video_fps": "90"})
|
||||||
}
|
}
|
||||||
if (max_fps !== 120) {
|
if (max_fps < 120) {
|
||||||
fpsListModel.append({"text": "120 FPS (Unsupported)", "video_fps": "120"})
|
fpsListModel.append({"text": "120 FPS (Unsupported)", "video_fps": "120"})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -325,7 +336,6 @@ ScrollView {
|
|||||||
id: vsyncCheck
|
id: vsyncCheck
|
||||||
text: "<font color=\"white\">Enable V-Sync</font>"
|
text: "<font color=\"white\">Enable V-Sync</font>"
|
||||||
font.pointSize: 12
|
font.pointSize: 12
|
||||||
visible: prefs.windowMode === StreamingPreferences.WM_FULLSCREEN
|
|
||||||
checked: prefs.enableVsync
|
checked: prefs.enableVsync
|
||||||
onCheckedChanged: {
|
onCheckedChanged: {
|
||||||
prefs.enableVsync = checked
|
prefs.enableVsync = checked
|
||||||
|
@ -417,6 +417,10 @@ bool Session::validateLaunch()
|
|||||||
|
|
||||||
if (m_Preferences.unsupportedFps && m_StreamConfig.fps > 60) {
|
if (m_Preferences.unsupportedFps && m_StreamConfig.fps > 60) {
|
||||||
emitLaunchWarning("Using unsupported FPS options may cause stuttering or lag.");
|
emitLaunchWarning("Using unsupported FPS options may cause stuttering or lag.");
|
||||||
|
|
||||||
|
if (m_Preferences.enableVsync) {
|
||||||
|
emitLaunchWarning("V-sync will be disabled when streaming at a higher frame rate than the display.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_StreamConfig.supportsHevc) {
|
if (m_StreamConfig.supportsHevc) {
|
||||||
@ -1001,18 +1005,31 @@ void Session::exec(int displayOriginX, int displayOriginY)
|
|||||||
SDL_FlushEvent(SDL_RENDER_DEVICE_RESET);
|
SDL_FlushEvent(SDL_RENDER_DEVICE_RESET);
|
||||||
SDL_FlushEvent(SDL_RENDER_TARGETS_RESET);
|
SDL_FlushEvent(SDL_RENDER_TARGETS_RESET);
|
||||||
|
|
||||||
// Choose a new decoder (hopefully the same one, but possibly
|
{
|
||||||
// not if a GPU was removed or something).
|
// If the stream exceeds the display refresh rate (plus some slack),
|
||||||
if (!chooseDecoder(m_Preferences.videoDecoderSelection,
|
// forcefully disable V-sync to allow the stream to render faster
|
||||||
m_Window, m_ActiveVideoFormat, m_ActiveVideoWidth,
|
// than the display.
|
||||||
m_ActiveVideoHeight, m_ActiveVideoFrameRate,
|
int displayHz = StreamUtils::getDisplayRefreshRate(m_Window);
|
||||||
m_Preferences.enableVsync,
|
bool enableVsync = m_Preferences.enableVsync;
|
||||||
s_ActiveSession->m_VideoDecoder)) {
|
if (displayHz + 5 < m_StreamConfig.fps) {
|
||||||
SDL_AtomicUnlock(&m_DecoderLock);
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
"Disabling V-sync because refresh rate limit exceeded");
|
||||||
"Failed to recreate decoder after reset");
|
enableVsync = false;
|
||||||
emit displayLaunchError("Unable to initialize video decoder. Please check your streaming settings and try again.");
|
}
|
||||||
goto DispatchDeferredCleanup;
|
|
||||||
|
// Choose a new decoder (hopefully the same one, but possibly
|
||||||
|
// not if a GPU was removed or something).
|
||||||
|
if (!chooseDecoder(m_Preferences.videoDecoderSelection,
|
||||||
|
m_Window, m_ActiveVideoFormat, m_ActiveVideoWidth,
|
||||||
|
m_ActiveVideoHeight, m_ActiveVideoFrameRate,
|
||||||
|
enableVsync,
|
||||||
|
s_ActiveSession->m_VideoDecoder)) {
|
||||||
|
SDL_AtomicUnlock(&m_DecoderLock);
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
|
"Failed to recreate decoder after reset");
|
||||||
|
emit displayLaunchError("Unable to initialize video decoder. Please check your streaming settings and try again.");
|
||||||
|
goto DispatchDeferredCleanup;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Request an IDR frame to complete the reset
|
// Request an IDR frame to complete the reset
|
||||||
|
@ -25,6 +25,52 @@ void StreamUtils::scaleSourceToDestinationSurface(SDL_Rect* src, SDL_Rect* dst)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int StreamUtils::getDisplayRefreshRate(SDL_Window* window)
|
||||||
|
{
|
||||||
|
int displayIndex = SDL_GetWindowDisplayIndex(window);
|
||||||
|
if (displayIndex < 0) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
|
"Failed to get current display: %s",
|
||||||
|
SDL_GetError());
|
||||||
|
|
||||||
|
// Assume display 0 if it fails
|
||||||
|
displayIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_DisplayMode mode;
|
||||||
|
if ((SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN) {
|
||||||
|
// Use the window display mode for full-screen exclusive mode
|
||||||
|
if (SDL_GetWindowDisplayMode(window, &mode) != 0) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
|
"SDL_GetWindowDisplayMode() failed: %s",
|
||||||
|
SDL_GetError());
|
||||||
|
|
||||||
|
// Assume 60 Hz
|
||||||
|
return 60;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Use the current display mode for windowed and borderless
|
||||||
|
if (SDL_GetCurrentDisplayMode(displayIndex, &mode) != 0) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
|
"SDL_GetCurrentDisplayMode() failed: %s",
|
||||||
|
SDL_GetError());
|
||||||
|
|
||||||
|
// Assume 60 Hz
|
||||||
|
return 60;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// May be zero if undefined
|
||||||
|
if (mode.refresh_rate == 0) {
|
||||||
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
|
"Refresh rate unknown; assuming 60 Hz");
|
||||||
|
mode.refresh_rate = 60;
|
||||||
|
}
|
||||||
|
|
||||||
|
return mode.refresh_rate;
|
||||||
|
}
|
||||||
|
|
||||||
bool StreamUtils::getRealDesktopMode(int displayIndex, SDL_DisplayMode* mode)
|
bool StreamUtils::getRealDesktopMode(int displayIndex, SDL_DisplayMode* mode)
|
||||||
{
|
{
|
||||||
#ifdef Q_OS_DARWIN
|
#ifdef Q_OS_DARWIN
|
||||||
|
@ -10,4 +10,7 @@ public:
|
|||||||
|
|
||||||
static
|
static
|
||||||
bool getRealDesktopMode(int displayIndex, SDL_DisplayMode* mode);
|
bool getRealDesktopMode(int displayIndex, SDL_DisplayMode* mode);
|
||||||
|
|
||||||
|
static
|
||||||
|
int getDisplayRefreshRate(SDL_Window* window);
|
||||||
};
|
};
|
||||||
|
@ -17,7 +17,6 @@ DEFINE_GUID(DXVADDI_Intel_ModeH264_E, 0x604F8E68,0x4951,0x4C54,0x88,0xFE,0xAB,0x
|
|||||||
#define SAFE_COM_RELEASE(x) if (x) { (x)->Release(); }
|
#define SAFE_COM_RELEASE(x) if (x) { (x)->Release(); }
|
||||||
|
|
||||||
DXVA2Renderer::DXVA2Renderer() :
|
DXVA2Renderer::DXVA2Renderer() :
|
||||||
m_ForceVsyncOn(false),
|
|
||||||
m_DecService(nullptr),
|
m_DecService(nullptr),
|
||||||
m_Decoder(nullptr),
|
m_Decoder(nullptr),
|
||||||
m_SurfacesUsed(0),
|
m_SurfacesUsed(0),
|
||||||
@ -467,7 +466,6 @@ bool DXVA2Renderer::initializeDevice(SDL_Window* window, bool enableVsync)
|
|||||||
D3DPRESENT_PARAMETERS d3dpp = {};
|
D3DPRESENT_PARAMETERS d3dpp = {};
|
||||||
d3dpp.hDeviceWindow = info.info.win.window;
|
d3dpp.hDeviceWindow = info.info.win.window;
|
||||||
d3dpp.Flags = D3DPRESENTFLAG_VIDEO;
|
d3dpp.Flags = D3DPRESENTFLAG_VIDEO;
|
||||||
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
|
|
||||||
|
|
||||||
if ((windowFlags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN) {
|
if ((windowFlags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN) {
|
||||||
d3dpp.Windowed = false;
|
d3dpp.Windowed = false;
|
||||||
@ -490,14 +488,20 @@ bool DXVA2Renderer::initializeDevice(SDL_Window* window, bool enableVsync)
|
|||||||
// to reduce latency by avoiding double v-syncing.
|
// to reduce latency by avoiding double v-syncing.
|
||||||
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
|
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
|
||||||
|
|
||||||
// D3DSWAPEFFECT_FLIPEX requires at least 2 back buffers to allow us to
|
// If V-sync is enabled (not rendering faster than display),
|
||||||
// continue while DWM is waiting to render the surface to the display.
|
// we can use FlipEx for more efficient swapping.
|
||||||
d3dpp.SwapEffect = D3DSWAPEFFECT_FLIPEX;
|
if (enableVsync) {
|
||||||
d3dpp.BackBufferCount = 2;
|
// D3DSWAPEFFECT_FLIPEX requires at least 2 back buffers to allow us to
|
||||||
|
// continue while DWM is waiting to render the surface to the display.
|
||||||
// We need our V-sync source enabled to synchronize with DWM composition
|
d3dpp.SwapEffect = D3DSWAPEFFECT_FLIPEX;
|
||||||
// and avoid micro-stuttering.
|
d3dpp.BackBufferCount = 2;
|
||||||
m_ForceVsyncOn = true;
|
}
|
||||||
|
else {
|
||||||
|
// With V-sync off, we won't use FlipEx because that will block while
|
||||||
|
// DWM is waiting to render our surface (effectively behaving like V-Sync).
|
||||||
|
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
|
||||||
|
d3dpp.BackBufferCount = 1;
|
||||||
|
}
|
||||||
|
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
"Windowed mode with DWM running");
|
"Windowed mode with DWM running");
|
||||||
@ -506,6 +510,7 @@ bool DXVA2Renderer::initializeDevice(SDL_Window* window, bool enableVsync)
|
|||||||
// Uncomposited desktop or full-screen exclusive mode with V-sync enabled
|
// Uncomposited desktop or full-screen exclusive mode with V-sync enabled
|
||||||
// We will enable V-sync in this scenario to avoid tearing.
|
// We will enable V-sync in this scenario to avoid tearing.
|
||||||
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_ONE;
|
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_ONE;
|
||||||
|
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
|
||||||
d3dpp.BackBufferCount = 1;
|
d3dpp.BackBufferCount = 1;
|
||||||
|
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
@ -515,6 +520,7 @@ bool DXVA2Renderer::initializeDevice(SDL_Window* window, bool enableVsync)
|
|||||||
// Uncomposited desktop or full-screen exclusive mode with V-sync disabled
|
// Uncomposited desktop or full-screen exclusive mode with V-sync disabled
|
||||||
// We will allowing tearing for lowest latency.
|
// We will allowing tearing for lowest latency.
|
||||||
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
|
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
|
||||||
|
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
|
||||||
d3dpp.BackBufferCount = 1;
|
d3dpp.BackBufferCount = 1;
|
||||||
|
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
@ -631,7 +637,7 @@ int DXVA2Renderer::getDecoderCapabilities()
|
|||||||
|
|
||||||
IFFmpegRenderer::VSyncConstraint DXVA2Renderer::getVsyncConstraint()
|
IFFmpegRenderer::VSyncConstraint DXVA2Renderer::getVsyncConstraint()
|
||||||
{
|
{
|
||||||
return m_ForceVsyncOn ? VSYNC_FORCE_ON : VSYNC_ANY;
|
return VSYNC_ANY;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DXVA2Renderer::renderFrameAtVsync(AVFrame *frame)
|
void DXVA2Renderer::renderFrameAtVsync(AVFrame *frame)
|
||||||
|
@ -45,7 +45,6 @@ private:
|
|||||||
int m_VideoFormat;
|
int m_VideoFormat;
|
||||||
int m_VideoWidth;
|
int m_VideoWidth;
|
||||||
int m_VideoHeight;
|
int m_VideoHeight;
|
||||||
bool m_ForceVsyncOn;
|
|
||||||
|
|
||||||
int m_DisplayWidth;
|
int m_DisplayWidth;
|
||||||
int m_DisplayHeight;
|
int m_DisplayHeight;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include "pacer.h"
|
#include "pacer.h"
|
||||||
|
#include "streaming/streamutils.h"
|
||||||
|
|
||||||
#include "nullthreadedvsyncsource.h"
|
#include "nullthreadedvsyncsource.h"
|
||||||
|
|
||||||
@ -120,44 +121,7 @@ bool Pacer::initialize(SDL_Window* window, int maxVideoFps, bool enableVsync)
|
|||||||
{
|
{
|
||||||
m_MaxVideoFps = maxVideoFps;
|
m_MaxVideoFps = maxVideoFps;
|
||||||
m_EnableVsync = enableVsync;
|
m_EnableVsync = enableVsync;
|
||||||
|
m_DisplayFps = StreamUtils::getDisplayRefreshRate(window);
|
||||||
int displayIndex = SDL_GetWindowDisplayIndex(window);
|
|
||||||
if (displayIndex < 0) {
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
|
||||||
"Failed to get current display: %s",
|
|
||||||
SDL_GetError());
|
|
||||||
|
|
||||||
// Assume display 0 if it fails
|
|
||||||
displayIndex = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_DisplayMode mode;
|
|
||||||
if ((SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN) {
|
|
||||||
// Use the window display mode for full-screen exclusive mode
|
|
||||||
if (SDL_GetWindowDisplayMode(window, &mode) != 0) {
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
|
||||||
"SDL_GetWindowDisplayMode() failed: %s",
|
|
||||||
SDL_GetError());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Use the current display mode for windowed and borderless
|
|
||||||
if (SDL_GetCurrentDisplayMode(displayIndex, &mode) != 0) {
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
|
||||||
"SDL_GetCurrentDisplayMode() failed: %s",
|
|
||||||
SDL_GetError());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// May be zero if undefined
|
|
||||||
m_DisplayFps = mode.refresh_rate;
|
|
||||||
if (m_DisplayFps == 0) {
|
|
||||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
|
||||||
"Refresh rate unknown; assuming 60 Hz");
|
|
||||||
m_DisplayFps = 60;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_EnableVsync) {
|
if (m_EnableVsync) {
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user