Fix a memleak pointed by @Zeno- in MeshUpdateQueue
[oweals/minetest.git] / src / shader.cpp
index 917d878bb213b510f7564ab9de68092857f97ee8..66f32c9a1846bc6e6439f63829841bbc72306698 100644 (file)
@@ -35,7 +35,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "EShaderTypes.h"
 #include "log.h"
 #include "gamedef.h"
-#include "strfnd.h" // trim()
 #include "client/tile.h"
 
 /*
@@ -168,29 +167,27 @@ private:
        }
 };
 
+
 /*
        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 (u32 i = 0; i < factories.size(); ++i)
+                       m_setters.push_back(factories[i]->create());
+       }
+
+       ~ShaderCallback()
+       {
+               for (u32 i = 0; i < m_setters.size(); ++i)
+                       delete m_setters[i];
+       }
 
        virtual void OnSetConstants(video::IMaterialRendererServices *services, s32 userData)
        {
@@ -199,18 +196,25 @@ public:
 
                bool is_highlevel = userData;
 
-               m_scsr->onSetConstants(services, is_highlevel, m_name);
+               for (u32 i = 0; i < m_setters.size(); ++i)
+                       m_setters[i]->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() {}
 
@@ -220,47 +224,40 @@ public:
                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);
@@ -303,22 +300,17 @@ public:
        // 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;
 
        // Cache of source shaders
        // This should be only accessed from the main thread
@@ -333,9 +325,11 @@ private:
        // 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)
@@ -346,10 +340,10 @@ IWritableShaderSource* createShaderSource(IrrlichtDevice *device)
 /*
        Generate shader given the shader name.
 */
-ShaderInfo generate_shader(std::string name,
+ShaderInfo generate_shader(const std::string &name,
                u8 material_type, u8 drawtype,
-               IrrlichtDevice *device,
-               video::IShaderConstantSetCallBack *callback,
+               IrrlichtDevice *device, std::vector<ShaderCallback *> &callbacks,
+               const std::vector<IShaderConstantSetterFactory*> &setter_factories,
                SourceShaderCache *sourcecache);
 
 /*
@@ -365,28 +359,24 @@ ShaderSource::ShaderSource(IrrlichtDevice *device):
 {
        assert(m_device); // Pre-condition
 
-       m_shader_callback = new ShaderCallback(this, "default");
-
        m_main_thread = thr_get_current_thread_id();
 
        // Add a dummy ShaderInfo as the first index, named ""
        m_shaderinfo_cache.push_back(ShaderInfo());
 
        // 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) {
+       for (std::vector<ShaderCallback *>::iterator iter = m_callbacks.begin();
+                       iter != m_callbacks.end(); ++iter) {
                delete *iter;
        }
-       m_global_setters.clear();
-
-       if (m_shader_callback) {
-               m_shader_callback->drop();
-               m_shader_callback = NULL;
+       for (std::vector<IShaderConstantSetterFactory *>::iterator iter = m_setter_factories.begin();
+                       iter != m_setter_factories.end(); ++iter) {
+               delete *iter;
        }
 }
 
@@ -462,8 +452,8 @@ u32 ShaderSource::getShaderIdDirect(const std::string &name,
                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_device, m_callbacks, m_setter_factories, &m_sourcecache);
 
        /*
                Add shader to caches (add dummy shaders too)
@@ -528,22 +518,16 @@ void ShaderSource::rebuildShaders()
                ShaderInfo *info = &m_shaderinfo_cache[i];
                if(info->name != ""){
                        *info = generate_shader(info->name, info->material_type,
-                                       info->drawtype, m_device, m_shader_callback, &m_sourcecache);
+                                       info->drawtype, m_device, 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,
+               IrrlichtDevice *device, std::vector<ShaderCallback *> &callbacks,
+               const std::vector<IShaderConstantSetterFactory*> &setter_factories,
                SourceShaderCache *sourcecache)
 {
        ShaderInfo shaderinfo;
@@ -559,7 +543,7 @@ ShaderInfo generate_shader(std::string name, u8 material_type, u8 drawtype,
                        shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
                        break;
                case TILE_MATERIAL_LIQUID_TRANSPARENT:
-                       shaderinfo.base_material = video::EMT_TRANSPARENT_VERTEX_ALPHA;
+                       shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
                        break;
                case TILE_MATERIAL_LIQUID_OPAQUE:
                        shaderinfo.base_material = video::EMT_SOLID;
@@ -573,7 +557,7 @@ ShaderInfo generate_shader(std::string name, u8 material_type, u8 drawtype,
        }
 
        bool enable_shaders = g_settings->getBool("enable_shaders");
-       if(!enable_shaders)
+       if (!enable_shaders)
                return shaderinfo;
 
        video::IVideoDriver* driver = device->getVideoDriver();
@@ -764,22 +748,30 @@ ShaderInfo generate_shader(std::string name, u8 material_type, u8 drawtype,
        else
                shaders_header += "0\n";
 
-       if(pixel_program != "")
-               pixel_program = shaders_header + pixel_program;
-       if(vertex_program != "")
-               vertex_program = shaders_header + vertex_program;
-       if(geometry_program != "")
-               geometry_program = shaders_header + geometry_program;
+       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;
        const c8* geometry_program_ptr = 0;
-       if(vertex_program != "")
+       if (!vertex_program.empty()) {
+               vertex_program = shaders_header + vertex_program;
                vertex_program_ptr = vertex_program.c_str();
-       if(pixel_program != "")
+       }
+       if (!pixel_program.empty()) {
+               pixel_program = shaders_header + pixel_program;
                pixel_program_ptr = pixel_program.c_str();
-       if(geometry_program != "")
+       }
+       if (!geometry_program.empty()) {
+               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;
@@ -789,14 +781,14 @@ ShaderInfo generate_shader(std::string name, u8 material_type, u8 drawtype,
                        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
                        );
@@ -805,6 +797,10 @@ ShaderInfo generate_shader(std::string name, u8 material_type, u8 drawtype,
                                        "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;
                }
        }
@@ -813,7 +809,7 @@ ShaderInfo generate_shader(std::string name, u8 material_type, u8 drawtype,
                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
                        );
@@ -823,9 +819,13 @@ ShaderInfo generate_shader(std::string name, u8 material_type, u8 drawtype,
                                        "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
@@ -868,3 +868,21 @@ void load_shaders(std::string name, SourceShaderCache *sourcecache,
        }
 
 }
+
+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;
+}