From 5e23e478b1f5348215f6c6caf1626523eea56e9e Mon Sep 17 00:00:00 2001 From: sfan5 Date: Thu, 7 Aug 2025 16:04:44 +0200 Subject: [PATCH] Increase flexibility of ShaderSource --- src/client/game.cpp | 4 +- src/client/renderingengine.cpp | 2 +- src/client/renderingengine.h | 2 +- src/client/shader.cpp | 94 ++++++++++++--------- src/client/shader.h | 34 ++++++-- src/client/shadows/shadowsshadercallbacks.h | 2 +- 6 files changed, 89 insertions(+), 49 deletions(-) diff --git a/src/client/game.cpp b/src/client/game.cpp index 0cb89cf0b..2de328152 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -401,8 +401,10 @@ public: created_nosky.clear(); } - virtual IShaderUniformSetter* create() + virtual IShaderUniformSetter* create(const std::string &name) { + if (str_starts_with(name, "shadow/")) + return nullptr; auto *scs = new GameGlobalShaderUniformSetter(m_sky, m_client); if (!m_sky) created_nosky.push_back(scs); diff --git a/src/client/renderingengine.cpp b/src/client/renderingengine.cpp index 766071ee5..f87e58df3 100644 --- a/src/client/renderingengine.cpp +++ b/src/client/renderingengine.cpp @@ -101,7 +101,7 @@ public: } }; -IShaderUniformSetter *FogShaderUniformSetterFactory::create() +IShaderUniformSetter *FogShaderUniformSetterFactory::create(const std::string &name) { return new FogShaderUniformSetter(); } diff --git a/src/client/renderingengine.h b/src/client/renderingengine.h index 54194c701..f71f16e95 100644 --- a/src/client/renderingengine.h +++ b/src/client/renderingengine.h @@ -55,7 +55,7 @@ class FogShaderUniformSetterFactory : public IShaderUniformSetterFactory { public: FogShaderUniformSetterFactory() {}; - virtual IShaderUniformSetter *create(); + virtual IShaderUniformSetter *create(const std::string &name); }; /* Rendering engine class */ diff --git a/src/client/shader.cpp b/src/client/shader.cpp index fa3dfb045..f79f1ae48 100644 --- a/src/client/shader.cpp +++ b/src/client/shader.cpp @@ -160,28 +160,44 @@ private: class ShaderCallback : public video::IShaderConstantSetCallBack { std::vector> m_setters; + irr_ptr m_extra_setter; public: template - ShaderCallback(const Factories &factories) + ShaderCallback(const std::string &name, const Factories &factories) { for (auto &&factory : factories) { - auto *setter = factory->create(); - if (setter) + auto *setter = factory->create(name); + if (setter) { + // since we use unique_ptr, the object may not be refcounted + assert(dynamic_cast(setter) == nullptr); m_setters.emplace_back(setter); + } } } + ~ShaderCallback() = default; + + void setExtraSetter(IShaderUniformSetterRC *setter) + { + assert(!m_extra_setter); + m_extra_setter.grab(setter); + } + virtual void OnSetConstants(video::IMaterialRendererServices *services, s32 userData) override { for (auto &&setter : m_setters) setter->onSetUniforms(services); + if (m_extra_setter) + m_extra_setter->onSetUniforms(services); } virtual void OnSetMaterial(const video::SMaterial& material) override { for (auto &&setter : m_setters) setter->onSetMaterial(material); + if (m_extra_setter) + m_extra_setter->onSetMaterial(material); } }; @@ -328,7 +344,7 @@ public: class MainShaderUniformSetterFactory : public IShaderUniformSetterFactory { public: - virtual IShaderUniformSetter* create() + virtual IShaderUniformSetter* create(const std::string &name) { return new MainShaderUniformSetter(); } }; @@ -350,7 +366,7 @@ public: The id 0 points to a null shader. Its material is EMT_SOLID. */ u32 getShaderIdDirect(const std::string &name, const ShaderConstants &input_const, - video::E_MATERIAL_TYPE base_mat); + video::E_MATERIAL_TYPE base_mat, IShaderUniformSetterRC *setter_cb); /* If shader specified by the name pointed by the id doesn't @@ -361,7 +377,8 @@ public: for processing. */ u32 getShader(const std::string &name, const ShaderConstants &input_const, - video::E_MATERIAL_TYPE base_mat) override; + video::E_MATERIAL_TYPE base_mat, + IShaderUniformSetterRC *setter_cb = nullptr) override; const ShaderInfo &getShaderInfo(u32 id) override; @@ -414,9 +431,8 @@ private: // Global uniform setter factories std::vector> m_uniform_factories; - // Generate shader given the shader name. - ShaderInfo generateShader(const std::string &name, - const ShaderConstants &input_const, video::E_MATERIAL_TYPE base_mat); + // Generate shader for given input parameters. + void generateShader(ShaderInfo &info); /// @brief outputs a constant to an ostream inline void putConstant(std::ostream &os, const ShaderConstants::mapped_type &it) @@ -465,14 +481,15 @@ ShaderSource::~ShaderSource() } u32 ShaderSource::getShader(const std::string &name, - const ShaderConstants &input_const, video::E_MATERIAL_TYPE base_mat) + const ShaderConstants &input_const, video::E_MATERIAL_TYPE base_mat, + IShaderUniformSetterRC *setter_cb) { /* Get shader */ if (std::this_thread::get_id() == m_main_thread) { - return getShaderIdDirect(name, input_const, base_mat); + return getShaderIdDirect(name, input_const, base_mat, setter_cb); } errorstream << "ShaderSource::getShader(): getting from " @@ -510,7 +527,8 @@ u32 ShaderSource::getShader(const std::string &name, This method generates all the shaders */ u32 ShaderSource::getShaderIdDirect(const std::string &name, - const ShaderConstants &input_const, video::E_MATERIAL_TYPE base_mat) + const ShaderConstants &input_const, video::E_MATERIAL_TYPE base_mat, + IShaderUniformSetterRC *setter_cb) { // Empty name means shader 0 if (name.empty()) { @@ -522,23 +540,23 @@ u32 ShaderSource::getShaderIdDirect(const std::string &name, for (u32 i = 0; i < m_shaderinfo_cache.size(); i++) { auto &info = m_shaderinfo_cache[i]; if (info.name == name && info.base_material == base_mat && - info.input_constants == input_const) + info.input_constants == input_const && info.setter_cb == setter_cb) return i; } - /* - Calling only allowed from main thread - */ - if (std::this_thread::get_id() != m_main_thread) { - errorstream<<"ShaderSource::getShaderIdDirect() " - "called not from main thread"<name.empty()) { - *info = generateShader(info->name, info->input_constants, info->base_material); + if (!i.name.empty()) { + generateShader(i); } } } -ShaderInfo ShaderSource::generateShader(const std::string &name, - const ShaderConstants &input_const, video::E_MATERIAL_TYPE base_mat) +void ShaderSource::generateShader(ShaderInfo &shaderinfo) { - ShaderInfo shaderinfo; - shaderinfo.name = name; - shaderinfo.input_constants = input_const; + const auto &name = shaderinfo.name; + const auto &input_const = shaderinfo.input_constants; + // fixed pipeline materials don't make sense here - assert(base_mat != video::EMT_TRANSPARENT_VERTEX_ALPHA && base_mat != video::EMT_ONETEXTURE_BLEND); - shaderinfo.base_material = base_mat; - shaderinfo.material = shaderinfo.base_material; + assert(shaderinfo.base_material != video::EMT_TRANSPARENT_VERTEX_ALPHA && + shaderinfo.base_material != video::EMT_ONETEXTURE_BLEND); auto *driver = RenderingEngine::get_video_driver(); // The null driver doesn't support shaders (duh), but we can pretend it does. - if (driver->getDriverType() == video::EDT_NULL) - return shaderinfo; + if (driver->getDriverType() == video::EDT_NULL) { + shaderinfo.material = shaderinfo.base_material; + return; + } auto *gpu = driver->getGPUProgrammingServices(); if (!driver->queryFeature(video::EVDF_ARB_GLSL) || !gpu) { @@ -746,7 +763,9 @@ ShaderInfo ShaderSource::generateShader(const std::string &name, geometry_shader_ptr = geometry_shader.c_str(); } - auto cb = make_irr(m_uniform_factories); + auto cb = make_irr(name, m_uniform_factories); + cb->setExtraSetter(shaderinfo.setter_cb.get()); + infostream << "Compiling high level shaders for " << log_name << std::endl; s32 shadermat = gpu->addHighLevelShaderMaterial( vertex_shader.c_str(), fragment_shader.c_str(), geometry_shader_ptr, @@ -765,7 +784,6 @@ ShaderInfo ShaderSource::generateShader(const std::string &name, // Apply the newly created material type shaderinfo.material = (video::E_MATERIAL_TYPE) shadermat; - return shaderinfo; } /* diff --git a/src/client/shader.h b/src/client/shader.h index 30d1c0ca1..2e5a7ff63 100644 --- a/src/client/shader.h +++ b/src/client/shader.h @@ -7,6 +7,7 @@ #include "irrlichttypes_bloated.h" #include +#include "irr_ptr.h" #include #include #include @@ -51,26 +52,41 @@ public: Abstraction for updating uniforms used by shaders */ -namespace video { - class IMaterialRendererServices; -} - - class IShaderUniformSetter { public: virtual ~IShaderUniformSetter() = default; + /** + * Called when uniforms need to be updated + * @param services interface for setting uniforms + */ virtual void onSetUniforms(video::IMaterialRendererServices *services) = 0; virtual void onSetMaterial(const video::SMaterial& material) { } }; +class IShaderUniformSetterRC : public IShaderUniformSetter, public IReferenceCounted +{ + // Reference counted variant for special use-cases +}; + class IShaderUniformSetterFactory { public: virtual ~IShaderUniformSetterFactory() = default; - virtual IShaderUniformSetter* create() = 0; + /** + * Called to create an uniform setter for a specific shader + * @param name name of the shader + * @return new uniform setter (or nullptr). caller takes ownership. + */ + virtual IShaderUniformSetter *create(const std::string &name) = 0; }; +/* + Helpers to set uniforms only when changed. + + Be warned that when using this you can't attach a IShaderUniformSetter to + multiple different shaders. But you probably don't want to anyway. +*/ template class CachedShaderSetting { @@ -217,6 +233,8 @@ struct ShaderInfo { video::E_MATERIAL_TYPE material = video::EMT_SOLID; // Input constants ShaderConstants input_constants; + // Extra uniform callback + irr_ptr setter_cb; }; class IShaderSource { @@ -239,11 +257,13 @@ public: * @param name name of the shader (directory on disk) * @param input_const primary key constants for this shader * @param base_mat base material to use + * @param setter_cb additional uniform setter to use * @return shader ID * @note `base_material` only controls alpha behavior */ virtual u32 getShader(const std::string &name, - const ShaderConstants &input_const, video::E_MATERIAL_TYPE base_mat) = 0; + const ShaderConstants &input_const, video::E_MATERIAL_TYPE base_mat, + IShaderUniformSetterRC *setter_cb = nullptr) = 0; /// @brief Helper: Generates or gets a shader suitable for nodes and entities u32 getShader(const std::string &name, diff --git a/src/client/shadows/shadowsshadercallbacks.h b/src/client/shadows/shadowsshadercallbacks.h index cd1ff3916..6686f92c7 100644 --- a/src/client/shadows/shadowsshadercallbacks.h +++ b/src/client/shadows/shadowsshadercallbacks.h @@ -42,7 +42,7 @@ public: class ShadowUniformSetterFactory : public IShaderUniformSetterFactory { public: - virtual IShaderUniformSetter *create() { + virtual IShaderUniformSetter *create(const std::string &name) { return new ShadowUniformSetter(); } };