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()
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):
217 ~MainShaderConstantSetter() {}
219 virtual void onSetConstants(video::IMaterialRendererServices *services,
222 video::IVideoDriver *driver = services->getVideoDriver();
225 // set inverted world matrix
226 core::matrix4 invWorld = driver->getTransform(video::ETS_WORLD);
227 invWorld.makeInverse();
229 services->setVertexShaderConstant("mInvWorld", invWorld.pointer(), 16);
231 services->setVertexShaderConstant(invWorld.pointer(), 0, 4);
234 core::matrix4 worldViewProj;
235 worldViewProj = driver->getTransform(video::ETS_PROJECTION);
236 worldViewProj *= driver->getTransform(video::ETS_VIEW);
237 worldViewProj *= driver->getTransform(video::ETS_WORLD);
239 services->setVertexShaderConstant("mWorldViewProj", worldViewProj.pointer(), 16);
241 services->setVertexShaderConstant(worldViewProj.pointer(), 4, 4);
243 // set transposed world matrix
244 core::matrix4 transWorld = driver->getTransform(video::ETS_WORLD);
245 transWorld = transWorld.getTransposed();
247 services->setVertexShaderConstant("mTransWorld", transWorld.pointer(), 16);
249 services->setVertexShaderConstant(transWorld.pointer(), 8, 4);
252 core::matrix4 world = driver->getTransform(video::ETS_WORLD);
254 services->setVertexShaderConstant("mWorld", world.pointer(), 16);
256 services->setVertexShaderConstant(world.pointer(), 8, 4);
261 IrrlichtDevice *m_device;
268 class ShaderSource : public IWritableShaderSource, public IShaderConstantSetterRegistry
271 ShaderSource(IrrlichtDevice *device);
275 - If shader material specified by name is found from cache,
276 return the cached id.
277 - Otherwise generate the shader material, add to cache and return id.
279 The id 0 points to a null shader. Its material is EMT_SOLID.
281 u32 getShaderIdDirect(const std::string &name,
282 const u8 material_type, const u8 drawtype);
285 If shader specified by the name pointed by the id doesn't
286 exist, create it, then return id.
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
293 u32 getShader(const std::string &name,
294 const u8 material_type, const u8 drawtype);
296 ShaderInfo getShaderInfo(u32 id);
298 // Processes queued shader requests from other threads.
299 // Shall be called from the main thread.
302 // Insert a shader program into the cache without touching the
303 // filesystem. Shall be called from the main thread.
304 void insertSourceShader(const std::string &name_of_shader,
305 const std::string &filename, const std::string &program);
307 // Rebuild shaders from the current set of source shaders
308 // Shall be called from the main thread.
309 void rebuildShaders();
311 void addGlobalConstantSetter(IShaderConstantSetter *setter)
313 m_global_setters.push_back(setter);
316 void onSetConstants(video::IMaterialRendererServices *services,
317 bool is_highlevel, const std::string &name);
321 // The id of the thread that is allowed to use irrlicht directly
322 threadid_t m_main_thread;
323 // The irrlicht device
324 IrrlichtDevice *m_device;
325 // The set-constants callback
326 ShaderCallback *m_shader_callback;
328 // Cache of source shaders
329 // This should be only accessed from the main thread
330 SourceShaderCache m_sourcecache;
332 // A shader id is index in this array.
333 // The first position contains a dummy shader.
334 std::vector<ShaderInfo> m_shaderinfo_cache;
335 // The former container is behind this mutex
336 JMutex m_shaderinfo_cache_mutex;
338 // Queued shader fetches (to be processed by the main thread)
339 RequestQueue<std::string, u32, u8, u8> m_get_shader_queue;
341 // Global constant setters
342 // TODO: Delete these in the destructor
343 std::vector<IShaderConstantSetter*> m_global_setters;
346 IWritableShaderSource* createShaderSource(IrrlichtDevice *device)
348 return new ShaderSource(device);
352 Generate shader given the shader name.
354 ShaderInfo generate_shader(std::string name,
355 u8 material_type, u8 drawtype,
356 IrrlichtDevice *device,
357 video::IShaderConstantSetCallBack *callback,
358 SourceShaderCache *sourcecache);
363 void load_shaders(std::string name, SourceShaderCache *sourcecache,
364 video::E_DRIVER_TYPE drivertype, bool enable_shaders,
365 std::string &vertex_program, std::string &pixel_program,
366 std::string &geometry_program, bool &is_highlevel);
368 ShaderSource::ShaderSource(IrrlichtDevice *device):
373 m_shader_callback = new ShaderCallback(this, "default");
375 m_main_thread = get_current_thread_id();
377 // Add a dummy ShaderInfo as the first index, named ""
378 m_shaderinfo_cache.push_back(ShaderInfo());
380 // Add main global constant setter
381 addGlobalConstantSetter(new MainShaderConstantSetter(device));
384 ShaderSource::~ShaderSource()
386 for (std::vector<IShaderConstantSetter*>::iterator iter = m_global_setters.begin();
387 iter != m_global_setters.end(); iter++) {
390 m_global_setters.clear();
392 if (m_shader_callback) {
393 m_shader_callback->drop();
394 m_shader_callback = NULL;
398 u32 ShaderSource::getShader(const std::string &name,
399 const u8 material_type, const u8 drawtype)
405 if(get_current_thread_id() == m_main_thread){
406 return getShaderIdDirect(name, material_type, drawtype);
408 /*errorstream<<"getShader(): Queued: name=\""<<name<<"\""<<std::endl;*/
410 // We're gonna ask the result to be put into here
412 static ResultQueue<std::string, u32, u8, u8> result_queue;
414 // Throw a request in
415 m_get_shader_queue.add(name, 0, 0, &result_queue);
417 /* infostream<<"Waiting for shader from main thread, name=\""
418 <<name<<"\""<<std::endl;*/
421 GetResult<std::string, u32, u8, u8>
422 result = result_queue.pop_frontNoEx();
424 if (result.key == name) {
428 errorstream << "Got shader with invalid name: " << result.key << std::endl;
434 infostream<<"getShader(): Failed"<<std::endl;
440 This method generates all the shaders
442 u32 ShaderSource::getShaderIdDirect(const std::string &name,
443 const u8 material_type, const u8 drawtype)
445 //infostream<<"getShaderIdDirect(): name=\""<<name<<"\""<<std::endl;
447 // Empty name means shader 0
449 infostream<<"getShaderIdDirect(): name is empty"<<std::endl;
453 // Check if already have such instance
454 for(u32 i=0; i<m_shaderinfo_cache.size(); i++){
455 ShaderInfo *info = &m_shaderinfo_cache[i];
456 if(info->name == name && info->material_type == material_type &&
457 info->drawtype == drawtype)
462 Calling only allowed from main thread
464 if(get_current_thread_id() != m_main_thread){
465 errorstream<<"ShaderSource::getShaderIdDirect() "
466 "called not from main thread"<<std::endl;
470 ShaderInfo info = generate_shader(name, material_type, drawtype, m_device,
471 m_shader_callback, &m_sourcecache);
474 Add shader to caches (add dummy shaders too)
477 JMutexAutoLock lock(m_shaderinfo_cache_mutex);
479 u32 id = m_shaderinfo_cache.size();
480 m_shaderinfo_cache.push_back(info);
482 infostream<<"getShaderIdDirect(): "
483 <<"Returning id="<<id<<" for name \""<<name<<"\""<<std::endl;
489 ShaderInfo ShaderSource::getShaderInfo(u32 id)
491 JMutexAutoLock lock(m_shaderinfo_cache_mutex);
493 if(id >= m_shaderinfo_cache.size())
496 return m_shaderinfo_cache[id];
499 void ShaderSource::processQueue()
505 void ShaderSource::insertSourceShader(const std::string &name_of_shader,
506 const std::string &filename, const std::string &program)
508 /*infostream<<"ShaderSource::insertSourceShader(): "
509 "name_of_shader=\""<<name_of_shader<<"\", "
510 "filename=\""<<filename<<"\""<<std::endl;*/
512 assert(get_current_thread_id() == m_main_thread);
514 m_sourcecache.insert(name_of_shader, filename, program, true);
517 void ShaderSource::rebuildShaders()
519 JMutexAutoLock lock(m_shaderinfo_cache_mutex);
521 /*// Oh well... just clear everything, they'll load sometime.
522 m_shaderinfo_cache.clear();
523 m_name_to_id.clear();*/
526 FIXME: Old shader materials can't be deleted in Irrlicht,
528 (This would be nice to do in the destructor too)
532 for(u32 i=0; i<m_shaderinfo_cache.size(); i++){
533 ShaderInfo *info = &m_shaderinfo_cache[i];
534 if(info->name != ""){
535 *info = generate_shader(info->name, info->material_type,
536 info->drawtype, m_device, m_shader_callback, &m_sourcecache);
541 void ShaderSource::onSetConstants(video::IMaterialRendererServices *services,
542 bool is_highlevel, const std::string &name)
544 for(u32 i=0; i<m_global_setters.size(); i++){
545 IShaderConstantSetter *setter = m_global_setters[i];
546 setter->onSetConstants(services, is_highlevel);
550 ShaderInfo generate_shader(std::string name, u8 material_type, u8 drawtype,
551 IrrlichtDevice *device, video::IShaderConstantSetCallBack *callback,
552 SourceShaderCache *sourcecache)
554 ShaderInfo shaderinfo;
555 shaderinfo.name = name;
556 shaderinfo.material_type = material_type;
557 shaderinfo.drawtype = drawtype;
558 shaderinfo.material = video::EMT_SOLID;
559 switch(material_type){
560 case TILE_MATERIAL_BASIC:
561 shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
563 case TILE_MATERIAL_ALPHA:
564 shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
566 case TILE_MATERIAL_LIQUID_TRANSPARENT:
567 shaderinfo.base_material = video::EMT_TRANSPARENT_VERTEX_ALPHA;
569 case TILE_MATERIAL_LIQUID_OPAQUE:
570 shaderinfo.base_material = video::EMT_SOLID;
572 case TILE_MATERIAL_WAVING_LEAVES:
573 shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
575 case TILE_MATERIAL_WAVING_PLANTS:
576 shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
580 bool enable_shaders = g_settings->getBool("enable_shaders");
584 video::IVideoDriver* driver = device->getVideoDriver();
587 video::IGPUProgrammingServices *gpu = driver->getGPUProgrammingServices();
589 errorstream<<"generate_shader(): "
590 "failed to generate \""<<name<<"\", "
591 "GPU programming not supported."
596 // Choose shader language depending on driver type and settings
598 std::string vertex_program;
599 std::string pixel_program;
600 std::string geometry_program;
602 load_shaders(name, sourcecache, driver->getDriverType(),
603 enable_shaders, vertex_program, pixel_program,
604 geometry_program, is_highlevel);
605 // Check hardware/driver support
606 if(vertex_program != "" &&
607 !driver->queryFeature(video::EVDF_VERTEX_SHADER_1_1) &&
608 !driver->queryFeature(video::EVDF_ARB_VERTEX_PROGRAM_1)){
609 infostream<<"generate_shader(): vertex shaders disabled "
610 "because of missing driver/hardware support."
614 if(pixel_program != "" &&
615 !driver->queryFeature(video::EVDF_PIXEL_SHADER_1_1) &&
616 !driver->queryFeature(video::EVDF_ARB_FRAGMENT_PROGRAM_1)){
617 infostream<<"generate_shader(): pixel shaders disabled "
618 "because of missing driver/hardware support."
622 if(geometry_program != "" &&
623 !driver->queryFeature(video::EVDF_GEOMETRY_SHADER)){
624 infostream<<"generate_shader(): geometry shaders disabled "
625 "because of missing driver/hardware support."
627 geometry_program = "";
630 // If no shaders are used, don't make a separate material type
631 if(vertex_program == "" && pixel_program == "" && geometry_program == "")
634 // Create shaders header
635 std::string shaders_header = "#version 120\n";
637 static const char* drawTypes[] = {
644 "NDT_ALLFACES_OPTIONAL",
651 "NDT_GLASSLIKE_FRAMED"
654 for (int i = 0; i < 14; i++){
655 shaders_header += "#define ";
656 shaders_header += drawTypes[i];
657 shaders_header += " ";
658 shaders_header += itos(i);
659 shaders_header += "\n";
662 static const char* materialTypes[] = {
663 "TILE_MATERIAL_BASIC",
664 "TILE_MATERIAL_ALPHA",
665 "TILE_MATERIAL_LIQUID_TRANSPARENT",
666 "TILE_MATERIAL_LIQUID_OPAQUE",
667 "TILE_MATERIAL_WAVING_LEAVES",
668 "TILE_MATERIAL_WAVING_PLANTS"
671 for (int i = 0; i < 6; i++){
672 shaders_header += "#define ";
673 shaders_header += materialTypes[i];
674 shaders_header += " ";
675 shaders_header += itos(i);
676 shaders_header += "\n";
679 shaders_header += "#define MATERIAL_TYPE ";
680 shaders_header += itos(material_type);
681 shaders_header += "\n";
682 shaders_header += "#define DRAW_TYPE ";
683 shaders_header += itos(drawtype);
684 shaders_header += "\n";
686 if (g_settings->getBool("generate_normalmaps")){
687 shaders_header += "#define GENERATE_NORMALMAPS\n";
688 shaders_header += "#define NORMALMAPS_STRENGTH ";
689 shaders_header += ftos(g_settings->getFloat("normalmaps_strength"));
690 shaders_header += "\n";
692 int smooth = (int)g_settings->getFloat("normalmaps_smooth");
695 sample_step = 0.0078125; // 1.0 / 128.0
698 sample_step = 0.00390625; // 1.0 / 256.0
701 sample_step = 0.001953125; // 1.0 / 512.0
704 sample_step = 0.0078125;
707 shaders_header += "#define SAMPLE_STEP ";
708 shaders_header += ftos(sample_step);
709 shaders_header += "\n";
712 if (g_settings->getBool("enable_bumpmapping"))
713 shaders_header += "#define ENABLE_BUMPMAPPING\n";
715 if (g_settings->getBool("enable_parallax_occlusion")){
716 shaders_header += "#define ENABLE_PARALLAX_OCCLUSION\n";
717 shaders_header += "#define PARALLAX_OCCLUSION_SCALE ";
718 shaders_header += ftos(g_settings->getFloat("parallax_occlusion_scale"));
719 shaders_header += "\n";
720 shaders_header += "#define PARALLAX_OCCLUSION_BIAS ";
721 shaders_header += ftos(g_settings->getFloat("parallax_occlusion_bias"));
722 shaders_header += "\n";
725 if (g_settings->getBool("enable_bumpmapping") || g_settings->getBool("enable_parallax_occlusion"))
726 shaders_header += "#define USE_NORMALMAPS\n";
728 if (g_settings->getBool("enable_waving_water")){
729 shaders_header += "#define ENABLE_WAVING_WATER 1\n";
730 shaders_header += "#define WATER_WAVE_HEIGHT ";
731 shaders_header += ftos(g_settings->getFloat("water_wave_height"));
732 shaders_header += "\n";
733 shaders_header += "#define WATER_WAVE_LENGTH ";
734 shaders_header += ftos(g_settings->getFloat("water_wave_length"));
735 shaders_header += "\n";
736 shaders_header += "#define WATER_WAVE_SPEED ";
737 shaders_header += ftos(g_settings->getFloat("water_wave_speed"));
738 shaders_header += "\n";
740 shaders_header += "#define ENABLE_WAVING_WATER 0\n";
743 shaders_header += "#define ENABLE_WAVING_LEAVES ";
744 if (g_settings->getBool("enable_waving_leaves"))
745 shaders_header += "1\n";
747 shaders_header += "0\n";
749 shaders_header += "#define ENABLE_WAVING_PLANTS ";
750 if (g_settings->getBool("enable_waving_plants"))
751 shaders_header += "1\n";
753 shaders_header += "0\n";
755 if(pixel_program != "")
756 pixel_program = shaders_header + pixel_program;
757 if(vertex_program != "")
758 vertex_program = shaders_header + vertex_program;
759 if(geometry_program != "")
760 geometry_program = shaders_header + geometry_program;
762 // Call addHighLevelShaderMaterial() or addShaderMaterial()
763 const c8* vertex_program_ptr = 0;
764 const c8* pixel_program_ptr = 0;
765 const c8* geometry_program_ptr = 0;
766 if(vertex_program != "")
767 vertex_program_ptr = vertex_program.c_str();
768 if(pixel_program != "")
769 pixel_program_ptr = pixel_program.c_str();
770 if(geometry_program != "")
771 geometry_program_ptr = geometry_program.c_str();
774 infostream<<"Compiling high level shaders for "<<name<<std::endl;
775 shadermat = gpu->addHighLevelShaderMaterial(
776 vertex_program_ptr, // Vertex shader program
777 "vertexMain", // Vertex shader entry point
778 video::EVST_VS_1_1, // Vertex shader version
779 pixel_program_ptr, // Pixel shader program
780 "pixelMain", // Pixel shader entry point
781 video::EPST_PS_1_1, // Pixel shader version
782 geometry_program_ptr, // Geometry shader program
783 "geometryMain", // Geometry shader entry point
784 video::EGST_GS_4_0, // Geometry shader version
785 scene::EPT_TRIANGLES, // Geometry shader input
786 scene::EPT_TRIANGLE_STRIP, // Geometry shader output
787 0, // Support maximum number of vertices
788 callback, // Set-constant callback
789 shaderinfo.base_material, // Base material
790 1 // Userdata passed to callback
793 errorstream<<"generate_shader(): "
794 "failed to generate \""<<name<<"\", "
795 "addHighLevelShaderMaterial failed."
801 infostream<<"Compiling assembly shaders for "<<name<<std::endl;
802 shadermat = gpu->addShaderMaterial(
803 vertex_program_ptr, // Vertex shader program
804 pixel_program_ptr, // Pixel shader program
805 callback, // Set-constant callback
806 shaderinfo.base_material, // Base material
807 0 // Userdata passed to callback
811 errorstream<<"generate_shader(): "
812 "failed to generate \""<<name<<"\", "
813 "addShaderMaterial failed."
819 // HACK, TODO: investigate this better
820 // Grab the material renderer once more so minetest doesn't crash on exit
821 driver->getMaterialRenderer(shadermat)->grab();
823 // Apply the newly created material type
824 shaderinfo.material = (video::E_MATERIAL_TYPE) shadermat;
828 void load_shaders(std::string name, SourceShaderCache *sourcecache,
829 video::E_DRIVER_TYPE drivertype, bool enable_shaders,
830 std::string &vertex_program, std::string &pixel_program,
831 std::string &geometry_program, bool &is_highlevel)
835 geometry_program = "";
836 is_highlevel = false;
839 // Look for high level shaders
840 if(drivertype == video::EDT_DIRECT3D9){
842 // (All shaders in one file)
843 vertex_program = sourcecache->getOrLoad(name, "d3d9.hlsl");
844 pixel_program = vertex_program;
845 geometry_program = vertex_program;
847 else if(drivertype == video::EDT_OPENGL){
849 vertex_program = sourcecache->getOrLoad(name, "opengl_vertex.glsl");
850 pixel_program = sourcecache->getOrLoad(name, "opengl_fragment.glsl");
851 geometry_program = sourcecache->getOrLoad(name, "opengl_geometry.glsl");
853 if(vertex_program != "" || pixel_program != "" || geometry_program != ""){