From 4acbfedd40691a13fd2f4735140a3dd2192bed04 Mon Sep 17 00:00:00 2001 From: Michael Fabian 'Xaymar' Dirks Date: Wed, 25 Dec 2019 10:09:56 +0100 Subject: [PATCH] gfx-shader-param: Support for input, slider, enum and more --- source/gfx/shader/gfx-shader-param-basic.cpp | 294 ++++++++++++++----- source/gfx/shader/gfx-shader-param-basic.hpp | 78 ++++- source/gfx/shader/gfx-shader-param.cpp | 10 +- source/gfx/shader/gfx-shader-param.hpp | 1 + 4 files changed, 292 insertions(+), 91 deletions(-) diff --git a/source/gfx/shader/gfx-shader-param-basic.cpp b/source/gfx/shader/gfx-shader-param-basic.cpp index 35c68ef..6352f33 100644 --- a/source/gfx/shader/gfx-shader-param-basic.cpp +++ b/source/gfx/shader/gfx-shader-param-basic.cpp @@ -16,9 +16,20 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA #include "gfx-shader-param-basic.hpp" +#include +#include #include +#include #include "strings.hpp" +#define ANNO_FIELD_TYPE "field_type" +#define ANNO_SUFFIX "suffix" +#define ANNO_VALUE_MINIMUM "minimum" +#define ANNO_VALUE_MAXIMUM "maximum" +#define ANNO_VALUE_STEP "step" +#define ANNO_VALUE_SCALE "scale" +#define ANNO_ENUM_VALUES "values" + inline bool get_annotation_string(gs::effect_parameter param, std::string anno_name, std::string& out) { if (!param) @@ -48,91 +59,193 @@ inline bool get_annotation_float(gs::effect_parameter param, std::string anno_na return false; } -gfx::shader::bool_parameter::bool_parameter(gs::effect_parameter param, std::string prefix) : parameter(param, prefix) -{} +gfx::shader::basic_field_type gfx::shader::get_field_type_from_string(std::string v) +{ + std::map matches = { + {"input", basic_field_type::Input}, + {"slider", basic_field_type::Slider}, + {"enum", basic_field_type::Enum}, + {"enumeration", basic_field_type::Enum}, + }; + + auto fnd = matches.find(v); + if (fnd != matches.end()) + return fnd->second; + + return basic_field_type::Input; +} + +gfx::shader::basic_parameter::basic_parameter(gs::effect_parameter param, std::string prefix) + : parameter(param, prefix), _field_type(basic_field_type::Input), _suffix(), _keys(), _names(), _min(), _max(), + _step(), _values() +{ + char string_buffer[256]; + + _keys.resize(get_size()); + _names.resize(get_size()); + /* + _min.resize(get_size()); + _max.resize(get_size()); + _step.resize(get_size()); + _scale.resize(get_size()); + _value.resize(get_size()); + */ + + // Build sub-keys + for (size_t idx = 0; idx < _keys.size(); idx++) { + snprintf(string_buffer, sizeof(string_buffer), "[%d]", static_cast(idx)); + _names[idx] = std::string(string_buffer, string_buffer + strnlen(string_buffer, sizeof(string_buffer))); + snprintf(string_buffer, sizeof(string_buffer), "%s[%d]", get_key().c_str(), static_cast(idx)); + _keys[idx] = std::string(string_buffer, string_buffer + strnlen(string_buffer, sizeof(string_buffer))); + } + + // Detect Field Types + if (auto anno = get_parameter().get_annotation(ANNO_FIELD_TYPE); anno) { + _field_type = get_field_type_from_string(anno.get_default_string()); + } + + // Read Suffix Data + if (auto anno = get_parameter().get_annotation(ANNO_SUFFIX); anno) { + if (anno.get_type() == gs::effect_parameter::type::String) + _suffix = anno.get_default_string(); + } + + // Read Enumeration Data if Enumeration + if (get_field_type() == basic_field_type::Enum) { + if (auto anno = get_parameter().get_annotation(ANNO_ENUM_VALUES); + anno && (anno.get_type() == gs::effect_parameter::type::Integer)) { + _values.resize(static_cast(std::max(anno.get_default_int(), 0))); + for (size_t idx = 0; idx < _values.size(); idx++) { + auto& entry = _values[idx]; + snprintf(string_buffer, sizeof(string_buffer), "_%zu", idx); + std::string key = + std::string(string_buffer, string_buffer + strnlen(string_buffer, sizeof(string_buffer))); + if (auto annoe = anno.get_annotation(key); + annoe && (annoe.get_type() == gs::effect_parameter::type::String)) { + entry.name = annoe.get_default_string(); + load_parameter_data(annoe, entry.data); + } else { + P_LOG_WARNING("[%s] Parameter enumeration entry '%s' is of invalid type, must be string.", + get_name().c_str(), string_buffer); + } + } + } else { + P_LOG_WARNING("[%s] Enumeration is missing entries.", get_name().c_str()); + _field_type = basic_field_type::Input; + } + } +} + +gfx::shader::basic_parameter::~basic_parameter() {} + +void gfx::shader::basic_parameter::load_parameter_data(gs::effect_parameter parameter, basic_data& data) +{ + data.i32 = 0; +} + +gfx::shader::basic_field_type gfx::shader::basic_parameter::get_field_type() +{ + return _field_type; +} + +const std::string& gfx::shader::basic_parameter::get_suffix() +{ + return _suffix; +} + +const std::string& gfx::shader::basic_parameter::get_keys(size_t idx) +{ + if (idx >= get_size()) + throw std::out_of_range("Index out of range."); + return _keys[idx]; +} + +const std::string& gfx::shader::basic_parameter::get_names(size_t idx) +{ + if (idx >= get_size()) + throw std::out_of_range("Index out of range."); + return _names[idx]; +} + +gfx::shader::bool_parameter::bool_parameter(gs::effect_parameter param, std::string prefix) + : basic_parameter(param, prefix) +{ + _min.resize(0); + _max.resize(0); + _step.resize(0); + _scale.resize(0); + + _data.resize(get_size(), true); +} gfx::shader::bool_parameter::~bool_parameter() {} void gfx::shader::bool_parameter::defaults(obs_data_t* settings) { - obs_data_set_default_bool(settings, _key.c_str(), _param.get_default_bool()); + // TODO: Support for bool[] + if (get_size() == 1) { + obs_data_set_default_bool(settings, get_key().c_str(), get_parameter().get_default_bool()); + } } void gfx::shader::bool_parameter::properties(obs_properties_t* props, obs_data_t* settings) { - auto p = obs_properties_add_list(props, _key.c_str(), _name.c_str(), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); - if (has_description()) - obs_property_set_long_description(p, get_description().c_str()); - obs_property_list_add_int(p, D_TRANSLATE(S_STATE_DISABLED), 0); - obs_property_list_add_int(p, D_TRANSLATE(S_STATE_ENABLED), 1); + // TODO: Support for bool[] + if (get_size() == 1) { + auto p = obs_properties_add_list(props, get_key().c_str(), get_name().c_str(), OBS_COMBO_TYPE_LIST, + OBS_COMBO_FORMAT_INT); + if (has_description()) + obs_property_set_long_description(p, get_description().c_str()); + obs_property_list_add_int(p, D_TRANSLATE(S_STATE_DISABLED), 0); + obs_property_list_add_int(p, D_TRANSLATE(S_STATE_ENABLED), 1); + } } void gfx::shader::bool_parameter::update(obs_data_t* settings) { - _value = obs_data_get_int(settings, _key.c_str()) != 0; + // TODO: Support for bool[] + if (get_size() == 1) { + _data[0] = static_cast(obs_data_get_int(settings, get_key().c_str())); + } } void gfx::shader::bool_parameter::assign() { - _param.set_bool(_value); + get_parameter().set_value(_data.data(), sizeof(uint8_t)); } -gfx::shader::float_parameter::float_parameter(gs::effect_parameter param, std::string prefix) : parameter(param, prefix) +gfx::shader::float_parameter::float_parameter(gs::effect_parameter param, std::string prefix) + : basic_parameter(param, prefix) { - switch (_param.get_type()) { - case gs::effect_parameter::type::Float: - _array_size = 1; - break; - case gs::effect_parameter::type::Float2: - _array_size = 2; - break; - case gs::effect_parameter::type::Float3: - _array_size = 3; - break; - case gs::effect_parameter::type::Float4: - _array_size = 4; - break; - default: - _array_size = 0; + _data.resize(get_size()); + + // Reset minimum, maximum, step and scale. + for (size_t idx = 0; idx < get_size(); idx++) { + _min[idx].f32 = std::numeric_limits::lowest(); + _max[idx].f32 = std::numeric_limits::max(); + _step[idx].f32 = 0.01f; + _scale[idx].f32 = 1.00f; } - // Build sub-keys - char buffer[16]; // Fits on the stack, and in the L1 cache. - for (size_t idx = 0; idx < _array_size; idx++) { - snprintf(buffer, sizeof(buffer), "[%d]", static_cast(idx)); - _names[idx] = std::string(buffer, buffer + sizeof(buffer)); - _keys[idx] = _key + _names[idx]; - - _min[idx] = std::numeric_limits::lowest(); - _max[idx] = std::numeric_limits::max(); - _step[idx] = 0.01f; - } - - if (auto anno = _param.get_annotation("minimum"); (anno != nullptr)) { - if (anno.get_type() == gs::effect_parameter::type::Float) { - for (size_t len = 0; len < _array_size; len++) { - anno.get_default_value(&_min[len], 1); - } - } else if (anno.get_type() == _param.get_type()) { - anno.get_default_value(_min, _array_size); + // Load Limits + if (auto anno = get_parameter().get_annotation(ANNO_VALUE_MINIMUM); anno) { + if (anno.get_type() == get_parameter().get_type()) { + anno.get_default_value(_min.data(), get_size()); } } - if (auto anno = _param.get_annotation("maximum"); (anno != nullptr)) { - if (anno.get_type() == gs::effect_parameter::type::Float) { - for (size_t len = 0; len < _array_size; len++) { - anno.get_default_value(&_max[len], 1); - } - } else if (anno.get_type() == _param.get_type()) { - anno.get_default_value(_max, _array_size); + if (auto anno = get_parameter().get_annotation(ANNO_VALUE_MAXIMUM); anno) { + if (anno.get_type() == get_parameter().get_type()) { + anno.get_default_value(_max.data(), get_size()); } } - if (auto anno = _param.get_annotation("step"); (anno != nullptr)) { - if (anno.get_type() == gs::effect_parameter::type::Float) { - for (size_t len = 0; len < _array_size; len++) { - anno.get_default_value(&_step[len], 1); - } - } else if (anno.get_type() == _param.get_type()) { - anno.get_default_value(_step, _array_size); + if (auto anno = get_parameter().get_annotation(ANNO_VALUE_STEP); anno) { + if (anno.get_type() == get_parameter().get_type()) { + anno.get_default_value(_step.data(), get_size()); + } + } + if (auto anno = get_parameter().get_annotation(ANNO_VALUE_SCALE); anno) { + if (anno.get_type() == get_parameter().get_type()) { + anno.get_default_value(_scale.data(), get_size()); } } } @@ -141,41 +254,74 @@ gfx::shader::float_parameter::~float_parameter() {} void gfx::shader::float_parameter::defaults(obs_data_t* settings) { - float_t defaults[4] = {0, 0, 0, 0}; - _param.get_default_value(defaults, _array_size); - for (size_t idx = 0; idx < _array_size; idx++) { - obs_data_set_default_double(settings, _keys[idx].c_str(), static_cast(defaults[idx])); + std::vector defaults; + defaults.resize(get_size()); + get_parameter().get_default_value(defaults.data(), get_size()); + + for (size_t idx = 0; idx < get_size(); idx++) { + obs_data_set_default_double(settings, get_keys(idx).c_str(), static_cast(defaults[idx])); + } +} + +static inline obs_property_t* build_float_property(gfx::shader::basic_field_type ft, obs_properties_t* props, + const char* key, const char* name, float_t min, float_t max, + float_t step, std::vector edata) +{ + switch (ft) { + case gfx::shader::basic_field_type::Enum: { + auto p = obs_properties_add_list(props, key, name, OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_FLOAT); + for (size_t idx = 0; idx < edata.size(); idx++) { + auto& el = edata.at(idx); + obs_property_list_add_float(p, el.name.c_str(), el.data.f32); + } + return p; + } + case gfx::shader::basic_field_type::Slider: + return obs_properties_add_float_slider(props, key, name, min, max, step); + default: + case gfx::shader::basic_field_type::Input: + return obs_properties_add_float(props, key, name, min, max, step); } } void gfx::shader::float_parameter::properties(obs_properties_t* props, obs_data_t* settings) { auto grp = obs_properties_create(); - auto p = obs_properties_add_group(props, _key.c_str(), _name.c_str(), OBS_GROUP_NORMAL, grp); - if (has_description()) - obs_property_set_long_description(p, get_description().c_str()); - - for (size_t idx = 0; idx < _array_size; idx++) { - auto p = - obs_properties_add_float(grp, _keys[idx].c_str(), _names[idx].c_str(), _min[idx], _max[idx], _step[idx]); + if (get_size() == 1) { + auto p = build_float_property(_field_type, props, _keys[0].c_str(), _names[0].c_str(), _min[0].f32, _max[0].f32, + _step[0].f32, _values); if (has_description()) obs_property_set_long_description(p, get_description().c_str()); + } else { + auto p = obs_properties_add_group(props, get_key().c_str(), has_name() ? get_name().c_str() : get_key().c_str(), + OBS_GROUP_NORMAL, grp); + if (has_description()) + obs_property_set_long_description(p, get_description().c_str()); + + for (size_t idx = 0; idx < get_size(); idx++) { + p = build_float_property(_field_type, grp, _keys[idx].c_str(), _names[idx].c_str(), _min[idx].f32, + _max[idx].f32, _step[idx].f32, _values); + if (has_description()) + obs_property_set_long_description(p, get_description().c_str()); + } } } void gfx::shader::float_parameter::update(obs_data_t* settings) { - for (size_t len = 0; len < _array_size; len++) { - _value[len] = static_cast(obs_data_get_double(settings, _keys[len].c_str())); + for (size_t idx = 0; idx < get_size(); idx++) { + _data[idx].f32 = static_cast(obs_data_get_double(settings, _keys[idx].c_str())); } } void gfx::shader::float_parameter::assign() { - _param.set_value(_value, _array_size); + get_parameter().set_value(_data.data(), get_size()); } -gfx::shader::int_parameter::int_parameter(gs::effect_parameter param, std::string prefix) : parameter(param, prefix) {} +gfx::shader::int_parameter::int_parameter(gs::effect_parameter param, std::string prefix) + : basic_parameter(param, prefix) +{} gfx::shader::int_parameter::~int_parameter() {} diff --git a/source/gfx/shader/gfx-shader-param-basic.hpp b/source/gfx/shader/gfx-shader-param-basic.hpp index 0f8c3ec..7bdb5f5 100644 --- a/source/gfx/shader/gfx-shader-param-basic.hpp +++ b/source/gfx/shader/gfx-shader-param-basic.hpp @@ -17,19 +17,74 @@ #pragma once #include +#include #include "gfx-shader-param.hpp" #include "obs/gs/gs-effect-parameter.hpp" namespace gfx { namespace shader { - class bool_parameter : public parameter { - bool _value; + enum class basic_field_type { + Input, + Slider, + Enum, + }; + + basic_field_type get_field_type_from_string(std::string v); + + struct basic_data { + union { + int32_t i32; + uint32_t ui32; + float_t f32; + }; + }; + + struct basic_enum_data { + std::string name; + basic_data data; + }; + + struct basic_parameter : public parameter { + // Descriptor + basic_field_type _field_type; + std::string _suffix; + std::vector _keys; + std::vector _names; + + protected: + // Limits + std::vector _min; + std::vector _max; + std::vector _step; + std::vector _scale; + + // Enumeration Information + std::vector _values; + + public: + basic_parameter(gs::effect_parameter param, std::string prefix); + virtual ~basic_parameter(); + + virtual void load_parameter_data(gs::effect_parameter parameter, basic_data& data); + + public: + basic_field_type get_field_type(); + + const std::string& get_suffix(); + + const std::string& get_keys(size_t idx); + + const std::string& get_names(size_t idx); + }; + + struct bool_parameter : public basic_parameter { + // std::vector doesn't allow .data() + std::vector _data; public: bool_parameter(gs::effect_parameter param, std::string prefix); virtual ~bool_parameter(); - public: virtual void defaults(obs_data_t* settings); virtual void properties(obs_properties_t* props, obs_data_t* settings) override; @@ -39,21 +94,13 @@ namespace gfx { virtual void assign() override; }; - struct float_parameter : public parameter { - size_t _array_size; - std::string _keys[4]; - std::string _names[4]; - - float_t _min[4]; - float_t _max[4]; - float_t _step[4]; - float_t _value[4]; + struct float_parameter : public basic_parameter { + std::vector _data; public: float_parameter(gs::effect_parameter param, std::string prefix); virtual ~float_parameter(); - public: virtual void defaults(obs_data_t* settings); virtual void properties(obs_properties_t* props, obs_data_t* settings) override; @@ -63,12 +110,13 @@ namespace gfx { virtual void assign() override; }; - struct int_parameter : public parameter { + struct int_parameter : public basic_parameter { + std::vector _data; + public: int_parameter(gs::effect_parameter param, std::string prefix); virtual ~int_parameter(); - public: virtual void properties(obs_properties_t* props, obs_data_t* settings) override; }; diff --git a/source/gfx/shader/gfx-shader-param.cpp b/source/gfx/shader/gfx-shader-param.cpp index 141dc00..5bf6cc5 100644 --- a/source/gfx/shader/gfx-shader-param.cpp +++ b/source/gfx/shader/gfx-shader-param.cpp @@ -57,6 +57,7 @@ gfx::shader::parameter_type gfx::shader::get_type_from_effect_type(gs::effect_pa size_t gfx::shader::get_length_from_effect_type(gs::effect_parameter::type type) { switch (type) { + default: case eptype::Unknown: case eptype::Invalid: case eptype::String: @@ -150,7 +151,7 @@ gfx::shader::parameter::parameter(gs::effect_parameter param, std::string key_pr // Read Size override. _size = get_length_from_effect_type(_param.get_type()); if (auto anno = _param.get_annotation(ANNO_SIZE); anno) { - size_t ov = anno.get_default_int(); + size_t ov = static_cast(anno.get_default_int()); if (ov > 0) _size = ov; } @@ -190,6 +191,11 @@ const std::string& gfx::shader::parameter::get_key() return _key; } +bool gfx::shader::parameter::is_visible() +{ + return true; +} + bool gfx::shader::parameter::has_name() { return _name.length() > 0; @@ -220,7 +226,7 @@ std::shared_ptr gfx::shader::parameter::make_parameter(g parameter_type real_type = get_type_from_effect_type(param.get_type()); if (auto anno = param.get_annotation(ANNO_TYPE); anno) { // We have a type override. - parameter_type real_type = get_type_from_string(param.get_default_string()); + real_type = get_type_from_string(param.get_default_string()); } switch (real_type) { diff --git a/source/gfx/shader/gfx-shader-param.hpp b/source/gfx/shader/gfx-shader-param.hpp index 9aa4046..3718b3a 100644 --- a/source/gfx/shader/gfx-shader-param.hpp +++ b/source/gfx/shader/gfx-shader-param.hpp @@ -82,6 +82,7 @@ namespace gfx { parameter(gs::effect_parameter param, std::string key_prefix); virtual ~parameter(){}; + public: virtual void defaults(obs_data_t* settings); virtual void properties(obs_properties_t* props, obs_data_t* settings);