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;
/*
- If shader material specified by name is found from cache,
return the cached id.
- If shader material is found from cache, return the cached 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.
*/
u32 getShaderIdDirect(const std::string &name,
MaterialType material_type, NodeDrawType drawtype);
u32 getShaderIdDirect(const std::string &name, const ShaderConstants &input_const,
video::E_MATERIAL_TYPE base_mat);
/*
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
for processing.
*/
u32 getShader(const std::string &name,
MaterialType material_type, NodeDrawType drawtype) override;
u32 getShader(const std::string &name, const ShaderConstants &input_const,
video::E_MATERIAL_TYPE base_mat) override;
u32 getShaderRaw(const std::string &name, bool blendAlpha) 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;
const ShaderInfo &getShaderInfo(u32 id) override;
// Processes queued shader requests from other threads.
// Shall be called from the main thread.
@ -492,7 +482,16 @@ private:
// Generate shader given the shader 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()
@ -520,22 +519,27 @@ ShaderSource::~ShaderSource()
// Delete materials
auto *gpu = RenderingEngine::get_video_driver()->getGPUProgrammingServices();
assert(gpu);
u32 n = 0;
for (ShaderInfo &i : m_shaderinfo_cache) {
if (!i.name.empty())
if (!i.name.empty()) {
gpu->deleteShaderMaterial(i.material);
n++;
}
}
m_shaderinfo_cache.clear();
infostream << "~ShaderSource() cleaned up " << n << " materials" << std::endl;
}
u32 ShaderSource::getShader(const std::string &name,
MaterialType material_type, NodeDrawType drawtype)
const ShaderConstants &input_const, video::E_MATERIAL_TYPE base_mat)
{
/*
Get shader
*/
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 "
@ -573,7 +577,7 @@ u32 ShaderSource::getShader(const std::string &name,
This method generates all the shaders
*/
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
if (name.empty()) {
@ -583,9 +587,9 @@ u32 ShaderSource::getShaderIdDirect(const std::string &name,
// Check if already have such instance
for (u32 i = 0; i < m_shaderinfo_cache.size(); i++) {
ShaderInfo *info = &m_shaderinfo_cache[i];
if(info->name == name && info->material_type == material_type &&
info->drawtype == drawtype)
auto &info = m_shaderinfo_cache[i];
if (info.name == name && info.base_material == base_mat &&
info.input_constants == input_const)
return i;
}
@ -598,7 +602,7 @@ u32 ShaderSource::getShaderIdDirect(const std::string &name,
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)
@ -607,19 +611,19 @@ u32 ShaderSource::getShaderIdDirect(const std::string &name,
MutexAutoLock lock(m_shaderinfo_cache_mutex);
u32 id = m_shaderinfo_cache.size();
m_shaderinfo_cache.push_back(info);
m_shaderinfo_cache.push_back(std::move(info));
return id;
}
ShaderInfo ShaderSource::getShaderInfo(u32 id)
const ShaderInfo &ShaderSource::getShaderInfo(u32 id)
{
MutexAutoLock lock(m_shaderinfo_cache_mutex);
if(id >= m_shaderinfo_cache.size())
return ShaderInfo();
if (id >= m_shaderinfo_cache.size()) {
static ShaderInfo empty;
return empty;
}
return m_shaderinfo_cache[id];
}
@ -655,46 +659,31 @@ void ShaderSource::rebuildShaders()
}
}
infostream << "ShaderSource: recreating " << m_shaderinfo_cache.size()
<< " shaders" << std::endl;
// Recreate shaders
for (ShaderInfo &i : m_shaderinfo_cache) {
ShaderInfo *info = &i;
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,
MaterialType material_type, NodeDrawType drawtype)
const ShaderConstants &input_const, video::E_MATERIAL_TYPE base_mat)
{
ShaderInfo shaderinfo;
shaderinfo.name = name;
shaderinfo.material_type = material_type;
shaderinfo.drawtype = drawtype;
switch (material_type) {
case TILE_MATERIAL_OPAQUE:
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.input_constants = input_const;
// 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;
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.
if (driver->getDriverType() == video::EDT_NULL)
return shaderinfo;
@ -770,17 +759,17 @@ ShaderInfo ShaderSource::generateShader(const std::string &name,
/// Unique name of this shader, for debug/logging
std::string log_name = name;
ShaderConstants constants;
// Temporary plumbing <-> NodeShaderConstantSetter
if (drawtype != NodeDrawType_END) {
constants["DRAWTYPE"] = (int)drawtype;
constants["MATERIAL_TYPE"] = (int)material_type;
log_name.append(" mat=").append(itos(material_type))
.append(" draw=").append(itos(drawtype));
for (auto &it : input_const) {
if (log_name.size() > 60) { // it shouldn't be too long
log_name.append("...");
break;
}
std::ostringstream oss;
putConstant(oss, it.second);
log_name.append(" ").append(it.first).append("=").append(oss.str());
}
ShaderConstants constants = input_const;
bool use_discard = fully_programmable;
if (!use_discard) {
@ -805,10 +794,7 @@ ShaderInfo ShaderSource::generateShader(const std::string &name,
// spaces could cause duplicates
assert(trim(it.first) == it.first);
shaders_header << "#define " << it.first << ' ';
if (auto *ival = std::get_if<int>(&it.second); ival)
shaders_header << *ival;
else
shaders_header << std::get<float>(it.second);
putConstant(shaders_header, it.second);
shaders_header << '\n';
}
@ -849,6 +835,39 @@ ShaderInfo ShaderSource::generateShader(const std::string &name,
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,
const std::string &program_type, std::string_view program)
{

View file

@ -28,17 +28,6 @@
std::string getShaderPath(const std::string &name_of_shader,
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
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 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 {
public:
IShaderSource() = default;
@ -229,19 +229,38 @@ public:
*
* Use this to get the material ID to plug into `video::SMaterial`.
*/
virtual 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;
virtual const ShaderInfo &getShaderInfo(u32 id) = 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 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?
* @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 {