#include <IMaterialRenderer.h>
#include <IMaterialRendererServices.h>
#include <IShaderConstantSetCallBack.h>
+#include "client/renderingengine.h"
#include "EShaderTypes.h"
#include "log.h"
#include "gamedef.h"
-#include "strfnd.h" // trim()
#include "client/tile.h"
/*
const std::string &filename)
{
std::string combined = name_of_shader + DIR_DELIM + filename;
- std::string fullpath = "";
+ std::string fullpath;
/*
Check from cache
*/
Check from shader_path
*/
std::string shader_path = g_settings->get("shader_path");
- if(shader_path != "")
- {
+ if (!shader_path.empty()) {
std::string testpath = shader_path + DIR_DELIM + combined;
if(fs::PathExists(testpath))
fullpath = testpath;
/*
Check from default data directory
*/
- if(fullpath == "")
- {
+ if (fullpath.empty()) {
std::string rel_path = std::string("client") + DIR_DELIM
+ "shaders" + DIR_DELIM
+ name_of_shader + DIR_DELIM
// Try to use local shader instead if asked to
if(prefer_local){
std::string path = getShaderPath(name_of_shader, filename);
- if(path != ""){
+ if(!path.empty()){
std::string p = readFile(path);
- if(p != ""){
+ if (!p.empty()) {
m_programs[combined] = p;
return;
}
if (n != m_programs.end())
return n->second;
std::string path = getShaderPath(name_of_shader, filename);
- if (path == "") {
+ if (path.empty()) {
infostream << "SourceShaderCache::getOrLoad(): No path found for \""
<< combined << "\"" << std::endl;
return "";
infostream << "SourceShaderCache::getOrLoad(): Loading path \""
<< path << "\"" << std::endl;
std::string p = readFile(path);
- if (p != "") {
+ if (!p.empty()) {
m_programs[combined] = p;
return p;
}
}
};
+
/*
ShaderCallback: Sets constants that can be used in shaders
*/
-class IShaderConstantSetterRegistry
-{
-public:
- virtual ~IShaderConstantSetterRegistry(){};
- virtual void onSetConstants(video::IMaterialRendererServices *services,
- bool is_highlevel, const std::string &name) = 0;
-};
-
class ShaderCallback : public video::IShaderConstantSetCallBack
{
- IShaderConstantSetterRegistry *m_scsr;
- std::string m_name;
+ std::vector<IShaderConstantSetter*> m_setters;
public:
- ShaderCallback(IShaderConstantSetterRegistry *scsr, const std::string &name):
- m_scsr(scsr),
- m_name(name)
- {}
- ~ShaderCallback() {}
+ ShaderCallback(const std::vector<IShaderConstantSetterFactory *> &factories)
+ {
+ for (IShaderConstantSetterFactory *factory : factories)
+ m_setters.push_back(factory->create());
+ }
+
+ ~ShaderCallback()
+ {
+ for (IShaderConstantSetter *setter : m_setters)
+ delete setter;
+ }
virtual void OnSetConstants(video::IMaterialRendererServices *services, s32 userData)
{
bool is_highlevel = userData;
- m_scsr->onSetConstants(services, is_highlevel, m_name);
+ for (IShaderConstantSetter *setter : m_setters)
+ setter->onSetConstants(services, is_highlevel);
}
};
+
/*
MainShaderConstantSetter: Set basic constants required for almost everything
*/
class MainShaderConstantSetter : public IShaderConstantSetter
{
+ CachedVertexShaderSetting<float, 16> m_world_view_proj;
+ CachedVertexShaderSetting<float, 16> m_world;
+
public:
- MainShaderConstantSetter(IrrlichtDevice *device)
+ MainShaderConstantSetter() :
+ m_world_view_proj("mWorldViewProj"),
+ m_world("mWorld")
{}
- ~MainShaderConstantSetter() {}
+ ~MainShaderConstantSetter() = default;
virtual void onSetConstants(video::IMaterialRendererServices *services,
bool is_highlevel)
video::IVideoDriver *driver = services->getVideoDriver();
sanity_check(driver);
- // set inverted world matrix
- core::matrix4 invWorld = driver->getTransform(video::ETS_WORLD);
- invWorld.makeInverse();
- if(is_highlevel)
- services->setVertexShaderConstant("mInvWorld", invWorld.pointer(), 16);
- else
- services->setVertexShaderConstant(invWorld.pointer(), 0, 4);
-
- // set clip matrix
+ // Set clip matrix
core::matrix4 worldViewProj;
worldViewProj = driver->getTransform(video::ETS_PROJECTION);
worldViewProj *= driver->getTransform(video::ETS_VIEW);
worldViewProj *= driver->getTransform(video::ETS_WORLD);
- if(is_highlevel)
- services->setVertexShaderConstant("mWorldViewProj", worldViewProj.pointer(), 16);
+ if (is_highlevel)
+ m_world_view_proj.set(*reinterpret_cast<float(*)[16]>(worldViewProj.pointer()), services);
else
- services->setVertexShaderConstant(worldViewProj.pointer(), 4, 4);
+ services->setVertexShaderConstant(worldViewProj.pointer(), 0, 4);
- // set transposed world matrix
- core::matrix4 transWorld = driver->getTransform(video::ETS_WORLD);
- transWorld = transWorld.getTransposed();
- if(is_highlevel)
- services->setVertexShaderConstant("mTransWorld", transWorld.pointer(), 16);
- else
- services->setVertexShaderConstant(transWorld.pointer(), 8, 4);
-
- // set world matrix
+ // Set world matrix
core::matrix4 world = driver->getTransform(video::ETS_WORLD);
- if(is_highlevel)
- services->setVertexShaderConstant("mWorld", world.pointer(), 16);
+ if (is_highlevel)
+ m_world.set(*reinterpret_cast<float(*)[16]>(world.pointer()), services);
else
- services->setVertexShaderConstant(world.pointer(), 8, 4);
+ services->setVertexShaderConstant(world.pointer(), 4, 4);
}
};
+
+class MainShaderConstantSetterFactory : public IShaderConstantSetterFactory
+{
+public:
+ virtual IShaderConstantSetter* create()
+ { return new MainShaderConstantSetter(); }
+};
+
+
/*
ShaderSource
*/
-class ShaderSource : public IWritableShaderSource, public IShaderConstantSetterRegistry
+class ShaderSource : public IWritableShaderSource
{
public:
- ShaderSource(IrrlichtDevice *device);
+ ShaderSource();
~ShaderSource();
/*
// Shall be called from the main thread.
void rebuildShaders();
- void addGlobalConstantSetter(IShaderConstantSetter *setter)
+ void addShaderConstantSetterFactory(IShaderConstantSetterFactory *setter)
{
- m_global_setters.push_back(setter);
+ m_setter_factories.push_back(setter);
}
- void onSetConstants(video::IMaterialRendererServices *services,
- bool is_highlevel, const std::string &name);
-
private:
// The id of the thread that is allowed to use irrlicht directly
- threadid_t m_main_thread;
- // The irrlicht device
- IrrlichtDevice *m_device;
- // The set-constants callback
- ShaderCallback *m_shader_callback;
+ std::thread::id m_main_thread;
// Cache of source shaders
// This should be only accessed from the main thread
// The first position contains a dummy shader.
std::vector<ShaderInfo> m_shaderinfo_cache;
// The former container is behind this mutex
- Mutex m_shaderinfo_cache_mutex;
+ std::mutex m_shaderinfo_cache_mutex;
// Queued shader fetches (to be processed by the main thread)
RequestQueue<std::string, u32, u8, u8> m_get_shader_queue;
- // Global constant setters
- // TODO: Delete these in the destructor
- std::vector<IShaderConstantSetter*> m_global_setters;
+ // Global constant setter factories
+ std::vector<IShaderConstantSetterFactory *> m_setter_factories;
+
+ // Shader callbacks
+ std::vector<ShaderCallback *> m_callbacks;
};
-IWritableShaderSource* createShaderSource(IrrlichtDevice *device)
+IWritableShaderSource *createShaderSource()
{
- return new ShaderSource(device);
+ return new ShaderSource();
}
/*
Generate shader given the shader name.
*/
-ShaderInfo generate_shader(std::string name,
- u8 material_type, u8 drawtype,
- IrrlichtDevice *device,
- video::IShaderConstantSetCallBack *callback,
+ShaderInfo generate_shader(const std::string &name,
+ u8 material_type, u8 drawtype, std::vector<ShaderCallback *> &callbacks,
+ const std::vector<IShaderConstantSetterFactory *> &setter_factories,
SourceShaderCache *sourcecache);
/*
Load shader programs
*/
-void load_shaders(std::string name, SourceShaderCache *sourcecache,
+void load_shaders(const std::string &name, SourceShaderCache *sourcecache,
video::E_DRIVER_TYPE drivertype, bool enable_shaders,
std::string &vertex_program, std::string &pixel_program,
std::string &geometry_program, bool &is_highlevel);
-ShaderSource::ShaderSource(IrrlichtDevice *device):
- m_device(device)
+ShaderSource::ShaderSource()
{
- assert(m_device); // Pre-condition
-
- m_shader_callback = new ShaderCallback(this, "default");
-
- m_main_thread = thr_get_current_thread_id();
+ m_main_thread = std::this_thread::get_id();
// Add a dummy ShaderInfo as the first index, named ""
- m_shaderinfo_cache.push_back(ShaderInfo());
+ m_shaderinfo_cache.emplace_back();
// Add main global constant setter
- addGlobalConstantSetter(new MainShaderConstantSetter(device));
+ addShaderConstantSetterFactory(new MainShaderConstantSetterFactory());
}
ShaderSource::~ShaderSource()
{
- for (std::vector<IShaderConstantSetter*>::iterator iter = m_global_setters.begin();
- iter != m_global_setters.end(); ++iter) {
- delete *iter;
+ for (ShaderCallback *callback : m_callbacks) {
+ delete callback;
}
- m_global_setters.clear();
-
- if (m_shader_callback) {
- m_shader_callback->drop();
- m_shader_callback = NULL;
+ for (IShaderConstantSetterFactory *setter_factorie : m_setter_factories) {
+ delete setter_factorie;
}
}
Get shader
*/
- if (thr_is_current_thread(m_main_thread)) {
+ if (std::this_thread::get_id() == m_main_thread) {
return getShaderIdDirect(name, material_type, drawtype);
- } else {
- /*errorstream<<"getShader(): Queued: name=\""<<name<<"\""<<std::endl;*/
+ }
- // We're gonna ask the result to be put into here
+ /*errorstream<<"getShader(): Queued: name=\""<<name<<"\""<<std::endl;*/
- static ResultQueue<std::string, u32, u8, u8> result_queue;
+ // We're gonna ask the result to be put into here
- // Throw a request in
- m_get_shader_queue.add(name, 0, 0, &result_queue);
+ static ResultQueue<std::string, u32, u8, u8> result_queue;
- /* infostream<<"Waiting for shader from main thread, name=\""
- <<name<<"\""<<std::endl;*/
+ // Throw a request in
+ m_get_shader_queue.add(name, 0, 0, &result_queue);
- while(true) {
- GetResult<std::string, u32, u8, u8>
- result = result_queue.pop_frontNoEx();
+ /* infostream<<"Waiting for shader from main thread, name=\""
+ <<name<<"\""<<std::endl;*/
- if (result.key == name) {
- return result.item;
- }
- else {
- errorstream << "Got shader with invalid name: " << result.key << std::endl;
- }
+ while(true) {
+ GetResult<std::string, u32, u8, u8>
+ result = result_queue.pop_frontNoEx();
+
+ if (result.key == name) {
+ return result.item;
}
+ errorstream << "Got shader with invalid name: " << result.key << std::endl;
}
- infostream<<"getShader(): Failed"<<std::endl;
+ infostream << "getShader(): Failed" << std::endl;
return 0;
}
//infostream<<"getShaderIdDirect(): name=\""<<name<<"\""<<std::endl;
// Empty name means shader 0
- if(name == ""){
+ if (name.empty()) {
infostream<<"getShaderIdDirect(): name is empty"<<std::endl;
return 0;
}
/*
Calling only allowed from main thread
*/
- if (!thr_is_current_thread(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 = generate_shader(name, material_type, drawtype, m_device,
- m_shader_callback, &m_sourcecache);
+ ShaderInfo info = generate_shader(name, material_type, drawtype,
+ m_callbacks, m_setter_factories, &m_sourcecache);
/*
Add shader to caches (add dummy shaders too)
"name_of_shader=\""<<name_of_shader<<"\", "
"filename=\""<<filename<<"\""<<std::endl;*/
- sanity_check(thr_is_current_thread(m_main_thread));
+ sanity_check(std::this_thread::get_id() == m_main_thread);
m_sourcecache.insert(name_of_shader, filename, program, true);
}
*/
// Recreate shaders
- for(u32 i=0; i<m_shaderinfo_cache.size(); i++){
- ShaderInfo *info = &m_shaderinfo_cache[i];
- if(info->name != ""){
+ for (ShaderInfo &i : m_shaderinfo_cache) {
+ ShaderInfo *info = &i;
+ if (!info->name.empty()) {
*info = generate_shader(info->name, info->material_type,
- info->drawtype, m_device, m_shader_callback, &m_sourcecache);
+ info->drawtype, m_callbacks,
+ m_setter_factories, &m_sourcecache);
}
}
}
-void ShaderSource::onSetConstants(video::IMaterialRendererServices *services,
- bool is_highlevel, const std::string &name)
-{
- for(u32 i=0; i<m_global_setters.size(); i++){
- IShaderConstantSetter *setter = m_global_setters[i];
- setter->onSetConstants(services, is_highlevel);
- }
-}
-ShaderInfo generate_shader(std::string name, u8 material_type, u8 drawtype,
- IrrlichtDevice *device, video::IShaderConstantSetCallBack *callback,
+ShaderInfo generate_shader(const std::string &name, u8 material_type, u8 drawtype,
+ std::vector<ShaderCallback *> &callbacks,
+ const std::vector<IShaderConstantSetterFactory *> &setter_factories,
SourceShaderCache *sourcecache)
{
ShaderInfo shaderinfo;
shaderinfo.material_type = material_type;
shaderinfo.drawtype = drawtype;
shaderinfo.material = video::EMT_SOLID;
- switch(material_type){
- case TILE_MATERIAL_BASIC:
- shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
- break;
- case TILE_MATERIAL_ALPHA:
- shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
- break;
- case TILE_MATERIAL_LIQUID_TRANSPARENT:
- shaderinfo.base_material = video::EMT_TRANSPARENT_VERTEX_ALPHA;
- break;
- case TILE_MATERIAL_LIQUID_OPAQUE:
- shaderinfo.base_material = video::EMT_SOLID;
- break;
- case TILE_MATERIAL_WAVING_LEAVES:
- shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
- break;
- case TILE_MATERIAL_WAVING_PLANTS:
- shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
+ switch (material_type) {
+ case TILE_MATERIAL_OPAQUE:
+ case TILE_MATERIAL_LIQUID_OPAQUE:
+ shaderinfo.base_material = video::EMT_SOLID;
+ break;
+ case TILE_MATERIAL_ALPHA:
+ case TILE_MATERIAL_LIQUID_TRANSPARENT:
+ shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
+ break;
+ case TILE_MATERIAL_BASIC:
+ case TILE_MATERIAL_WAVING_LEAVES:
+ case TILE_MATERIAL_WAVING_PLANTS:
+ shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
break;
}
bool enable_shaders = g_settings->getBool("enable_shaders");
- if(!enable_shaders)
+ if (!enable_shaders)
return shaderinfo;
- video::IVideoDriver* driver = device->getVideoDriver();
- sanity_check(driver);
+ video::IVideoDriver *driver = RenderingEngine::get_video_driver();
video::IGPUProgrammingServices *gpu = driver->getGPUProgrammingServices();
if(!gpu){
enable_shaders, vertex_program, pixel_program,
geometry_program, is_highlevel);
// Check hardware/driver support
- if(vertex_program != "" &&
+ if (!vertex_program.empty() &&
!driver->queryFeature(video::EVDF_VERTEX_SHADER_1_1) &&
!driver->queryFeature(video::EVDF_ARB_VERTEX_PROGRAM_1)){
infostream<<"generate_shader(): vertex shaders disabled "
<<std::endl;
vertex_program = "";
}
- if(pixel_program != "" &&
+ if (!pixel_program.empty() &&
!driver->queryFeature(video::EVDF_PIXEL_SHADER_1_1) &&
!driver->queryFeature(video::EVDF_ARB_FRAGMENT_PROGRAM_1)){
infostream<<"generate_shader(): pixel shaders disabled "
<<std::endl;
pixel_program = "";
}
- if(geometry_program != "" &&
+ if (!geometry_program.empty() &&
!driver->queryFeature(video::EVDF_GEOMETRY_SHADER)){
infostream<<"generate_shader(): geometry shaders disabled "
"because of missing driver/hardware support."
}
// If no shaders are used, don't make a separate material type
- if(vertex_program == "" && pixel_program == "" && geometry_program == "")
+ if (vertex_program.empty() && pixel_program.empty() && geometry_program.empty())
return shaderinfo;
// Create shaders header
"NDT_NODEBOX",
"NDT_GLASSLIKE_FRAMED",
"NDT_FIRELIKE",
- "NDT_GLASSLIKE_FRAMED_OPTIONAL"
+ "NDT_GLASSLIKE_FRAMED_OPTIONAL",
+ "NDT_PLANTLIKE_ROOTED",
};
for (int i = 0; i < 14; i++){
"TILE_MATERIAL_LIQUID_TRANSPARENT",
"TILE_MATERIAL_LIQUID_OPAQUE",
"TILE_MATERIAL_WAVING_LEAVES",
- "TILE_MATERIAL_WAVING_PLANTS"
+ "TILE_MATERIAL_WAVING_PLANTS",
+ "TILE_MATERIAL_OPAQUE"
};
- for (int i = 0; i < 6; i++){
+ for (int i = 0; i < 7; i++){
shaders_header += "#define ";
shaders_header += materialTypes[i];
shaders_header += " ";
if (g_settings->getBool("tone_mapping"))
shaders_header += "#define ENABLE_TONE_MAPPING\n";
+ shaders_header += "#define FOG_START ";
+ shaders_header += ftos(rangelim(g_settings->getFloat("fog_start"), 0.0f, 0.99f));
+ shaders_header += "\n";
+
// Call addHighLevelShaderMaterial() or addShaderMaterial()
const c8* vertex_program_ptr = 0;
const c8* pixel_program_ptr = 0;
geometry_program = shaders_header + geometry_program;
geometry_program_ptr = geometry_program.c_str();
}
+ ShaderCallback *cb = new ShaderCallback(setter_factories);
s32 shadermat = -1;
if(is_highlevel){
infostream<<"Compiling high level shaders for "<<name<<std::endl;
video::EVST_VS_1_1, // Vertex shader version
pixel_program_ptr, // Pixel shader program
"pixelMain", // Pixel shader entry point
- video::EPST_PS_1_1, // Pixel shader version
+ video::EPST_PS_1_2, // Pixel shader version
geometry_program_ptr, // Geometry shader program
"geometryMain", // Geometry shader entry point
video::EGST_GS_4_0, // Geometry shader version
scene::EPT_TRIANGLES, // Geometry shader input
scene::EPT_TRIANGLE_STRIP, // Geometry shader output
0, // Support maximum number of vertices
- callback, // Set-constant callback
+ cb, // Set-constant callback
shaderinfo.base_material, // Base material
1 // Userdata passed to callback
);
"failed to generate \""<<name<<"\", "
"addHighLevelShaderMaterial failed."
<<std::endl;
+ dumpShaderProgram(warningstream, "Vertex", vertex_program);
+ dumpShaderProgram(warningstream, "Pixel", pixel_program);
+ dumpShaderProgram(warningstream, "Geometry", geometry_program);
+ delete cb;
return shaderinfo;
}
}
shadermat = gpu->addShaderMaterial(
vertex_program_ptr, // Vertex shader program
pixel_program_ptr, // Pixel shader program
- callback, // Set-constant callback
+ cb, // Set-constant callback
shaderinfo.base_material, // Base material
0 // Userdata passed to callback
);
"failed to generate \""<<name<<"\", "
"addShaderMaterial failed."
<<std::endl;
+ dumpShaderProgram(warningstream, "Vertex", vertex_program);
+ dumpShaderProgram(warningstream,"Pixel", pixel_program);
+ delete cb;
return shaderinfo;
}
}
+ callbacks.push_back(cb);
// HACK, TODO: investigate this better
// Grab the material renderer once more so minetest doesn't crash on exit
return shaderinfo;
}
-void load_shaders(std::string name, SourceShaderCache *sourcecache,
+void load_shaders(const std::string &name, SourceShaderCache *sourcecache,
video::E_DRIVER_TYPE drivertype, bool enable_shaders,
std::string &vertex_program, std::string &pixel_program,
std::string &geometry_program, bool &is_highlevel)
pixel_program = sourcecache->getOrLoad(name, "opengl_fragment.glsl");
geometry_program = sourcecache->getOrLoad(name, "opengl_geometry.glsl");
}
- if(vertex_program != "" || pixel_program != "" || geometry_program != ""){
+ if (!vertex_program.empty() || !pixel_program.empty() || !geometry_program.empty()){
is_highlevel = true;
return;
}
}
}
+
+void dumpShaderProgram(std::ostream &output_stream,
+ const std::string &program_type, const std::string &program)
+{
+ output_stream << program_type << " shader program:" << std::endl <<
+ "----------------------------------" << std::endl;
+ size_t pos = 0;
+ size_t prev = 0;
+ s16 line = 1;
+ while ((pos = program.find('\n', prev)) != std::string::npos) {
+ 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;
+}