mirror of
https://github.com/moonlight-stream/moonlight-qt.git
synced 2026-02-16 02:30:52 +00:00
Move heavy SystemProperties initialization operations off the main thread
This dramatically improves startup performance on slow devices.
This commit is contained in:
@@ -12,6 +12,69 @@
|
|||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
class SystemPropertyQueryThread : public QThread
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SystemPropertyQueryThread(SystemProperties* properties)
|
||||||
|
: QThread(properties), m_Properties(properties)
|
||||||
|
{
|
||||||
|
setObjectName("System Properties Async Query Thread");
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void run() override
|
||||||
|
{
|
||||||
|
bool hasHardwareAcceleration;
|
||||||
|
bool rendererAlwaysFullScreen;
|
||||||
|
bool supportsHdr;
|
||||||
|
QSize maximumResolution;
|
||||||
|
|
||||||
|
if (SDL_InitSubSystem(SDL_INIT_VIDEO) != 0) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
|
"SDL_InitSubSystem(SDL_INIT_VIDEO) failed: %s",
|
||||||
|
SDL_GetError());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update display related attributes (max FPS, native resolution, etc).
|
||||||
|
m_Properties->refreshDisplays();
|
||||||
|
|
||||||
|
SDL_Window* testWindow = SDL_CreateWindow("", 0, 0, 1280, 720,
|
||||||
|
SDL_WINDOW_HIDDEN | StreamUtils::getPlatformWindowFlags());
|
||||||
|
if (!testWindow) {
|
||||||
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
|
"Failed to create test window with platform flags: %s",
|
||||||
|
SDL_GetError());
|
||||||
|
|
||||||
|
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());
|
||||||
|
SDL_QuitSubSystem(SDL_INIT_VIDEO);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Session::getDecoderInfo(testWindow, hasHardwareAcceleration, rendererAlwaysFullScreen, supportsHdr, maximumResolution);
|
||||||
|
|
||||||
|
SDL_DestroyWindow(testWindow);
|
||||||
|
|
||||||
|
SDL_QuitSubSystem(SDL_INIT_VIDEO);
|
||||||
|
|
||||||
|
// Propagate the decoder properties to the SystemProperties singleton and emit any change signals on the main thread
|
||||||
|
QMetaObject::invokeMethod(m_Properties, "updateDecoderProperties",
|
||||||
|
Qt::QueuedConnection,
|
||||||
|
Q_ARG(bool, hasHardwareAcceleration),
|
||||||
|
Q_ARG(bool, rendererAlwaysFullScreen),
|
||||||
|
Q_ARG(QSize, maximumResolution),
|
||||||
|
Q_ARG(bool, supportsHdr));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
SystemProperties* m_Properties;
|
||||||
|
};
|
||||||
|
|
||||||
SystemProperties::SystemProperties()
|
SystemProperties::SystemProperties()
|
||||||
{
|
{
|
||||||
versionString = QString(VERSION_STR);
|
versionString = QString(VERSION_STR);
|
||||||
@@ -68,71 +131,63 @@ SystemProperties::SystemProperties()
|
|||||||
|
|
||||||
unmappedGamepads = SdlInputHandler::getUnmappedGamepads();
|
unmappedGamepads = SdlInputHandler::getUnmappedGamepads();
|
||||||
|
|
||||||
// Populate data that requires talking to SDL. We do it all in one shot
|
// These will be queried asynchronously to avoid blocking the UI
|
||||||
// and cache the results to speed up future queries on this data.
|
hasHardwareAcceleration = true;
|
||||||
querySdlVideoInfo();
|
rendererAlwaysFullScreen = false;
|
||||||
|
supportsHdr = true;
|
||||||
|
maximumResolution = QSize(0, 0);
|
||||||
|
|
||||||
Q_ASSERT(!monitorRefreshRates.isEmpty());
|
systemPropertyQueryThread = new SystemPropertyQueryThread(this);
|
||||||
Q_ASSERT(!monitorNativeResolutions.isEmpty());
|
systemPropertyQueryThread->start();
|
||||||
Q_ASSERT(!monitorSafeAreaResolutions.isEmpty());
|
}
|
||||||
|
|
||||||
|
void SystemProperties::updateDecoderProperties(bool hasHardwareAcceleration, bool rendererAlwaysFullScreen, QSize maximumResolution, bool supportsHdr)
|
||||||
|
{
|
||||||
|
if (hasHardwareAcceleration != this->hasHardwareAcceleration) {
|
||||||
|
this->hasHardwareAcceleration = hasHardwareAcceleration;
|
||||||
|
emit hasHardwareAccelerationChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rendererAlwaysFullScreen != this->rendererAlwaysFullScreen) {
|
||||||
|
this->rendererAlwaysFullScreen = rendererAlwaysFullScreen;
|
||||||
|
emit rendererAlwaysFullScreenChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maximumResolution != this->maximumResolution) {
|
||||||
|
this->maximumResolution = maximumResolution;
|
||||||
|
emit maximumResolutionChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (supportsHdr != this->supportsHdr) {
|
||||||
|
this->supportsHdr = supportsHdr;
|
||||||
|
emit supportsHdrChanged();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QRect SystemProperties::getNativeResolution(int displayIndex)
|
QRect SystemProperties::getNativeResolution(int displayIndex)
|
||||||
{
|
{
|
||||||
// Returns default constructed QRect if out of bounds
|
// Returns default constructed QRect if out of bounds
|
||||||
|
systemPropertyQueryThread->wait();
|
||||||
|
Q_ASSERT(!monitorNativeResolutions.isEmpty());
|
||||||
return monitorNativeResolutions.value(displayIndex);
|
return monitorNativeResolutions.value(displayIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
QRect SystemProperties::getSafeAreaResolution(int displayIndex)
|
QRect SystemProperties::getSafeAreaResolution(int displayIndex)
|
||||||
{
|
{
|
||||||
// Returns default constructed QRect if out of bounds
|
// Returns default constructed QRect if out of bounds
|
||||||
|
systemPropertyQueryThread->wait();
|
||||||
|
Q_ASSERT(!monitorSafeAreaResolutions.isEmpty());
|
||||||
return monitorSafeAreaResolutions.value(displayIndex);
|
return monitorSafeAreaResolutions.value(displayIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
int SystemProperties::getRefreshRate(int displayIndex)
|
int SystemProperties::getRefreshRate(int displayIndex)
|
||||||
{
|
{
|
||||||
// Returns 0 if out of bounds
|
// Returns 0 if out of bounds
|
||||||
|
systemPropertyQueryThread->wait();
|
||||||
|
Q_ASSERT(!monitorRefreshRates.isEmpty());
|
||||||
return monitorRefreshRates.value(displayIndex);
|
return monitorRefreshRates.value(displayIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SystemProperties::querySdlVideoInfo()
|
|
||||||
{
|
|
||||||
hasHardwareAcceleration = false;
|
|
||||||
|
|
||||||
if (SDL_InitSubSystem(SDL_INIT_VIDEO) != 0) {
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
|
||||||
"SDL_InitSubSystem(SDL_INIT_VIDEO) failed: %s",
|
|
||||||
SDL_GetError());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update display related attributes (max FPS, native resolution, etc).
|
|
||||||
refreshDisplays();
|
|
||||||
|
|
||||||
SDL_Window* testWindow = SDL_CreateWindow("", 0, 0, 1280, 720,
|
|
||||||
SDL_WINDOW_HIDDEN | StreamUtils::getPlatformWindowFlags());
|
|
||||||
if (!testWindow) {
|
|
||||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
|
||||||
"Failed to create test window with platform flags: %s",
|
|
||||||
SDL_GetError());
|
|
||||||
|
|
||||||
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());
|
|
||||||
SDL_QuitSubSystem(SDL_INIT_VIDEO);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Session::getDecoderInfo(testWindow, hasHardwareAcceleration, rendererAlwaysFullScreen, supportsHdr, maximumResolution);
|
|
||||||
|
|
||||||
SDL_DestroyWindow(testWindow);
|
|
||||||
|
|
||||||
SDL_QuitSubSystem(SDL_INIT_VIDEO);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SystemProperties::refreshDisplays()
|
void SystemProperties::refreshDisplays()
|
||||||
{
|
{
|
||||||
if (SDL_InitSubSystem(SDL_INIT_VIDEO) != 0) {
|
if (SDL_InitSubSystem(SDL_INIT_VIDEO) != 0) {
|
||||||
|
|||||||
@@ -7,14 +7,12 @@ class SystemProperties : public QObject
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
friend class QuerySdlVideoThread;
|
friend class SystemPropertyQueryThread;
|
||||||
friend class RefreshDisplaysThread;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SystemProperties();
|
SystemProperties();
|
||||||
|
|
||||||
Q_PROPERTY(bool hasHardwareAcceleration MEMBER hasHardwareAcceleration CONSTANT)
|
// Static properties queried synchronously during the constructor
|
||||||
Q_PROPERTY(bool rendererAlwaysFullScreen MEMBER rendererAlwaysFullScreen CONSTANT)
|
|
||||||
Q_PROPERTY(bool isRunningWayland MEMBER isRunningWayland CONSTANT)
|
Q_PROPERTY(bool isRunningWayland MEMBER isRunningWayland CONSTANT)
|
||||||
Q_PROPERTY(bool isRunningXWayland MEMBER isRunningXWayland CONSTANT)
|
Q_PROPERTY(bool isRunningXWayland MEMBER isRunningXWayland CONSTANT)
|
||||||
Q_PROPERTY(bool isWow64 MEMBER isWow64 CONSTANT)
|
Q_PROPERTY(bool isWow64 MEMBER isWow64 CONSTANT)
|
||||||
@@ -22,11 +20,15 @@ public:
|
|||||||
Q_PROPERTY(bool hasDesktopEnvironment MEMBER hasDesktopEnvironment CONSTANT)
|
Q_PROPERTY(bool hasDesktopEnvironment MEMBER hasDesktopEnvironment CONSTANT)
|
||||||
Q_PROPERTY(bool hasBrowser MEMBER hasBrowser CONSTANT)
|
Q_PROPERTY(bool hasBrowser MEMBER hasBrowser CONSTANT)
|
||||||
Q_PROPERTY(bool hasDiscordIntegration MEMBER hasDiscordIntegration CONSTANT)
|
Q_PROPERTY(bool hasDiscordIntegration MEMBER hasDiscordIntegration CONSTANT)
|
||||||
Q_PROPERTY(QString unmappedGamepads MEMBER unmappedGamepads NOTIFY unmappedGamepadsChanged)
|
|
||||||
Q_PROPERTY(QSize maximumResolution MEMBER maximumResolution CONSTANT)
|
|
||||||
Q_PROPERTY(QString versionString MEMBER versionString CONSTANT)
|
|
||||||
Q_PROPERTY(bool supportsHdr MEMBER supportsHdr CONSTANT)
|
|
||||||
Q_PROPERTY(bool usesMaterial3Theme MEMBER usesMaterial3Theme CONSTANT)
|
Q_PROPERTY(bool usesMaterial3Theme MEMBER usesMaterial3Theme CONSTANT)
|
||||||
|
Q_PROPERTY(QString versionString MEMBER versionString CONSTANT)
|
||||||
|
|
||||||
|
// Properties queried asynchronously
|
||||||
|
Q_PROPERTY(bool hasHardwareAcceleration MEMBER hasHardwareAcceleration NOTIFY hasHardwareAccelerationChanged)
|
||||||
|
Q_PROPERTY(bool rendererAlwaysFullScreen MEMBER rendererAlwaysFullScreen NOTIFY rendererAlwaysFullScreenChanged)
|
||||||
|
Q_PROPERTY(QString unmappedGamepads MEMBER unmappedGamepads NOTIFY unmappedGamepadsChanged)
|
||||||
|
Q_PROPERTY(QSize maximumResolution MEMBER maximumResolution NOTIFY maximumResolutionChanged)
|
||||||
|
Q_PROPERTY(bool supportsHdr MEMBER supportsHdr NOTIFY supportsHdrChanged)
|
||||||
|
|
||||||
Q_INVOKABLE void refreshDisplays();
|
Q_INVOKABLE void refreshDisplays();
|
||||||
Q_INVOKABLE QRect getNativeResolution(int displayIndex);
|
Q_INVOKABLE QRect getNativeResolution(int displayIndex);
|
||||||
@@ -35,12 +37,18 @@ public:
|
|||||||
|
|
||||||
signals:
|
signals:
|
||||||
void unmappedGamepadsChanged();
|
void unmappedGamepadsChanged();
|
||||||
|
void hasHardwareAccelerationChanged();
|
||||||
|
void rendererAlwaysFullScreenChanged();
|
||||||
|
void maximumResolutionChanged();
|
||||||
|
void supportsHdrChanged();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void updateDecoderProperties(bool hasHardwareAcceleration, bool rendererAlwaysFullScreen, QSize maximumResolution, bool supportsHdr);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void querySdlVideoInfo();
|
QThread* systemPropertyQueryThread;
|
||||||
|
|
||||||
bool hasHardwareAcceleration;
|
// Properties set by the constructor
|
||||||
bool rendererAlwaysFullScreen;
|
|
||||||
bool isRunningWayland;
|
bool isRunningWayland;
|
||||||
bool isRunningXWayland;
|
bool isRunningXWayland;
|
||||||
bool isWow64;
|
bool isWow64;
|
||||||
@@ -49,12 +57,18 @@ private:
|
|||||||
bool hasBrowser;
|
bool hasBrowser;
|
||||||
bool hasDiscordIntegration;
|
bool hasDiscordIntegration;
|
||||||
QString unmappedGamepads;
|
QString unmappedGamepads;
|
||||||
|
QString versionString;
|
||||||
|
bool usesMaterial3Theme;
|
||||||
|
|
||||||
|
// Properties set by updateDecoderProperties()
|
||||||
|
bool hasHardwareAcceleration;
|
||||||
|
bool rendererAlwaysFullScreen;
|
||||||
QSize maximumResolution;
|
QSize maximumResolution;
|
||||||
|
bool supportsHdr;
|
||||||
|
|
||||||
|
// Properties set by refreshDisplays()
|
||||||
QList<QRect> monitorNativeResolutions;
|
QList<QRect> monitorNativeResolutions;
|
||||||
QList<QRect> monitorSafeAreaResolutions;
|
QList<QRect> monitorSafeAreaResolutions;
|
||||||
QList<int> monitorRefreshRates;
|
QList<int> monitorRefreshRates;
|
||||||
QString versionString;
|
|
||||||
bool supportsHdr;
|
|
||||||
bool usesMaterial3Theme;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -54,7 +54,18 @@ ApplicationWindow {
|
|||||||
if (SystemProperties.isWow64) {
|
if (SystemProperties.isWow64) {
|
||||||
wow64Dialog.open()
|
wow64Dialog.open()
|
||||||
}
|
}
|
||||||
else if (!SystemProperties.hasHardwareAcceleration && StreamingPreferences.videoDecoderSelection !== StreamingPreferences.VDS_FORCE_SOFTWARE) {
|
|
||||||
|
if (SystemProperties.unmappedGamepads) {
|
||||||
|
unmappedGamepadDialog.unmappedGamepads = SystemProperties.unmappedGamepads
|
||||||
|
unmappedGamepadDialog.open()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hardware acceleration is checked asynchronously
|
||||||
|
SystemProperties.hasHardwareAccelerationChanged.connect(hasHardwareAccelerationChanged)
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasHardwareAccelerationChanged() {
|
||||||
|
if (!SystemProperties.hasHardwareAcceleration && StreamingPreferences.videoDecoderSelection !== StreamingPreferences.VDS_FORCE_SOFTWARE) {
|
||||||
if (SystemProperties.isRunningXWayland) {
|
if (SystemProperties.isRunningXWayland) {
|
||||||
xWaylandDialog.open()
|
xWaylandDialog.open()
|
||||||
}
|
}
|
||||||
@@ -62,13 +73,8 @@ ApplicationWindow {
|
|||||||
noHwDecoderDialog.open()
|
noHwDecoderDialog.open()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SystemProperties.unmappedGamepads) {
|
|
||||||
unmappedGamepadDialog.unmappedGamepads = SystemProperties.unmappedGamepads
|
|
||||||
unmappedGamepadDialog.open()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// It would be better to use TextMetrics here, but it always lays out
|
// It would be better to use TextMetrics here, but it always lays out
|
||||||
// the text slightly more compactly than real Text does in ToolTip,
|
// the text slightly more compactly than real Text does in ToolTip,
|
||||||
// causing unexpected line breaks to be inserted
|
// causing unexpected line breaks to be inserted
|
||||||
|
|||||||
Reference in New Issue
Block a user