From 93e0a2e52ac6d9c8d9ce26b97b4f5cbae155deee Mon Sep 17 00:00:00 2001 From: flatulation <36717206+flatulation@users.noreply.github.com> Date: Sun, 20 May 2018 23:03:40 +0100 Subject: [PATCH] Qt: Implement post-processing shader configuration window --- Source/Core/DolphinQt2/CMakeLists.txt | 1 + .../Config/Graphics/EnhancementsWidget.cpp | 9 + .../Config/Graphics/EnhancementsWidget.h | 1 + .../Graphics/PostProcessingConfigWindow.cpp | 361 ++++++++++++++++++ .../Graphics/PostProcessingConfigWindow.h | 78 ++++ Source/Core/DolphinQt2/DolphinQt2.vcxproj | 3 + 6 files changed, 453 insertions(+) create mode 100644 Source/Core/DolphinQt2/Config/Graphics/PostProcessingConfigWindow.cpp create mode 100644 Source/Core/DolphinQt2/Config/Graphics/PostProcessingConfigWindow.h diff --git a/Source/Core/DolphinQt2/CMakeLists.txt b/Source/Core/DolphinQt2/CMakeLists.txt index d48ae51122..3d205741b3 100644 --- a/Source/Core/DolphinQt2/CMakeLists.txt +++ b/Source/Core/DolphinQt2/CMakeLists.txt @@ -39,6 +39,7 @@ add_executable(dolphin-emu Config/Graphics/GraphicsSlider.cpp Config/Graphics/GraphicsWidget.cpp Config/Graphics/GraphicsWindow.cpp + Config/Graphics/PostProcessingConfigWindow.cpp Config/Graphics/SoftwareRendererWidget.cpp Config/InfoWidget.cpp Config/LogConfigWidget.cpp diff --git a/Source/Core/DolphinQt2/Config/Graphics/EnhancementsWidget.cpp b/Source/Core/DolphinQt2/Config/Graphics/EnhancementsWidget.cpp index 872b05101b..4164fe2cdd 100644 --- a/Source/Core/DolphinQt2/Config/Graphics/EnhancementsWidget.cpp +++ b/Source/Core/DolphinQt2/Config/Graphics/EnhancementsWidget.cpp @@ -18,6 +18,7 @@ #include "DolphinQt2/Config/Graphics/GraphicsChoice.h" #include "DolphinQt2/Config/Graphics/GraphicsSlider.h" #include "DolphinQt2/Config/Graphics/GraphicsWindow.h" +#include "DolphinQt2/Config/Graphics/PostProcessingConfigWindow.h" #include "DolphinQt2/Settings.h" #include "UICommon/VideoUtils.h" #include "VideoCommon/PostProcessing.h" @@ -131,6 +132,8 @@ void EnhancementsWidget::ConnectWidgets() [this](int) { SaveSettings(); }); connect(m_3d_mode, static_cast(&QComboBox::currentIndexChanged), [this](int) { SaveSettings(); }); + connect(m_configure_pp_effect, &QPushButton::pressed, this, + &EnhancementsWidget::ConfigurePostProcessingShader); } void EnhancementsWidget::LoadSettings() @@ -318,3 +321,9 @@ void EnhancementsWidget::AddDescriptions() AddDescription(m_3d_convergence, TR_3D_CONVERGENCE_DESCRIPTION); AddDescription(m_3d_swap_eyes, TR_3D_SWAP_EYES_DESCRIPTION); } + +void EnhancementsWidget::ConfigurePostProcessingShader() +{ + const std::string shader = Config::Get(Config::GFX_ENHANCE_POST_SHADER); + PostProcessingConfigWindow(this, shader).exec(); +} diff --git a/Source/Core/DolphinQt2/Config/Graphics/EnhancementsWidget.h b/Source/Core/DolphinQt2/Config/Graphics/EnhancementsWidget.h index 84001a9633..7997ec7ea4 100644 --- a/Source/Core/DolphinQt2/Config/Graphics/EnhancementsWidget.h +++ b/Source/Core/DolphinQt2/Config/Graphics/EnhancementsWidget.h @@ -25,6 +25,7 @@ private: void CreateWidgets(); void ConnectWidgets(); void AddDescriptions(); + void ConfigurePostProcessingShader(); // Enhancements QComboBox* m_ir_combo; diff --git a/Source/Core/DolphinQt2/Config/Graphics/PostProcessingConfigWindow.cpp b/Source/Core/DolphinQt2/Config/Graphics/PostProcessingConfigWindow.cpp new file mode 100644 index 0000000000..383426ccf2 --- /dev/null +++ b/Source/Core/DolphinQt2/Config/Graphics/PostProcessingConfigWindow.cpp @@ -0,0 +1,361 @@ +// Copyright 2018 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "DolphinQt2/Config/Graphics/PostProcessingConfigWindow.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "DolphinQt2/Config/Graphics/EnhancementsWidget.h" +#include "VideoCommon/PostProcessing.h" +#include "VideoCommon/RenderBase.h" +#include "VideoCommon/VideoConfig.h" + +using ConfigurationOption = PostProcessingShaderConfiguration::ConfigurationOption; +using OptionType = ConfigurationOption::OptionType; + +PostProcessingConfigWindow::PostProcessingConfigWindow(EnhancementsWidget* parent, + const std::string& shader) + : QDialog(parent), m_shader(shader) +{ + if (g_renderer && g_renderer->GetPostProcessor()) + { + m_post_processor = g_renderer->GetPostProcessor()->GetConfig(); + } + else + { + m_post_processor = new PostProcessingShaderConfiguration(); + m_post_processor->LoadShader(m_shader); + } + + setWindowTitle(tr("Post Processing Shader Configuration")); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + + PopulateGroups(); + Create(); + ConnectWidgets(); +} + +PostProcessingConfigWindow::~PostProcessingConfigWindow() +{ + m_post_processor->SaveOptionsConfiguration(); + if (!(g_renderer && g_renderer->GetPostProcessor())) + { + delete m_post_processor; + } +} + +void PostProcessingConfigWindow::PopulateGroups() +{ + const PostProcessingShaderConfiguration::ConfigMap& config_map = m_post_processor->GetOptions(); + + auto config_groups = std::vector>(); + for (const auto& it : config_map) + { + auto config_group = std::make_unique(&it.second); + m_config_map[it.first] = config_group.get(); + config_groups.push_back(std::move(config_group)); + } + + for (auto& config_group : config_groups) + { + const std::string& parent_name = config_group->GetParent(); + if (parent_name.empty()) + { + m_config_groups.emplace_back(std::move(config_group)); + } + else + { + m_config_map[parent_name]->AddSubGroup(std::move(config_group)); + } + } +} + +void PostProcessingConfigWindow::Create() +{ + m_tabs = new QTabWidget(); + auto* const general = new QWidget(m_tabs); + auto* const general_layout = new QGridLayout(general); + + u32 row = 0; + bool add_general_page = false; + for (const auto& it : m_config_groups) + { + if (it->HasSubGroups()) + { + auto* const tab = CreateDependentTab(it); + m_tabs->addTab(tab, QString::fromStdString(it->GetGUIName())); + } + else + { + if (!add_general_page) + { + add_general_page = true; + } + row = it->AddWidgets(this, general_layout, row); + } + } + + if (add_general_page) + { + m_tabs->insertTab(0, general, tr("General")); + } + + m_buttons = new QDialogButtonBox(QDialogButtonBox::Ok); + + auto* layout = new QVBoxLayout(this); + layout->addWidget(m_tabs); + layout->addWidget(m_buttons); +} + +void PostProcessingConfigWindow::ConnectWidgets() +{ + connect(m_buttons, &QDialogButtonBox::accepted, this, &PostProcessingConfigWindow::accept); +} + +QWidget* +PostProcessingConfigWindow::CreateDependentTab(const std::unique_ptr& config_group) +{ + auto* const tab = new QWidget(m_tabs); + auto* const layout = new QGridLayout(tab); + + u32 row = config_group->AddWidgets(this, layout, 0); + for (const auto& child : config_group->GetSubGroups()) + { + row = child->AddWidgets(this, layout, row); + } + + return tab; +} + +void PostProcessingConfigWindow::UpdateBool(ConfigGroup* const config_group, const bool state) +{ + m_post_processor->SetOptionb(config_group->GetOptionName(), state); + + config_group->EnableSuboptions(state); +} + +void PostProcessingConfigWindow::UpdateInteger(ConfigGroup* const config_group, const int value) +{ + const ConfigurationOption& config_option = + m_post_processor->GetOption(config_group->GetOptionName()); + + const size_t vector_size = config_option.m_integer_values.size(); + + for (size_t i = 0; i < vector_size; ++i) + { + const int current_step = config_group->GetSliderValue(i); + const s32 current_value = config_option.m_integer_step_values[i] * current_step + + config_option.m_integer_min_values[i]; + m_post_processor->SetOptioni(config_option.m_option_name, static_cast(i), current_value); + config_group->SetSliderText(i, QString::number(current_value)); + } +} + +void PostProcessingConfigWindow::UpdateFloat(ConfigGroup* const config_group, const int value) +{ + const ConfigurationOption& config_option = + m_post_processor->GetOption(config_group->GetOptionName()); + + const size_t vector_size = config_option.m_float_values.size(); + + for (size_t i = 0; i < vector_size; ++i) + { + const int current_step = config_group->GetSliderValue(static_cast(i)); + const float current_value = + config_option.m_float_step_values[i] * current_step + config_option.m_float_min_values[i]; + m_post_processor->SetOptionf(config_option.m_option_name, static_cast(i), current_value); + config_group->SetSliderText(i, QString::asprintf("%f", current_value)); + } +} + +PostProcessingConfigWindow::ConfigGroup::ConfigGroup(const ConfigurationOption* config_option) + : m_config_option(config_option) +{ +} + +const std::string& PostProcessingConfigWindow::ConfigGroup::GetGUIName() const noexcept +{ + return m_config_option->m_gui_name; +} + +const std::string& PostProcessingConfigWindow::ConfigGroup::GetParent() const noexcept +{ + return m_config_option->m_dependent_option; +} + +const std::string& PostProcessingConfigWindow::ConfigGroup::GetOptionName() const noexcept +{ + return m_config_option->m_option_name; +} + +void PostProcessingConfigWindow::ConfigGroup::AddSubGroup(std::unique_ptr&& subgroup) +{ + m_subgroups.emplace_back(std::move(subgroup)); +} + +bool PostProcessingConfigWindow::ConfigGroup::HasSubGroups() const noexcept +{ + return !m_subgroups.empty(); +} + +const std::vector>& +PostProcessingConfigWindow::ConfigGroup::GetSubGroups() const noexcept +{ + return m_subgroups; +} + +u32 PostProcessingConfigWindow::ConfigGroup::AddWidgets(PostProcessingConfigWindow* const parent, + QGridLayout* const grid, const u32 row) +{ + auto* const name = new QLabel(QString::fromStdString(m_config_option->m_gui_name)); + grid->addWidget(name, row, 0); + + switch (m_config_option->m_type) + { + case OptionType::OPTION_BOOL: + return AddBool(parent, grid, row); + case OptionType::OPTION_FLOAT: + return AddFloat(parent, grid, row); + case OptionType::OPTION_INTEGER: + return AddInteger(parent, grid, row); + default: + // obviously shouldn't get here + std::abort(); + } +} + +u32 PostProcessingConfigWindow::ConfigGroup::AddBool(PostProcessingConfigWindow* const parent, + QGridLayout* const grid, const u32 row) +{ + m_checkbox = new QCheckBox(); + m_checkbox->setChecked(m_config_option->m_bool_value); + QObject::connect(m_checkbox, &QCheckBox::toggled, + [this, parent](bool checked) { parent->UpdateBool(this, checked); }); + grid->addWidget(m_checkbox, row, 2); + + return row + 1; +} + +u32 PostProcessingConfigWindow::ConfigGroup::AddInteger(PostProcessingConfigWindow* const parent, + QGridLayout* const grid, u32 row) +{ + const size_t vector_size = m_config_option->m_integer_values.size(); + + for (size_t i = 0; i < vector_size; ++i) + { + const int current_value = m_config_option->m_integer_values[i]; + const double range = + m_config_option->m_integer_max_values[i] - m_config_option->m_integer_min_values[i]; + // "How many steps we have is the range divided by the step interval configured. + // This may not be 100% spot on accurate since developers can have odd stepping intervals + // set. + // Round up so if it is outside our range, then set it to the minimum or maximum" + const int steps = + std::ceil(range / static_cast(m_config_option->m_integer_step_values[i])); + + auto* const slider = new QSlider(Qt::Orientation::Horizontal); + slider->setMinimum(0); + slider->setMaximum(steps); + slider->setValue(current_value); + slider->setTickInterval(range / steps); + QObject::connect(slider, &QSlider::valueChanged, + [this, parent](int value) { parent->UpdateInteger(this, value); }); + + auto* const value_box = new QLineEdit(QString::number(current_value)); + value_box->setEnabled(false); + + grid->addWidget(slider, row, 1); + grid->addWidget(value_box, row, 2); + + m_sliders.push_back(slider); + m_value_boxes.push_back(value_box); + if (vector_size > 1) + { + row++; + } + } + + return row + 1; +} + +u32 PostProcessingConfigWindow::ConfigGroup::AddFloat(PostProcessingConfigWindow* const parent, + QGridLayout* const grid, u32 row) +{ + const size_t vector_size = m_config_option->m_float_values.size(); + + for (size_t i = 0; i < vector_size; ++i) + { + const int current_value = + m_config_option->m_float_values[i] / m_config_option->m_float_step_values[i]; + const float range = + m_config_option->m_float_max_values[i] - m_config_option->m_float_min_values[i]; + const int steps = std::ceil(range / m_config_option->m_float_step_values[i]); + + auto* const slider = new QSlider(Qt::Orientation::Horizontal); + slider->setMinimum(0); + slider->setMaximum(steps); + slider->setValue(current_value); + slider->setTickInterval(range / steps); + QObject::connect(slider, &QSlider::valueChanged, + [this, parent](int value) { parent->UpdateFloat(this, value); }); + + auto* const value_box = + new QLineEdit(QString::asprintf("%f", m_config_option->m_float_values[i])); + value_box->setEnabled(false); + + grid->addWidget(slider, row, 1); + grid->addWidget(value_box, row, 2); + + m_sliders.push_back(slider); + m_value_boxes.push_back(value_box); + if (vector_size > 1) + { + row++; + } + } + + return row + 1; +} + +void PostProcessingConfigWindow::ConfigGroup::EnableSuboptions(const bool state) +{ + for (auto& it : m_subgroups) + { + if (it->m_config_option->m_type == OptionType::OPTION_BOOL) + { + it->m_checkbox->setEnabled(state); + } + else + { + for (auto& slider : it->m_sliders) + { + slider->setEnabled(state); + } + } + it->EnableSuboptions(state); + } +} + +int PostProcessingConfigWindow::ConfigGroup::GetSliderValue(size_t index) const +{ + return m_sliders[index]->value(); +} + +void PostProcessingConfigWindow::ConfigGroup::SetSliderText(size_t index, const QString& text) +{ + m_value_boxes[index]->setText(text); +} diff --git a/Source/Core/DolphinQt2/Config/Graphics/PostProcessingConfigWindow.h b/Source/Core/DolphinQt2/Config/Graphics/PostProcessingConfigWindow.h new file mode 100644 index 0000000000..4d78404981 --- /dev/null +++ b/Source/Core/DolphinQt2/Config/Graphics/PostProcessingConfigWindow.h @@ -0,0 +1,78 @@ +// Copyright 2018 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include +#include + +#include + +#include "Common/CommonTypes.h" +#include "VideoCommon/PostProcessing.h" + +class EnhancementsWidget; +class QCheckBox; +class QDialogButtonBox; +class QGridLayout; +class QLineEdit; +class QSlider; +class QTabWidget; +class QWidget; + +class PostProcessingConfigWindow final : public QDialog +{ + Q_OBJECT +public: + explicit PostProcessingConfigWindow(EnhancementsWidget* parent, const std::string& shader); + ~PostProcessingConfigWindow(); + +private: + class ConfigGroup final + { + public: + explicit ConfigGroup( + const PostProcessingShaderConfiguration::ConfigurationOption* config_option); + + const std::string& GetGUIName() const noexcept; + const std::string& GetParent() const noexcept; + const std::string& GetOptionName() const noexcept; + void AddSubGroup(std::unique_ptr&& subgroup); + bool HasSubGroups() const noexcept; + const std::vector>& GetSubGroups() const noexcept; + u32 AddWidgets(PostProcessingConfigWindow* parent, QGridLayout* grid, u32 row); + void EnableSuboptions(bool state); + int GetSliderValue(size_t index) const; + void SetSliderText(size_t index, const QString& text); + + private: + u32 AddBool(PostProcessingConfigWindow* parent, QGridLayout* grid, u32 row); + u32 AddInteger(PostProcessingConfigWindow* parent, QGridLayout* grid, u32 row); + u32 AddFloat(PostProcessingConfigWindow* parent, QGridLayout* grid, u32 row); + + QCheckBox* m_checkbox; + std::vector m_sliders; + std::vector m_value_boxes; + + const PostProcessingShaderConfiguration::ConfigurationOption* m_config_option; + std::vector> m_subgroups; + }; + void Create(); + void ConnectWidgets(); + QWidget* CreateDependentTab(const std::unique_ptr& config_group); + void PopulateGroups(); + void UpdateBool(ConfigGroup* config_group, bool state); + void UpdateInteger(ConfigGroup* config_group, int value); + void UpdateFloat(ConfigGroup* config_group, int value); + + QTabWidget* m_tabs; + QDialogButtonBox* m_buttons; + + const std::string& m_shader; + PostProcessingShaderConfiguration* m_post_processor; + std::unordered_map m_config_map; + std::vector> m_config_groups; +}; diff --git a/Source/Core/DolphinQt2/DolphinQt2.vcxproj b/Source/Core/DolphinQt2/DolphinQt2.vcxproj index 9e5cc99d14..9a3334767a 100644 --- a/Source/Core/DolphinQt2/DolphinQt2.vcxproj +++ b/Source/Core/DolphinQt2/DolphinQt2.vcxproj @@ -99,6 +99,7 @@ + @@ -232,6 +233,7 @@ + @@ -269,6 +271,7 @@ +