Only initialize the video subsystem once per launch

This commit is contained in:
Cameron Gutman 2019-03-23 17:46:42 -07:00
parent c0bf8b9c25
commit fa4c0e82bd
4 changed files with 67 additions and 106 deletions

View File

@ -5,11 +5,6 @@
SystemProperties::SystemProperties() SystemProperties::SystemProperties()
{ {
hasHardwareAcceleration =
Session::isHardwareDecodeAvailable(StreamingPreferences::VDS_AUTO,
VIDEO_FORMAT_H264,
1920, 1080, 60);
isRunningWayland = qgetenv("XDG_SESSION_TYPE") == "wayland"; isRunningWayland = qgetenv("XDG_SESSION_TYPE") == "wayland";
#ifdef Q_OS_WIN32 #ifdef Q_OS_WIN32
@ -51,17 +46,11 @@ void SystemProperties::querySdlVideoInfo()
{ {
monitorDesktopResolutions.clear(); monitorDesktopResolutions.clear();
monitorNativeResolutions.clear(); monitorNativeResolutions.clear();
hasHardwareAcceleration = false;
// Never let the maximum drop below 60 FPS // Never let the maximum drop below 60 FPS
maximumStreamingFrameRate = 60; maximumStreamingFrameRate = 60;
if (SDL_InitSubSystem(SDL_INIT_VIDEO) != 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_InitSubSystem(SDL_INIT_VIDEO) failed: %s",
SDL_GetError());
return;
}
SDL_DisplayMode bestMode; SDL_DisplayMode bestMode;
for (int displayIndex = 0; displayIndex < SDL_GetNumVideoDisplays(); displayIndex++) { for (int displayIndex = 0; displayIndex < SDL_GetNumVideoDisplays(); displayIndex++) {
SDL_DisplayMode desktopMode; SDL_DisplayMode desktopMode;
@ -97,5 +86,19 @@ void SystemProperties::querySdlVideoInfo()
} }
} }
SDL_QuitSubSystem(SDL_INIT_VIDEO); SDL_Window* testWindow = SDL_CreateWindow("", 0, 0, 1280, 720, SDL_WINDOW_HIDDEN);
if (!testWindow) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Failed to create window for hardware decode test: %s",
SDL_GetError());
return;
}
hasHardwareAcceleration =
Session::isHardwareDecodeAvailable(testWindow,
StreamingPreferences::VDS_AUTO,
VIDEO_FORMAT_H264,
1920, 1080, 60);
SDL_DestroyWindow(testWindow);
} }

View File

@ -308,15 +308,15 @@ int main(int argc, char *argv[])
// Register custom metatypes for use in signals // Register custom metatypes for use in signals
qRegisterMetaType<NvApp>("NvApp"); qRegisterMetaType<NvApp>("NvApp");
#ifdef STEAM_LINK
// Steam Link requires that we initialize video before creating our // Steam Link requires that we initialize video before creating our
// QGuiApplication in order to configure the framebuffer correctly. // QGuiApplication in order to configure the framebuffer correctly.
if (SDL_InitSubSystem(SDL_INIT_VIDEO) != 0) { // It's fine to do on other platforms too, and it can save some time
// doing initialization and teardown of the video subsystem after streaming.
if (SDL_InitSubSystem(SDL_INIT_VIDEO | SDL_INIT_TIMER) != 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_InitSubSystem(SDL_INIT_VIDEO) failed: %s", "SDL_InitSubSystem(SDL_INIT_VIDEO | SDL_INIT_TIMER) failed: %s",
SDL_GetError()); SDL_GetError());
} }
#endif
QGuiApplication app(argc, argv); QGuiApplication app(argc, argv);
@ -438,12 +438,6 @@ int main(int argc, char *argv[])
if (engine.rootObjects().isEmpty()) if (engine.rootObjects().isEmpty())
return -1; return -1;
if (SDL_InitSubSystem(SDL_INIT_TIMER) != 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_InitSubSystem(SDL_INIT_TIMER) failed: %s",
SDL_GetError());
}
// Use atexit() to ensure SDL_Quit() is called. This avoids // Use atexit() to ensure SDL_Quit() is called. This avoids
// racing with object destruction where SDL may be used. // racing with object destruction where SDL may be used.
atexit(SDL_Quit); atexit(SDL_Quit);

View File

