From 90e25e60d61b469118fe0813415690b0b113f89f Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Fri, 3 Feb 2023 00:45:27 -0600 Subject: [PATCH] Add FPS values for all attached displays and support custom FPS values Fixes #926 --- app/backend/systemproperties.cpp | 23 +++++++-- app/backend/systemproperties.h | 4 +- app/gui/SettingsView.qml | 84 +++++++++++++++++++------------- 3 files changed, 70 insertions(+), 41 deletions(-) diff --git a/app/backend/systemproperties.cpp b/app/backend/systemproperties.cpp index ce4c9c81..4a10e970 100644 --- a/app/backend/systemproperties.cpp +++ b/app/backend/systemproperties.cpp @@ -70,7 +70,7 @@ SystemProperties::SystemProperties() // and cache the results to speed up future queries on this data. querySdlVideoInfo(); - Q_ASSERT(maximumStreamingFrameRate >= 60); + Q_ASSERT(!monitorRefreshRates.isEmpty()); Q_ASSERT(!monitorNativeResolutions.isEmpty()); } @@ -80,6 +80,12 @@ QRect SystemProperties::getNativeResolution(int displayIndex) return monitorNativeResolutions.value(displayIndex); } +int SystemProperties::getRefreshRate(int displayIndex) +{ + // Returns 0 if out of bounds + return monitorRefreshRates.value(displayIndex); +} + class QuerySdlVideoThread : public QThread { public: @@ -188,9 +194,6 @@ void SystemProperties::refreshDisplaysInternal() monitorNativeResolutions.clear(); - // Never let the maximum drop below 60 FPS - maximumStreamingFrameRate = 60; - SDL_DisplayMode bestMode; for (int displayIndex = 0; displayIndex < SDL_GetNumVideoDisplays(); displayIndex++) { SDL_DisplayMode desktopMode; @@ -218,7 +221,17 @@ void SystemProperties::refreshDisplaysInternal() } } - maximumStreamingFrameRate = qMax(maximumStreamingFrameRate, bestMode.refresh_rate); + // Try to normalize values around our our standard refresh rates. + // Some displays/OSes report values that are slightly off. + if (bestMode.refresh_rate >= 58 && bestMode.refresh_rate <= 62) { + monitorRefreshRates.append(60); + } + else if (bestMode.refresh_rate >= 28 && bestMode.refresh_rate <= 32) { + monitorRefreshRates.append(30); + } + else { + monitorRefreshRates.append(bestMode.refresh_rate); + } } } diff --git a/app/backend/systemproperties.h b/app/backend/systemproperties.h index 960e2384..39b1c330 100644 --- a/app/backend/systemproperties.h +++ b/app/backend/systemproperties.h @@ -23,13 +23,13 @@ public: Q_PROPERTY(bool hasBrowser MEMBER hasBrowser CONSTANT) Q_PROPERTY(bool hasDiscordIntegration MEMBER hasDiscordIntegration CONSTANT) Q_PROPERTY(QString unmappedGamepads MEMBER unmappedGamepads NOTIFY unmappedGamepadsChanged) - Q_PROPERTY(int maximumStreamingFrameRate MEMBER maximumStreamingFrameRate CONSTANT) Q_PROPERTY(QSize maximumResolution MEMBER maximumResolution CONSTANT) Q_PROPERTY(QString versionString MEMBER versionString CONSTANT) Q_PROPERTY(bool supportsHdr MEMBER supportsHdr CONSTANT) Q_INVOKABLE void refreshDisplays(); Q_INVOKABLE QRect getNativeResolution(int displayIndex); + Q_INVOKABLE int getRefreshRate(int displayIndex); signals: void unmappedGamepadsChanged(); @@ -49,9 +49,9 @@ private: bool hasBrowser; bool hasDiscordIntegration; QString unmappedGamepads; - int maximumStreamingFrameRate; QSize maximumResolution; QList monitorNativeResolutions; + QList monitorRefreshRates; QString versionString; bool supportsHdr; }; diff --git a/app/gui/SettingsView.qml b/app/gui/SettingsView.qml index 549410a7..b8d3f242 100644 --- a/app/gui/SettingsView.qml +++ b/app/gui/SettingsView.qml @@ -388,48 +388,58 @@ Flickable { } AutoResizingComboBox { + function addRefreshRateOrdered(fpsListModel, refreshRate, description) { + var indexToAdd = 0 + for (var j = 0; j < fpsListModel.count; j++) { + var existing_fps = parseInt(fpsListModel.get(j).video_fps); + + if (refreshRate === existing_fps) { + // Duplicate entry, skip + indexToAdd = -1 + break + } + else if (refreshRate > existing_fps) { + // Candidate entrypoint after this entry + indexToAdd = j + 1 + } + } + + // Insert this display's resolution if it's not a duplicate + if (indexToAdd >= 0) { + fpsListModel.insert(indexToAdd, + { + "text": description, + "video_fps": ""+refreshRate + }) + } + + return indexToAdd + } + function createModel() { var fpsListModel = Qt.createQmlObject('import QtQuick 2.0; ListModel {}', parent, '') - var max_fps = SystemProperties.maximumStreamingFrameRate - // Default entries fpsListModel.append({"text": qsTr("%1 FPS").arg("30"), "video_fps": "30"}) fpsListModel.append({"text": qsTr("%1 FPS").arg("60"), "video_fps": "60"}) - // Add unsupported FPS values that come before the display max FPS - if (StreamingPreferences.unsupportedFps) { - if (max_fps > 90) { - fpsListModel.append({"text": qsTr("%1 FPS (Unsupported)").arg("90"), "video_fps": "90"}) - } - if (max_fps > 120) { - fpsListModel.append({"text": qsTr("%1 FPS (Unsupported)").arg("120"), "video_fps": "120"}) + // Add native refresh rate for all attached displays + var done = false + for (var displayIndex = 0; !done; displayIndex++) { + var refreshRate = SystemProperties.getRefreshRate(displayIndex); + if (refreshRate === 0) { + // Exceeded max count of displays + done = true + break } + + addRefreshRateOrdered(fpsListModel, refreshRate, qsTr("%1 FPS").arg(refreshRate)) } - // Use 64 as the cutoff for adding a separate option to - // handle wonky displays that report just over 60 Hz. - if (max_fps > 64) { - // Mark any FPS value greater than 120 as unsupported - if (StreamingPreferences.unsupportedFps && max_fps > 120) { - fpsListModel.append({"text": qsTr("%1 FPS (Unsupported)").arg(max_fps), "video_fps": ""+max_fps}) - } - else if (max_fps > 120) { - fpsListModel.append({"text": qsTr("%1 FPS").arg("120"), "video_fps": "120"}) - } - else { - fpsListModel.append({"text": qsTr("%1 FPS").arg(max_fps), "video_fps": ""+max_fps}) - } - } - - // Add unsupported FPS values that come after the display max FPS + // Add unsupported FPS values if (StreamingPreferences.unsupportedFps) { - if (max_fps < 90) { - fpsListModel.append({"text":qsTr("%1 FPS (Unsupported)").arg("90"), "video_fps": "90"}) - } - if (max_fps < 120) { - fpsListModel.append({"text":qsTr("%1 FPS (Unsupported)").arg("120"), "video_fps": "120"}) - } + addRefreshRateOrdered(fpsListModel, 90, qsTr("%1 FPS (Unsupported)").arg(90)) + addRefreshRateOrdered(fpsListModel, 120, qsTr("%1 FPS (Unsupported)").arg(120)) } return fpsListModel @@ -439,16 +449,22 @@ Flickable { model = createModel() var saved_fps = StreamingPreferences.fps - currentIndex = 0 + currentIndex = -1 for (var i = 0; i < model.count; i++) { var el_fps = parseInt(model.get(i).video_fps); - // Pick the highest value lesser or equal to the saved FPS - if (saved_fps >= el_fps) { + // Look for a matching frame rate + if (saved_fps === el_fps) { currentIndex = i + break } } + // If we didn't find one, add a custom frame rate for the current value + if (currentIndex === -1) { + currentIndex = addRefreshRateOrdered(model, saved_fps, qsTr("%1 FPS (Custom)").arg(saved_fps)) + } + // Persist the selected value activated(currentIndex) }