1
0
Fork 0
mirror of https://github.com/luanti-org/luanti.git synced 2025-06-27 16:36:03 +00:00

Change shaders to be defined by input constants rather than drawtype/material type

This commit is contained in:
sfan5 2025-04-17 23:48:08 +02:00
parent baa4c7cd21
commit f3c2bbfb48
2 changed files with 127 additions and 89 deletions

View file

@ -410,14 +410,13 @@ public:
~ShaderSource() override; ~ShaderSource() override;
/* /*
- If shader material specified by name is found from cache, - If shader material is found from cache, return the cached id.
return the cached id.
- Otherwise generate the shader material, add to cache and return id. - Otherwise generate the shader material, add to cache and return id.
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, u32 getShaderIdDirect(const std::string &name, const ShaderConstants &input_const,
MaterialType material_type, NodeDrawType drawtype); video::E_MATERIAL_TYPE base_mat);
/* /*
If shader specified by the name pointed by the id doesn't If shader specified by the name pointed by the id doesn't
@ -427,19 +426,10 @@ public:
and not found in cache, the call is queued to the main thread and not found in cache, the call is queued to the main thread
for processing. for processing.
*/ */
u32 getShader(const std::string &name, u32 getShader(const std::string &name, const ShaderConstants &input_const,
MaterialType material_type, NodeDrawType drawtype) override; video::E_MATERIAL_TYPE base_mat) override;
u32 getShaderRaw(const std::string &name, bool blendAlpha) override const ShaderInfo &getShaderInfo(u32 id) override;
{
// TODO: the shader system should be refactored to be much more generic.
// Just let callers pass arbitrary constants, this would also deal with
// runtime changes cleanly.
return getShader(name, blendAlpha ? TILE_MATERIAL_ALPHA : TILE_MATERIAL_BASIC,
NodeDrawType_END);
}
ShaderInfo getShaderInfo(u32 id) override;
// Processes queued shader requests from other threads. // Processes queued shader requests from other threads.
// Shall be called from the main thread. // Shall be called from the main thread.
@ -492,7 +482,16 @@ private:
// Generate shader given the shader name. // Generate shader given the shader name.
ShaderInfo generateShader(const std::string &name, ShaderInfo generateShader(const std::string &name,
MaterialType material_type, NodeDrawType drawtype); const ShaderConstants &input_const, video::E_MATERIAL_TYPE base_mat);
/// @brief outputs a constant to an ostream
inline void putConstant(std::ostream &os, const ShaderConstants::mapped_type &it)
{
if (auto *ival = std::get_if<int>(&it); ival)
os << *ival;
else
os << std::get<float>(it);
}
}; };
IWritableShaderSource *createShaderSource() IWritableShaderSource *createShaderSource()
@ -520,22 +519,27 @@ ShaderSource::~ShaderSource()
// Delete materials // Delete materials
auto *gpu = RenderingEngine::get_video_driver()->getGPUProgrammingServices(); auto *gpu = RenderingEngine::get_video_driver()->getGPUProgrammingServices();
assert(gpu); assert(gpu);
u32 n = 0;
for (ShaderInfo &i : m_shaderinfo_cache) { for (ShaderInfo &i : m_shaderinfo_cache) {
if (!i.name.empty()) if (!i.name.empty()) {
gpu->deleteShaderMaterial(i.material); gpu->deleteShaderMaterial(i.material);
n++;
}
} }
m_shaderinfo_cache.clear(); m_shaderinfo_cache.clear();
infostream << "~ShaderSource() cleaned up " << n << " materials" << std::endl;
} }
u32 ShaderSource::getShader(const std::string &name, u32 ShaderSource::getShader(const std::string &name,
MaterialType material_type, NodeDrawType drawtype) const ShaderConstants &input_const, video::E_MATERIAL_TYPE base_mat)
{ {
/* /*
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, material_type, drawtype); return getShaderIdDirect(name, input_const, base_mat);
} }
errorstream << "ShaderSource::getShader(): getting from " errorstream << "ShaderSource::getShader(): getting from "
@ -573,7 +577,7 @@ 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,
MaterialType material_type, NodeDrawType drawtype) const ShaderConstants &input_const, video::E_MATERIAL_TYPE base_mat)
{ {
// Empty name means shader 0 // Empty name means shader 0
if (name.empty()) { if (name.empty()) {
@ -582,10 +586,10 @@ u32 ShaderSource::getShaderIdDirect(const std::string &name,
} }
// Check if already have such instance // Check if already have such instance
for(u32 i=0; i<m_shaderinfo_cache.size(); i++){ for (u32 i = 0; i < m_shaderinfo_cache.size(); i++) {
ShaderInfo *info = &m_shaderinfo_cache[i]; auto &info = m_shaderinfo_cache[i];
if(info->name == name && info->material_type == material_type && if (info.name == name && info.base_material == base_mat &&
info->drawtype == drawtype) info.input_constants == input_const)
return i; return i;
} }
@ -598,7 +602,7 @@ u32 ShaderSource::getShaderIdDirect(const std::string &name,
return 0; return 0;
} }
ShaderInfo info = generateShader(name, material_type, drawtype); ShaderInfo info = generateShader(name, input_const, base_mat);
/* /*
Add shader to caches (add dummy shaders too) Add shader to caches (add dummy shaders too)
@ -607,19 +611,19 @@ u32 ShaderSource::getShaderIdDirect(const std::string &name,
MutexAutoLock lock(m_shaderinfo_cache_mutex); MutexAutoLock lock(m_shaderinfo_cache_mutex);
u32 id = m_shaderinfo_cache.size(); u32 id = m_shaderinfo_cache.size();
m_shaderinfo_cache.push_back(info); m_shaderinfo_cache.push_back(std::move(info));
return id; return id;
} }
ShaderInfo ShaderSource::getShaderInfo(u32 id) const ShaderInfo &ShaderSource::getShaderInfo(u32 id)
{ {
MutexAutoLock lock(m_shaderinfo_cache_mutex); MutexAutoLock lock(m_shaderinfo_cache_mutex);
if(id >= m_shaderinfo_cache.size()) if (id >= m_shaderinfo_cache.size()) {
return ShaderInfo(); static ShaderInfo empty;
return empty;
}
return m_shaderinfo_cache[id]; return m_shaderinfo_cache[id];
} }
@ -655,46 +659,31 @@ void ShaderSource::rebuildShaders()
} }
} }
infostream << "ShaderSource: recreating " << m_shaderinfo_cache.size()
<< " shaders" << std::endl;
// Recreate shaders // Recreate shaders
for (ShaderInfo &i : m_shaderinfo_cache) { for (ShaderInfo &i : m_shaderinfo_cache) {
ShaderInfo *info = &i; ShaderInfo *info = &i;
if (!info->name.empty()) { if (!info->name.empty()) {
*info = generateShader(info->name, info->material_type, info->drawtype); *info = generateShader(info->name, info->input_constants, info->base_material);
} }
} }
} }
ShaderInfo ShaderSource::generateShader(const std::string &name, ShaderInfo ShaderSource::generateShader(const std::string &name,
MaterialType material_type, NodeDrawType drawtype) const ShaderConstants &input_const, video::E_MATERIAL_TYPE base_mat)
{ {
ShaderInfo shaderinfo; ShaderInfo shaderinfo;
shaderinfo.name = name; shaderinfo.name = name;
shaderinfo.material_type = material_type; shaderinfo.input_constants = input_const;
shaderinfo.drawtype = drawtype; // fixed pipeline materials don't make sense here
switch (material_type) { assert(base_mat != video::EMT_TRANSPARENT_VERTEX_ALPHA && base_mat != video::EMT_ONETEXTURE_BLEND);
case TILE_MATERIAL_OPAQUE: shaderinfo.base_material = base_mat;
case TILE_MATERIAL_LIQUID_OPAQUE:
case TILE_MATERIAL_WAVING_LIQUID_OPAQUE:
shaderinfo.base_material = video::EMT_SOLID;
break;
case TILE_MATERIAL_ALPHA:
case TILE_MATERIAL_PLAIN_ALPHA:
case TILE_MATERIAL_LIQUID_TRANSPARENT:
case TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT:
shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
break;
case TILE_MATERIAL_BASIC:
case TILE_MATERIAL_PLAIN:
case TILE_MATERIAL_WAVING_LEAVES:
case TILE_MATERIAL_WAVING_PLANTS:
case TILE_MATERIAL_WAVING_LIQUID_BASIC:
shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
break;
}
shaderinfo.material = shaderinfo.base_material; shaderinfo.material = shaderinfo.base_material;
video::IVideoDriver *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; return shaderinfo;
@ -770,18 +759,18 @@ ShaderInfo ShaderSource::generateShader(const std::string &name,
/// Unique name of this shader, for debug/logging /// Unique name of this shader, for debug/logging
std::string log_name = name; std::string log_name = name;
for (auto &it : input_const) {
ShaderConstants constants; if (log_name.size() > 60) { // it shouldn't be too long
log_name.append("...");
// Temporary plumbing <-> NodeShaderConstantSetter break;
if (drawtype != NodeDrawType_END) { }
constants["DRAWTYPE"] = (int)drawtype; std::ostringstream oss;
constants["MATERIAL_TYPE"] = (int)material_type; putConstant(oss, it.second);
log_name.append(" ").append(it.first).append("=").append(oss.str());
log_name.append(" mat=").append(itos(material_type))
.append(" draw=").append(itos(drawtype));
} }
ShaderConstants constants = input_const;
bool use_discard = fully_programmable; bool use_discard = fully_programmable;
if (!use_discard) { if (!use_discard) {
// workaround for a certain OpenGL implementation lacking GL_ALPHA_TEST // workaround for a certain OpenGL implementation lacking GL_ALPHA_TEST
@ -805,10 +794,7 @@ ShaderInfo ShaderSource::generateShader(const std::string &name,
// spaces could cause duplicates // spaces could cause duplicates
assert(trim(it.first) == it.first); assert(trim(it.first) == it.first);
shaders_header << "#define " << it.first << ' '; shaders_header << "#define " << it.first << ' ';
if (auto *ival = std::get_if<int>(&it.second); ival) putConstant(shaders_header, it.second);
shaders_header << *ival;
else
shaders_header << std::get<float>(it.second);
shaders_header << '\n'; shaders_header << '\n';
} }
@ -849,6 +835,39 @@ ShaderInfo ShaderSource::generateShader(const std::string &name,
return shaderinfo; return shaderinfo;
} }
/*
Other functions and helpers
*/
u32 IShaderSource::getShader(const std::string &name,
MaterialType material_type, NodeDrawType drawtype)
{
ShaderConstants input_const;
input_const["MATERIAL_TYPE"] = (int)material_type;
input_const["DRAWTYPE"] = (int)drawtype;
video::E_MATERIAL_TYPE base_mat = video::EMT_SOLID;
switch (material_type) {
case TILE_MATERIAL_ALPHA:
case TILE_MATERIAL_PLAIN_ALPHA:
case TILE_MATERIAL_LIQUID_TRANSPARENT:
case TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT:
base_mat = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
break;
case TILE_MATERIAL_BASIC:
case TILE_MATERIAL_PLAIN:
case TILE_MATERIAL_WAVING_LEAVES:
case TILE_MATERIAL_WAVING_PLANTS:
case TILE_MATERIAL_WAVING_LIQUID_BASIC:
base_mat = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
break;
default:
break;
}
return getShader(name, input_const, base_mat);
}
void dumpShaderProgram(std::ostream &output_stream, void dumpShaderProgram(std::ostream &output_stream,
const std::string &program_type, std::string_view program) const std::string &program_type, std::string_view program)
{ {

View file

@ -28,17 +28,6 @@
std::string getShaderPath(const std::string &name_of_shader, std::string getShaderPath(const std::string &name_of_shader,
const std::string &filename); const std::string &filename);
struct ShaderInfo {
std::string name = "";
video::E_MATERIAL_TYPE base_material = video::EMT_SOLID;
video::E_MATERIAL_TYPE material = video::EMT_SOLID;
NodeDrawType drawtype = NDT_NORMAL;
MaterialType material_type = TILE_MATERIAL_BASIC;
ShaderInfo() = default;
virtual ~ShaderInfo() = default;
};
/* /*
Abstraction for pushing constants (or what we pretend is) into Abstraction for pushing constants (or what we pretend is) into
shaders. These end up as `#define` prepended to the shader source. shaders. These end up as `#define` prepended to the shader source.
@ -218,7 +207,18 @@ using CachedStructPixelShaderSetting = CachedStructShaderSetting<T, count, cache
A "shader" could more precisely be called a "shader material" and comprises A "shader" could more precisely be called a "shader material" and comprises
a vertex, fragment and optional geometry shader. a vertex, fragment and optional geometry shader.
It is uniquely identified by a name, base material and the input constants.
*/ */
struct ShaderInfo {
std::string name;
video::E_MATERIAL_TYPE base_material = video::EMT_SOLID;
// Material ID the shader has received from Irrlicht
video::E_MATERIAL_TYPE material = video::EMT_SOLID;
// Input constants
ShaderConstants input_constants;
};
class IShaderSource { class IShaderSource {
public: public:
IShaderSource() = default; IShaderSource() = default;
@ -229,19 +229,38 @@ public:
* *
* Use this to get the material ID to plug into `video::SMaterial`. * Use this to get the material ID to plug into `video::SMaterial`.
*/ */
virtual ShaderInfo getShaderInfo(u32 id) = 0; virtual const ShaderInfo &getShaderInfo(u32 id) = 0;
/// @brief Generates or gets a shader suitable for nodes and entities
virtual u32 getShader(const std::string &name,
MaterialType material_type, NodeDrawType drawtype = NDT_NORMAL) = 0;
/** /**
* Generates or gets a shader for general use. * Generates or gets a shader.
*
* Note that the input constants are not for passing the entire world into
* the shader. Use `IShaderConstantSetter` to handle user settings.
* @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 base_mat base material 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;
/// @brief Helper: Generates or gets a shader suitable for nodes and entities
u32 getShader(const std::string &name,
MaterialType material_type, NodeDrawType drawtype = NDT_NORMAL);
/**
* Helper: Generates or gets a shader for common, general use.
* @param name name of the shader
* @param blendAlpha enable alpha blending for this material? * @param blendAlpha enable alpha blending for this material?
* @return shader ID * @return shader ID
*/ */
virtual u32 getShaderRaw(const std::string &name, bool blendAlpha = false) = 0; inline u32 getShaderRaw(const std::string &name, bool blendAlpha = false)
{
auto base_mat = blendAlpha ? video::EMT_TRANSPARENT_ALPHA_CHANNEL :
video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
return getShader(name, ShaderConstants(), base_mat);
}
}; };
class IWritableShaderSource : public IShaderSource { class IWritableShaderSource : public IShaderSource {