From 29bbe22bec65a8dc0aa2709121cbeec3a09b3701 Mon Sep 17 00:00:00 2001 From: Michael Fabian 'Xaymar' Dirks Date: Sun, 7 Nov 2021 14:37:05 +0100 Subject: [PATCH] obs/gs/effect: Preprocess shaders to improve platform compatibility Improves cross-platform compatibility of Shaders written for StreamFX through the use of preprocessing to make things a bit more compatible. While we don't perform any proper parsing, this will be able to prevent basic issues. --- source/filters/filter-blur.cpp | 6 +- source/filters/filter-color-grade.cpp | 53 ++++++++------- source/filters/filter-displacement.cpp | 14 +++- source/filters/filter-dynamic-mask.cpp | 20 ++++-- source/filters/filter-sdf-effects.cpp | 10 +-- source/filters/filter-transform.cpp | 70 ++++++++++--------- source/gfx/blur/gfx-blur-box-linear.cpp | 12 ++-- source/gfx/blur/gfx-blur-box.cpp | 11 +-- source/gfx/blur/gfx-blur-dual-filtering.cpp | 12 ++-- source/gfx/blur/gfx-blur-gaussian-linear.cpp | 15 ++++- source/gfx/blur/gfx-blur-gaussian.cpp | 11 ++- source/obs/gs/gs-effect.cpp | 71 +++++++++++++++++--- source/obs/gs/gs-effect.hpp | 15 +++-- source/obs/gs/gs-mipmapper.cpp | 11 ++- 14 files changed, 223 insertions(+), 108 deletions(-) diff --git a/source/filters/filter-blur.cpp b/source/filters/filter-blur.cpp index 61621a0b..253ae683 100644 --- a/source/filters/filter-blur.cpp +++ b/source/filters/filter-blur.cpp @@ -153,11 +153,11 @@ blur_instance::blur_instance(obs_data_t* settings, obs_source_t* self) // Load Effects { - auto file = streamfx::data_file_path("effects/mask.effect").string(); + auto file = streamfx::data_file_path("effects/mask.effect"); try { _effect_mask = streamfx::obs::gs::effect::create(file); - } catch (std::runtime_error& ex) { - DLOG_ERROR(" Loading effect '%s' failed with error(s): %s", file.c_str(), ex.what()); + } catch (std::exception& ex) { + DLOG_ERROR("Error loading '%s': %s", file.generic_u8string().c_str(), ex.what()); } } } diff --git a/source/filters/filter-color-grade.cpp b/source/filters/filter-color-grade.cpp index a81667af..d11093b3 100644 --- a/source/filters/filter-color-grade.cpp +++ b/source/filters/filter-color-grade.cpp @@ -137,38 +137,39 @@ color_grade_instance::color_grade_instance(obs_data_t* data, obs_source_t* self) _lut_initialized(false), _lut_dirty(true), _lut_producer(), _lut_consumer() { - // Load the color grading effect. - auto path = streamfx::data_file_path("effects/color-grade.effect"); - if (!std::filesystem::exists(path)) { - D_LOG_ERROR("Failed to locate effect file '%s'.", path.u8string().c_str()); - throw std::runtime_error("Failed to load color grade effect."); - } else { + { + auto gctx = streamfx::obs::gs::context(); + + // Load the color grading effect. + { + auto file = streamfx::data_file_path("effects/color-grade.effect"); + try { + _effect = streamfx::obs::gs::effect::create(file); + } catch (std::exception& ex) { + D_LOG_ERROR("Error loading '%s': %s", file.u8string().c_str(), ex.what()); + throw; + } + } + + // Initialize LUT work flow. try { - _effect = streamfx::obs::gs::effect::create(path.u8string()); + _lut_producer = std::make_shared(); + _lut_consumer = std::make_shared(); + _lut_initialized = true; } catch (std::exception const& ex) { - D_LOG_ERROR("Failed to load effect '%s': %s", path.u8string().c_str(), ex.what()); + D_LOG_WARNING("Failed to initialize LUT rendering, falling back to direct rendering.\n%s", ex.what()); + _lut_initialized = false; + } + + // Allocate render target for rendering. + try { + allocate_rendertarget(GS_RGBA); + } catch (std::exception const& ex) { + D_LOG_ERROR("Failed to acquire render target for rendering: %s", ex.what()); throw; } } - // Initialize LUT work flow. - try { - _lut_producer = std::make_shared(); - _lut_consumer = std::make_shared(); - _lut_initialized = true; - } catch (std::exception const& ex) { - D_LOG_WARNING("Failed to initialize LUT rendering, falling back to direct rendering.\n%s", ex.what()); - _lut_initialized = false; - } - - // Allocate render target for rendering. - try { - allocate_rendertarget(GS_RGBA); - } catch (std::exception const& ex) { - D_LOG_ERROR("Failed to acquire render target for rendering: %s", ex.what()); - throw; - } - update(data); } diff --git a/source/filters/filter-displacement.cpp b/source/filters/filter-displacement.cpp index 4af803cb..063130e0 100644 --- a/source/filters/filter-displacement.cpp +++ b/source/filters/filter-displacement.cpp @@ -51,7 +51,19 @@ using namespace streamfx::filter::displacement; displacement_instance::displacement_instance(obs_data_t* data, obs_source_t* context) : obs::source_instance(data, context) { - _effect = streamfx::obs::gs::effect::create(streamfx::data_file_path("effects/displace.effect").u8string()); + { + auto gctx = streamfx::obs::gs::context(); + + { + auto file = streamfx::data_file_path("effects/displace.effect"); + try { + _effect = streamfx::obs::gs::effect::create(file); + } catch (std::exception& ex) { + D_LOG_ERROR("Error loading '%s': %s", file.u8string().c_str(), ex.what()); + throw; + } + } + } update(data); } diff --git a/source/filters/filter-dynamic-mask.cpp b/source/filters/filter-dynamic-mask.cpp index f65118ad..fbc0956b 100644 --- a/source/filters/filter-dynamic-mask.cpp +++ b/source/filters/filter-dynamic-mask.cpp @@ -77,13 +77,21 @@ dynamic_mask_instance::dynamic_mask_instance(obs_data_t* settings, obs_source_t* _filter_texture(), _have_input_texture(false), _input(), _input_capture(), _input_texture(), _have_final_texture(false), _final_rt(), _final_texture(), _channels(), _precalc() { - _filter_rt = std::make_shared(GS_RGBA, GS_ZS_NONE); - _final_rt = std::make_shared(GS_RGBA, GS_ZS_NONE); + { + auto gctx = streamfx::obs::gs::context(); - try { - _effect = streamfx::obs::gs::effect::create(streamfx::data_file_path("effects/channel-mask.effect").u8string()); - } catch (const std::exception& ex) { - DLOG_ERROR("Loading channel mask effect failed with error(s):\n%s", ex.what()); + _filter_rt = std::make_shared(GS_RGBA, GS_ZS_NONE); + _final_rt = std::make_shared(GS_RGBA, GS_ZS_NONE); + + { + auto file = streamfx::data_file_path("effects/channel-mask.effect"); + try { + _effect = streamfx::obs::gs::effect::create(file); + } catch (std::exception& ex) { + D_LOG_ERROR("Error loading '%s': %s", file.u8string().c_str(), ex.what()); + throw; + } + } } update(settings); diff --git a/source/filters/filter-sdf-effects.cpp b/source/filters/filter-sdf-effects.cpp index 10fc19f1..8bfe1467 100644 --- a/source/filters/filter-sdf-effects.cpp +++ b/source/filters/filter-sdf-effects.cpp @@ -145,12 +145,12 @@ sdf_effects_instance::sdf_effects_instance(obs_data_t* settings, obs_source_t* s {"effects/sdf/sdf-consumer.effect", _sdf_consumer_effect}, }; for (auto& kv : load_arr) { - auto path = streamfx::data_file_path(kv.first).u8string(); + auto file = streamfx::data_file_path(kv.first); try { - kv.second = streamfx::obs::gs::effect::create(path); - } catch (const std::exception& ex) { - D_LOG_ERROR("Failed to load effect '%s' (located at '%s') with error(s): %s", kv.first, path.c_str(), - ex.what()); + kv.second = streamfx::obs::gs::effect::create(file); + } catch (std::exception& ex) { + D_LOG_ERROR("Error loading '%s': %s", file.u8string().c_str(), ex.what()); + throw; } } } diff --git a/source/filters/filter-transform.cpp b/source/filters/filter-transform.cpp index 8ffd0ae9..e413fb78 100644 --- a/source/filters/filter-transform.cpp +++ b/source/filters/filter-transform.cpp @@ -114,42 +114,46 @@ transform_instance::transform_instance(obs_data_t* data, obs_source_t* context) _sampler(), _params(), _corners(), _cache_rendered(), _mipmap_enabled(), _source_rendered(), _source_size(), _update_mesh(true) { - _cache_rt = std::make_shared(GS_RGBA, GS_ZS_NONE); - _source_rt = std::make_shared(GS_RGBA, GS_ZS_NONE); - _vertex_buffer = std::make_shared(uint32_t(4u), uint8_t(1u)); { - auto file = streamfx::data_file_path("effects/standard.effect"); - try { - _standard_effect = streamfx::obs::gs::effect::create(file.generic_u8string()); - } catch (const std::exception& ex) { - DLOG_ERROR("Error loading '%s' from disk: %s", file.generic_u8string().c_str(), ex.what()); - } - } - { - auto file = streamfx::data_file_path("effects/transform.effect"); - try { - _transform_effect = streamfx::obs::gs::effect::create(file.generic_u8string()); - } catch (const std::exception& ex) { - DLOG_ERROR("Error loading '%s' from disk: %s", file.generic_u8string().c_str(), ex.what()); - } - } - { - _sampler.set_address_mode_u(GS_ADDRESS_CLAMP); - _sampler.set_address_mode_v(GS_ADDRESS_CLAMP); - _sampler.set_address_mode_w(GS_ADDRESS_CLAMP); - _sampler.set_filter(GS_FILTER_LINEAR); - _sampler.set_max_anisotropy(8); - } + auto gctx = obs::gs::context(); - vec3_set(&_params.position, 0, 0, 0); - vec3_set(&_params.rotation, 0, 0, 0); - vec3_set(&_params.scale, 1, 1, 1); - vec3_set(&_params.shear, 0, 0, 0); + _cache_rt = std::make_shared(GS_RGBA, GS_ZS_NONE); + _source_rt = std::make_shared(GS_RGBA, GS_ZS_NONE); + _vertex_buffer = std::make_shared(uint32_t(4u), uint8_t(1u)); + { + auto file = streamfx::data_file_path("effects/standard.effect"); + try { + _standard_effect = streamfx::obs::gs::effect::create(file); + } catch (const std::exception& ex) { + DLOG_ERROR("Error loading '%s': %s", file.generic_u8string().c_str(), ex.what()); + } + } + { + auto file = streamfx::data_file_path("effects/transform.effect"); + try { + _transform_effect = streamfx::obs::gs::effect::create(file); + } catch (const std::exception& ex) { + DLOG_ERROR("Error loading '%s': %s", file.generic_u8string().c_str(), ex.what()); + } + } + { + _sampler.set_address_mode_u(GS_ADDRESS_CLAMP); + _sampler.set_address_mode_v(GS_ADDRESS_CLAMP); + _sampler.set_address_mode_w(GS_ADDRESS_CLAMP); + _sampler.set_filter(GS_FILTER_LINEAR); + _sampler.set_max_anisotropy(8); + } - vec2_set(&_corners.tl, 0, 0); - vec2_set(&_corners.tr, 1, 0); - vec2_set(&_corners.bl, 0, 1); - vec2_set(&_corners.br, 1, 1); + vec3_set(&_params.position, 0, 0, 0); + vec3_set(&_params.rotation, 0, 0, 0); + vec3_set(&_params.scale, 1, 1, 1); + vec3_set(&_params.shear, 0, 0, 0); + + vec2_set(&_corners.tl, 0, 0); + vec2_set(&_corners.tr, 1, 0); + vec2_set(&_corners.bl, 0, 1); + vec2_set(&_corners.br, 1, 1); + } update(data); } diff --git a/source/gfx/blur/gfx-blur-box-linear.cpp b/source/gfx/blur/gfx-blur-box-linear.cpp index e19047a8..c6ef7d92 100644 --- a/source/gfx/blur/gfx-blur-box-linear.cpp +++ b/source/gfx/blur/gfx-blur-box-linear.cpp @@ -37,11 +37,13 @@ streamfx::gfx::blur::box_linear_data::box_linear_data() { auto gctx = streamfx::obs::gs::context(); - try { - _effect = - streamfx::obs::gs::effect::create(streamfx::data_file_path("effects/blur/box-linear.effect").u8string()); - } catch (...) { - DLOG_ERROR(" Failed to load _effect."); + { + auto file = streamfx::data_file_path("effects/blur/box-linear.effect"); + try { + _effect = streamfx::obs::gs::effect::create(file); + } catch (const std::exception& ex) { + DLOG_ERROR("Error loading '%s': %s", file.generic_u8string().c_str(), ex.what()); + } } } diff --git a/source/gfx/blur/gfx-blur-box.cpp b/source/gfx/blur/gfx-blur-box.cpp index 979267d9..40dd554e 100644 --- a/source/gfx/blur/gfx-blur-box.cpp +++ b/source/gfx/blur/gfx-blur-box.cpp @@ -37,10 +37,13 @@ streamfx::gfx::blur::box_data::box_data() { auto gctx = streamfx::obs::gs::context(); - try { - _effect = streamfx::obs::gs::effect::create(streamfx::data_file_path("effects/blur/box.effect").u8string()); - } catch (...) { - DLOG_ERROR(" Failed to load _effect."); + { + auto file = streamfx::data_file_path("effects/blur/box.effect"); + try { + _effect = streamfx::obs::gs::effect::create(file); + } catch (const std::exception& ex) { + DLOG_ERROR("Error loading '%s': %s", file.generic_u8string().c_str(), ex.what()); + } } } diff --git a/source/gfx/blur/gfx-blur-dual-filtering.cpp b/source/gfx/blur/gfx-blur-dual-filtering.cpp index fdfe4f47..d56d26ca 100644 --- a/source/gfx/blur/gfx-blur-dual-filtering.cpp +++ b/source/gfx/blur/gfx-blur-dual-filtering.cpp @@ -54,11 +54,13 @@ streamfx::gfx::blur::dual_filtering_data::dual_filtering_data() { auto gctx = streamfx::obs::gs::context(); - try { - _effect = streamfx::obs::gs::effect::create( - streamfx::data_file_path("effects/blur/dual-filtering.effect").u8string()); - } catch (...) { - DLOG_ERROR(" Failed to load _effect."); + { + auto file = streamfx::data_file_path("effects/blur/dual-filtering.effect"); + try { + _effect = streamfx::obs::gs::effect::create(file); + } catch (const std::exception& ex) { + DLOG_ERROR("Error loading '%s': %s", file.generic_u8string().c_str(), ex.what()); + } } } diff --git a/source/gfx/blur/gfx-blur-gaussian-linear.cpp b/source/gfx/blur/gfx-blur-gaussian-linear.cpp index b1ebc85f..fe266cfd 100644 --- a/source/gfx/blur/gfx-blur-gaussian-linear.cpp +++ b/source/gfx/blur/gfx-blur-gaussian-linear.cpp @@ -42,9 +42,18 @@ streamfx::gfx::blur::gaussian_linear_data::gaussian_linear_data() { - auto gctx = streamfx::obs::gs::context(); - _effect = - streamfx::obs::gs::effect::create(streamfx::data_file_path("effects/blur/gaussian-linear.effect").u8string()); + { + auto gctx = streamfx::obs::gs::context(); + + { + auto file = streamfx::data_file_path("effects/blur/gaussian-linear.effect"); + try { + _effect = streamfx::obs::gs::effect::create(file); + } catch (const std::exception& ex) { + DLOG_ERROR("Error loading '%s': %s", file.generic_u8string().c_str(), ex.what()); + } + } + } // Precalculate Kernels for (std::size_t kernel_size = 1; kernel_size <= ST_MAX_BLUR_SIZE; kernel_size++) { diff --git a/source/gfx/blur/gfx-blur-gaussian.cpp b/source/gfx/blur/gfx-blur-gaussian.cpp index bc7bdb0f..e705c4f1 100644 --- a/source/gfx/blur/gfx-blur-gaussian.cpp +++ b/source/gfx/blur/gfx-blur-gaussian.cpp @@ -46,8 +46,15 @@ streamfx::gfx::blur::gaussian_data::gaussian_data() { auto gctx = streamfx::obs::gs::context(); - _effect = - streamfx::obs::gs::effect::create(streamfx::data_file_path("effects/blur/gaussian.effect").u8string()); + + { + auto file = streamfx::data_file_path("effects/blur/gaussian.effect"); + try { + _effect = streamfx::obs::gs::effect::create(file); + } catch (const std::exception& ex) { + DLOG_ERROR("Error loading '%s': %s", file.generic_u8string().c_str(), ex.what()); + } + } } //#define ST_USE_PASCAL_TRIANGLE diff --git a/source/obs/gs/gs-effect.cpp b/source/obs/gs/gs-effect.cpp index ee8f7b6a..a47e8ccc 100644 --- a/source/obs/gs/gs-effect.cpp +++ b/source/obs/gs/gs-effect.cpp @@ -19,28 +19,78 @@ #include "gs-effect.hpp" #include +#include #include #include #include "obs/gs/gs-helper.hpp" +#include "util/util-platform.hpp" -#define MAX_EFFECT_SIZE 32 * 1024 * 1024 +#define MAX_EFFECT_SIZE 32 * 1024 * 1024 // 32 MiB, big enough for everything. -static std::string load_file_as_code(std::filesystem::path file) +static std::string load_file_as_code(std::filesystem::path shader_file) { - uintmax_t size = std::filesystem::file_size(file); + std::stringstream shader_stream; + std::filesystem::path shader_path = std::filesystem::absolute(shader_file); + std::filesystem::path shader_root = shader_path.parent_path(); + + // Ensure it meets size limits. + uintmax_t size = std::filesystem::file_size(shader_path); if (size > MAX_EFFECT_SIZE) { throw std::runtime_error("File is too large to be loaded."); } - std::ifstream ifs(file, std::ios::binary); + // Try to open as-is. + std::ifstream ifs(shader_path, std::ios::in); if (!ifs.is_open() || ifs.bad()) { - throw std::runtime_error("An unknown error occured trying to open the file."); + throw std::runtime_error("Failed to open file."); } - std::vector buf(size_t(size + 1), 0); - ifs.read(buf.data(), static_cast(size)); + // Push Graphics API to shader. + switch (gs_get_device_type()) { + case GS_DEVICE_DIRECT3D_11: + shader_stream << "#define GS_DEVICE_DIRECT3D_11" << std::endl; + shader_stream << "#define GS_DEVICE_DIRECT3D" << std::endl; + break; + case GS_DEVICE_OPENGL: + shader_stream << "#define GS_DEVICE_OPENGL" << std::endl; + break; + } - return std::string(buf.data(), buf.data() + size); + // Pre-process the shader. + std::string line; + while (std::getline(ifs, line)) { + std::string line_trimmed = line; + + { // Figure out the length of the trim. + size_t trim_length = 0; + for (size_t idx = 0, edx = line_trimmed.length(); idx < edx; idx++) { + char ch = line_trimmed.at(idx); + if ((ch != ' ') && (ch != '\t')) { + trim_length = idx; + break; + } + } + if (trim_length > 0) { + line_trimmed.erase(0, trim_length); + } + } + + // Handle '#include' + if (line_trimmed.substr(0, 8) == "#include") { + std::string include_str = line_trimmed.substr(10, line_trimmed.size() - 11); // '#include "' + std::filesystem::path include_path = include_str; + + if (!include_path.is_absolute()) { + include_path = shader_root / include_str; + } + + line = load_file_as_code(include_path); + } + + shader_stream << line << std::endl; + } + + return shader_stream.str(); } streamfx::obs::gs::effect::effect(const std::string& code, const std::string& name) @@ -58,7 +108,10 @@ streamfx::obs::gs::effect::effect(const std::string& code, const std::string& na reset(effect, [](gs_effect_t* ptr) { gs_effect_destroy(ptr); }); } -streamfx::obs::gs::effect::effect(std::filesystem::path file) : effect(load_file_as_code(file), file.u8string()) {} +streamfx::obs::gs::effect::effect(std::filesystem::path file) + : effect(load_file_as_code(file), + streamfx::util::platform::utf8_to_native(std::filesystem::absolute(file)).generic_u8string()) +{} streamfx::obs::gs::effect::~effect() { diff --git a/source/obs/gs/gs-effect.hpp b/source/obs/gs/gs-effect.hpp index a5ca5dde..6b00e52e 100644 --- a/source/obs/gs/gs-effect.hpp +++ b/source/obs/gs/gs-effect.hpp @@ -49,14 +49,19 @@ namespace streamfx::obs::gs { return get(); } - static streamfx::obs::gs::effect create(const std::string& file) - { - return streamfx::obs::gs::effect(file); - }; - static streamfx::obs::gs::effect create(const std::string& code, const std::string& name) { return streamfx::obs::gs::effect(code, name); }; + + static streamfx::obs::gs::effect create(const std::string& file) + { + return streamfx::obs::gs::effect(std::filesystem::path(file)); + }; + + static streamfx::obs::gs::effect create(const std::filesystem::path& file) + { + return streamfx::obs::gs::effect(file); + }; }; } // namespace streamfx::obs::gs diff --git a/source/obs/gs/gs-mipmapper.cpp b/source/obs/gs/gs-mipmapper.cpp index 6a654c64..8ac59f87 100644 --- a/source/obs/gs/gs-mipmapper.cpp +++ b/source/obs/gs/gs-mipmapper.cpp @@ -45,6 +45,8 @@ streamfx::obs::gs::mipmapper::~mipmapper() streamfx::obs::gs::mipmapper::mipmapper() { + auto gctx = streamfx::obs::gs::context(); + _vb = std::make_unique(uint32_t(3u), uint8_t(1u)); { @@ -71,7 +73,14 @@ streamfx::obs::gs::mipmapper::mipmapper() _vb->update(); - _effect = streamfx::obs::gs::effect::create(streamfx::data_file_path("effects/mipgen.effect").u8string()); + { + auto file = streamfx::data_file_path("effects/mipgen.effect"); + try { + _effect = streamfx::obs::gs::effect::create(file); + } catch (const std::exception& ex) { + DLOG_ERROR("Error loading '%s': %s", file.generic_u8string().c_str(), ex.what()); + } + } } void streamfx::obs::gs::mipmapper::rebuild(std::shared_ptr source,