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 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);
260 IrrlichtDevice *m_device;
267 class ShaderSource : public IWritableShaderSource, public IShaderConstantSetterRegistry
270 ShaderSource(IrrlichtDevice *device);
274 Gets a shader material id from cache or
275 - if main thread, from getShaderIdDirect
276 - if other thread, adds to request queue and waits for main thread
278 u32 getShaderId(const std::string &name);
281 - If shader material specified by name is found from cache,
282 return the cached id.
283 - Otherwise generate the shader material, add to cache and return id.
285 The id 0 points to a null shader. Its material is EMT_SOLID.
287 u32 getShaderIdDirect(const std::string &name);
289 // Finds out the name of a cached shader.
290 std::string getShaderName(u32 id);
293 If shader specified by the name pointed by the id doesn't
294 exist, create it, then return the cached shader.
296 Can be called from any thread. If called from some other thread
297 and not found in cache, the call is queued to the main thread
300 ShaderInfo getShader(u32 id);
302 ShaderInfo getShader(const std::string &name)
304 return getShader(getShaderId(name));
307 // Processes queued shader requests from other threads.
308 // Shall be called from the main thread.
311 // Insert a shader program into the cache without touching the
312 // filesystem. Shall be called from the main thread.
313 void insertSourceShader(const std::string &name_of_shader,
314 const std::string &filename, const std::string &program);
316 // Rebuild shaders from the current set of source shaders
317 // Shall be called from the main thread.
318 void rebuildShaders();
320 void addGlobalConstantSetter(IShaderConstantSetter *setter)
322 m_global_setters.push_back(setter);
325 void onSetConstants(video::IMaterialRendererServices *services,
326 bool is_highlevel, const std::string &name);
330 // The id of the thread that is allowed to use irrlicht directly
331 threadid_t m_main_thread;
332 // The irrlicht device
333 IrrlichtDevice *m_device;
334 // The set-constants callback
335 ShaderCallback *m_shader_callback;
337 // Cache of source shaders
338 // This should be only accessed from the main thread
339 SourceShaderCache m_sourcecache;
341 // A shader id is index in this array.
342 // The first position contains a dummy shader.
343 std::vector<ShaderInfo> m_shaderinfo_cache;
344 // Maps a shader name to an index in the former.
345 std::map<std::string, u32> m_name_to_id;
346 // The two former containers are behind this mutex
347 JMutex m_shaderinfo_cache_mutex;
349 // Queued shader fetches (to be processed by the main thread)
350 RequestQueue<std::string, u32, u8, u8> m_get_shader_queue;
352 // Global constant setters
353 // TODO: Delete these in the destructor
354 std::vector<IShaderConstantSetter*> m_global_setters;
357 IWritableShaderSource* createShaderSource(IrrlichtDevice *device)
359 return new ShaderSource(device);
363 Generate shader given the shader name.
365 ShaderInfo generate_shader(std::string name, IrrlichtDevice *device,
366 video::IShaderConstantSetCallBack *callback,
367 SourceShaderCache *sourcecache);
372 void load_shaders(std::string name, SourceShaderCache *sourcecache,
373 video::E_DRIVER_TYPE drivertype, bool enable_shaders,
374 std::string &vertex_program, std::string &pixel_program,
375 std::string &geometry_program, bool &is_highlevel);
377 ShaderSource::ShaderSource(IrrlichtDevice *device):
382 m_shader_callback = new ShaderCallback(this, "default");
384 m_main_thread = get_current_thread_id();
386 // Add a dummy ShaderInfo as the first index, named ""
387 m_shaderinfo_cache.push_back(ShaderInfo());
388 m_name_to_id[""] = 0;
390 // Add main global constant setter
391 addGlobalConstantSetter(new MainShaderConstantSetter(device));
394 ShaderSource::~ShaderSource()
396 //m_shader_callback->drop();
398 for (std::vector<IShaderConstantSetter*>::iterator iter = m_global_setters.begin();
399 iter != m_global_setters.end(); iter++) {
402 m_global_setters.clear();
405 u32 ShaderSource::getShaderId(const std::string &name)
407 //infostream<<"getShaderId(): \""<<name<<"\""<<std::endl;
411 See if shader already exists
413 JMutexAutoLock lock(m_shaderinfo_cache_mutex);
414 std::map<std::string, u32>::iterator n;
415 n = m_name_to_id.find(name);
416 if(n != m_name_to_id.end())
423 if(get_current_thread_id() == m_main_thread){
424 return getShaderIdDirect(name);
426 /*errorstream<<"getShaderId(): Queued: name=\""<<name<<"\""<<std::endl;*/
428 // We're gonna ask the result to be put into here
430 static ResultQueue<std::string, u32, u8, u8> result_queue;
432 // Throw a request in
433 m_get_shader_queue.add(name, 0, 0, &result_queue);
435 /* infostream<<"Waiting for shader from main thread, name=\""
436 <<name<<"\""<<std::endl;*/
439 GetResult<std::string, u32, u8, u8>
440 result = result_queue.pop_frontNoEx();
442 if (result.key == name) {
446 errorstream << "Got shader with invalid name: " << result.key << std::endl;
452 infostream<<"getShaderId(): Failed"<<std::endl;
458 This method generates all the shaders
460 u32 ShaderSource::getShaderIdDirect(const std::string &name)
462 //infostream<<"getShaderIdDirect(): name=\""<<name<<"\""<<std::endl;
464 // Empty name means shader 0
466 infostream<<"getShaderIdDirect(): name is empty"<<std::endl;
471 Calling only allowed from main thread
473 if(get_current_thread_id() != m_main_thread){
474 errorstream<<"ShaderSource::getShaderIdDirect() "
475 "called not from main thread"<<std::endl;
480 See if shader already exists
483 JMutexAutoLock lock(m_shaderinfo_cache_mutex);
485 std::map<std::string, u32>::iterator n;
486 n = m_name_to_id.find(name);
487 if(n != m_name_to_id.end()){
488 /*infostream<<"getShaderIdDirect(): \""<<name
489 <<"\" found in cache"<<std::endl;*/
494 /*infostream<<"getShaderIdDirect(): \""<<name
495 <<"\" NOT found in cache. Creating it."<<std::endl;*/
497 ShaderInfo info = generate_shader(name, m_device,
498 m_shader_callback, &m_sourcecache);
501 Add shader to caches (add dummy shaders too)
504 JMutexAutoLock lock(m_shaderinfo_cache_mutex);
506 u32 id = m_shaderinfo_cache.size();
507 m_shaderinfo_cache.push_back(info);
508 m_name_to_id[name] = id;
510 /*infostream<<"getShaderIdDirect(): "
511 <<"Returning id="<<id<<" for name \""<<name<<"\""<<std::endl;*/
516 std::string ShaderSource::getShaderName(u32 id)
518 JMutexAutoLock lock(m_shaderinfo_cache_mutex);
520 if(id >= m_shaderinfo_cache.size()){
521 errorstream<<"ShaderSource::getShaderName(): id="<<id
522 <<" >= m_shaderinfo_cache.size()="
523 <<m_shaderinfo_cache.size()<<std::endl;
527 return m_shaderinfo_cache[id].name;
530 ShaderInfo ShaderSource::getShader(u32 id)
532 JMutexAutoLock lock(m_shaderinfo_cache_mutex);
534 if(id >= m_shaderinfo_cache.size())
537 return m_shaderinfo_cache[id];
540 void ShaderSource::processQueue()
545 //NOTE this is only thread safe for ONE consumer thread!
546 if(!m_get_shader_queue.empty()){
547 GetRequest<std::string, u32, u8, u8>
548 request = m_get_shader_queue.pop();
550 /**errorstream<<"ShaderSource::processQueue(): "
551 <<"got shader request with "
552 <<"name=\""<<request.key<<"\""
555 m_get_shader_queue.pushResult(request,getShaderIdDirect(request.key));
559 void ShaderSource::insertSourceShader(const std::string &name_of_shader,
560 const std::string &filename, const std::string &program)
562 /*infostream<<"ShaderSource::insertSourceShader(): "
563 "name_of_shader=\""<<name_of_shader<<"\", "
564 "filename=\""<<filename<<"\""<<std::endl;*/
566 assert(get_current_thread_id() == m_main_thread);
568 m_sourcecache.insert(name_of_shader, filename, program, true);
571 void ShaderSource::rebuildShaders()
573 JMutexAutoLock lock(m_shaderinfo_cache_mutex);
575 /*// Oh well... just clear everything, they'll load sometime.
576 m_shaderinfo_cache.clear();
577 m_name_to_id.clear();*/
580 FIXME: Old shader materials can't be deleted in Irrlicht,
582 (This would be nice to do in the destructor too)
586 for(u32 i=0; i<m_shaderinfo_cache.size(); i++){
587 ShaderInfo *info = &m_shaderinfo_cache[i];
588 if(info->name != ""){
589 *info = generate_shader(info->name, m_device,
590 m_shader_callback, &m_sourcecache);
595 void ShaderSource::onSetConstants(video::IMaterialRendererServices *services,
596 bool is_highlevel, const std::string &name)
598 for(u32 i=0; i<m_global_setters.size(); i++){
599 IShaderConstantSetter *setter = m_global_setters[i];
600 setter->onSetConstants(services, is_highlevel);
604 ShaderInfo generate_shader(std::string name, IrrlichtDevice *device,
605 video::IShaderConstantSetCallBack *callback,
606 SourceShaderCache *sourcecache)
608 /*infostream<<"generate_shader(): "
609 "\""<<name<<"\""<<std::endl;*/
611 ShaderInfo shaderinfo;
612 shaderinfo.name = name;
613 shaderinfo.material = video::EMT_SOLID;
616 Get the base material
618 std::string base_material_name =
619 trim(sourcecache->getOrLoad(name, "base.txt"));
620 for(s32 i = 0; video::sBuiltInMaterialTypeNames[i] != 0; i++){
621 if(video::sBuiltInMaterialTypeNames[i] == base_material_name){
622 shaderinfo.material = (video::E_MATERIAL_TYPE) i;
627 bool enable_shaders = g_settings->getBool("enable_shaders");
631 video::IVideoDriver* driver = device->getVideoDriver();
634 video::IGPUProgrammingServices *gpu = driver->getGPUProgrammingServices();
636 errorstream<<"generate_shader(): "
637 "failed to generate \""<<name<<"\", "
638 "GPU programming not supported."
643 // Choose shader language depending on driver type and settings
645 std::string vertex_program;
646 std::string pixel_program;
647 std::string geometry_program;
649 load_shaders(name, sourcecache, driver->getDriverType(),
650 enable_shaders, vertex_program, pixel_program,
651 geometry_program, is_highlevel);
653 // Check hardware/driver support
654 if(vertex_program != "" &&
655 !driver->queryFeature(video::EVDF_VERTEX_SHADER_1_1) &&
656 !driver->queryFeature(video::EVDF_ARB_VERTEX_PROGRAM_1)){
657 infostream<<"generate_shader(): vertex shaders disabled "
658 "because of missing driver/hardware support."
662 if(pixel_program != "" &&
663 !driver->queryFeature(video::EVDF_PIXEL_SHADER_1_1) &&
664 !driver->queryFeature(video::EVDF_ARB_FRAGMENT_PROGRAM_1)){
665 infostream<<"generate_shader(): pixel shaders disabled "
666 "because of missing driver/hardware support."
670 if(geometry_program != "" &&
671 !driver->queryFeature(video::EVDF_GEOMETRY_SHADER)){
672 infostream<<"generate_shader(): geometry shaders disabled "
673 "because of missing driver/hardware support."
675 geometry_program = "";
678 // If no shaders are used, don't make a separate material type
679 if(vertex_program == "" && pixel_program == "" && geometry_program == "")
682 // Create shaders header
683 std::string shaders_header = "#version 120\n";
685 if (g_settings->getBool("generate_normalmaps")){
686 shaders_header += "#define GENERATE_NORMALMAPS\n";
687 shaders_header += "#define NORMALMAPS_STRENGTH ";
688 shaders_header += ftos(g_settings->getFloat("normalmaps_strength"));
689 shaders_header += "\n";
691 int smooth = (int)g_settings->getFloat("normalmaps_smooth");
694 sample_step = 0.0078125; // 1.0 / 128.0
697 sample_step = 0.00390625; // 1.0 / 256.0
700 sample_step = 0.001953125; // 1.0 / 512.0
703 sample_step = 0.0078125;
706 shaders_header += "#define SAMPLE_STEP ";
707 shaders_header += ftos(sample_step);
708 shaders_header += "\n";
711 if (g_settings->getBool("enable_bumpmapping"))
712 shaders_header += "#define ENABLE_BUMPMAPPING\n";
714 if (g_settings->getBool("enable_parallax_occlusion")){
715 shaders_header += "#define ENABLE_PARALLAX_OCCLUSION\n";
716 shaders_header += "#define PARALLAX_OCCLUSION_SCALE ";
717 shaders_header += ftos(g_settings->getFloat("parallax_occlusion_scale"));
718 shaders_header += "\n";
719 shaders_header += "#define PARALLAX_OCCLUSION_BIAS ";
720 shaders_header += ftos(g_settings->getFloat("parallax_occlusion_bias"));
721 shaders_header += "\n";
724 if (g_settings->getBool("enable_bumpmapping") || g_settings->getBool("enable_parallax_occlusion"))
725 shaders_header += "#define USE_NORMALMAPS\n";
727 if (g_settings->getBool("enable_waving_water")){
728 shaders_header += "#define ENABLE_WAVING_WATER\n";
729 shaders_header += "#define WATER_WAVE_HEIGHT ";
730 shaders_header += ftos(g_settings->getFloat("water_wave_height"));
731 shaders_header += "\n";
732 shaders_header += "#define WATER_WAVE_LENGTH ";
733 shaders_header += ftos(g_settings->getFloat("water_wave_length"));
734 shaders_header += "\n";
735 shaders_header += "#define WATER_WAVE_SPEED ";
736 shaders_header += ftos(g_settings->getFloat("water_wave_speed"));
737 shaders_header += "\n";
740 if (g_settings->getBool("enable_waving_leaves"))
741 shaders_header += "#define ENABLE_WAVING_LEAVES\n";
743 if (g_settings->getBool("enable_waving_plants"))
744 shaders_header += "#define ENABLE_WAVING_PLANTS\n";
746 if(pixel_program != "")
747 pixel_program = shaders_header + pixel_program;
748 if(vertex_program != "")
749 vertex_program = shaders_header + vertex_program;
750 if(geometry_program != "")
751 geometry_program = shaders_header + geometry_program;
753 // Call addHighLevelShaderMaterial() or addShaderMaterial()
754 const c8* vertex_program_ptr = 0;
755 const c8* pixel_program_ptr = 0;
756 const c8* geometry_program_ptr = 0;
757 if(vertex_program != "")
758 vertex_program_ptr = vertex_program.c_str();
759 if(pixel_program != "")
760 pixel_program_ptr = pixel_program.c_str();
761 if(geometry_program != "")
762 geometry_program_ptr = geometry_program.c_str();
765 infostream<<"Compiling high level shaders for "<<name<<std::endl;
766 shadermat = gpu->addHighLevelShaderMaterial(
767 vertex_program_ptr, // Vertex shader program
768 "vertexMain", // Vertex shader entry point
769 video::EVST_VS_1_1, // Vertex shader version
770 pixel_program_ptr, // Pixel shader program
771 "pixelMain", // Pixel shader entry point
772 video::EPST_PS_1_1, // Pixel shader version
773 geometry_program_ptr, // Geometry shader program
774 "geometryMain", // Geometry shader entry point
775 video::EGST_GS_4_0, // Geometry shader version
776 scene::EPT_TRIANGLES, // Geometry shader input
777 scene::EPT_TRIANGLE_STRIP, // Geometry shader output
778 0, // Support maximum number of vertices
779 callback, // Set-constant callback
780 shaderinfo.material, // Base material
781 1 // Userdata passed to callback
785 errorstream<<"generate_shader(): "
786 "failed to generate \""<<name<<"\", "
787 "addHighLevelShaderMaterial failed."
793 infostream<<"Compiling assembly shaders for "<<name<<std::endl;
794 shadermat = gpu->addShaderMaterial(
795 vertex_program_ptr, // Vertex shader program
796 pixel_program_ptr, // Pixel shader program
797 callback, // Set-constant callback
798 shaderinfo.material, // Base material
799 0 // Userdata passed to callback
803 errorstream<<"generate_shader(): "
804 "failed to generate \""<<name<<"\", "
805 "addShaderMaterial failed."
811 // HACK, TODO: investigate this better
812 // Grab the material renderer once more so minetest doesn't crash on exit
813 driver->getMaterialRenderer(shadermat)->grab();
815 // Apply the newly created material type
816 shaderinfo.material = (video::E_MATERIAL_TYPE) shadermat;
820 void load_shaders(std::string name, SourceShaderCache *sourcecache,
821 video::E_DRIVER_TYPE drivertype, bool enable_shaders,
822 std::string &vertex_program, std::string &pixel_program,
823 std::string &geometry_program, bool &is_highlevel)
827 geometry_program = "";
828 is_highlevel = false;
831 // Look for high level shaders
832 if(drivertype == video::EDT_DIRECT3D9){
834 // (All shaders in one file)
835 vertex_program = sourcecache->getOrLoad(name, "d3d9.hlsl");
836 pixel_program = vertex_program;
837 geometry_program = vertex_program;
839 else if(drivertype == video::EDT_OPENGL){
841 vertex_program = sourcecache->getOrLoad(name, "opengl_vertex.glsl");
842 pixel_program = sourcecache->getOrLoad(name, "opengl_fragment.glsl");
843 geometry_program = sourcecache->getOrLoad(name, "opengl_geometry.glsl");
845 if(vertex_program != "" || pixel_program != "" || geometry_program != ""){