feat: Improve FSR2 UI with dropdown quality mode and conditional FSR sharpness

- Convert FSR2 quality mode from number input to dropdown with 4 options
- Disable and grey out FSR sharpness slider when FSR2 is selected
- Add proper enum-based settings for FSR2 quality modes
- Update both Vulkan and OpenGL FSR2 implementations
- Connect UI state changes automatically

Provides better UX by using appropriate controls and preventing confusion
between FSR 1.0 and FSR 2.0 sharpening options.

Signed-off-by: Zephyron <zephyron@citron-emu.org>
This commit is contained in:
Zephyron
2025-07-29 15:49:05 +10:00
parent 3b8da3d80a
commit 568999fd59
7 changed files with 93 additions and 41 deletions

View File

@@ -1,4 +1,5 @@
// SPDX-FileCopyrightText: 2016 Citra Emulator Project // SPDX-FileCopyrightText: 2016 Citra Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm> #include <algorithm>
@@ -288,6 +289,11 @@ void ConfigureGraphics::Setup(const ConfigurationShared::Builder& builder) {
continue; continue;
} }
// Store reference to the FSR sharpness widget for later use
if (setting->Id() == Settings::values.fsr_sharpening_slider.Id()) {
fsr_sharpness_widget = widget;
}
if (setting->Id() == Settings::values.renderer_backend.Id()) { if (setting->Id() == Settings::values.renderer_backend.Id()) {
// Add the renderer combobox now so it's at the top // Add the renderer combobox now so it's at the top
api_grid_layout->addWidget(widget); api_grid_layout->addWidget(widget);
@@ -366,6 +372,41 @@ void ConfigureGraphics::Setup(const ConfigurationShared::Builder& builder) {
api_grid_layout->addWidget(widget); api_grid_layout->addWidget(widget);
} }
// Set up FSR sharpness slider conditional enabling
if (fsr_sharpness_widget) {
// Create a function to update the enabled state based on scaling filter
auto update_fsr_sharpness_enabled = [this]() {
if (fsr_sharpness_widget) {
const auto scaling_filter = Settings::values.scaling_filter.GetValue();
const bool fsr2_selected = (scaling_filter == Settings::ScalingFilter::Fsr2);
fsr_sharpness_widget->setEnabled(!fsr2_selected);
// Grey out the widget when disabled
if (fsr2_selected) {
fsr_sharpness_widget->setStyleSheet(QStringLiteral("QWidget { color: gray; }"));
} else {
fsr_sharpness_widget->setStyleSheet(QStringLiteral(""));
}
}
};
// Initial state
update_fsr_sharpness_enabled();
// Connect to scaling filter changes
if (!Settings::IsConfiguringGlobal()) {
// Find the scaling filter widget and connect to its changes
for (const auto& [id, widget] : hold_graphics) {
if (id == Settings::values.scaling_filter.Id()) {
auto* config_widget = static_cast<ConfigurationShared::Widget*>(widget);
QObject::connect(config_widget->combobox, QOverload<int>::of(&QComboBox::activated),
[update_fsr_sharpness_enabled]() { update_fsr_sharpness_enabled(); });
break;
}
}
}
}
// Background color is too specific to build into the new system, so we manage it here // Background color is too specific to build into the new system, so we manage it here
// (3 settings, all collected into a single widget with a QColor to manage on top) // (3 settings, all collected into a single widget with a QColor to manage on top)
if (Settings::IsConfiguringGlobal()) { if (Settings::IsConfiguringGlobal()) {

View File

@@ -1,4 +1,5 @@
// SPDX-FileCopyrightText: 2016 Citra Emulator Project // SPDX-FileCopyrightText: 2016 Citra Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once
@@ -113,4 +114,5 @@ private:
QWidget* shader_backend_widget; QWidget* shader_backend_widget;
QComboBox* aspect_ratio_combobox; QComboBox* aspect_ratio_combobox;
QComboBox* resolution_combobox; QComboBox* resolution_combobox;
QWidget* fsr_sharpness_widget;
}; };

View File

@@ -404,6 +404,13 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent) {
PAIR(AntiAliasing, Fxaa, tr("FXAA")), PAIR(AntiAliasing, Fxaa, tr("FXAA")),
PAIR(AntiAliasing, Smaa, tr("SMAA")), PAIR(AntiAliasing, Smaa, tr("SMAA")),
}}); }});
translations->insert({Settings::EnumMetadata<Settings::FSR2QualityMode>::Index(),
{
PAIR(FSR2QualityMode, Quality, tr("Quality")),
PAIR(FSR2QualityMode, Balanced, tr("Balanced")),
PAIR(FSR2QualityMode, Performance, tr("Performance")),
PAIR(FSR2QualityMode, UltraPerformance, tr("Ultra Performance")),
}});
translations->insert({Settings::EnumMetadata<Settings::AspectRatio>::Index(), translations->insert({Settings::EnumMetadata<Settings::AspectRatio>::Index(),
{ {
PAIR(AspectRatio, R16_9, tr("Default (16:9)")), PAIR(AspectRatio, R16_9, tr("Default (16:9)")),

View File

@@ -348,10 +348,10 @@ struct Values {
true, true,
true}; true};
SwitchableSetting<int, true> fsr2_quality_mode{linkage, SwitchableSetting<FSR2QualityMode, true> fsr2_quality_mode{linkage,
2, // Performance by default FSR2QualityMode::Performance, // Performance by default
0, // Quality FSR2QualityMode::Quality,
3, // Ultra Performance FSR2QualityMode::UltraPerformance,
"fsr2_quality_mode", "fsr2_quality_mode",
Category::Renderer, Category::Renderer,
Specialization::Default, Specialization::Default,

View File

@@ -151,6 +151,8 @@ ENUM(ScalingFilter, NearestNeighbor, Bilinear, Bicubic, Gaussian, ScaleForce, Fs
ENUM(AntiAliasing, None, Fxaa, Smaa, MaxEnum); ENUM(AntiAliasing, None, Fxaa, Smaa, MaxEnum);
ENUM(FSR2QualityMode, Quality, Balanced, Performance, UltraPerformance);
ENUM(AspectRatio, R16_9, R4_3, R21_9, R16_10, R32_9, Stretch); ENUM(AspectRatio, R16_9, R4_3, R21_9, R16_10, R32_9, Stretch);
ENUM(ConsoleMode, Handheld, Docked); ENUM(ConsoleMode, Handheld, Docked);

View File

@@ -68,16 +68,16 @@ GLuint FSR2::Draw(ProgramManager& program_manager, GLuint texture, u32 input_ima
input_height, output_width, output_height, viewport_x, viewport_y); input_height, output_width, output_height, viewport_x, viewport_y);
// FSR 2.0 uses a different sharpening calculation based on quality mode // FSR 2.0 uses a different sharpening calculation based on quality mode
const int quality_mode = Settings::values.fsr2_quality_mode.GetValue(); const auto quality_mode = Settings::values.fsr2_quality_mode.GetValue();
const float sharpening = [quality_mode]() { const float sharpening = [quality_mode]() {
switch (quality_mode) { switch (quality_mode) {
case 0: // Quality case Settings::FSR2QualityMode::Quality:
return 0.2f; return 0.2f;
case 1: // Balanced case Settings::FSR2QualityMode::Balanced:
return 0.4f; return 0.4f;
case 2: // Performance case Settings::FSR2QualityMode::Performance:
return 0.6f; return 0.6f;
case 3: // Ultra Performance case Settings::FSR2QualityMode::UltraPerformance:
return 0.8f; return 0.8f;
default: default:
return 0.4f; return 0.4f;

View File

@@ -189,16 +189,16 @@ VkImageView FSR2::Draw(Scheduler& scheduler, size_t image_index, VkImage source_
viewport_y); viewport_y);
// FSR 2.0 uses a different sharpening calculation based on quality mode // FSR 2.0 uses a different sharpening calculation based on quality mode
const int quality_mode = Settings::values.fsr2_quality_mode.GetValue(); const auto quality_mode = Settings::values.fsr2_quality_mode.GetValue();
const float sharpening = [quality_mode]() { const float sharpening = [quality_mode]() {
switch (quality_mode) { switch (quality_mode) {
case 0: // Quality case Settings::FSR2QualityMode::Quality:
return 0.2f; return 0.2f;
case 1: // Balanced case Settings::FSR2QualityMode::Balanced:
return 0.4f; return 0.4f;
case 2: // Performance case Settings::FSR2QualityMode::Performance:
return 0.6f; return 0.6f;
case 3: // Ultra Performance case Settings::FSR2QualityMode::UltraPerformance:
return 0.8f; return 0.8f;
default: default:
return 0.4f; return 0.4f;