99#include < regex>
1010#include < set>
1111#include < sstream>
12+ #include < system_error>
1213#include < unordered_map>
1314
1415#include < osg/Program>
@@ -73,22 +74,6 @@ namespace
7374 += static_cast <int >(std::count (source.begin () + lineDirectivePosition, source.begin () + foundPos, ' \n ' ));
7475 return lineNumber;
7576 }
76- }
77-
78- namespace Shader
79- {
80-
81- ShaderManager::ShaderManager ()
82- {
83- mHotReloadManager = std::make_unique<HotReloadManager>();
84- }
85-
86- ShaderManager::~ShaderManager () = default ;
87-
88- void ShaderManager::setShaderPath (const std::filesystem::path& path)
89- {
90- mPath = path;
91- }
9277
9378 bool addLineDirectivesAfterConditionalBlocks (std::string& source)
9479 {
@@ -121,7 +106,7 @@ namespace Shader
121106 // Adjusts #line statements accordingly and detects cyclic includes.
122107 // cycleIncludeChecker is the set of files that include this file directly or indirectly, and is intentionally not a
123108 // reference to allow automatic cleanup.
124- static bool parseIncludes (const std::filesystem::path& shaderPath, std::string& source, const std::string& fileName,
109+ bool parseIncludes (const std::filesystem::path& shaderPath, std::string& source, const std::string& fileName,
125110 int & fileNumber, std::set<std::filesystem::path> cycleIncludeChecker,
126111 std::set<std::filesystem::path>& includedFiles)
127112 {
@@ -169,7 +154,8 @@ namespace Shader
169154 includeFstream.open (includePath);
170155 if (includeFstream.fail ())
171156 {
172- Log (Debug::Error) << " Shader " << fileName << " error: Failed to open include " << includePath;
157+ Log (Debug::Error) << " Shader " << fileName << " error: Failed to open include " << includePath << " : "
158+ << std::generic_category ().message (errno);
173159 return false ;
174160 }
175161 int includedFileNumber = fileNumber++;
@@ -193,6 +179,127 @@ namespace Shader
193179 }
194180 return true ;
195181 }
182+ }
183+
184+ namespace Shader
185+ {
186+ struct HotReloadManager
187+ {
188+ using KeysHolder = std::set<ShaderManager::MapKey>;
189+
190+ std::unordered_map<std::string, KeysHolder> mShaderFiles ;
191+ std::unordered_map<std::string, std::set<std::filesystem::path>> templateIncludedFiles;
192+ std::filesystem::file_time_type mLastAutoRecompileTime ;
193+ bool mHotReloadEnabled ;
194+ bool mTriggerReload ;
195+
196+ HotReloadManager ()
197+ {
198+ mTriggerReload = false ;
199+ mHotReloadEnabled = false ;
200+ mLastAutoRecompileTime = std::filesystem::file_time_type::clock::now ();
201+ }
202+
203+ void addShaderFiles (const std::string& templateName, const ShaderManager::DefineMap& defines)
204+ {
205+ const std::set<std::filesystem::path>& shaderFiles = templateIncludedFiles[templateName];
206+ for (const std::filesystem::path& file : shaderFiles)
207+ {
208+ mShaderFiles [Files::pathToUnicodeString (file)].insert (std::make_pair (templateName, defines));
209+ }
210+ }
211+
212+ void update (ShaderManager& manager, osgViewer::Viewer& viewer)
213+ {
214+ auto timeSinceLastCheckMillis = std::chrono::duration_cast<std::chrono::milliseconds>(
215+ std::filesystem::file_time_type::clock::now () - mLastAutoRecompileTime );
216+ if ((mHotReloadEnabled && timeSinceLastCheckMillis.count () > 200 ) || mTriggerReload == true )
217+ {
218+ reloadTouchedShaders (manager, viewer);
219+ }
220+ mTriggerReload = false ;
221+ }
222+
223+ void reloadTouchedShaders (ShaderManager& manager, osgViewer::Viewer& viewer)
224+ {
225+ bool threadsRunningToStop = false ;
226+ for (auto & [pathShaderToTest, shaderKeys] : mShaderFiles )
227+ {
228+ const std::filesystem::file_time_type writeTime = std::filesystem::last_write_time (pathShaderToTest);
229+ if (writeTime.time_since_epoch () > mLastAutoRecompileTime .time_since_epoch ())
230+ {
231+ if (!threadsRunningToStop)
232+ {
233+ threadsRunningToStop = viewer.areThreadsRunning ();
234+ if (threadsRunningToStop)
235+ viewer.stopThreading ();
236+ }
237+
238+ for (const auto & [templateName, shaderDefines] : shaderKeys)
239+ {
240+ ShaderManager::ShaderMap::iterator shaderIt
241+ = manager.mShaders .find (std::make_pair (templateName, shaderDefines));
242+ if (shaderIt == manager.mShaders .end ())
243+ {
244+ Log (Debug::Error) << " Failed to find shader " << templateName;
245+ continue ;
246+ }
247+
248+ ShaderManager::TemplateMap::iterator templateIt = manager.mShaderTemplates .find (
249+ templateName); // Can't be Null, if we're here it means the template was added
250+ assert (templateIt != manager.mShaderTemplates .end ());
251+ std::string& shaderSource = templateIt->second ;
252+ std::set<std::filesystem::path> insertedPaths;
253+ std::filesystem::path path = (std::filesystem::path (manager.mPath ) / templateName);
254+ std::ifstream stream;
255+ stream.open (path);
256+ if (stream.fail ())
257+ {
258+ Log (Debug::Error)
259+ << " Failed to open " << path << " : " << std::generic_category ().message (errno);
260+ continue ;
261+ }
262+ std::stringstream buffer;
263+ buffer << stream.rdbuf ();
264+
265+ // parse includes
266+ int fileNumber = 1 ;
267+ std::string source = buffer.str ();
268+ if (!addLineDirectivesAfterConditionalBlocks (source)
269+ || !parseIncludes (std::filesystem::path (manager.mPath ), source, templateName, fileNumber,
270+ {}, insertedPaths))
271+ {
272+ break ;
273+ }
274+ shaderSource = std::move (source);
275+
276+ std::vector<std::string> linkedShaderNames;
277+ if (!manager.createSourceFromTemplate (
278+ shaderSource, linkedShaderNames, templateName, shaderDefines))
279+ {
280+ break ;
281+ }
282+ shaderIt->second ->setShaderSource (shaderSource);
283+ }
284+ }
285+ }
286+ if (threadsRunningToStop)
287+ viewer.startThreading ();
288+ mLastAutoRecompileTime = std::filesystem::file_time_type::clock::now ();
289+ }
290+ };
291+
292+ ShaderManager::ShaderManager ()
293+ {
294+ mHotReloadManager = std::make_unique<HotReloadManager>();
295+ }
296+
297+ ShaderManager::~ShaderManager () = default ;
298+
299+ void ShaderManager::setShaderPath (const std::filesystem::path& path)
300+ {
301+ mPath = path;
302+ }
196303
197304 bool parseForeachDirective (std::string& source, const std::string& templateName, size_t foundPos)
198305 {
@@ -397,110 +504,6 @@ namespace Shader
397504 return true ;
398505 }
399506
400- struct HotReloadManager
401- {
402- using KeysHolder = std::set<ShaderManager::MapKey>;
403-
404- std::unordered_map<std::string, KeysHolder> mShaderFiles ;
405- std::unordered_map<std::string, std::set<std::filesystem::path>> templateIncludedFiles;
406- std::filesystem::file_time_type mLastAutoRecompileTime ;
407- bool mHotReloadEnabled ;
408- bool mTriggerReload ;
409-
410- HotReloadManager ()
411- {
412- mTriggerReload = false ;
413- mHotReloadEnabled = false ;
414- mLastAutoRecompileTime = std::filesystem::file_time_type::clock::now ();
415- }
416-
417- void addShaderFiles (const std::string& templateName, const ShaderManager::DefineMap& defines)
418- {
419- const std::set<std::filesystem::path>& shaderFiles = templateIncludedFiles[templateName];
420- for (const std::filesystem::path& file : shaderFiles)
421- {
422- mShaderFiles [Files::pathToUnicodeString (file)].insert (std::make_pair (templateName, defines));
423- }
424- }
425-
426- void update (ShaderManager& manager, osgViewer::Viewer& viewer)
427- {
428- auto timeSinceLastCheckMillis = std::chrono::duration_cast<std::chrono::milliseconds>(
429- std::filesystem::file_time_type::clock::now () - mLastAutoRecompileTime );
430- if ((mHotReloadEnabled && timeSinceLastCheckMillis.count () > 200 ) || mTriggerReload == true )
431- {
432- reloadTouchedShaders (manager, viewer);
433- }
434- mTriggerReload = false ;
435- }
436-
437- void reloadTouchedShaders (ShaderManager& manager, osgViewer::Viewer& viewer)
438- {
439- bool threadsRunningToStop = false ;
440- for (auto & [pathShaderToTest, shaderKeys] : mShaderFiles )
441- {
442- const std::filesystem::file_time_type writeTime = std::filesystem::last_write_time (pathShaderToTest);
443- if (writeTime.time_since_epoch () > mLastAutoRecompileTime .time_since_epoch ())
444- {
445- if (!threadsRunningToStop)
446- {
447- threadsRunningToStop = viewer.areThreadsRunning ();
448- if (threadsRunningToStop)
449- viewer.stopThreading ();
450- }
451-
452- for (const auto & [templateName, shaderDefines] : shaderKeys)
453- {
454- ShaderManager::ShaderMap::iterator shaderIt
455- = manager.mShaders .find (std::make_pair (templateName, shaderDefines));
456- if (shaderIt == manager.mShaders .end ())
457- {
458- Log (Debug::Error) << " Failed to find shader " << templateName;
459- continue ;
460- }
461-
462- ShaderManager::TemplateMap::iterator templateIt = manager.mShaderTemplates .find (
463- templateName); // Can't be Null, if we're here it means the template was added
464- assert (templateIt != manager.mShaderTemplates .end ());
465- std::string& shaderSource = templateIt->second ;
466- std::set<std::filesystem::path> insertedPaths;
467- std::filesystem::path path = (std::filesystem::path (manager.mPath ) / templateName);
468- std::ifstream stream;
469- stream.open (path);
470- if (stream.fail ())
471- {
472- Log (Debug::Error) << " Failed to open " << Files::pathToUnicodeString (path);
473- }
474- std::stringstream buffer;
475- buffer << stream.rdbuf ();
476-
477- // parse includes
478- int fileNumber = 1 ;
479- std::string source = buffer.str ();
480- if (!addLineDirectivesAfterConditionalBlocks (source)
481- || !parseIncludes (std::filesystem::path (manager.mPath ), source, templateName, fileNumber,
482- {}, insertedPaths))
483- {
484- break ;
485- }
486- shaderSource = std::move (source);
487-
488- std::vector<std::string> linkedShaderNames;
489- if (!manager.createSourceFromTemplate (
490- shaderSource, linkedShaderNames, templateName, shaderDefines))
491- {
492- break ;
493- }
494- shaderIt->second ->setShaderSource (shaderSource);
495- }
496- }
497- }
498- if (threadsRunningToStop)
499- viewer.startThreading ();
500- mLastAutoRecompileTime = std::filesystem::file_time_type::clock::now ();
501- }
502- };
503-
504507 osg::ref_ptr<osg::Shader> ShaderManager::getShader (
505508 std::string templateName, const ShaderManager::DefineMap& defines, std::optional<osg::Shader::Type> type)
506509 {
@@ -522,7 +525,7 @@ namespace Shader
522525 stream.open (path);
523526 if (stream.fail ())
524527 {
525- Log (Debug::Error) << " Failed to open " << path;
528+ Log (Debug::Error) << " Failed to open shader " << path << " : " << std::generic_category (). message (errno) ;
526529 return nullptr ;
527530 }
528531 std::stringstream buffer;
0 commit comments