1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-01-30 12:32:36 +00:00
OpenMW/apps/openmw/mwrender/postprocessor.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

841 lines
32 KiB
C++
Raw Normal View History

2021-06-01 12:15:25 -07:00
#include "postprocessor.hpp"
#include <SDL_opengl_glext.h>
2022-05-21 06:42:05 +00:00
#include <algorithm>
#include <chrono>
#include <thread>
2022-02-07 11:51:59 -08:00
2022-05-13 18:58:00 -07:00
#include <osg/Texture1D>
2021-06-01 12:15:25 -07:00
#include <osg/Texture2D>
#include <osg/Texture2DArray>
2022-06-21 15:55:06 +00:00
#include <osg/Texture2DMultisample>
2022-05-13 18:58:00 -07:00
#include <osg/Texture3D>
2021-06-01 12:15:25 -07:00
#include <components/files/conversion.hpp>
2021-06-01 12:15:25 -07:00
#include <components/misc/strings/algorithm.hpp>
#include <components/misc/strings/lower.hpp>
2021-11-21 02:25:05 +00:00
#include <components/resource/scenemanager.hpp>
#include <components/sceneutil/color.hpp>
#include <components/sceneutil/depth.hpp>
#include <components/sceneutil/nodecallback.hpp>
#include <components/settings/values.hpp>
2022-05-13 18:58:00 -07:00
#include <components/shader/shadermanager.hpp>
#include <components/stereo/multiview.hpp>
#include <components/stereo/stereomanager.hpp>
#include <components/vfs/manager.hpp>
#include <components/vfs/recursivedirectoryiterator.hpp>
2022-05-13 18:58:00 -07:00
#include "../mwbase/environment.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwgui/postprocessorhud.hpp"
2023-11-10 08:02:53 -08:00
#include "distortion.hpp"
2022-05-13 18:58:00 -07:00
#include "pingpongcull.hpp"
2023-11-10 08:02:53 -08:00
#include "renderbin.hpp"
2022-05-13 18:58:00 -07:00
#include "renderingmanager.hpp"
#include "sky.hpp"
#include "transparentpass.hpp"
#include "vismask.hpp"
2021-06-01 12:15:25 -07:00
namespace
{
2022-05-13 18:58:00 -07:00
struct ResizedCallback : osg::GraphicsContext::ResizedCallback
2021-06-01 12:15:25 -07:00
{
2022-05-13 18:58:00 -07:00
ResizedCallback(MWRender::PostProcessor* postProcessor)
: mPostProcessor(postProcessor)
{
}
2021-06-01 12:15:25 -07:00
2022-05-13 18:58:00 -07:00
void resizedImplementation(osg::GraphicsContext* gc, int x, int y, int width, int height) override
{
gc->resizedImplementation(x, y, width, height);
2021-06-01 12:15:25 -07:00
2022-05-13 18:58:00 -07:00
mPostProcessor->setRenderTargetSize(width, height);
mPostProcessor->resize();
}
2021-06-01 12:15:25 -07:00
2022-05-13 18:58:00 -07:00
MWRender::PostProcessor* mPostProcessor;
};
2021-06-01 12:15:25 -07:00
2022-05-13 18:58:00 -07:00
class HUDCullCallback : public SceneUtil::NodeCallback<HUDCullCallback, osg::Camera*, osgUtil::CullVisitor*>
2021-06-01 12:15:25 -07:00
{
public:
2022-05-13 18:58:00 -07:00
void operator()(osg::Camera* camera, osgUtil::CullVisitor* cv)
2021-08-04 17:49:57 -07:00
{
2022-05-13 18:58:00 -07:00
osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet;
auto& sm = Stereo::Manager::instance();
auto* fullViewport = camera->getViewport();
if (sm.getEye(cv) == Stereo::Eye::Left)
stateset->setAttributeAndModes(
new osg::Viewport(0, 0, fullViewport->width() / 2, fullViewport->height()));
if (sm.getEye(cv) == Stereo::Eye::Right)
stateset->setAttributeAndModes(
new osg::Viewport(fullViewport->width() / 2, 0, fullViewport->width() / 2, fullViewport->height()));
cv->pushStateSet(stateset);
traverse(camera, cv);
2022-06-21 15:55:06 +00:00
cv->popStateSet();
2021-08-04 17:49:57 -07:00
}
2022-05-13 18:58:00 -07:00
};
2022-06-21 15:55:06 +00:00
enum class Usage
{
RENDER_BUFFER,
TEXTURE,
};
static osg::FrameBufferAttachment createFrameBufferAttachmentFromTemplate(
Usage usage, int width, int height, osg::Texture* template_, int samples)
{
if (usage == Usage::RENDER_BUFFER && !Stereo::getMultiview())
{
osg::ref_ptr<osg::RenderBuffer> attachment
= new osg::RenderBuffer(width, height, template_->getInternalFormat(), samples);
return osg::FrameBufferAttachment(attachment);
}
auto texture = Stereo::createMultiviewCompatibleTexture(width, height, samples);
2022-06-21 15:55:06 +00:00
texture->setSourceFormat(template_->getSourceFormat());
texture->setSourceType(template_->getSourceType());
texture->setInternalFormat(template_->getInternalFormat());
texture->setFilter(osg::Texture2D::MIN_FILTER, template_->getFilter(osg::Texture2D::MIN_FILTER));
texture->setFilter(osg::Texture2D::MAG_FILTER, template_->getFilter(osg::Texture2D::MAG_FILTER));
texture->setWrap(osg::Texture::WRAP_S, template_->getWrap(osg::Texture2D::WRAP_S));
texture->setWrap(osg::Texture::WRAP_T, template_->getWrap(osg::Texture2D::WRAP_T));
return Stereo::createMultiviewCompatibleAttachment(texture);
2022-06-21 15:55:06 +00:00
}
2023-11-10 08:02:53 -08:00
constexpr float DistortionRatio = 0.25;
2022-05-13 18:58:00 -07:00
}
2021-06-01 12:15:25 -07:00
2022-05-13 18:58:00 -07:00
namespace MWRender
{
PostProcessor::PostProcessor(
RenderingManager& rendering, osgViewer::Viewer* viewer, osg::Group* rootNode, const VFS::Manager* vfs)
: osg::Group()
, mRootNode(rootNode)
, mHUDCamera(new osg::Camera)
2022-05-13 18:58:00 -07:00
, mRendering(rendering)
, mViewer(viewer)
, mVFS(vfs)
, mUsePostProcessing(Settings::postProcessing().mEnabled)
, mSamples(Settings::video().mAntialiasing)
, mPingPongCull(new PingPongCull(this))
2023-11-10 08:02:53 -08:00
, mDistortionCallback(new DistortionCallback)
2022-05-13 18:58:00 -07:00
{
auto& shaderManager = mRendering.getResourceSystem()->getSceneManager()->getShaderManager();
std::shared_ptr<LuminanceCalculator> luminanceCalculator = std::make_shared<LuminanceCalculator>(shaderManager);
for (auto& canvas : mCanvases)
canvas = new PingPongCanvas(shaderManager, luminanceCalculator);
mHUDCamera->setReferenceFrame(osg::Camera::ABSOLUTE_RF);
mHUDCamera->setRenderOrder(osg::Camera::POST_RENDER);
mHUDCamera->setClearColor(osg::Vec4(0.45, 0.45, 0.14, 1.0));
mHUDCamera->setClearMask(0);
mHUDCamera->setProjectionMatrix(osg::Matrix::ortho2D(0, 1, 0, 1));
mHUDCamera->setAllowEventFocus(false);
mHUDCamera->setViewport(0, 0, mWidth, mHeight);
mHUDCamera->setNodeMask(Mask_RenderToTexture);
mHUDCamera->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
mHUDCamera->getOrCreateStateSet()->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
mHUDCamera->addChild(mCanvases[0]);
mHUDCamera->addChild(mCanvases[1]);
mHUDCamera->setCullCallback(new HUDCullCallback);
mViewer->getCamera()->addCullCallback(mPingPongCull);
2023-11-10 08:02:53 -08:00
// resolves the multisampled depth buffer and optionally draws an additional depth postpass
mTransparentDepthPostPass
= new TransparentDepthBinCallback(mRendering.getResourceSystem()->getSceneManager()->getShaderManager(),
Settings::postProcessing().mTransparentPostpass);
osgUtil::RenderBin::getRenderBinPrototype("DepthSortedBin")->setDrawCallback(mTransparentDepthPostPass);
osg::ref_ptr<osgUtil::RenderBin> distortionRenderBin
= new osgUtil::RenderBin(osgUtil::RenderBin::SORT_BACK_TO_FRONT);
// This is silly to have to do, but if nothing is drawn then the drawcallback is never called and the distortion
// texture will never be cleared
osg::ref_ptr<osg::Node> dummyNodeToClear = new osg::Node;
dummyNodeToClear->setCullingActive(false);
dummyNodeToClear->getOrCreateStateSet()->setRenderBinDetails(RenderBin_Distortion, "Distortion");
rootNode->addChild(dummyNodeToClear);
distortionRenderBin->setDrawCallback(mDistortionCallback);
distortionRenderBin->getStateSet()->setDefine("DISTORTION", "1", osg::StateAttribute::ON);
// Give the renderbin access to the opaque depth sampler so it can write its occlusion
// Distorted geometry is drawn with ALWAYS depth function and depths writes disbled.
const int unitSoftEffect
= shaderManager.reserveGlobalTextureUnits(Shader::ShaderManager::Slot::OpaqueDepthTexture);
distortionRenderBin->getStateSet()->addUniform(new osg::Uniform("opaqueDepthTex", unitSoftEffect));
osgUtil::RenderBin::addRenderBinPrototype("Distortion", distortionRenderBin);
auto defines = shaderManager.getGlobalDefines();
defines["distorionRTRatio"] = std::to_string(DistortionRatio);
shaderManager.setGlobalDefines(defines);
createObjectsForFrame(0);
createObjectsForFrame(1);
populateTechniqueFiles();
2023-11-10 08:02:53 -08:00
auto distortion = loadTechnique("internal_distortion");
distortion->setInternal(true);
distortion->setLocked(true);
mInternalTechniques.push_back(distortion);
2022-05-13 18:58:00 -07:00
osg::GraphicsContext* gc = viewer->getCamera()->getGraphicsContext();
osg::GLExtensions* ext = gc->getState()->get<osg::GLExtensions>();
mWidth = gc->getTraits()->width;
mHeight = gc->getTraits()->height;
if (!ext->glDisablei && ext->glDisableIndexedEXT)
ext->glDisablei = ext->glDisableIndexedEXT;
2022-05-15 09:36:21 -07:00
#ifdef ANDROID
ext->glDisablei = nullptr;
#endif
2022-05-13 18:58:00 -07:00
if (ext->glDisablei)
mNormalsSupported = true;
else
Log(Debug::Error) << "'glDisablei' unsupported, pass normals will not be available to shaders.";
mGLSLVersion = ext->glslLanguageVersion * 100;
2022-07-02 23:13:44 +04:00
mUBO = ext->isUniformBufferObjectSupported && mGLSLVersion >= 330;
2022-05-13 18:58:00 -07:00
mStateUpdater = new fx::StateUpdater(mUBO);
addChild(mHUDCamera);
addChild(mRootNode);
2022-05-13 18:58:00 -07:00
mViewer->setSceneData(this);
mViewer->getCamera()->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);
mViewer->getCamera()->getGraphicsContext()->setResizedCallback(new ResizedCallback(this));
mViewer->getCamera()->setUserData(this);
setCullCallback(mStateUpdater);
if (mUsePostProcessing)
enable();
2022-05-13 18:58:00 -07:00
}
PostProcessor::~PostProcessor()
{
if (auto* bin = osgUtil::RenderBin::getRenderBinPrototype("DepthSortedBin"))
bin->setDrawCallback(nullptr);
}
void PostProcessor::resize()
{
2022-06-21 15:55:06 +00:00
mHUDCamera->resize(mWidth, mHeight);
mViewer->getCamera()->resize(mWidth, mHeight);
if (Stereo::getStereo())
Stereo::Manager::instance().screenResolutionChanged();
2022-05-13 18:58:00 -07:00
size_t frameId = frame() % 2;
2021-06-01 12:15:25 -07:00
2022-05-13 18:58:00 -07:00
createObjectsForFrame(frameId);
mRendering.updateProjectionMatrix();
mRendering.setScreenRes(renderWidth(), renderHeight());
2022-05-13 18:58:00 -07:00
dirtyTechniques(true);
2022-05-13 18:58:00 -07:00
mDirty = true;
mDirtyFrameId = !frameId;
}
2022-05-21 06:42:05 +00:00
void PostProcessor::populateTechniqueFiles()
{
for (const auto& name : mVFS->getRecursiveDirectoryIterator(fx::Technique::sSubdir))
{
std::filesystem::path path = Files::pathFromUnicodeString(name);
std::string fileExt = Misc::StringUtils::lowerCase(Files::pathToUnicodeString(path.extension()));
2022-05-21 06:42:05 +00:00
if (!path.parent_path().has_parent_path() && fileExt == fx::Technique::sExt)
{
const auto absolutePath = mVFS->getAbsoluteFileName(path);
mTechniqueFileMap[Files::pathToUnicodeString(absolutePath.stem())] = absolutePath;
2022-05-21 06:42:05 +00:00
}
}
}
void PostProcessor::enable()
2021-06-01 12:15:25 -07:00
{
2022-05-13 18:58:00 -07:00
mReload = true;
mUsePostProcessing = true;
2022-05-13 18:58:00 -07:00
}
2022-05-13 18:58:00 -07:00
void PostProcessor::disable()
{
mUsePostProcessing = false;
mRendering.getSkyManager()->setSunglare(true);
}
void PostProcessor::traverse(osg::NodeVisitor& nv)
{
size_t frameId = nv.getTraversalNumber() % 2;
2021-06-01 12:15:25 -07:00
2022-05-13 18:58:00 -07:00
if (nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR)
cull(frameId, static_cast<osgUtil::CullVisitor*>(&nv));
else if (nv.getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR)
update(frameId);
osg::Group::traverse(nv);
}
void PostProcessor::cull(size_t frameId, osgUtil::CullVisitor* cv)
2021-06-01 12:15:25 -07:00
{
if (const auto& fbo = getFbo(FBO_Intercept, frameId))
2022-05-13 18:58:00 -07:00
{
osgUtil::RenderStage* rs = cv->getRenderStage();
if (rs && rs->getMultisampleResolveFramebufferObject())
rs->setMultisampleResolveFramebufferObject(fbo);
}
2021-10-20 09:42:18 -07:00
mCanvases[frameId]->setPostProcessing(mUsePostProcessing);
mCanvases[frameId]->setTextureNormals(mNormals ? getTexture(Tex_Normal, frameId) : nullptr);
mCanvases[frameId]->setMask(mUnderwater, mExteriorFlag);
mCanvases[frameId]->setCalculateAvgLum(mHDR);
mCanvases[frameId]->setTextureScene(getTexture(Tex_Scene, frameId));
2023-11-10 08:02:53 -08:00
mCanvases[frameId]->setTextureDepth(getTexture(Tex_OpaqueDepth, frameId));
mCanvases[frameId]->setTextureDistortion(getTexture(Tex_Distortion, frameId));
2023-11-10 08:02:53 -08:00
mTransparentDepthPostPass->mFbo[frameId] = mFbos[frameId][FBO_Primary];
mTransparentDepthPostPass->mMsaaFbo[frameId] = mFbos[frameId][FBO_Multisample];
mTransparentDepthPostPass->mOpaqueFbo[frameId] = mFbos[frameId][FBO_OpaqueDepth];
mDistortionCallback->setFBO(mFbos[frameId][FBO_Distortion], frameId);
mDistortionCallback->setOriginalFBO(mFbos[frameId][FBO_Primary], frameId);
2022-05-13 18:58:00 -07:00
size_t frame = cv->getTraversalNumber();
mStateUpdater->setResolution(osg::Vec2f(cv->getViewport()->width(), cv->getViewport()->height()));
// per-frame data
if (frame != mLastFrameNumber)
{
2022-05-13 18:58:00 -07:00
mLastFrameNumber = frame;
auto stamp = cv->getFrameStamp();
mStateUpdater->setSimulationTime(static_cast<float>(stamp->getSimulationTime()));
mStateUpdater->setDeltaSimulationTime(static_cast<float>(stamp->getSimulationTime() - mLastSimulationTime));
mLastSimulationTime = stamp->getSimulationTime();
for (const auto& dispatchNode : mCanvases[frameId]->getPasses())
2022-05-13 18:58:00 -07:00
{
for (auto& uniform : dispatchNode.mHandle->getUniformMap())
{
if (uniform->getType().has_value() && !uniform->mSamplerType)
if (auto* u = dispatchNode.mRootStateSet->getUniform(uniform->mName))
uniform->setUniform(u);
}
}
}
2022-05-13 18:58:00 -07:00
}
2022-05-21 06:42:05 +00:00
void PostProcessor::updateLiveReload()
2022-05-13 18:58:00 -07:00
{
if (!mEnableLiveReload && !mTriggerShaderReload)
2022-05-21 06:42:05 +00:00
return;
mTriggerShaderReload = false; // Done only once
2022-05-21 06:42:05 +00:00
for (auto& technique : mTechniques)
{
2022-05-21 06:42:05 +00:00
if (technique->getStatus() == fx::Technique::Status::File_Not_exists)
continue;
2022-05-13 18:58:00 -07:00
2022-05-21 06:42:05 +00:00
const auto lastWriteTime = std::filesystem::last_write_time(mTechniqueFileMap[technique->getName()]);
const bool isDirty = technique->setLastModificationTime(lastWriteTime);
2022-05-13 18:58:00 -07:00
2022-05-23 21:27:32 -07:00
if (!isDirty)
2022-05-21 06:42:05 +00:00
continue;
2022-05-13 18:58:00 -07:00
// TODO: Temporary workaround to avoid conflicts with external programs saving the file, especially
// problematic on Windows.
// If we move to a file watcher using native APIs this should be removed.
std::this_thread::sleep_for(std::chrono::milliseconds(5));
2022-05-21 06:42:05 +00:00
if (technique->compile())
Log(Debug::Info) << "Reloaded technique : " << mTechniqueFileMap[technique->getName()];
2022-05-13 18:58:00 -07:00
2022-05-21 06:42:05 +00:00
mReload = technique->isValid();
}
2022-05-21 06:42:05 +00:00
}
2022-05-21 06:42:05 +00:00
void PostProcessor::reloadIfRequired()
{
if (!mReload)
return;
2022-05-13 18:58:00 -07:00
2022-05-21 06:42:05 +00:00
mReload = false;
2021-06-01 12:15:25 -07:00
loadChain();
resize();
2022-05-21 06:42:05 +00:00
}
void PostProcessor::update(size_t frameId)
{
2022-05-22 18:53:38 -07:00
while (!mQueuedTemplates.empty())
{
mTemplates.push_back(std::move(mQueuedTemplates.back()));
mQueuedTemplates.pop_back();
}
2022-05-21 06:42:05 +00:00
updateLiveReload();
reloadIfRequired();
2021-06-01 12:15:25 -07:00
mCanvases[frameId]->setNodeMask(~0u);
mCanvases[!frameId]->setNodeMask(0);
2022-05-13 18:58:00 -07:00
if (mDirty && mDirtyFrameId == frameId)
{
2022-05-13 18:58:00 -07:00
createObjectsForFrame(frameId);
mDirty = false;
mCanvases[frameId]->setPasses(fx::DispatchArray(mTemplateData));
}
2021-06-01 12:15:25 -07:00
2022-05-14 22:53:53 -07:00
if ((mNormalsSupported && mNormals != mPrevNormals) || (mPassLights != mPrevPassLights))
2022-05-13 18:58:00 -07:00
{
mPrevNormals = mNormals;
2022-05-14 22:53:53 -07:00
mPrevPassLights = mPassLights;
2022-05-13 18:58:00 -07:00
mViewer->stopThreading();
auto& shaderManager = MWBase::Environment::get().getResourceSystem()->getSceneManager()->getShaderManager();
auto defines = shaderManager.getGlobalDefines();
defines["disableNormals"] = mNormals ? "0" : "1";
shaderManager.setGlobalDefines(defines);
2022-05-14 22:53:53 -07:00
mRendering.getLightRoot()->setCollectPPLights(mPassLights);
mStateUpdater->bindPointLights(mPassLights ? mRendering.getLightRoot()->getPPLightsBuffer() : nullptr);
mStateUpdater->reset();
2022-05-13 18:58:00 -07:00
mViewer->startThreading();
createObjectsForFrame(frameId);
2022-05-14 22:53:53 -07:00
2022-05-13 18:58:00 -07:00
mDirty = true;
mDirtyFrameId = !frameId;
}
2021-06-01 12:15:25 -07:00
}
2022-05-13 18:58:00 -07:00
void PostProcessor::createObjectsForFrame(size_t frameId)
2021-06-01 12:15:25 -07:00
{
2022-05-13 18:58:00 -07:00
auto& textures = mTextures[frameId];
int width = renderWidth();
int height = renderHeight();
for (osg::ref_ptr<osg::Texture>& texture : textures)
2021-10-20 09:42:18 -07:00
{
if (!texture)
{
if (Stereo::getMultiview())
texture = new osg::Texture2DArray;
else
texture = new osg::Texture2D;
}
Stereo::setMultiviewCompatibleTextureSize(texture, width, height);
texture->setSourceFormat(GL_RGBA);
texture->setSourceType(GL_UNSIGNED_BYTE);
texture->setInternalFormat(GL_RGBA);
texture->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture::LINEAR);
texture->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture::LINEAR);
texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
texture->setResizeNonPowerOfTwoHint(false);
Stereo::setMultiviewCompatibleTextureSize(texture, width, height);
texture->dirtyTextureObject();
}
2022-05-13 18:58:00 -07:00
textures[Tex_Normal]->setSourceFormat(GL_RGB);
textures[Tex_Normal]->setInternalFormat(GL_RGB);
2023-11-10 08:02:53 -08:00
textures[Tex_Distortion]->setSourceFormat(GL_RGB);
textures[Tex_Distortion]->setInternalFormat(GL_RGB);
Stereo::setMultiviewCompatibleTextureSize(
textures[Tex_Distortion], width * DistortionRatio, height * DistortionRatio);
textures[Tex_Distortion]->dirtyTextureObject();
auto setupDepth = [](osg::Texture* tex) {
tex->setSourceFormat(GL_DEPTH_STENCIL_EXT);
tex->setSourceType(SceneUtil::AutoDepth::depthSourceType());
tex->setInternalFormat(SceneUtil::AutoDepth::depthInternalFormat());
};
setupDepth(textures[Tex_Depth]);
2023-11-10 08:02:53 -08:00
setupDepth(textures[Tex_OpaqueDepth]);
textures[Tex_OpaqueDepth]->setName("opaqueTexMap");
auto& fbos = mFbos[frameId];
2021-10-20 09:42:18 -07:00
2022-05-13 18:58:00 -07:00
fbos[FBO_Primary] = new osg::FrameBufferObject;
fbos[FBO_Primary]->setAttachment(
osg::Camera::COLOR_BUFFER0, Stereo::createMultiviewCompatibleAttachment(textures[Tex_Scene]));
2022-05-13 18:58:00 -07:00
if (mNormals && mNormalsSupported)
fbos[FBO_Primary]->setAttachment(
osg::Camera::COLOR_BUFFER1, Stereo::createMultiviewCompatibleAttachment(textures[Tex_Normal]));
fbos[FBO_Primary]->setAttachment(
osg::Camera::PACKED_DEPTH_STENCIL_BUFFER, Stereo::createMultiviewCompatibleAttachment(textures[Tex_Depth]));
2021-06-01 12:15:25 -07:00
2022-05-13 18:58:00 -07:00
fbos[FBO_FirstPerson] = new osg::FrameBufferObject;
2022-06-21 15:55:06 +00:00
auto fpDepthRb = createFrameBufferAttachmentFromTemplate(
Usage::RENDER_BUFFER, width, height, textures[Tex_Depth], mSamples);
2022-05-13 18:58:00 -07:00
fbos[FBO_FirstPerson]->setAttachment(osg::FrameBufferObject::BufferComponent::PACKED_DEPTH_STENCIL_BUFFER,
osg::FrameBufferAttachment(fpDepthRb));
2021-06-01 12:15:25 -07:00
2022-05-13 18:58:00 -07:00
if (mSamples > 1)
2021-06-01 12:15:25 -07:00
{
2022-05-13 18:58:00 -07:00
fbos[FBO_Multisample] = new osg::FrameBufferObject;
2022-06-21 15:55:06 +00:00
auto colorRB = createFrameBufferAttachmentFromTemplate(
Usage::RENDER_BUFFER, width, height, textures[Tex_Scene], mSamples);
2022-05-13 18:58:00 -07:00
if (mNormals && mNormalsSupported)
{
2022-06-21 15:55:06 +00:00
auto normalRB = createFrameBufferAttachmentFromTemplate(
Usage::RENDER_BUFFER, width, height, textures[Tex_Normal], mSamples);
fbos[FBO_Multisample]->setAttachment(osg::FrameBufferObject::BufferComponent::COLOR_BUFFER1, normalRB);
2023-11-10 08:02:53 -08:00
fbos[FBO_FirstPerson]->setAttachment(osg::FrameBufferObject::BufferComponent::COLOR_BUFFER1, normalRB);
2022-05-13 18:58:00 -07:00
}
2022-06-21 15:55:06 +00:00
auto depthRB = createFrameBufferAttachmentFromTemplate(
Usage::RENDER_BUFFER, width, height, textures[Tex_Depth], mSamples);
fbos[FBO_Multisample]->setAttachment(osg::FrameBufferObject::BufferComponent::COLOR_BUFFER0, colorRB);
fbos[FBO_Multisample]->setAttachment(
osg::FrameBufferObject::BufferComponent::PACKED_DEPTH_STENCIL_BUFFER, depthRB);
fbos[FBO_FirstPerson]->setAttachment(osg::FrameBufferObject::BufferComponent::COLOR_BUFFER0, colorRB);
2022-05-13 18:58:00 -07:00
fbos[FBO_Intercept] = new osg::FrameBufferObject;
fbos[FBO_Intercept]->setAttachment(osg::FrameBufferObject::BufferComponent::COLOR_BUFFER0,
Stereo::createMultiviewCompatibleAttachment(textures[Tex_Scene]));
fbos[FBO_Intercept]->setAttachment(osg::FrameBufferObject::BufferComponent::COLOR_BUFFER1,
Stereo::createMultiviewCompatibleAttachment(textures[Tex_Normal]));
2022-05-13 18:58:00 -07:00
}
else
{
fbos[FBO_FirstPerson]->setAttachment(osg::FrameBufferObject::BufferComponent::COLOR_BUFFER0,
Stereo::createMultiviewCompatibleAttachment(textures[Tex_Scene]));
2022-05-13 18:58:00 -07:00
if (mNormals && mNormalsSupported)
fbos[FBO_FirstPerson]->setAttachment(osg::FrameBufferObject::BufferComponent::COLOR_BUFFER1,
Stereo::createMultiviewCompatibleAttachment(textures[Tex_Normal]));
2021-06-01 12:15:25 -07:00
}
2023-11-10 08:02:53 -08:00
fbos[FBO_OpaqueDepth] = new osg::FrameBufferObject;
fbos[FBO_OpaqueDepth]->setAttachment(osg::FrameBufferObject::BufferComponent::PACKED_DEPTH_STENCIL_BUFFER,
Stereo::createMultiviewCompatibleAttachment(textures[Tex_OpaqueDepth]));
fbos[FBO_Distortion] = new osg::FrameBufferObject;
fbos[FBO_Distortion]->setAttachment(osg::FrameBufferObject::BufferComponent::COLOR_BUFFER0,
Stereo::createMultiviewCompatibleAttachment(textures[Tex_Distortion]));
2022-05-13 18:58:00 -07:00
#ifdef __APPLE__
if (textures[Tex_OpaqueDepth])
fbos[FBO_OpaqueDepth]->setAttachment(osg::FrameBufferObject::BufferComponent::COLOR_BUFFER,
osg::FrameBufferAttachment(new osg::RenderBuffer(textures[Tex_OpaqueDepth]->getTextureWidth(),
textures[Tex_OpaqueDepth]->getTextureHeight(), textures[Tex_Scene]->getInternalFormat())));
#endif
mCanvases[frameId]->dirty();
2021-06-01 12:15:25 -07:00
}
void PostProcessor::dirtyTechniques(bool dirtyAttachments)
{
size_t frameId = frame() % 2;
mDirty = true;
mDirtyFrameId = !frameId;
mTemplateData = {};
2022-05-13 18:58:00 -07:00
bool sunglare = true;
mHDR = false;
mNormals = false;
2022-05-14 22:53:53 -07:00
mPassLights = false;
2022-05-13 18:58:00 -07:00
std::vector<fx::Types::RenderTarget> attachmentsToDirty;
2022-05-13 18:58:00 -07:00
for (const auto& technique : mTechniques)
{
2022-05-13 18:58:00 -07:00
if (!technique->isValid())
continue;
2022-05-13 18:58:00 -07:00
if (technique->getGLSLVersion() > mGLSLVersion)
{
2022-05-13 18:58:00 -07:00
Log(Debug::Warning) << "Technique " << technique->getName() << " requires GLSL version "
<< technique->getGLSLVersion() << " which is unsupported by your hardware.";
continue;
}
2022-05-13 18:58:00 -07:00
fx::DispatchNode node;
node.mFlags = technique->getFlags();
if (technique->getHDR())
mHDR = true;
if (technique->getNormals())
mNormals = true;
2022-05-14 22:53:53 -07:00
if (technique->getLights())
mPassLights = true;
2022-05-13 18:58:00 -07:00
if (node.mFlags & fx::Technique::Flag_Disable_SunGlare)
sunglare = false;
// required default samplers available to every shader pass
node.mRootStateSet->addUniform(new osg::Uniform("omw_SamplerLastShader", Unit_LastShader));
node.mRootStateSet->addUniform(new osg::Uniform("omw_SamplerLastPass", Unit_LastPass));
node.mRootStateSet->addUniform(new osg::Uniform("omw_SamplerDepth", Unit_Depth));
2023-11-10 08:02:53 -08:00
node.mRootStateSet->addUniform(new osg::Uniform("omw_SamplerDistortion", Unit_Distortion));
2022-05-13 18:58:00 -07:00
if (mNormals)
node.mRootStateSet->addUniform(new osg::Uniform("omw_SamplerNormals", Unit_Normals));
if (technique->getHDR())
node.mRootStateSet->addUniform(new osg::Uniform("omw_EyeAdaptation", Unit_EyeAdaptation));
2023-11-10 08:02:53 -08:00
node.mRootStateSet->addUniform(new osg::Uniform("omw_SamplerDistortion", Unit_Distortion));
2022-05-13 18:58:00 -07:00
int texUnit = Unit_NextFree;
// user-defined samplers
for (const osg::Texture* texture : technique->getTextures())
{
if (const auto* tex1D = dynamic_cast<const osg::Texture1D*>(texture))
node.mRootStateSet->setTextureAttribute(texUnit, new osg::Texture1D(*tex1D));
2022-05-13 18:58:00 -07:00
else if (const auto* tex2D = dynamic_cast<const osg::Texture2D*>(texture))
node.mRootStateSet->setTextureAttribute(texUnit, new osg::Texture2D(*tex2D));
2022-05-13 18:58:00 -07:00
else if (const auto* tex3D = dynamic_cast<const osg::Texture3D*>(texture))
node.mRootStateSet->setTextureAttribute(texUnit, new osg::Texture3D(*tex3D));
2022-05-13 18:58:00 -07:00
node.mRootStateSet->addUniform(new osg::Uniform(texture->getName().c_str(), texUnit++));
}
// user-defined uniforms
for (auto& uniform : technique->getUniformMap())
{
if (uniform->mSamplerType)
continue;
if (auto type = uniform->getType())
2022-05-22 18:53:38 -07:00
uniform->setUniform(node.mRootStateSet->getOrCreateUniform(
uniform->mName.c_str(), *type, uniform->getNumElements()));
2022-05-13 18:58:00 -07:00
}
for (const auto& pass : technique->getPasses())
{
int subTexUnit = texUnit;
2022-05-13 18:58:00 -07:00
fx::DispatchNode::SubPass subPass;
pass->prepareStateSet(subPass.mStateSet, technique->getName());
node.mHandle = technique;
if (!pass->getTarget().empty())
{
auto& renderTarget = technique->getRenderTargetsMap()[pass->getTarget()];
subPass.mSize = renderTarget.mSize;
subPass.mRenderTexture = renderTarget.mTarget;
subPass.mMipMap = renderTarget.mMipMap;
2022-05-13 18:58:00 -07:00
subPass.mRenderTarget = new osg::FrameBufferObject;
subPass.mRenderTarget->setAttachment(osg::FrameBufferObject::BufferComponent::COLOR_BUFFER0,
osg::FrameBufferAttachment(subPass.mRenderTexture));
const auto [w, h] = renderTarget.mSize.get(renderWidth(), renderHeight());
2022-05-13 18:58:00 -07:00
subPass.mStateSet->setAttributeAndModes(new osg::Viewport(0, 0, w, h));
if (std::find_if(attachmentsToDirty.cbegin(), attachmentsToDirty.cend(),
[renderTarget](const auto& rt) { return renderTarget.mTarget == rt.mTarget; })
== attachmentsToDirty.cend())
{
attachmentsToDirty.push_back(fx::Types::RenderTarget(renderTarget));
}
}
2022-05-13 18:58:00 -07:00
for (const auto& name : pass->getRenderTargets())
{
2024-01-18 07:15:35 -08:00
if (name.empty())
{
continue;
}
auto& renderTarget = technique->getRenderTargetsMap()[name];
subPass.mStateSet->setTextureAttribute(subTexUnit, renderTarget.mTarget);
subPass.mStateSet->addUniform(new osg::Uniform(name.c_str(), subTexUnit));
if (std::find_if(attachmentsToDirty.cbegin(), attachmentsToDirty.cend(),
[renderTarget](const auto& rt) { return renderTarget.mTarget == rt.mTarget; })
== attachmentsToDirty.cend())
{
attachmentsToDirty.push_back(fx::Types::RenderTarget(renderTarget));
}
subTexUnit++;
2022-05-13 18:58:00 -07:00
}
2022-05-13 18:58:00 -07:00
node.mPasses.emplace_back(std::move(subPass));
}
node.compile();
mTemplateData.emplace_back(std::move(node));
}
2022-05-13 18:58:00 -07:00
mCanvases[frameId]->setPasses(fx::DispatchArray(mTemplateData));
2022-05-13 18:58:00 -07:00
if (auto hud = MWBase::Environment::get().getWindowManager()->getPostProcessorHud())
hud->updateTechniques();
mRendering.getSkyManager()->setSunglare(sunglare);
if (dirtyAttachments)
mCanvases[frameId]->setDirtyAttachments(attachmentsToDirty);
2022-05-13 18:58:00 -07:00
}
PostProcessor::Status PostProcessor::enableTechnique(
std::shared_ptr<fx::Technique> technique, std::optional<int> location)
2022-05-13 18:58:00 -07:00
{
if (!technique || technique->getLocked() || (location.has_value() && location.value() < 0))
return Status_Error;
2022-05-13 18:58:00 -07:00
disableTechnique(technique, false);
2023-11-10 08:02:53 -08:00
int pos = std::min<int>(location.value_or(mTechniques.size()) + mInternalTechniques.size(), mTechniques.size());
2022-05-13 18:58:00 -07:00
mTechniques.insert(mTechniques.begin() + pos, technique);
dirtyTechniques(Settings::ShaderManager::get().getMode() == Settings::ShaderManager::Mode::Debug);
2022-05-13 18:58:00 -07:00
return Status_Toggled;
2022-05-13 18:58:00 -07:00
}
PostProcessor::Status PostProcessor::disableTechnique(std::shared_ptr<fx::Technique> technique, bool dirty)
2022-05-13 18:58:00 -07:00
{
2022-06-20 06:58:40 +00:00
if (technique->getLocked())
return Status_Error;
2022-05-23 21:27:32 -07:00
2022-05-21 06:42:05 +00:00
auto it = std::find(mTechniques.begin(), mTechniques.end(), technique);
if (it == std::end(mTechniques))
return Status_Unchanged;
2022-05-21 06:42:05 +00:00
mTechniques.erase(it);
if (dirty)
dirtyTechniques();
return Status_Toggled;
2022-05-13 18:58:00 -07:00
}
bool PostProcessor::isTechniqueEnabled(const std::shared_ptr<fx::Technique>& technique) const
{
2022-05-21 06:42:05 +00:00
if (auto it = std::find(mTechniques.begin(), mTechniques.end(), technique); it == mTechniques.end())
return false;
2022-05-21 06:42:05 +00:00
return technique->isValid();
2022-05-13 18:58:00 -07:00
}
2022-05-22 18:53:38 -07:00
std::shared_ptr<fx::Technique> PostProcessor::loadTechnique(const std::string& name, bool loadNextFrame)
2022-05-13 18:58:00 -07:00
{
2022-05-22 18:53:38 -07:00
for (const auto& technique : mTemplates)
if (Misc::StringUtils::ciEqual(technique->getName(), name))
return technique;
for (const auto& technique : mQueuedTemplates)
if (Misc::StringUtils::ciEqual(technique->getName(), name))
return technique;
2021-06-01 12:15:25 -07:00
2022-06-21 15:55:06 +00:00
auto technique = std::make_shared<fx::Technique>(*mVFS, *mRendering.getResourceSystem()->getImageManager(),
name, renderWidth(), renderHeight(), mUBO, mNormalsSupported);
2022-05-13 18:58:00 -07:00
technique->compile();
2022-05-13 18:58:00 -07:00
if (technique->getStatus() != fx::Technique::Status::File_Not_exists)
2022-05-21 06:42:05 +00:00
technique->setLastModificationTime(
std::filesystem::last_write_time(mTechniqueFileMap[technique->getName()]));
if (loadNextFrame)
2022-05-22 18:53:38 -07:00
{
mQueuedTemplates.push_back(technique);
2022-05-13 18:58:00 -07:00
return technique;
2022-05-22 18:53:38 -07:00
}
2022-05-13 18:58:00 -07:00
mTemplates.push_back(std::move(technique));
return mTemplates.back();
}
void PostProcessor::loadChain()
2022-05-13 18:58:00 -07:00
{
mTechniques.clear();
2023-11-10 08:02:53 -08:00
for (const auto& technique : mInternalTechniques)
{
mTechniques.push_back(technique);
}
for (const std::string& techniqueName : Settings::postProcessing().mChain.get())
2022-05-13 18:58:00 -07:00
{
if (techniqueName.empty())
2022-05-13 18:58:00 -07:00
continue;
2022-05-13 18:58:00 -07:00
mTechniques.push_back(loadTechnique(techniqueName));
}
dirtyTechniques();
}
2021-06-01 12:15:25 -07:00
void PostProcessor::saveChain()
2022-05-13 18:58:00 -07:00
{
std::vector<std::string> chain;
2021-06-01 12:15:25 -07:00
for (const auto& technique : mTechniques)
{
2023-11-10 08:02:53 -08:00
if (!technique || technique->getDynamic() || technique->getInternal())
continue;
chain.push_back(technique->getName());
}
2022-05-13 18:58:00 -07:00
Settings::postProcessing().mChain.set(chain);
2021-06-01 12:15:25 -07:00
}
2022-05-13 18:58:00 -07:00
void PostProcessor::toggleMode()
{
for (auto& technique : mTemplates)
technique->compile();
dirtyTechniques(true);
2022-05-13 18:58:00 -07:00
}
void PostProcessor::disableDynamicShaders()
{
for (auto& technique : mTechniques)
if (technique->getDynamic())
disableTechnique(technique);
}
2022-06-21 15:55:06 +00:00
int PostProcessor::renderWidth() const
{
if (Stereo::getStereo())
return Stereo::Manager::instance().eyeResolution().x();
return mWidth;
}
int PostProcessor::renderHeight() const
{
if (Stereo::getStereo())
return Stereo::Manager::instance().eyeResolution().y();
return mHeight;
}
void PostProcessor::triggerShaderReload()
{
mTriggerShaderReload = true;
}
2021-06-01 12:15:25 -07:00
}