@ -244,30 +244,13 @@ int Session::drSubmitDecodeUnit(PDECODE_UNIT du)
} }
} }
bool Session::isHardwareDecodeAvailable(StreamingPreferences::VideoDecoderSelection vds, bool Session::isHardwareDecodeAvailable(SDL_Window* window,
StreamingPreferences::VideoDecoderSelection vds,
int videoFormat, int width, int height, int frameRate) int videoFormat, int width, int height, int frameRate)
{ {
IVideoDecoder* decoder; IVideoDecoder* decoder;
if (SDL_InitSubSystem(SDL_INIT_VIDEO) != 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_InitSubSystem(SDL_INIT_VIDEO) failed: %s",
SDL_GetError());
return false;
}
SDL_Window* window = SDL_CreateWindow("", 0, 0, width, height, SDL_WINDOW_HIDDEN);
if (!window) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Failed to create window for hardware decode test: %s",
SDL_GetError());
SDL_QuitSubSystem(SDL_INIT_VIDEO);
return false;
}
if (!chooseDecoder(vds, window, videoFormat, width, height, frameRate, true, false, true, decoder)) { if (!chooseDecoder(vds, window, videoFormat, width, height, frameRate, true, false, true, decoder)) {
SDL_DestroyWindow(window);
SDL_QuitSubSystem(SDL_INIT_VIDEO);
return false; return false;
} }
@ -275,35 +258,16 @@ bool Session::isHardwareDecodeAvailable(StreamingPreferences::VideoDecoderSelect
delete decoder; delete decoder;
// This must be called after the decoder is deleted, because
// the renderer may want to interact with the window
SDL_DestroyWindow(window);
SDL_QuitSubSystem(SDL_INIT_VIDEO);
return ret; return ret;
} }
int Session::getDecoderCapabilities(StreamingPreferences::VideoDecoderSelection vds, int Session::getDecoderCapabilities(SDL_Window* window,
StreamingPreferences::VideoDecoderSelection vds,
int videoFormat, int width, int height, int frameRate) int videoFormat, int width, int height, int frameRate)
{ {
IVideoDecoder* decoder; IVideoDecoder* decoder;
// Video must already be initialized to use this function
SDL_assert(SDL_WasInit(SDL_INIT_VIDEO));
SDL_Window* window = SDL_CreateWindow("", 0, 0, width, height, SDL_WINDOW_HIDDEN);
if (!window) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Failed to create window for hardware decode test: %s",
SDL_GetError());
SDL_QuitSubSystem(SDL_INIT_VIDEO);
return false;
}
if (!chooseDecoder(vds, window, videoFormat, width, height, frameRate, true, false, true, decoder)) { if (!chooseDecoder(vds, window, videoFormat, width, height, frameRate, true, false, true, decoder)) {
SDL_DestroyWindow(window);
SDL_QuitSubSystem(SDL_INIT_VIDEO);
return false; return false;
} }
@ -311,10 +275,6 @@ int Session::getDecoderCapabilities(StreamingPreferences::VideoDecoderSelection
delete decoder; delete decoder;
// This must be called after the decoder is deleted, because
// the renderer may want to interact with the window
SDL_DestroyWindow(window);
return caps; return caps;
} }
@ -350,8 +310,17 @@ Session::~Session()
s_ActiveSessionSemaphore.release(); s_ActiveSessionSemaphore.release();
} }
void Session::initialize() bool Session::initialize()
{ {
// Create a hidden window to use for decoder initialization tests
SDL_Window* testWindow = SDL_CreateWindow("", 0, 0, 1280, 720, SDL_WINDOW_HIDDEN);
if (!testWindow) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Failed to create window for hardware decode test: %s",
SDL_GetError());
return false;
}
qInfo() << "Server GPU:" << m_Computer->gpuModel; qInfo() << "Server GPU:" << m_Computer->gpuModel;
qInfo() << "Server GFE version:" << m_Computer->gfeVersion; qInfo() << "Server GFE version:" << m_Computer->gfeVersion;
@ -404,7 +373,8 @@ void Session::initialize()
case StreamingPreferences::VCC_AUTO: case StreamingPreferences::VCC_AUTO:
// TODO: Determine if HEVC is better depending on the decoder // TODO: Determine if HEVC is better depending on the decoder
m_StreamConfig.supportsHevc = m_StreamConfig.supportsHevc =
isHardwareDecodeAvailable(m_Preferences->videoDecoderSelection, isHardwareDecodeAvailable(testWindow,
m_Preferences->videoDecoderSelection,
VIDEO_FORMAT_H265, VIDEO_FORMAT_H265,
m_StreamConfig.width, m_StreamConfig.width,
m_StreamConfig.height, m_StreamConfig.height,
@ -441,6 +411,15 @@ void Session::initialize()
break; break;
} }
// Add the capability flags from the chosen decoder/renderer
// Requires m_StreamConfig.supportsHevc to be initialized
m_VideoCallbacks.capabilities |= getDecoderCapabilities(testWindow,
m_Preferences->videoDecoderSelection,
m_StreamConfig.supportsHevc ? VIDEO_FORMAT_H265 : VIDEO_FORMAT_H264,
m_StreamConfig.width,
m_StreamConfig.height,
m_StreamConfig.fps);
switch (m_Preferences->windowMode) switch (m_Preferences->windowMode)
{ {
case StreamingPreferences::WM_FULLSCREEN_DESKTOP: case StreamingPreferences::WM_FULLSCREEN_DESKTOP:
@ -451,6 +430,14 @@ void Session::initialize()
m_FullScreenFlag = SDL_WINDOW_FULLSCREEN; m_FullScreenFlag = SDL_WINDOW_FULLSCREEN;
break; break;
} }
// Check for validation errors/warnings and emit
// signals for them, if appropriate
bool ret = validateLaunch(testWindow);
SDL_DestroyWindow(testWindow);
return ret;
} }
void Session::emitLaunchWarning(QString text) void Session::emitLaunchWarning(QString text)
@ -469,7 +456,7 @@ void Session::emitLaunchWarning(QString text)
} }
} }
bool Session::validateLaunch() bool Session::validateLaunch(SDL_Window* testWindow)
{ {
QStringList warningList; QStringList warningList;
@ -489,7 +476,8 @@ bool Session::validateLaunch()
bool hevcForced = m_Preferences->videoCodecConfig == StreamingPreferences::VCC_FORCE_HEVC || bool hevcForced = m_Preferences->videoCodecConfig == StreamingPreferences::VCC_FORCE_HEVC ||
m_Preferences->videoCodecConfig == StreamingPreferences::VCC_FORCE_HEVC_HDR; m_Preferences->videoCodecConfig == StreamingPreferences::VCC_FORCE_HEVC_HDR;
if (!isHardwareDecodeAvailable(m_Preferences->videoDecoderSelection, if (!isHardwareDecodeAvailable(testWindow,
m_Preferences->videoDecoderSelection,
VIDEO_FORMAT_H265, VIDEO_FORMAT_H265,
m_StreamConfig.width, m_StreamConfig.width,
m_StreamConfig.height, m_StreamConfig.height,
@ -530,7 +518,8 @@ bool Session::validateLaunch()
emitLaunchWarning("Your host PC GPU doesn't support HDR streaming. " emitLaunchWarning("Your host PC GPU doesn't support HDR streaming. "
"A GeForce GTX 1000-series (Pascal) or later GPU is required for HDR streaming."); "A GeForce GTX 1000-series (Pascal) or later GPU is required for HDR streaming.");
} }
else if (!isHardwareDecodeAvailable(m_Preferences->videoDecoderSelection, else if (!isHardwareDecodeAvailable(testWindow,
m_Preferences->videoDecoderSelection,
VIDEO_FORMAT_H265_MAIN10, VIDEO_FORMAT_H265_MAIN10,
m_StreamConfig.width, m_StreamConfig.width,
m_StreamConfig.height, m_StreamConfig.height,
@ -579,7 +568,8 @@ bool Session::validateLaunch()
} }
if (m_Preferences->videoDecoderSelection == StreamingPreferences::VDS_FORCE_HARDWARE && if (m_Preferences->videoDecoderSelection == StreamingPreferences::VDS_FORCE_HARDWARE &&
!isHardwareDecodeAvailable(m_Preferences->videoDecoderSelection, !isHardwareDecodeAvailable(testWindow,
m_Preferences->videoDecoderSelection,
m_StreamConfig.supportsHevc ? VIDEO_FORMAT_H265 : VIDEO_FORMAT_H264, m_StreamConfig.supportsHevc ? VIDEO_FORMAT_H265 : VIDEO_FORMAT_H264,
m_StreamConfig.width, m_StreamConfig.width,
m_StreamConfig.height, m_StreamConfig.height,
@ -816,11 +806,7 @@ void Session::exec(int displayOriginX, int displayOriginY)
// Complete initialization in this deferred context to avoid // Complete initialization in this deferred context to avoid
// calling expensive functions in the constructor (during the // calling expensive functions in the constructor (during the
// process of loading the StreamSegue). // process of loading the StreamSegue).
initialize(); if (!initialize()) {
// Check for validation errors/warnings and emit
// signals for them, if appropriate
if (!validateLaunch()) {
emit sessionFinished(); emit sessionFinished();
return; return;
} }
@ -891,18 +877,6 @@ void Session::exec(int displayOriginX, int displayOriginY)
return; return;
} }
SDL_assert(!SDL_WasInit(SDL_INIT_VIDEO));
if (SDL_InitSubSystem(SDL_INIT_VIDEO) != 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_InitSubSystem(SDL_INIT_VIDEO) failed: %s",
SDL_GetError());
delete m_InputHandler;
m_InputHandler = nullptr;
emit displayLaunchError(QString::fromLocal8Bit(SDL_GetError()));
QThreadPool::globalInstance()->start(new DeferredSessionCleanupTask(this));
return;
}
QByteArray hostnameStr = m_Computer->activeAddress.toLatin1(); QByteArray hostnameStr = m_Computer->activeAddress.toLatin1();
QByteArray siAppVersion = m_Computer->appVersion.toLatin1(); QByteArray siAppVersion = m_Computer->appVersion.toLatin1();
@ -919,14 +893,6 @@ void Session::exec(int displayOriginX, int displayOriginY)
hostInfo.serverInfoGfeVersion = siGfeVersion.data(); hostInfo.serverInfoGfeVersion = siGfeVersion.data();
} }
// Add the capability flags from the chosen decoder/renderer
// Requires SDL_INIT_VIDEO already done.
m_VideoCallbacks.capabilities |= getDecoderCapabilities(m_Preferences->videoDecoderSelection,
m_StreamConfig.supportsHevc ? VIDEO_FORMAT_H265 : VIDEO_FORMAT_H264,
m_StreamConfig.width,
m_StreamConfig.height,
m_StreamConfig.fps);
int err = LiStartConnection(&hostInfo, &m_StreamConfig, &k_ConnCallbacks, int err = LiStartConnection(&hostInfo, &m_StreamConfig, &k_ConnCallbacks,
&m_VideoCallbacks, &m_VideoCallbacks,
m_AudioDisabled ? nullptr : &k_AudioCallbacks, m_AudioDisabled ? nullptr : &k_AudioCallbacks,
@ -936,7 +902,6 @@ void Session::exec(int displayOriginX, int displayOriginY)
// listener. // listener.
delete m_InputHandler; delete m_InputHandler;
m_InputHandler = nullptr; m_InputHandler = nullptr;
SDL_QuitSubSystem(SDL_INIT_VIDEO);
QThreadPool::globalInstance()->start(new DeferredSessionCleanupTask(this)); QThreadPool::globalInstance()->start(new DeferredSessionCleanupTask(this));
return; return;
} }
@ -960,7 +925,6 @@ void Session::exec(int displayOriginX, int displayOriginY)
SDL_GetError()); SDL_GetError());
delete m_InputHandler; delete m_InputHandler;
m_InputHandler = nullptr; m_InputHandler = nullptr;
SDL_QuitSubSystem(SDL_INIT_VIDEO);
QThreadPool::globalInstance()->start(new DeferredSessionCleanupTask(this)); QThreadPool::globalInstance()->start(new DeferredSessionCleanupTask(this));
return; return;
} }
@ -1276,8 +1240,6 @@ DispatchDeferredCleanup:
if (iconSurface != nullptr) { if (iconSurface != nullptr) {
SDL_FreeSurface(iconSurface); SDL_FreeSurface(iconSurface);
} }
SDL_QuitSubSystem(SDL_INIT_VIDEO);
SDL_assert(!SDL_WasInit(SDL_INIT_VIDEO));
// Cleanup can take a while, so dispatch it to a worker thread. // Cleanup can take a while, so dispatch it to a worker thread.
// When it is complete, it will release our s_ActiveSessionSemaphore // When it is complete, it will release our s_ActiveSessionSemaphore

View File

@ -25,7 +25,8 @@ public:
Q_INVOKABLE void exec(int displayOriginX, int displayOriginY); Q_INVOKABLE void exec(int displayOriginX, int displayOriginY);
static static
bool isHardwareDecodeAvailable(StreamingPreferences::VideoDecoderSelection vds, bool isHardwareDecodeAvailable(SDL_Window* window,
StreamingPreferences::VideoDecoderSelection vds,
int videoFormat, int width, int height, int frameRate); int videoFormat, int width, int height, int frameRate);
static Session* get() static Session* get()
@ -54,14 +55,15 @@ signals:
void sessionFinished(); void sessionFinished();
private: private:
void initialize(); bool initialize();
bool validateLaunch(); bool validateLaunch(SDL_Window* testWindow);
void emitLaunchWarning(QString text); void emitLaunchWarning(QString text);
static static
int getDecoderCapabilities(StreamingPreferences::VideoDecoderSelection vds, int getDecoderCapabilities(SDL_Window* window,
StreamingPreferences::VideoDecoderSelection vds,
int videoFormat, int width, int height, int frameRate); int videoFormat, int width, int height, int frameRate);
IAudioRenderer* createAudioRenderer(); IAudioRenderer* createAudioRenderer();