mirror of
https://github.com/OpenMW/openmw.git
synced 2025-05-20 09:41:29 +00:00
Merge branch 'SHADER_HOT_RELOAD' into 'master'
Shaders: Hot reload, togglable by lua debug command See merge request OpenMW/openmw!2238
This commit is contained in:
commit
4078f19c74
10 changed files with 198 additions and 34 deletions
|
@ -152,7 +152,6 @@ bool Launcher::AdvancedPage::loadSettings()
|
||||||
|
|
||||||
connect(postprocessEnabledCheckBox, SIGNAL(toggled(bool)), this, SLOT(slotPostProcessToggled(bool)));
|
connect(postprocessEnabledCheckBox, SIGNAL(toggled(bool)), this, SLOT(slotPostProcessToggled(bool)));
|
||||||
loadSettingBool(postprocessEnabledCheckBox, "enabled", "Post Processing");
|
loadSettingBool(postprocessEnabledCheckBox, "enabled", "Post Processing");
|
||||||
loadSettingBool(postprocessLiveReloadCheckBox, "live reload", "Post Processing");
|
|
||||||
loadSettingBool(postprocessTransparentPostpassCheckBox, "transparent postpass", "Post Processing");
|
loadSettingBool(postprocessTransparentPostpassCheckBox, "transparent postpass", "Post Processing");
|
||||||
postprocessHDRTimeComboBox->setValue(Settings::Manager::getDouble("auto exposure speed", "Post Processing"));
|
postprocessHDRTimeComboBox->setValue(Settings::Manager::getDouble("auto exposure speed", "Post Processing"));
|
||||||
|
|
||||||
|
@ -311,7 +310,6 @@ void Launcher::AdvancedPage::saveSettings()
|
||||||
saveSettingBool(nightDaySwitchesCheckBox, "day night switches", "Game");
|
saveSettingBool(nightDaySwitchesCheckBox, "day night switches", "Game");
|
||||||
|
|
||||||
saveSettingBool(postprocessEnabledCheckBox, "enabled", "Post Processing");
|
saveSettingBool(postprocessEnabledCheckBox, "enabled", "Post Processing");
|
||||||
saveSettingBool(postprocessLiveReloadCheckBox, "live reload", "Post Processing");
|
|
||||||
saveSettingBool(postprocessTransparentPostpassCheckBox, "transparent postpass", "Post Processing");
|
saveSettingBool(postprocessTransparentPostpassCheckBox, "transparent postpass", "Post Processing");
|
||||||
double hdrExposureTime = postprocessHDRTimeComboBox->value();
|
double hdrExposureTime = postprocessHDRTimeComboBox->value();
|
||||||
if (hdrExposureTime != Settings::Manager::getDouble("auto exposure speed", "Post Processing"))
|
if (hdrExposureTime != Settings::Manager::getDouble("auto exposure speed", "Post Processing"))
|
||||||
|
@ -477,7 +475,6 @@ void Launcher::AdvancedPage::slotAnimSourcesToggled(bool checked)
|
||||||
|
|
||||||
void Launcher::AdvancedPage::slotPostProcessToggled(bool checked)
|
void Launcher::AdvancedPage::slotPostProcessToggled(bool checked)
|
||||||
{
|
{
|
||||||
postprocessLiveReloadCheckBox->setEnabled(checked);
|
|
||||||
postprocessTransparentPostpassCheckBox->setEnabled(checked);
|
postprocessTransparentPostpassCheckBox->setEnabled(checked);
|
||||||
postprocessHDRTimeComboBox->setEnabled(checked);
|
postprocessHDRTimeComboBox->setEnabled(checked);
|
||||||
postprocessHDRTimeLabel->setEnabled(checked);
|
postprocessHDRTimeLabel->setEnabled(checked);
|
||||||
|
|
|
@ -5,6 +5,11 @@
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
#include "../mwbase/world.hpp"
|
#include "../mwbase/world.hpp"
|
||||||
#include "../mwrender/renderingmanager.hpp"
|
#include "../mwrender/renderingmanager.hpp"
|
||||||
|
#include "../mwrender/postprocessor.hpp"
|
||||||
|
|
||||||
|
#include <components/resource/resourcesystem.hpp>
|
||||||
|
#include <components/resource/scenemanager.hpp>
|
||||||
|
#include <components/shader/shadermanager.hpp>
|
||||||
|
|
||||||
#include <components/lua/luastate.hpp>
|
#include <components/lua/luastate.hpp>
|
||||||
|
|
||||||
|
@ -46,6 +51,27 @@ namespace MWLua
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
api["triggerShaderReload"] = [context]()
|
||||||
|
{
|
||||||
|
context.mLuaManager->addAction([]
|
||||||
|
{
|
||||||
|
auto world = MWBase::Environment::get().getWorld();
|
||||||
|
|
||||||
|
world->getRenderingManager()->getResourceSystem()->getSceneManager()->getShaderManager().triggerShaderReload();
|
||||||
|
world->getPostProcessor()->triggerShaderReload();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
api["setShaderHotReloadEnabled"] = [context](bool value)
|
||||||
|
{
|
||||||
|
context.mLuaManager->addAction([value]
|
||||||
|
{
|
||||||
|
auto world = MWBase::Environment::get().getWorld();
|
||||||
|
world->getRenderingManager()->getResourceSystem()->getSceneManager()->getShaderManager().setHotReloadEnabled(value);
|
||||||
|
world->getPostProcessor()->mEnableLiveReload = value;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return LuaUtil::makeReadOnly(api);
|
return LuaUtil::makeReadOnly(api);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,6 +105,7 @@ namespace MWRender
|
||||||
{
|
{
|
||||||
PostProcessor::PostProcessor(RenderingManager& rendering, osgViewer::Viewer* viewer, osg::Group* rootNode, const VFS::Manager* vfs)
|
PostProcessor::PostProcessor(RenderingManager& rendering, osgViewer::Viewer* viewer, osg::Group* rootNode, const VFS::Manager* vfs)
|
||||||
: osg::Group()
|
: osg::Group()
|
||||||
|
, mEnableLiveReload(false)
|
||||||
, mRootNode(rootNode)
|
, mRootNode(rootNode)
|
||||||
, mSamples(Settings::Manager::getInt("antialiasing", "Video"))
|
, mSamples(Settings::Manager::getInt("antialiasing", "Video"))
|
||||||
, mDirty(false)
|
, mDirty(false)
|
||||||
|
@ -112,6 +113,7 @@ namespace MWRender
|
||||||
, mRendering(rendering)
|
, mRendering(rendering)
|
||||||
, mViewer(viewer)
|
, mViewer(viewer)
|
||||||
, mVFS(vfs)
|
, mVFS(vfs)
|
||||||
|
, mTriggerShaderReload(false)
|
||||||
, mReload(false)
|
, mReload(false)
|
||||||
, mEnabled(false)
|
, mEnabled(false)
|
||||||
, mUsePostProcessing(false)
|
, mUsePostProcessing(false)
|
||||||
|
@ -364,10 +366,11 @@ namespace MWRender
|
||||||
|
|
||||||
void PostProcessor::updateLiveReload()
|
void PostProcessor::updateLiveReload()
|
||||||
{
|
{
|
||||||
static const bool liveReload = Settings::Manager::getBool("live reload", "Post Processing");
|
if (!mEnableLiveReload && !mTriggerShaderReload)
|
||||||
if (!liveReload)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
mTriggerShaderReload = false;//Done only once
|
||||||
|
|
||||||
for (auto& technique : mTechniques)
|
for (auto& technique : mTechniques)
|
||||||
{
|
{
|
||||||
if (technique->getStatus() == fx::Technique::Status::File_Not_exists)
|
if (technique->getStatus() == fx::Technique::Status::File_Not_exists)
|
||||||
|
@ -860,5 +863,10 @@ namespace MWRender
|
||||||
return Stereo::Manager::instance().eyeResolution().y();
|
return Stereo::Manager::instance().eyeResolution().y();
|
||||||
return mHeight;
|
return mHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PostProcessor::triggerShaderReload()
|
||||||
|
{
|
||||||
|
mTriggerShaderReload = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -180,9 +180,14 @@ namespace MWRender
|
||||||
int renderWidth() const;
|
int renderWidth() const;
|
||||||
int renderHeight() const;
|
int renderHeight() const;
|
||||||
|
|
||||||
|
void triggerShaderReload();
|
||||||
|
|
||||||
|
bool mEnableLiveReload;
|
||||||
|
|
||||||
void loadChain();
|
void loadChain();
|
||||||
void saveChain();
|
void saveChain();
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
void populateTechniqueFiles();
|
void populateTechniqueFiles();
|
||||||
|
@ -226,6 +231,7 @@ namespace MWRender
|
||||||
osgViewer::Viewer* mViewer;
|
osgViewer::Viewer* mViewer;
|
||||||
const VFS::Manager* mVFS;
|
const VFS::Manager* mVFS;
|
||||||
|
|
||||||
|
bool mTriggerShaderReload;
|
||||||
bool mReload;
|
bool mReload;
|
||||||
bool mEnabled;
|
bool mEnabled;
|
||||||
bool mUsePostProcessing;
|
bool mUsePostProcessing;
|
||||||
|
|
|
@ -903,6 +903,8 @@ namespace MWRender
|
||||||
{
|
{
|
||||||
reportStats();
|
reportStats();
|
||||||
|
|
||||||
|
mResourceSystem->getSceneManager()->getShaderManager().update(*mViewer);
|
||||||
|
|
||||||
float rainIntensity = mSky->getPrecipitationAlpha();
|
float rainIntensity = mSky->getPrecipitationAlpha();
|
||||||
mWater->setRainIntensity(rainIntensity);
|
mWater->setRainIntensity(rainIntensity);
|
||||||
|
|
||||||
|
|
|
@ -5,9 +5,11 @@
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
#include <set>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <chrono>
|
||||||
#include <osg/Program>
|
#include <osg/Program>
|
||||||
|
#include <osgViewer/Viewer>
|
||||||
#include <components/debug/debuglog.hpp>
|
#include <components/debug/debuglog.hpp>
|
||||||
#include <components/misc/strings/algorithm.hpp>
|
#include <components/misc/strings/algorithm.hpp>
|
||||||
#include <components/misc/strings/format.hpp>
|
#include <components/misc/strings/format.hpp>
|
||||||
|
@ -18,8 +20,11 @@ namespace Shader
|
||||||
|
|
||||||
ShaderManager::ShaderManager()
|
ShaderManager::ShaderManager()
|
||||||
{
|
{
|
||||||
|
mHotReloadManager = std::make_unique<HotReloadManager>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ShaderManager::~ShaderManager() = default;
|
||||||
|
|
||||||
void ShaderManager::setShaderPath(const std::string &path)
|
void ShaderManager::setShaderPath(const std::string &path)
|
||||||
{
|
{
|
||||||
mPath = path;
|
mPath = path;
|
||||||
|
@ -68,11 +73,12 @@ namespace Shader
|
||||||
|
|
||||||
// Recursively replaces include statements with the actual source of the included files.
|
// Recursively replaces include statements with the actual source of the included files.
|
||||||
// Adjusts #line statements accordingly and detects cyclic includes.
|
// 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.
|
// 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, int& fileNumber, std::set<std::filesystem::path> includingFiles)
|
static bool parseIncludes(const std::filesystem::path& shaderPath, std::string& source, const std::string& fileName, int& fileNumber, std::set<std::filesystem::path> cycleIncludeChecker,std::set<std::filesystem::path>& includedFiles)
|
||||||
{
|
{
|
||||||
|
includedFiles.insert(shaderPath / fileName);
|
||||||
// An include is cyclic if it is being included by itself
|
// An include is cyclic if it is being included by itself
|
||||||
if (includingFiles.insert(shaderPath/fileName).second == false)
|
if (cycleIncludeChecker.insert(shaderPath/fileName).second == false)
|
||||||
{
|
{
|
||||||
Log(Debug::Error) << "Shader " << fileName << " error: Detected cyclic #includes";
|
Log(Debug::Error) << "Shader " << fileName << " error: Detected cyclic #includes";
|
||||||
return false;
|
return false;
|
||||||
|
@ -129,7 +135,7 @@ namespace Shader
|
||||||
buffer << includeFstream.rdbuf();
|
buffer << includeFstream.rdbuf();
|
||||||
std::string stringRepresentation = buffer.str();
|
std::string stringRepresentation = buffer.str();
|
||||||
if (!addLineDirectivesAfterConditionalBlocks(stringRepresentation)
|
if (!addLineDirectivesAfterConditionalBlocks(stringRepresentation)
|
||||||
|| !parseIncludes(shaderPath, stringRepresentation, includeFilename, fileNumber, includingFiles))
|
|| !parseIncludes(shaderPath, stringRepresentation, includeFilename, fileNumber, cycleIncludeChecker, includedFiles))
|
||||||
{
|
{
|
||||||
Log(Debug::Error) << "In file included from " << fileName << "." << lineNumber;
|
Log(Debug::Error) << "In file included from " << fileName << "." << lineNumber;
|
||||||
return false;
|
return false;
|
||||||
|
@ -356,12 +362,109 @@ namespace Shader
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct HotReloadManager
|
||||||
|
{
|
||||||
|
using KeysHolder = std::set<ShaderManager::MapKey>;
|
||||||
|
|
||||||
|
std::unordered_map<std::string, KeysHolder> mShaderFiles;
|
||||||
|
std::unordered_map<std::string, std::set<std::filesystem::path>> 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<std::filesystem::path>& shaderFiles = templateIncludedFiles[templateName];
|
||||||
|
for (const std::filesystem::path& file : shaderFiles)
|
||||||
|
{
|
||||||
|
mShaderFiles[file.string()].insert(std::make_pair(templateName, defines));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void update(ShaderManager& Manager,osgViewer::Viewer& viewer)
|
||||||
|
{
|
||||||
|
auto timeSinceLastCheckMillis = std::chrono::duration_cast<std::chrono::milliseconds>(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)
|
||||||
|
{
|
||||||
|
|
||||||
|
std::filesystem::file_time_type write_time = std::filesystem::last_write_time(pathShaderToTest);
|
||||||
|
if (write_time.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));
|
||||||
|
|
||||||
|
ShaderManager::TemplateMap::iterator templateIt = Manager.mShaderTemplates.find(templateName); //Can't be Null, if we're here it means the template was added
|
||||||
|
std::string& shaderSource = templateIt->second;
|
||||||
|
std::set<std::filesystem::path> 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))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
shaderSource = source;
|
||||||
|
|
||||||
|
std::vector<std::string> 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<osg::Shader> ShaderManager::getShader(const std::string &templateName, const ShaderManager::DefineMap &defines, osg::Shader::Type shaderType)
|
osg::ref_ptr<osg::Shader> ShaderManager::getShader(const std::string &templateName, const ShaderManager::DefineMap &defines, osg::Shader::Type shaderType)
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lock(mMutex);
|
std::unique_lock<std::mutex> lock(mMutex);
|
||||||
|
|
||||||
// read the template if we haven't already
|
// read the template if we haven't already
|
||||||
TemplateMap::iterator templateIt = mShaderTemplates.find(templateName);
|
TemplateMap::iterator templateIt = mShaderTemplates.find(templateName);
|
||||||
|
std::set<std::filesystem::path> insertedPaths;
|
||||||
|
|
||||||
if (templateIt == mShaderTemplates.end())
|
if (templateIt == mShaderTemplates.end())
|
||||||
{
|
{
|
||||||
std::filesystem::path path = (std::filesystem::path(mPath) / templateName);
|
std::filesystem::path path = (std::filesystem::path(mPath) / templateName);
|
||||||
|
@ -379,9 +482,9 @@ namespace Shader
|
||||||
int fileNumber = 1;
|
int fileNumber = 1;
|
||||||
std::string source = buffer.str();
|
std::string source = buffer.str();
|
||||||
if (!addLineDirectivesAfterConditionalBlocks(source)
|
if (!addLineDirectivesAfterConditionalBlocks(source)
|
||||||
|| !parseIncludes(std::filesystem::path(mPath), source, templateName, fileNumber, {}))
|
|| !parseIncludes(std::filesystem::path(mPath), source, templateName, fileNumber, {}, insertedPaths))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
mHotReloadManager->templateIncludedFiles[templateName] = insertedPaths;
|
||||||
templateIt = mShaderTemplates.insert(std::make_pair(templateName, source)).first;
|
templateIt = mShaderTemplates.insert(std::make_pair(templateName, source)).first;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -404,6 +507,8 @@ namespace Shader
|
||||||
static unsigned int counter = 0;
|
static unsigned int counter = 0;
|
||||||
shader->setName(Misc::StringUtils::format("%u %s", counter++, templateName));
|
shader->setName(Misc::StringUtils::format("%u %s", counter++, templateName));
|
||||||
|
|
||||||
|
mHotReloadManager->addShaderFiles(templateName, defines);
|
||||||
|
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
getLinkedShaders(shader, linkedShaderNames, defines);
|
getLinkedShaders(shader, linkedShaderNames, defines);
|
||||||
lock.lock();
|
lock.lock();
|
||||||
|
@ -536,4 +641,19 @@ namespace Shader
|
||||||
return unit;
|
return unit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ShaderManager::update(osgViewer::Viewer& viewer)
|
||||||
|
{
|
||||||
|
mHotReloadManager->update(*this, viewer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShaderManager::setHotReloadEnabled(bool value)
|
||||||
|
{
|
||||||
|
mHotReloadManager->mHotReloadEnabled = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShaderManager::triggerShaderReload()
|
||||||
|
{
|
||||||
|
mHotReloadManager->mTriggerReload = true;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,22 +6,28 @@
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include <osg/ref_ptr>
|
#include <osg/ref_ptr>
|
||||||
|
|
||||||
#include <osg/Shader>
|
#include <osg/Shader>
|
||||||
#include <osg/Program>
|
#include <osg/Program>
|
||||||
|
|
||||||
|
namespace osgViewer {
|
||||||
|
class Viewer;
|
||||||
|
}
|
||||||
|
|
||||||
namespace Shader
|
namespace Shader
|
||||||
{
|
{
|
||||||
|
struct HotReloadManager;
|
||||||
/// @brief Reads shader template files and turns them into a concrete shader, based on a list of define's.
|
/// @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.
|
/// @par Shader templates can get the value of a define with the syntax @define.
|
||||||
class ShaderManager
|
class ShaderManager
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
friend HotReloadManager;
|
||||||
ShaderManager();
|
ShaderManager();
|
||||||
|
~ShaderManager();
|
||||||
|
|
||||||
void setShaderPath(const std::string& path);
|
void setShaderPath(const std::string& path);
|
||||||
|
|
||||||
|
@ -67,6 +73,9 @@ namespace Shader
|
||||||
|
|
||||||
int reserveGlobalTextureUnits(Slot slot);
|
int reserveGlobalTextureUnits(Slot slot);
|
||||||
|
|
||||||
|
void update(osgViewer::Viewer& viewer);
|
||||||
|
void setHotReloadEnabled(bool value);
|
||||||
|
void triggerShaderReload();
|
||||||
private:
|
private:
|
||||||
void getLinkedShaders(osg::ref_ptr<osg::Shader> shader, const std::vector<std::string>& linkedShaderNames, const DefineMap& defines);
|
void getLinkedShaders(osg::ref_ptr<osg::Shader> shader, const std::vector<std::string>& linkedShaderNames, const DefineMap& defines);
|
||||||
void addLinkedShaders(osg::ref_ptr<osg::Shader> shader, osg::ref_ptr<osg::Program> program);
|
void addLinkedShaders(osg::ref_ptr<osg::Shader> shader, osg::ref_ptr<osg::Program> program);
|
||||||
|
@ -96,7 +105,7 @@ namespace Shader
|
||||||
|
|
||||||
int mMaxTextureUnits = 0;
|
int mMaxTextureUnits = 0;
|
||||||
int mReservedTextureUnits = 0;
|
int mReservedTextureUnits = 0;
|
||||||
|
std::unique_ptr<HotReloadManager> mHotReloadManager;
|
||||||
std::array<int, 2> mReservedTextureUnitsBySlot = {-1, -1};
|
std::array<int, 2> mReservedTextureUnitsBySlot = {-1, -1};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -45,8 +45,9 @@ Hot Reloading
|
||||||
=============
|
=============
|
||||||
|
|
||||||
It is possible to modify a shader without restarting OpenMW, :ref:`live reload`
|
It is possible to modify a shader without restarting OpenMW, :ref:`live reload`
|
||||||
must be enabled in ``settings.cfg``. Whenever a file is modified and saved, the
|
must be enabled by using the lua command `debug.setShaderHotReloadEnabled(true)`.
|
||||||
shader will automatically reload in game. This allows shaders to be written in a
|
Whenever a file is modified and saved, the shader will automatically reload in game.
|
||||||
text editor you are comfortable with. The only restriction is that the VFS is not
|
You can also trigger a single reload using `debug.triggerShaderReload()`
|
||||||
aware of new files or changes in non-shader files, so new shaders and localization
|
This allows shaders to be written in a text editor you are comfortable with.
|
||||||
strings can not be used.
|
The only restriction is that the VFS is not aware of new files or changes in non-shader files,
|
||||||
|
so new shaders and localization strings can not be used.
|
||||||
|
|
|
@ -41,4 +41,12 @@
|
||||||
-- @function [parent=#debug] setNavMeshRenderMode
|
-- @function [parent=#debug] setNavMeshRenderMode
|
||||||
-- @param #NAV_MESH_RENDER_MODE value
|
-- @param #NAV_MESH_RENDER_MODE value
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Enable/disable automatic reload of modified shaders
|
||||||
|
-- @function [parent=#debug] setShaderHotReloadEnabled
|
||||||
|
-- @param #bool value
|
||||||
|
|
||||||
|
---
|
||||||
|
-- To reload modified shaders
|
||||||
|
-- @function [parent=#debug] triggerShaderReload
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -670,19 +670,6 @@
|
||||||
<property name="leftMargin">
|
<property name="leftMargin">
|
||||||
<number>20</number>
|
<number>20</number>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
|
||||||
<widget class="QCheckBox" name="postprocessLiveReloadCheckBox">
|
|
||||||
<property name="enabled">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="toolTip">
|
|
||||||
<string><html><head/><body><p>Debug Mode. Automatically reload active shaders when they are modified on filesystem.</p></body></html></string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Live reload</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="postprocessTransparentPostpassCheckBox">
|
<widget class="QCheckBox" name="postprocessTransparentPostpassCheckBox">
|
||||||
<property name="enabled">
|
<property name="enabled">
|
||||||
|
|
Loading…
Reference in a new issue