From e59bc2fdd2f3b88bc451be8d618039440cbf36c2 Mon Sep 17 00:00:00 2001 From: Michael Fabian 'Xaymar' Dirks Date: Wed, 7 Aug 2019 12:43:23 +0200 Subject: [PATCH] filter-custom-shader: Rewrite from scratch --- source/filters/filter-custom-shader.cpp | 638 ++++++------------------ source/filters/filter-custom-shader.hpp | 83 ++- 2 files changed, 197 insertions(+), 524 deletions(-) diff --git a/source/filters/filter-custom-shader.cpp b/source/filters/filter-custom-shader.cpp index 1194e5b..9e78ae2 100644 --- a/source/filters/filter-custom-shader.cpp +++ b/source/filters/filter-custom-shader.cpp @@ -18,508 +18,186 @@ */ #include "filter-custom-shader.hpp" -#include -#include -#include #include "strings.hpp" +#include "utility.hpp" -// OBS -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4201) -#endif -#include -#include -#include -#ifdef _MSC_VER -#pragma warning(pop) -#endif +#define ST "Filter.Shader" -#define S "Filter.CustomShader" - -/** Shader Definitions - * - Only one technique, any number of passes. - * - Technique must be named 'Draw'. - * - Parameters are split by the last underscore (_) to determine if it is a special parameter or not. - * - * Predefined Parameters: - * - ViewProj: The current view projection matrix (float4x4). - * - ViewSize: The current rendered size (float2). - * - ViewSizeI: The current rendered size as an int (int2). - * - Time: Time passed during total rendering in seconds (float). - * - TimeActive: Time since last activation (float). - * - image: The source being filtered (texture2d). - * - * Texture Special Parameters: - * - $(name)_Size: Texture Size (float2). - * - $(name)_SizeI: Texture Size (int2). - * - $(name)_Texel: Texture Texel Size (1/Texture Size) (float2). -**/ - -enum class ShaderType : int64_t { Text, File }; - -static filter::CustomShader* handler; -P_INITIALIZER(HandlerInit) +P_INITIALIZER(FilterShaderInit) { - initializer_functions.push_back([] { handler = new filter::CustomShader(); }); - finalizer_functions.push_back([] { delete handler; }); + initializer_functions.push_back([] { filter::shader::shader_factory::initialize(); }); + finalizer_functions.push_back([] { filter::shader::shader_factory::finalize(); }); } -filter::CustomShader::CustomShader() +static std::shared_ptr factory_instance = nullptr; + +void filter::shader::shader_factory::initialize() { - return; // TEMP - memset(&sourceInfo, 0, sizeof(obs_source_info)); - sourceInfo.id = "obs-stream-effects-filter-custom-shader"; - sourceInfo.type = OBS_SOURCE_TYPE_FILTER; - sourceInfo.output_flags = OBS_SOURCE_VIDEO; - sourceInfo.get_name = get_name; - sourceInfo.get_defaults = get_defaults; - sourceInfo.get_properties = get_properties; - - sourceInfo.create = create; - sourceInfo.destroy = destroy; - sourceInfo.update = update; - sourceInfo.activate = activate; - sourceInfo.deactivate = deactivate; - sourceInfo.video_tick = video_tick; - sourceInfo.video_render = video_render; - - obs_register_source(&sourceInfo); + factory_instance = std::make_shared(); } -filter::CustomShader::~CustomShader() {} - -const char* filter::CustomShader::get_name(void*) +void filter::shader::shader_factory::finalize() { - return D_TRANSLATE(S); + factory_instance.reset(); } -void filter::CustomShader::get_defaults(obs_data_t* data) +std::shared_ptr filter::shader::shader_factory::get() { - gfx::effect_source::get_defaults(data); + return factory_instance; } -obs_properties_t* filter::CustomShader::get_properties(void* ptr) +static void get_defaults(obs_data_t* data) {} + +filter::shader::shader_factory::shader_factory() { - obs_properties_t* pr = obs_properties_create_param(ptr, nullptr); - reinterpret_cast(ptr)->get_properties(pr); - return pr; -} - -void* filter::CustomShader::create(obs_data_t* data, obs_source_t* src) -{ - return new Instance(data, src); -} - -void filter::CustomShader::destroy(void* ptr) -{ - delete reinterpret_cast(ptr); -} - -uint32_t filter::CustomShader::get_width(void* ptr) -{ - return reinterpret_cast(ptr)->get_width(); -} - -uint32_t filter::CustomShader::get_height(void* ptr) -{ - return reinterpret_cast(ptr)->get_height(); -} - -void filter::CustomShader::update(void* ptr, obs_data_t* data) -{ - reinterpret_cast(ptr)->update(data); -} - -void filter::CustomShader::activate(void* ptr) -{ - reinterpret_cast(ptr)->activate(); -} - -void filter::CustomShader::deactivate(void* ptr) -{ - reinterpret_cast(ptr)->deactivate(); -} - -void filter::CustomShader::video_tick(void* ptr, float time) -{ - reinterpret_cast(ptr)->video_tick(time); -} - -void filter::CustomShader::video_render(void* ptr, gs_effect_t* effect) -{ - reinterpret_cast(ptr)->video_render(effect); -} - -filter::CustomShader::Instance::Instance(obs_data_t* data, obs_source_t* source) : gfx::effect_source(data, source) -{ - m_defaultShaderPath = "shaders/filter/example._effect"; - m_renderTarget = std::make_shared(GS_RGBA, GS_ZS_NONE); - update(data); -} - -filter::CustomShader::Instance::~Instance() {} - -//void filter::CustomShader::Instance::update(obs_data_t *data) { -// ShaderType shaderType = (ShaderType)obs_data_get_int(data, S_TYPE); -// if (shaderType == ShaderType::Text) { -// const char* shaderText = obs_data_get_string(data, S_CONTENT_TEXT); -// try { -// m_effect.effect = std::make_unique(shaderText, "Text Shader"); -// } catch (std::runtime_error& ex) { -// const char* filterName = obs_source_get_name(source); -// P_LOG_ERROR("[%s] Shader loading failed with error(s): %s", filterName, ex.what()); -// } -// } else if (shaderType == ShaderType::File) { -// CheckShaderFile(obs_data_get_string(data, S_CONTENT_FILE), 0.0f); -// } -// -// m_effectParameters.clear(); -// if (m_effect.effect && m_effect.effect->count_parameters() > 0) { -// for (auto p : m_effect.effect->get_parameters()) { -// std::string p_name = p.get_name(); -// std::string p_desc = p_name; -// -// if (IsSpecialParameter(p_name, p.get_type())) -// continue; -// -// Parameter prm; -// prm.name = p_name; -// prm.type = p.get_type(); -// -// switch (p.get_type()) { -// case gs::effect_parameter::type::Boolean: -// { -// prm.uiNames.push_back(p_name); -// prm.uiDescriptions.push_back(p_desc); -// prm.value.b = obs_data_get_bool(data, p_name.c_str()); -// 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: -// { -// { -// prm.uiNames.push_back(p_name + "0"); -// prm.uiDescriptions.push_back(p_desc + "[0]"); -// prm.value.f[0] = (float_t)obs_data_get_double(data, prm.uiNames.back().c_str()); -// } -// if (p.get_type() >= gs::effect_parameter::type::Float2) { -// prm.uiNames.push_back(p_name + "1"); -// prm.uiDescriptions.push_back(p_desc + "[1]"); -// prm.value.f[1] = (float_t)obs_data_get_double(data, prm.uiNames.back().c_str()); -// } -// if (p.get_type() >= gs::effect_parameter::type::Float3) { -// prm.uiNames.push_back(p_name + "2"); -// prm.uiDescriptions.push_back(p_desc + "[2]"); -// prm.value.f[2] = (float_t)obs_data_get_double(data, prm.uiNames.back().c_str()); -// } -// if (p.get_type() >= gs::effect_parameter::type::Float4) { -// prm.uiNames.push_back(p_name + "3"); -// prm.uiDescriptions.push_back(p_desc + "[3]"); -// prm.value.f[3] = (float_t)obs_data_get_double(data, prm.uiNames.back().c_str()); -// } -// break; -// } -// case gs::effect_parameter::type::Integer: -// case gs::effect_parameter::type::Integer2: -// case gs::effect_parameter::type::Integer3: -// case gs::effect_parameter::type::Integer4: -// { -// { -// prm.uiNames.push_back(p_name + "0"); -// prm.uiDescriptions.push_back(p_desc + "[0]"); -// prm.value.i[0] = (int32_t)obs_data_get_int(data, prm.uiNames.back().c_str()); -// } -// if (p.get_type() >= gs::effect_parameter::type::Integer2) { -// prm.uiNames.push_back(p_name + "1"); -// prm.uiDescriptions.push_back(p_desc + "[1]"); -// prm.value.i[1] = (int32_t)obs_data_get_int(data, prm.uiNames.back().c_str()); -// } -// if (p.get_type() >= gs::effect_parameter::type::Integer3) { -// prm.uiNames.push_back(p_name + "2"); -// prm.uiDescriptions.push_back(p_desc + "[2]"); -// prm.value.i[2] = (int32_t)obs_data_get_int(data, prm.uiNames.back().c_str()); -// } -// if (p.get_type() >= gs::effect_parameter::type::Integer4) { -// prm.uiNames.push_back(p_name + "3"); -// prm.uiDescriptions.push_back(p_desc + "[3]"); -// prm.value.i[3] = (int32_t)obs_data_get_int(data, prm.uiNames.back().c_str()); -// } -// break; -// } -// case gs::effect_parameter::type::Texture: -// { -// prm.uiNames.push_back(p_name + "_type"); -// prm.uiDescriptions.push_back(p_desc + " Type"); -// prm.value.textureIsSource = obs_data_get_int(data, prm.uiNames.back().c_str()) == 1; -// prm.uiNames.push_back(p_name + "_source"); -// prm.uiDescriptions.push_back(p_desc); -// prm.value.source.name = obs_data_get_string(data, prm.uiNames.back().c_str()); -// prm.uiNames.push_back(p_name + "_file"); -// prm.uiDescriptions.push_back(p_desc); -// if (obs_data_has_user_value(data, prm.uiNames.back().c_str())) { -// prm.value.file.path = obs_data_get_string(data, prm.uiNames.back().c_str()); -// } else { -// prm.value.file.path = obs_module_file("white.png"); -// } -// break; -// } -// } -// m_effectParameters.emplace_back(prm); -// } -// } -// -// for (Parameter& prm : m_effectParameters) { -// switch (prm.type) { -// case gs::effect_parameter::type::Boolean: -// prm.value.b = obs_data_get_bool(data, prm.uiNames[0].c_str()); -// break; -// case gs::effect_parameter::type::Float4: -// prm.value.f[3] = (float_t)obs_data_get_double(data, prm.uiNames[3].c_str()); -// case gs::effect_parameter::type::Float3: -// prm.value.f[2] = (float_t)obs_data_get_double(data, prm.uiNames[2].c_str()); -// case gs::effect_parameter::type::Float2: -// prm.value.f[1] = (float_t)obs_data_get_double(data, prm.uiNames[1].c_str()); -// case gs::effect_parameter::type::Float: -// prm.value.f[0] = (float_t)obs_data_get_double(data, prm.uiNames[0].c_str()); -// break; -// case gs::effect_parameter::type::Integer4: -// prm.value.i[3] = (int32_t)obs_data_get_int(data, prm.uiNames[3].c_str()); -// case gs::effect_parameter::type::Integer3: -// prm.value.i[2] = (int32_t)obs_data_get_int(data, prm.uiNames[2].c_str()); -// case gs::effect_parameter::type::Integer2: -// prm.value.i[1] = (int32_t)obs_data_get_int(data, prm.uiNames[1].c_str()); -// case gs::effect_parameter::type::Integer: -// prm.value.i[0] = (int32_t)obs_data_get_int(data, prm.uiNames[0].c_str()); -// break; -// case gs::effect_parameter::type::Texture: -// prm.value.textureIsSource = obs_data_get_int(data, prm.uiNames[0].c_str()) == 1; -// std::string nName = obs_data_get_string(data, prm.uiNames[1].c_str()); -// if (nName != prm.value.source.name) { -// prm.value.source.name = nName; -// prm.value.source.dirty = true; -// } -// std::string nPath = obs_data_get_string(data, prm.uiNames[2].c_str()); -// if (nPath != prm.value.file.path) { -// prm.value.file.path = nPath; -// prm.value.file.dirty = true; -// } -// break; -// } -// } -//} - -uint32_t filter::CustomShader::Instance::get_width() -{ - return 0; -} - -uint32_t filter::CustomShader::Instance::get_height() -{ - return 0; -} - -bool filter::CustomShader::Instance::is_special_parameter(std::string name, gs::effect_parameter::type type) -{ - std::pair reservedParameters[] = { - {"ViewProj", gs::effect_parameter::type::Matrix}, {"ViewSize", gs::effect_parameter::type::Float2}, - {"ViewSizeI", gs::effect_parameter::type::Integer2}, {"Time", gs::effect_parameter::type::Float}, - {"TimeActive", gs::effect_parameter::type::Float}, {"Image", gs::effect_parameter::type::Texture}, - }; - std::pair textureParameters[] = { - {"Size", gs::effect_parameter::type::Float2}, - {"SizeI", gs::effect_parameter::type::Integer2}, - {"Texel", gs::effect_parameter::type::Float2}}; - - for (auto& kv : reservedParameters) { - if ((name == kv.first) && (type == kv.second)) - return true; - } - - // Split by last underscore(_) (if there is one). - size_t posUnderscore = name.find_last_of('_'); - if (posUnderscore != std::string::npos) { - std::string firstPart, secondPart; - firstPart = name.substr(0, posUnderscore); - secondPart = name.substr(posUnderscore + 1); + memset(&_source_info, 0, sizeof(obs_source_info)); + _source_info.id = "obs-stream-effects-filter-shader"; + _source_info.type = OBS_SOURCE_TYPE_FILTER; + _source_info.output_flags = OBS_SOURCE_VIDEO; + _source_info.get_name = [](void*) { return D_TRANSLATE(ST); }; + _source_info.get_defaults = get_defaults; + _source_info.create = [](obs_data_t* data, obs_source_t* self) { try { - gs::effect_parameter prm = m_shader.effect->get_parameter(firstPart); - if (prm.get_type() == gs::effect_parameter::type::Texture) { - for (auto& kv : textureParameters) { - if ((secondPart == kv.first) && (type == kv.second)) - return true; - } - } + return static_cast(new filter::shader::shader_instance(data, self)); + } catch (std::exception& ex) { + P_LOG_ERROR(" Failed to create source, error: %s", ex.what()); } catch (...) { - return false; + P_LOG_ERROR(" Failed to create source."); } - } - - return false; -} - -bool filter::CustomShader::Instance::apply_special_parameters(uint32_t, uint32_t) -{ - std::unique_ptr imageTexture; - m_renderTarget->get_texture(imageTexture); - - if (m_shader.effect->has_parameter("Image", gs::effect_parameter::type::Texture)) { - m_shader.effect->get_parameter("Image").set_texture(imageTexture->get_object()); - } else { - return false; - } - if (m_shader.effect->has_parameter("Image_Size", gs::effect_parameter::type::Float2)) { - m_shader.effect->get_parameter("Image_Size") - .set_float2(float_t(imageTexture->get_width()), float_t(imageTexture->get_height())); - } - if (m_shader.effect->has_parameter("Image_SizeI" /*, gs::effect_parameter::type::Integer2*/)) { - m_shader.effect->get_parameter("Image_SizeI") - .set_int2(static_cast(imageTexture->get_width()), - static_cast(imageTexture->get_height())); - } - if (m_shader.effect->has_parameter("Image_Texel", gs::effect_parameter::type::Float2)) { - m_shader.effect->get_parameter("Image_Texel") - .set_float2(float_t(1.0 / imageTexture->get_width()), float_t(1.0 / imageTexture->get_height())); - } - - return true; -} - -bool filter::CustomShader::Instance::video_tick_impl(float_t) -{ - return true; -} - -bool filter::CustomShader::Instance::video_render_impl(gs_effect_t* parent_effect, uint32_t viewW, uint32_t viewH) -{ - // Render original source to render target. - { - auto op = m_renderTarget->render(viewW, viewH); - vec4 black; - vec4_zero(&black); - gs_ortho(0, (float_t)viewW, 0, (float_t)viewH, 0, 1); - gs_clear(GS_CLEAR_COLOR, &black, 0, 0); - if (obs_source_process_filter_begin(m_source, GS_RGBA, OBS_NO_DIRECT_RENDERING)) { - obs_source_process_filter_end( - m_source, parent_effect ? parent_effect : obs_get_base_effect(OBS_EFFECT_DEFAULT), viewW, viewH); + return static_cast(nullptr); + }; + _source_info.destroy = [](void* ptr) { + try { + delete reinterpret_cast(ptr); + } catch (std::exception& ex) { + P_LOG_ERROR(" Failed to delete source, error: %s", ex.what()); + } catch (...) { + P_LOG_ERROR(" Failed to delete source."); } - } - gs_texture_t* sourceTexture = m_renderTarget->get_object(); - if (!sourceTexture) { - return false; - } + }; + _source_info.get_properties = [](void* ptr) { + obs_properties_t* pr = obs_properties_create(); + try { + if (ptr) + reinterpret_cast(ptr)->properties(pr); + } catch (std::exception& ex) { + P_LOG_ERROR(" Failed to retrieve options, error: %s", ex.what()); + } catch (...) { + P_LOG_ERROR(" Failed to retrieve options."); + } + return pr; + }; + _source_info.update = [](void* ptr, obs_data_t* data) { + try { + if (ptr) + reinterpret_cast(ptr)->update(data); + } catch (std::exception& ex) { + P_LOG_ERROR(" Failed to update, error: %s", ex.what()); + } catch (...) { + P_LOG_ERROR(" Failed to update."); + } + }; + _source_info.activate = [](void* ptr) { + try { + if (ptr) + reinterpret_cast(ptr)->activate(); + } catch (std::exception& ex) { + P_LOG_ERROR(" Failed to activate, error: %s", ex.what()); + } catch (...) { + P_LOG_ERROR(" Failed to activate."); + } + }; + _source_info.deactivate = [](void* ptr) { + try { + if (ptr) + reinterpret_cast(ptr)->deactivate(); + } catch (std::exception& ex) { + P_LOG_ERROR(" Failed to deactivate, error: %s", ex.what()); + } catch (...) { + P_LOG_ERROR(" Failed to deactivate."); + } + }; + _source_info.video_tick = [](void* ptr, float_t time) { + try { + if (ptr) + reinterpret_cast(ptr)->video_tick(time); + } catch (std::exception& ex) { + P_LOG_ERROR(" Failed to tick, error: %s", ex.what()); + } catch (...) { + P_LOG_ERROR(" Failed to tick."); + } + }; + _source_info.video_render = [](void* ptr, gs_effect_t* effect) { + try { + if (ptr) + reinterpret_cast(ptr)->video_render(effect); + } catch (std::exception& ex) { + P_LOG_ERROR(" Failed to render, error: %s", ex.what()); + } catch (...) { + P_LOG_ERROR(" Failed to render."); + } + }; - if (!apply_special_parameters(viewW, viewH)) { - return false; - } - - return true; + obs_register_source(&_source_info); } -//void filter::CustomShader::Instance::video_render(gs_effect_t *effect) { -// for (Parameter& prm : m_effectParameters) { -// gs::effect_parameter eprm = m_effect.effect->get_parameter(prm.name); -// switch (prm.type) { -// case gs::effect_parameter::type::Texture: -// if (prm.value.textureIsSource) { -// if (prm.value.source.rendertarget && prm.value.source.source) { -// uint32_t w, h; -// w = obs_source_get_width(prm.value.source.source); -// h = obs_source_get_height(prm.value.source.source); -// { -// auto op = prm.value.source.rendertarget->render(w, h); -// vec4 black; vec4_zero(&black); -// gs_ortho(0, (float_t)w, 0, (float_t)h, 0, 1); -// gs_clear(GS_CLEAR_COLOR, &black, 0, 0); -// obs_source_video_render(prm.value.source.source); -// } -// eprm.set_texture(prm.value.source.rendertarget->get_object()); -// } -// } else { -// if (prm.value.file.texture) { -// eprm.set_texture(prm.value.file.texture); -// } -// } -// break; -// } -// } -// -//} +filter::shader::shader_factory::~shader_factory() {} -//void filter::CustomShader::Instance::CheckTextures(float_t time) { -// -// for (Parameter& prm : m_effectParameters) { -// if (prm.type != gs::effect_parameter::type::Texture) -// continue; -// -// if (prm.value.textureIsSource) { -// // If the source field is empty, simply clear the source reference. -// if (prm.value.source.name.empty()) { -// if (prm.value.source.source) -// obs_source_release(source); -// prm.value.source.source = nullptr; -// continue; -// } -// -// // Ensure that a render target exists. -// if (!prm.value.source.rendertarget) -// prm.value.source.rendertarget = std::make_shared(GS_RGBA, GS_ZS_NONE); -// -// // Finally check if the source property was modified or is empty. -// if (prm.value.source.dirty || !prm.value.source.source) { -// prm.value.source.dirty = false; -// if (prm.value.source.source) -// obs_source_release(prm.value.source.source); -// prm.value.source.source = obs_get_source_by_name(prm.value.source.name.c_str()); -// } -// } else { -// bool doRefresh = false; -// -// // If the path is empty, don't attempt to update any files and simply null the texture. -// if (prm.value.file.path.empty()) { -// prm.value.file.texture = nullptr; -// continue; -// } -// -// // If the property was modified or the texture is empty, force a refresh. -// if (prm.value.file.dirty || !prm.value.file.texture) { -// doRefresh = true; -// } -// -// // Skip testing if the last test was less than 1/2 of a second away. -// prm.value.file.lastCheck += time; -// if (prm.value.file.lastCheck < 0.5f) { -// if (!doRefresh) -// continue; -// } else { -// prm.value.file.lastCheck = prm.value.file.lastCheck - 0.5f; -// } -// -// // Test if the file was modified. -// struct stat stats; -// if (os_stat(prm.value.file.path.c_str(), &stats) == 0) { -// doRefresh = doRefresh || -// (prm.value.file.createTime != stats.st_ctime) || -// (prm.value.file.modifiedTime != stats.st_mtime) || -// (prm.value.file.fileSize != stats.st_size); -// prm.value.file.createTime = stats.st_ctime; -// prm.value.file.modifiedTime = stats.st_mtime; -// prm.value.file.fileSize = stats.st_size; -// } -// -// if (doRefresh) { -// prm.value.file.dirty = false; -// try { -// prm.value.file.texture = std::make_shared(prm.value.file.path); -// } catch (std::runtime_error& ex) { -// const char* filterName = obs_source_get_name(source); -// P_LOG_ERROR("[%s] Loading texture file '%s' failed with error(s): %s", -// filterName, prm.value.file.path.c_str(), ex.what()); -// } -// } -// } -// } -//} +filter::shader::shader_instance::shader_instance(obs_data_t* data, obs_source_t* self) + : _self(self), _active(true), _width(0), _height(0) +{} + +filter::shader::shader_instance::~shader_instance() {} + +uint32_t filter::shader::shader_instance::width() +{ + return _width; +} + +uint32_t filter::shader::shader_instance::height() +{ + return _height; +} + +void filter::shader::shader_instance::properties(obs_properties_t* props) {} + +void filter::shader::shader_instance::update(obs_data_t* data) {} + +void filter::shader::shader_instance::activate() +{ + _active = true; +} + +void filter::shader::shader_instance::deactivate() +{ + _active = false; +} + +void filter::shader::shader_instance::video_tick(float_t sec_since_last) +{ + obs_source_t* target = obs_filter_get_target(_self); + + { // Update width and height. + _width = obs_source_get_base_width(target); + _height = obs_source_get_base_height(target); + } +} + +void filter::shader::shader_instance::video_render(gs_effect_t* effect) +{ + // Grab initial values. + obs_source_t* parent = obs_filter_get_parent(_self); + obs_source_t* target = obs_filter_get_target(_self); + uint32_t width = obs_source_get_base_width(target); + uint32_t height = obs_source_get_base_height(target); + gs_effect_t* effect_default = obs_get_base_effect(obs_base_effect::OBS_EFFECT_DEFAULT); + + // Skip filter if anything is wrong. + if (!_active || !parent || !target || !width || !height || !effect_default) { + obs_source_skip_video_filter(_self); + return; + } + + obs_source_skip_video_filter(_self); +} diff --git a/source/filters/filter-custom-shader.hpp b/source/filters/filter-custom-shader.hpp index 6d78ebf..6c1c0ff 100644 --- a/source/filters/filter-custom-shader.hpp +++ b/source/filters/filter-custom-shader.hpp @@ -18,55 +18,50 @@ */ #pragma once -#include -#include -#include -#include "gfx/gfx-effect-source.hpp" -#include "obs/gs/gs-effect.hpp" -#include "obs/gs/gs-rendertarget.hpp" + #include "plugin.hpp" +extern "C" { +#include +} + namespace filter { - class CustomShader { - public: - CustomShader(); - ~CustomShader(); + namespace shader { + class shader_factory { + obs_source_info _source_info; - static const char* get_name(void*); - static void get_defaults(obs_data_t*); - static obs_properties_t* get_properties(void*); - - static void* create(obs_data_t*, obs_source_t*); - static void destroy(void*); - static uint32_t get_width(void*); - static uint32_t get_height(void*); - static void update(void*, obs_data_t*); - static void activate(void*); - static void deactivate(void*); - static void video_tick(void*, float); - static void video_render(void*, gs_effect_t*); - - private: - obs_source_info sourceInfo; - - private: - class Instance : public gfx::effect_source { - friend class CustomShader; - - std::shared_ptr m_renderTarget; - - protected: - bool apply_special_parameters(uint32_t viewW, uint32_t viewH); - virtual bool is_special_parameter(std::string name, gs::effect_parameter::type type) override; - virtual bool video_tick_impl(float_t time) override; - virtual bool video_render_impl(gs_effect_t* parent_effect, uint32_t viewW, uint32_t viewH) override; + public: // Singleton + static void initialize(); + static void finalize(); + static std::shared_ptr get(); public: - Instance(obs_data_t*, obs_source_t*); - virtual ~Instance() override; - - uint32_t get_width(); - uint32_t get_height(); + shader_factory(); + ~shader_factory(); }; - }; + + class shader_instance { + obs_source_t* _self; + bool _active; + + uint32_t _width, _height; + + public: + shader_instance(obs_data_t* data, obs_source_t* self); + ~shader_instance(); + + uint32_t width(); + uint32_t height(); + + void properties(obs_properties_t* props); + + void update(obs_data_t* data); + + void activate(); + void deactivate(); + + void video_tick(float_t sec_since_last); + void video_render(gs_effect_t* effect); + }; + } // namespace shader } // namespace filter