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.
24 #include "irrlichttypes_extrabloated.h"
27 #include "util/container.h"
28 #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 "client/renderingengine.h"
36 #include "EShaderTypes.h"
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;
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");
71 if (!shader_path.empty()) {
72 std::string testpath = shader_path + DIR_DELIM + combined;
73 if(fs::PathExists(testpath))
78 Check from default data directory
80 if (fullpath.empty()) {
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, const std::string &filename,
105 const std::string &program, bool prefer_local)
107 std::string combined = name_of_shader + DIR_DELIM + filename;
108 // Try to use local shader instead if asked to
110 std::string path = getShaderPath(name_of_shader, filename);
112 std::string p = readFile(path);
114 m_programs[combined] = p;
119 m_programs[combined] = program;
122 std::string get(const std::string &name_of_shader,
123 const std::string &filename)
125 std::string combined = name_of_shader + DIR_DELIM + filename;
126 StringMap::iterator n = m_programs.find(combined);
127 if (n != m_programs.end())
132 // Primarily fetches from cache, secondarily tries to read from filesystem
133 std::string getOrLoad(const std::string &name_of_shader,
134 const std::string &filename)
136 std::string combined = name_of_shader + DIR_DELIM + filename;
137 StringMap::iterator n = m_programs.find(combined);
138 if (n != m_programs.end())
140 std::string path = getShaderPath(name_of_shader, filename);
142 infostream << "SourceShaderCache::getOrLoad(): No path found for \""
143 << combined << "\"" << std::endl;
146 infostream << "SourceShaderCache::getOrLoad(): Loading path \""
147 << path << "\"" << std::endl;
148 std::string p = readFile(path);
150 m_programs[combined] = p;
156 StringMap m_programs;
158 std::string readFile(const std::string &path)
160 std::ifstream is(path.c_str(), std::ios::binary);
163 std::ostringstream tmp_os;
164 tmp_os << is.rdbuf();
171 ShaderCallback: Sets constants that can be used in shaders
174 class ShaderCallback : public video::IShaderConstantSetCallBack
176 std::vector<IShaderConstantSetter*> m_setters;
179 ShaderCallback(const std::vector<IShaderConstantSetterFactory *> &factories)
181 for (IShaderConstantSetterFactory *factory : factories)
182 m_setters.push_back(factory->create());
187 for (IShaderConstantSetter *setter : m_setters)
191 virtual void OnSetConstants(video::IMaterialRendererServices *services, s32 userData)
193 video::IVideoDriver *driver = services->getVideoDriver();
194 sanity_check(driver != NULL);
196 bool is_highlevel = userData;
198 for (IShaderConstantSetter *setter : m_setters)
199 setter->onSetConstants(services, is_highlevel);
205 MainShaderConstantSetter: Set basic constants required for almost everything
208 class MainShaderConstantSetter : public IShaderConstantSetter
210 CachedVertexShaderSetting<float, 16> m_world_view_proj;
211 CachedVertexShaderSetting<float, 16> m_world;
214 MainShaderConstantSetter() :
215 m_world_view_proj("mWorldViewProj"),
218 ~MainShaderConstantSetter() = default;
220 virtual void onSetConstants(video::IMaterialRendererServices *services,
223 video::IVideoDriver *driver = services->getVideoDriver();
224 sanity_check(driver);
227 core::matrix4 worldViewProj;
228 worldViewProj = driver->getTransform(video::ETS_PROJECTION);
229 worldViewProj *= driver->getTransform(video::ETS_VIEW);
230 worldViewProj *= driver->getTransform(video::ETS_WORLD);
232 m_world_view_proj.set(*reinterpret_cast<float(*)[16]>(worldViewProj.pointer()), services);
234 services->setVertexShaderConstant(worldViewProj.pointer(), 0, 4);
237 core::matrix4 world = driver->getTransform(video::ETS_WORLD);
239 m_world.set(*reinterpret_cast<float(*)[16]>(world.pointer()), services);
241 services->setVertexShaderConstant(world.pointer(), 4, 4);
247 class MainShaderConstantSetterFactory : public IShaderConstantSetterFactory
250 virtual IShaderConstantSetter* create()
251 { return new MainShaderConstantSetter(); }
259 class ShaderSource : public IWritableShaderSource
266 - If shader material specified by name is found from cache,
267 return the cached id.
268 - Otherwise generate the shader material, add to cache and return id.
270 The id 0 points to a null shader. Its material is EMT_SOLID.
272 u32 getShaderIdDirect(const std::string &name,
273 const u8 material_type, const u8 drawtype);
276 If shader specified by the name pointed by the id doesn't
277 exist, create it, then return id.
279 Can be called from any thread. If called from some other thread
280 and not found in cache, the call is queued to the main thread
284 u32 getShader(const std::string &name,
285 const u8 material_type, const u8 drawtype);
287 ShaderInfo getShaderInfo(u32 id);
289 // Processes queued shader requests from other threads.
290 // Shall be called from the main thread.
293 // Insert a shader program into the cache without touching the
294 // filesystem. Shall be called from the main thread.
295 void insertSourceShader(const std::string &name_of_shader,
296 const std::string &filename, const std::string &program);
298 // Rebuild shaders from the current set of source shaders
299 // Shall be called from the main thread.
300 void rebuildShaders();
302 void addShaderConstantSetterFactory(IShaderConstantSetterFactory *setter)
304 m_setter_factories.push_back(setter);
309 // The id of the thread that is allowed to use irrlicht directly
310 std::thread::id m_main_thread;
312 // Cache of source shaders
313 // This should be only accessed from the main thread
314 SourceShaderCache m_sourcecache;
316 // A shader id is index in this array.
317 // The first position contains a dummy shader.
318 std::vector<ShaderInfo> m_shaderinfo_cache;
319 // The former container is behind this mutex
320 std::mutex m_shaderinfo_cache_mutex;
322 // Queued shader fetches (to be processed by the main thread)
323 RequestQueue<std::string, u32, u8, u8> m_get_shader_queue;
325 // Global constant setter factories
326 std::vector<IShaderConstantSetterFactory *> m_setter_factories;
329 std::vector<ShaderCallback *> m_callbacks;
332 IWritableShaderSource *createShaderSource()
334 return new ShaderSource();
338 Generate shader given the shader name.
340 ShaderInfo generate_shader(const std::string &name,
341 u8 material_type, u8 drawtype, std::vector<ShaderCallback *> &callbacks,
342 const std::vector<IShaderConstantSetterFactory *> &setter_factories,
343 SourceShaderCache *sourcecache);
348 void load_shaders(const std::string &name, SourceShaderCache *sourcecache,
349 video::E_DRIVER_TYPE drivertype, bool enable_shaders,
350 std::string &vertex_program, std::string &pixel_program,
351 std::string &geometry_program, bool &is_highlevel);
353 ShaderSource::ShaderSource()
355 m_main_thread = std::this_thread::get_id();
357 // Add a dummy ShaderInfo as the first index, named ""
358 m_shaderinfo_cache.emplace_back();
360 // Add main global constant setter
361 addShaderConstantSetterFactory(new MainShaderConstantSetterFactory());
364 ShaderSource::~ShaderSource()
366 for (ShaderCallback *callback : m_callbacks) {
369 for (IShaderConstantSetterFactory *setter_factorie : m_setter_factories) {
370 delete setter_factorie;
374 u32 ShaderSource::getShader(const std::string &name,
375 const u8 material_type, const u8 drawtype)
381 if (std::this_thread::get_id() == m_main_thread) {
382 return getShaderIdDirect(name, material_type, drawtype);
385 /*errorstream<<"getShader(): Queued: name=\""<<name<<"\""<<std::endl;*/
387 // We're gonna ask the result to be put into here
389 static ResultQueue<std::string, u32, u8, u8> result_queue;
391 // Throw a request in
392 m_get_shader_queue.add(name, 0, 0, &result_queue);
394 /* infostream<<"Waiting for shader from main thread, name=\""
395 <<name<<"\""<<std::endl;*/
398 GetResult<std::string, u32, u8, u8>
399 result = result_queue.pop_frontNoEx();
401 if (result.key == name) {
405 errorstream << "Got shader with invalid name: " << result.key << std::endl;
408 infostream << "getShader(): Failed" << std::endl;
414 This method generates all the shaders
416 u32 ShaderSource::getShaderIdDirect(const std::string &name,
417 const u8 material_type, const u8 drawtype)
419 //infostream<<"getShaderIdDirect(): name=\""<<name<<"\""<<std::endl;
421 // Empty name means shader 0
423 infostream<<"getShaderIdDirect(): name is empty"<<std::endl;
427 // Check if already have such instance
428 for(u32 i=0; i<m_shaderinfo_cache.size(); i++){
429 ShaderInfo *info = &m_shaderinfo_cache[i];
430 if(info->name == name && info->material_type == material_type &&
431 info->drawtype == drawtype)
436 Calling only allowed from main thread
438 if (std::this_thread::get_id() != m_main_thread) {
439 errorstream<<"ShaderSource::getShaderIdDirect() "
440 "called not from main thread"<<std::endl;
444 ShaderInfo info = generate_shader(name, material_type, drawtype,
445 m_callbacks, m_setter_factories, &m_sourcecache);
448 Add shader to caches (add dummy shaders too)
451 MutexAutoLock lock(m_shaderinfo_cache_mutex);
453 u32 id = m_shaderinfo_cache.size();
454 m_shaderinfo_cache.push_back(info);
456 infostream<<"getShaderIdDirect(): "
457 <<"Returning id="<<id<<" for name \""<<name<<"\""<<std::endl;
463 ShaderInfo ShaderSource::getShaderInfo(u32 id)
465 MutexAutoLock lock(m_shaderinfo_cache_mutex);
467 if(id >= m_shaderinfo_cache.size())
470 return m_shaderinfo_cache[id];
473 void ShaderSource::processQueue()
479 void ShaderSource::insertSourceShader(const std::string &name_of_shader,
480 const std::string &filename, const std::string &program)
482 /*infostream<<"ShaderSource::insertSourceShader(): "
483 "name_of_shader=\""<<name_of_shader<<"\", "
484 "filename=\""<<filename<<"\""<<std::endl;*/
486 sanity_check(std::this_thread::get_id() == m_main_thread);
488 m_sourcecache.insert(name_of_shader, filename, program, true);
491 void ShaderSource::rebuildShaders()
493 MutexAutoLock lock(m_shaderinfo_cache_mutex);
495 /*// Oh well... just clear everything, they'll load sometime.
496 m_shaderinfo_cache.clear();
497 m_name_to_id.clear();*/
500 FIXME: Old shader materials can't be deleted in Irrlicht,
502 (This would be nice to do in the destructor too)
506 for (ShaderInfo &i : m_shaderinfo_cache) {
507 ShaderInfo *info = &i;
508 if (!info->name.empty()) {
509 *info = generate_shader(info->name, info->material_type,
510 info->drawtype, m_callbacks,
511 m_setter_factories, &m_sourcecache);
517 ShaderInfo generate_shader(const std::string &name, u8 material_type, u8 drawtype,
518 std::vector<ShaderCallback *> &callbacks,
519 const std::vector<IShaderConstantSetterFactory *> &setter_factories,
520 SourceShaderCache *sourcecache)
522 ShaderInfo shaderinfo;
523 shaderinfo.name = name;
524 shaderinfo.material_type = material_type;
525 shaderinfo.drawtype = drawtype;
526 shaderinfo.material = video::EMT_SOLID;
527 switch (material_type) {
528 case TILE_MATERIAL_OPAQUE:
529 case TILE_MATERIAL_LIQUID_OPAQUE:
530 shaderinfo.base_material = video::EMT_SOLID;
532 case TILE_MATERIAL_ALPHA:
533 case TILE_MATERIAL_LIQUID_TRANSPARENT:
534 shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
536 case TILE_MATERIAL_BASIC:
537 case TILE_MATERIAL_WAVING_LEAVES:
538 case TILE_MATERIAL_WAVING_PLANTS:
539 shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
543 bool enable_shaders = g_settings->getBool("enable_shaders");
547 video::IVideoDriver *driver = RenderingEngine::get_video_driver();
549 video::IGPUProgrammingServices *gpu = driver->getGPUProgrammingServices();
551 errorstream<<"generate_shader(): "
552 "failed to generate \""<<name<<"\", "
553 "GPU programming not supported."
558 // Choose shader language depending on driver type and settings
560 std::string vertex_program;
561 std::string pixel_program;
562 std::string geometry_program;
564 load_shaders(name, sourcecache, driver->getDriverType(),
565 enable_shaders, vertex_program, pixel_program,
566 geometry_program, is_highlevel);
567 // Check hardware/driver support
568 if (!vertex_program.empty() &&
569 !driver->queryFeature(video::EVDF_VERTEX_SHADER_1_1) &&
570 !driver->queryFeature(video::EVDF_ARB_VERTEX_PROGRAM_1)){
571 infostream<<"generate_shader(): vertex shaders disabled "
572 "because of missing driver/hardware support."
576 if (!pixel_program.empty() &&
577 !driver->queryFeature(video::EVDF_PIXEL_SHADER_1_1) &&
578 !driver->queryFeature(video::EVDF_ARB_FRAGMENT_PROGRAM_1)){
579 infostream<<"generate_shader(): pixel shaders disabled "
580 "because of missing driver/hardware support."
584 if (!geometry_program.empty() &&
585 !driver->queryFeature(video::EVDF_GEOMETRY_SHADER)){
586 infostream<<"generate_shader(): geometry shaders disabled "
587 "because of missing driver/hardware support."
589 geometry_program = "";
592 // If no shaders are used, don't make a separate material type
593 if (vertex_program.empty() && pixel_program.empty() && geometry_program.empty())
596 // Create shaders header
597 std::string shaders_header = "#version 120\n";
599 static const char* drawTypes[] = {
606 "NDT_ALLFACES_OPTIONAL",
613 "NDT_GLASSLIKE_FRAMED",
615 "NDT_GLASSLIKE_FRAMED_OPTIONAL",
616 "NDT_PLANTLIKE_ROOTED",
619 for (int i = 0; i < 14; i++){
620 shaders_header += "#define ";
621 shaders_header += drawTypes[i];
622 shaders_header += " ";
623 shaders_header += itos(i);
624 shaders_header += "\n";
627 static const char* materialTypes[] = {
628 "TILE_MATERIAL_BASIC",
629 "TILE_MATERIAL_ALPHA",
630 "TILE_MATERIAL_LIQUID_TRANSPARENT",
631 "TILE_MATERIAL_LIQUID_OPAQUE",
632 "TILE_MATERIAL_WAVING_LEAVES",
633 "TILE_MATERIAL_WAVING_PLANTS",
634 "TILE_MATERIAL_OPAQUE"
637 for (int i = 0; i < 7; i++){
638 shaders_header += "#define ";
639 shaders_header += materialTypes[i];
640 shaders_header += " ";
641 shaders_header += itos(i);
642 shaders_header += "\n";
645 shaders_header += "#define MATERIAL_TYPE ";
646 shaders_header += itos(material_type);
647 shaders_header += "\n";
648 shaders_header += "#define DRAW_TYPE ";
649 shaders_header += itos(drawtype);
650 shaders_header += "\n";
652 if (g_settings->getBool("generate_normalmaps")) {
653 shaders_header += "#define GENERATE_NORMALMAPS 1\n";
655 shaders_header += "#define GENERATE_NORMALMAPS 0\n";
657 shaders_header += "#define NORMALMAPS_STRENGTH ";
658 shaders_header += ftos(g_settings->getFloat("normalmaps_strength"));
659 shaders_header += "\n";
661 int smooth = (int)g_settings->getFloat("normalmaps_smooth");
664 sample_step = 0.0078125; // 1.0 / 128.0
667 sample_step = 0.00390625; // 1.0 / 256.0
670 sample_step = 0.001953125; // 1.0 / 512.0
673 sample_step = 0.0078125;
676 shaders_header += "#define SAMPLE_STEP ";
677 shaders_header += ftos(sample_step);
678 shaders_header += "\n";
680 if (g_settings->getBool("enable_bumpmapping"))
681 shaders_header += "#define ENABLE_BUMPMAPPING\n";
683 if (g_settings->getBool("enable_parallax_occlusion")){
684 int mode = g_settings->getFloat("parallax_occlusion_mode");
685 float scale = g_settings->getFloat("parallax_occlusion_scale");
686 float bias = g_settings->getFloat("parallax_occlusion_bias");
687 int iterations = g_settings->getFloat("parallax_occlusion_iterations");
688 shaders_header += "#define ENABLE_PARALLAX_OCCLUSION\n";
689 shaders_header += "#define PARALLAX_OCCLUSION_MODE ";
690 shaders_header += itos(mode);
691 shaders_header += "\n";
692 shaders_header += "#define PARALLAX_OCCLUSION_SCALE ";
693 shaders_header += ftos(scale);
694 shaders_header += "\n";
695 shaders_header += "#define PARALLAX_OCCLUSION_BIAS ";
696 shaders_header += ftos(bias);
697 shaders_header += "\n";
698 shaders_header += "#define PARALLAX_OCCLUSION_ITERATIONS ";
699 shaders_header += itos(iterations);
700 shaders_header += "\n";
703 shaders_header += "#define USE_NORMALMAPS ";
704 if (g_settings->getBool("enable_bumpmapping") || g_settings->getBool("enable_parallax_occlusion"))
705 shaders_header += "1\n";
707 shaders_header += "0\n";
709 if (g_settings->getBool("enable_waving_water")){
710 shaders_header += "#define ENABLE_WAVING_WATER 1\n";
711 shaders_header += "#define WATER_WAVE_HEIGHT ";
712 shaders_header += ftos(g_settings->getFloat("water_wave_height"));
713 shaders_header += "\n";
714 shaders_header += "#define WATER_WAVE_LENGTH ";
715 shaders_header += ftos(g_settings->getFloat("water_wave_length"));
716 shaders_header += "\n";
717 shaders_header += "#define WATER_WAVE_SPEED ";
718 shaders_header += ftos(g_settings->getFloat("water_wave_speed"));
719 shaders_header += "\n";
721 shaders_header += "#define ENABLE_WAVING_WATER 0\n";
724 shaders_header += "#define ENABLE_WAVING_LEAVES ";
725 if (g_settings->getBool("enable_waving_leaves"))
726 shaders_header += "1\n";
728 shaders_header += "0\n";
730 shaders_header += "#define ENABLE_WAVING_PLANTS ";
731 if (g_settings->getBool("enable_waving_plants"))
732 shaders_header += "1\n";
734 shaders_header += "0\n";
736 if (g_settings->getBool("tone_mapping"))
737 shaders_header += "#define ENABLE_TONE_MAPPING\n";
739 shaders_header += "#define FOG_START ";
740 shaders_header += ftos(rangelim(g_settings->getFloat("fog_start"), 0.0f, 0.99f));
741 shaders_header += "\n";
743 // Call addHighLevelShaderMaterial() or addShaderMaterial()
744 const c8* vertex_program_ptr = 0;
745 const c8* pixel_program_ptr = 0;
746 const c8* geometry_program_ptr = 0;
747 if (!vertex_program.empty()) {
748 vertex_program = shaders_header + vertex_program;
749 vertex_program_ptr = vertex_program.c_str();
751 if (!pixel_program.empty()) {
752 pixel_program = shaders_header + pixel_program;
753 pixel_program_ptr = pixel_program.c_str();
755 if (!geometry_program.empty()) {
756 geometry_program = shaders_header + geometry_program;
757 geometry_program_ptr = geometry_program.c_str();
759 ShaderCallback *cb = new ShaderCallback(setter_factories);
762 infostream<<"Compiling high level shaders for "<<name<<std::endl;
763 shadermat = gpu->addHighLevelShaderMaterial(
764 vertex_program_ptr, // Vertex shader program
765 "vertexMain", // Vertex shader entry point
766 video::EVST_VS_1_1, // Vertex shader version
767 pixel_program_ptr, // Pixel shader program
768 "pixelMain", // Pixel shader entry point
769 video::EPST_PS_1_2, // Pixel shader version
770 geometry_program_ptr, // Geometry shader program
771 "geometryMain", // Geometry shader entry point
772 video::EGST_GS_4_0, // Geometry shader version
773 scene::EPT_TRIANGLES, // Geometry shader input
774 scene::EPT_TRIANGLE_STRIP, // Geometry shader output
775 0, // Support maximum number of vertices
776 cb, // Set-constant callback
777 shaderinfo.base_material, // Base material
778 1 // Userdata passed to callback
781 errorstream<<"generate_shader(): "
782 "failed to generate \""<<name<<"\", "
783 "addHighLevelShaderMaterial failed."
785 dumpShaderProgram(warningstream, "Vertex", vertex_program);
786 dumpShaderProgram(warningstream, "Pixel", pixel_program);
787 dumpShaderProgram(warningstream, "Geometry", geometry_program);
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 cb, // Set-constant callback
798 shaderinfo.base_material, // Base material
799 0 // Userdata passed to callback
803 errorstream<<"generate_shader(): "
804 "failed to generate \""<<name<<"\", "
805 "addShaderMaterial failed."
807 dumpShaderProgram(warningstream, "Vertex", vertex_program);
808 dumpShaderProgram(warningstream,"Pixel", pixel_program);
813 callbacks.push_back(cb);
815 // HACK, TODO: investigate this better
816 // Grab the material renderer once more so minetest doesn't crash on exit
817 driver->getMaterialRenderer(shadermat)->grab();
819 // Apply the newly created material type
820 shaderinfo.material = (video::E_MATERIAL_TYPE) shadermat;
824 void load_shaders(const std::string &name, SourceShaderCache *sourcecache,
825 video::E_DRIVER_TYPE drivertype, bool enable_shaders,
826 std::string &vertex_program, std::string &pixel_program,
827 std::string &geometry_program, bool &is_highlevel)
831 geometry_program = "";
832 is_highlevel = false;
835 // Look for high level shaders
836 if(drivertype == video::EDT_DIRECT3D9){
838 // (All shaders in one file)
839 vertex_program = sourcecache->getOrLoad(name, "d3d9.hlsl");
840 pixel_program = vertex_program;
841 geometry_program = vertex_program;
843 else if(drivertype == video::EDT_OPENGL){
845 vertex_program = sourcecache->getOrLoad(name, "opengl_vertex.glsl");
846 pixel_program = sourcecache->getOrLoad(name, "opengl_fragment.glsl");
847 geometry_program = sourcecache->getOrLoad(name, "opengl_geometry.glsl");
849 if (!vertex_program.empty() || !pixel_program.empty() || !geometry_program.empty()){
857 void dumpShaderProgram(std::ostream &output_stream,
858 const std::string &program_type, const std::string &program)
860 output_stream << program_type << " shader program:" << std::endl <<
861 "----------------------------------" << std::endl;
865 while ((pos = program.find('\n', prev)) != std::string::npos) {
866 output_stream << line++ << ": "<< program.substr(prev, pos - prev) <<
870 output_stream << line << ": " << program.substr(prev) << std::endl <<
871 "End of " << program_type << " shader program." << std::endl <<