gfx-effect-source: Support for textures and some crash resistance

This commit is contained in:
Michael Fabian 'Xaymar' Dirks 2019-08-07 20:21:57 +02:00
parent f945f08ab0
commit a04d102568
4 changed files with 238 additions and 48 deletions

View file

@ -173,7 +173,7 @@ filter::shader::shader_factory::~shader_factory() {}
filter::shader::shader_instance::shader_instance(obs_data_t* data, obs_source_t* self)
: _self(self), _active(true), _width(0), _height(0)
{
_fx = std::make_shared<gfx::effect_source::effect_source>();
_fx = std::make_shared<gfx::effect_source::effect_source>(self);
_fx->set_valid_property_cb(std::bind(&filter::shader::shader_instance::valid_param, this, std::placeholders::_1));
_fx->set_override_cb(std::bind(&filter::shader::shader_instance::override_param, this, std::placeholders::_1));

View file

@ -23,11 +23,16 @@
#include <sstream>
#include <sys/stat.h>
#include "obs/gs/gs-helper.hpp"
#include "obs/obs-source-tracker.hpp"
#include "strings.hpp"
#define ST_FILE S_SHADER_FILE
#define ST_TECHNIQUE S_SHADER_TECHNIQUE
#define ST_TEXTURE_TYPE "Shader.Texture.Type"
#define ST_TEXTURE_FILE S_FILETYPE_IMAGE
#define ST_TEXTURE_SOURCE S_SOURCETYPE_SOURCE
#define UNIQUE_PREFIX "HelloThisIsPatrick."
static std::vector<std::string> static_parameters{
@ -36,9 +41,10 @@ static std::vector<std::string> static_parameters{
"Random",
};
gfx::effect_source::parameter::parameter(std::shared_ptr<gs::effect> effect,
std::shared_ptr<gs::effect_parameter> param)
: _effect(effect), _param(param), _description(""), _formulae(""), _visible(true)
gfx::effect_source::parameter::parameter(std::shared_ptr<gfx::effect_source::effect_source> parent,
std::shared_ptr<gs::effect> effect,
std::shared_ptr<gs::effect_parameter> param)
: _parent(parent), _effect(effect), _param(param), _description(""), _formulae(""), _visible(true)
{
if (!effect)
throw std::invalid_argument("effect");
@ -87,8 +93,9 @@ std::shared_ptr<gs::effect_parameter> gfx::effect_source::parameter::get_param()
}
std::shared_ptr<gfx::effect_source::parameter>
gfx::effect_source::parameter::create(std::shared_ptr<gs::effect> effect,
std::shared_ptr<gs::effect_parameter> param)
gfx::effect_source::parameter::create(std::shared_ptr<gfx::effect_source::effect_source> parent,
std::shared_ptr<gs::effect> effect,
std::shared_ptr<gs::effect_parameter> param)
{
if (!effect)
throw std::invalid_argument("effect");
@ -99,7 +106,7 @@ std::shared_ptr<gfx::effect_source::parameter>
switch (param->get_type()) {
case gs::effect_parameter::type::Boolean:
return std::make_shared<gfx::effect_source::bool_parameter>(effect, param);
return std::make_shared<gfx::effect_source::bool_parameter>(parent, effect, param);
case gs::effect_parameter::type::Float:
case gs::effect_parameter::type::Float2:
case gs::effect_parameter::type::Float3:
@ -108,19 +115,20 @@ std::shared_ptr<gfx::effect_source::parameter>
case gs::effect_parameter::type::Integer2:
case gs::effect_parameter::type::Integer3:
case gs::effect_parameter::type::Integer4:
return std::make_shared<gfx::effect_source::value_parameter>(effect, param);
return std::make_shared<gfx::effect_source::value_parameter>(parent, effect, param);
case gs::effect_parameter::type::Matrix:
return std::make_shared<gfx::effect_source::matrix_parameter>(effect, param);
return std::make_shared<gfx::effect_source::matrix_parameter>(parent, effect, param);
case gs::effect_parameter::type::Texture:
return std::make_shared<gfx::effect_source::texture_parameter>(effect, param);
return std::make_shared<gfx::effect_source::texture_parameter>(parent, effect, param);
}
return nullptr;
}
gfx::effect_source::bool_parameter::bool_parameter(std::shared_ptr<gs::effect> effect,
std::shared_ptr<gs::effect_parameter> param)
: parameter(effect, param)
gfx::effect_source::bool_parameter::bool_parameter(std::shared_ptr<gfx::effect_source::effect_source> parent,
std::shared_ptr<gs::effect> effect,
std::shared_ptr<gs::effect_parameter> param)
: parameter(parent, effect, param)
{
if (param->get_type() != gs::effect_parameter::type::Boolean)
throw std::bad_cast();
@ -128,7 +136,8 @@ gfx::effect_source::bool_parameter::bool_parameter(std::shared_ptr<gs::effect>
param->get_default_bool(_value);
}
void gfx::effect_source::bool_parameter::defaults(obs_properties_t* props, obs_data_t* data) {
void gfx::effect_source::bool_parameter::defaults(obs_properties_t* props, obs_data_t* data)
{
obs_data_set_default_bool(data, _name.c_str(), _value);
obs_data_set_bool(data, _name.c_str(), _value);
}
@ -159,9 +168,10 @@ void gfx::effect_source::bool_parameter::assign()
_param->set_bool(_value);
}
gfx::effect_source::value_parameter::value_parameter(std::shared_ptr<gs::effect> effect,
std::shared_ptr<gs::effect_parameter> param)
: parameter(effect, param)
gfx::effect_source::value_parameter::value_parameter(std::shared_ptr<gfx::effect_source::effect_source> parent,
std::shared_ptr<gs::effect> effect,
std::shared_ptr<gs::effect_parameter> param)
: parameter(parent, effect, param)
{
std::shared_ptr<gs::effect_parameter> min = param->get_annotation("minimum");
std::shared_ptr<gs::effect_parameter> max = param->get_annotation("maximum");
@ -547,9 +557,10 @@ void gfx::effect_source::value_parameter::assign()
}
}
gfx::effect_source::matrix_parameter::matrix_parameter(std::shared_ptr<gs::effect> effect,
std::shared_ptr<gs::effect_parameter> param)
: parameter(effect, param)
gfx::effect_source::matrix_parameter::matrix_parameter(std::shared_ptr<gfx::effect_source::effect_source> parent,
std::shared_ptr<gs::effect> effect,
std::shared_ptr<gs::effect_parameter> param)
: parameter(parent, effect, param)
{
std::shared_ptr<gs::effect_parameter> min = param->get_annotation("minimum");
std::shared_ptr<gs::effect_parameter> max = param->get_annotation("maximum");
@ -617,7 +628,7 @@ void gfx::effect_source::matrix_parameter::defaults(obs_properties_t* props, obs
}
for (size_t y = 0; y < 4; y++) {
size_t idx = x * 4 + y;
size_t idx = x * 4 + y;
obs_data_set_double(data, _cache.name[idx].c_str(), v_ref.ptr[y]);
obs_data_set_default_double(data, _cache.name[idx].c_str(), v_ref.ptr[y]);
}
@ -713,9 +724,10 @@ void gfx::effect_source::matrix_parameter::assign()
_param->set_matrix(_value);
}
gfx::effect_source::string_parameter::string_parameter(std::shared_ptr<gs::effect> effect,
std::shared_ptr<gs::effect_parameter> param)
: parameter(effect, param)
gfx::effect_source::string_parameter::string_parameter(std::shared_ptr<gfx::effect_source::effect_source> parent,
std::shared_ptr<gs::effect> effect,
std::shared_ptr<gs::effect_parameter> param)
: parameter(parent, effect, param)
{
param->get_default_string(_value);
}
@ -734,24 +746,167 @@ void gfx::effect_source::string_parameter::prepare() {}
void gfx::effect_source::string_parameter::assign() {}
gfx::effect_source::texture_parameter::texture_parameter(std::shared_ptr<gs::effect> effect,
std::shared_ptr<gs::effect_parameter> param)
: parameter(effect, param)
{}
void gfx::effect_source::texture_parameter::load_texture(std::string file)
{
_file_name = file;
void gfx::effect_source::texture_parameter::defaults(obs_properties_t* props, obs_data_t* data) {}
struct stat st;
if (os_stat(_file_name.c_str(), &st) == -1) {
_last_size = 0;
_last_modify_time = 0;
_last_create_time = 0;
throw std::system_error(std::error_code(ENOENT, std::system_category()), file.c_str());
} else {
_last_size = st.st_size;
_last_modify_time = st.st_mtime;
_last_create_time = st.st_ctime;
}
void gfx::effect_source::texture_parameter::properties(obs_properties_t* props) {}
_file = std::make_shared<gs::texture>(_file_name);
}
gfx::effect_source::texture_parameter::texture_parameter(std::shared_ptr<gfx::effect_source::effect_source> parent,
std::shared_ptr<gs::effect> effect,
std::shared_ptr<gs::effect_parameter> param)
: parameter(parent, effect, param)
{
_cache.name[0] = _name + ".Type";
_cache.visible_name[0] = _visible_name + " " + D_TRANSLATE(ST_TEXTURE_TYPE);
_cache.name[1] = _name + ".File";
_cache.visible_name[1] = _visible_name;
_cache.name[2] = _name + ".Source";
_cache.visible_name[2] = _visible_name;
}
bool gfx::effect_source::texture_parameter::modified2(obs_properties_t* props, obs_property_t* property,
obs_data_t* settings)
{
_mode = static_cast<texture_mode>(obs_data_get_int(settings, _cache.name[0].c_str()));
if (_mode == texture_mode::FILE) {
obs_property_set_visible(obs_properties_get(props, _cache.name[1].c_str()), true);
obs_property_set_visible(obs_properties_get(props, _cache.name[2].c_str()), false);
} else if (_mode == texture_mode::SOURCE) {
obs_property_set_visible(obs_properties_get(props, _cache.name[1].c_str()), false);
obs_property_set_visible(obs_properties_get(props, _cache.name[2].c_str()), true);
}
return true;
}
void gfx::effect_source::texture_parameter::defaults(obs_properties_t* props, obs_data_t* data)
{
obs_data_set_int(data, _cache.name[0].c_str(), static_cast<int64_t>(_mode));
obs_data_set_string(data, _cache.name[1].c_str(), _file_name.c_str());
obs_data_set_string(data, _cache.name[2].c_str(), _source_name.c_str());
}
void gfx::effect_source::texture_parameter::properties(obs_properties_t* props)
{
auto grp = props;
if (!util::are_property_groups_broken()) {
grp = obs_properties_create();
auto p = obs_properties_add_group(props, _name.c_str(), _visible_name.c_str(), OBS_GROUP_NORMAL, grp);
obs_property_set_long_description(p, _description.c_str());
obs_property_set_visible(p, _visible);
}
{
auto p = obs_properties_add_list(grp, _cache.name[0].c_str(), _cache.visible_name[0].c_str(),
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
obs_property_set_modified_callback2(
p,
[](void* priv, obs_properties_t* props, obs_property_t* property, obs_data_t* settings) {
return reinterpret_cast<texture_parameter*>(priv)->modified2(props, property, settings);
},
this);
obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_TEXTURE_TYPE)));
obs_property_list_add_int(p, D_TRANSLATE(ST_TEXTURE_FILE), static_cast<int64_t>(texture_mode::FILE));
obs_property_list_add_int(p, D_TRANSLATE(ST_TEXTURE_SOURCE), static_cast<int64_t>(texture_mode::SOURCE));
}
{
auto p = obs_properties_add_path(grp, _cache.name[1].c_str(), _cache.visible_name[1].c_str(), OBS_PATH_FILE,
"Textures (" S_FILEFILTERS_TEXTURE ")", nullptr);
obs_property_set_long_description(p, _description.c_str());
}
{
auto p = obs_properties_add_list(grp, _cache.name[2].c_str(), _cache.visible_name[2].c_str(),
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING);
obs::source_tracker::get()->enumerate(
[&p](std::string name, obs_source_t*) {
obs_property_list_add_string(p, std::string(name + " (Source)").c_str(), name.c_str());
return false;
},
obs::source_tracker::filter_video_sources);
obs::source_tracker::get()->enumerate(
[&p](std::string name, obs_source_t*) {
obs_property_list_add_string(p, std::string(name + " (Scene)").c_str(), name.c_str());
return false;
},
obs::source_tracker::filter_scenes);
obs_property_set_long_description(p, _description.c_str());
}
}
void gfx::effect_source::texture_parameter::remove_properties(obs_properties_t* props) {}
void gfx::effect_source::texture_parameter::update(obs_data_t* data) {}
void gfx::effect_source::texture_parameter::update(obs_data_t* data)
{
try {
if (_mode == texture_mode::FILE) {
load_texture(obs_data_get_string(data, _cache.name[1].c_str()));
} else if (_mode == texture_mode::SOURCE) {
_source_name = obs_data_get_string(data, _cache.name[2].c_str());
if (!_source || (obs_source_get_name(_source->get()) != _source_name)) {
_source = std::make_shared<obs::source>(_source_name);
if (!_parent.expired())
_source_renderer = std::make_shared<gfx::source_texture>(_source, _parent.lock()->get_self());
}
}
} catch (std::exception& ex) {
P_LOG_ERROR("Update failed, error: %s", ex.what());
}
}
void gfx::effect_source::texture_parameter::tick(float_t time) {}
void gfx::effect_source::texture_parameter::tick(float_t time)
{
_last_check += time;
if (_last_check >= 0.5f) {
_last_check -= 0.5f;
bool changed = false;
void gfx::effect_source::texture_parameter::prepare() {}
struct stat st;
if (os_stat(_file_name.c_str(), &st) != -1) {
changed =
(_last_size != st.st_size) || (_last_modify_time != st.st_mtime) || (_last_create_time != st.st_ctime);
}
if (changed) {
try {
load_texture(_file_name);
} catch (std::exception& ex) {
P_LOG_ERROR("Loading texture \"%s\" failed, error: %s", _file_name.c_str(), ex.what());
}
}
}
}
void gfx::effect_source::texture_parameter::assign() {}
void gfx::effect_source::texture_parameter::prepare()
{
if (_mode == texture_mode::SOURCE) {
if (_source_renderer) {
_source_tex = _source_renderer->render(_source->width(), _source->height());
}
}
}
void gfx::effect_source::texture_parameter::assign()
{
if (_mode == texture_mode::FILE) {
if (_file)
_param->set_texture(_file);
} else {
if (_source_tex)
_param->set_texture(_source_tex);
}
}
bool gfx::effect_source::effect_source::modified2(obs_properties_t* props, obs_property_t* property,
obs_data_t* settings)
@ -825,12 +980,12 @@ void gfx::effect_source::effect_source::load_file(std::string file)
if (skip)
continue;
_params.emplace(identity, parameter::create(_effect, prm));
_params.emplace(identity, parameter::create(this->shared_from_this(), _effect, prm));
}
}
gfx::effect_source::effect_source::effect_source()
: _last_check(0), _last_size(0), _last_modify_time(0), _last_create_time(0), _time(0), _time_active(0),
gfx::effect_source::effect_source::effect_source(obs_source_t* self)
: _self(self), _last_check(0), _last_size(0), _last_modify_time(0), _last_create_time(0), _time(0), _time_active(0),
_time_since_last_tick(0)
{
auto gctx = gs::context();
@ -988,6 +1143,11 @@ void gfx::effect_source::effect_source::render()
gs_blend_state_pop();
}
obs_source_t* gfx::effect_source::effect_source::get_self()
{
return _self;
}
void gfx::effect_source::effect_source::set_valid_property_cb(valid_property_cb_t cb)
{
_cb_valid = cb;

View file

@ -62,8 +62,12 @@ namespace gfx {
SOURCE,
};
class effect_source;
class parameter {
protected:
std::weak_ptr<gfx::effect_source::effect_source> _parent;
std::shared_ptr<gs::effect> _effect;
std::shared_ptr<gs::effect_parameter> _param;
@ -74,7 +78,8 @@ namespace gfx {
bool _visible;
public:
parameter(std::shared_ptr<gs::effect> effect, std::shared_ptr<gs::effect_parameter> param);
parameter(std::shared_ptr<gfx::effect_source::effect_source> parent, std::shared_ptr<gs::effect> effect,
std::shared_ptr<gs::effect_parameter> param);
virtual ~parameter();
virtual void defaults(obs_properties_t* props, obs_data_t* data) = 0;
@ -94,15 +99,17 @@ namespace gfx {
std::shared_ptr<gs::effect_parameter> get_param();
public:
static std::shared_ptr<gfx::effect_source::parameter> create(std::shared_ptr<gs::effect> effect,
std::shared_ptr<gs::effect_parameter> param);
static std::shared_ptr<gfx::effect_source::parameter>
create(std::shared_ptr<gfx::effect_source::effect_source> parent, std::shared_ptr<gs::effect> effect,
std::shared_ptr<gs::effect_parameter> param);
};
class bool_parameter : public parameter {
bool _value;
public:
bool_parameter(std::shared_ptr<gs::effect> effect, std::shared_ptr<gs::effect_parameter> param);
bool_parameter(std::shared_ptr<gfx::effect_source::effect_source> parent,
std::shared_ptr<gs::effect> effect, std::shared_ptr<gs::effect_parameter> param);
virtual void defaults(obs_properties_t* props, obs_data_t* data) override;
@ -144,7 +151,8 @@ namespace gfx {
} _cache;
public:
value_parameter(std::shared_ptr<gs::effect> effect, std::shared_ptr<gs::effect_parameter> param);
value_parameter(std::shared_ptr<gfx::effect_source::effect_source> parent,
std::shared_ptr<gs::effect> effect, std::shared_ptr<gs::effect_parameter> param);
virtual void defaults(obs_properties_t* props, obs_data_t* data) override;
@ -174,7 +182,8 @@ namespace gfx {
} _cache;
public:
matrix_parameter(std::shared_ptr<gs::effect> effect, std::shared_ptr<gs::effect_parameter> param);
matrix_parameter(std::shared_ptr<gfx::effect_source::effect_source> parent,
std::shared_ptr<gs::effect> effect, std::shared_ptr<gs::effect_parameter> param);
virtual void defaults(obs_properties_t* props, obs_data_t* data) override;
@ -196,7 +205,8 @@ namespace gfx {
string_mode _mode = string_mode::TEXT;
public:
string_parameter(std::shared_ptr<gs::effect> effect, std::shared_ptr<gs::effect_parameter> param);
string_parameter(std::shared_ptr<gfx::effect_source::effect_source> parent,
std::shared_ptr<gs::effect> effect, std::shared_ptr<gs::effect_parameter> param);
virtual void defaults(obs_properties_t* props, obs_data_t* data) override;
@ -217,14 +227,30 @@ namespace gfx {
std::string _file_name;
std::shared_ptr<gs::texture> _file;
float_t _last_check;
size_t _last_size;
time_t _last_modify_time;
time_t _last_create_time;
std::string _source_name;
std::shared_ptr<obs::source> _source;
std::shared_ptr<gfx::source_texture> _source_renderer;
std::shared_ptr<gs::texture> _source_tex;
texture_mode _mode = texture_mode::FILE;
struct {
std::string name[4];
std::string visible_name[4];
} _cache;
void load_texture(std::string file);
public:
texture_parameter(std::shared_ptr<gs::effect> effect, std::shared_ptr<gs::effect_parameter> param);
texture_parameter(std::shared_ptr<gfx::effect_source::effect_source> parent,
std::shared_ptr<gs::effect> effect, std::shared_ptr<gs::effect_parameter> param);
bool modified2(obs_properties_t* props, obs_property_t* property, obs_data_t* settings);
virtual void defaults(obs_properties_t* props, obs_data_t* data) override;
@ -245,7 +271,9 @@ namespace gfx {
typedef std::function<bool(std::shared_ptr<gs::effect_parameter> param)> valid_property_cb_t;
typedef std::function<void(std::shared_ptr<gs::effect> effect)> param_override_cb_t;
class effect_source {
class effect_source : public std::enable_shared_from_this<effect_source> {
obs_source_t* _self;
std::string _file;
std::shared_ptr<gs::effect> _effect;
std::string _tech;
@ -273,7 +301,7 @@ namespace gfx {
void load_file(std::string file);
public:
effect_source();
effect_source(obs_source_t* self);
~effect_source();
void properties(obs_properties_t* props);
@ -284,6 +312,8 @@ namespace gfx {
void render();
obs_source_t* get_self();
public:
void set_valid_property_cb(valid_property_cb_t cb);

View file

@ -177,7 +177,7 @@ source::shader::shader_factory::~shader_factory() {}
source::shader::shader_instance::shader_instance(obs_data_t* data, obs_source_t* self)
: _self(self), _active(true), _width(0), _height(0)
{
_fx = std::make_shared<gfx::effect_source::effect_source>();
_fx = std::make_shared<gfx::effect_source::effect_source>(self);
_fx->set_valid_property_cb(std::bind(&source::shader::shader_instance::valid_param, this, std::placeholders::_1));
_fx->set_override_cb(std::bind(&source::shader::shader_instance::override_param, this, std::placeholders::_1));