3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4 Copyright (C) 2013 Kahrl <kahrl@gmx.net>
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License along
17 with this program; if not, write to the Free Software Foundation, Inc.,
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include "irrlichttypes_extrabloated.h"
24 #include "main.h" // for g_settings
26 #include "util/container.h"
27 #include "util/thread.h"
30 #include <ICameraSceneNode.h>
31 #include <IGPUProgrammingServices.h>
32 #include <IMaterialRenderer.h>
33 #include <IMaterialRendererServices.h>
34 #include <IShaderConstantSetCallBack.h>
35 #include "EShaderTypes.h"
38 #include "strfnd.h" // trim()
41 A cache from shader name to shader path
43 MutexedMap<std::string, std::string> g_shadername_to_path_cache;
46 Gets the path to a shader by first checking if the file
47 name_of_shader/filename
48 exists in shader_path and if not, using the data path.
50 If not found, returns "".
52 Utilizes a thread-safe cache.
54 std::string getShaderPath(const std::string &name_of_shader,
55 const std::string &filename)
57 std::string combined = name_of_shader + DIR_DELIM + filename;
58 std::string fullpath = "";
62 bool incache = g_shadername_to_path_cache.get(combined, &fullpath);
67 Check from shader_path
69 std::string shader_path = g_settings->get("shader_path");
72 std::string testpath = shader_path + DIR_DELIM + combined;
73 if(fs::PathExists(testpath))
78 Check from default data directory
82 std::string rel_path = std::string("client") + DIR_DELIM
83 + "shaders" + DIR_DELIM
84 + name_of_shader + DIR_DELIM
86 std::string testpath = porting::path_share + DIR_DELIM + rel_path;
87 if(fs::PathExists(testpath))
91 // Add to cache (also an empty result is cached)
92 g_shadername_to_path_cache.set(combined, fullpath);
99 SourceShaderCache: A cache used for storing source shaders.
102 class SourceShaderCache
105 void insert(const std::string &name_of_shader,
106 const std::string &filename,
107 const std::string &program,
110 std::string combined = name_of_shader + DIR_DELIM + filename;
111 // Try to use local shader instead if asked to
113 std::string path = getShaderPath(name_of_shader, filename);
115 std::string p = readFile(path);
117 m_programs[combined] = p;
122 m_programs[combined] = program;
124 std::string get(const std::string &name_of_shader,
125 const std::string &filename)
127 std::string combined = name_of_shader + DIR_DELIM + filename;
128 std::map<std::string, std::string>::iterator n;
129 n = m_programs.find(combined);
130 if(n != m_programs.end())
134 // Primarily fetches from cache, secondarily tries to read from filesystem
135 std::string getOrLoad(const std::string &name_of_shader,
136 const std::string &filename)
138 std::string combined = name_of_shader + DIR_DELIM + filename;
139 std::map<std::string, std::string>::iterator n;
140 n = m_programs.find(combined);
141 if(n != m_programs.end())
143 std::string path = getShaderPath(name_of_shader, filename);
145 infostream<<"SourceShaderCache::getOrLoad(): No path found for \""
146 <<combined<<"\""<<std::endl;
149 infostream<<"SourceShaderCache::getOrLoad(): Loading path \""<<path
151 std::string p = readFile(path);
153 m_programs[combined] = p;
159 std::map<std::string, std::string> m_programs;
160 std::string readFile(const std::string &path)
162 std::ifstream is(path.c_str(), std::ios::binary);
165 std::ostringstream tmp_os;
166 tmp_os << is.rdbuf();
172 ShaderCallback: Sets constants that can be used in shaders
175 class IShaderConstantSetterRegistry
178 virtual ~IShaderConstantSetterRegistry(){};
179 virtual void onSetConstants(video::IMaterialRendererServices *services,
180 bool is_highlevel, const std::string &name) = 0;
183 class ShaderCallback : public video::IShaderConstantSetCallBack
185 IShaderConstantSetterRegistry *m_scsr;
189 ShaderCallback(IShaderConstantSetterRegistry *scsr, const std::string &name):
195 virtual void OnSetConstants(video::IMaterialRendererServices *services, s32 userData)
197 video::IVideoDriver *driver = services->getVideoDriver();
200 bool is_highlevel = userData;
202 m_scsr->onSetConstants(services, is_highlevel, m_name);
207 MainShaderConstantSetter: Set basic constants required for almost everything
210 class MainShaderConstantSetter : public IShaderConstantSetter
213 MainShaderConstantSetter(IrrlichtDevice *device):
216 ~MainShaderConstantSetter() {}
218 virtual void onSetConstants(video::IMaterialRendererServices *services,
221 video::IVideoDriver *driver = services->getVideoDriver();
224 // set inverted world matrix
225 core::matrix4 invWorld = driver->getTransform(video::ETS_WORLD);
226 invWorld.makeInverse();
228 services->setVertexShaderConstant("mInvWorld", invWorld.pointer(), 16);
230 services->setVertexShaderConstant(invWorld.pointer(), 0, 4);
233 core::matrix4 worldViewProj;
234 worldViewProj = driver->getTransform(video::ETS_PROJECTION);
235 worldViewProj *= driver->getTransform(video::ETS_VIEW);
236 worldViewProj *= driver->getTransform(video::ETS_WORLD);
238 services->setVertexShaderConstant("mWorldViewProj", worldViewProj.pointer(), 16);
240 services->setVertexShaderConstant(worldViewProj.pointer(), 4, 4);
242 // set transposed world matrix
243 core::matrix4 world = driver->getTransform(video::ETS_WORLD);
244 world = world.getTransposed();
246 services->setVertexShaderConstant("mTransWorld", world.pointer(), 16);
248 services->setVertexShaderConstant(world.pointer(), 8, 4);
252 IrrlichtDevice *m_device;
259 class ShaderSource : public IWritableShaderSource, public IShaderConstantSetterRegistry
262 ShaderSource(IrrlichtDevice *device);
266 Gets a shader material id from cache or
267 - if main thread, from getShaderIdDirect
268 - if other thread, adds to request queue and waits for main thread
270 u32 getShaderId(const std::string &name);
273 - If shader material specified by name is found from cache,
274 return the cached id.
275 - Otherwise generate the shader material, add to cache and return id.
277 The id 0 points to a null shader. Its material is EMT_SOLID.
279 u32 getShaderIdDirect(const std::string &name);
281 // Finds out the name of a cached shader.
282 std::string getShaderName(u32 id);
285 If shader specified by the name pointed by the id doesn't
286 exist, create it, then return the cached shader.
288 Can be called from any thread. If called from some other thread
289 and not found in cache, the call is queued to the main thread
292 ShaderInfo getShader(u32 id);
294 ShaderInfo getShader(const std::string &name)
296 return getShader(getShaderId(name));
299 // Processes queued shader requests from other threads.
300 // Shall be called from the main thread.
303 // Insert a shader program into the cache without touching the
304 // filesystem. Shall be called from the main thread.
305 void insertSourceShader(const std::string &name_of_shader,
306 const std::string &filename, const std::string &program);
308 // Rebuild shaders from the current set of source shaders
309 // Shall be called from the main thread.
310 void rebuildShaders();
312 void addGlobalConstantSetter(IShaderConstantSetter *setter)
314 m_global_setters.push_back(setter);
317 void onSetConstants(video::IMaterialRendererServices *services,
318 bool is_highlevel, const std::string &name);
322 // The id of the thread that is allowed to use irrlicht directly
323 threadid_t m_main_thread;
324 // The irrlicht device
325 IrrlichtDevice *m_device;
326 // The set-constants callback
327 ShaderCallback *m_shader_callback;
329 // Cache of source shaders
330 // This should be only accessed from the main thread
331 SourceShaderCache m_sourcecache;
333 // A shader id is index in this array.
334 // The first position contains a dummy shader.
335 std::vector<ShaderInfo> m_shaderinfo_cache;
336 // Maps a shader name to an index in the former.
337 std::map<std::string, u32> m_name_to_id;
338 // The two former containers are behind this mutex
339 JMutex m_shaderinfo_cache_mutex;
341 // Queued shader fetches (to be processed by the main thread)
342 RequestQueue<std::string, u32, u8, u8> m_get_shader_queue;
344 // Global constant setters
345 // TODO: Delete these in the destructor
346 std::vector<IShaderConstantSetter*> m_global_setters;
349 IWritableShaderSource* createShaderSource(IrrlichtDevice *device)
351 return new ShaderSource(device);
355 Generate shader given the shader name.
357 ShaderInfo generate_shader(std::string name, IrrlichtDevice *device,
358 video::IShaderConstantSetCallBack *callback,
359 SourceShaderCache *sourcecache);
364 void load_shaders(std::string name, SourceShaderCache *sourcecache,
365 video::E_DRIVER_TYPE drivertype, bool enable_shaders,
366 std::string &vertex_program, std::string &pixel_program,
367 std::string &geometry_program, bool &is_highlevel);
369 ShaderSource::ShaderSource(IrrlichtDevice *device):
374 m_shader_callback = new ShaderCallback(this, "default");
376 m_main_thread = get_current_thread_id();
378 // Add a dummy ShaderInfo as the first index, named ""
379 m_shaderinfo_cache.push_back(ShaderInfo());
380 m_name_to_id[""] = 0;
382 // Add main global constant setter
383 addGlobalConstantSetter(new MainShaderConstantSetter(device));
386 ShaderSource::~ShaderSource()
388 //m_shader_callback->drop();
390 for (std::vector<IShaderConstantSetter*>::iterator iter = m_global_setters.begin();
391 iter != m_global_setters.end(); iter++) {
394 m_global_setters.clear();
397 u32 ShaderSource::getShaderId(const std::string &name)
399 //infostream<<"getShaderId(): \""<<name<<"\""<<std::endl;
403 See if shader already exists
405 JMutexAutoLock lock(m_shaderinfo_cache_mutex);
406 std::map<std::string, u32>::iterator n;
407 n = m_name_to_id.find(name);
408 if(n != m_name_to_id.end())
415 if(get_current_thread_id() == m_main_thread){
416 return getShaderIdDirect(name);
418 /*errorstream<<"getShaderId(): Queued: name=\""<<name<<"\""<<std::endl;*/
420 // We're gonna ask the result to be put into here
422 static ResultQueue<std::string, u32, u8, u8> result_queue;
424 // Throw a request in
425 m_get_shader_queue.add(name, 0, 0, &result_queue);
427 /* infostream<<"Waiting for shader from main thread, name=\""
428 <<name<<"\""<<std::endl;*/
432 // Wait result for a second
433 GetResult<std::string, u32, u8, u8>
434 result = result_queue.pop_front(1000);
436 if (result.key == name) {
441 catch(ItemNotFoundException &e){
442 errorstream<<"Waiting for shader " << name << " timed out."<<std::endl;
447 infostream<<"getShaderId(): Failed"<<std::endl;
453 This method generates all the shaders
455 u32 ShaderSource::getShaderIdDirect(const std::string &name)
457 //infostream<<"getShaderIdDirect(): name=\""<<name<<"\""<<std::endl;
459 // Empty name means shader 0
461 infostream<<"getShaderIdDirect(): name is empty"<<std::endl;
466 Calling only allowed from main thread
468 if(get_current_thread_id() != m_main_thread){
469 errorstream<<"ShaderSource::getShaderIdDirect() "
470 "called not from main thread"<<std::endl;
475 See if shader already exists
478 JMutexAutoLock lock(m_shaderinfo_cache_mutex);
480 std::map<std::string, u32>::iterator n;
481 n = m_name_to_id.find(name);
482 if(n != m_name_to_id.end()){
483 /*infostream<<"getShaderIdDirect(): \""<<name
484 <<"\" found in cache"<<std::endl;*/
489 /*infostream<<"getShaderIdDirect(): \""<<name
490 <<"\" NOT found in cache. Creating it."<<std::endl;*/
492 ShaderInfo info = generate_shader(name, m_device,
493 m_shader_callback, &m_sourcecache);
496 Add shader to caches (add dummy shaders too)
499 JMutexAutoLock lock(m_shaderinfo_cache_mutex);
501 u32 id = m_shaderinfo_cache.size();
502 m_shaderinfo_cache.push_back(info);
503 m_name_to_id[name] = id;
505 /*infostream<<"getShaderIdDirect(): "
506 <<"Returning id="<<id<<" for name \""<<name<<"\""<<std::endl;*/
511 std::string ShaderSource::getShaderName(u32 id)
513 JMutexAutoLock lock(m_shaderinfo_cache_mutex);
515 if(id >= m_shaderinfo_cache.size()){
516 errorstream<<"ShaderSource::getShaderName(): id="<<id
517 <<" >= m_shaderinfo_cache.size()="
518 <<m_shaderinfo_cache.size()<<std::endl;
522 return m_shaderinfo_cache[id].name;
525 ShaderInfo ShaderSource::getShader(u32 id)
527 JMutexAutoLock lock(m_shaderinfo_cache_mutex);
529 if(id >= m_shaderinfo_cache.size())
532 return m_shaderinfo_cache[id];
535 void ShaderSource::processQueue()
540 if(!m_get_shader_queue.empty()){
541 GetRequest<std::string, u32, u8, u8>
542 request = m_get_shader_queue.pop();
544 /**errorstream<<"ShaderSource::processQueue(): "
545 <<"got shader request with "
546 <<"name=\""<<request.key<<"\""
549 m_get_shader_queue.pushResult(request,getShaderIdDirect(request.key));
553 void ShaderSource::insertSourceShader(const std::string &name_of_shader,
554 const std::string &filename, const std::string &program)
556 /*infostream<<"ShaderSource::insertSourceShader(): "
557 "name_of_shader=\""<<name_of_shader<<"\", "
558 "filename=\""<<filename<<"\""<<std::endl;*/
560 assert(get_current_thread_id() == m_main_thread);
562 m_sourcecache.insert(name_of_shader, filename, program, true);
565 void ShaderSource::rebuildShaders()
567 JMutexAutoLock lock(m_shaderinfo_cache_mutex);
569 /*// Oh well... just clear everything, they'll load sometime.
570 m_shaderinfo_cache.clear();
571 m_name_to_id.clear();*/
574 FIXME: Old shader materials can't be deleted in Irrlicht,
576 (This would be nice to do in the destructor too)
580 for(u32 i=0; i<m_shaderinfo_cache.size(); i++){
581 ShaderInfo *info = &m_shaderinfo_cache[i];
582 if(info->name != ""){
583 *info = generate_shader(info->name, m_device,
584 m_shader_callback, &m_sourcecache);
589 void ShaderSource::onSetConstants(video::IMaterialRendererServices *services,
590 bool is_highlevel, const std::string &name)
592 for(u32 i=0; i<m_global_setters.size(); i++){
593 IShaderConstantSetter *setter = m_global_setters[i];
594 setter->onSetConstants(services, is_highlevel);
598 ShaderInfo generate_shader(std::string name, IrrlichtDevice *device,
599 video::IShaderConstantSetCallBack *callback,
600 SourceShaderCache *sourcecache)
602 /*infostream<<"generate_shader(): "
603 "\""<<name<<"\""<<std::endl;*/
605 ShaderInfo shaderinfo;
606 shaderinfo.name = name;
607 shaderinfo.material = video::EMT_SOLID;
610 Get the base material
612 std::string base_material_name =
613 trim(sourcecache->getOrLoad(name, "base.txt"));
614 for(s32 i = 0; video::sBuiltInMaterialTypeNames[i] != 0; i++){
615 if(video::sBuiltInMaterialTypeNames[i] == base_material_name){
616 shaderinfo.material = (video::E_MATERIAL_TYPE) i;
621 bool enable_shaders = g_settings->getBool("enable_shaders");
625 video::IVideoDriver* driver = device->getVideoDriver();
628 video::IGPUProgrammingServices *gpu = driver->getGPUProgrammingServices();
630 errorstream<<"generate_shader(): "
631 "failed to generate \""<<name<<"\", "
632 "GPU programming not supported."
637 // Choose shader language depending on driver type and settings
639 std::string vertex_program;
640 std::string pixel_program;
641 std::string geometry_program;
643 load_shaders(name, sourcecache, driver->getDriverType(),
644 enable_shaders, vertex_program, pixel_program,
645 geometry_program, is_highlevel);
647 // Check hardware/driver support
648 if(vertex_program != "" &&
649 !driver->queryFeature(video::EVDF_VERTEX_SHADER_1_1) &&
650 !driver->queryFeature(video::EVDF_ARB_VERTEX_PROGRAM_1)){
651 infostream<<"generate_shader(): vertex shaders disabled "
652 "because of missing driver/hardware support."
656 if(pixel_program != "" &&
657 !driver->queryFeature(video::EVDF_PIXEL_SHADER_1_1) &&
658 !driver->queryFeature(video::EVDF_ARB_FRAGMENT_PROGRAM_1)){
659 infostream<<"generate_shader(): pixel shaders disabled "
660 "because of missing driver/hardware support."
664 if(geometry_program != "" &&
665 !driver->queryFeature(video::EVDF_GEOMETRY_SHADER)){
666 infostream<<"generate_shader(): geometry shaders disabled "
667 "because of missing driver/hardware support."
669 geometry_program = "";
672 // If no shaders are used, don't make a separate material type
673 if(vertex_program == "" && pixel_program == "" && geometry_program == "")
676 // Create shaders header
677 std::string shaders_header = "#version 120\n";
679 if (g_settings->getBool("enable_bumpmapping"))
680 shaders_header += "#define ENABLE_BUMPMAPPING\n";
682 if (g_settings->getBool("enable_parallax_occlusion")){
683 shaders_header += "#define ENABLE_PARALLAX_OCCLUSION\n";
684 shaders_header += "#define PARALLAX_OCCLUSION_SCALE ";
685 shaders_header += ftos(g_settings->getFloat("parallax_occlusion_scale"));
686 shaders_header += "\n";
687 shaders_header += "#define PARALLAX_OCCLUSION_BIAS ";
688 shaders_header += ftos(g_settings->getFloat("parallax_occlusion_bias"));
689 shaders_header += "\n";
692 if (g_settings->getBool("enable_bumpmapping") || g_settings->getBool("enable_parallax_occlusion"))
693 shaders_header += "#define USE_NORMALMAPS\n";
695 if (g_settings->getBool("enable_waving_water")){
696 shaders_header += "#define ENABLE_WAVING_WATER\n";
697 shaders_header += "#define WATER_WAVE_HEIGHT ";
698 shaders_header += ftos(g_settings->getFloat("water_wave_height"));
699 shaders_header += "\n";
700 shaders_header += "#define WATER_WAVE_LENGTH ";
701 shaders_header += ftos(g_settings->getFloat("water_wave_length"));
702 shaders_header += "\n";
703 shaders_header += "#define WATER_WAVE_SPEED ";
704 shaders_header += ftos(g_settings->getFloat("water_wave_speed"));
705 shaders_header += "\n";
708 if (g_settings->getBool("enable_waving_leaves"))
709 shaders_header += "#define ENABLE_WAVING_LEAVES\n";
711 if (g_settings->getBool("enable_waving_plants"))
712 shaders_header += "#define ENABLE_WAVING_PLANTS\n";
714 if(pixel_program != "")
715 pixel_program = shaders_header + pixel_program;
716 if(vertex_program != "")
717 vertex_program = shaders_header + vertex_program;
718 if(geometry_program != "")
719 geometry_program = shaders_header + geometry_program;
721 // Call addHighLevelShaderMaterial() or addShaderMaterial()
722 const c8* vertex_program_ptr = 0;
723 const c8* pixel_program_ptr = 0;
724 const c8* geometry_program_ptr = 0;
725 if(vertex_program != "")
726 vertex_program_ptr = vertex_program.c_str();
727 if(pixel_program != "")
728 pixel_program_ptr = pixel_program.c_str();
729 if(geometry_program != "")
730 geometry_program_ptr = geometry_program.c_str();
733 infostream<<"Compiling high level shaders for "<<name<<std::endl;
734 shadermat = gpu->addHighLevelShaderMaterial(
735 vertex_program_ptr, // Vertex shader program
736 "vertexMain", // Vertex shader entry point
737 video::EVST_VS_1_1, // Vertex shader version
738 pixel_program_ptr, // Pixel shader program
739 "pixelMain", // Pixel shader entry point
740 video::EPST_PS_1_1, // Pixel shader version
741 geometry_program_ptr, // Geometry shader program
742 "geometryMain", // Geometry shader entry point
743 video::EGST_GS_4_0, // Geometry shader version
744 scene::EPT_TRIANGLES, // Geometry shader input
745 scene::EPT_TRIANGLE_STRIP, // Geometry shader output
746 0, // Support maximum number of vertices
747 callback, // Set-constant callback
748 shaderinfo.material, // Base material
749 1 // Userdata passed to callback
753 errorstream<<"generate_shader(): "
754 "failed to generate \""<<name<<"\", "
755 "addHighLevelShaderMaterial failed."
761 infostream<<"Compiling assembly shaders for "<<name<<std::endl;
762 shadermat = gpu->addShaderMaterial(
763 vertex_program_ptr, // Vertex shader program
764 pixel_program_ptr, // Pixel shader program
765 callback, // Set-constant callback
766 shaderinfo.material, // Base material
767 0 // Userdata passed to callback
771 errorstream<<"generate_shader(): "
772 "failed to generate \""<<name<<"\", "
773 "addShaderMaterial failed."
779 // HACK, TODO: investigate this better
780 // Grab the material renderer once more so minetest doesn't crash on exit
781 driver->getMaterialRenderer(shadermat)->grab();
783 // Apply the newly created material type
784 shaderinfo.material = (video::E_MATERIAL_TYPE) shadermat;
788 void load_shaders(std::string name, SourceShaderCache *sourcecache,
789 video::E_DRIVER_TYPE drivertype, bool enable_shaders,
790 std::string &vertex_program, std::string &pixel_program,
791 std::string &geometry_program, bool &is_highlevel)
795 geometry_program = "";
796 is_highlevel = false;
799 // Look for high level shaders
800 if(drivertype == video::EDT_DIRECT3D9){
802 // (All shaders in one file)
803 vertex_program = sourcecache->getOrLoad(name, "d3d9.hlsl");
804 pixel_program = vertex_program;
805 geometry_program = vertex_program;
807 else if(drivertype == video::EDT_OPENGL){
809 vertex_program = sourcecache->getOrLoad(name, "opengl_vertex.glsl");
810 pixel_program = sourcecache->getOrLoad(name, "opengl_fragment.glsl");
811 geometry_program = sourcecache->getOrLoad(name, "opengl_geometry.glsl");
813 if(vertex_program != "" || pixel_program != "" || geometry_program != ""){