diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 8c39c13941..2aef65029c 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -902,7 +902,7 @@ namespace MWRender void RenderingManager::update(float dt, bool paused) { reportStats(); - + mResourceSystem->getSceneManager()->getShaderManager().update(); float rainIntensity = mSky->getPrecipitationAlpha(); mWater->setRainIntensity(rainIntensity); diff --git a/components/shader/shadermanager.cpp b/components/shader/shadermanager.cpp index 8136bab447..7030042fb2 100644 --- a/components/shader/shadermanager.cpp +++ b/components/shader/shadermanager.cpp @@ -5,7 +5,7 @@ #include #include #include - +#include #include #include @@ -16,6 +16,11 @@ namespace Shader { ShaderManager::ShaderManager() + { + mHotReloadManager = std::make_unique(); + } + + ShaderManager::~ShaderManager() { } @@ -68,7 +73,7 @@ namespace Shader // Recursively replaces include statements with the actual source of the included files. // Adjusts #line statements accordingly and detects cyclic includes. // includingFiles 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, int& fileNumber, std::set includingFiles) + static bool parseIncludes(const std::filesystem::path& shaderPath, std::string& source, const std::string& fileName, int& fileNumber, std::set& includingFiles) { // An include is cyclic if it is being included by itself if (includingFiles.insert(shaderPath/fileName).second == false) @@ -355,12 +360,100 @@ namespace Shader return true; } + struct HotReloadManager + { + + + std::filesystem::file_time_type mLastAutoRecompileTime; + using KeysHolder = std::set; + std::unordered_map mShaderFiles; + HotReloadManager() + { + mLastAutoRecompileTime = std::chrono::file_clock::now(); + } + + void addShaderFiles(std::set& shaderFiles,const osg::ref_ptr& shader,const std::string& templateName,const ShaderManager::DefineMap& defines ) + { + for (const std::filesystem::path& file : shaderFiles) + { + KeysHolder* shaderSet_ptr; + auto found = mShaderFiles.find(file.string()); + if (found != mShaderFiles.end()) //Apparently there is an issue that prevents me from using operator[] + { + shaderSet_ptr = &found->second; + } + else + { + shaderSet_ptr = &mShaderFiles.insert(std::make_pair<>(file.string(), KeysHolder())).first->second; + } + auto& shaderSet = *shaderSet_ptr; + shaderSet.insert(std::make_pair( templateName, defines )); + } + } + + void update(ShaderManager& Manager) + { + for (auto& shader : mShaderFiles) + { + std::filesystem::path pathShaderToTest = (shader.first); + + std::filesystem::file_time_type write_time = std::filesystem::last_write_time(pathShaderToTest); + //Log(Debug::Info) << std::format("{} write time is {} compared to {} ", shader.first, write_time, mLastAutoRecompileTime); + if (write_time.time_since_epoch() > mLastAutoRecompileTime.time_since_epoch()) + { + for (const ShaderManager:: MapKey& descriptor : shader.second) + { + const std::string& templateName = descriptor.first; + ShaderManager::ShaderMap::iterator shaderIt = Manager.mShaders.find(std::make_pair(templateName, descriptor.second)); + + ShaderManager::TemplateMap::iterator templateIt = Manager.mShaderTemplates.find(templateName); //Can't be Null, if we're here it means the template was added + 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.string(); + } + 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)) + { + } + templateIt->second = source; + + //if (shaderIt == Manager.mShaders.end()) + { + std::string shaderSource = templateIt->second; + std::vector linkedShaderNames; + if (!Manager.createSourceFromTemplate(shaderSource, linkedShaderNames, templateName, descriptor.second)) + { + + } + shaderIt->second->setShaderSource(shaderSource); + } + } + } + } + } + mLastAutoRecompileTime = std::chrono::file_clock::now(); + } + }; + osg::ref_ptr ShaderManager::getShader(const std::string &templateName, const ShaderManager::DefineMap &defines, osg::Shader::Type shaderType) { std::unique_lock lock(mMutex); // read the template if we haven't already TemplateMap::iterator templateIt = mShaderTemplates.find(templateName); + std::set insertedPaths; + if (templateIt == mShaderTemplates.end()) { std::filesystem::path path = (std::filesystem::path(mPath) / templateName); @@ -378,7 +471,7 @@ namespace Shader int fileNumber = 1; std::string source = buffer.str(); if (!addLineDirectivesAfterConditionalBlocks(source) - || !parseIncludes(std::filesystem::path(mPath), source, templateName, fileNumber, {})) + || !parseIncludes(std::filesystem::path(mPath), source, templateName, fileNumber, insertedPaths)) return nullptr; templateIt = mShaderTemplates.insert(std::make_pair(templateName, source)).first; @@ -402,6 +495,7 @@ namespace Shader // Append shader source filename for debugging. static unsigned int counter = 0; shader->setName(Misc::StringUtils::format("%u %s", counter++, templateName)); + mHotReloadManager->addShaderFiles(insertedPaths, shader, templateName, defines); lock.unlock(); getLinkedShaders(shader, linkedShaderNames, defines); @@ -535,4 +629,9 @@ namespace Shader return unit; } + void ShaderManager::update() + { + mHotReloadManager->update(*this); + } + } diff --git a/components/shader/shadermanager.hpp b/components/shader/shadermanager.hpp index 4d3cc9937a..e0649047ef 100644 --- a/components/shader/shadermanager.hpp +++ b/components/shader/shadermanager.hpp @@ -14,14 +14,15 @@ namespace Shader { - + struct HotReloadManager; /// @brief Reads shader template files and turns them into a concrete shader, based on a list of define's. /// @par Shader templates can get the value of a define with the syntax @define. class ShaderManager { public: - + friend HotReloadManager; ShaderManager(); + ~ShaderManager(); void setShaderPath(const std::string& path); @@ -67,6 +68,7 @@ namespace Shader int reserveGlobalTextureUnits(Slot slot); + void update(); private: void getLinkedShaders(osg::ref_ptr shader, const std::vector& linkedShaderNames, const DefineMap& defines); void addLinkedShaders(osg::ref_ptr shader, osg::ref_ptr program); @@ -96,7 +98,7 @@ namespace Shader int mMaxTextureUnits = 0; int mReservedTextureUnits = 0; - + std::unique_ptr mHotReloadManager; std::array mReservedTextureUnitsBySlot = {-1, -1}; };