Improve unsupported FPS options and performance

This commit is contained in:
Cameron Gutman 2018-09-08 15:09:46 -07:00
parent 64a08f0533
commit c9a7c15f98
7 changed files with 110 additions and 65 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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);
}; };

View File

@ -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)

View File

@ -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;

View File

@ -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,