[UI] Fix post-processing dialog.

Should now have correct sharpening options and work right with gamepad
input. Also fixing the per-game UI override as that had an extra entry
for some reason.
This commit is contained in:
Herman S.
2025-12-12 00:03:27 +09:00
parent 590c1f9546
commit f663f041ed
10 changed files with 394 additions and 155 deletions
+7
View File
@@ -1005,6 +1005,13 @@ void EmulatorWindow::UpdateDitherCvar(bool value) {
}
}
void EmulatorWindow::UpdateFsrMaxUpsamplingPassesCvar(uint32_t value) {
auto cvars_config = GetGuestOutputPaintConfigForCvars();
if (cvars_config.GetFsrMaxUpsamplingPasses() != value) {
OVERRIDE_uint32(postprocess_ffx_fsr_max_upsampling_passes, value);
}
}
void EmulatorWindow::OnKeyDown(ui::KeyEvent& e) {
if (!emulator_initialized_) {
return;
+17 -16
View File
@@ -131,6 +131,7 @@ class EmulatorWindow {
void UpdateScalingAndSharpeningCvar(
ui::Presenter::GuestOutputPaintConfig::Effect effect);
void UpdateFsrSharpnessCvar(float value);
void UpdateFsrMaxUpsamplingPassesCvar(uint32_t value);
void UpdateCasSharpnessCvar(float value);
void UpdateDitherCvar(bool value);
@@ -170,6 +171,22 @@ class EmulatorWindow {
}
};
// For comparisons, use GetSwapPostEffectForCvarValue instead as the default
// fallback may be used for multiple values.
static const char* GetCvarValueForSwapPostEffect(
gpu::CommandProcessor::SwapPostEffect effect);
static gpu::CommandProcessor::SwapPostEffect GetSwapPostEffectForCvarValue(
const std::string& cvar_value);
// For comparisons, use GetGuestOutputPaintEffectForCvarValue instead as the
// default fallback may be used for multiple values.
static const char* GetCvarValueForGuestOutputPaintEffect(
ui::Presenter::GuestOutputPaintConfig::Effect effect);
static ui::Presenter::GuestOutputPaintConfig::Effect
GetGuestOutputPaintEffectForCvarValue(const std::string& cvar_value);
static ui::Presenter::GuestOutputPaintConfig
GetGuestOutputPaintConfigForCvars();
void ApplyDisplayConfigForCvars();
private:
class EmulatorWindowListener final : public ui::WindowListener,
public ui::WindowInputListener {
@@ -198,22 +215,6 @@ class EmulatorWindow {
bool Initialize();
// For comparisons, use GetSwapPostEffectForCvarValue instead as the default
// fallback may be used for multiple values.
static const char* GetCvarValueForSwapPostEffect(
gpu::CommandProcessor::SwapPostEffect effect);
static gpu::CommandProcessor::SwapPostEffect GetSwapPostEffectForCvarValue(
const std::string& cvar_value);
// For comparisons, use GetGuestOutputPaintEffectForCvarValue instead as the
// default fallback may be used for multiple values.
static const char* GetCvarValueForGuestOutputPaintEffect(
ui::Presenter::GuestOutputPaintConfig::Effect effect);
static ui::Presenter::GuestOutputPaintConfig::Effect
GetGuestOutputPaintEffectForCvarValue(const std::string& cvar_value);
static ui::Presenter::GuestOutputPaintConfig
GetGuestOutputPaintConfigForCvars();
void ApplyDisplayConfigForCvars();
void OnKeyDown(ui::KeyEvent& e);
void OnMouseDown(const ui::MouseEvent& e);
void OnMouseDoubleClick(const ui::MouseEvent& e);
+85
View File
@@ -17,6 +17,7 @@
#include "xenia/base/string.h"
#include "xenia/base/string_buffer.h"
#include "xenia/base/system.h"
#include "xenia/emulator.h"
#include "xenia/ui/config_helpers.h"
toml::parse_result ParseFile(const std::filesystem::path& filename) {
@@ -271,6 +272,90 @@ void SaveGameConfig(uint32_t title_id, const toml::table& config_table) {
}
}
void SaveGameConfigSetting(xe::Emulator* emulator, const char* section,
const char* cvar_name, const std::string& value) {
if (!emulator || !emulator->is_title_open()) {
return;
}
uint32_t title_id = emulator->title_id();
toml::table config_table = LoadGameConfig(title_id);
if (!config_table.contains(section)) {
config_table.insert(section, toml::table{});
}
auto* section_table = config_table[section].as_table();
if (section_table) {
section_table->insert_or_assign(cvar_name, value);
}
SaveGameConfig(title_id, config_table);
}
void SaveGameConfigSetting(xe::Emulator* emulator, const char* section,
const char* cvar_name, bool value) {
if (!emulator || !emulator->is_title_open()) {
return;
}
uint32_t title_id = emulator->title_id();
toml::table config_table = LoadGameConfig(title_id);
if (!config_table.contains(section)) {
config_table.insert(section, toml::table{});
}
auto* section_table = config_table[section].as_table();
if (section_table) {
section_table->insert_or_assign(cvar_name, value);
}
SaveGameConfig(title_id, config_table);
}
void SaveGameConfigSetting(xe::Emulator* emulator, const char* section,
const char* cvar_name, uint32_t value) {
if (!emulator || !emulator->is_title_open()) {
return;
}
uint32_t title_id = emulator->title_id();
toml::table config_table = LoadGameConfig(title_id);
if (!config_table.contains(section)) {
config_table.insert(section, toml::table{});
}
auto* section_table = config_table[section].as_table();
if (section_table) {
section_table->insert_or_assign(cvar_name, value);
}
SaveGameConfig(title_id, config_table);
}
void SaveGameConfigSetting(xe::Emulator* emulator, const char* section,
const char* cvar_name, double value) {
if (!emulator || !emulator->is_title_open()) {
return;
}
uint32_t title_id = emulator->title_id();
toml::table config_table = LoadGameConfig(title_id);
if (!config_table.contains(section)) {
config_table.insert(section, toml::table{});
}
auto* section_table = config_table[section].as_table();
if (section_table) {
section_table->insert_or_assign(cvar_name, value);
}
SaveGameConfig(title_id, config_table);
}
toml::table LoadGameConfig(uint32_t title_id) {
const auto game_config_path =
config_folder / "config" /
+12
View File
@@ -14,6 +14,10 @@
#include <functional>
#include "third_party/tomlplusplus/toml.hpp"
namespace xe {
class Emulator;
} // namespace xe
toml::parse_result ParseFile(const std::filesystem::path& filename);
namespace config {
@@ -25,6 +29,14 @@ std::vector<std::string> LoadGameConfigAsArgs(const std::string_view title_id);
toml::table LoadGameConfig(uint32_t title_id);
void SaveConfig();
void SaveGameConfig(uint32_t title_id, const toml::table& config_table);
void SaveGameConfigSetting(xe::Emulator* emulator, const char* section,
const char* cvar_name, const std::string& value);
void SaveGameConfigSetting(xe::Emulator* emulator, const char* section,
const char* cvar_name, bool value);
void SaveGameConfigSetting(xe::Emulator* emulator, const char* section,
const char* cvar_name, uint32_t value);
void SaveGameConfigSetting(xe::Emulator* emulator, const char* section,
const char* cvar_name, double value);
void ReloadConfig();
void SetConfigSavedCallback(std::function<void()> callback);
} // namespace config
+1 -2
View File
@@ -82,8 +82,7 @@ GetKnownEnumOptions() {
{"readback_resolve", {"fast", "some", "full", "none"}},
{"render_target_path", {"performance", "accuracy"}},
{"postprocess_antialiasing", {"off", "fxaa", "fxaa_extreme"}},
{"postprocess_scaling_and_sharpening",
{"bilinear", "cas", "fsr", "nearest"}},
{"postprocess_scaling_and_sharpening", {"none", "cas", "fsr"}},
{"spirv_version_override", {"auto", "1.0", "1.3", "1.4", "1.5", "1.6"}},
{"xma_decoder", {"old", "new", "master", "fake"}},
{"user_language",
+2 -1
View File
@@ -48,6 +48,8 @@ class GamepadDialog : public QDialog {
// Override to customize which widgets are focusable
virtual bool IsWidgetGamepadFocusable(QWidget* widget) const;
void UpdateFocusableWidgets();
bool eventFilter(QObject* obj, QEvent* event) override;
void showEvent(QShowEvent* event) override;
@@ -55,7 +57,6 @@ class GamepadDialog : public QDialog {
void PollGamepad();
private:
void UpdateFocusableWidgets();
void NavigateFocusVertical(int direction);
void NavigateFocusHorizontal(int direction);
void AcceptFocusedButton();
+10 -57
View File
@@ -322,58 +322,6 @@ void PerformanceTuningDialogQt::LoadCurrentSettings() {
clear_memory_checkbox_->blockSignals(false);
}
void PerformanceTuningDialogQt::SaveToGameConfig(const char* cvar_name,
const std::string& value) {
auto emulator = emulator_window_->emulator();
if (!emulator || !emulator->is_title_open()) {
return;
}
uint32_t title_id = emulator->title_id();
if (title_id == 0) {
return;
}
toml::table config_table = config::LoadGameConfig(title_id);
if (!config_table.contains("GPU")) {
config_table.insert("GPU", toml::table{});
}
auto* gpu_table = config_table["GPU"].as_table();
if (gpu_table) {
gpu_table->insert_or_assign(cvar_name, value);
}
config::SaveGameConfig(title_id, config_table);
}
void PerformanceTuningDialogQt::SaveToGameConfig(const char* cvar_name,
bool value) {
auto emulator = emulator_window_->emulator();
if (!emulator || !emulator->is_title_open()) {
return;
}
uint32_t title_id = emulator->title_id();
if (title_id == 0) {
return;
}
toml::table config_table = config::LoadGameConfig(title_id);
if (!config_table.contains("GPU")) {
config_table.insert("GPU", toml::table{});
}
auto* gpu_table = config_table["GPU"].as_table();
if (gpu_table) {
gpu_table->insert_or_assign(cvar_name, value);
}
config::SaveGameConfig(title_id, config_table);
}
void PerformanceTuningDialogQt::ShowNotification(const QString& title,
const QString& description) {
auto* notification =
@@ -384,14 +332,16 @@ void PerformanceTuningDialogQt::ShowNotification(const QString& title,
void PerformanceTuningDialogQt::OnVsyncChanged(int state) {
bool enabled = (state == Qt::Checked);
SetVsync(enabled);
SaveToGameConfig("vsync", enabled);
config::SaveGameConfigSetting(emulator_window_->emulator(), "GPU", "vsync",
enabled);
ShowNotification("VSync", enabled ? "Enabled" : "Disabled");
}
void PerformanceTuningDialogQt::OnOcclusionQueryChanged(int state) {
bool enabled = (state == Qt::Checked);
SetOcclusionQueryEnable(enabled);
SaveToGameConfig("occlusion_query_enable", enabled);
config::SaveGameConfigSetting(emulator_window_->emulator(), "GPU",
"occlusion_query_enable", enabled);
ShowNotification("Occlusion Queries", enabled ? "Enabled" : "Disabled");
}
@@ -447,8 +397,10 @@ void PerformanceTuningDialogQt::OnReadbackMemexportChanged(int value) {
gpu::SaveGPUSetting(gpu::GPUSetting::ReadbackMemexport, memexport_enabled);
gpu::SaveGPUSetting(gpu::GPUSetting::ReadbackMemexportFast, memexport_fast);
SaveToGameConfig("readback_memexport", memexport_enabled);
SaveToGameConfig("readback_memexport_fast", memexport_fast);
config::SaveGameConfigSetting(emulator_window_->emulator(), "GPU",
"readback_memexport", memexport_enabled);
config::SaveGameConfigSetting(emulator_window_->emulator(), "GPU",
"readback_memexport_fast", memexport_fast);
const char* mode_names[] = {"None", "Fast", "Full"};
ShowNotification("Readback Memexport", mode_names[value]);
@@ -457,7 +409,8 @@ void PerformanceTuningDialogQt::OnReadbackMemexportChanged(int value) {
void PerformanceTuningDialogQt::OnClearMemoryPageStateChanged(int state) {
bool enabled = (state == Qt::Checked);
gpu::SaveGPUSetting(gpu::GPUSetting::ClearMemoryPageState, enabled);
SaveToGameConfig("clear_memory_page_state", enabled);
config::SaveGameConfigSetting(emulator_window_->emulator(), "GPU",
"clear_memory_page_state", enabled);
ShowNotification("Clear Memory Page State", enabled ? "Enabled" : "Disabled");
}
@@ -49,8 +49,6 @@ class PerformanceTuningDialogQt : public ui::GamepadDialog {
private:
void SetupUI();
void LoadCurrentSettings();
void SaveToGameConfig(const char* cvar_name, const std::string& value);
void SaveToGameConfig(const char* cvar_name, bool value);
void ShowNotification(const QString& title, const QString& description);
EmulatorWindow* emulator_window_;
+248 -77
View File
@@ -20,12 +20,14 @@
#include "third_party/fmt/include/fmt/format.h"
#include "xenia/app/emulator_window.h"
#include "xenia/base/cvar.h"
#include "xenia/config.h"
#include "xenia/gpu/graphics_system.h"
#include "xenia/ui/qt_util.h"
DECLARE_bool(postprocess_dither);
DECLARE_double(postprocess_ffx_cas_additional_sharpness);
DECLARE_double(postprocess_ffx_fsr_sharpness_reduction);
DECLARE_uint32(postprocess_ffx_fsr_max_upsampling_passes);
DECLARE_string(postprocess_antialiasing);
DECLARE_string(postprocess_scaling_and_sharpening);
@@ -210,32 +212,162 @@ void PostProcessingDialogQt::SetupUI() {
resampling_layout->setSpacing(8);
resampling_button_group_ = new QButtonGroup(this);
// None / Bilinear option (no controls)
effect_bilinear_radio_ =
new QRadioButton("None / Bilinear", resampling_group_);
effect_cas_radio_ = new QRadioButton(
"AMD FidelityFX Contrast Adaptive Sharpening (CAS)", resampling_group_);
effect_fsr_radio_ = new QRadioButton(
"AMD FidelityFX Super Resolution 1.0 (FSR)", resampling_group_);
resampling_button_group_->addButton(
effect_bilinear_radio_,
static_cast<int>(
ui::Presenter::GuestOutputPaintConfig::Effect::kBilinear));
resampling_layout->addWidget(effect_bilinear_radio_);
// CAS option with controls as children
auto* cas_container = new QWidget(resampling_group_);
auto* cas_container_layout = new QVBoxLayout(cas_container);
cas_container_layout->setContentsMargins(0, 0, 0, 0);
cas_container_layout->setSpacing(4);
effect_cas_radio_ = new QRadioButton(
"AMD FidelityFX Contrast Adaptive Sharpening (CAS)", cas_container);
resampling_button_group_->addButton(
effect_cas_radio_,
static_cast<int>(ui::Presenter::GuestOutputPaintConfig::Effect::kCas));
cas_container_layout->addWidget(effect_cas_radio_);
// CAS controls as children of cas_container
cas_sharpness_widget_ = new QWidget(cas_container);
auto* cas_layout = new QVBoxLayout(cas_sharpness_widget_);
cas_layout->setContentsMargins(20, 4, 0, 0);
cas_sharpness_label_ = new QLabel(
"CAS additional sharpness (higher is sharper):", cas_sharpness_widget_);
cas_layout->addWidget(cas_sharpness_label_);
auto* cas_slider_layout = new QHBoxLayout();
cas_sharpness_slider_ = new QSlider(Qt::Horizontal, cas_sharpness_widget_);
cas_sharpness_slider_->setRange(0, 100); // Will be mapped to 0.0-1.0
cas_sharpness_slider_->setFocusPolicy(Qt::StrongFocus);
cas_sharpness_value_label_ = new QLabel("0%", cas_sharpness_widget_);
cas_sharpness_value_label_->setMinimumWidth(50);
cas_reset_button_ = new QPushButton("Reset", cas_sharpness_widget_);
cas_reset_button_->setMaximumWidth(50);
cas_reset_button_->setFocusPolicy(Qt::StrongFocus);
cas_slider_layout->addWidget(cas_sharpness_slider_);
cas_slider_layout->addWidget(cas_sharpness_value_label_);
cas_slider_layout->addWidget(cas_reset_button_);
cas_layout->addLayout(cas_slider_layout);
cas_container_layout->addWidget(cas_sharpness_widget_);
resampling_layout->addWidget(cas_container);
// FSR option with controls as children
auto* fsr_container = new QWidget(resampling_group_);
auto* fsr_container_layout = new QVBoxLayout(fsr_container);
fsr_container_layout->setContentsMargins(0, 0, 0, 0);
fsr_container_layout->setSpacing(4);
effect_fsr_radio_ = new QRadioButton(
"AMD FidelityFX Super Resolution 1.0 (FSR)", fsr_container);
resampling_button_group_->addButton(
effect_fsr_radio_,
static_cast<int>(ui::Presenter::GuestOutputPaintConfig::Effect::kFsr));
fsr_container_layout->addWidget(effect_fsr_radio_);
resampling_layout->addWidget(effect_bilinear_radio_);
resampling_layout->addWidget(effect_cas_radio_);
resampling_layout->addWidget(effect_fsr_radio_);
// FSR sharpness controls as children of fsr_container
fsr_sharpness_widget_ = new QWidget(fsr_container);
auto* fsr_layout = new QVBoxLayout(fsr_sharpness_widget_);
fsr_layout->setContentsMargins(20, 4, 0, 0);
fsr_sharpness_label_ =
new QLabel("FSR sharpness reduction when upscaling (lower is sharper):",
fsr_sharpness_widget_);
fsr_layout->addWidget(fsr_sharpness_label_);
auto* fsr_slider_layout = new QHBoxLayout();
fsr_sharpness_slider_ = new QSlider(Qt::Horizontal, fsr_sharpness_widget_);
fsr_sharpness_slider_->setRange(0, 200); // Will be mapped to 0.0-2.0
fsr_sharpness_slider_->setFocusPolicy(Qt::StrongFocus);
fsr_sharpness_value_label_ = new QLabel("0%", fsr_sharpness_widget_);
fsr_sharpness_value_label_->setMinimumWidth(50);
fsr_reset_button_ = new QPushButton("Reset", fsr_sharpness_widget_);
fsr_reset_button_->setMaximumWidth(50);
fsr_reset_button_->setFocusPolicy(Qt::StrongFocus);
fsr_slider_layout->addWidget(fsr_sharpness_slider_);
fsr_slider_layout->addWidget(fsr_sharpness_value_label_);
fsr_slider_layout->addWidget(fsr_reset_button_);
fsr_layout->addLayout(fsr_slider_layout);
fsr_container_layout->addWidget(fsr_sharpness_widget_);
// FSR max upsampling controls as children of fsr_container
fsr_max_upsampling_widget_ = new QWidget(fsr_container);
auto* fsr_upsampling_layout = new QVBoxLayout(fsr_max_upsampling_widget_);
fsr_upsampling_layout->setContentsMargins(20, 4, 0, 0);
fsr_max_upsampling_label_ = new QLabel(
"FSR max upsampling passes (lower is faster, higher is better quality):",
fsr_max_upsampling_widget_);
fsr_upsampling_layout->addWidget(fsr_max_upsampling_label_);
fsr_max_upsampling_button_group_ = new QButtonGroup(this);
fsr_max_upsampling_radio_1_ =
new QRadioButton("1", fsr_max_upsampling_widget_);
fsr_max_upsampling_radio_1_->setFocusPolicy(Qt::StrongFocus);
fsr_max_upsampling_radio_2_ =
new QRadioButton("2", fsr_max_upsampling_widget_);
fsr_max_upsampling_radio_2_->setFocusPolicy(Qt::StrongFocus);
fsr_max_upsampling_radio_3_ =
new QRadioButton("3", fsr_max_upsampling_widget_);
fsr_max_upsampling_radio_3_->setFocusPolicy(Qt::StrongFocus);
fsr_max_upsampling_radio_4_ =
new QRadioButton("4", fsr_max_upsampling_widget_);
fsr_max_upsampling_radio_4_->setFocusPolicy(Qt::StrongFocus);
fsr_max_upsampling_button_group_->addButton(fsr_max_upsampling_radio_1_, 1);
fsr_max_upsampling_button_group_->addButton(fsr_max_upsampling_radio_2_, 2);
fsr_max_upsampling_button_group_->addButton(fsr_max_upsampling_radio_3_, 3);
fsr_max_upsampling_button_group_->addButton(fsr_max_upsampling_radio_4_, 4);
auto* fsr_upsampling_radio_layout = new QHBoxLayout();
fsr_upsampling_radio_layout->addWidget(fsr_max_upsampling_radio_1_);
fsr_upsampling_radio_layout->addWidget(fsr_max_upsampling_radio_2_);
fsr_upsampling_radio_layout->addWidget(fsr_max_upsampling_radio_3_);
fsr_upsampling_radio_layout->addWidget(fsr_max_upsampling_radio_4_);
fsr_max_upsampling_reset_button_ =
new QPushButton("Reset", fsr_max_upsampling_widget_);
fsr_max_upsampling_reset_button_->setMaximumWidth(50);
fsr_max_upsampling_reset_button_->setFocusPolicy(Qt::StrongFocus);
fsr_upsampling_radio_layout->addWidget(fsr_max_upsampling_reset_button_);
fsr_upsampling_layout->addLayout(fsr_upsampling_radio_layout);
fsr_container_layout->addWidget(fsr_max_upsampling_widget_);
resampling_layout->addWidget(fsr_container);
connect(resampling_button_group_,
QOverload<int>::of(&QButtonGroup::idClicked), this,
&PostProcessingDialogQt::OnResamplingEffectChanged);
// Connect all the signal handlers
connect(fsr_sharpness_slider_, &QSlider::valueChanged, this,
&PostProcessingDialogQt::OnFsrSharpnessChanged);
connect(fsr_reset_button_, &QPushButton::clicked, this,
&PostProcessingDialogQt::OnResetFsrSharpness);
connect(fsr_max_upsampling_button_group_,
QOverload<int>::of(&QButtonGroup::idClicked), this,
&PostProcessingDialogQt::OnFsrMaxUpsamplingPassesChanged);
connect(fsr_max_upsampling_reset_button_, &QPushButton::clicked, this,
&PostProcessingDialogQt::OnResetFsrMaxUpsamplingPasses);
connect(cas_sharpness_slider_, &QSlider::valueChanged, this,
&PostProcessingDialogQt::OnCasSharpnessChanged);
connect(cas_reset_button_, &QPushButton::clicked, this,
&PostProcessingDialogQt::OnResetCasSharpness);
// Effect description
effect_description_label_ = new QLabel(resampling_group_);
effect_description_label_->setWordWrap(true);
@@ -251,63 +383,6 @@ void PostProcessingDialogQt::SetupUI() {
"QLabel { margin-top: 10px; color: #aad4ff; font-weight: bold; }");
resampling_layout->addWidget(fxaa_recommendation_label_);
// FSR sharpness controls
fsr_sharpness_widget_ = new QWidget(resampling_group_);
auto* fsr_layout = new QVBoxLayout(fsr_sharpness_widget_);
fsr_layout->setContentsMargins(0, 10, 0, 0);
fsr_sharpness_label_ =
new QLabel("FSR sharpness reduction when upscaling (lower is sharper):",
fsr_sharpness_widget_);
fsr_layout->addWidget(fsr_sharpness_label_);
auto* fsr_slider_layout = new QHBoxLayout();
fsr_sharpness_slider_ = new QSlider(Qt::Horizontal, fsr_sharpness_widget_);
fsr_sharpness_slider_->setRange(0, 200); // Will be mapped to 0.0-2.0
fsr_sharpness_value_label_ = new QLabel("0%", fsr_sharpness_widget_);
fsr_sharpness_value_label_->setMinimumWidth(50);
fsr_reset_button_ = new QPushButton("Reset", fsr_sharpness_widget_);
fsr_slider_layout->addWidget(fsr_sharpness_slider_);
fsr_slider_layout->addWidget(fsr_sharpness_value_label_);
fsr_slider_layout->addWidget(fsr_reset_button_);
fsr_layout->addLayout(fsr_slider_layout);
connect(fsr_sharpness_slider_, &QSlider::valueChanged, this,
&PostProcessingDialogQt::OnFsrSharpnessChanged);
connect(fsr_reset_button_, &QPushButton::clicked, this,
&PostProcessingDialogQt::OnResetFsrSharpness);
resampling_layout->addWidget(fsr_sharpness_widget_);
// CAS sharpness controls
cas_sharpness_widget_ = new QWidget(resampling_group_);
auto* cas_layout = new QVBoxLayout(cas_sharpness_widget_);
cas_layout->setContentsMargins(0, 10, 0, 0);
cas_sharpness_label_ = new QLabel(
"CAS additional sharpness (higher is sharper):", cas_sharpness_widget_);
cas_layout->addWidget(cas_sharpness_label_);
auto* cas_slider_layout = new QHBoxLayout();
cas_sharpness_slider_ = new QSlider(Qt::Horizontal, cas_sharpness_widget_);
cas_sharpness_slider_->setRange(0, 100); // Will be mapped to 0.0-1.0
cas_sharpness_value_label_ = new QLabel("0%", cas_sharpness_widget_);
cas_sharpness_value_label_->setMinimumWidth(50);
cas_reset_button_ = new QPushButton("Reset", cas_sharpness_widget_);
cas_slider_layout->addWidget(cas_sharpness_slider_);
cas_slider_layout->addWidget(cas_sharpness_value_label_);
cas_slider_layout->addWidget(cas_reset_button_);
cas_layout->addLayout(cas_slider_layout);
connect(cas_sharpness_slider_, &QSlider::valueChanged, this,
&PostProcessingDialogQt::OnCasSharpnessChanged);
connect(cas_reset_button_, &QPushButton::clicked, this,
&PostProcessingDialogQt::OnResetCasSharpness);
resampling_layout->addWidget(cas_sharpness_widget_);
content_layout->addWidget(resampling_group_);
// Dithering group
@@ -339,6 +414,14 @@ void PostProcessingDialogQt::LoadCurrentSettings() {
return;
}
// Block signals while loading to prevent triggering save handlers
aa_button_group_->blockSignals(true);
resampling_button_group_->blockSignals(true);
fsr_sharpness_slider_->blockSignals(true);
fsr_max_upsampling_button_group_->blockSignals(true);
cas_sharpness_slider_->blockSignals(true);
dither_checkbox_->blockSignals(true);
// Load anti-aliasing settings
auto command_processor = graphics_system->command_processor();
if (command_processor) {
@@ -357,18 +440,41 @@ void PostProcessingDialogQt::LoadCurrentSettings() {
// Set FSR sharpness (convert from 0.0-2.0 to 0-200)
float fsr_sharpness = config.GetFsrSharpnessReduction();
// Apply power 2.0 scaling as done in ImGui version
fsr_sharpness = sqrt(2.f * fsr_sharpness);
fsr_sharpness_slider_->setValue(static_cast<int>(fsr_sharpness * 100));
// Apply power 2.0 scaling as done in ImGui version for slider position
float slider_position = sqrt(2.f * fsr_sharpness);
fsr_sharpness_slider_->setValue(static_cast<int>(slider_position * 100));
// Update label to show actual FSR sharpness percentage
fsr_sharpness_value_label_->setText(SafeQString(
fmt::format("{} %", static_cast<int>(fsr_sharpness * 100))));
// Set FSR max upsampling passes
uint32_t fsr_max_upsampling = config.GetFsrMaxUpsamplingPasses();
// Clamp to valid range 1-4
fsr_max_upsampling =
std::max(uint32_t(1), std::min(uint32_t(4), fsr_max_upsampling));
fsr_max_upsampling_button_group_
->button(static_cast<int>(fsr_max_upsampling))
->setChecked(true);
// Set CAS sharpness (convert from 0.0-1.0 to 0-100)
float cas_sharpness = config.GetCasAdditionalSharpness();
cas_sharpness_slider_->setValue(static_cast<int>(cas_sharpness * 100));
// Update label to match slider value
cas_sharpness_value_label_->setText(SafeQString(
fmt::format("{} %", static_cast<int>(cas_sharpness * 100))));
// Set dithering
dither_checkbox_->setChecked(config.GetDither());
}
// Re-enable signals
aa_button_group_->blockSignals(false);
resampling_button_group_->blockSignals(false);
fsr_sharpness_slider_->blockSignals(false);
fsr_max_upsampling_button_group_->blockSignals(false);
cas_sharpness_slider_->blockSignals(false);
dither_checkbox_->blockSignals(false);
UpdateEffectDescription();
UpdateSharpnessControls();
}
@@ -394,6 +500,12 @@ void PostProcessingDialogQt::OnAntiAliasingChanged(int index) {
// Update cvar
emulator_window_->UpdateAntiAliasingCvar(new_effect);
// Save to game config
const char* effect_str =
EmulatorWindow::GetCvarValueForSwapPostEffect(new_effect);
config::SaveGameConfigSetting(emulator, "Display", "postprocess_antialiasing",
std::string(effect_str));
}
void PostProcessingDialogQt::OnResamplingEffectChanged(int index) {
@@ -421,6 +533,13 @@ void PostProcessingDialogQt::OnResamplingEffectChanged(int index) {
// Update cvar
emulator_window_->UpdateScalingAndSharpeningCvar(new_effect);
// Save to game config
const char* effect_str =
EmulatorWindow::GetCvarValueForGuestOutputPaintEffect(new_effect);
config::SaveGameConfigSetting(emulator, "Display",
"postprocess_scaling_and_sharpening",
std::string(effect_str));
UpdateEffectDescription();
UpdateSharpnessControls();
}
@@ -456,6 +575,11 @@ void PostProcessingDialogQt::OnFsrSharpnessChanged(int value) {
// Update cvar
emulator_window_->UpdateFsrSharpnessCvar(fsr_sharpness);
// Save to game config
config::SaveGameConfigSetting(emulator, "Display",
"postprocess_ffx_fsr_sharpness_reduction",
fsr_sharpness);
}
void PostProcessingDialogQt::OnCasSharpnessChanged(int value) {
@@ -487,6 +611,11 @@ void PostProcessingDialogQt::OnCasSharpnessChanged(int value) {
// Update cvar
emulator_window_->UpdateCasSharpnessCvar(cas_sharpness);
// Save to game config
config::SaveGameConfigSetting(emulator, "Display",
"postprocess_ffx_cas_additional_sharpness",
cas_sharpness);
}
void PostProcessingDialogQt::OnDitherChanged(int state) {
@@ -513,6 +642,10 @@ void PostProcessingDialogQt::OnDitherChanged(int state) {
// Update cvar
emulator_window_->UpdateDitherCvar(dither);
// Save to game config
config::SaveGameConfigSetting(emulator, "Display", "postprocess_dither",
dither);
}
void PostProcessingDialogQt::OnResetFsrSharpness() {
@@ -530,6 +663,45 @@ void PostProcessingDialogQt::OnResetCasSharpness() {
cas_sharpness_slider_->setValue(static_cast<int>(default_value * 100));
}
void PostProcessingDialogQt::OnFsrMaxUpsamplingPassesChanged(int value) {
auto emulator = emulator_window_->emulator();
if (!emulator) {
return;
}
auto graphics_system = emulator->graphics_system();
if (!graphics_system) {
return;
}
auto presenter = graphics_system->presenter();
if (!presenter) {
return;
}
// Convert slider value (1-8) to FSR max upsampling passes
uint32_t max_upsampling_passes = static_cast<uint32_t>(value);
auto config = presenter->GetGuestOutputPaintConfigFromUIThread();
config.SetFsrMaxUpsamplingPasses(max_upsampling_passes);
presenter->SetGuestOutputPaintConfigFromUIThread(config);
// Update cvar
emulator_window_->UpdateFsrMaxUpsamplingPassesCvar(max_upsampling_passes);
// Save to game config
config::SaveGameConfigSetting(emulator, "Display",
"postprocess_ffx_fsr_max_upsampling_passes",
max_upsampling_passes);
}
void PostProcessingDialogQt::OnResetFsrMaxUpsamplingPasses() {
uint32_t default_value =
ui::Presenter::GuestOutputPaintConfig::kFsrMaxUpscalingPassesMax;
fsr_max_upsampling_button_group_->button(static_cast<int>(default_value))
->setChecked(true);
}
void PostProcessingDialogQt::UpdateEffectDescription() {
auto emulator = emulator_window_->emulator();
if (!emulator) {
@@ -607,23 +779,22 @@ void PostProcessingDialogQt::UpdateSharpnessControls() {
// Show/hide FXAA recommendation
fxaa_recommendation_label_->setVisible(is_cas || is_fsr);
// Show/hide FSR sharpness controls
// With the new widget hierarchy, we can simply show/hide the control widgets
fsr_sharpness_widget_->setVisible(is_fsr);
fsr_max_upsampling_widget_->setVisible(is_fsr);
cas_sharpness_widget_->setVisible(is_cas);
// Show/hide CAS sharpness controls
cas_sharpness_widget_->setVisible(is_cas || is_fsr);
// Update CAS label text based on effect
if (is_fsr) {
cas_sharpness_label_->setText(
"CAS additional sharpness when not upscaling (higher is sharper):");
} else {
// CAS label text (only shown for pure CAS, not FSR)
if (is_cas) {
cas_sharpness_label_->setText(
"CAS additional sharpness (higher is sharper):");
}
// Resize dialog to fit content after showing/hiding widgets
adjustSize();
// Update the list of focusable widgets since visibility has changed
UpdateFocusableWidgets();
}
} // namespace app
+12
View File
@@ -41,9 +41,11 @@ class PostProcessingDialogQt : public ui::GamepadDialog {
void OnResamplingEffectChanged(int index);
void OnFsrSharpnessChanged(int value);
void OnCasSharpnessChanged(int value);
void OnFsrMaxUpsamplingPassesChanged(int value);
void OnDitherChanged(int state);
void OnResetFsrSharpness();
void OnResetCasSharpness();
void OnResetFsrMaxUpsamplingPasses();
protected:
void mousePressEvent(QMouseEvent* event) override;
@@ -89,6 +91,16 @@ class PostProcessingDialogQt : public ui::GamepadDialog {
QLabel* cas_sharpness_value_label_;
QPushButton* cas_reset_button_;
// FSR max upsampling passes widgets
QWidget* fsr_max_upsampling_widget_;
QLabel* fsr_max_upsampling_label_;
QButtonGroup* fsr_max_upsampling_button_group_;
QRadioButton* fsr_max_upsampling_radio_1_;
QRadioButton* fsr_max_upsampling_radio_2_;
QRadioButton* fsr_max_upsampling_radio_3_;
QRadioButton* fsr_max_upsampling_radio_4_;
QPushButton* fsr_max_upsampling_reset_button_;
// Dithering widgets
QGroupBox* dither_group_;
QCheckBox* dither_checkbox_;