2024-10-28 15:57:39 +01:00
|
|
|
// Luanti
|
|
|
|
// SPDX-License-Identifier: LGPL-2.1-or-later
|
|
|
|
// Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
|
|
|
|
// Copyright (C) 2013 Kahrl <kahrl@gmx.net>
|
2012-03-19 02:59:12 +01:00
|
|
|
|
2015-04-01 23:01:28 +10:00
|
|
|
#include <fstream>
|
|
|
|
#include <iterator>
|
2012-03-19 02:59:12 +01:00
|
|
|
#include "shader.h"
|
2020-12-19 22:57:10 +03:00
|
|
|
#include "irr_ptr.h"
|
2012-03-19 02:59:12 +01:00
|
|
|
#include "debug.h"
|
|
|
|
#include "filesys.h"
|
|
|
|
#include "util/container.h"
|
|
|
|
#include "util/thread.h"
|
|
|
|
#include "settings.h"
|
|
|
|
#include <ICameraSceneNode.h>
|
|
|
|
#include <IGPUProgrammingServices.h>
|
|
|
|
#include <IMaterialRenderer.h>
|
|
|
|
#include <IMaterialRendererServices.h>
|
|
|
|
#include <IShaderConstantSetCallBack.h>
|
2017-06-26 20:11:17 +02:00
|
|
|
#include "client/renderingengine.h"
|
2024-01-19 11:51:46 +01:00
|
|
|
#include "gettext.h"
|
2012-03-19 02:59:12 +01:00
|
|
|
#include "log.h"
|
|
|
|
#include "gamedef.h"
|
2015-03-05 11:52:57 +01:00
|
|
|
#include "client/tile.h"
|
2020-10-25 20:01:03 +03:00
|
|
|
#include "config.h"
|
2012-03-19 02:59:12 +01:00
|
|
|
|
2022-02-26 15:07:00 +01:00
|
|
|
#include <mt_opengl.h>
|
2020-08-25 20:49:51 +02:00
|
|
|
|
2012-03-19 02:59:12 +01:00
|
|
|
/*
|
|
|
|
A cache from shader name to shader path
|
|
|
|
*/
|
2024-04-11 13:54:09 +02:00
|
|
|
static MutexedMap<std::string, std::string> g_shadername_to_path_cache;
|
2012-03-19 02:59:12 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
Gets the path to a shader by first checking if the file
|
|
|
|
name_of_shader/filename
|
|
|
|
exists in shader_path and if not, using the data path.
|
|
|
|
|
|
|
|
If not found, returns "".
|
|
|
|
|
|
|
|
Utilizes a thread-safe cache.
|
|
|
|
*/
|
|
|
|
std::string getShaderPath(const std::string &name_of_shader,
|
|
|
|
const std::string &filename)
|
|
|
|
{
|
|
|
|
std::string combined = name_of_shader + DIR_DELIM + filename;
|
2017-08-19 14:25:35 +02:00
|
|
|
std::string fullpath;
|
2012-03-19 02:59:12 +01:00
|
|
|
/*
|
|
|
|
Check from cache
|
|
|
|
*/
|
|
|
|
bool incache = g_shadername_to_path_cache.get(combined, &fullpath);
|
|
|
|
if(incache)
|
|
|
|
return fullpath;
|
|
|
|
|
|
|
|
/*
|
|
|
|
Check from shader_path
|
|
|
|
*/
|
|
|
|
std::string shader_path = g_settings->get("shader_path");
|
2017-08-19 14:25:35 +02:00
|
|
|
if (!shader_path.empty()) {
|
2012-03-19 02:59:12 +01:00
|
|
|
std::string testpath = shader_path + DIR_DELIM + combined;
|
|
|
|
if(fs::PathExists(testpath))
|
|
|
|
fullpath = testpath;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Check from default data directory
|
|
|
|
*/
|
2017-08-19 14:25:35 +02:00
|
|
|
if (fullpath.empty()) {
|
2012-03-19 02:59:12 +01:00
|
|
|
std::string rel_path = std::string("client") + DIR_DELIM
|
|
|
|
+ "shaders" + DIR_DELIM
|
|
|
|
+ name_of_shader + DIR_DELIM
|
|
|
|
+ filename;
|
|
|
|
std::string testpath = porting::path_share + DIR_DELIM + rel_path;
|
|
|
|
if(fs::PathExists(testpath))
|
|
|
|
fullpath = testpath;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add to cache (also an empty result is cached)
|
|
|
|
g_shadername_to_path_cache.set(combined, fullpath);
|
|
|
|
|
|
|
|
// Finally return it
|
|
|
|
return fullpath;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
SourceShaderCache: A cache used for storing source shaders.
|
|
|
|
*/
|
|
|
|
|
|
|
|
class SourceShaderCache
|
|
|
|
{
|
|
|
|
public:
|
2015-05-19 02:24:14 -04:00
|
|
|
void insert(const std::string &name_of_shader, const std::string &filename,
|
|
|
|
const std::string &program, bool prefer_local)
|
2012-03-19 02:59:12 +01:00
|
|
|
{
|
|
|
|
std::string combined = name_of_shader + DIR_DELIM + filename;
|
|
|
|
// Try to use local shader instead if asked to
|
|
|
|
if(prefer_local){
|
|
|
|
std::string path = getShaderPath(name_of_shader, filename);
|
2017-08-19 14:25:35 +02:00
|
|
|
if(!path.empty()){
|
2012-03-19 02:59:12 +01:00
|
|
|
std::string p = readFile(path);
|
2017-08-19 14:25:35 +02:00
|
|
|
if (!p.empty()) {
|
2012-03-19 02:59:12 +01:00
|
|
|
m_programs[combined] = p;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m_programs[combined] = program;
|
|
|
|
}
|
2015-05-19 02:24:14 -04:00
|
|
|
|
2012-03-19 02:59:12 +01:00
|
|
|
std::string get(const std::string &name_of_shader,
|
2015-05-19 02:24:14 -04:00
|
|
|
const std::string &filename)
|
2012-03-19 02:59:12 +01:00
|
|
|
{
|
|
|
|
std::string combined = name_of_shader + DIR_DELIM + filename;
|
2015-05-19 02:24:14 -04:00
|
|
|
StringMap::iterator n = m_programs.find(combined);
|
|
|
|
if (n != m_programs.end())
|
2012-12-20 21:19:49 +04:00
|
|
|
return n->second;
|
2012-03-19 02:59:12 +01:00
|
|
|
return "";
|
|
|
|
}
|
2015-05-19 02:24:14 -04:00
|
|
|
|
2012-03-19 02:59:12 +01:00
|
|
|
// Primarily fetches from cache, secondarily tries to read from filesystem
|
|
|
|
std::string getOrLoad(const std::string &name_of_shader,
|
2015-05-19 02:24:14 -04:00
|
|
|
const std::string &filename)
|
2012-03-19 02:59:12 +01:00
|
|
|
{
|
|
|
|
std::string combined = name_of_shader + DIR_DELIM + filename;
|
2015-05-19 02:24:14 -04:00
|
|
|
StringMap::iterator n = m_programs.find(combined);
|
|
|
|
if (n != m_programs.end())
|
2012-12-20 21:19:49 +04:00
|
|
|
return n->second;
|
2012-03-19 02:59:12 +01:00
|
|
|
std::string path = getShaderPath(name_of_shader, filename);
|
2017-08-19 14:25:35 +02:00
|
|
|
if (path.empty()) {
|
2015-05-19 02:24:14 -04:00
|
|
|
infostream << "SourceShaderCache::getOrLoad(): No path found for \""
|
|
|
|
<< combined << "\"" << std::endl;
|
2012-03-19 02:59:12 +01:00
|
|
|
return "";
|
|
|
|
}
|
2015-05-19 02:24:14 -04:00
|
|
|
infostream << "SourceShaderCache::getOrLoad(): Loading path \""
|
|
|
|
<< path << "\"" << std::endl;
|
2012-03-19 02:59:12 +01:00
|
|
|
std::string p = readFile(path);
|
2017-08-19 14:25:35 +02:00
|
|
|
if (!p.empty()) {
|
2012-03-19 02:59:12 +01:00
|
|
|
m_programs[combined] = p;
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
private:
|
2015-05-19 02:24:14 -04:00
|
|
|
StringMap m_programs;
|
|
|
|
|
2024-05-08 20:37:10 +02:00
|
|
|
inline std::string readFile(const std::string &path)
|
2012-03-19 02:59:12 +01:00
|
|
|
{
|
2024-02-20 10:38:29 +01:00
|
|
|
std::string ret;
|
2024-05-08 20:37:10 +02:00
|
|
|
if (!fs::ReadFile(path, ret, true))
|
2024-02-20 10:38:29 +01:00
|
|
|
ret.clear();
|
|
|
|
return ret;
|
2012-03-19 02:59:12 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-11-22 09:05:39 -05:00
|
|
|
|
2012-03-19 02:59:12 +01:00
|
|
|
/*
|
|
|
|
ShaderCallback: Sets constants that can be used in shaders
|
|
|
|
*/
|
|
|
|
|
|
|
|
class ShaderCallback : public video::IShaderConstantSetCallBack
|
|
|
|
{
|
2025-04-17 22:15:14 +02:00
|
|
|
std::vector<std::unique_ptr<IShaderUniformSetter>> m_setters;
|
2012-12-01 03:02:16 +02:00
|
|
|
|
2012-03-19 02:59:12 +01:00
|
|
|
public:
|
2020-12-19 22:57:10 +03:00
|
|
|
template <typename Factories>
|
|
|
|
ShaderCallback(const Factories &factories)
|
2016-11-22 09:05:39 -05:00
|
|
|
{
|
2024-02-20 10:38:29 +01:00
|
|
|
for (auto &&factory : factories) {
|
|
|
|
auto *setter = factory->create();
|
|
|
|
if (setter)
|
|
|
|
m_setters.emplace_back(setter);
|
|
|
|
}
|
2016-11-22 09:05:39 -05:00
|
|
|
}
|
2012-03-19 02:59:12 +01:00
|
|
|
|
2020-02-16 22:37:28 +03:00
|
|
|
virtual void OnSetConstants(video::IMaterialRendererServices *services, s32 userData) override
|
2012-03-19 02:59:12 +01:00
|
|
|
{
|
2020-12-19 22:57:10 +03:00
|
|
|
for (auto &&setter : m_setters)
|
2025-04-17 22:15:14 +02:00
|
|
|
setter->onSetUniforms(services);
|
2012-12-01 03:02:16 +02:00
|
|
|
}
|
2020-02-16 22:37:28 +03:00
|
|
|
|
|
|
|
virtual void OnSetMaterial(const video::SMaterial& material) override
|
|
|
|
{
|
2020-12-19 22:57:10 +03:00
|
|
|
for (auto &&setter : m_setters)
|
2020-02-16 22:37:28 +03:00
|
|
|
setter->onSetMaterial(material);
|
|
|
|
}
|
2012-12-01 03:02:16 +02:00
|
|
|
};
|
|
|
|
|
2016-11-22 09:05:39 -05:00
|
|
|
|
2025-04-17 23:13:44 +02:00
|
|
|
/*
|
2025-04-18 10:09:00 +02:00
|
|
|
MainShaderConstantSetter: Sets some random general constants
|
2025-04-17 23:13:44 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
class MainShaderConstantSetter : public IShaderConstantSetter
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
MainShaderConstantSetter() = default;
|
|
|
|
~MainShaderConstantSetter() = default;
|
|
|
|
|
|
|
|
void onGenerate(const std::string &name, ShaderConstants &constants) override
|
|
|
|
{
|
|
|
|
constants["ENABLE_TONE_MAPPING"] = g_settings->getBool("tone_mapping") ? 1 : 0;
|
|
|
|
|
|
|
|
if (g_settings->getBool("enable_dynamic_shadows")) {
|
|
|
|
constants["ENABLE_DYNAMIC_SHADOWS"] = 1;
|
|
|
|
if (g_settings->getBool("shadow_map_color"))
|
|
|
|
constants["COLORED_SHADOWS"] = 1;
|
|
|
|
|
|
|
|
if (g_settings->getBool("shadow_poisson_filter"))
|
|
|
|
constants["POISSON_FILTER"] = 1;
|
|
|
|
|
|
|
|
if (g_settings->getBool("enable_water_reflections"))
|
|
|
|
constants["ENABLE_WATER_REFLECTIONS"] = 1;
|
|
|
|
|
|
|
|
if (g_settings->getBool("enable_translucent_foliage"))
|
|
|
|
constants["ENABLE_TRANSLUCENT_FOLIAGE"] = 1;
|
|
|
|
|
2025-05-17 15:02:47 +02:00
|
|
|
// FIXME: The node specular effect is currently disabled due to mixed in-game
|
|
|
|
// results. This shader should not be applied to all nodes equally. See #15898
|
|
|
|
if (false)
|
2025-04-17 23:13:44 +02:00
|
|
|
constants["ENABLE_NODE_SPECULAR"] = 1;
|
|
|
|
|
|
|
|
s32 shadow_filter = g_settings->getS32("shadow_filters");
|
|
|
|
constants["SHADOW_FILTER"] = shadow_filter;
|
|
|
|
|
|
|
|
float shadow_soft_radius = std::max(1.f,
|
|
|
|
g_settings->getFloat("shadow_soft_radius"));
|
|
|
|
constants["SOFTSHADOWRADIUS"] = shadow_soft_radius;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (g_settings->getBool("enable_bloom")) {
|
|
|
|
constants["ENABLE_BLOOM"] = 1;
|
|
|
|
if (g_settings->getBool("enable_bloom_debug"))
|
|
|
|
constants["ENABLE_BLOOM_DEBUG"] = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (g_settings->getBool("enable_auto_exposure"))
|
|
|
|
constants["ENABLE_AUTO_EXPOSURE"] = 1;
|
|
|
|
|
|
|
|
if (g_settings->get("antialiasing") == "ssaa") {
|
|
|
|
constants["ENABLE_SSAA"] = 1;
|
|
|
|
u16 ssaa_scale = std::max<u16>(2, g_settings->getU16("fsaa"));
|
|
|
|
constants["SSAA_SCALE"] = ssaa_scale;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (g_settings->getBool("debanding"))
|
|
|
|
constants["ENABLE_DITHERING"] = 1;
|
|
|
|
|
|
|
|
if (g_settings->getBool("enable_volumetric_lighting"))
|
|
|
|
constants["VOLUMETRIC_LIGHT"] = 1;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2012-12-01 03:02:16 +02:00
|
|
|
/*
|
2025-04-17 22:15:14 +02:00
|
|
|
MainShaderUniformSetter: Set basic uniforms required for almost everything
|
2012-12-01 03:02:16 +02:00
|
|
|
*/
|
|
|
|
|
2025-04-17 22:15:14 +02:00
|
|
|
class MainShaderUniformSetter : public IShaderUniformSetter
|
2012-12-01 03:02:16 +02:00
|
|
|
{
|
2025-04-18 10:09:00 +02:00
|
|
|
using SamplerLayer_t = s32;
|
|
|
|
|
2024-01-07 18:54:31 +01:00
|
|
|
CachedVertexShaderSetting<f32, 16> m_world_view_proj{"mWorldViewProj"};
|
|
|
|
CachedVertexShaderSetting<f32, 16> m_world{"mWorld"};
|
2021-06-06 18:51:21 +02:00
|
|
|
|
2020-10-25 20:01:03 +03:00
|
|
|
// Modelview matrix
|
2024-01-07 18:54:31 +01:00
|
|
|
CachedVertexShaderSetting<float, 16> m_world_view{"mWorldView"};
|
2020-10-25 20:01:03 +03:00
|
|
|
// Texture matrix
|
2024-01-07 18:54:31 +01:00
|
|
|
CachedVertexShaderSetting<float, 16> m_texture{"mTexture"};
|
2016-11-22 09:05:39 -05:00
|
|
|
|
2025-04-18 10:09:00 +02:00
|
|
|
CachedPixelShaderSetting<SamplerLayer_t> m_texture0{"texture0"};
|
|
|
|
CachedPixelShaderSetting<SamplerLayer_t> m_texture1{"texture1"};
|
|
|
|
CachedPixelShaderSetting<SamplerLayer_t> m_texture2{"texture2"};
|
|
|
|
CachedPixelShaderSetting<SamplerLayer_t> m_texture3{"texture3"};
|
|
|
|
|
2024-02-19 19:04:20 +01:00
|
|
|
// commonly used way to pass material color to shader
|
2024-09-18 12:18:28 +02:00
|
|
|
video::SColor m_material_color;
|
|
|
|
CachedPixelShaderSetting<float, 4> m_material_color_setting{"materialColor"};
|
2024-02-19 19:04:20 +01:00
|
|
|
|
2012-12-01 03:02:16 +02:00
|
|
|
public:
|
2025-04-17 22:15:14 +02:00
|
|
|
~MainShaderUniformSetter() = default;
|
2012-12-01 03:02:16 +02:00
|
|
|
|
2024-02-19 19:04:20 +01:00
|
|
|
virtual void onSetMaterial(const video::SMaterial& material) override
|
|
|
|
{
|
2024-09-18 12:18:28 +02:00
|
|
|
m_material_color = material.ColorParam;
|
2024-02-19 19:04:20 +01:00
|
|
|
}
|
|
|
|
|
2025-04-17 22:15:14 +02:00
|
|
|
virtual void onSetUniforms(video::IMaterialRendererServices *services) override
|
2012-12-01 03:02:16 +02:00
|
|
|
{
|
|
|
|
video::IVideoDriver *driver = services->getVideoDriver();
|
2024-02-19 19:04:20 +01:00
|
|
|
assert(driver);
|
2012-12-01 03:02:16 +02:00
|
|
|
|
2020-10-25 20:01:03 +03:00
|
|
|
// Set world matrix
|
|
|
|
core::matrix4 world = driver->getTransform(video::ETS_WORLD);
|
2024-02-20 14:59:47 +01:00
|
|
|
m_world.set(world, services);
|
2020-10-25 20:01:03 +03:00
|
|
|
|
2016-11-22 09:05:39 -05:00
|
|
|
// Set clip matrix
|
2020-10-25 20:01:03 +03:00
|
|
|
core::matrix4 worldView;
|
|
|
|
worldView = driver->getTransform(video::ETS_VIEW);
|
|
|
|
worldView *= world;
|
2020-12-19 22:57:10 +03:00
|
|
|
|
2012-03-19 02:59:12 +01:00
|
|
|
core::matrix4 worldViewProj;
|
|
|
|
worldViewProj = driver->getTransform(video::ETS_PROJECTION);
|
2020-10-25 20:01:03 +03:00
|
|
|
worldViewProj *= worldView;
|
2024-02-20 14:59:47 +01:00
|
|
|
m_world_view_proj.set(worldViewProj, services);
|
2012-03-19 02:59:12 +01:00
|
|
|
|
2024-01-16 23:09:18 +03:00
|
|
|
if (driver->getDriverType() == video::EDT_OGLES2 || driver->getDriverType() == video::EDT_OPENGL3) {
|
2024-09-06 10:32:05 +02:00
|
|
|
auto &texture = driver->getTransform(video::ETS_TEXTURE_0);
|
2024-02-20 14:59:47 +01:00
|
|
|
m_world_view.set(worldView, services);
|
|
|
|
m_texture.set(texture, services);
|
2023-03-05 15:10:44 +01:00
|
|
|
}
|
2024-02-19 19:04:20 +01:00
|
|
|
|
2025-04-18 10:09:00 +02:00
|
|
|
SamplerLayer_t tex_id;
|
|
|
|
tex_id = 0;
|
|
|
|
m_texture0.set(&tex_id, services);
|
|
|
|
tex_id = 1;
|
|
|
|
m_texture1.set(&tex_id, services);
|
|
|
|
tex_id = 2;
|
|
|
|
m_texture2.set(&tex_id, services);
|
|
|
|
tex_id = 3;
|
|
|
|
m_texture3.set(&tex_id, services);
|
|
|
|
|
2024-09-18 12:18:28 +02:00
|
|
|
video::SColorf colorf(m_material_color);
|
|
|
|
m_material_color_setting.set(colorf, services);
|
2012-03-19 02:59:12 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-11-22 09:05:39 -05:00
|
|
|
|
2025-04-17 22:15:14 +02:00
|
|
|
class MainShaderUniformSetterFactory : public IShaderUniformSetterFactory
|
2016-11-22 09:05:39 -05:00
|
|
|
{
|
|
|
|
public:
|
2025-04-17 22:15:14 +02:00
|
|
|
virtual IShaderUniformSetter* create()
|
|
|
|
{ return new MainShaderUniformSetter(); }
|
2016-11-22 09:05:39 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2012-03-19 02:59:12 +01:00
|
|
|
/*
|
|
|
|
ShaderSource
|
|
|
|
*/
|
|
|
|
|
2016-11-22 09:05:39 -05:00
|
|
|
class ShaderSource : public IWritableShaderSource
|
2012-03-19 02:59:12 +01:00
|
|
|
{
|
|
|
|
public:
|
2017-06-26 20:11:17 +02:00
|
|
|
ShaderSource();
|
2024-02-20 21:44:47 +01:00
|
|
|
~ShaderSource() override;
|
2012-03-19 02:59:12 +01:00
|
|
|
|
|
|
|
/*
|
2025-04-17 23:48:08 +02:00
|
|
|
- If shader material is found from cache, return the cached id.
|
2012-03-19 02:59:12 +01:00
|
|
|
- Otherwise generate the shader material, add to cache and return id.
|
|
|
|
|
|
|
|
The id 0 points to a null shader. Its material is EMT_SOLID.
|
|
|
|
*/
|
2025-04-17 23:48:08 +02:00
|
|
|
u32 getShaderIdDirect(const std::string &name, const ShaderConstants &input_const,
|
|
|
|
video::E_MATERIAL_TYPE base_mat);
|
2012-03-19 02:59:12 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
If shader specified by the name pointed by the id doesn't
|
2015-05-19 02:24:14 -04:00
|
|
|
exist, create it, then return id.
|
2012-03-19 02:59:12 +01:00
|
|
|
|
|
|
|
Can be called from any thread. If called from some other thread
|
|
|
|
and not found in cache, the call is queued to the main thread
|
|
|
|
for processing.
|
|
|
|
*/
|
2025-04-17 23:48:08 +02:00
|
|
|
u32 getShader(const std::string &name, const ShaderConstants &input_const,
|
|
|
|
video::E_MATERIAL_TYPE base_mat) override;
|
2015-05-19 02:24:14 -04:00
|
|
|
|
2025-04-17 23:48:08 +02:00
|
|
|
const ShaderInfo &getShaderInfo(u32 id) override;
|
2015-05-19 02:24:14 -04:00
|
|
|
|
2012-03-19 02:59:12 +01:00
|
|
|
// Processes queued shader requests from other threads.
|
|
|
|
// Shall be called from the main thread.
|
2020-12-19 22:57:10 +03:00
|
|
|
void processQueue() override;
|
2012-03-19 02:59:12 +01:00
|
|
|
|
|
|
|
// Insert a shader program into the cache without touching the
|
|
|
|
// filesystem. Shall be called from the main thread.
|
|
|
|
void insertSourceShader(const std::string &name_of_shader,
|
2020-12-19 22:57:10 +03:00
|
|
|
const std::string &filename, const std::string &program) override;
|
2012-03-19 02:59:12 +01:00
|
|
|
|
|
|
|
// Rebuild shaders from the current set of source shaders
|
|
|
|
// Shall be called from the main thread.
|
2020-12-19 22:57:10 +03:00
|
|
|
void rebuildShaders() override;
|
2012-03-19 02:59:12 +01:00
|
|
|
|
2025-08-06 23:17:34 +02:00
|
|
|
void addShaderConstantSetter(std::unique_ptr<IShaderConstantSetter> setter) override
|
2025-04-17 23:13:44 +02:00
|
|
|
{
|
2025-08-06 23:17:34 +02:00
|
|
|
m_constant_setters.emplace_back(std::move(setter));
|
2025-04-17 23:13:44 +02:00
|
|
|
}
|
|
|
|
|
2025-08-06 23:17:34 +02:00
|
|
|
void addShaderUniformSetterFactory(std::unique_ptr<IShaderUniformSetterFactory> setter) override
|
2012-12-01 03:02:16 +02:00
|
|
|
{
|
2025-08-06 23:17:34 +02:00
|
|
|
m_uniform_factories.emplace_back(std::move(setter));
|
2012-12-01 03:02:16 +02:00
|
|
|
}
|
|
|
|
|
2012-03-19 02:59:12 +01:00
|
|
|
private:
|
|
|
|
|
|
|
|
// The id of the thread that is allowed to use irrlicht directly
|
2017-06-11 03:43:05 -04:00
|
|
|
std::thread::id m_main_thread;
|
2012-03-19 02:59:12 +01:00
|
|
|
|
|
|
|
// Cache of source shaders
|
|
|
|
// This should be only accessed from the main thread
|
|
|
|
SourceShaderCache m_sourcecache;
|
|
|
|
|
|
|
|
// A shader id is index in this array.
|
|
|
|
// The first position contains a dummy shader.
|
2012-12-20 21:19:49 +04:00
|
|
|
std::vector<ShaderInfo> m_shaderinfo_cache;
|
2014-05-14 23:19:31 +02:00
|
|
|
// The former container is behind this mutex
|
2017-06-06 16:29:28 +02:00
|
|
|
std::mutex m_shaderinfo_cache_mutex;
|
2012-03-19 02:59:12 +01:00
|
|
|
|
2024-02-20 11:30:51 +01:00
|
|
|
#if 0
|
2012-03-19 02:59:12 +01:00
|
|
|
// Queued shader fetches (to be processed by the main thread)
|
|
|
|
RequestQueue<std::string, u32, u8, u8> m_get_shader_queue;
|
2024-02-20 11:30:51 +01:00
|
|
|
#endif
|
2012-12-01 03:02:16 +02:00
|
|
|
|
2025-04-17 23:13:44 +02:00
|
|
|
// Global constant setter factories
|
|
|
|
std::vector<std::unique_ptr<IShaderConstantSetter>> m_constant_setters;
|
|
|
|
|
2025-04-17 22:15:14 +02:00
|
|
|
// Global uniform setter factories
|
|
|
|
std::vector<std::unique_ptr<IShaderUniformSetterFactory>> m_uniform_factories;
|
2016-11-22 09:05:39 -05:00
|
|
|
|
2020-12-19 22:57:10 +03:00
|
|
|
// Generate shader given the shader name.
|
|
|
|
ShaderInfo generateShader(const std::string &name,
|
2025-04-17 23:48:08 +02:00
|
|
|
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);
|
|
|
|
}
|
2012-03-19 02:59:12 +01:00
|
|
|
};
|
|
|
|
|
2017-06-26 20:11:17 +02:00
|
|
|
IWritableShaderSource *createShaderSource()
|
2012-03-19 02:59:12 +01:00
|
|
|
{
|
2017-06-26 20:11:17 +02:00
|
|
|
return new ShaderSource();
|
2012-03-19 02:59:12 +01:00
|
|
|
}
|
|
|
|
|
2017-06-26 20:11:17 +02:00
|
|
|
ShaderSource::ShaderSource()
|
2012-03-19 02:59:12 +01:00
|
|
|
{
|
2017-06-11 03:43:05 -04:00
|
|
|
m_main_thread = std::this_thread::get_id();
|
2012-03-19 02:59:12 +01:00
|
|
|
|
|
|
|
// Add a dummy ShaderInfo as the first index, named ""
|
2017-08-19 14:25:35 +02:00
|
|
|
m_shaderinfo_cache.emplace_back();
|
2012-12-01 03:02:16 +02:00
|
|
|
|
2025-04-17 23:13:44 +02:00
|
|
|
// Add global stuff
|
2025-08-06 23:17:34 +02:00
|
|
|
addShaderConstantSetter(std::make_unique<MainShaderConstantSetter>());
|
|
|
|
addShaderUniformSetterFactory(std::make_unique<MainShaderUniformSetterFactory>());
|
2012-03-19 02:59:12 +01:00
|
|
|
}
|
|
|
|
|
2024-02-20 21:44:47 +01:00
|
|
|
ShaderSource::~ShaderSource()
|
|
|
|
{
|
|
|
|
MutexAutoLock lock(m_shaderinfo_cache_mutex);
|
|
|
|
|
|
|
|
// Delete materials
|
2024-09-30 22:43:08 +02:00
|
|
|
auto *gpu = RenderingEngine::get_video_driver()->getGPUProgrammingServices();
|
2024-11-13 14:24:01 +01:00
|
|
|
assert(gpu);
|
2025-04-17 23:48:08 +02:00
|
|
|
u32 n = 0;
|
2024-02-20 21:44:47 +01:00
|
|
|
for (ShaderInfo &i : m_shaderinfo_cache) {
|
2025-04-17 23:48:08 +02:00
|
|
|
if (!i.name.empty()) {
|
2024-02-20 21:44:47 +01:00
|
|
|
gpu->deleteShaderMaterial(i.material);
|
2025-04-17 23:48:08 +02:00
|
|
|
n++;
|
|
|
|
}
|
2024-02-20 21:44:47 +01:00
|
|
|
}
|
|
|
|
m_shaderinfo_cache.clear();
|
2025-04-17 23:48:08 +02:00
|
|
|
|
|
|
|
infostream << "~ShaderSource() cleaned up " << n << " materials" << std::endl;
|
2024-02-20 21:44:47 +01:00
|
|
|
}
|
|
|
|
|
2015-05-19 02:24:14 -04:00
|
|
|
u32 ShaderSource::getShader(const std::string &name,
|
2025-04-17 23:48:08 +02:00
|
|
|
const ShaderConstants &input_const, video::E_MATERIAL_TYPE base_mat)
|
2012-03-19 02:59:12 +01:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
Get shader
|
|
|
|
*/
|
2014-05-14 23:19:31 +02:00
|
|
|
|
2017-06-11 03:43:05 -04:00
|
|
|
if (std::this_thread::get_id() == m_main_thread) {
|
2025-04-17 23:48:08 +02:00
|
|
|
return getShaderIdDirect(name, input_const, base_mat);
|
2017-08-19 14:25:35 +02:00
|
|
|
}
|
2012-03-19 02:59:12 +01:00
|
|
|
|
2024-02-20 11:30:51 +01:00
|
|
|
errorstream << "ShaderSource::getShader(): getting from "
|
|
|
|
"other thread not implemented" << std::endl;
|
2013-11-14 18:30:43 +01:00
|
|
|
|
2024-02-20 11:30:51 +01:00
|
|
|
#if 0
|
2017-08-19 14:25:35 +02:00
|
|
|
// We're gonna ask the result to be put into here
|
2012-03-19 02:59:12 +01:00
|
|
|
|
2017-08-19 14:25:35 +02:00
|
|
|
static ResultQueue<std::string, u32, u8, u8> result_queue;
|
2012-03-19 02:59:12 +01:00
|
|
|
|
2017-08-19 14:25:35 +02:00
|
|
|
// Throw a request in
|
|
|
|
m_get_shader_queue.add(name, 0, 0, &result_queue);
|
2012-03-19 02:59:12 +01:00
|
|
|
|
2017-08-19 14:25:35 +02:00
|
|
|
/* infostream<<"Waiting for shader from main thread, name=\""
|
|
|
|
<<name<<"\""<<std::endl;*/
|
2014-01-06 12:45:42 +01:00
|
|
|
|
2017-08-19 14:25:35 +02:00
|
|
|
while(true) {
|
|
|
|
GetResult<std::string, u32, u8, u8>
|
|
|
|
result = result_queue.pop_frontNoEx();
|
|
|
|
|
|
|
|
if (result.key == name) {
|
|
|
|
return result.item;
|
2012-03-19 02:59:12 +01:00
|
|
|
}
|
2014-01-06 12:45:42 +01:00
|
|
|
|
2017-08-19 14:25:35 +02:00
|
|
|
errorstream << "Got shader with invalid name: " << result.key << std::endl;
|
2012-03-19 02:59:12 +01:00
|
|
|
}
|
|
|
|
|
2017-08-19 14:25:35 +02:00
|
|
|
infostream << "getShader(): Failed" << std::endl;
|
2024-02-20 11:30:51 +01:00
|
|
|
#endif
|
2012-03-19 02:59:12 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
This method generates all the shaders
|
|
|
|
*/
|
2015-05-19 02:24:14 -04:00
|
|
|
u32 ShaderSource::getShaderIdDirect(const std::string &name,
|
2025-04-17 23:48:08 +02:00
|
|
|
const ShaderConstants &input_const, video::E_MATERIAL_TYPE base_mat)
|
2012-03-19 02:59:12 +01:00
|
|
|
{
|
|
|
|
// Empty name means shader 0
|
2017-08-19 14:25:35 +02:00
|
|
|
if (name.empty()) {
|
2012-03-19 02:59:12 +01:00
|
|
|
infostream<<"getShaderIdDirect(): name is empty"<<std::endl;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-05-14 23:19:31 +02:00
|
|
|
// Check if already have such instance
|
2025-04-17 23:48:08 +02:00
|
|
|
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)
|
2014-05-14 23:19:31 +02:00
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
2012-03-19 02:59:12 +01:00
|
|
|
/*
|
|
|
|
Calling only allowed from main thread
|
|
|
|
*/
|
2017-06-11 03:43:05 -04:00
|
|
|
if (std::this_thread::get_id() != m_main_thread) {
|
2012-03-19 02:59:12 +01:00
|
|
|
errorstream<<"ShaderSource::getShaderIdDirect() "
|
|
|
|
"called not from main thread"<<std::endl;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2025-04-17 23:48:08 +02:00
|
|
|
ShaderInfo info = generateShader(name, input_const, base_mat);
|
2012-03-19 02:59:12 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
Add shader to caches (add dummy shaders too)
|
|
|
|
*/
|
|
|
|
|
2015-04-07 06:13:12 -04:00
|
|
|
MutexAutoLock lock(m_shaderinfo_cache_mutex);
|
2012-03-19 02:59:12 +01:00
|
|
|
|
|
|
|
u32 id = m_shaderinfo_cache.size();
|
2025-04-17 23:48:08 +02:00
|
|
|
m_shaderinfo_cache.push_back(std::move(info));
|
2012-03-19 02:59:12 +01:00
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2025-04-17 23:48:08 +02:00
|
|
|
const ShaderInfo &ShaderSource::getShaderInfo(u32 id)
|
2012-03-19 02:59:12 +01:00
|
|
|
{
|
2015-04-07 06:13:12 -04:00
|
|
|
MutexAutoLock lock(m_shaderinfo_cache_mutex);
|
2012-03-19 02:59:12 +01:00
|
|
|
|
2025-04-17 23:48:08 +02:00
|
|
|
if (id >= m_shaderinfo_cache.size()) {
|
|
|
|
static ShaderInfo empty;
|
|
|
|
return empty;
|
|
|
|
}
|
2012-03-19 02:59:12 +01:00
|
|
|
return m_shaderinfo_cache[id];
|
|
|
|
}
|
|
|
|
|
|
|
|
void ShaderSource::processQueue()
|
|
|
|
{
|
2015-05-19 02:24:14 -04:00
|
|
|
|
2012-03-19 02:59:12 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void ShaderSource::insertSourceShader(const std::string &name_of_shader,
|
|
|
|
const std::string &filename, const std::string &program)
|
|
|
|
{
|
|
|
|
/*infostream<<"ShaderSource::insertSourceShader(): "
|
|
|
|
"name_of_shader=\""<<name_of_shader<<"\", "
|
|
|
|
"filename=\""<<filename<<"\""<<std::endl;*/
|
|
|
|
|
2017-06-11 03:43:05 -04:00
|
|
|
sanity_check(std::this_thread::get_id() == m_main_thread);
|
2012-03-19 02:59:12 +01:00
|
|
|
|
|
|
|
m_sourcecache.insert(name_of_shader, filename, program, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ShaderSource::rebuildShaders()
|
|
|
|
{
|
2015-04-07 06:13:12 -04:00
|
|
|
MutexAutoLock lock(m_shaderinfo_cache_mutex);
|
2012-03-19 02:59:12 +01:00
|
|
|
|
2024-02-20 21:44:47 +01:00
|
|
|
// Delete materials
|
2024-09-30 22:43:08 +02:00
|
|
|
auto *gpu = RenderingEngine::get_video_driver()->getGPUProgrammingServices();
|
2024-11-13 14:24:01 +01:00
|
|
|
assert(gpu);
|
2024-02-20 21:44:47 +01:00
|
|
|
for (ShaderInfo &i : m_shaderinfo_cache) {
|
|
|
|
if (!i.name.empty()) {
|
|
|
|
gpu->deleteShaderMaterial(i.material);
|
|
|
|
i.material = video::EMT_SOLID; // invalidate
|
|
|
|
}
|
|
|
|
}
|
2012-03-19 02:59:12 +01:00
|
|
|
|
2025-04-17 23:48:08 +02:00
|
|
|
infostream << "ShaderSource: recreating " << m_shaderinfo_cache.size()
|
|
|
|
<< " shaders" << std::endl;
|
|
|
|
|
2012-03-19 02:59:12 +01:00
|
|
|
// Recreate shaders
|
2017-08-19 14:25:35 +02:00
|
|
|
for (ShaderInfo &i : m_shaderinfo_cache) {
|
|
|
|
ShaderInfo *info = &i;
|
|
|
|
if (!info->name.empty()) {
|
2025-04-17 23:48:08 +02:00
|
|
|
*info = generateShader(info->name, info->input_constants, info->base_material);
|
2013-07-05 04:24:05 +02:00
|
|
|
}
|
2012-03-19 02:59:12 +01:00
|
|
|
}
|
|
|
|
}
|
2012-12-01 03:02:16 +02:00
|
|
|
|
2013-11-14 18:30:43 +01:00
|
|
|
|
2020-12-19 22:57:10 +03:00
|
|
|
ShaderInfo ShaderSource::generateShader(const std::string &name,
|
2025-04-17 23:48:08 +02:00
|
|
|
const ShaderConstants &input_const, video::E_MATERIAL_TYPE base_mat)
|
2012-03-19 02:59:12 +01:00
|
|
|
{
|
|
|
|
ShaderInfo shaderinfo;
|
|
|
|
shaderinfo.name = name;
|
2025-04-17 23:48:08 +02:00
|
|
|
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;
|
2020-11-22 17:36:59 +03:00
|
|
|
shaderinfo.material = shaderinfo.base_material;
|
2015-05-19 02:24:14 -04:00
|
|
|
|
2025-04-17 23:48:08 +02:00
|
|
|
auto *driver = RenderingEngine::get_video_driver();
|
2024-11-13 14:24:01 +01:00
|
|
|
// The null driver doesn't support shaders (duh), but we can pretend it does.
|
|
|
|
if (driver->getDriverType() == video::EDT_NULL)
|
2012-03-19 02:59:12 +01:00
|
|
|
return shaderinfo;
|
|
|
|
|
2024-09-30 22:43:08 +02:00
|
|
|
auto *gpu = driver->getGPUProgrammingServices();
|
2024-02-19 19:04:20 +01:00
|
|
|
if (!driver->queryFeature(video::EVDF_ARB_GLSL) || !gpu) {
|
2024-11-13 14:24:01 +01:00
|
|
|
throw ShaderException(gettext("GLSL is not supported by the driver"));
|
2012-03-19 02:59:12 +01:00
|
|
|
}
|
|
|
|
|
2013-12-09 13:25:43 +01:00
|
|
|
// Create shaders header
|
2024-01-16 23:09:18 +03:00
|
|
|
bool fully_programmable = driver->getDriverType() == video::EDT_OGLES2 || driver->getDriverType() == video::EDT_OPENGL3;
|
2024-09-28 11:08:42 +02:00
|
|
|
std::ostringstream shaders_header;
|
2020-12-19 22:57:10 +03:00
|
|
|
shaders_header
|
|
|
|
<< std::noboolalpha
|
|
|
|
<< std::showpoint // for GLSL ES
|
|
|
|
;
|
|
|
|
std::string vertex_header, fragment_header, geometry_header;
|
2024-01-16 23:09:18 +03:00
|
|
|
if (fully_programmable) {
|
|
|
|
if (driver->getDriverType() == video::EDT_OPENGL3) {
|
|
|
|
shaders_header << "#version 150\n";
|
|
|
|
} else {
|
|
|
|
shaders_header << "#version 100\n";
|
|
|
|
}
|
2024-09-06 10:32:05 +02:00
|
|
|
// cf. EVertexAttributes.h for the predefined ones
|
2020-10-25 20:01:03 +03:00
|
|
|
vertex_header = R"(
|
2021-04-18 16:07:13 +02:00
|
|
|
precision mediump float;
|
|
|
|
|
2020-10-25 20:01:03 +03:00
|
|
|
uniform highp mat4 mWorldView;
|
|
|
|
uniform highp mat4 mWorldViewProj;
|
|
|
|
uniform mediump mat4 mTexture;
|
|
|
|
|
|
|
|
attribute highp vec4 inVertexPosition;
|
|
|
|
attribute lowp vec4 inVertexColor;
|
2024-09-06 10:32:05 +02:00
|
|
|
attribute mediump vec2 inTexCoord0;
|
2020-10-25 20:01:03 +03:00
|
|
|
attribute mediump vec3 inVertexNormal;
|
|
|
|
attribute mediump vec4 inVertexTangent;
|
|
|
|
attribute mediump vec4 inVertexBinormal;
|
2021-04-18 16:07:13 +02:00
|
|
|
)";
|
2024-09-28 11:08:42 +02:00
|
|
|
// Our vertex color has components reversed compared to what OpenGL
|
|
|
|
// normally expects, so we need to take that into account.
|
|
|
|
vertex_header += "#define inVertexColor (inVertexColor.bgra)\n";
|
2020-12-19 22:57:10 +03:00
|
|
|
fragment_header = R"(
|
2020-10-25 20:01:03 +03:00
|
|
|
precision mediump float;
|
2021-04-18 16:07:13 +02:00
|
|
|
)";
|
2020-10-25 20:01:03 +03:00
|
|
|
} else {
|
2024-09-28 11:08:42 +02:00
|
|
|
/* legacy OpenGL driver */
|
2020-12-19 22:57:10 +03:00
|
|
|
shaders_header << R"(
|
2020-10-25 20:01:03 +03:00
|
|
|
#version 120
|
|
|
|
#define lowp
|
|
|
|
#define mediump
|
|
|
|
#define highp
|
2021-04-18 16:07:13 +02:00
|
|
|
)";
|
2020-10-25 20:01:03 +03:00
|
|
|
vertex_header = R"(
|
|
|
|
#define mWorldView gl_ModelViewMatrix
|
|
|
|
#define mWorldViewProj gl_ModelViewProjectionMatrix
|
|
|
|
#define mTexture (gl_TextureMatrix[0])
|
|
|
|
|
|
|
|
#define inVertexPosition gl_Vertex
|
|
|
|
#define inVertexColor gl_Color
|
|
|
|
#define inTexCoord0 gl_MultiTexCoord0
|
|
|
|
#define inVertexNormal gl_Normal
|
|
|
|
#define inVertexTangent gl_MultiTexCoord1
|
|
|
|
#define inVertexBinormal gl_MultiTexCoord2
|
2021-04-18 16:07:13 +02:00
|
|
|
)";
|
2020-10-25 20:01:03 +03:00
|
|
|
}
|
|
|
|
|
2022-09-29 20:34:05 +02:00
|
|
|
// map legacy semantic texture names to texture identifiers
|
|
|
|
fragment_header += R"(
|
|
|
|
#define baseTexture texture0
|
|
|
|
#define normalTexture texture1
|
|
|
|
#define textureFlags texture2
|
|
|
|
)";
|
|
|
|
|
2024-12-29 18:58:26 +01:00
|
|
|
/// Unique name of this shader, for debug/logging
|
|
|
|
std::string log_name = name;
|
2025-04-17 23:48:08 +02:00
|
|
|
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());
|
2025-04-17 23:13:44 +02:00
|
|
|
}
|
2024-12-29 18:58:26 +01:00
|
|
|
|
2025-04-17 23:48:08 +02:00
|
|
|
ShaderConstants constants = input_const;
|
|
|
|
|
2024-01-16 23:09:18 +03:00
|
|
|
bool use_discard = fully_programmable;
|
2024-12-13 16:11:21 +01:00
|
|
|
if (!use_discard) {
|
|
|
|
// workaround for a certain OpenGL implementation lacking GL_ALPHA_TEST
|
|
|
|
const char *renderer = reinterpret_cast<const char*>(GL.GetString(GL.RENDERER));
|
|
|
|
if (strstr(renderer, "GC7000"))
|
|
|
|
use_discard = true;
|
|
|
|
}
|
2021-09-10 21:59:29 +02:00
|
|
|
if (use_discard) {
|
|
|
|
if (shaderinfo.base_material == video::EMT_TRANSPARENT_ALPHA_CHANNEL)
|
2025-04-17 23:13:44 +02:00
|
|
|
constants["USE_DISCARD"] = 1;
|
2021-09-10 21:59:29 +02:00
|
|
|
else if (shaderinfo.base_material == video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF)
|
2025-04-17 23:13:44 +02:00
|
|
|
constants["USE_DISCARD_REF"] = 1;
|
2025-01-13 09:39:20 +01:00
|
|
|
}
|
2020-12-19 22:57:10 +03:00
|
|
|
|
2025-04-17 23:13:44 +02:00
|
|
|
/* Let the constant setters do their job and emit constants */
|
|
|
|
for (auto &setter : m_constant_setters) {
|
|
|
|
setter->onGenerate(name, constants);
|
2024-12-13 16:11:21 +01:00
|
|
|
}
|
|
|
|
|
2025-04-17 23:13:44 +02:00
|
|
|
for (auto &it : constants) {
|
|
|
|
// spaces could cause duplicates
|
|
|
|
assert(trim(it.first) == it.first);
|
|
|
|
shaders_header << "#define " << it.first << ' ';
|
2025-04-17 23:48:08 +02:00
|
|
|
putConstant(shaders_header, it.second);
|
2025-04-17 23:13:44 +02:00
|
|
|
shaders_header << '\n';
|
2023-10-23 17:05:31 -07:00
|
|
|
}
|
|
|
|
|
2020-12-19 22:57:10 +03:00
|
|
|
std::string common_header = shaders_header.str();
|
2024-11-12 10:52:20 +01:00
|
|
|
const char *final_header = "#line 0\n"; // reset the line counter for meaningful diagnostics
|
2020-12-19 22:57:10 +03:00
|
|
|
|
|
|
|
std::string vertex_shader = m_sourcecache.getOrLoad(name, "opengl_vertex.glsl");
|
|
|
|
std::string fragment_shader = m_sourcecache.getOrLoad(name, "opengl_fragment.glsl");
|
|
|
|
std::string geometry_shader = m_sourcecache.getOrLoad(name, "opengl_geometry.glsl");
|
|
|
|
|
2024-11-12 10:52:20 +01:00
|
|
|
vertex_shader = common_header + vertex_header + final_header + vertex_shader;
|
|
|
|
fragment_shader = common_header + fragment_header + final_header + fragment_shader;
|
2020-12-19 22:57:10 +03:00
|
|
|
const char *geometry_shader_ptr = nullptr; // optional
|
|
|
|
if (!geometry_shader.empty()) {
|
2024-11-12 10:52:20 +01:00
|
|
|
geometry_shader = common_header + geometry_header + final_header + geometry_shader;
|
2020-12-19 22:57:10 +03:00
|
|
|
geometry_shader_ptr = geometry_shader.c_str();
|
|
|
|
}
|
|
|
|
|
2025-04-17 22:15:14 +02:00
|
|
|
auto cb = make_irr<ShaderCallback>(m_uniform_factories);
|
2024-12-29 18:58:26 +01:00
|
|
|
infostream << "Compiling high level shaders for " << log_name << std::endl;
|
2020-12-19 22:57:10 +03:00
|
|
|
s32 shadermat = gpu->addHighLevelShaderMaterial(
|
2024-12-29 18:58:26 +01:00
|
|
|
vertex_shader.c_str(), fragment_shader.c_str(), geometry_shader_ptr,
|
|
|
|
log_name.c_str(), scene::EPT_TRIANGLES, scene::EPT_TRIANGLES, 0,
|
|
|
|
cb.get(), shaderinfo.base_material);
|
2020-12-19 22:57:10 +03:00
|
|
|
if (shadermat == -1) {
|
2024-12-29 18:58:26 +01:00
|
|
|
errorstream << "generateShader(): failed to generate shaders for "
|
|
|
|
<< log_name << ", addHighLevelShaderMaterial failed." << std::endl;
|
2020-12-19 22:57:10 +03:00
|
|
|
dumpShaderProgram(warningstream, "Vertex", vertex_shader);
|
|
|
|
dumpShaderProgram(warningstream, "Fragment", fragment_shader);
|
|
|
|
dumpShaderProgram(warningstream, "Geometry", geometry_shader);
|
2024-01-19 11:51:46 +01:00
|
|
|
throw ShaderException(
|
2025-04-07 20:23:13 +02:00
|
|
|
fmtgettext("Failed to compile the \"%s\" shader.", log_name.c_str()) +
|
2024-01-19 11:51:46 +01:00
|
|
|
strgettext("\nCheck debug.txt for details."));
|
2012-03-19 02:59:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Apply the newly created material type
|
|
|
|
shaderinfo.material = (video::E_MATERIAL_TYPE) shadermat;
|
|
|
|
return shaderinfo;
|
|
|
|
}
|
|
|
|
|
2025-04-17 23:48:08 +02:00
|
|
|
/*
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2016-02-10 04:27:45 +01:00
|
|
|
void dumpShaderProgram(std::ostream &output_stream,
|
2024-02-17 15:35:33 +01:00
|
|
|
const std::string &program_type, std::string_view program)
|
2016-02-10 04:27:45 +01:00
|
|
|
{
|
|
|
|
output_stream << program_type << " shader program:" << std::endl <<
|
|
|
|
"----------------------------------" << std::endl;
|
|
|
|
size_t pos = 0;
|
|
|
|
size_t prev = 0;
|
|
|
|
s16 line = 1;
|
2017-08-19 14:25:35 +02:00
|
|
|
while ((pos = program.find('\n', prev)) != std::string::npos) {
|
2016-02-10 04:27:45 +01:00
|
|
|
output_stream << line++ << ": "<< program.substr(prev, pos - prev) <<
|
|
|
|
std::endl;
|
|
|
|
prev = pos + 1;
|
|
|
|
}
|
|
|
|
output_stream << line << ": " << program.substr(prev) << std::endl <<
|
|
|
|
"End of " << program_type << " shader program." << std::endl <<
|
|
|
|
" " << std::endl;
|
|
|
|
}
|