diff --git a/components/shader/shadermanager.cpp b/components/shader/shadermanager.cpp index 7e0b94eb2b..42260373af 100644 --- a/components/shader/shadermanager.cpp +++ b/components/shader/shadermanager.cpp @@ -74,22 +74,6 @@ namespace += static_cast(std::count(source.begin() + lineDirectivePosition, source.begin() + foundPos, '\n')); return lineNumber; } -} - -namespace Shader -{ - - ShaderManager::ShaderManager() - { - mHotReloadManager = std::make_unique(); - } - - ShaderManager::~ShaderManager() = default; - - void ShaderManager::setShaderPath(const std::filesystem::path& path) - { - mPath = path; - } bool addLineDirectivesAfterConditionalBlocks(std::string& source) { @@ -122,7 +106,7 @@ namespace Shader // Adjusts #line statements accordingly and detects cyclic includes. // cycleIncludeChecker is the set of files that include this file directly or indirectly, and is intentionally not a // reference to allow automatic cleanup. - static bool parseIncludes(const std::filesystem::path& shaderPath, std::string& source, const std::string& fileName, + bool parseIncludes(const std::filesystem::path& shaderPath, std::string& source, const std::string& fileName, int& fileNumber, std::set cycleIncludeChecker, std::set& includedFiles) { @@ -195,6 +179,127 @@ namespace Shader } return true; } +} + +namespace Shader +{ + struct HotReloadManager + { + using KeysHolder = std::set; + + std::unordered_map mShaderFiles; + std::unordered_map> templateIncludedFiles; + std::filesystem::file_time_type mLastAutoRecompileTime; + bool mHotReloadEnabled; + bool mTriggerReload; + + HotReloadManager() + { + mTriggerReload = false; + mHotReloadEnabled = false; + mLastAutoRecompileTime = std::filesystem::file_time_type::clock::now(); + } + + void addShaderFiles(const std::string& templateName, const ShaderManager::DefineMap& defines) + { + const std::set& shaderFiles = templateIncludedFiles[templateName]; + for (const std::filesystem::path& file : shaderFiles) + { + mShaderFiles[Files::pathToUnicodeString(file)].insert(std::make_pair(templateName, defines)); + } + } + + void update(ShaderManager& manager, osgViewer::Viewer& viewer) + { + auto timeSinceLastCheckMillis = std::chrono::duration_cast( + std::filesystem::file_time_type::clock::now() - mLastAutoRecompileTime); + if ((mHotReloadEnabled && timeSinceLastCheckMillis.count() > 200) || mTriggerReload == true) + { + reloadTouchedShaders(manager, viewer); + } + mTriggerReload = false; + } + + void reloadTouchedShaders(ShaderManager& manager, osgViewer::Viewer& viewer) + { + bool threadsRunningToStop = false; + for (auto& [pathShaderToTest, shaderKeys] : mShaderFiles) + { + const std::filesystem::file_time_type writeTime = std::filesystem::last_write_time(pathShaderToTest); + if (writeTime.time_since_epoch() > mLastAutoRecompileTime.time_since_epoch()) + { + if (!threadsRunningToStop) + { + threadsRunningToStop = viewer.areThreadsRunning(); + if (threadsRunningToStop) + viewer.stopThreading(); + } + + for (const auto& [templateName, shaderDefines] : shaderKeys) + { + ShaderManager::ShaderMap::iterator shaderIt + = manager.mShaders.find(std::make_pair(templateName, shaderDefines)); + if (shaderIt == manager.mShaders.end()) + { + Log(Debug::Error) << "Failed to find shader " << templateName; + continue; + } + + ShaderManager::TemplateMap::iterator templateIt = manager.mShaderTemplates.find( + templateName); // Can't be Null, if we're here it means the template was added + assert(templateIt != manager.mShaderTemplates.end()); + std::string& shaderSource = templateIt->second; + std::set insertedPaths; + std::filesystem::path path = (std::filesystem::path(manager.mPath) / templateName); + std::ifstream stream; + stream.open(path); + if (stream.fail()) + { + Log(Debug::Error) + << "Failed to open " << path << ": " << std::generic_category().message(errno); + continue; + } + std::stringstream buffer; + buffer << stream.rdbuf(); + + // parse includes + int fileNumber = 1; + std::string source = buffer.str(); + if (!addLineDirectivesAfterConditionalBlocks(source) + || !parseIncludes(std::filesystem::path(manager.mPath), source, templateName, fileNumber, + {}, insertedPaths)) + { + break; + } + shaderSource = std::move(source); + + std::vector linkedShaderNames; + if (!manager.createSourceFromTemplate( + shaderSource, linkedShaderNames, templateName, shaderDefines)) + { + break; + } + shaderIt->second->setShaderSource(shaderSource); + } + } + } + if (threadsRunningToStop) + viewer.startThreading(); + mLastAutoRecompileTime = std::filesystem::file_time_type::clock::now(); + } + }; + + ShaderManager::ShaderManager() + { + mHotReloadManager = std::make_unique(); + } + + ShaderManager::~ShaderManager() = default; + + void ShaderManager::setShaderPath(const std::filesystem::path& path) + { + mPath = path; + } bool parseForeachDirective(std::string& source, const std::string& templateName, size_t foundPos) { @@ -399,112 +504,6 @@ namespace Shader return true; } - struct HotReloadManager - { - using KeysHolder = std::set; - - std::unordered_map mShaderFiles; - std::unordered_map> templateIncludedFiles; - std::filesystem::file_time_type mLastAutoRecompileTime; - bool mHotReloadEnabled; - bool mTriggerReload; - - HotReloadManager() - { - mTriggerReload = false; - mHotReloadEnabled = false; - mLastAutoRecompileTime = std::filesystem::file_time_type::clock::now(); - } - - void addShaderFiles(const std::string& templateName, const ShaderManager::DefineMap& defines) - { - const std::set& shaderFiles = templateIncludedFiles[templateName]; - for (const std::filesystem::path& file : shaderFiles) - { - mShaderFiles[Files::pathToUnicodeString(file)].insert(std::make_pair(templateName, defines)); - } - } - - void update(ShaderManager& manager, osgViewer::Viewer& viewer) - { - auto timeSinceLastCheckMillis = std::chrono::duration_cast( - std::filesystem::file_time_type::clock::now() - mLastAutoRecompileTime); - if ((mHotReloadEnabled && timeSinceLastCheckMillis.count() > 200) || mTriggerReload == true) - { - reloadTouchedShaders(manager, viewer); - } - mTriggerReload = false; - } - - void reloadTouchedShaders(ShaderManager& manager, osgViewer::Viewer& viewer) - { - bool threadsRunningToStop = false; - for (auto& [pathShaderToTest, shaderKeys] : mShaderFiles) - { - const std::filesystem::file_time_type writeTime = std::filesystem::last_write_time(pathShaderToTest); - if (writeTime.time_since_epoch() > mLastAutoRecompileTime.time_since_epoch()) - { - if (!threadsRunningToStop) - { - threadsRunningToStop = viewer.areThreadsRunning(); - if (threadsRunningToStop) - viewer.stopThreading(); - } - - for (const auto& [templateName, shaderDefines] : shaderKeys) - { - ShaderManager::ShaderMap::iterator shaderIt - = manager.mShaders.find(std::make_pair(templateName, shaderDefines)); - if (shaderIt == manager.mShaders.end()) - { - Log(Debug::Error) << "Failed to find shader " << templateName; - continue; - } - - ShaderManager::TemplateMap::iterator templateIt = manager.mShaderTemplates.find( - templateName); // Can't be Null, if we're here it means the template was added - assert(templateIt != manager.mShaderTemplates.end()); - std::string& shaderSource = templateIt->second; - std::set insertedPaths; - std::filesystem::path path = (std::filesystem::path(manager.mPath) / templateName); - std::ifstream stream; - stream.open(path); - if (stream.fail()) - { - Log(Debug::Error) - << "Failed to open " << path << ": " << std::generic_category().message(errno); - continue; - } - std::stringstream buffer; - buffer << stream.rdbuf(); - - // parse includes - int fileNumber = 1; - std::string source = buffer.str(); - if (!addLineDirectivesAfterConditionalBlocks(source) - || !parseIncludes(std::filesystem::path(manager.mPath), source, templateName, fileNumber, - {}, insertedPaths)) - { - break; - } - shaderSource = std::move(source); - - std::vector linkedShaderNames; - if (!manager.createSourceFromTemplate( - shaderSource, linkedShaderNames, templateName, shaderDefines)) - { - break; - } - shaderIt->second->setShaderSource(shaderSource); - } - } - } - if (threadsRunningToStop) - viewer.startThreading(); - mLastAutoRecompileTime = std::filesystem::file_time_type::clock::now(); - } - }; - osg::ref_ptr ShaderManager::getShader( std::string templateName, const ShaderManager::DefineMap& defines, std::optional type) {