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()
39 #include "client/tile.h"
42 A cache from shader name to shader path
44 MutexedMap<std::string, std::string> g_shadername_to_path_cache;
47 Gets the path to a shader by first checking if the file
48 name_of_shader/filename
49 exists in shader_path and if not, using the data path.
51 If not found, returns "".
53 Utilizes a thread-safe cache.
55 std::string getShaderPath(const std::string &name_of_shader,
56 const std::string &filename)
58 std::string combined = name_of_shader + DIR_DELIM + filename;
59 std::string fullpath = "";
63 bool incache = g_shadername_to_path_cache.get(combined, &fullpath);
68 Check from shader_path
70 std::string shader_path = g_settings->get("shader_path");
73 std::string testpath = shader_path + DIR_DELIM + combined;
74 if(fs::PathExists(testpath))
79 Check from default data directory
83 std::string rel_path = std::string("client") + DIR_DELIM
84 + "shaders" + DIR_DELIM
85 + name_of_shader + DIR_DELIM
87 std::string testpath = porting::path_share + DIR_DELIM + rel_path;
88 if(fs::PathExists(testpath))
92 // Add to cache (also an empty result is cached)
93 g_shadername_to_path_cache.set(combined, fullpath);
100 SourceShaderCache: A cache used for storing source shaders.
103 class SourceShaderCache
106 void insert(const std::string &name_of_shader,
107 const std::string &filename,
108 const std::string &program,
111 std::string combined = name_of_shader + DIR_DELIM + filename;
112 // Try to use local shader instead if asked to
114 std::string path = getShaderPath(name_of_shader, filename);
116 std::string p = readFile(path);
118 m_programs[combined] = p;
123 m_programs[combined] = program;
125 std::string get(const std::string &name_of_shader,
126 const std::string &filename)
128 std::string combined = name_of_shader + DIR_DELIM + filename;
129 std::map<std::string, std::string>::iterator n;
130 n = m_programs.find(combined);
131 if(n != m_programs.end())
135 // Primarily fetches from cache, secondarily tries to read from filesystem
136 std::string getOrLoad(const std::string &name_of_shader,
137 const std::string &filename)
139 std::string combined = name_of_shader + DIR_DELIM + filename;
140 std::map<std::string, std::string>::iterator n;
141 n = m_programs.find(combined);
142 if(n != m_programs.end())
144 std::string path = getShaderPath(name_of_shader, filename);
146 infostream<<"SourceShaderCache::getOrLoad(): No path found for \""
147 <<combined<<"\""<<std::endl;
150 infostream<<"SourceShaderCache::getOrLoad(): Loading path \""<<path
152 std::string p = readFile(path);
154 m_programs[combined] = p;
160 std::map<std::string, std::string> m_programs;
161 std::string readFile(const std::string &path)
163 std::ifstream is(path.c_str(), std::ios::binary);
166 std::ostringstream tmp_os;
167 tmp_os << is.rdbuf();
173 ShaderCallback: Sets constants that can be used in shaders
176 class IShaderConstantSetterRegistry
179 virtual ~IShaderConstantSetterRegistry(){};
180 virtual void onSetConstants(video::IMaterialRendererServices *services,
181 bool is_highlevel, const std::string &name) = 0;
184 class ShaderCallback : public video::IShaderConstantSetCallBack
186 IShaderConstantSetterRegistry *m_scsr;
190 ShaderCallback(IShaderConstantSetterRegistry *scsr, const std::string &name):
196 virtual void OnSetConstants(video::IMaterialRendererServices *services, s32 userData)
198 video::IVideoDriver *driver = services->getVideoDriver();
201 bool is_highlevel = userData;
203 m_scsr->onSetConstants(services, is_highlevel, m_name);
208 MainShaderConstantSetter: Set basic constants required for almost everything
211 class MainShaderConstantSetter : public IShaderConstantSetter
214 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 transWorld = driver->getTransform(video::ETS_WORLD);
244 transWorld = transWorld.getTransposed();
246 services->setVertexShaderConstant("mTransWorld", transWorld.pointer(), 16);
248 services->setVertexShaderConstant(transWorld.pointer(), 8, 4);
251 core::matrix4 world = driver->getTransform(video::ETS_WORLD);
253 services->setVertexShaderConstant("mWorld", world.pointer(), 16);
255 services->setVertexShaderConstant(world.pointer(), 8, 4);
264 class ShaderSource : public IWritableShaderSource, public IShaderConstantSetterRegistry
267 ShaderSource(IrrlichtDevice *device);
271 - If shader material specified by name is found from cache,
272 return the cached id.
273 - Otherwise generate the shader material, add to cache and return id.
275 The id 0 points to a null shader. Its material is EMT_SOLID.
277 u32 getShaderIdDirect(const std::string &name,
278 const u8 material_type, const u8 drawtype);
281 If shader specified by the name pointed by the id doesn't
282 exist, create it, then return id.
284 Can be called from any thread. If called from some other thread
285 and not found in cache, the call is queued to the main thread
289 u32 getShader(const std::string &name,
290 const u8 material_type, const u8 drawtype);
292 ShaderInfo getShaderInfo(u32 id);
294 // Processes queued shader requests from other threads.
295 // Shall be called from the main thread.
298 // Insert a shader program into the cache without touching the
299 // filesystem. Shall be called from the main thread.
300 void insertSourceShader(const std::string &name_of_shader,
301 const std::string &filename, const std::string &program);
303 // Rebuild shaders from the current set of source shaders
304 // Shall be called from the main thread.
305 void rebuildShaders();
307 void addGlobalConstantSetter(IShaderConstantSetter *setter)
309 m_global_setters.push_back(setter);
312 void onSetConstants(video::IMaterialRendererServices *services,
313 bool is_highlevel, const std::string &name);
317 // The id of the thread that is allowed to use irrlicht directly
318 threadid_t m_main_thread;
319 // The irrlicht device
320 IrrlichtDevice *m_device;
321 // The set-constants callback
322 ShaderCallback *m_shader_callback;
324 // Cache of source shaders
325 // This should be only accessed from the main thread
326 SourceShaderCache m_sourcecache;
328 // A shader id is index in this array.
329 // The first position contains a dummy shader.
330 std::vector<ShaderInfo> m_shaderinfo_cache;
331 // The former container is behind this mutex
332 JMutex m_shaderinfo_cache_mutex;
334 // Queued shader fetches (to be processed by the main thread)
335 RequestQueue<std::string, u32, u8, u8> m_get_shader_queue;
337 // Global constant setters
338 // TODO: Delete these in the destructor
339 std::vector<IShaderConstantSetter*> m_global_setters;
342 IWritableShaderSource* createShaderSource(IrrlichtDevice *device)
344 return new ShaderSource(device);
348 Generate shader given the shader name.
350 ShaderInfo generate_shader(std::string name,
351 u8 material_type, u8 drawtype,
352 IrrlichtDevice *device,
353 video::IShaderConstantSetCallBack *callback,
354 SourceShaderCache *sourcecache);
359 void load_shaders(std::string name, SourceShaderCache *sourcecache,
360 video::E_DRIVER_TYPE drivertype, bool enable_shaders,
361 std::string &vertex_program, std::string &pixel_program,
362 std::string &geometry_program, bool &is_highlevel);
364 ShaderSource::ShaderSource(IrrlichtDevice *device):
369 m_shader_callback = new ShaderCallback(this, "default");
371 m_main_thread = get_current_thread_id();
373 // Add a dummy ShaderInfo as the first index, named ""
374 m_shaderinfo_cache.push_back(ShaderInfo());
376 // Add main global constant setter
377 addGlobalConstantSetter(new MainShaderConstantSetter(device));
380 ShaderSource::~ShaderSource()
382 for (std::vector<IShaderConstantSetter*>::iterator iter = m_global_setters.begin();
383 iter != m_global_setters.end(); iter++) {
386 m_global_setters.clear();
388 if (m_shader_callback) {
389 m_shader_callback->drop();
390 m_shader_callback = NULL;
394 u32 ShaderSource::getShader(const std::string &name,
395 const u8 material_type, const u8 drawtype)
401 if(get_current_thread_id() == m_main_thread){
402 return getShaderIdDirect(name, material_type, drawtype);
404 /*errorstream<<"getShader(): Queued: name=\""<<name<<"\""<<std::endl;*/
406 // We're gonna ask the result to be put into here
408 static ResultQueue<std::string, u32, u8, u8> result_queue;
410 // Throw a request in
411 m_get_shader_queue.add(name, 0, 0, &result_queue);
413 /* infostream<<"Waiting for shader from main thread, name=\""
414 <<name<<"\""<<std::endl;*/
417 GetResult<std::string, u32, u8, u8>
418 result = result_queue.pop_frontNoEx();
420 if (result.key == name) {
424 errorstream << "Got shader with invalid name: " << result.key << std::endl;
430 infostream<<"getShader(): Failed"<<std::endl;
436 This method generates all the shaders
438 u32 ShaderSource::getShaderIdDirect(const std::string &name,
439 const u8 material_type, const u8 drawtype)
441 //infostream<<"getShaderIdDirect(): name=\""<<name<<"\""<<std::endl;
443 // Empty name means shader 0
445 infostream<<"getShaderIdDirect(): name is empty"<<std::endl;
449 // Check if already have such instance
450 for(u32 i=0; i<m_shaderinfo_cache.size(); i++){
451 ShaderInfo *info = &m_shaderinfo_cache[i];
452 if(info->name == name && info->material_type == material_type &&
453 info->drawtype == drawtype)
458 Calling only allowed from main thread
460 if(get_current_thread_id() != m_main_thread){
461 errorstream<<"ShaderSource::getShaderIdDirect() "
462 "called not from main thread"<<std::endl;
466 ShaderInfo info = generate_shader(name, material_type, drawtype, m_device,
467 m_shader_callback, &m_sourcecache);
470 Add shader to caches (add dummy shaders too)
473 JMutexAutoLock lock(m_shaderinfo_cache_mutex);
475 u32 id = m_shaderinfo_cache.size();
476 m_shaderinfo_cache.push_back(info);
478 infostream<<"getShaderIdDirect(): "
479 <<"Returning id="<<id<<" for name \""<<name<<"\""<<std::endl;
485 ShaderInfo ShaderSource::getShaderInfo(u32 id)
487 JMutexAutoLock lock(m_shaderinfo_cache_mutex);
489 if(id >= m_shaderinfo_cache.size())
492 return m_shaderinfo_cache[id];
495 void ShaderSource::processQueue()
501 void ShaderSource::insertSourceShader(const std::string &name_of_shader,
502 const std::string &filename, const std::string &program)
504 /*infostream<<"ShaderSource::insertSourceShader(): "
505 "name_of_shader=\""<<name_of_shader<<"\", "
506 "filename=\""<<filename<<"\""<<std::endl;*/
508 assert(get_current_thread_id() == m_main_thread);
510 m_sourcecache.insert(name_of_shader, filename, program, true);
513 void ShaderSource::rebuildShaders()
515 JMutexAutoLock lock(m_shaderinfo_cache_mutex);
517 /*// Oh well... just clear everything, they'll load sometime.
518 m_shaderinfo_cache.clear();
519 m_name_to_id.clear();*/
522 FIXME: Old shader materials can't be deleted in Irrlicht,
524 (This would be nice to do in the destructor too)
528 for(u32 i=0; i<m_shaderinfo_cache.size(); i++){
529 ShaderInfo *info = &m_shaderinfo_cache[i];
530 if(info->name != ""){
531 *info = generate_shader(info->name, info->material_type,
532 info->drawtype, m_device, m_shader_callback, &m_sourcecache);
537 void ShaderSource::onSetConstants(video::IMaterialRendererServices *services,
538 bool is_highlevel, const std::string &name)
540 for(u32 i=0; i<m_global_setters.size(); i++){
541 IShaderConstantSetter *setter = m_global_setters[i];
542 setter->onSetConstants(services, is_highlevel);
546 ShaderInfo generate_shader(std::string name, u8 material_type, u8 drawtype,
547 IrrlichtDevice *device, video::IShaderConstantSetCallBack *callback,
548 SourceShaderCache *sourcecache)
550 ShaderInfo shaderinfo;
551 shaderinfo.name = name;
552 shaderinfo.material_type = material_type;
553 shaderinfo.drawtype = drawtype;
554 shaderinfo.material = video::EMT_SOLID;
555 switch(material_type){
556 case TILE_MATERIAL_BASIC:
557 shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
559 case TILE_MATERIAL_ALPHA:
560 shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
562 case TILE_MATERIAL_LIQUID_TRANSPARENT:
563 shaderinfo.base_material = video::EMT_TRANSPARENT_VERTEX_ALPHA;
565 case TILE_MATERIAL_LIQUID_OPAQUE:
566 shaderinfo.base_material = video::EMT_SOLID;
568 case TILE_MATERIAL_WAVING_LEAVES:
569 shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
571 case TILE_MATERIAL_WAVING_PLANTS:
572 shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
576 bool enable_shaders = g_settings->getBool("enable_shaders");
580 video::IVideoDriver* driver = device->getVideoDriver();
583 video::IGPUProgrammingServices *gpu = driver->getGPUProgrammingServices();
585 errorstream<<"generate_shader(): "
586 "failed to generate \""<<name<<"\", "
587 "GPU programming not supported."
592 // Choose shader language depending on driver type and settings
594 std::string vertex_program;
595 std::string pixel_program;
596 std::string geometry_program;
598 load_shaders(name, sourcecache, driver->getDriverType(),
599 enable_shaders, vertex_program, pixel_program,
600 geometry_program, is_highlevel);
601 // Check hardware/driver support
602 if(vertex_program != "" &&
603 !driver->queryFeature(video::EVDF_VERTEX_SHADER_1_1) &&
604 !driver->queryFeature(video::EVDF_ARB_VERTEX_PROGRAM_1)){
605 infostream<<"generate_shader(): vertex shaders disabled "
606 "because of missing driver/hardware support."
610 if(pixel_program != "" &&
611 !driver->queryFeature(video::EVDF_PIXEL_SHADER_1_1) &&
612 !driver->queryFeature(video::EVDF_ARB_FRAGMENT_PROGRAM_1)){
613 infostream<<"generate_shader(): pixel shaders disabled "
614 "because of missing driver/hardware support."
618 if(geometry_program != "" &&
619 !driver->queryFeature(video::EVDF_GEOMETRY_SHADER)){
620 infostream<<"generate_shader(): geometry shaders disabled "
621 "because of missing driver/hardware support."
623 geometry_program = "";
626 // If no shaders are used, don't make a separate material type
627 if(vertex_program == "" && pixel_program == "" && geometry_program == "")
630 // Create shaders header
631 std::string shaders_header = "#version 120\n";
633 static const char* drawTypes[] = {
640 "NDT_ALLFACES_OPTIONAL",
647 "NDT_GLASSLIKE_FRAMED",
649 "NDT_GLASSLIKE_FRAMED_OPTIONAL"
652 for (int i = 0; i < 14; i++){
653 shaders_header += "#define ";
654 shaders_header += drawTypes[i];
655 shaders_header += " ";
656 shaders_header += itos(i);
657 shaders_header += "\n";
660 static const char* materialTypes[] = {
661 "TILE_MATERIAL_BASIC",
662 "TILE_MATERIAL_ALPHA",
663 "TILE_MATERIAL_LIQUID_TRANSPARENT",
664 "TILE_MATERIAL_LIQUID_OPAQUE",
665 "TILE_MATERIAL_WAVING_LEAVES",
666 "TILE_MATERIAL_WAVING_PLANTS"
669 for (int i = 0; i < 6; i++){
670 shaders_header += "#define ";
671 shaders_header += materialTypes[i];
672 shaders_header += " ";
673 shaders_header += itos(i);
674 shaders_header += "\n";
677 shaders_header += "#define MATERIAL_TYPE ";
678 shaders_header += itos(material_type);
679 shaders_header += "\n";
680 shaders_header += "#define DRAW_TYPE ";
681 shaders_header += itos(drawtype);
682 shaders_header += "\n";
684 if (g_settings->getBool("generate_normalmaps")){
685 shaders_header += "#define GENERATE_NORMALMAPS\n";
686 shaders_header += "#define NORMALMAPS_STRENGTH ";
687 shaders_header += ftos(g_settings->getFloat("normalmaps_strength"));
688 shaders_header += "\n";
690 int smooth = (int)g_settings->getFloat("normalmaps_smooth");
693 sample_step = 0.0078125; // 1.0 / 128.0
696 sample_step = 0.00390625; // 1.0 / 256.0
699 sample_step = 0.001953125; // 1.0 / 512.0
702 sample_step = 0.0078125;
705 shaders_header += "#define SAMPLE_STEP ";
706 shaders_header += ftos(sample_step);
707 shaders_header += "\n";
710 if (g_settings->getBool("enable_bumpmapping"))
711 shaders_header += "#define ENABLE_BUMPMAPPING\n";
713 if (g_settings->getBool("enable_parallax_occlusion")){
714 shaders_header += "#define ENABLE_PARALLAX_OCCLUSION\n";
715 shaders_header += "#define PARALLAX_OCCLUSION_SCALE ";
716 shaders_header += ftos(g_settings->getFloat("parallax_occlusion_scale"));
717 shaders_header += "\n";
718 shaders_header += "#define PARALLAX_OCCLUSION_BIAS ";
719 shaders_header += ftos(g_settings->getFloat("parallax_occlusion_bias"));
720 shaders_header += "\n";
723 if (g_settings->getBool("enable_bumpmapping") || g_settings->getBool("enable_parallax_occlusion"))
724 shaders_header += "#define USE_NORMALMAPS\n";
726 if (g_settings->getBool("enable_waving_water")){
727 shaders_header += "#define ENABLE_WAVING_WATER 1\n";
728 shaders_header += "#define WATER_WAVE_HEIGHT ";
729 shaders_header += ftos(g_settings->getFloat("water_wave_height"));
730 shaders_header += "\n";
731 shaders_header += "#define WATER_WAVE_LENGTH ";
732 shaders_header += ftos(g_settings->getFloat("water_wave_length"));
733 shaders_header += "\n";
734 shaders_header += "#define WATER_WAVE_SPEED ";
735 shaders_header += ftos(g_settings->getFloat("water_wave_speed"));
736 shaders_header += "\n";
738 shaders_header += "#define ENABLE_WAVING_WATER 0\n";
741 shaders_header += "#define ENABLE_WAVING_LEAVES ";
742 if (g_settings->getBool("enable_waving_leaves"))
743 shaders_header += "1\n";
745 shaders_header += "0\n";
747 shaders_header += "#define ENABLE_WAVING_PLANTS ";
748 if (g_settings->getBool("enable_waving_plants"))
749 shaders_header += "1\n";
751 shaders_header += "0\n";
753 if(pixel_program != "")
754 pixel_program = shaders_header + pixel_program;
755 if(vertex_program != "")
756 vertex_program = shaders_header + vertex_program;
757 if(geometry_program != "")
758 geometry_program = shaders_header + geometry_program;
760 // Call addHighLevelShaderMaterial() or addShaderMaterial()
761 const c8* vertex_program_ptr = 0;
762 const c8* pixel_program_ptr = 0;
763 const c8* geometry_program_ptr = 0;
764 if(vertex_program != "")
765 vertex_program_ptr = vertex_program.c_str();
766 if(pixel_program != "")
767 pixel_program_ptr = pixel_program.c_str();
768 if(geometry_program != "")
769 geometry_program_ptr = geometry_program.c_str();
772 infostream<<"Compiling high level shaders for "<<name<<std::endl;
773 shadermat = gpu->addHighLevelShaderMaterial(
774 vertex_program_ptr, // Vertex shader program
775 "vertexMain", // Vertex shader entry point
776 video::EVST_VS_1_1, // Vertex shader version
777 pixel_program_ptr, // Pixel shader program
778 "pixelMain", // Pixel shader entry point
779 video::EPST_PS_1_1, // Pixel shader version
780 geometry_program_ptr, // Geometry shader program
781 "geometryMain", // Geometry shader entry point
782 video::EGST_GS_4_0, // Geometry shader version
783 scene::EPT_TRIANGLES, // Geometry shader input
784 scene::EPT_TRIANGLE_STRIP, // Geometry shader output
785 0, // Support maximum number of vertices
786 callback, // Set-constant callback
787 shaderinfo.base_material, // Base material
788 1 // Userdata passed to callback
791 errorstream<<"generate_shader(): "
792 "failed to generate \""<<name<<"\", "
793 "addHighLevelShaderMaterial failed."
799 infostream<<"Compiling assembly shaders for "<<name<<std::endl;
800 shadermat = gpu->addShaderMaterial(
801 vertex_program_ptr, // Vertex shader program
802 pixel_program_ptr, // Pixel shader program
803 callback, // Set-constant callback
804 shaderinfo.base_material, // Base material
805 0 // Userdata passed to callback
809 errorstream<<"generate_shader(): "
810 "failed to generate \""<<name<<"\", "
811 "addShaderMaterial failed."
817 // HACK, TODO: investigate this better
818 // Grab the material renderer once more so minetest doesn't crash on exit
819 driver->getMaterialRenderer(shadermat)->grab();
821 // Apply the newly created material type
822 shaderinfo.material = (video::E_MATERIAL_TYPE) shadermat;
826 void load_shaders(std::string name, SourceShaderCache *sourcecache,
827 video::E_DRIVER_TYPE drivertype, bool enable_shaders,
828 std::string &vertex_program, std::string &pixel_program,
829 std::string &geometry_program, bool &is_highlevel)
833 geometry_program = "";
834 is_highlevel = false;
837 // Look for high level shaders
838 if(drivertype == video::EDT_DIRECT3D9){
840 // (All shaders in one file)
841 vertex_program = sourcecache->getOrLoad(name, "d3d9.hlsl");
842 pixel_program = vertex_program;
843 geometry_program = vertex_program;
845 else if(drivertype == video::EDT_OPENGL){
847 vertex_program = sourcecache->getOrLoad(name, "opengl_vertex.glsl");
848 pixel_program = sourcecache->getOrLoad(name, "opengl_fragment.glsl");
849 geometry_program = sourcecache->getOrLoad(name, "opengl_geometry.glsl");
851 if(vertex_program != "" || pixel_program != "" || geometry_program != ""){