diff --git a/src/duckstation-qt/CMakeLists.txt b/src/duckstation-qt/CMakeLists.txt index 7a156faf5..3d265f20b 100644 --- a/src/duckstation-qt/CMakeLists.txt +++ b/src/duckstation-qt/CMakeLists.txt @@ -54,6 +54,11 @@ set(SRCS mainwindow.ui memorycardsettingswidget.cpp memorycardsettingswidget.h + postprocessingchainconfigwidget.cpp + postprocessingchainconfigwidget.h + postprocessingchainconfigwidget.ui + postprocessingshaderconfigwidget.cpp + postprocessingshaderconfigwidget.h qtdisplaywidget.cpp qtdisplaywidget.h qthostinterface.cpp diff --git a/src/duckstation-qt/displaysettingswidget.cpp b/src/duckstation-qt/displaysettingswidget.cpp index 7946140ca..2c3f5243f 100644 --- a/src/duckstation-qt/displaysettingswidget.cpp +++ b/src/duckstation-qt/displaysettingswidget.cpp @@ -1,9 +1,11 @@ #include "displaysettingswidget.h" #include "core/gpu.h" #include "core/settings.h" +#include "postprocessingchainconfigwidget.h" #include "qtutils.h" #include "settingsdialog.h" #include "settingwidgetbinder.h" +#include // For enumerating adapters. #include "frontend-common/vulkan_host_display.h" @@ -45,6 +47,25 @@ DisplaySettingsWidget::DisplaySettingsWidget(QtHostInterface* host_interface, QW &DisplaySettingsWidget::onGPUAdapterIndexChanged); populateGPUAdapters(); + { + std::string post_chain = g_host_interface->GetStringSettingValue("Display", "PostProcessChain"); + if (!post_chain.empty() && !m_ui.postChain->setConfigString(post_chain)) + { + QMessageBox::critical(this, tr("Error"), + tr("The current post-processing chain is invalid, it has been reset. Any changes made will " + "overwrite the existing config.")); + } + } + connect(m_ui.postChain, &PostProcessingChainConfigWidget::chainConfigStringChanged, + [this](const std::string& new_config) { + if (new_config.empty()) + m_host_interface->RemoveSettingValue("Display", "PostProcessChain"); + else + m_host_interface->SetStringSettingValue("Display", "PostProcessChain", new_config.c_str()); + + m_host_interface->applySettings(); + }); + dialog->registerWidgetHelp( m_ui.renderer, tr("Renderer"), Settings::GetRendererDisplayName(Settings::DEFAULT_GPU_RENDERER), tr("Chooses the backend to use for rendering the console/game visuals.
Depending on your system and hardware, " diff --git a/src/duckstation-qt/displaysettingswidget.h b/src/duckstation-qt/displaysettingswidget.h index 9a941ace1..a940b0486 100644 --- a/src/duckstation-qt/displaysettingswidget.h +++ b/src/duckstation-qt/displaysettingswidget.h @@ -5,6 +5,7 @@ #include "ui_displaysettingswidget.h" class QtHostInterface; +class PostProcessingChainConfigWidget; class SettingsDialog; class DisplaySettingsWidget : public QWidget diff --git a/src/duckstation-qt/displaysettingswidget.ui b/src/duckstation-qt/displaysettingswidget.ui index 22a5eb30b..79cfcaaa6 100644 --- a/src/duckstation-qt/displaysettingswidget.ui +++ b/src/duckstation-qt/displaysettingswidget.ui @@ -26,86 +26,90 @@ 0 - + - + Basic - - + + - + - Renderer: + Renderer: - + - + - + - Adapter: + Adapter: - + - - - - - - - - - Screen Display - - - - - - Aspect Ratio: - - - - - - - - - - Crop: - - - - - + - + - Linear Upscaling + VSync - + - - - - Integer Upscaling - - - - - - - VSync - - - - + - - + + + + + Screen Display + + + + + + Aspect Ratio: + + + + + + + + + + Crop: + + + + + + + + + + + + Linear Upscaling + + + + + + + Integer Upscaling + + + + + + + + + On-Screen Display @@ -149,21 +153,53 @@ - + + + + Post-Processing Chain + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + - + Qt::Vertical - - + + - 20 - 40 + 20 + 40 - + - + + + + PostProcessingChainConfigWidget + QWidget +
postprocessingchainconfigwidget.h
+ 1 +
+
diff --git a/src/duckstation-qt/duckstation-qt.vcxproj b/src/duckstation-qt/duckstation-qt.vcxproj index ad2d52bf4..9598d1651 100644 --- a/src/duckstation-qt/duckstation-qt.vcxproj +++ b/src/duckstation-qt/duckstation-qt.vcxproj @@ -56,6 +56,8 @@ + + @@ -84,6 +86,8 @@ + + @@ -143,6 +147,9 @@ Document + + Document + @@ -169,6 +176,8 @@ + + diff --git a/src/duckstation-qt/postprocessingchainconfigwidget.cpp b/src/duckstation-qt/postprocessingchainconfigwidget.cpp new file mode 100644 index 000000000..1968b9140 --- /dev/null +++ b/src/duckstation-qt/postprocessingchainconfigwidget.cpp @@ -0,0 +1,174 @@ +#include "postprocessingchainconfigwidget.h" +#include "frontend-common/postprocessing_chain.h" +#include +#include +#include + +PostProcessingChainConfigWidget::PostProcessingChainConfigWidget(QWidget* parent) : QWidget(parent) +{ + m_ui.setupUi(this); + connectUi(); + updateButtonStates(); +} + +PostProcessingChainConfigWidget::~PostProcessingChainConfigWidget() = default; + +void PostProcessingChainConfigWidget::connectUi() +{ + connect(m_ui.add, &QPushButton::clicked, this, &PostProcessingChainConfigWidget::onAddButtonClicked); + connect(m_ui.remove, &QPushButton::clicked, this, &PostProcessingChainConfigWidget::onRemoveButtonClicked); + connect(m_ui.clear, &QPushButton::clicked, this, &PostProcessingChainConfigWidget::onClearButtonClicked); + connect(m_ui.moveUp, &QPushButton::clicked, this, &PostProcessingChainConfigWidget::onMoveUpButtonClicked); + connect(m_ui.moveDown, &QPushButton::clicked, this, &PostProcessingChainConfigWidget::onMoveDownButtonClicked); + connect(m_ui.shaderSettings, &QPushButton::clicked, this, + &PostProcessingChainConfigWidget::onShaderConfigButtonClicked); + connect(m_ui.shaders, &QListWidget::itemSelectionChanged, this, &PostProcessingChainConfigWidget::updateButtonStates); + + m_ui.loadPreset->setEnabled(false); + m_ui.savePreset->setEnabled(false); +} + +bool PostProcessingChainConfigWidget::setConfigString(const std::string_view& config_string) +{ + if (!m_chain.CreateFromString(config_string)) + return false; + + updateList(); + return true; +} + +std::optional PostProcessingChainConfigWidget::getSelectedIndex() const +{ + QList selected_items = m_ui.shaders->selectedItems(); + return selected_items.empty() ? std::nullopt : + std::optional(selected_items.first()->data(Qt::UserRole).toUInt()); +} + +void PostProcessingChainConfigWidget::updateList() +{ + m_ui.shaders->clear(); + + for (u32 i = 0; i < m_chain.GetStageCount(); i++) + { + const FrontendCommon::PostProcessingShader& shader = m_chain.GetShaderStage(i); + + QListWidgetItem* item = new QListWidgetItem(QString::fromStdString(shader.GetName()), m_ui.shaders); + item->setData(Qt::UserRole, QVariant(i)); + } + + updateButtonStates(); +} + +void PostProcessingChainConfigWidget::configChanged() +{ + if (m_chain.IsEmpty()) + chainConfigStringChanged(std::string()); + else + chainConfigStringChanged(m_chain.GetConfigString()); +} + +void PostProcessingChainConfigWidget::updateButtonStates() +{ + std::optional index = getSelectedIndex(); + m_ui.remove->setEnabled(index.has_value()); + m_ui.clear->setEnabled(!m_chain.IsEmpty()); + m_ui.shaderSettings->setEnabled(index.has_value()); + + if (index.has_value()) + { + m_ui.moveUp->setEnabled(index.value() > 0); + m_ui.moveDown->setEnabled(index.value() < (m_chain.GetStageCount() - 1u)); + } + else + { + m_ui.moveUp->setEnabled(false); + m_ui.moveDown->setEnabled(false); + } +} + +void PostProcessingChainConfigWidget::onAddButtonClicked() +{ + QMenu menu; + + const std::vector shaders(FrontendCommon::PostProcessingChain::GetAvailableShaderNames()); + if (shaders.empty()) + { + menu.addAction(tr("No Shaders Available"))->setEnabled(false); + } + else + { + for (const std::string& shader : shaders) + { + QAction* action = menu.addAction(QString::fromStdString(shader)); + connect(action, &QAction::triggered, [this, &shader]() { + if (!m_chain.AddStage(shader)) + { + QMessageBox::critical(this, tr("Error"), tr("Failed to add shader. The log may contain more information.")); + return; + } + + updateList(); + configChanged(); + }); + } + } + + menu.exec(QCursor::pos()); +} + +void PostProcessingChainConfigWidget::onRemoveButtonClicked() +{ + QList selected_items = m_ui.shaders->selectedItems(); + if (selected_items.empty()) + return; + + QListWidgetItem* item = selected_items.first(); + u32 index = item->data(Qt::UserRole).toUInt(); + if (index < m_chain.GetStageCount()) + { + m_chain.RemoveStage(index); + updateList(); + configChanged(); + } +} + +void PostProcessingChainConfigWidget::onClearButtonClicked() +{ + if (QMessageBox::question(this, tr("Question"), tr("Are you sure you want to clear all shader stages?"), + QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) + { + m_chain.ClearStages(); + updateList(); + configChanged(); + } +} + +void PostProcessingChainConfigWidget::onMoveUpButtonClicked() +{ + std::optional index = getSelectedIndex(); + if (index.has_value()) + { + m_chain.MoveStageUp(index.value()); + updateList(); + configChanged(); + } +} + +void PostProcessingChainConfigWidget::onMoveDownButtonClicked() +{ + std::optional index = getSelectedIndex(); + if (index.has_value()) + { + m_chain.MoveStageDown(index.value()); + updateList(); + configChanged(); + } +} + +void PostProcessingChainConfigWidget::onShaderConfigButtonClicked() +{ + std::optional index = getSelectedIndex(); + if (index.has_value()) + { + } +} diff --git a/src/duckstation-qt/postprocessingchainconfigwidget.h b/src/duckstation-qt/postprocessingchainconfigwidget.h new file mode 100644 index 000000000..3e15ca14a --- /dev/null +++ b/src/duckstation-qt/postprocessingchainconfigwidget.h @@ -0,0 +1,45 @@ +#pragma once +#include "common/types.h" +#include "ui_postprocessingchainconfigwidget.h" +#include "frontend-common/postprocessing_chain.h" +#include +#include +#include +#include + +namespace FrontendCommon { +class PostProcessingChain; +} + +class PostProcessingChainConfigWidget : public QWidget +{ + Q_OBJECT + +public: + PostProcessingChainConfigWidget(QWidget* parent); + ~PostProcessingChainConfigWidget(); + + bool setConfigString(const std::string_view& config_string); + +Q_SIGNALS: + void chainConfigStringChanged(const std::string& new_config_string); + +private Q_SLOTS: + void onAddButtonClicked(); + void onRemoveButtonClicked(); + void onClearButtonClicked(); + void onMoveUpButtonClicked(); + void onMoveDownButtonClicked(); + void onShaderConfigButtonClicked(); + void updateButtonStates(); + +private: + void connectUi(); + std::optional getSelectedIndex() const; + void updateList(); + void configChanged(); + + Ui::PostProcessingChainConfigWidget m_ui; + + FrontendCommon::PostProcessingChain m_chain; +}; diff --git a/src/duckstation-qt/postprocessingchainconfigwidget.ui b/src/duckstation-qt/postprocessingchainconfigwidget.ui new file mode 100644 index 000000000..2f9647492 --- /dev/null +++ b/src/duckstation-qt/postprocessingchainconfigwidget.ui @@ -0,0 +1,130 @@ + + + PostProcessingChainConfigWidget + + + + 0 + 0 + 497 + 151 + + + + Form + + + + + + + + Add + + + + :/icons/list-add.png:/icons/list-add.png + + + + + + + Remove + + + + :/icons/list-remove.png:/icons/list-remove.png + + + + + + + Clear + + + + :/icons/edit-clear-16.png:/icons/edit-clear-16.png + + + + + + + Move Up + + + + :/icons/go-up-16.png:/icons/go-up-16.png + + + + + + + Move Down + + + + :/icons/go-down-16.png:/icons/go-down-16.png + + + + + + + Shader Settings... + + + + :/icons/preferences-system@2x.png:/icons/preferences-system@2x.png + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Load Preset + + + + + + + Save Preset + + + + + + + + + + 0 + 50 + + + + + + + + + + + diff --git a/src/duckstation-qt/postprocessingshaderconfigwidget.cpp b/src/duckstation-qt/postprocessingshaderconfigwidget.cpp new file mode 100644 index 000000000..e69de29bb diff --git a/src/duckstation-qt/postprocessingshaderconfigwidget.h b/src/duckstation-qt/postprocessingshaderconfigwidget.h new file mode 100644 index 000000000..e69de29bb diff --git a/src/duckstation-qt/resources/icons/edit-clear-16.png b/src/duckstation-qt/resources/icons/edit-clear-16.png new file mode 100644 index 000000000..44bb5d77d Binary files /dev/null and b/src/duckstation-qt/resources/icons/edit-clear-16.png differ diff --git a/src/duckstation-qt/resources/icons/edit-clear-16@2x.png b/src/duckstation-qt/resources/icons/edit-clear-16@2x.png new file mode 100644 index 000000000..3450882d7 Binary files /dev/null and b/src/duckstation-qt/resources/icons/edit-clear-16@2x.png differ diff --git a/src/duckstation-qt/resources/icons/go-down-16.png b/src/duckstation-qt/resources/icons/go-down-16.png new file mode 100644 index 000000000..52df792dc Binary files /dev/null and b/src/duckstation-qt/resources/icons/go-down-16.png differ diff --git a/src/duckstation-qt/resources/icons/go-down-16@2x.png b/src/duckstation-qt/resources/icons/go-down-16@2x.png new file mode 100644 index 000000000..84d64c2d7 Binary files /dev/null and b/src/duckstation-qt/resources/icons/go-down-16@2x.png differ diff --git a/src/duckstation-qt/resources/icons/go-up-16.png b/src/duckstation-qt/resources/icons/go-up-16.png new file mode 100644 index 000000000..f06bbbdf8 Binary files /dev/null and b/src/duckstation-qt/resources/icons/go-up-16.png differ diff --git a/src/duckstation-qt/resources/icons/go-up-16@2x.png b/src/duckstation-qt/resources/icons/go-up-16@2x.png new file mode 100644 index 000000000..18bbd26a4 Binary files /dev/null and b/src/duckstation-qt/resources/icons/go-up-16@2x.png differ diff --git a/src/duckstation-qt/resources/resources.qrc b/src/duckstation-qt/resources/resources.qrc index a70865ce5..9e86a91e1 100644 --- a/src/duckstation-qt/resources/resources.qrc +++ b/src/duckstation-qt/resources/resources.qrc @@ -32,6 +32,8 @@ icons/duck.png icons/duck_128.png icons/duck_64.png + icons/edit-clear-16.png + icons/edit-clear-16@2x.png icons/edit-find.png icons/flag-eu.png icons/flag-eu@2x.png @@ -43,6 +45,10 @@ icons/flag-us@2x.png icons/folder-open.png icons/folder-open@2x.png + icons/go-down-16.png + icons/go-down-16@2x.png + icons/go-up-16.png + icons/go-up-16@2x.png icons/input-gaming.png icons/input-gaming@2x.png icons/list-add.png