mirror of
https://github.com/moonlight-stream/moonlight-qt.git
synced 2026-02-16 02:30:52 +00:00
Move heavy SystemProperties loads to an opt-in model
This avoids heavyweight operations when we're not actually running config checks.
This commit is contained in:
@@ -98,51 +98,16 @@ SystemProperties::SystemProperties()
|
||||
hasDiscordIntegration = false;
|
||||
#endif
|
||||
|
||||
unmappedGamepads = SdlInputHandler::getUnmappedGamepads();
|
||||
|
||||
// These will be queried asynchronously to avoid blocking the UI
|
||||
hasHardwareAcceleration = true;
|
||||
rendererAlwaysFullScreen = false;
|
||||
supportsHdr = true;
|
||||
maximumResolution = QSize(0, 0);
|
||||
|
||||
// We initialize the video subsystem and test window on the main thread
|
||||
// because some platforms (macOS) do not support window creation on
|
||||
// non-main threads.
|
||||
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();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
systemPropertyQueryThread = new SystemPropertyQueryThread(this);
|
||||
systemPropertyQueryThread->start();
|
||||
}
|
||||
|
||||
SystemProperties::~SystemProperties()
|
||||
{
|
||||
systemPropertyQueryThread->wait();
|
||||
waitForAsyncLoad();
|
||||
}
|
||||
|
||||
void SystemProperties::updateDecoderProperties(bool hasHardwareAcceleration, bool rendererAlwaysFullScreen, QSize maximumResolution, bool supportsHdr)
|
||||
@@ -177,7 +142,6 @@ void SystemProperties::updateDecoderProperties(bool hasHardwareAcceleration, boo
|
||||
QRect SystemProperties::getNativeResolution(int displayIndex)
|
||||
{
|
||||
// Returns default constructed QRect if out of bounds
|
||||
systemPropertyQueryThread->wait();
|
||||
Q_ASSERT(!monitorNativeResolutions.isEmpty());
|
||||
return monitorNativeResolutions.value(displayIndex);
|
||||
}
|
||||
@@ -185,7 +149,6 @@ QRect SystemProperties::getNativeResolution(int displayIndex)
|
||||
QRect SystemProperties::getSafeAreaResolution(int displayIndex)
|
||||
{
|
||||
// Returns default constructed QRect if out of bounds
|
||||
systemPropertyQueryThread->wait();
|
||||
Q_ASSERT(!monitorSafeAreaResolutions.isEmpty());
|
||||
return monitorSafeAreaResolutions.value(displayIndex);
|
||||
}
|
||||
@@ -193,11 +156,66 @@ QRect SystemProperties::getSafeAreaResolution(int displayIndex)
|
||||
int SystemProperties::getRefreshRate(int displayIndex)
|
||||
{
|
||||
// Returns 0 if out of bounds
|
||||
systemPropertyQueryThread->wait();
|
||||
Q_ASSERT(!monitorRefreshRates.isEmpty());
|
||||
return monitorRefreshRates.value(displayIndex);
|
||||
}
|
||||
|
||||
void SystemProperties::startAsyncLoad()
|
||||
{
|
||||
if (systemPropertyQueryThread) {
|
||||
// Already started/completed
|
||||
return;
|
||||
}
|
||||
|
||||
// This isn't actually asynchronous (due to the need to synchronize with
|
||||
// SdlGamepadKeyNavigation), but we don't query it in the constructor
|
||||
// because it's expensive.
|
||||
unmappedGamepads = SdlInputHandler::getUnmappedGamepads();
|
||||
if (!unmappedGamepads.isEmpty()) {
|
||||
emit unmappedGamepadsChanged();
|
||||
}
|
||||
|
||||
// We initialize the video subsystem and test window on the main thread
|
||||
// because some platforms (macOS) do not support window creation on
|
||||
// non-main threads.
|
||||
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();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
systemPropertyQueryThread = new SystemPropertyQueryThread(this);
|
||||
systemPropertyQueryThread->start();
|
||||
}
|
||||
|
||||
void SystemProperties::waitForAsyncLoad()
|
||||
{
|
||||
if (systemPropertyQueryThread) {
|
||||
systemPropertyQueryThread->wait();
|
||||
}
|
||||
}
|
||||
|
||||
void SystemProperties::refreshDisplays()
|
||||
{
|
||||
if (SDL_InitSubSystem(SDL_INIT_VIDEO) != 0) {
|
||||
|
||||
@@ -26,18 +26,22 @@ public:
|
||||
Q_PROPERTY(bool usesMaterial3Theme MEMBER usesMaterial3Theme CONSTANT)
|
||||
Q_PROPERTY(QString versionString MEMBER versionString CONSTANT)
|
||||
|
||||
// Properties queried asynchronously
|
||||
// Properties queried asynchronously (startAsyncLoad() must be called!)
|
||||
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();
|
||||
// Either startAsyncLoad()+waitForAsyncLoad() or refreshDisplays() must be invoked first
|
||||
Q_INVOKABLE QRect getNativeResolution(int displayIndex);
|
||||
Q_INVOKABLE QRect getSafeAreaResolution(int displayIndex);
|
||||
Q_INVOKABLE int getRefreshRate(int displayIndex);
|
||||
|
||||
Q_INVOKABLE void startAsyncLoad();
|
||||
Q_INVOKABLE void waitForAsyncLoad();
|
||||
Q_INVOKABLE void refreshDisplays();
|
||||
|
||||
signals:
|
||||
void unmappedGamepadsChanged();
|
||||
void hasHardwareAccelerationChanged();
|
||||
@@ -49,8 +53,8 @@ private slots:
|
||||
void updateDecoderProperties(bool hasHardwareAcceleration, bool rendererAlwaysFullScreen, QSize maximumResolution, bool supportsHdr);
|
||||
|
||||
private:
|
||||
QThread* systemPropertyQueryThread;
|
||||
SDL_Window* testWindow;
|
||||
QThread* systemPropertyQueryThread = nullptr;
|
||||
SDL_Window* testWindow = nullptr;
|
||||
|
||||
// Properties set by the constructor
|
||||
bool isRunningWayland;
|
||||
@@ -60,15 +64,15 @@ private:
|
||||
bool hasDesktopEnvironment;
|
||||
bool hasBrowser;
|
||||
bool hasDiscordIntegration;
|
||||
QString unmappedGamepads;
|
||||
QString versionString;
|
||||
bool usesMaterial3Theme;
|
||||
|
||||
// Properties set by updateDecoderProperties()
|
||||
// Properties only set if startAsyncLoad() is called
|
||||
bool hasHardwareAcceleration;
|
||||
bool rendererAlwaysFullScreen;
|
||||
QSize maximumResolution;
|
||||
bool supportsHdr;
|
||||
QString unmappedGamepads;
|
||||
|
||||
// Properties set by refreshDisplays()
|
||||
QList<QRect> monitorNativeResolutions;
|
||||
|
||||
@@ -4,6 +4,7 @@ import QtQuick.Window 2.2
|
||||
|
||||
import SdlGamepadKeyNavigation 1.0
|
||||
import Session 1.0
|
||||
import SystemProperties 1.0
|
||||
|
||||
Item {
|
||||
property Session session
|
||||
@@ -120,6 +121,10 @@ Item {
|
||||
session.sessionFinished.connect(sessionFinished)
|
||||
session.readyForDeletion.connect(sessionReadyForDeletion)
|
||||
|
||||
// Ensure the SystemProperties async thread is finished,
|
||||
// since it may currently be using the SDL video subsystem
|
||||
SystemProperties.waitForAsyncLoad()
|
||||
|
||||
// Kick off the stream
|
||||
spinnerTimer.start()
|
||||
streamLoader.active = true
|
||||
|
||||
@@ -56,13 +56,10 @@ ApplicationWindow {
|
||||
wow64Dialog.open()
|
||||
}
|
||||
|
||||
if (SystemProperties.unmappedGamepads) {
|
||||
unmappedGamepadDialog.unmappedGamepads = SystemProperties.unmappedGamepads
|
||||
unmappedGamepadDialog.open()
|
||||
}
|
||||
|
||||
// Hardware acceleration is checked asynchronously
|
||||
// Hardware acceleration and unmapped gamepads are checked asynchronously
|
||||
SystemProperties.hasHardwareAccelerationChanged.connect(hasHardwareAccelerationChanged)
|
||||
SystemProperties.unmappedGamepadsChanged.connect(hasUnmappedGamepadsChanged)
|
||||
SystemProperties.startAsyncLoad()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,6 +74,13 @@ ApplicationWindow {
|
||||
}
|
||||
}
|
||||
|
||||
function hasUnmappedGamepadsChanged() {
|
||||
if (SystemProperties.unmappedGamepads) {
|
||||
unmappedGamepadDialog.unmappedGamepads = SystemProperties.unmappedGamepads
|
||||
unmappedGamepadDialog.open()
|
||||
}
|
||||
}
|
||||
|
||||
// It would be better to use TextMetrics here, but it always lays out
|
||||
// the text slightly more compactly than real Text does in ToolTip,
|
||||
// causing unexpected line breaks to be inserted
|
||||
|
||||
Reference in New Issue
Block a user