1
0
mirror of https://github.com/luanti-org/luanti.git synced 2025-10-21 20:05:45 +02:00

Increase flexibility of ShaderSource

This commit is contained in:
sfan5
2025-08-07 16:04:44 +02:00
parent 1b37eb2e0b
commit 5e23e478b1
6 changed files with 89 additions and 49 deletions

View File

@@ -401,8 +401,10 @@ public:
created_nosky.clear(); 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); auto *scs = new GameGlobalShaderUniformSetter(m_sky, m_client);
if (!m_sky) if (!m_sky)
created_nosky.push_back(scs); created_nosky.push_back(scs);

View File

@@ -101,7 +101,7 @@ public:
} }
}; };
IShaderUniformSetter *FogShaderUniformSetterFactory::create() IShaderUniformSetter *FogShaderUniformSetterFactory::create(const std::string &name)
{ {
return new FogShaderUniformSetter(); return new FogShaderUniformSetter();
} }

View File

@@ -55,7 +55,7 @@ class FogShaderUniformSetterFactory : public IShaderUniformSetterFactory
{ {
public: public:
FogShaderUniformSetterFactory() {}; FogShaderUniformSetterFactory() {};
virtual IShaderUniformSetter *create(); virtual IShaderUniformSetter *create(const std::string &name);
}; };
/* Rendering engine class */ /* Rendering engine class */

View File

