From 1092dc1de1272f1d477b9be547fb8ab4df39d22f Mon Sep 17 00:00:00 2001 From: Michael Fabian 'Xaymar' Dirks Date: Wed, 7 Aug 2019 12:42:25 +0200 Subject: [PATCH] gfx-effect-source: Complete rewrite from scratch --- source/gfx/gfx-effect-source.cpp | 773 ++++++++----------------------- source/gfx/gfx-effect-source.hpp | 170 +++---- 2 files changed, 255 insertions(+), 688 deletions(-) diff --git a/source/gfx/gfx-effect-source.cpp b/source/gfx/gfx-effect-source.cpp index 6104a35d..c9e2310a 100644 --- a/source/gfx/gfx-effect-source.cpp +++ b/source/gfx/gfx-effect-source.cpp @@ -23,611 +23,228 @@ #include #include "strings.hpp" -// OBS -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4201) -#endif -#include -#ifdef _MSC_VER -#pragma warning(pop) -#endif +#define ST "Shader" +#define ST_FILE "Shader.File" -bool gfx::effect_source::property_type_modified(void*, obs_properties_t* props, obs_property_t*, obs_data_t* sett) +gfx::effect_source::parameter::parameter(std::shared_ptr effect, std::string name) + : effect(effect), name(name), description(""), formulae(""), visible(true) { - switch (static_cast(obs_data_get_int(sett, D_TYPE))) { + if (!effect) + throw std::invalid_argument("effect"); + if (!effect->has_parameter(name)) + throw std::invalid_argument("name"); + param = effect->get_parameter(name); + + if (param->has_annotation("description", gs::effect_parameter::type::String)) { + param->get_annotation("description")->get_default_string(description); + } + if (param->has_annotation("formulae", gs::effect_parameter::type::String)) { + param->get_annotation("formulae")->get_default_string(formulae); + } + if (param->has_annotation("visible", gs::effect_parameter::type::Boolean)) { + param->get_annotation("visible")->get_default_bool(visible); + } else { + visible = true; + } +} + +gfx::effect_source::bool_parameter::bool_parameter(std::shared_ptr effect, std::string name) + : parameter(effect, name) +{ + if (param->get_type() != gs::effect_parameter::type::Boolean) + throw std::bad_cast(); + + param->get_default_bool(value); +} + +gfx::effect_source::value_parameter::value_parameter(std::shared_ptr effect, std::string name) + : parameter(effect, name) +{ + std::shared_ptr min = param->get_annotation("minimum"); + std::shared_ptr max = param->get_annotation("maximum"); + + switch (param->get_type()) { + case gs::effect_parameter::type::Float: + param->get_default_float(value.f[0]); + if (min) + min->get_default_float(minimum.f[0]); + if (max) + max->get_default_float(maximum.f[0]); + break; + case gs::effect_parameter::type::Float2: + param->get_default_float2(value.f[0], value.f[1]); + if (min) + min->get_default_float2(minimum.f[0], minimum.f[1]); + if (max) + max->get_default_float2(maximum.f[0], maximum.f[1]); + break; + case gs::effect_parameter::type::Float3: + param->get_default_float3(value.f[0], value.f[1], value.f[2]); + if (min) + min->get_default_float3(minimum.f[0], minimum.f[1], minimum.f[2]); + if (max) + max->get_default_float3(maximum.f[0], maximum.f[1], maximum.f[2]); + break; + case gs::effect_parameter::type::Float4: + param->get_default_float4(value.f[0], value.f[1], value.f[2], value.f[3]); + if (min) + min->get_default_float4(minimum.f[0], minimum.f[1], minimum.f[2], minimum.f[3]); + if (max) + max->get_default_float4(maximum.f[0], maximum.f[1], maximum.f[2], maximum.f[3]); + break; + case gs::effect_parameter::type::Integer: + param->get_default_int(value.i[0]); + if (min) + min->get_default_int(minimum.i[0]); + if (max) + max->get_default_int(maximum.i[0]); + break; + case gs::effect_parameter::type::Integer2: + param->get_default_int2(value.i[0], value.i[1]); + if (min) + min->get_default_int2(minimum.i[0], minimum.i[1]); + if (max) + max->get_default_int2(maximum.i[0], maximum.i[1]); + break; + case gs::effect_parameter::type::Integer3: + param->get_default_int3(value.i[0], value.i[1], value.i[2]); + if (min) + min->get_default_int3(minimum.i[0], minimum.i[1], minimum.i[2]); + if (max) + max->get_default_int3(maximum.i[0], maximum.i[1], maximum.i[2]); + break; + case gs::effect_parameter::type::Integer4: + param->get_default_int4(value.i[0], value.i[1], value.i[2], value.i[3]); + if (min) + min->get_default_int4(minimum.i[0], minimum.i[1], minimum.i[2], minimum.i[3]); + if (max) + max->get_default_int4(maximum.i[0], maximum.i[1], maximum.i[2], maximum.i[3]); + break; default: - case InputTypes::Text: - obs_property_set_visible(obs_properties_get(props, D_INPUT_TEXT), true); - obs_property_set_visible(obs_properties_get(props, D_INPUT_FILE), false); - break; - case InputTypes::File: - obs_property_set_visible(obs_properties_get(props, D_INPUT_TEXT), false); - obs_property_set_visible(obs_properties_get(props, D_INPUT_FILE), true); - break; + throw std::bad_cast(); } - return true; } -bool gfx::effect_source::property_input_modified(void* obj, obs_properties_t*, obs_property_t*, obs_data_t* sett) +gfx::effect_source::matrix_parameter::matrix_parameter(std::shared_ptr effect, std::string name) + : parameter(effect, name) { - const char* text = nullptr; - const char* file = nullptr; + std::shared_ptr min = param->get_annotation("minimum"); + std::shared_ptr max = param->get_annotation("maximum"); - switch ((InputTypes)obs_data_get_int(sett, D_TYPE)) { - default: - case InputTypes::Text: - text = obs_data_get_string(sett, D_INPUT_TEXT); - break; - case InputTypes::File: - file = obs_data_get_string(sett, D_INPUT_FILE); - break; + param->get_default_matrix(value); + if (min) + min->get_default_matrix(minimum); + if (max) + max->get_default_matrix(maximum); +} + +gfx::effect_source::string_parameter::string_parameter(std::shared_ptr effect, std::string name) + : parameter(effect, name) +{ + param->get_default_string(value); +} + +gfx::effect_source::texture_parameter::texture_parameter(std::shared_ptr effect, std::string name) + : parameter(effect, name) +{} + +void gfx::effect_source::shader_instance::load_file(std::string file) +{ + _params.clear(); + _effect.reset(); + _file = file; + + struct stat st; + if (!os_stat(_file.c_str(), &st)) { + _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; } - return reinterpret_cast(obj)->test_for_updates(text, file); -} + _effect = gs::effect::create(file); + auto prms = _effect->get_parameters(); + for (auto prm : prms) { + param_ident_t identity; + identity.first = prm->get_type(); + identity.second = prm->get_name(); + std::shared_ptr parameter; -void gfx::effect_source::fill_source_list(obs_property_t* prop) -{ - obs_enum_sources( - [](void* ptr, obs_source_t* src) { - obs_property_t* pro = (obs_property_t*)ptr; - const char* sname = obs_source_get_name(src); - obs_property_list_add_string(pro, sname, sname); - return true; - }, - prop); -} + switch (prm->get_type()) { + case gs::effect_parameter::type::Boolean: + parameter = std::make_shared(_effect, prm->get_name()); + break; + case gs::effect_parameter::type::Float: + case gs::effect_parameter::type::Float2: + case gs::effect_parameter::type::Float3: + case gs::effect_parameter::type::Float4: + case gs::effect_parameter::type::Integer: + case gs::effect_parameter::type::Integer2: + case gs::effect_parameter::type::Integer3: + case gs::effect_parameter::type::Integer4: + parameter = std::make_shared(_effect, prm->get_name()); + break; + case gs::effect_parameter::type::Matrix: + parameter = std::make_shared(_effect, prm->get_name()); + break; + case gs::effect_parameter::type::String: + parameter = std::make_shared(_effect, prm->get_name()); + break; + case gs::effect_parameter::type::Texture: + parameter = std::make_shared(_effect, prm->get_name()); + break; + } -bool gfx::effect_source::property_texture_type_modified(void* priv, obs_properties_t* props, obs_property_t* prop, - obs_data_t* sett) -{ - texture_parameter* tpm = reinterpret_cast(priv); - - int64_t v = obs_data_get_int(sett, obs_property_name(prop)); - if (v == 0) { // File - obs_property_set_visible(obs_properties_get(props, tpm->ui.names[1]), true); - obs_property_set_visible(obs_properties_get(props, tpm->ui.names[2]), false); - } else { // Source - obs_property_set_visible(obs_properties_get(props, tpm->ui.names[1]), false); - obs_property_set_visible(obs_properties_get(props, tpm->ui.names[2]), true); + _params.emplace(identity, parameter); } - - return true; } -bool gfx::effect_source::property_texture_input_modified(void*, obs_properties_t*, obs_property_t*, obs_data_t*) +gfx::effect_source::shader_instance::shader_instance(std::string file) + : _last_check(0), _last_size(0), _last_modify_time(0), _last_create_time(0) { - return false; + load_file(file); } -gfx::effect_source::effect_source(obs_data_t*, obs_source_t* owner) +gfx::effect_source::shader_instance::~shader_instance() {} + +void gfx::effect_source::shader_instance::tick(float_t time) { - m_source = owner; - m_timeExisting = 0; - m_timeActive = 0; + _last_check += time; + if (_last_check >= 0.5f) { + _last_check -= 0.5f; + bool changed = false; - m_quadBuffer = std::make_shared(4); - m_quadBuffer->set_uv_layers(1); - auto vtx = m_quadBuffer->at(0); - vec3_set(vtx.position, 0, 0, 0); - vec4_set(vtx.uv[0], 0, 0, 0, 0); - vtx = m_quadBuffer->at(2); - vec3_set(vtx.position, 1, 0, 0); - vec4_set(vtx.uv[0], 1, 0, 0, 0); - vtx = m_quadBuffer->at(1); - vec3_set(vtx.position, 0, 1, 0); - vec4_set(vtx.uv[0], 0, 1, 0, 0); - vtx = m_quadBuffer->at(3); - vec3_set(vtx.position, 1, 1, 0); - vec4_set(vtx.uv[0], 1, 1, 0, 0); - m_quadBuffer->update(true); -} - -gfx::effect_source::~effect_source() -{ - m_quadBuffer = nullptr; -} - -void gfx::effect_source::get_properties(obs_properties_t* properties) -{ - obs_property_t* p = nullptr; - - p = obs_properties_add_list(properties, D_TYPE, D_TRANSLATE(T_TYPE), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); - obs_property_set_long_description(p, D_TRANSLATE(D_DESC(T_TYPE))); - obs_property_list_add_int(p, D_TRANSLATE(T_TYPE_TEXT), (long long)InputTypes::Text); - obs_property_list_add_int(p, D_TRANSLATE(T_TYPE_FILE), (long long)InputTypes::File); - obs_property_set_modified_callback2(p, property_type_modified, this); - - p = obs_properties_add_text(properties, D_INPUT_TEXT, D_TRANSLATE(T_INPUT_TEXT), OBS_TEXT_MULTILINE); - obs_property_set_long_description(p, D_TRANSLATE(D_DESC(T_INPUT_TEXT))); - obs_property_set_modified_callback2(p, property_input_modified, this); - - { - char* tmp_path = obs_module_file(m_defaultShaderPath.c_str()); - p = obs_properties_add_path( - properties, D_INPUT_FILE, D_TRANSLATE(T_INPUT_FILE), OBS_PATH_FILE, - "Any (*._effect *.shader *.hlsl);;Effect (*._effect);;Shader (*.shader);;DirectX (*.hlsl)", tmp_path); - obs_property_set_long_description(p, D_TRANSLATE(D_DESC(T_INPUT_FILE))); - obs_property_set_modified_callback2(p, property_input_modified, this); - bfree(tmp_path); - } - - // ToDo: Place updated properties here or somewhere else? - for (auto prm : m_parameters) { - if (prm.first.second == gs::effect_parameter::type::Boolean) { - obs_properties_add_bool(properties, prm.second->ui.names[0], prm.second->ui.descs[0]); - } else if (prm.first.second >= gs::effect_parameter::type::Integer - && prm.first.second <= gs::effect_parameter::type::Integer4) { - size_t cnt = - static_cast(prm.first.second) - static_cast(gs::effect_parameter::type::Integer); - - for (size_t idx = 0; idx <= cnt; idx++) { - obs_properties_add_int(properties, prm.second->ui.names[idx], prm.second->ui.descs[idx], INT_MIN, - INT_MAX, 1); - } - } else if (prm.first.second >= gs::effect_parameter::type::Float - && prm.first.second <= gs::effect_parameter::type::Float4) { - size_t cnt = static_cast(prm.first.second) - static_cast(gs::effect_parameter::type::Float); - - for (size_t idx = 0; idx <= cnt; idx++) { - obs_properties_add_float(properties, prm.second->ui.names[idx], prm.second->ui.descs[idx], -DBL_MAX, - DBL_MAX, 0.01); - } - } else if (prm.first.second == gs::effect_parameter::type::Texture) { - // Switch between File and Source Input - p = obs_properties_add_list(properties, prm.second->ui.names[0], prm.second->ui.descs[0], - obs_combo_type::OBS_COMBO_TYPE_LIST, obs_combo_format::OBS_COMBO_FORMAT_INT); - obs_property_set_long_description(p, D_TRANSLATE(D_DESC(T_TEXTURE_TYPE))); - obs_property_set_modified_callback2(p, property_texture_type_modified, prm.second.get()); - obs_property_list_add_int(p, D_TRANSLATE(T_TEXTURE_TYPE_FILE), 0); - obs_property_list_add_int(p, D_TRANSLATE(T_TEXTURE_TYPE_SOURCE), 1); - - // Texture Path - char* defaultPath = obs_module_file(""); - p = obs_properties_add_path(properties, prm.second->ui.names[1], prm.second->ui.descs[1], - obs_path_type::OBS_PATH_FILE, "Images (*.bmp *.jpeg *.jpg *.png *.tga *.tiff)", - defaultPath); - obs_property_set_modified_callback2(p, property_texture_input_modified, prm.second.get()); - bfree(defaultPath); - - // Source Name - p = obs_properties_add_list(properties, prm.second->ui.names[2], prm.second->ui.descs[2], - obs_combo_type::OBS_COMBO_TYPE_LIST, obs_combo_format::OBS_COMBO_FORMAT_STRING); - obs_property_set_modified_callback2(p, property_texture_input_modified, prm.second.get()); - fill_source_list(p); + struct stat st; + if (os_stat(_file.c_str(), &st)) { + changed = + (_last_size != st.st_size) || (_last_modify_time != st.st_mtime) || (_last_create_time != st.st_ctime); + } + if (changed) { + load_file(_file); } } } -void gfx::effect_source::get_defaults(obs_data_t* data) +void gfx::effect_source::shader_instance::render(std::string technique) { - obs_data_set_default_int(data, D_TYPE, static_cast(InputTypes::Text)); - obs_data_set_default_string(data, D_INPUT_TEXT, ""); - obs_data_set_default_string(data, D_INPUT_FILE, ""); -} - -void gfx::effect_source::update(obs_data_t* data) -{ - obs_data_addref(data); - - // Update Shader - InputTypes input_type = static_cast(obs_data_get_int(data, D_TYPE)); - if (input_type == InputTypes::Text) { - const char* text = obs_data_get_string(data, D_INPUT_TEXT); - test_for_updates(text, nullptr); - } else if (input_type == InputTypes::File) { - const char* path = obs_data_get_string(data, D_INPUT_FILE); - test_for_updates(nullptr, path); - } - - update_parameters(data); - - obs_data_release(data); -} - -bool gfx::effect_source::test_for_updates(const char* text, const char* path) -{ - bool is_shader_different = false; - if (text != nullptr) { - if (text != m_shader.text) { - m_shader.text = text; - is_shader_different = true; - } - - if (is_shader_different) { - try { - m_shader.effect = std::make_unique(m_shader.text, "Text"); - } catch (...) { - m_shader.effect = nullptr; - } - } - } else if (path != nullptr) { - if (path != this->m_shader.path) { - this->m_shader.path = path; - this->m_shader.file_info.time_updated = 0; - this->m_shader.file_info.time_create = 0; - this->m_shader.file_info.time_modified = 0; - this->m_shader.file_info.file_size = 0; - is_shader_different = true; - } - - // If the update timer is 0 or less, grab new file information. - if (m_shader.file_info.time_updated <= 0) { - struct stat stats; - if (os_stat(m_shader.path.c_str(), &stats) == 0) { - m_shader.file_info.modified = (m_shader.file_info.time_create != stats.st_ctime) - | (m_shader.file_info.time_modified != stats.st_mtime) - | (m_shader.file_info.file_size != static_cast(stats.st_size)); - - // Mark shader as different if the file was changed. - is_shader_different = is_shader_different | m_shader.file_info.modified; - - // Update own information - m_shader.file_info.time_create = stats.st_ctime; - m_shader.file_info.time_modified = stats.st_mtime; - m_shader.file_info.file_size = static_cast(stats.st_size); - } - - // Increment timer so that the next check is a reasonable timespan away. - m_shader.file_info.time_updated += 0.1f; - } - - if (is_shader_different || m_shader.file_info.modified) { - // gs_effect_create_from_file caches results, which is bad for us. - std::vector content; - std::ifstream fs(m_shader.path.c_str(), std::ios::binary); - - if (fs.good()) { - size_t beg = static_cast(fs.tellg()); - fs.seekg(0, std::ios::end); - size_t sz = size_t(fs.tellg()) - beg; - content.resize(sz + 1); - fs.seekg(0, std::ios::beg); - fs.read(content.data(), static_cast(sz)); - fs.close(); - content[sz] = '\0'; - - try { - m_shader.effect = std::make_unique(std::string(content.data()), m_shader.path); - } catch (...) { - m_shader.effect = nullptr; - } - } - } - } - - // If the shader is different, rebuild the parameter list. - if (is_shader_different) { - if (m_shader.effect) { - // ToDo: Figure out if a recycling approach would work. - // Might improve stability in low memory situations. - std::map> new_params; - auto effect_param_list = m_shader.effect->get_parameters(); - for (auto effect_param : effect_param_list) { - paramident_t ident; - ident.first = effect_param.get_name(); - ident.second = effect_param.get_type(); - - if (is_special_parameter(ident.first, ident.second)) - continue; - - auto entry = m_parameters.find(ident); - if (entry != m_parameters.end()) { - entry->second->param = std::make_shared(effect_param); - new_params.insert_or_assign(ident, entry->second); - m_parameters.erase(entry); - } else { - std::shared_ptr param; - - if (ident.second == gs::effect_parameter::type::Boolean) { - std::shared_ptr nparam = std::make_shared(); - - std::string ui_name, ui_desc; - ui_name = ident.first; - ui_desc = ident.first; - - nparam->ui.buffer.resize(ui_name.size() + 1 + ui_desc.size() + 1); - memset(nparam->ui.buffer.data(), 0, nparam->ui.buffer.size()); - memcpy(nparam->ui.buffer.data(), ui_name.c_str(), ui_name.size()); - memcpy(nparam->ui.buffer.data() + ui_name.size() + 1, ui_desc.c_str(), ui_desc.size()); - - nparam->ui.names.resize(1); - nparam->ui.names[0] = nparam->ui.buffer.data(); - - nparam->ui.descs.resize(1); - nparam->ui.descs[0] = nparam->ui.buffer.data() + ui_name.size() + 1; - - param = std::dynamic_pointer_cast(nparam); - } else if (ident.second >= gs::effect_parameter::type::Integer - && ident.second <= gs::effect_parameter::type::Integer4) { - std::shared_ptr nparam = std::make_shared(); - - size_t cnt = (size_t)ident.second - (size_t)gs::effect_parameter::type::Integer; - - std::string ui_name[4], ui_desc[4]; - size_t bufsize = 0; - if (cnt > 0) { - for (size_t idx = 0; idx <= cnt; idx++) { - ui_name[idx] = ident.first + (char)(48 + idx); - ui_desc[idx] = ident.first + "[" + (char)(48 + idx) + "]"; - - bufsize += ui_name[idx].size() + 1; - bufsize += ui_desc[idx].size() + 1; - } - } else { - ui_name[0] = ident.first; - ui_desc[0] = ident.first; - bufsize += ui_name[0].size() + 1; - bufsize += ui_desc[0].size() + 1; - } - - nparam->ui.names.resize(cnt + 1); - nparam->ui.descs.resize(cnt + 1); - - nparam->ui.buffer.resize(bufsize); - memset(nparam->ui.buffer.data(), 0, bufsize); - size_t off = 0; - for (size_t idx = 0; idx <= cnt; idx++) { - memcpy(nparam->ui.buffer.data() + off, ui_name[idx].c_str(), ui_name[idx].size()); - nparam->ui.names[idx] = nparam->ui.buffer.data() + off; - off += ui_name[idx].size() + 1; - - memcpy(nparam->ui.buffer.data() + off, ui_desc[idx].c_str(), ui_desc[idx].size()); - nparam->ui.descs[idx] = nparam->ui.buffer.data() + off; - off += ui_desc[idx].size() + 1; - } - - param = std::dynamic_pointer_cast(nparam); - } else if (ident.second >= gs::effect_parameter::type::Float - && ident.second <= gs::effect_parameter::type::Float4) { - std::shared_ptr nparam = std::make_shared(); - - size_t cnt = (size_t)ident.second - (size_t)gs::effect_parameter::type::Float; - - std::string ui_name[4], ui_desc[4]; - size_t bufsize = 0; - if (cnt > 0) { - for (size_t idx = 0; idx <= cnt; idx++) { - ui_name[idx] = ident.first + (char)(48 + idx); - ui_desc[idx] = ident.first + "[" + (char)(48 + idx) + "]"; - - bufsize += ui_name[idx].size() + 1; - bufsize += ui_desc[idx].size() + 1; - } - } else { - ui_name[0] = ident.first; - ui_desc[0] = ident.first; - bufsize += ui_name[0].size() + 1; - bufsize += ui_desc[0].size() + 1; - } - - nparam->ui.names.resize(cnt + 1); - nparam->ui.descs.resize(cnt + 1); - - nparam->ui.buffer.resize(bufsize); - memset(nparam->ui.buffer.data(), 0, bufsize); - size_t off = 0; - for (size_t idx = 0; idx <= cnt; idx++) { - memcpy(nparam->ui.buffer.data() + off, ui_name[idx].c_str(), ui_name[idx].size()); - nparam->ui.names[idx] = nparam->ui.buffer.data() + off; - off += ui_name[idx].size() + 1; - - memcpy(nparam->ui.buffer.data() + off, ui_desc[idx].c_str(), ui_desc[idx].size()); - nparam->ui.descs[idx] = nparam->ui.buffer.data() + off; - off += ui_desc[idx].size() + 1; - } - - param = std::dynamic_pointer_cast(nparam); - } else if (ident.second == gs::effect_parameter::type::Texture) { - std::shared_ptr nparam = std::make_shared(); - - std::string ui_name[3], ui_desc[3]; - size_t bufsize = 0; - - ui_name[0] = ident.first + "_type"; - ui_desc[0] = ident.first + " Type"; - ui_name[1] = ident.first + "_file"; - ui_desc[1] = ident.first + " File"; - ui_desc[2] = ident.first + "_source"; - ui_desc[2] = ident.first + " Source"; - - for (size_t i = 0; i <= 2; i++) { - bufsize += ui_name[i].size() + ui_desc[i].size() + 2; - } - - nparam->ui.names.resize(3); - nparam->ui.descs.resize(3); - - nparam->ui.buffer.resize(bufsize); - memset(nparam->ui.buffer.data(), 0, bufsize); - size_t off = 0; - for (size_t idx = 0; idx <= 2; idx++) { - memcpy(nparam->ui.buffer.data() + off, ui_name[idx].c_str(), ui_name[idx].size()); - nparam->ui.names[idx] = nparam->ui.buffer.data() + off; - off += ui_name[idx].size() + 1; - - memcpy(nparam->ui.buffer.data() + off, ui_desc[idx].c_str(), ui_desc[idx].size()); - nparam->ui.descs[idx] = nparam->ui.buffer.data() + off; - off += ui_desc[idx].size() + 1; - } - - param = std::dynamic_pointer_cast(nparam); - } - - if (param) { - param->name = ident.first; - param->param = std::make_shared(effect_param); - new_params.insert_or_assign(ident, param); - } - } - } - m_parameters = std::move(new_params); - } else { - m_parameters.clear(); - } - } - - return is_shader_different; -} - -void gfx::effect_source::update_parameters(obs_data_t* data) -{ - for (auto prm : m_parameters) { - if (prm.first.second == gs::effect_parameter::type::Boolean) { - auto param = std::static_pointer_cast(prm.second); - param->value = obs_data_get_bool(data, prm.second->ui.names[0]); - } else if (prm.first.second >= gs::effect_parameter::type::Integer - && prm.first.second <= gs::effect_parameter::type::Integer4) { - auto param = std::static_pointer_cast(prm.second); - for (size_t idx = 0; idx < prm.second->ui.names.size(); idx++) { - param->value[idx] = int32_t(obs_data_get_int(data, prm.second->ui.names[idx])); - } - } else if (prm.first.second >= gs::effect_parameter::type::Float - && prm.first.second <= gs::effect_parameter::type::Float4) { - auto param = std::static_pointer_cast(prm.second); - for (size_t idx = 0; idx < prm.second->ui.names.size(); idx++) { - param->value[idx] = float_t(obs_data_get_double(data, prm.second->ui.names[idx])); - } - } - } -} - -void gfx::effect_source::apply_parameters() -{ - for (auto prm : m_parameters) { - if (prm.first.second == gs::effect_parameter::type::Boolean) { - auto param = std::static_pointer_cast(prm.second); - param->param->set_bool(param->value); - } else if (prm.first.second >= gs::effect_parameter::type::Integer - && prm.first.second <= gs::effect_parameter::type::Integer4) { - auto param = std::static_pointer_cast(prm.second); - switch (prm.first.second) { - case gs::effect_parameter::type::Integer: - param->param->set_int(param->value[0]); - break; - case gs::effect_parameter::type::Integer2: - param->param->set_int2(param->value[0], param->value[1]); - break; - case gs::effect_parameter::type::Integer3: - param->param->set_int3(param->value[0], param->value[1], param->value[2]); - break; - case gs::effect_parameter::type::Integer4: - param->param->set_int4(param->value[0], param->value[1], param->value[2], param->value[3]); - break; - default: - break; - } - } else if (prm.first.second >= gs::effect_parameter::type::Float - && prm.first.second <= gs::effect_parameter::type::Float4) { - auto param = std::static_pointer_cast(prm.second); - switch (prm.first.second) { - case gs::effect_parameter::type::Float: - param->param->set_float(param->value[0]); - break; - case gs::effect_parameter::type::Float2: - param->param->set_float2(param->value[0], param->value[1]); - break; - case gs::effect_parameter::type::Float3: - param->param->set_float3(param->value[0], param->value[1], param->value[2]); - break; - case gs::effect_parameter::type::Float4: - param->param->set_float4(param->value[0], param->value[1], param->value[2], param->value[3]); - break; - default: - break; - } - } - } -} - -void gfx::effect_source::activate() -{ - m_timeActive = 0; -} - -void gfx::effect_source::deactivate() -{ - m_timeActive = 0; -} - -std::string gfx::effect_source::get_shader_file() -{ - return m_shader.path; -} - -uint32_t gfx::effect_source::get_width() -{ - return 0; -} - -uint32_t gfx::effect_source::get_height() -{ - return 0; -} - -void gfx::effect_source::video_tick(float time) -{ - // Shader Timers - m_timeExisting += time; - m_timeActive += time; - - // File Timer - m_shader.file_info.time_updated -= time; - - video_tick_impl(time); -} - -void gfx::effect_source::video_render(gs_effect_t* parent_effect) -{ - if (!m_source) { - obs_source_skip_video_filter(m_source); - return; - } - - obs_source_t* parent = obs_filter_get_parent(m_source); - obs_source_t* target = obs_filter_get_target(m_source); - if (!parent || !target || !m_shader.effect) { - obs_source_skip_video_filter(m_source); - return; - } - - uint32_t viewW = obs_source_get_base_width(target), viewH = obs_source_get_base_height(target); - if (!viewW || !viewH) { - obs_source_skip_video_filter(m_source); - return; - } - - apply_parameters(); - if (!video_render_impl(parent_effect, viewW, viewH)) { - obs_source_skip_video_filter(m_source); - return; - } - if (m_shader.effect->has_parameter("ViewSize", gs::effect_parameter::type::Float2)) { - m_shader.effect->get_parameter("ViewSize").set_float2(float_t(viewW), float_t(viewH)); - } - if (m_shader.effect->has_parameter("ViewSizeI" /*, gs::effect_parameter::type::Integer2*/)) { - m_shader.effect->get_parameter("ViewSizeI").set_int2(int32_t(viewW), int32_t(viewH)); - } - if (m_shader.effect->has_parameter("Time", gs::effect_parameter::type::Float)) { - m_shader.effect->get_parameter("Time").set_float(m_timeExisting); - } - if (m_shader.effect->has_parameter("TimeActive", gs::effect_parameter::type::Float)) { - m_shader.effect->get_parameter("TimeActive").set_float(m_timeActive); - } - - gs_load_indexbuffer(nullptr); - gs_load_vertexbuffer(m_quadBuffer->update()); + gs_blend_state_push(); + gs_matrix_push(); gs_reset_blend_state(); + gs_enable_blending(false); + gs_enable_color(true, true, true, true); gs_enable_depth_test(false); - gs_matrix_push(); - gs_matrix_scale3f(float_t(viewW), float_t(viewH), 1.0f); - while (gs_effect_loop(m_shader.effect->get_object(), "Draw")) { - gs_draw(gs_draw_mode::GS_TRISTRIP, 0, 4); - } - gs_matrix_pop(); + gs_enable_stencil_test(false); + gs_enable_stencil_write(false); + gs_ortho(0, 1, 0, 1, -1., 1.); - gs_load_indexbuffer(nullptr); - gs_load_vertexbuffer(nullptr); + while (gs_effect_loop(_effect->get_object(), technique.c_str())) { + } + + gs_matrix_pop(); + gs_blend_state_pop(); } diff --git a/source/gfx/gfx-effect-source.hpp b/source/gfx/gfx-effect-source.hpp index 6d4c4d0c..0a0ef6b7 100644 --- a/source/gfx/gfx-effect-source.hpp +++ b/source/gfx/gfx-effect-source.hpp @@ -29,143 +29,93 @@ #include "obs/gs/gs-vertexbuffer.hpp" // OBS +extern "C" { #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4201) #endif #include +#include #ifdef _MSC_VER #pragma warning(pop) #endif - -// Data Defines -#define D_TYPE "CustomShader.Type" -#define D_INPUT_TEXT "CustomShader.Input.Text" -#define D_INPUT_FILE "CustomShader.Input.File" - -// Translation Defines -#define T_TYPE "CustomShader.Type" -#define T_TYPE_TEXT "CustomShader.Type.Text" -#define T_TYPE_FILE "CustomShader.Type.File" -#define T_INPUT_TEXT "CustomShader.Input.Text" -#define T_INPUT_FILE "CustomShader.Input.File" -#define T_TEXTURE_TYPE "CustomShader.Texture.Type" -#define T_TEXTURE_TYPE_FILE "CustomShader.Texture.Type.File" -#define T_TEXTURE_TYPE_SOURCE "CustomShader.Texture.Type.Source" +} namespace gfx { - class effect_source { - public: + namespace effect_source { struct parameter { - std::string name = ""; + std::shared_ptr effect; std::shared_ptr param; - struct { - std::vector buffer; - std::vector names; - std::vector descs; - } ui; + std::string name; + std::string description; + std::string formulae; + bool visible; + + parameter(std::shared_ptr effect, std::string name); }; struct bool_parameter : parameter { - bool value = false; + bool value; + + bool_parameter(std::shared_ptr effect, std::string name); }; - struct int_parameter : parameter { - int32_t value[4] = {0, 0, 0, 0}; + struct value_parameter : parameter { + union { + float_t f[16]; + int32_t i[4]; + } value; + union { + float_t f[16]; + int32_t i[4]; + } minimum; + union { + float_t f[16]; + int32_t i[4]; + } maximum; + + value_parameter(std::shared_ptr effect, std::string name); }; - struct float_parameter : parameter { - float_t value[4] = {0, 0, 0, 0}; + struct matrix_parameter : parameter { + matrix4 value; + matrix4 minimum; + matrix4 maximum; + + matrix_parameter(std::shared_ptr effect, std::string name); + }; + struct string_parameter : parameter { + std::string value; + + string_parameter(std::shared_ptr effect, std::string name); }; struct texture_parameter : parameter { - bool isSource = false; + std::shared_ptr value; - struct { - std::string path = ""; - std::shared_ptr tex; - struct { - float_t time_updated = 0; - time_t time_create = 0; - time_t time_modified = 0; - size_t file_size = 0; - bool modified = true; - } info; - } file; - struct { - std::string name = ""; - std::shared_ptr tex; - std::shared_ptr final_tex; - } source; - - struct { - bool doResample = false; - std::pair resolution = {10, 10}; - std::shared_ptr rt; - } resample; + texture_parameter(std::shared_ptr effect, std::string name); }; - struct matrix_parameter : parameter {}; - typedef std::pair paramident_t; - private: - protected: - obs_source_t* m_source; - std::shared_ptr m_quadBuffer; + typedef std::pair param_ident_t; - // Effect Information - struct { - std::shared_ptr effect; - std::string text; - std::string path; - struct { - float_t time_updated; - time_t time_create; - time_t time_modified; - size_t file_size; - bool modified; - } file_info; - } m_shader; - std::map> m_parameters; + class shader_instance { + std::string _file; + std::shared_ptr _effect; + std::map> _params; - // Status - float_t m_timeExisting; - float_t m_timeActive; + std::shared_ptr _tri; - std::string m_defaultShaderPath = "shaders/"; + float_t _last_check; + size_t _last_size; + time_t _last_modify_time; + time_t _last_create_time; - static bool property_type_modified(void* priv, obs_properties_t* props, obs_property_t* prop, obs_data_t* sett); - static bool property_input_modified(void* priv, obs_properties_t* props, obs_property_t* prop, - obs_data_t* sett); - static void fill_source_list(obs_property_t* prop); - static bool property_texture_type_modified(void* priv, obs_properties_t* props, obs_property_t* prop, - obs_data_t* sett); - static bool property_texture_input_modified(void* priv, obs_properties_t* props, obs_property_t* prop, - obs_data_t* sett); + void load_file(std::string file); - virtual bool is_special_parameter(std::string name, gs::effect_parameter::type type) = 0; + public: + shader_instance(std::string file); + ~shader_instance(); - virtual bool video_tick_impl(float_t time) = 0; - virtual bool video_render_impl(gs_effect_t* parent_effect, uint32_t viewW, uint32_t viewH) = 0; + void tick(float_t time); - public: - effect_source(obs_data_t* data, obs_source_t* owner); - virtual ~effect_source(); - - void get_properties(obs_properties_t* properties); - static void get_defaults(obs_data_t* data); - void update(obs_data_t* data); - bool test_for_updates(const char* text, const char* path); - void update_parameters(obs_data_t* data); - void apply_parameters(); - - void activate(); - void deactivate(); - - std::string get_shader_file(); - - uint32_t get_width(); - uint32_t get_height(); - void video_tick(float time); - void video_render(gs_effect_t* parent_effect); - - public: - enum class InputTypes { Text, File }; - }; + void render(std::string technique); + }; + } // namespace effect_source } // namespace gfx