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)
215 ~MainShaderConstantSetter() {}
217 virtual void onSetConstants(video::IMaterialRendererServices *services,
220 video::IVideoDriver *driver = services->getVideoDriver();
223 // set inverted world matrix
224 core::matrix4 invWorld = driver->getTransform(video::ETS_WORLD);
225 invWorld.makeInverse();
227 services->setVertexShaderConstant("mInvWorld", invWorld.pointer(), 16);
229 services->setVertexShaderConstant(invWorld.pointer(), 0, 4);
232 core::matrix4 worldViewProj;
233 worldViewProj = driver->getTransform(video::ETS_PROJECTION);
234 worldViewProj *= driver->getTransform(video::ETS_VIEW);
235 worldViewProj *= driver->getTransform(video::ETS_WORLD);
237 services->setVertexShaderConstant("mWorldViewProj", worldViewProj.pointer(), 16);
239 services->setVertexShaderConstant(worldViewProj.pointer(), 4, 4);
241 // set transposed world matrix
242 core::matrix4 transWorld = driver->getTransform(video::ETS_WORLD);
243 transWorld = transWorld.getTransposed();
245 services->setVertexShaderConstant("mTransWorld", transWorld.pointer(), 16);
247 services->setVertexShaderConstant(transWorld.pointer(), 8, 4);
250 core::matrix4 world = driver->getTransform(video::ETS_WORLD);
252 services->setVertexShaderConstant("mWorld", world.pointer(), 16);
254 services->setVertexShaderConstant(world.pointer(), 8, 4);
263 class ShaderSource : public IWritableShaderSource, public IShaderConstantSetterRegistry
266 ShaderSource(IrrlichtDevice *device);
270 Gets a shader material id from cache or
271 - if main thread, from getShaderIdDirect
272 - if other thread, adds to request queue and waits for main thread
274 u32 getShaderId(const std::string &name);
277 - If shader material specified by name is found from cache,
278 return the cached id.
279 - Otherwise generate the shader material, add to cache and return id.
281 The id 0 points to a null shader. Its material is EMT_SOLID.
283 u32 getShaderIdDirect(const std::string &name);
285 // Finds out the name of a cached shader.
286 std::string getShaderName(u32 id);
289 If shader specified by the name pointed by the id doesn't
290 exist, create it, then return the cached shader.
292 Can be called from any thread. If called from some other thread
293 and not found in cache, the call is queued to the main thread
296 ShaderInfo getShader(u32 id);
298 ShaderInfo getShader(const std::string &name)
300 return getShader(getShaderId(name));
303 // Processes queued shader requests from other threads.
304 // Shall be called from the main thread.
307 // Insert a shader program into the cache without touching the
308 // filesystem. Shall be called from the main thread.
309 void insertSourceShader(const std::string &name_of_shader,
310 const std::string &filename, const std::string &program);
312 // Rebuild shaders from the current set of source shaders
313 // Shall be called from the main thread.
314 void rebuildShaders();
316 void addGlobalConstantSetter(IShaderConstantSetter *setter)
318 m_global_setters.push_back(setter);
321 void onSetConstants(video::IMaterialRendererServices *services,
322 bool is_highlevel, const std::string &name);
326 // The id of the thread that is allowed to use irrlicht directly
327 threadid_t m_main_thread;
328 // The irrlicht device
329 IrrlichtDevice *m_device;
330 // The set-constants callback
331 ShaderCallback *m_shader_callback;
333 // Cache of source shaders
334 // This should be only accessed from the main thread
335 SourceShaderCache m_sourcecache;
337 // A shader id is index in this array.
338 // The first position contains a dummy shader.
339 std::vector<ShaderInfo> m_shaderinfo_cache;
340 // Maps a shader name to an index in the former.
341 std::map<std::string, u32> m_name_to_id;
342 // The two former containers are behind this mutex
343 JMutex m_shaderinfo_cache_mutex;
345 // Queued shader fetches (to be processed by the main thread)
346 RequestQueue<std::string, u32, u8, u8> m_get_shader_queue;
348 // Global constant setters
349 // TODO: Delete these in the destructor
350 std::vector<IShaderConstantSetter*> m_global_setters;
353 IWritableShaderSource* createShaderSource(IrrlichtDevice *device)
355 return new ShaderSource(device);
359 Generate shader given the shader name.
361 ShaderInfo generate_shader(std::string name, IrrlichtDevice *device,
362 video::IShaderConstantSetCallBack *callback,
363 SourceShaderCache *sourcecache);
368 void load_shaders(std::string name, SourceShaderCache *sourcecache,
369 video::E_DRIVER_TYPE drivertype, bool enable_shaders,
370 std::string &vertex_program, std::string &pixel_program,
371 std::string &geometry_program, bool &is_highlevel);
373 ShaderSource::ShaderSource(IrrlichtDevice *device):
378 m_shader_callback = new ShaderCallback(this, "default");
380 m_main_thread = get_current_thread_id();
382 // Add a dummy ShaderInfo as the first index, named ""
383 m_shaderinfo_cache.push_back(ShaderInfo());
384 m_name_to_id[""] = 0;
386 // Add main global constant setter
387 addGlobalConstantSetter(new MainShaderConstantSetter(device));
390 ShaderSource::~ShaderSource()
392 //m_shader_callback->drop();
394 for (std::vector<IShaderConstantSetter*>::iterator iter = m_global_setters.begin();
395 iter != m_global_setters.end(); iter++) {
398 m_global_setters.clear();
401 u32 ShaderSource::getShaderId(const std::string &name)
403 //infostream<<"getShaderId(): \""<<name<<"\""<<std::endl;
407 See if shader already exists
409 JMutexAutoLock lock(m_shaderinfo_cache_mutex);
410 std::map<std::string, u32>::iterator n;
411 n = m_name_to_id.find(name);
412 if(n != m_name_to_id.end())
419 if(get_current_thread_id() == m_main_thread){
420 return getShaderIdDirect(name);
422 /*errorstream<<"getShaderId(): Queued: name=\""<<name<<"\""<<std::endl;*/
424 // We're gonna ask the result to be put into here
426 static ResultQueue<std::string, u32, u8, u8> result_queue;
428 // Throw a request in
429 m_get_shader_queue.add(name, 0, 0, &result_queue);
431 /* infostream<<"Waiting for shader from main thread, name=\""
432 <<name<<"\""<<std::endl;*/
435 GetResult<std::string, u32, u8, u8>
436 result = result_queue.pop_frontNoEx();
438 if (result.key == name) {
442 errorstream << "Got shader with invalid name: " << result.key << std::endl;
448 infostream<<"getShaderId(): Failed"<<std::endl;
454 This method generates all the shaders
456 u32 ShaderSource::getShaderIdDirect(const std::string &name)
458 //infostream<<"getShaderIdDirect(): name=\""<<name<<"\""<<std::endl;
460 // Empty name means shader 0
462 infostream<<"getShaderIdDirect(): name is empty"<<std::endl;
467 Calling only allowed from main thread
469 if(get_current_thread_id() != m_main_thread){
470 errorstream<<"ShaderSource::getShaderIdDirect() "
471 "called not from main thread"<<std::endl;
476 See if shader already exists
479 JMutexAutoLock lock(m_shaderinfo_cache_mutex);
481 std::map<std::string, u32>::iterator n;
482 n = m_name_to_id.find(name);
483 if(n != m_name_to_id.end()){
484 /*infostream<<"getShaderIdDirect(): \""<<name
485 <<"\" found in cache"<<std::endl;*/
490 /*infostream<<"getShaderIdDirect(): \""<<name
491 <<"\" NOT found in cache. Creating it."<<std::endl;*/
493 ShaderInfo info = generate_shader(name, m_device,
494 m_shader_callback, &m_sourcecache);
497 Add shader to caches (add dummy shaders too)
500 JMutexAutoLock lock(m_shaderinfo_cache_mutex);
502 u32 id = m_shaderinfo_cache.size();
503 m_shaderinfo_cache.push_back(info);
504 m_name_to_id[name] = id;
506 /*infostream<<"getShaderIdDirect(): "
507 <<"Returning id="<<id<<" for name \""<<name<<"\""<<std::endl;*/
512 std::string ShaderSource::getShaderName(u32 id)
514 JMutexAutoLock lock(m_shaderinfo_cache_mutex);
516 if(id >= m_shaderinfo_cache.size()){
517 errorstream<<"ShaderSource::getShaderName(): id="<<id
518 <<" >= m_shaderinfo_cache.size()="
519 <<m_shaderinfo_cache.size()<<std::endl;
523 return m_shaderinfo_cache[id].name;
526 ShaderInfo ShaderSource::getShader(u32 id)
528 JMutexAutoLock lock(m_shaderinfo_cache_mutex);
530 if(id >= m_shaderinfo_cache.size())
533 return m_shaderinfo_cache[id];
536 void ShaderSource::processQueue()
541 //NOTE this is only thread safe for ONE consumer thread!
542 if(!m_get_shader_queue.empty()){
543 GetRequest<std::string, u32, u8, u8>
544 request = m_get_shader_queue.pop();
546 /**errorstream<<"ShaderSource::processQueue(): "
547 <<"got shader request with "
548 <<"name=\""<<request.key<<"\""
551 m_get_shader_queue.pushResult(request,getShaderIdDirect(request.key));
555 void ShaderSource::insertSourceShader(const std::string &name_of_shader,
556 const std::string &filename, const std::string &program)
558 /*infostream<<"ShaderSource::insertSourceShader(): "
559 "name_of_shader=\""<<name_of_shader<<"\", "
560 "filename=\""<<filename<<"\""<<std::endl;*/
562 assert(get_current_thread_id() == m_main_thread);
564 m_sourcecache.insert(name_of_shader, filename, program, true);
567 void ShaderSource::rebuildShaders()
569 JMutexAutoLock lock(m_shaderinfo_cache_mutex);
571 /*// Oh well... just clear everything, they'll load sometime.
572 m_shaderinfo_cache.clear();
573 m_name_to_id.clear();*/
576 FIXME: Old shader materials can't be deleted in Irrlicht,
578 (This would be nice to do in the destructor too)
582 for(u32 i=0; i<m_shaderinfo_cache.size(); i++){
583 ShaderInfo *info = &m_shaderinfo_cache[i];
584 if(info->name != ""){
585 *info = generate_shader(info->name, m_device,
586 m_shader_callback, &m_sourcecache);
591 void ShaderSource::onSetConstants(video::IMaterialRendererServices *services,
592 bool is_highlevel, const std::string &name)
594 for(u32 i=0; i<m_global_setters.size(); i++){
595 IShaderConstantSetter *setter = m_global_setters[i];
596 setter->onSetConstants(services, is_highlevel);
600 ShaderInfo generate_shader(std::string name, IrrlichtDevice *device,
601 video::IShaderConstantSetCallBack *callback,
602 SourceShaderCache *sourcecache)
604 /*infostream<<"generate_shader(): "
605 "\""<<name<<"\""<<std::endl;*/
607 ShaderInfo shaderinfo;
608 shaderinfo.name = name;
609 shaderinfo.material = video::EMT_SOLID;
612 Get the base material
614 std::string base_material_name =
615 trim(sourcecache->getOrLoad(name, "base.txt"));
616 for(s32 i = 0; video::sBuiltInMaterialTypeNames[i] != 0; i++){
617 if(video::sBuiltInMaterialTypeNames[i] == base_material_name){
618 shaderinfo.material = (video::E_MATERIAL_TYPE) i;
623 bool enable_shaders = g_settings->getBool("enable_shaders");
627 video::IVideoDriver* driver = device->getVideoDriver();
630 video::IGPUProgrammingServices *gpu = driver->getGPUProgrammingServices();
632 errorstream<<"generate_shader(): "
633 "failed to generate \""<<name<<"\", "
634 "GPU programming not supported."
639 // Choose shader language depending on driver type and settings
641 std::string vertex_program;
642 std::string pixel_program;
643 std::string geometry_program;
645 load_shaders(name, sourcecache, driver->getDriverType(),
646 enable_shaders, vertex_program, pixel_program,
647 geometry_program, is_highlevel);
649 // Check hardware/driver support
650 if(vertex_program != "" &&
651 !driver->queryFeature(video::EVDF_VERTEX_SHADER_1_1) &&
652 !driver->queryFeature(video::EVDF_ARB_VERTEX_PROGRAM_1)){
653 infostream<<"generate_shader(): vertex shaders disabled "
654 "because of missing driver/hardware support."
658 if(pixel_program != "" &&
659 !driver->queryFeature(video::EVDF_PIXEL_SHADER_1_1) &&
660 !driver->queryFeature(video::EVDF_ARB_FRAGMENT_PROGRAM_1)){
661 infostream<<"generate_shader(): pixel shaders disabled "
662 "because of missing driver/hardware support."
666 if(geometry_program != "" &&
667 !driver->queryFeature(video::EVDF_GEOMETRY_SHADER)){
668 infostream<<"generate_shader(): geometry shaders disabled "
669 "because of missing driver/hardware support."
671 geometry_program = "";
674 // If no shaders are used, don't make a separate material type
675 if(vertex_program == "" && pixel_program == "" && geometry_program == "")
678 // Create shaders header
679 std::string shaders_header = "#version 120\n";
681 if (g_settings->getBool("generate_normalmaps")){
682 shaders_header += "#define GENERATE_NORMALMAPS\n";
683 shaders_header += "#define NORMALMAPS_STRENGTH ";
684 shaders_header += ftos(g_settings->getFloat("normalmaps_strength"));
685 shaders_header += "\n";
687 int smooth = (int)g_settings->getFloat("normalmaps_smooth");
690 sample_step = 0.0078125; // 1.0 / 128.0
693 sample_step = 0.00390625; // 1.0 / 256.0
696 sample_step = 0.001953125; // 1.0 / 512.0
699 sample_step = 0.0078125;
702 shaders_header += "#define SAMPLE_STEP ";
703 shaders_header += ftos(sample_step);
704 shaders_header += "\n";
707 if (g_settings->getBool("enable_bumpmapping"))
708 shaders_header += "#define ENABLE_BUMPMAPPING\n";
710 if (g_settings->getBool("enable_parallax_occlusion")){
711 shaders_header += "#define ENABLE_PARALLAX_OCCLUSION\n";
712 shaders_header += "#define PARALLAX_OCCLUSION_SCALE ";
713 shaders_header += ftos(g_settings->getFloat("parallax_occlusion_scale"));
714 shaders_header += "\n";
715 shaders_header += "#define PARALLAX_OCCLUSION_BIAS ";
716 shaders_header += ftos(g_settings->getFloat("parallax_occlusion_bias"));
717 shaders_header += "\n";
720 if (g_settings->getBool("enable_bumpmapping") || g_settings->getBool("enable_parallax_occlusion"))
721 shaders_header += "#define USE_NORMALMAPS\n";
723 if (g_settings->getBool("enable_waving_water")){
724 shaders_header += "#define ENABLE_WAVING_WATER\n";
725 shaders_header += "#define WATER_WAVE_HEIGHT ";
726 shaders_header += ftos(g_settings->getFloat("water_wave_height"));
727 shaders_header += "\n";
728 shaders_header += "#define WATER_WAVE_LENGTH ";
729 shaders_header += ftos(g_settings->getFloat("water_wave_length"));
730 shaders_header += "\n";
731 shaders_header += "#define WATER_WAVE_SPEED ";
732 shaders_header += ftos(g_settings->getFloat("water_wave_speed"));
733 shaders_header += "\n";
736 if (g_settings->getBool("enable_waving_leaves"))
737 shaders_header += "#define ENABLE_WAVING_LEAVES\n";
739 if (g_settings->getBool("enable_waving_plants"))
740 shaders_header += "#define ENABLE_WAVING_PLANTS\n";
742 if(pixel_program != "")
743 pixel_program = shaders_header + pixel_program;
744 if(vertex_program != "")
745 vertex_program = shaders_header + vertex_program;
746 if(geometry_program != "")
747 geometry_program = shaders_header + geometry_program;
749 // Call addHighLevelShaderMaterial() or addShaderMaterial()
750 const c8* vertex_program_ptr = 0;
751 const c8* pixel_program_ptr = 0;
752 const c8* geometry_program_ptr = 0;
753 if(vertex_program != "")
754 vertex_program_ptr = vertex_program.c_str();
755 if(pixel_program != "")
756 pixel_program_ptr = pixel_program.c_str();
757 if(geometry_program != "")
758 geometry_program_ptr = geometry_program.c_str();
761 infostream<<"Compiling high level shaders for "<<name<<std::endl;
762 shadermat = gpu->addHighLevelShaderMaterial(
763 vertex_program_ptr, // Vertex shader program
764 "vertexMain", // Vertex shader entry point
765 video::EVST_VS_1_1, // Vertex shader version
766 pixel_program_ptr, // Pixel shader program
767 "pixelMain", // Pixel shader entry point
768 video::EPST_PS_1_1, // Pixel shader version
769 geometry_program_ptr, // Geometry shader program
770 "geometryMain", // Geometry shader entry point
771 video::EGST_GS_4_0, // Geometry shader version
772 scene::EPT_TRIANGLES, // Geometry shader input
773 scene::EPT_TRIANGLE_STRIP, // Geometry shader output
774 0, // Support maximum number of vertices
775 callback, // Set-constant callback
776 shaderinfo.material, // Base material
777 1 // Userdata passed to callback
781 errorstream<<"generate_shader(): "
782 "failed to generate \""<<name<<"\", "
783 "addHighLevelShaderMaterial failed."
789 infostream<<"Compiling assembly shaders for "<<name<<std::endl;
790 shadermat = gpu->addShaderMaterial(
791 vertex_program_ptr, // Vertex shader program
792 pixel_program_ptr, // Pixel shader program
793 callback, // Set-constant callback
794 shaderinfo.material, // Base material
795 0 // Userdata passed to callback
799 errorstream<<"generate_shader(): "
800 "failed to generate \""<<name<<"\", "
801 "addShaderMaterial failed."
807 // HACK, TODO: investigate this better
808 // Grab the material renderer once more so minetest doesn't crash on exit
809 driver->getMaterialRenderer(shadermat)->grab();
811 // Apply the newly created material type
812 shaderinfo.material = (video::E_MATERIAL_TYPE) shadermat;
816 void load_shaders(std::string name, SourceShaderCache *sourcecache,
817 video::E_DRIVER_TYPE drivertype, bool enable_shaders,
818 std::string &vertex_program, std::string &pixel_program,
819 std::string &geometry_program, bool &is_highlevel)
823 geometry_program = "";
824 is_highlevel = false;
827 // Look for high level shaders
828 if(drivertype == video::EDT_DIRECT3D9){
830 // (All shaders in one file)
831 vertex_program = sourcecache->getOrLoad(name, "d3d9.hlsl");
832 pixel_program = vertex_program;
833 geometry_program = vertex_program;
835 else if(drivertype == video::EDT_OPENGL){
837 vertex_program = sourcecache->getOrLoad(name, "opengl_vertex.glsl");
838 pixel_program = sourcecache->getOrLoad(name, "opengl_fragment.glsl");
839 geometry_program = sourcecache->getOrLoad(name, "opengl_geometry.glsl");
841 if(vertex_program != "" || pixel_program != "" || geometry_program != ""){