PostProcessing: Add categories/combos/tooltips

This commit is contained in:
Stenzek 2023-12-07 15:17:28 +10:00
parent 5e45d365c2
commit 10473f1cfb
No known key found for this signature in database
3 changed files with 136 additions and 8 deletions

View file

@ -308,14 +308,45 @@ void PostProcessingShaderConfigWidget::createUi()
{ {
u32 row = 0; u32 row = 0;
const std::string* last_category = nullptr;
for (PostProcessing::ShaderOption& option : m_options) for (PostProcessing::ShaderOption& option : m_options)
{ {
if (option.ui_name.empty()) if (option.ui_name.empty())
continue; continue;
if (!last_category || option.category != *last_category)
{
if (last_category)
m_layout->addItem(new QSpacerItem(1, 4), row++, 0);
if (!option.category.empty())
{
QLabel* label = new QLabel(QString::fromStdString(option.category), this);
QFont label_font(label->font());
label_font.setPointSizeF(12.0f);
label->setFont(label_font);
m_layout->addWidget(label, row++, 0, 1, 3, Qt::AlignLeft);
}
if (last_category)
{
QLabel* line = new QLabel(this);
line->setFrameShape(QFrame::HLine);
line->setFixedHeight(4);
line->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
m_layout->addWidget(line, row++, 0, 1, 3);
}
last_category = &option.category;
}
const QString tooltip = QString::fromStdString(option.tooltip);
if (option.type == PostProcessing::ShaderOption::Type::Bool) if (option.type == PostProcessing::ShaderOption::Type::Bool)
{ {
QCheckBox* checkbox = new QCheckBox(QString::fromStdString(option.ui_name), this); QCheckBox* checkbox = new QCheckBox(QString::fromStdString(option.ui_name), this);
checkbox->setToolTip(tooltip);
checkbox->setChecked(option.value[0].int_value != 0); checkbox->setChecked(option.value[0].int_value != 0);
connect(checkbox, &QCheckBox::stateChanged, [this, &option](int state) { connect(checkbox, &QCheckBox::stateChanged, [this, &option](int state) {
option.value[0].int_value = (state == Qt::Checked) ? 1 : 0; option.value[0].int_value = (state == Qt::Checked) ? 1 : 0;
@ -325,6 +356,24 @@ void PostProcessingShaderConfigWidget::createUi()
m_widgets.push_back(checkbox); m_widgets.push_back(checkbox);
row++; row++;
} }
else if (option.type == PostProcessing::ShaderOption::Type::Int && !option.choice_options.empty())
{
QLabel* label = new QLabel(QString::fromStdString(option.ui_name), this);
label->setToolTip(tooltip);
m_layout->addWidget(label, row, 0, 1, 1, Qt::AlignLeft);
QComboBox* combo = new QComboBox(this);
combo->setToolTip(tooltip);
for (const std::string& combo_option : option.choice_options)
combo->addItem(QString::fromStdString(combo_option));
connect(combo, &QComboBox::currentIndexChanged, [this, &option](int index) {
option.value[0].int_value = index;
updateConfigForOption(option);
});
m_layout->addWidget(combo, row, 1, 1, 2, Qt::AlignLeft);
m_widgets.push_back(combo);
row++;
}
else else
{ {
for (u32 i = 0; i < option.vector_size; i++) for (u32 i = 0; i < option.vector_size; i++)
@ -342,14 +391,17 @@ void PostProcessingShaderConfigWidget::createUi()
} }
QWidget* label_w = new QLabel(label, this); QWidget* label_w = new QLabel(label, this);
label_w->setToolTip(tooltip);
m_layout->addWidget(label_w, row, 0, 1, 1, Qt::AlignLeft); m_layout->addWidget(label_w, row, 0, 1, 1, Qt::AlignLeft);
m_widgets.push_back(label_w); m_widgets.push_back(label_w);
QSlider* slider = new QSlider(Qt::Horizontal, this); QSlider* slider = new QSlider(Qt::Horizontal, this);
slider->setToolTip(tooltip);
m_layout->addWidget(slider, row, 1, 1, 1, Qt::AlignLeft); m_layout->addWidget(slider, row, 1, 1, 1, Qt::AlignLeft);
m_widgets.push_back(slider); m_widgets.push_back(slider);
QLabel* slider_label = new QLabel(this); QLabel* slider_label = new QLabel(this);
slider_label->setToolTip(tooltip);
m_layout->addWidget(slider_label, row, 2, 1, 1, Qt::AlignLeft); m_layout->addWidget(slider_label, row, 2, 1, 1, Qt::AlignLeft);
m_widgets.push_back(slider_label); m_widgets.push_back(slider_label);

View file

@ -51,6 +51,8 @@ struct ShaderOption
std::string name; std::string name;
std::string ui_name; std::string ui_name;
std::string dependent_option; std::string dependent_option;
std::string category;
std::string tooltip;
Type type; Type type;
u32 vector_size; u32 vector_size;
u32 buffer_size; u32 buffer_size;
@ -60,6 +62,7 @@ struct ShaderOption
ValueVector max_value; ValueVector max_value;
ValueVector step_value; ValueVector step_value;
ValueVector value; ValueVector value;
std::vector<std::string> choice_options;
static u32 ParseIntVector(const std::string_view& line, ValueVector* values); static u32 ParseIntVector(const std::string_view& line, ValueVector* values);
static u32 ParseFloatVector(const std::string_view& line, ValueVector* values); static u32 ParseFloatVector(const std::string_view& line, ValueVector* values);

View file

@ -430,9 +430,20 @@ bool PostProcessing::ReShadeFXShader::CreateModule(s32 buffer_width, s32 buffer_
return true; return true;
} }
static bool HasAnnotationWithName(const reshadefx::uniform_info& uniform, const std::string_view& annotation_name)
{
for (const reshadefx::annotation& an : uniform.annotations)
{
if (an.name == annotation_name)
return true;
}
return false;
}
static std::string_view GetStringAnnotationValue(const std::vector<reshadefx::annotation>& annotations, static std::string_view GetStringAnnotationValue(const std::vector<reshadefx::annotation>& annotations,
const std::string_view& annotation_name, const std::string_view annotation_name,
const std::string_view& default_value) const std::string_view default_value)
{ {
for (const reshadefx::annotation& an : annotations) for (const reshadefx::annotation& an : annotations)
{ {
@ -449,7 +460,7 @@ static std::string_view GetStringAnnotationValue(const std::vector<reshadefx::an
} }
static bool GetBooleanAnnotationValue(const std::vector<reshadefx::annotation>& annotations, static bool GetBooleanAnnotationValue(const std::vector<reshadefx::annotation>& annotations,
const std::string_view& annotation_name, bool default_value) const std::string_view annotation_name, bool default_value)
{ {
for (const reshadefx::annotation& an : annotations) for (const reshadefx::annotation& an : annotations)
{ {
@ -466,7 +477,7 @@ static bool GetBooleanAnnotationValue(const std::vector<reshadefx::annotation>&
} }
static PostProcessing::ShaderOption::ValueVector static PostProcessing::ShaderOption::ValueVector
GetVectorAnnotationValue(const reshadefx::uniform_info& uniform, const std::string_view& annotation_name, GetVectorAnnotationValue(const reshadefx::uniform_info& uniform, const std::string_view annotation_name,
const PostProcessing::ShaderOption::ValueVector& default_value) const PostProcessing::ShaderOption::ValueVector& default_value)
{ {
PostProcessing::ShaderOption::ValueVector vv = default_value; PostProcessing::ShaderOption::ValueVector vv = default_value;
@ -477,7 +488,7 @@ GetVectorAnnotationValue(const reshadefx::uniform_info& uniform, const std::stri
const u32 components = std::min<u32>(an.type.components(), PostProcessing::ShaderOption::MAX_VECTOR_COMPONENTS); const u32 components = std::min<u32>(an.type.components(), PostProcessing::ShaderOption::MAX_VECTOR_COMPONENTS);
if (an.type.base == uniform.type.base) if (an.type.base == uniform.type.base || (an.type.is_integral() && uniform.type.is_integral())) // int<->uint
{ {
if (components > 0) if (components > 0)
std::memcpy(&vv[0].float_value, &an.value.as_float[0], sizeof(float) * components); std::memcpy(&vv[0].float_value, &an.value.as_float[0], sizeof(float) * components);
@ -584,6 +595,8 @@ bool PostProcessing::ReShadeFXShader::CreateOptions(const reshadefx::module& mod
ShaderOption opt; ShaderOption opt;
opt.name = ui.name; opt.name = ui.name;
opt.category = GetStringAnnotationValue(ui.annotations, "ui_category", std::string_view());
opt.tooltip = GetStringAnnotationValue(ui.annotations, "ui_tooltip", std::string_view());
if (!GetBooleanAnnotationValue(ui.annotations, "hidden", false)) if (!GetBooleanAnnotationValue(ui.annotations, "hidden", false))
{ {
@ -592,7 +605,7 @@ bool PostProcessing::ReShadeFXShader::CreateOptions(const reshadefx::module& mod
opt.ui_name = ui.name; opt.ui_name = ui.name;
} }
// const std::string_view ui_type = GetStringAnnotationValue(ui.annotations, "ui_type", std::string_view(); const std::string_view ui_type = GetStringAnnotationValue(ui.annotations, "ui_type", std::string_view());
switch (ui.type.base) switch (ui.type.base)
{ {
@ -624,8 +637,8 @@ bool PostProcessing::ReShadeFXShader::CreateOptions(const reshadefx::module& mod
return false; return false;
} }
opt.min_value = GetVectorAnnotationValue(ui, "ui_min", {}); opt.min_value = GetVectorAnnotationValue(ui, "ui_min", opt.default_value);
opt.max_value = GetVectorAnnotationValue(ui, "ui_max", {}); opt.max_value = GetVectorAnnotationValue(ui, "ui_max", opt.default_value);
ShaderOption::ValueVector default_step = {}; ShaderOption::ValueVector default_step = {};
switch (opt.type) switch (opt.type)
{ {
@ -654,6 +667,25 @@ bool PostProcessing::ReShadeFXShader::CreateOptions(const reshadefx::module& mod
} }
opt.step_value = GetVectorAnnotationValue(ui, "ui_step", default_step); opt.step_value = GetVectorAnnotationValue(ui, "ui_step", default_step);
// set a default maximum based on step if there isn't one
if (!HasAnnotationWithName(ui, "ui_max") && HasAnnotationWithName(ui, "ui_step"))
{
for (u32 i = 0; i < opt.vector_size; i++)
{
switch (opt.type)
{
case ShaderOption::Type::Float:
opt.max_value[i].float_value = opt.min_value[i].float_value + (opt.step_value[i].float_value * 100.0f);
break;
case ShaderOption::Type::Int:
opt.max_value[i].int_value = opt.min_value[i].int_value + (opt.step_value[i].int_value * 100);
break;
default:
break;
}
}
}
if (ui.has_initializer_value) if (ui.has_initializer_value)
{ {
std::memcpy(&opt.default_value[0].float_value, &ui.initializer_value.as_float[0], std::memcpy(&opt.default_value[0].float_value, &ui.initializer_value.as_float[0],
@ -667,9 +699,44 @@ bool PostProcessing::ReShadeFXShader::CreateOptions(const reshadefx::module& mod
// Assume default if user doesn't set it. // Assume default if user doesn't set it.
opt.value = opt.default_value; opt.value = opt.default_value;
if (!ui_type.empty() && opt.vector_size > 1)
{
Log_WarningFmt("Uniform '{}' has UI type of '{}' but is vector not scalar ({}), ignoring", opt.name, ui_type,
opt.vector_size);
}
else if (!ui_type.empty())
{
if ((ui_type == "combo" || ui_type == "radio") && opt.type == ShaderOption::Type::Int)
{
const std::string_view ui_values = GetStringAnnotationValue(ui.annotations, "ui_items", std::string_view());
size_t start_pos = 0;
while (start_pos < ui_values.size())
{
size_t end_pos = start_pos;
while (end_pos < ui_values.size() && ui_values[end_pos] != '\0')
end_pos++;
const size_t len = end_pos - start_pos;
if (len > 0)
opt.choice_options.emplace_back(ui_values.substr(start_pos, len));
start_pos = end_pos + 1;
}
// update max if it hasn't been specified
const size_t num_choices = opt.choice_options.size();
if (num_choices > 0)
opt.max_value[0].int_value = std::max(static_cast<s32>(num_choices - 1), opt.max_value[0].int_value);
}
}
m_options.push_back(std::move(opt)); m_options.push_back(std::move(opt));
} }
// sort based on category
std::sort(m_options.begin(), m_options.end(),
[](const ShaderOption& lhs, const ShaderOption& rhs) { return lhs.category < rhs.category; });
m_uniforms_size = mod.total_uniform_size; m_uniforms_size = mod.total_uniform_size;
Log_DevFmt("{}: {} options", m_filename, m_options.size()); Log_DevFmt("{}: {} options", m_filename, m_options.size());
return true; return true;
@ -741,6 +808,12 @@ bool PostProcessing::ReShadeFXShader::GetSourceOption(const reshadefx::uniform_i
*si = SourceOptionType::MousePoint; *si = SourceOptionType::MousePoint;
return true; return true;
} }
else if (source == "mousebutton")
{
Log_WarningFmt("Ignoring mousebutton source in uniform '{}', not supported.", ui.name);
*si = SourceOptionType::Zero;
return true;
}
else if (source == "random") else if (source == "random")
{ {
if ((!ui.type.is_floating_point() && !ui.type.is_integral()) || ui.type.components() != 1) if ((!ui.type.is_floating_point() && !ui.type.is_integral()) || ui.type.components() != 1)