3 Copyright (C) 2012 celeron55, Perttu Ahola <celeron55@gmail.com>
4 Copyright (C) 2012 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 General Public License as published by
8 the Free Software Foundation; either version 2 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 General Public License for more details.
16 You should have received a copy of the GNU 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"
40 A cache from shader name to shader path
42 MutexedMap<std::string, std::string> g_shadername_to_path_cache;
45 Gets the path to a shader by first checking if the file
46 name_of_shader/filename
47 exists in shader_path and if not, using the data path.
49 If not found, returns "".
51 Utilizes a thread-safe cache.
53 std::string getShaderPath(const std::string &name_of_shader,
54 const std::string &filename)
56 std::string combined = name_of_shader + DIR_DELIM + filename;
57 std::string fullpath = "";
61 bool incache = g_shadername_to_path_cache.get(combined, &fullpath);
66 Check from shader_path
68 std::string shader_path = g_settings->get("shader_path");
71 std::string testpath = shader_path + DIR_DELIM + combined;
72 if(fs::PathExists(testpath))
77 Check from default data directory
81 std::string rel_path = std::string("client") + DIR_DELIM
82 + "shaders" + DIR_DELIM
83 + name_of_shader + DIR_DELIM
85 std::string testpath = porting::path_share + DIR_DELIM + rel_path;
86 if(fs::PathExists(testpath))
90 // Add to cache (also an empty result is cached)
91 g_shadername_to_path_cache.set(combined, fullpath);
98 SourceShaderCache: A cache used for storing source shaders.
101 class SourceShaderCache
104 void insert(const std::string &name_of_shader,
105 const std::string &filename,
106 const std::string &program,
109 std::string combined = name_of_shader + DIR_DELIM + filename;
110 // Try to use local shader instead if asked to
112 std::string path = getShaderPath(name_of_shader, filename);
114 std::string p = readFile(path);
116 m_programs[combined] = p;
121 m_programs[combined] = program;
123 std::string get(const std::string &name_of_shader,
124 const std::string &filename)
126 std::string combined = name_of_shader + DIR_DELIM + filename;
127 core::map<std::string, std::string>::Node *n;
128 n = m_programs.find(combined);
130 return n->getValue();
133 // Primarily fetches from cache, secondarily tries to read from filesystem
134 std::string getOrLoad(const std::string &name_of_shader,
135 const std::string &filename)
137 std::string combined = name_of_shader + DIR_DELIM + filename;
138 core::map<std::string, std::string>::Node *n;
139 n = m_programs.find(combined);
141 return n->getValue();
142 std::string path = getShaderPath(name_of_shader, filename);
144 infostream<<"SourceShaderCache::getOrLoad(): No path found for \""
145 <<combined<<"\""<<std::endl;
148 infostream<<"SourceShaderCache::getOrLoad(): Loading path \""<<path
150 std::string p = readFile(path);
152 m_programs[combined] = p;
158 core::map<std::string, std::string> m_programs;
159 std::string readFile(const std::string &path)
161 std::ifstream is(path.c_str(), std::ios::binary);
164 std::ostringstream tmp_os;
165 tmp_os << is.rdbuf();
171 ShaderCallback: Sets constants that can be used in shaders
174 class ShaderCallback : public video::IShaderConstantSetCallBack
177 ShaderCallback(IrrlichtDevice *device): m_device(device) {}
180 virtual void OnSetConstants(video::IMaterialRendererServices *services, s32 userData)
182 video::IVideoDriver *driver = services->getVideoDriver();
185 bool is_highlevel = userData;
187 // set inverted world matrix
188 core::matrix4 invWorld = driver->getTransform(video::ETS_WORLD);
189 invWorld.makeInverse();
191 services->setVertexShaderConstant("mInvWorld", invWorld.pointer(), 16);
193 services->setVertexShaderConstant(invWorld.pointer(), 0, 4);
196 core::matrix4 worldViewProj;
197 worldViewProj = driver->getTransform(video::ETS_PROJECTION);
198 worldViewProj *= driver->getTransform(video::ETS_VIEW);
199 worldViewProj *= driver->getTransform(video::ETS_WORLD);
201 services->setVertexShaderConstant("mWorldViewProj", worldViewProj.pointer(), 16);
203 services->setVertexShaderConstant(worldViewProj.pointer(), 4, 4);
205 // set transposed world matrix
206 core::matrix4 world = driver->getTransform(video::ETS_WORLD);
207 world = world.getTransposed();
209 services->setVertexShaderConstant("mTransWorld", world.pointer(), 16);
211 services->setVertexShaderConstant(world.pointer(), 8, 4);
215 IrrlichtDevice *m_device;
222 class ShaderSource : public IWritableShaderSource
225 ShaderSource(IrrlichtDevice *device);
229 Gets a shader material id from cache or
230 - if main thread, from getShaderIdDirect
231 - if other thread, adds to request queue and waits for main thread
233 u32 getShaderId(const std::string &name);
236 - If shader material specified by name is found from cache,
237 return the cached id.
238 - Otherwise generate the shader material, add to cache and return id.
240 The id 0 points to a null shader. Its material is EMT_SOLID.
242 u32 getShaderIdDirect(const std::string &name);
244 // Finds out the name of a cached shader.
245 std::string getShaderName(u32 id);
248 If shader specified by the name pointed by the id doesn't
249 exist, create it, then return the cached shader.
251 Can be called from any thread. If called from some other thread
252 and not found in cache, the call is queued to the main thread
255 ShaderInfo getShader(u32 id);
257 ShaderInfo getShader(const std::string &name)
259 return getShader(getShaderId(name));
262 // Processes queued shader requests from other threads.
263 // Shall be called from the main thread.
266 // Insert a shader program into the cache without touching the
267 // filesystem. Shall be called from the main thread.
268 void insertSourceShader(const std::string &name_of_shader,
269 const std::string &filename, const std::string &program);
271 // Rebuild shaders from the current set of source shaders
272 // Shall be called from the main thread.
273 void rebuildShaders();
277 // The id of the thread that is allowed to use irrlicht directly
278 threadid_t m_main_thread;
279 // The irrlicht device
280 IrrlichtDevice *m_device;
281 // The set-constants callback
282 ShaderCallback *m_shader_callback;
284 // Cache of source shaders
285 // This should be only accessed from the main thread
286 SourceShaderCache m_sourcecache;
288 // A shader id is index in this array.
289 // The first position contains a dummy shader.
290 core::array<ShaderInfo> m_shaderinfo_cache;
291 // Maps a shader name to an index in the former.
292 core::map<std::string, u32> m_name_to_id;
293 // The two former containers are behind this mutex
294 JMutex m_shaderinfo_cache_mutex;
296 // Queued shader fetches (to be processed by the main thread)
297 RequestQueue<std::string, u32, u8, u8> m_get_shader_queue;
300 IWritableShaderSource* createShaderSource(IrrlichtDevice *device)
302 return new ShaderSource(device);
306 Generate shader given the shader name.
308 ShaderInfo generate_shader(std::string name, IrrlichtDevice *device,
309 video::IShaderConstantSetCallBack *callback,
310 SourceShaderCache *sourcecache);
315 void load_shaders(std::string name, SourceShaderCache *sourcecache,
316 video::E_DRIVER_TYPE drivertype, s32 enable_shaders,
317 std::string &vertex_program, std::string &pixel_program,
318 std::string &geometry_program, bool &is_highlevel);
320 ShaderSource::ShaderSource(IrrlichtDevice *device):
325 m_shader_callback = new ShaderCallback(device);
327 m_shaderinfo_cache_mutex.Init();
329 m_main_thread = get_current_thread_id();
331 // Add a dummy ShaderInfo as the first index, named ""
332 m_shaderinfo_cache.push_back(ShaderInfo());
333 m_name_to_id[""] = 0;
336 ShaderSource::~ShaderSource()
338 //m_shader_callback->drop();
341 u32 ShaderSource::getShaderId(const std::string &name)
343 //infostream<<"getShaderId(): \""<<name<<"\""<<std::endl;
347 See if shader already exists
349 JMutexAutoLock lock(m_shaderinfo_cache_mutex);
350 core::map<std::string, u32>::Node *n;
351 n = m_name_to_id.find(name);
353 return n->getValue();
359 if(get_current_thread_id() == m_main_thread){
360 return getShaderIdDirect(name);
362 infostream<<"getShaderId(): Queued: name=\""<<name<<"\""<<std::endl;
364 // We're gonna ask the result to be put into here
365 ResultQueue<std::string, u32, u8, u8> result_queue;
367 // Throw a request in
368 m_get_shader_queue.add(name, 0, 0, &result_queue);
370 infostream<<"Waiting for shader from main thread, name=\""
371 <<name<<"\""<<std::endl;
374 // Wait result for a second
375 GetResult<std::string, u32, u8, u8>
376 result = result_queue.pop_front(1000);
378 // Check that at least something worked OK
379 assert(result.key == name);
383 catch(ItemNotFoundException &e){
384 infostream<<"Waiting for shader timed out."<<std::endl;
389 infostream<<"getShaderId(): Failed"<<std::endl;
395 This method generates all the shaders
397 u32 ShaderSource::getShaderIdDirect(const std::string &name)
399 //infostream<<"getShaderIdDirect(): name=\""<<name<<"\""<<std::endl;
401 // Empty name means shader 0
403 infostream<<"getShaderIdDirect(): name is empty"<<std::endl;
408 Calling only allowed from main thread
410 if(get_current_thread_id() != m_main_thread){
411 errorstream<<"ShaderSource::getShaderIdDirect() "
412 "called not from main thread"<<std::endl;
417 See if shader already exists
420 JMutexAutoLock lock(m_shaderinfo_cache_mutex);
422 core::map<std::string, u32>::Node *n;
423 n = m_name_to_id.find(name);
425 /*infostream<<"getShaderIdDirect(): \""<<name
426 <<"\" found in cache"<<std::endl;*/
427 return n->getValue();
431 /*infostream<<"getShaderIdDirect(): \""<<name
432 <<"\" NOT found in cache. Creating it."<<std::endl;*/
434 ShaderInfo info = generate_shader(name, m_device,
435 m_shader_callback, &m_sourcecache);
438 Add shader to caches (add dummy shaders too)
441 JMutexAutoLock lock(m_shaderinfo_cache_mutex);
443 u32 id = m_shaderinfo_cache.size();
444 m_shaderinfo_cache.push_back(info);
445 m_name_to_id.insert(name, id);
447 /*infostream<<"getShaderIdDirect(): "
448 <<"Returning id="<<id<<" for name \""<<name<<"\""<<std::endl;*/
453 std::string ShaderSource::getShaderName(u32 id)
455 JMutexAutoLock lock(m_shaderinfo_cache_mutex);
457 if(id >= m_shaderinfo_cache.size()){
458 errorstream<<"ShaderSource::getShaderName(): id="<<id
459 <<" >= m_shaderinfo_cache.size()="
460 <<m_shaderinfo_cache.size()<<std::endl;
464 return m_shaderinfo_cache[id].name;
467 ShaderInfo ShaderSource::getShader(u32 id)
469 JMutexAutoLock lock(m_shaderinfo_cache_mutex);
471 if(id >= m_shaderinfo_cache.size())
474 return m_shaderinfo_cache[id];
477 void ShaderSource::processQueue()
482 if(m_get_shader_queue.size() > 0){
483 GetRequest<std::string, u32, u8, u8>
484 request = m_get_shader_queue.pop();
486 /*infostream<<"ShaderSource::processQueue(): "
487 <<"got shader request with "
488 <<"name=\""<<request.key<<"\""
491 GetResult<std::string, u32, u8, u8>
493 result.key = request.key;
494 result.callers = request.callers;
495 result.item = getShaderIdDirect(request.key);
497 request.dest->push_back(result);
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 *info = generate_shader(info->name, m_device,
531 m_shader_callback, &m_sourcecache);
535 ShaderInfo generate_shader(std::string name, IrrlichtDevice *device,
536 video::IShaderConstantSetCallBack *callback,
537 SourceShaderCache *sourcecache)
539 /*infostream<<"generate_shader(): "
540 "\""<<name<<"\""<<std::endl;*/
542 ShaderInfo shaderinfo;
543 shaderinfo.name = name;
544 shaderinfo.material = video::EMT_SOLID;
547 Get the base material
549 std::string base_material_name = sourcecache->getOrLoad(name, "base.txt");
550 for(s32 i = 0; video::sBuiltInMaterialTypeNames[i] != 0; i++){
551 if(video::sBuiltInMaterialTypeNames[i] == base_material_name){
552 shaderinfo.material = (video::E_MATERIAL_TYPE) i;
557 // 0 = off, 1 = assembly shaders only, 2 = highlevel or assembly
558 s32 enable_shaders = g_settings->getS32("enable_shaders");
559 if(enable_shaders <= 0)
562 video::IVideoDriver* driver = device->getVideoDriver();
565 video::IGPUProgrammingServices *gpu = driver->getGPUProgrammingServices();
567 errorstream<<"generate_shader(): "
568 "failed to generate \""<<name<<"\", "
569 "GPU programming not supported."
574 // Choose shader language depending on driver type and settings
576 std::string vertex_program;
577 std::string pixel_program;
578 std::string geometry_program;
580 load_shaders(name, sourcecache, driver->getDriverType(),
581 enable_shaders, vertex_program, pixel_program,
582 geometry_program, is_highlevel);
584 // Check hardware/driver support
585 if(vertex_program != "" &&
586 !driver->queryFeature(video::EVDF_VERTEX_SHADER_1_1) &&
587 !driver->queryFeature(video::EVDF_ARB_VERTEX_PROGRAM_1)){
588 infostream<<"generate_shader(): vertex shaders disabled "
589 "because of missing driver/hardware support."
593 if(pixel_program != "" &&
594 !driver->queryFeature(video::EVDF_PIXEL_SHADER_1_1) &&
595 !driver->queryFeature(video::EVDF_ARB_FRAGMENT_PROGRAM_1)){
596 infostream<<"generate_shader(): pixel shaders disabled "
597 "because of missing driver/hardware support."
601 if(geometry_program != "" &&
602 !driver->queryFeature(video::EVDF_GEOMETRY_SHADER)){
603 infostream<<"generate_shader(): geometry shaders disabled "
604 "because of missing driver/hardware support."
606 geometry_program = "";
609 // If no shaders are used, don't make a separate material type
610 if(vertex_program == "" && pixel_program == "" && geometry_program == "")
613 // Call addHighLevelShaderMaterial() or addShaderMaterial()
614 const c8* vertex_program_ptr = 0;
615 const c8* pixel_program_ptr = 0;
616 const c8* geometry_program_ptr = 0;
617 if(vertex_program != "")
618 vertex_program_ptr = vertex_program.c_str();
619 if(pixel_program != "")
620 pixel_program_ptr = pixel_program.c_str();
621 if(geometry_program != "")
622 geometry_program_ptr = geometry_program.c_str();
625 infostream<<"Compiling high level shaders for "<<name<<std::endl;
626 shadermat = gpu->addHighLevelShaderMaterial(
627 vertex_program_ptr, // Vertex shader program
628 "vertexMain", // Vertex shader entry point
629 video::EVST_VS_1_1, // Vertex shader version
630 pixel_program_ptr, // Pixel shader program
631 "pixelMain", // Pixel shader entry point
632 video::EPST_PS_1_1, // Pixel shader version
633 geometry_program_ptr, // Geometry shader program
634 "geometryMain", // Geometry shader entry point
635 video::EGST_GS_4_0, // Geometry shader version
636 scene::EPT_TRIANGLES, // Geometry shader input
637 scene::EPT_TRIANGLE_STRIP, // Geometry shader output
638 0, // Support maximum number of vertices
639 callback, // Set-constant callback
640 shaderinfo.material, // Base material
641 1 // Userdata passed to callback
645 errorstream<<"generate_shader(): "
646 "failed to generate \""<<name<<"\", "
647 "addHighLevelShaderMaterial failed."
653 infostream<<"Compiling assembly shaders for "<<name<<std::endl;
654 shadermat = gpu->addShaderMaterial(
655 vertex_program_ptr, // Vertex shader program
656 pixel_program_ptr, // Pixel shader program
657 callback, // Set-constant callback
658 shaderinfo.material, // Base material
659 0 // Userdata passed to callback
663 errorstream<<"generate_shader(): "
664 "failed to generate \""<<name<<"\", "
665 "addShaderMaterial failed."
671 // HACK, TODO: investigate this better
672 // Grab the material renderer once more so minetest doesn't crash on exit
673 driver->getMaterialRenderer(shadermat)->grab();
675 // Apply the newly created material type
676 shaderinfo.material = (video::E_MATERIAL_TYPE) shadermat;
680 void load_shaders(std::string name, SourceShaderCache *sourcecache,
681 video::E_DRIVER_TYPE drivertype, s32 enable_shaders,
682 std::string &vertex_program, std::string &pixel_program,
683 std::string &geometry_program, bool &is_highlevel)
687 geometry_program = "";
688 is_highlevel = false;
690 if(enable_shaders >= 2){
691 // Look for high level shaders
692 if(drivertype == video::EDT_DIRECT3D9){
694 // (All shaders in one file)
695 vertex_program = sourcecache->getOrLoad(name, "d3d9.hlsl");
696 pixel_program = vertex_program;
697 geometry_program = vertex_program;
699 else if(drivertype == video::EDT_OPENGL){
701 vertex_program = sourcecache->getOrLoad(name, "opengl_vertex.glsl");
702 pixel_program = sourcecache->getOrLoad(name, "opengl_fragment.glsl");
703 geometry_program = sourcecache->getOrLoad(name, "opengl_geometry.glsl");
705 if(vertex_program != "" || pixel_program != "" || geometry_program != ""){
711 if(enable_shaders >= 1){
712 // Look for assembly shaders
713 if(drivertype == video::EDT_DIRECT3D8){
714 // Direct3D 8 assembly shaders
715 vertex_program = sourcecache->getOrLoad(name, "d3d8_vertex.asm");
716 pixel_program = sourcecache->getOrLoad(name, "d3d8_pixel.asm");
718 else if(drivertype == video::EDT_DIRECT3D9){
719 // Direct3D 9 assembly shaders
720 vertex_program = sourcecache->getOrLoad(name, "d3d9_vertex.asm");
721 pixel_program = sourcecache->getOrLoad(name, "d3d9_pixel.asm");
723 else if(drivertype == video::EDT_OPENGL){
724 // OpenGL assembly shaders
725 vertex_program = sourcecache->getOrLoad(name, "opengl_vertex.asm");
726 pixel_program = sourcecache->getOrLoad(name, "opengl_fragment.asm");
728 if(vertex_program != "" || pixel_program != "")