@@ -160,28 +160,44 @@ private:
class ShaderCallback : public video::IShaderConstantSetCallBack class ShaderCallback : public video::IShaderConstantSetCallBack
{ {
std::vector<std::unique_ptr<IShaderUniformSetter>> m_setters; std::vector<std::unique_ptr<IShaderUniformSetter>> m_setters;
irr_ptr<IShaderUniformSetterRC> m_extra_setter;
public: public:
template <typename Factories> template <typename Factories>
ShaderCallback(const Factories &factories) ShaderCallback(const std::string &name, const Factories &factories)
{ {
for (auto &&factory : factories) { for (auto &&factory : factories) {
auto *setter = factory->create(); auto *setter = factory->create(name);
if (setter) if (setter) {
// since we use unique_ptr, the object may not be refcounted
assert(dynamic_cast<IReferenceCounted*>(setter) == nullptr);
m_setters.emplace_back(setter); 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 virtual void OnSetConstants(video::IMaterialRendererServices *services, s32 userData) override
{ {
for (auto &&setter : m_setters) for (auto &&setter : m_setters)
setter->onSetUniforms(services); setter->onSetUniforms(services);
if (m_extra_setter)
m_extra_setter->onSetUniforms(services);
} }
virtual void OnSetMaterial(const video::SMaterial& material) override virtual void OnSetMaterial(const video::SMaterial& material) override
{ {
for (auto &&setter : m_setters) for (auto &&setter : m_setters)
setter->onSetMaterial(material); setter->onSetMaterial(material);
if (m_extra_setter)
m_extra_setter->onSetMaterial(material);
} }
}; };
@@ -328,7 +344,7 @@ public:
class MainShaderUniformSetterFactory : public IShaderUniformSetterFactory class MainShaderUniformSetterFactory : public IShaderUniformSetterFactory
{ {
public: public:
virtual IShaderUniformSetter* create() virtual IShaderUniformSetter* create(const std::string &name)
{ return new MainShaderUniformSetter(); } { return new MainShaderUniformSetter(); }
}; };
@@ -350,7 +366,7 @@ public:
The id 0 points to a null shader. Its material is EMT_SOLID. The id 0 points to a null shader. Its material is EMT_SOLID.
*/ */
u32 getShaderIdDirect(const std::string &name, const ShaderConstants &input_const, 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 If shader specified by the name pointed by the id doesn't
@@ -361,7 +377,8 @@ public:
for processing. for processing.
*/ */
u32 getShader(const std::string &name, const ShaderConstants &input_const, 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; const ShaderInfo &getShaderInfo(u32 id) override;
@@ -414,9 +431,8 @@ private:
// Global uniform setter factories // Global uniform setter factories
std::vector<std::unique_ptr<IShaderUniformSetterFactory>> m_uniform_factories; std::vector<std::unique_ptr<IShaderUniformSetterFactory>> m_uniform_factories;
// Generate shader given the shader name. // Generate shader for given input parameters.
ShaderInfo generateShader(const std::string &name, void generateShader(ShaderInfo &info);
const ShaderConstants &input_const, video::E_MATERIAL_TYPE base_mat);
/// @brief outputs a constant to an ostream /// @brief outputs a constant to an ostream
inline void putConstant(std::ostream &os, const ShaderConstants::mapped_type &it) inline void putConstant(std::ostream &os, const ShaderConstants::mapped_type &it)
@@ -465,14 +481,15 @@ ShaderSource::~ShaderSource()
} }
u32 ShaderSource::getShader(const std::string &name, 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 Get shader
*/ */
if (std::this_thread::get_id() == m_main_thread) { 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 " errorstream << "ShaderSource::getShader(): getting from "
@@ -510,7 +527,8 @@ u32 ShaderSource::getShader(const std::string &name,
This method generates all the shaders This method generates all the shaders
*/ */
u32 ShaderSource::getShaderIdDirect(const std::string &name, 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 // Empty name means shader 0
if (name.empty()) { if (name.empty()) {
@@ -522,23 +540,23 @@ u32 ShaderSource::getShaderIdDirect(const std::string &name,
for (u32 i = 0; i < m_shaderinfo_cache.size(); i++) { for (u32 i = 0; i < m_shaderinfo_cache.size(); i++) {
auto &info = m_shaderinfo_cache[i]; auto &info = m_shaderinfo_cache[i];
if (info.name == name && info.base_material == base_mat && 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; return i;
} }
/* // Calling only allowed from main thread
Calling only allowed from main thread sanity_check(std::this_thread::get_id() == m_main_thread);
*/
if (std::this_thread::get_id() != m_main_thread) {
errorstream<<"ShaderSource::getShaderIdDirect() "
"called not from main thread"<<std::endl;
return 0;
}
ShaderInfo info = generateShader(name, input_const, base_mat); ShaderInfo info;
info.name = name;
info.input_constants = input_const;
info.base_material = base_mat;
info.setter_cb.grab(setter_cb);
generateShader(info);
/* /*
Add shader to caches (add dummy shaders too) Add shader to caches
*/ */
MutexAutoLock lock(m_shaderinfo_cache_mutex); MutexAutoLock lock(m_shaderinfo_cache_mutex);
@@ -597,29 +615,28 @@ void ShaderSource::rebuildShaders()
// Recreate shaders // Recreate shaders
for (ShaderInfo &i : m_shaderinfo_cache) { for (ShaderInfo &i : m_shaderinfo_cache) {
ShaderInfo *info = &i; if (!i.name.empty()) {
if (!info->name.empty()) { generateShader(i);
*info = generateShader(info->name, info->input_constants, info->base_material);
} }
} }
} }
ShaderInfo ShaderSource::generateShader(const std::string &name, void ShaderSource::generateShader(ShaderInfo &shaderinfo)
const ShaderConstants &input_const, video::E_MATERIAL_TYPE base_mat)
{ {
ShaderInfo shaderinfo; const auto &name = shaderinfo.name;
shaderinfo.name = name; const auto &input_const = shaderinfo.input_constants;
shaderinfo.input_constants = input_const;
// fixed pipeline materials don't make sense here // fixed pipeline materials don't make sense here
assert(base_mat != video::EMT_TRANSPARENT_VERTEX_ALPHA && base_mat != video::EMT_ONETEXTURE_BLEND); assert(shaderinfo.base_material != video::EMT_TRANSPARENT_VERTEX_ALPHA &&
shaderinfo.base_material = base_mat; shaderinfo.base_material != video::EMT_ONETEXTURE_BLEND);
shaderinfo.material = shaderinfo.base_material;
auto *driver = RenderingEngine::get_video_driver(); auto *driver = RenderingEngine::get_video_driver();
// The null driver doesn't support shaders (duh), but we can pretend it does. // The null driver doesn't support shaders (duh), but we can pretend it does.
if (driver->getDriverType() == video::EDT_NULL) if (driver->getDriverType() == video::EDT_NULL) {
return shaderinfo; shaderinfo.material = shaderinfo.base_material;
return;
}
auto *gpu = driver->getGPUProgrammingServices(); auto *gpu = driver->getGPUProgrammingServices();
if (!driver->queryFeature(video::EVDF_ARB_GLSL) || !gpu) { 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(); geometry_shader_ptr = geometry_shader.c_str();
} }
auto cb = make_irr<ShaderCallback>(m_uniform_factories); auto cb = make_irr<ShaderCallback>(name, m_uniform_factories);
cb->setExtraSetter(shaderinfo.setter_cb.get());
infostream << "Compiling high level shaders for " << log_name << std::endl; infostream << "Compiling high level shaders for " << log_name << std::endl;
s32 shadermat = gpu->addHighLevelShaderMaterial( s32 shadermat = gpu->addHighLevelShaderMaterial(
vertex_shader.c_str(), fragment_shader.c_str(), geometry_shader_ptr, 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 // Apply the newly created material type
shaderinfo.material = (video::E_MATERIAL_TYPE) shadermat; shaderinfo.material = (video::E_MATERIAL_TYPE) shadermat;
return shaderinfo;
} }
/* /*

View File

@@ -7,6 +7,7 @@
#include "irrlichttypes_bloated.h" #include "irrlichttypes_bloated.h"
#include <IMaterialRendererServices.h> #include <IMaterialRendererServices.h>
#include "irr_ptr.h"
#include <string> #include <string>
#include <map> #include <map>
#include <variant> #include <variant>
@@ -51,26 +52,41 @@ public:
Abstraction for updating uniforms used by shaders Abstraction for updating uniforms used by shaders
*/ */
namespace video {
class IMaterialRendererServices;
}
class IShaderUniformSetter { class IShaderUniformSetter {
public: public:
virtual ~IShaderUniformSetter() = default; 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 onSetUniforms(video::IMaterialRendererServices *services) = 0;
virtual void onSetMaterial(const video::SMaterial& material) virtual void onSetMaterial(const video::SMaterial& material)
{ } { }
}; };
class IShaderUniformSetterRC : public IShaderUniformSetter, public IReferenceCounted
{
// Reference counted variant for special use-cases
};
class IShaderUniformSetterFactory { class IShaderUniformSetterFactory {
public: public:
virtual ~IShaderUniformSetterFactory() = default; 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 <typename T, std::size_t count, bool cache> template <typename T, std::size_t count, bool cache>
class CachedShaderSetting { class CachedShaderSetting {
@@ -217,6 +233,8 @@ struct ShaderInfo {
video::E_MATERIAL_TYPE material = video::EMT_SOLID; video::E_MATERIAL_TYPE material = video::EMT_SOLID;
// Input constants // Input constants
ShaderConstants input_constants; ShaderConstants input_constants;
// Extra uniform callback
irr_ptr<IShaderUniformSetterRC> setter_cb;
}; };
class IShaderSource { class IShaderSource {
@@ -239,11 +257,13 @@ public:
* @param name name of the shader (directory on disk) * @param name name of the shader (directory on disk)
* @param input_const primary key constants for this shader * @param input_const primary key constants for this shader
* @param base_mat base material to use * @param base_mat base material to use
* @param setter_cb additional uniform setter to use
* @return shader ID * @return shader ID
* @note `base_material` only controls alpha behavior * @note `base_material` only controls alpha behavior
*/ */
virtual u32 getShader(const std::string &name, 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 /// @brief Helper: Generates or gets a shader suitable for nodes and entities
u32 getShader(const std::string &name, u32 getShader(const std::string &name,

View File

@@ -42,7 +42,7 @@ public:
class ShadowUniformSetterFactory : public IShaderUniformSetterFactory class ShadowUniformSetterFactory : public IShaderUniformSetterFactory
{ {
public: public:
virtual IShaderUniformSetter *create() { virtual IShaderUniformSetter *create(const std::string &name) {
return new ShadowUniformSetter(); return new ShadowUniformSetter();
} }
}; };