diff --git a/src/qt_common/config/shared_translation.cpp b/src/qt_common/config/shared_translation.cpp index 0db439691f..4ef97c602a 100644 --- a/src/qt_common/config/shared_translation.cpp +++ b/src/qt_common/config/shared_translation.cpp @@ -151,6 +151,9 @@ std::unique_ptr InitializeTranslations(QObject* parent) { tr("The anti-aliasing method to use.\nSMAA offers the best quality.\nFXAA " "can produce a more stable picture in lower resolutions.")); INSERT(Settings, post_shader_chain, QString(), QString()); + INSERT(Settings, post_shader_preset, QString(), QString()); + INSERT(Settings, post_shader_enabled, tr("Enable post-processing effects"), + tr("Applies post-processing effects to the final image.")); INSERT(Settings, fullscreen_mode, tr("Fullscreen Mode:"), tr("The method used to render the window in fullscreen.\nBorderless offers the best " "compatibility with the on-screen keyboard that some games request for " diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp index f294403397..641f65d82a 100644 --- a/src/yuzu/configuration/configure_graphics.cpp +++ b/src/yuzu/configuration/configure_graphics.cpp @@ -37,6 +37,10 @@ #include "yuzu/configuration/configure_graphics.h" #include "yuzu/configuration/shared_widget.h" +#ifdef HAS_RESHADE +#include "yuzu/configuration/configure_post_processing.h" +#endif + ConfigureGraphics::ConfigureGraphics( const Core::System& system_, std::vector& records_, const std::function& expose_compute_option_, @@ -216,6 +220,12 @@ void ConfigureGraphics::Setup(const ConfigurationShared::Builder& builder) { std::vector hold_api; for (const auto setting : Settings::values.linkage.by_category[Settings::Category::Renderer]) { +#ifndef HAS_RESHADE + if (setting->Id() == Settings::values.post_shader_enabled.Id()) { + continue; + } +#endif + ConfigurationShared::Widget* widget = [&]() { if (setting->Id() == Settings::values.fsr_sharpening_slider.Id()) { // FSR needs a reversed slider and a 0.5 multiplier @@ -295,6 +305,23 @@ void ConfigureGraphics::Setup(const ConfigurationShared::Builder& builder) { // Keep track of the resolution combobox to update other UI tabs that need it resolution_combobox = widget->combobox; hold_graphics.emplace(setting->Id(), widget); +#ifdef HAS_RESHADE + } else if (setting->Id() == Settings::values.post_shader_enabled.Id()) { + QPushButton* post_shader_button = new QPushButton(tr("Configure Effects..."), widget); + post_shader_button->setVisible(widget->checkbox->isChecked()); + + connect(post_shader_button, &QAbstractButton::clicked, this, [this]() { + ConfigurePostProcessing dialog(this); + dialog.exec(); + }); + connect(widget->checkbox, &QCheckBox::toggled, post_shader_button, + &QWidget::setVisible); + + QBoxLayout* row = qobject_cast(widget->layout()); + row->insertWidget(1, post_shader_button); + + hold_graphics.emplace(setting->Id(), widget); +#endif } else { hold_graphics.emplace(setting->Id(), widget); } diff --git a/src/yuzu/configuration/configure_post_processing.cpp b/src/yuzu/configuration/configure_post_processing.cpp index 6fe46da6c5..659c3618f2 100644 --- a/src/yuzu/configuration/configure_post_processing.cpp +++ b/src/yuzu/configuration/configure_post_processing.cpp @@ -70,10 +70,8 @@ ConfigurePostProcessing::ConfigurePostProcessing(QWidget* parent) : QDialog(pare auto* root = new QVBoxLayout(this); - auto* description = new QLabel( - tr("ReShade FX effects are loaded from the post_shaders folder in the Eden data " - "directory. Changes apply immediately while a game is running."), - this); + auto* description = + new QLabel(tr("Changes apply immediately while a game is running."), this); description->setWordWrap(true); root->addWidget(description); @@ -128,17 +126,15 @@ ConfigurePostProcessing::ConfigurePostProcessing(QWidget* parent) : QDialog(pare }); preset_row->addWidget(save_button); - auto* delete_button = new QPushButton(tr("Delete"), this); - connect(delete_button, &QPushButton::clicked, this, [this]() { - const QString name = preset_combo->currentData().toString(); - if (name.isEmpty()) { - return; - } - VideoCore::DeleteFxPreset(name.toStdString()); - PopulatePresetCombo(); - RefreshPresetStatus(); + auto* clear_button = new QPushButton(tr("Clear"), this); + clear_button->setToolTip(tr("Remove every effect in use and deselect the preset.")); + connect(clear_button, &QPushButton::clicked, this, [this]() { + VideoCore::FxChain::Instance().Clear(); + VideoCore::SetActiveFxPreset(std::string()); + preset_combo->setCurrentIndex(0); + ApplyStructuralChange(); }); - preset_row->addWidget(delete_button); + preset_row->addWidget(clear_button); root->addLayout(preset_row); @@ -203,6 +199,7 @@ void ConfigurePostProcessing::ApplyStructuralChange() { void ConfigurePostProcessing::PopulatePresetCombo() { const QString previous = preset_combo->currentData().toString(); preset_combo->clear(); + preset_combo->addItem(tr("None"), QString()); for (const auto& preset : VideoCore::GetFxPresetCatalog()) { const QString name = QString::fromStdString(preset.name); @@ -212,7 +209,7 @@ void ConfigurePostProcessing::PopulatePresetCombo() { } const int restored = preset_combo->findData(previous); - if (restored >= 0) { + if (!previous.isEmpty() && restored >= 0) { preset_combo->setCurrentIndex(restored); return; } @@ -220,12 +217,21 @@ void ConfigurePostProcessing::PopulatePresetCombo() { const int active = preset_combo->findData(QString::fromStdString(VideoCore::GetActiveFxPreset())); if (active >= 0) { preset_combo->setCurrentIndex(active); + return; } + + preset_combo->setCurrentIndex(0); } void ConfigurePostProcessing::RefreshPresetStatus() { const std::string active = VideoCore::GetActiveFxPreset(); if (active.empty()) { + if (VideoCore::FxChain::Instance().Size() == 0) { + preset_status->setText( + tr("No effects in use. Pick a preset and press Apply, or add effects one " + "by one.")); + return; + } preset_status->setText(tr("Custom chain. Pick a preset above and press Apply to replace it.")); return; } diff --git a/src/yuzu/main_window.cpp b/src/yuzu/main_window.cpp index 95b91a4f38..1d579239a1 100644 --- a/src/yuzu/main_window.cpp +++ b/src/yuzu/main_window.cpp @@ -15,6 +15,7 @@ #include "render/performance_overlay.h" #ifdef HAS_RESHADE #include "configuration/configure_post_processing.h" +#include "video_core/post_processing/fx_preset.h" #endif #include "updater/update_dialog.h" @@ -1090,6 +1091,41 @@ void MainWindow::InitializeWidgets() { statusBar()->insertPermanentWidget(0, volume_button); +#ifdef HAS_RESHADE + post_shader_status_button = new QPushButton(); + post_shader_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton")); + post_shader_status_button->setFocusPolicy(Qt::NoFocus); + post_shader_status_button->setCheckable(true); + connect(post_shader_status_button, &QPushButton::clicked, this, [this] { + const bool enabled = Settings::values.post_shader_enabled.GetValue(); + Settings::values.post_shader_enabled.SetValue(!enabled); + UpdatePostShaderText(); + }); + UpdatePostShaderText(); + post_shader_status_button->setContextMenuPolicy(Qt::CustomContextMenu); + connect(post_shader_status_button, &QPushButton::customContextMenuRequested, + [this](const QPoint& menu_location) { + QMenu context_menu; + + for (auto const& preset : VideoCore::GetFxPresetCatalog()) { + context_menu.addAction(QString::fromStdString(preset.name), + [this, name = preset.name] { + VideoCore::ApplyFxPreset(name); + Settings::values.post_shader_enabled.SetValue(true); + UpdatePostShaderText(); + }); + } + + context_menu.addSeparator(); + context_menu.addAction(tr("Configure Effects..."), this, + &MainWindow::OnPostProcessingShaders); + + context_menu.exec(post_shader_status_button->mapToGlobal(menu_location)); + post_shader_status_button->repaint(); + }); + statusBar()->insertPermanentWidget(0, post_shader_status_button); +#endif + // setup AA button aa_status_button = new QPushButton(); aa_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton")); @@ -3908,6 +3944,7 @@ void MainWindow::OnPostProcessingShaders() { connect(post_processing_dialog, &QDialog::finished, post_processing_dialog, [this]() { post_processing_dialog->deleteLater(); post_processing_dialog = nullptr; + UpdatePostShaderText(); }); } @@ -4262,6 +4299,26 @@ void MainWindow::UpdateAAText() { : aa_text.toUpper()); } +#ifdef HAS_RESHADE +void MainWindow::UpdatePostShaderText() { + const bool enabled = Settings::values.post_shader_enabled.GetValue(); + post_shader_status_button->setChecked(enabled); + + if (!enabled) { + post_shader_status_button->setText(tr("NO FX")); + return; + } + + const std::string preset = VideoCore::GetActiveFxPreset(); + if (preset.empty()) { + post_shader_status_button->setText(tr("FX")); + return; + } + + post_shader_status_button->setText(QString::fromStdString(preset).toUpper()); +} +#endif + void MainWindow::UpdateVolumeUI() { const auto volume_value = static_cast(Settings::values.volume.GetValue()); volume_slider->setValue(volume_value); diff --git a/src/yuzu/main_window.h b/src/yuzu/main_window.h index 2e92df135d..8aedeec721 100644 --- a/src/yuzu/main_window.h +++ b/src/yuzu/main_window.h @@ -448,6 +448,7 @@ private: void UpdateAPIText(); void UpdateFilterText(); void UpdateAAText(); + void UpdatePostShaderText(); void UpdateVolumeUI(); void UpdateStatusBar(); void UpdateGPUAccuracyButton(); @@ -524,6 +525,7 @@ private: QPushButton* dock_status_button = nullptr; QPushButton* filter_status_button = nullptr; QPushButton* aa_status_button = nullptr; + QPushButton* post_shader_status_button = nullptr; VolumeButton* volume_button = nullptr; QWidget* volume_popup = nullptr; QSlider* volume_slider = nullptr;