Merge branch 'fbos_for_all' into 'master'

[renderer] render scene exclusively to fbo

Closes #7364 and #7354

See merge request OpenMW/openmw!3243
macos_ci_fix
uramer 1 year ago
commit 6f4e7ed5dc

@ -328,46 +328,37 @@ namespace MWRender
{
osg::State* state = renderInfo.getState();
PostProcessor* postProcessor = dynamic_cast<PostProcessor*>(renderInfo.getCurrentCamera()->getUserData());
PostProcessor* postProcessor = static_cast<PostProcessor*>(renderInfo.getCurrentCamera()->getUserData());
state->applyAttribute(mDepth);
unsigned int frameId = state->getFrameStamp()->getFrameNumber() % 2;
if (postProcessor && postProcessor->getFbo(PostProcessor::FBO_FirstPerson, frameId))
postProcessor->getFbo(PostProcessor::FBO_FirstPerson, frameId)->apply(*state);
if (mPassNormals)
{
postProcessor->getFbo(PostProcessor::FBO_FirstPerson, frameId)->apply(*state);
if (mPassNormals)
{
state->get<osg::GLExtensions>()->glColorMaski(1, true, true, true, true);
state->haveAppliedAttribute(osg::StateAttribute::COLORMASK);
}
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
// color accumulation pass
bin->drawImplementation(renderInfo, previous);
state->get<osg::GLExtensions>()->glColorMaski(1, true, true, true, true);
state->haveAppliedAttribute(osg::StateAttribute::COLORMASK);
}
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
// color accumulation pass
bin->drawImplementation(renderInfo, previous);
auto primaryFBO = postProcessor->getPrimaryFbo(frameId);
auto primaryFBO = postProcessor->getPrimaryFbo(frameId);
if (postProcessor->getFbo(PostProcessor::FBO_OpaqueDepth, frameId))
postProcessor->getFbo(PostProcessor::FBO_OpaqueDepth, frameId)->apply(*state);
else
primaryFBO->apply(*state);
if (postProcessor->getFbo(PostProcessor::FBO_OpaqueDepth, frameId))
postProcessor->getFbo(PostProcessor::FBO_OpaqueDepth, frameId)->apply(*state);
else
primaryFBO->apply(*state);
// depth accumulation pass
osg::ref_ptr<osg::StateSet> restore = bin->getStateSet();
bin->setStateSet(mStateSet);
bin->drawImplementation(renderInfo, previous);
bin->setStateSet(restore);
// depth accumulation pass
osg::ref_ptr<osg::StateSet> restore = bin->getStateSet();
bin->setStateSet(mStateSet);
bin->drawImplementation(renderInfo, previous);
bin->setStateSet(restore);
if (postProcessor->getFbo(PostProcessor::FBO_OpaqueDepth, frameId))
primaryFBO->apply(*state);
}
else
{
// fallback to standard depth clear when we are not rendering our main scene via an intermediate FBO
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
bin->drawImplementation(renderInfo, previous);
}
if (postProcessor->getFbo(PostProcessor::FBO_OpaqueDepth, frameId))
primaryFBO->apply(*state);
state->checkGLErrors("after DepthClearCallback::drawImplementation");
}

@ -43,19 +43,16 @@ namespace MWRender
mMultiviewResolveStateSet->addUniform(new osg::Uniform("lastShader", 0));
}
void PingPongCanvas::setCurrentFrameData(size_t frameId, fx::DispatchArray&& data)
void PingPongCanvas::setPasses(fx::DispatchArray&& passes)
{
mBufferData[frameId].data = std::move(data);
mPasses = std::move(passes);
}
void PingPongCanvas::setMask(size_t frameId, bool underwater, bool exterior)
void PingPongCanvas::setMask(bool underwater, bool exterior)
{
mBufferData[frameId].mask = 0;
mBufferData[frameId].mask
|= underwater ? fx::Technique::Flag_Disable_Underwater : fx::Technique::Flag_Disable_Abovewater;
mBufferData[frameId].mask
|= exterior ? fx::Technique::Flag_Disable_Exteriors : fx::Technique::Flag_Disable_Interiors;
mMask = 0;
mMask |= underwater ? fx::Technique::Flag_Disable_Underwater : fx::Technique::Flag_Disable_Abovewater;
mMask |= exterior ? fx::Technique::Flag_Disable_Exteriors : fx::Technique::Flag_Disable_Interiors;
}
void PingPongCanvas::drawGeometry(osg::RenderInfo& renderInfo) const
@ -77,19 +74,15 @@ namespace MWRender
size_t frameId = state.getFrameStamp()->getFrameNumber() % 2;
auto& bufferData = mBufferData[frameId];
const auto& data = bufferData.data;
std::vector<size_t> filtered;
filtered.reserve(data.size());
filtered.reserve(mPasses.size());
for (size_t i = 0; i < data.size(); ++i)
for (size_t i = 0; i < mPasses.size(); ++i)
{
const auto& node = data[i];
const auto& node = mPasses[i];
if (bufferData.mask & node.mFlags)
if (mMask & node.mFlags)
continue;
filtered.push_back(i);
@ -97,7 +90,7 @@ namespace MWRender
auto* resolveViewport = state.getCurrentViewport();
if (filtered.empty() || !bufferData.postprocessing)
if (filtered.empty() || !mPostprocessing)
{
state.pushStateSet(mFallbackStateSet);
state.apply();
@ -108,7 +101,7 @@ namespace MWRender
state.apply();
}
state.applyTextureAttribute(0, bufferData.sceneTex);
state.applyTextureAttribute(0, mTextureScene);
resolveViewport->apply(state);
drawGeometry(renderInfo);
@ -124,13 +117,12 @@ namespace MWRender
const unsigned int handle = mFbos[0] ? mFbos[0]->getHandle(state.getContextID()) : 0;
if (handle == 0 || bufferData.dirty)
if (handle == 0 || mDirty)
{
for (auto& fbo : mFbos)
{
fbo = new osg::FrameBufferObject;
attachCloneOfTemplate(
fbo, osg::FrameBufferObject::BufferComponent::COLOR_BUFFER0, bufferData.sceneTexLDR);
attachCloneOfTemplate(fbo, osg::FrameBufferObject::BufferComponent::COLOR_BUFFER0, mTextureScene);
fbo->apply(state);
glClearColor(0.5, 0.5, 0.5, 1);
glClear(GL_COLOR_BUFFER_BIT);
@ -140,7 +132,7 @@ namespace MWRender
{
mMultiviewResolveFramebuffer = new osg::FrameBufferObject();
attachCloneOfTemplate(mMultiviewResolveFramebuffer,
osg::FrameBufferObject::BufferComponent::COLOR_BUFFER0, bufferData.sceneTexLDR);
osg::FrameBufferObject::BufferComponent::COLOR_BUFFER0, mTextureScene);
mMultiviewResolveFramebuffer->apply(state);
glClearColor(0.5, 0.5, 0.5, 1);
glClear(GL_COLOR_BUFFER_BIT);
@ -150,15 +142,15 @@ namespace MWRender
.getTexture());
}
mLuminanceCalculator.dirty(bufferData.sceneTex->getTextureWidth(), bufferData.sceneTex->getTextureHeight());
mLuminanceCalculator.dirty(mTextureScene->getTextureWidth(), mTextureScene->getTextureHeight());
if (Stereo::getStereo())
mRenderViewport = new osg::Viewport(
0, 0, bufferData.sceneTex->getTextureWidth(), bufferData.sceneTex->getTextureHeight());
mRenderViewport
= new osg::Viewport(0, 0, mTextureScene->getTextureWidth(), mTextureScene->getTextureHeight());
else
mRenderViewport = nullptr;
bufferData.dirty = false;
mDirty = false;
}
constexpr std::array<std::array<int, 2>, 3> buffers
@ -166,7 +158,7 @@ namespace MWRender
{ GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT2_EXT },
{ GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT } } };
(bufferData.hdr) ? mLuminanceCalculator.enable() : mLuminanceCalculator.disable();
(mAvgLum) ? mLuminanceCalculator.enable() : mLuminanceCalculator.disable();
// A histogram based approach is superior way to calculate scene luminance. Using mipmaps is more broadly
// supported, so that's what we use for now.
@ -181,8 +173,7 @@ namespace MWRender
const unsigned int cid = state.getContextID();
const osg::ref_ptr<osg::FrameBufferObject>& destinationFbo
= bufferData.destination ? bufferData.destination : nullptr;
const osg::ref_ptr<osg::FrameBufferObject>& destinationFbo = mDestinationFBO ? mDestinationFBO : nullptr;
unsigned int destinationHandle = destinationFbo ? destinationFbo->getHandle(cid) : 0;
auto bindDestinationFbo = [&]() {
@ -206,17 +197,16 @@ namespace MWRender
for (const size_t& index : filtered)
{
const auto& node = data[index];
const auto& node = mPasses[index];
node.mRootStateSet->setTextureAttribute(PostProcessor::Unit_Depth, bufferData.depthTex);
node.mRootStateSet->setTextureAttribute(PostProcessor::Unit_Depth, mTextureDepth);
if (bufferData.hdr)
if (mAvgLum)
node.mRootStateSet->setTextureAttribute(
PostProcessor::TextureUnits::Unit_EyeAdaptation, mLuminanceCalculator.getLuminanceTexture(frameId));
if (bufferData.normalsTex)
node.mRootStateSet->setTextureAttribute(
PostProcessor::TextureUnits::Unit_Normals, bufferData.normalsTex);
if (mTextureNormals)
node.mRootStateSet->setTextureAttribute(PostProcessor::TextureUnits::Unit_Normals, mTextureNormals);
state.pushStateSet(node.mRootStateSet);
state.apply();
@ -231,7 +221,7 @@ namespace MWRender
// VR-TODO: This won't actually work for tex2darrays
if (lastShader == 0)
pass.mStateSet->setTextureAttribute(PostProcessor::Unit_LastShader, bufferData.sceneTex);
pass.mStateSet->setTextureAttribute(PostProcessor::Unit_LastShader, mTextureScene);
else
pass.mStateSet->setTextureAttribute(PostProcessor::Unit_LastShader,
(osg::Texture*)mFbos[lastShader - GL_COLOR_ATTACHMENT0_EXT]
@ -239,7 +229,7 @@ namespace MWRender
.getTexture());
if (lastDraw == 0)
pass.mStateSet->setTextureAttribute(PostProcessor::Unit_LastPass, bufferData.sceneTex);
pass.mStateSet->setTextureAttribute(PostProcessor::Unit_LastPass, mTextureScene);
else
pass.mStateSet->setTextureAttribute(PostProcessor::Unit_LastPass,
(osg::Texture*)mFbos[lastDraw - GL_COLOR_ATTACHMENT0_EXT]
@ -260,7 +250,6 @@ namespace MWRender
}
lastApplied = pass.mRenderTarget->getHandle(state.getContextID());
;
}
else if (pass.mResolve && index == filtered.back())
{

@ -24,76 +24,52 @@ namespace MWRender
public:
PingPongCanvas(Shader::ShaderManager& shaderManager);
void drawImplementation(osg::RenderInfo& renderInfo) const override;
void drawGeometry(osg::RenderInfo& renderInfo) const;
void dirty(size_t frameId) { mBufferData[frameId].dirty = true; }
void drawImplementation(osg::RenderInfo& renderInfo) const override;
const fx::DispatchArray& getCurrentFrameData(size_t frame) { return mBufferData[frame % 2].data; }
void dirty() { mDirty = true; }
// Sets current frame pass data and stores copy of dispatch array to apply to next frame data
void setCurrentFrameData(size_t frameId, fx::DispatchArray&& data);
const fx::DispatchArray& getPasses() { return mPasses; }
void setMask(size_t frameId, bool underwater, bool exterior);
void setPasses(fx::DispatchArray&& passes);
void setSceneTexture(size_t frameId, osg::ref_ptr<osg::Texture> tex) { mBufferData[frameId].sceneTex = tex; }
void setMask(bool underwater, bool exterior);
void setLDRSceneTexture(size_t frameId, osg::ref_ptr<osg::Texture> tex)
{
mBufferData[frameId].sceneTexLDR = tex;
}
void setTextureScene(osg::ref_ptr<osg::Texture> tex) { mTextureScene = tex; }
void setDepthTexture(size_t frameId, osg::ref_ptr<osg::Texture> tex) { mBufferData[frameId].depthTex = tex; }
void setTextureDepth(osg::ref_ptr<osg::Texture> tex) { mTextureDepth = tex; }
void setNormalsTexture(size_t frameId, osg::ref_ptr<osg::Texture> tex)
{
mBufferData[frameId].normalsTex = tex;
}
void setTextureNormals(osg::ref_ptr<osg::Texture> tex) { mTextureNormals = tex; }
void setHDR(size_t frameId, bool hdr) { mBufferData[frameId].hdr = hdr; }
void setCalculateAvgLum(bool enabled) { mAvgLum = enabled; }
void setPostProcessing(size_t frameId, bool postprocessing)
{
mBufferData[frameId].postprocessing = postprocessing;
}
void setPostProcessing(bool enabled) { mPostprocessing = enabled; }
const osg::ref_ptr<osg::Texture>& getSceneTexture(size_t frameId) const
{
return mBufferData[frameId].sceneTex;
}
void drawGeometry(osg::RenderInfo& renderInfo) const;
const osg::ref_ptr<osg::Texture>& getSceneTexture(size_t frameId) const { return mTextureScene; }
private:
void copyNewFrameData(size_t frameId) const;
bool mAvgLum = false;
bool mPostprocessing = false;
mutable LuminanceCalculator mLuminanceCalculator;
fx::DispatchArray mPasses;
fx::FlagsType mMask;
osg::ref_ptr<osg::Program> mFallbackProgram;
osg::ref_ptr<osg::Program> mMultiviewResolveProgram;
osg::ref_ptr<osg::StateSet> mFallbackStateSet;
osg::ref_ptr<osg::StateSet> mMultiviewResolveStateSet;
mutable osg::ref_ptr<osg::FrameBufferObject> mMultiviewResolveFramebuffer;
struct BufferData
{
bool dirty = false;
bool hdr = false;
bool postprocessing = true;
osg::ref_ptr<osg::Texture> mTextureScene;
osg::ref_ptr<osg::Texture> mTextureDepth;
osg::ref_ptr<osg::Texture> mTextureNormals;
fx::DispatchArray data;
fx::FlagsType mask;
osg::ref_ptr<osg::FrameBufferObject> destination;
osg::ref_ptr<osg::Texture> sceneTex;
osg::ref_ptr<osg::Texture> depthTex;
osg::ref_ptr<osg::Texture> sceneTexLDR;
osg::ref_ptr<osg::Texture> normalsTex;
};
mutable std::array<BufferData, 2> mBufferData;
mutable std::array<osg::ref_ptr<osg::FrameBufferObject>, 3> mFbos;
mutable bool mDirty = false;
mutable osg::ref_ptr<osg::Viewport> mRenderViewport;
mutable osg::ref_ptr<osg::FrameBufferObject> mMultiviewResolveFramebuffer;
mutable osg::ref_ptr<osg::FrameBufferObject> mDestinationFBO;
mutable std::array<osg::ref_ptr<osg::FrameBufferObject>, 3> mFbos;
mutable LuminanceCalculator mLuminanceCalculator;
};
}

@ -21,7 +21,7 @@ namespace MWRender
if (Stereo::getStereo())
{
mViewportStateset = new osg::StateSet();
mViewport = new osg::Viewport(0, 0, pp->renderWidth(), pp->renderHeight());
mViewport = new osg::Viewport;
mViewportStateset->setAttribute(mViewport);
}
}
@ -37,41 +37,31 @@ namespace MWRender
size_t frame = cv->getTraversalNumber();
size_t frameId = frame % 2;
MWRender::PostProcessor* postProcessor
= dynamic_cast<MWRender::PostProcessor*>(cv->getCurrentCamera()->getUserData());
if (!postProcessor)
throw std::runtime_error("PingPongCull: failed to get a PostProcessor!");
if (Stereo::getStereo())
{
auto& sm = Stereo::Manager::instance();
auto view = sm.getEye(cv);
int index = view == Stereo::Eye::Right ? 1 : 0;
auto projectionMatrix = sm.computeEyeProjection(index, true);
postProcessor->getStateUpdater()->setProjectionMatrix(projectionMatrix);
mPostProcessor->getStateUpdater()->setProjectionMatrix(projectionMatrix);
}
postProcessor->getStateUpdater()->setViewMatrix(cv->getCurrentCamera()->getViewMatrix());
postProcessor->getStateUpdater()->setPrevViewMatrix(mLastViewMatrix[0]);
mPostProcessor->getStateUpdater()->setViewMatrix(cv->getCurrentCamera()->getViewMatrix());
mPostProcessor->getStateUpdater()->setPrevViewMatrix(mLastViewMatrix[0]);
mLastViewMatrix[0] = cv->getCurrentCamera()->getViewMatrix();
postProcessor->getStateUpdater()->setEyePos(cv->getEyePoint());
postProcessor->getStateUpdater()->setEyeVec(cv->getLookVectorLocal());
mPostProcessor->getStateUpdater()->setEyePos(cv->getEyePoint());
mPostProcessor->getStateUpdater()->setEyeVec(cv->getLookVectorLocal());
if (!postProcessor->getFbo(PostProcessor::FBO_Primary, frameId))
{
renderStage->setMultisampleResolveFramebufferObject(nullptr);
renderStage->setFrameBufferObject(nullptr);
}
else if (!postProcessor->getFbo(PostProcessor::FBO_Multisample, frameId))
if (!mPostProcessor->getFbo(PostProcessor::FBO_Multisample, frameId))
{
renderStage->setFrameBufferObject(postProcessor->getFbo(PostProcessor::FBO_Primary, frameId));
renderStage->setFrameBufferObject(mPostProcessor->getFbo(PostProcessor::FBO_Primary, frameId));
}
else
{
renderStage->setMultisampleResolveFramebufferObject(
postProcessor->getFbo(PostProcessor::FBO_Primary, frameId));
renderStage->setFrameBufferObject(postProcessor->getFbo(PostProcessor::FBO_Multisample, frameId));
mPostProcessor->getFbo(PostProcessor::FBO_Primary, frameId));
renderStage->setFrameBufferObject(mPostProcessor->getFbo(PostProcessor::FBO_Multisample, frameId));
// The MultiView patch has a bug where it does not update resolve layers if the resolve framebuffer is
// changed. So we do blit manually in this case

@ -110,30 +110,45 @@ namespace MWRender
PostProcessor::PostProcessor(
RenderingManager& rendering, osgViewer::Viewer* viewer, osg::Group* rootNode, const VFS::Manager* vfs)
: osg::Group()
, mEnableLiveReload(false)
, mRootNode(rootNode)
, mSamples(Settings::video().mAntialiasing)
, mDirty(false)
, mDirtyFrameId(0)
, mHUDCamera(new osg::Camera)
, mRendering(rendering)
, mViewer(viewer)
, mVFS(vfs)
, mTriggerShaderReload(false)
, mReload(false)
, mEnabled(false)
, mUsePostProcessing(Settings::postProcessing().mEnabled)
, mDisableDepthPasses(false)
, mLastFrameNumber(0)
, mLastSimulationTime(0.f)
, mExteriorFlag(false)
, mUnderwater(false)
, mHDR(false)
, mNormals(false)
, mPrevNormals(false)
, mNormalsSupported(false)
, mPassLights(false)
, mPrevPassLights(false)
, mSamples(Settings::video().mAntialiasing)
, mPingPongCull(new PingPongCull(this))
, mCanvases({ new PingPongCanvas(mRendering.getResourceSystem()->getSceneManager()->getShaderManager()),
new PingPongCanvas(mRendering.getResourceSystem()->getSceneManager()->getShaderManager()) })
{
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);
if (Settings::shaders().mSoftParticles || Settings::postProcessing().mTransparentPostpass)
{
mTransparentDepthPostPass
= new TransparentDepthBinCallback(mRendering.getResourceSystem()->getSceneManager()->getShaderManager(),
Settings::postProcessing().mTransparentPostpass);
osgUtil::RenderBin::getRenderBinPrototype("DepthSortedBin")->setDrawCallback(mTransparentDepthPostPass);
}
createObjectsForFrame(0);
createObjectsForFrame(1);
populateTechniqueFiles();
osg::GraphicsContext* gc = viewer->getCamera()->getGraphicsContext();
osg::GLExtensions* ext = gc->getState()->get<osg::GLExtensions>();
@ -169,11 +184,18 @@ namespace MWRender
mUBO = ext->isUniformBufferObjectSupported && mGLSLVersion >= 330;
mStateUpdater = new fx::StateUpdater(mUBO);
if (!Stereo::getStereo() && !SceneUtil::AutoDepth::isReversed() && !Settings::shaders().mSoftParticles
&& !mUsePostProcessing)
return;
addChild(mHUDCamera);
addChild(mRootNode);
enable(mUsePostProcessing);
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();
}
PostProcessor::~PostProcessor()
@ -202,7 +224,6 @@ namespace MWRender
size_t frameId = frame() % 2;
createTexturesAndCamera(frameId);
createObjectsForFrame(frameId);
mRendering.updateProjectionMatrix();
@ -210,8 +231,6 @@ namespace MWRender
dirtyTechniques();
mPingPongCanvas->dirty(frameId);
mDirty = true;
mDirtyFrameId = !frameId;
}
@ -230,77 +249,20 @@ namespace MWRender
}
}
void PostProcessor::enable(bool usePostProcessing)
void PostProcessor::enable()
{
mReload = true;
mEnabled = true;
const bool postPass = Settings::postProcessing().mTransparentPostpass;
mUsePostProcessing = usePostProcessing;
mDisableDepthPasses = !Settings::shaders().mSoftParticles && !postPass;
#ifdef ANDROID
mDisableDepthPasses = true;
#endif
if (!mDisableDepthPasses)
{
mTransparentDepthPostPass = new TransparentDepthBinCallback(
mRendering.getResourceSystem()->getSceneManager()->getShaderManager(), postPass);
osgUtil::RenderBin::getRenderBinPrototype("DepthSortedBin")->setDrawCallback(mTransparentDepthPostPass);
}
if (mUsePostProcessing && mTechniqueFileMap.empty())
{
populateTechniqueFiles();
}
createTexturesAndCamera(frame() % 2);
removeChild(mHUDCamera);
removeChild(mRootNode);
addChild(mHUDCamera);
addChild(mRootNode);
mViewer->setSceneData(this);
mViewer->getCamera()->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);
mViewer->getCamera()->getGraphicsContext()->setResizedCallback(new ResizedCallback(this));
mViewer->getCamera()->setUserData(this);
setCullCallback(mStateUpdater);
mHUDCamera->setCullCallback(new HUDCullCallback);
mUsePostProcessing = true;
}
void PostProcessor::disable()
{
if (!Settings::shaders().mSoftParticles)
osgUtil::RenderBin::getRenderBinPrototype("DepthSortedBin")->setDrawCallback(nullptr);
if (!SceneUtil::AutoDepth::isReversed() && !Settings::shaders().mSoftParticles)
{
removeChild(mHUDCamera);
setCullCallback(nullptr);
mViewer->getCamera()->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER);
mViewer->getCamera()->getGraphicsContext()->setResizedCallback(nullptr);
mViewer->getCamera()->setUserData(nullptr);
mEnabled = false;
}
mUsePostProcessing = false;
mRendering.getSkyManager()->setSunglare(true);
}
void PostProcessor::traverse(osg::NodeVisitor& nv)
{
if (!mEnabled)
{
osg::Group::traverse(nv);
return;
}
size_t frameId = nv.getTraversalNumber() % 2;
if (nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR)
@ -313,26 +275,23 @@ namespace MWRender
void PostProcessor::cull(size_t frameId, osgUtil::CullVisitor* cv)
{
const auto& fbo = getFbo(FBO_Intercept, frameId);
if (fbo)
if (const auto& fbo = getFbo(FBO_Intercept, frameId))
{
osgUtil::RenderStage* rs = cv->getRenderStage();
if (rs && rs->getMultisampleResolveFramebufferObject())
rs->setMultisampleResolveFramebufferObject(fbo);
}
mPingPongCanvas->setPostProcessing(frameId, mUsePostProcessing);
mPingPongCanvas->setNormalsTexture(frameId, mNormals ? getTexture(Tex_Normal, frameId) : nullptr);
mPingPongCanvas->setMask(frameId, mUnderwater, mExteriorFlag);
mPingPongCanvas->setHDR(frameId, getHDR());
mCanvases[frameId]->setPostProcessing(mUsePostProcessing);
mCanvases[frameId]->setTextureNormals(mNormals ? getTexture(Tex_Normal, frameId) : nullptr);
mCanvases[frameId]->setMask(mUnderwater, mExteriorFlag);
mCanvases[frameId]->setCalculateAvgLum(mHDR);
mPingPongCanvas->setSceneTexture(frameId, getTexture(Tex_Scene, frameId));
if (mDisableDepthPasses)
mPingPongCanvas->setDepthTexture(frameId, getTexture(Tex_Depth, frameId));
mCanvases[frameId]->setTextureScene(getTexture(Tex_Scene, frameId));
if (mTransparentDepthPostPass)
mCanvases[frameId]->setTextureDepth(getTexture(Tex_OpaqueDepth, frameId));
else
mPingPongCanvas->setDepthTexture(frameId, getTexture(Tex_OpaqueDepth, frameId));
mPingPongCanvas->setLDRSceneTexture(frameId, getTexture(Tex_Scene_LDR, frameId));
mCanvases[frameId]->setTextureDepth(getTexture(Tex_Depth, frameId));
if (mTransparentDepthPostPass)
{
@ -355,7 +314,7 @@ namespace MWRender
mStateUpdater->setDeltaSimulationTime(static_cast<float>(stamp->getSimulationTime() - mLastSimulationTime));
mLastSimulationTime = stamp->getSimulationTime();
for (const auto& dispatchNode : mPingPongCanvas->getCurrentFrameData(frame))
for (const auto& dispatchNode : mCanvases[frameId]->getPasses())
{
for (auto& uniform : dispatchNode.mHandle->getUniformMap())
{
@ -421,13 +380,15 @@ namespace MWRender
reloadIfRequired();
mCanvases[frameId]->setNodeMask(~0u);
mCanvases[!frameId]->setNodeMask(0);
if (mDirty && mDirtyFrameId == frameId)
{
createTexturesAndCamera(frameId);
createObjectsForFrame(frameId);
mDirty = false;
mPingPongCanvas->setCurrentFrameData(frameId, fx::DispatchArray(mTemplateData));
mDirty = false;
mCanvases[frameId]->setPasses(fx::DispatchArray(mTemplateData));
}
if ((mNormalsSupported && mNormals != mPrevNormals) || (mPassLights != mPrevPassLights))
@ -448,7 +409,6 @@ namespace MWRender
mViewer->startThreading();
createTexturesAndCamera(frameId);
createObjectsForFrame(frameId);
mDirty = true;
@ -458,19 +418,55 @@ namespace MWRender
void PostProcessor::createObjectsForFrame(size_t frameId)
{
auto& fbos = mFbos[frameId];
auto& textures = mTextures[frameId];
auto width = renderWidth();
auto height = renderHeight();
for (auto& tex : textures)
int width = renderWidth();
int height = renderHeight();
for (osg::ref_ptr<osg::Texture>& texture : textures)
{
if (!tex)
continue;
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();
}
Stereo::setMultiviewCompatibleTextureSize(tex, width, height);
tex->dirtyTextureObject();
textures[Tex_Normal]->setSourceFormat(GL_RGB);
textures[Tex_Normal]->setInternalFormat(GL_RGB);
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]);
if (!mTransparentDepthPostPass)
{
textures[Tex_OpaqueDepth] = nullptr;
}
else
{
setupDepth(textures[Tex_OpaqueDepth]);
textures[Tex_OpaqueDepth]->setName("opaqueTexMap");
}
auto& fbos = mFbos[frameId];
fbos[FBO_Primary] = new osg::FrameBufferObject;
fbos[FBO_Primary]->setAttachment(
@ -534,13 +530,12 @@ namespace MWRender
osg::FrameBufferAttachment(new osg::RenderBuffer(textures[Tex_OpaqueDepth]->getTextureWidth(),
textures[Tex_OpaqueDepth]->getTextureHeight(), textures[Tex_Scene]->getInternalFormat())));
#endif
mCanvases[frameId]->dirty();
}
void PostProcessor::dirtyTechniques()
{
if (!isEnabled())
return;
size_t frameId = frame() % 2;
mDirty = true;
@ -667,7 +662,7 @@ namespace MWRender
mTemplateData.emplace_back(std::move(node));
}
mPingPongCanvas->setCurrentFrameData(frameId, fx::DispatchArray(mTemplateData));
mCanvases[frameId]->setPasses(fx::DispatchArray(mTemplateData));
if (auto hud = MWBase::Environment::get().getWindowManager()->getPostProcessorHud())
hud->updateTechniques();
@ -678,12 +673,6 @@ namespace MWRender
PostProcessor::Status PostProcessor::enableTechnique(
std::shared_ptr<fx::Technique> technique, std::optional<int> location)
{
if (!isEnabled())
{
Log(Debug::Warning) << "PostProcessing disabled, cannot load technique '" << technique->getName() << "'";
return Status_Error;
}
if (!technique || technique->getLocked() || (location.has_value() && location.value() < 0))
return Status_Error;
@ -721,86 +710,8 @@ namespace MWRender
return technique->isValid();
}
void PostProcessor::createTexturesAndCamera(size_t frameId)
{
auto& textures = mTextures[frameId];
auto width = renderWidth();
auto height = renderHeight();
for (auto& texture : textures)
{
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);
}
textures[Tex_Normal]->setSourceFormat(GL_RGB);
textures[Tex_Normal]->setInternalFormat(GL_RGB);
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]);
if (mDisableDepthPasses)
{
textures[Tex_OpaqueDepth] = nullptr;
}
else
{
setupDepth(textures[Tex_OpaqueDepth]);
textures[Tex_OpaqueDepth]->setName("opaqueTexMap");
}
if (mHUDCamera)
return;
mHUDCamera = new osg::Camera;
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);
mViewer->getCamera()->removeCullCallback(mPingPongCull);
mPingPongCull = new PingPongCull(this);
mViewer->getCamera()->addCullCallback(mPingPongCull);
mPingPongCanvas = new PingPongCanvas(mRendering.getResourceSystem()->getSceneManager()->getShaderManager());
mHUDCamera->addChild(mPingPongCanvas);
mHUDCamera->setNodeMask(Mask_RenderToTexture);
mHUDCamera->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
mHUDCamera->getOrCreateStateSet()->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
}
std::shared_ptr<fx::Technique> PostProcessor::loadTechnique(const std::string& name, bool loadNextFrame)
{
if (!isEnabled())
{
Log(Debug::Warning) << "PostProcessing disabled, cannot load technique '" << name << "'";
return nullptr;
}
for (const auto& technique : mTemplates)
if (Misc::StringUtils::ciEqual(technique->getName(), name))
return technique;
@ -831,9 +742,6 @@ namespace MWRender
void PostProcessor::loadChain()
{
if (!isEnabled())
return;
mTechniques.clear();
for (const std::string& techniqueName : Settings::postProcessing().mChain.get())

@ -115,14 +115,14 @@ namespace MWRender
return mFbos[frameId][FBO_Multisample] ? mFbos[frameId][FBO_Multisample] : mFbos[frameId][FBO_Primary];
}
osg::ref_ptr<osg::Camera> getHUDCamera() { return mHUDCamera; }
osg::ref_ptr<fx::StateUpdater> getStateUpdater() { return mStateUpdater; }
const TechniqueList& getTechniques() { return mTechniques; }
const TechniqueList& getTemplates() const { return mTemplates; }
osg::ref_ptr<PingPongCanvas> getCanvas() { return mPingPongCanvas; }
const auto& getTechniqueMap() const { return mTechniqueFileMap; }
void resize();
@ -173,13 +173,11 @@ namespace MWRender
std::shared_ptr<fx::Technique> loadTechnique(const std::string& name, bool loadNextFrame = false);
bool isEnabled() const { return mUsePostProcessing && mEnabled; }
bool getHDR() const { return mHDR; }
bool isEnabled() const { return mUsePostProcessing; }
void disable();
void enable(bool usePostProcessing = true);
void enable();
void setRenderTargetSize(int width, int height)
{
@ -194,7 +192,7 @@ namespace MWRender
void triggerShaderReload();
bool mEnableLiveReload;
bool mEnableLiveReload = false;
void loadChain();
void saveChain();
@ -206,10 +204,6 @@ namespace MWRender
void createObjectsForFrame(size_t frameId);
void createTexturesAndCamera(size_t frameId);
void reloadMainPass(fx::Technique& technique);
void dirtyTechniques();
void update(size_t frameId);
@ -232,43 +226,39 @@ namespace MWRender
std::unordered_map<std::string, std::filesystem::path> mTechniqueFileMap;
int mSamples;
bool mDirty;
size_t mDirtyFrameId;
RenderingManager& mRendering;
osgViewer::Viewer* mViewer;
const VFS::Manager* mVFS;
bool mTriggerShaderReload;
bool mReload;
bool mEnabled;
bool mUsePostProcessing;
bool mDisableDepthPasses;
size_t mLastFrameNumber;
float mLastSimulationTime;
bool mExteriorFlag;
bool mUnderwater;
bool mHDR;
bool mNormals;
bool mPrevNormals;
bool mNormalsSupported;
bool mPassLights;
bool mPrevPassLights;
bool mUBO;
size_t mDirtyFrameId = 0;
size_t mLastFrameNumber = 0;
float mLastSimulationTime = 0.f;
bool mDirty = false;
bool mReload = true;
bool mTriggerShaderReload = false;
bool mUsePostProcessing = false;
bool mUBO = false;
bool mHDR = false;
bool mNormals = false;
bool mUnderwater = false;
bool mPassLights = false;
bool mPrevNormals = false;
bool mExteriorFlag = false;
bool mNormalsSupported = false;
bool mPrevPassLights = false;
int mGLSLVersion;
int mWidth;
int mHeight;
int mSamples;
osg::ref_ptr<fx::StateUpdater> mStateUpdater;
osg::ref_ptr<PingPongCull> mPingPongCull;
osg::ref_ptr<PingPongCanvas> mPingPongCanvas;
std::array<osg::ref_ptr<PingPongCanvas>, 2> mCanvases;
osg::ref_ptr<TransparentDepthBinCallback> mTransparentDepthPostPass;
int mWidth;
int mHeight;
fx::DispatchArray mTemplateData;
};
}

@ -1256,7 +1256,7 @@ namespace MWRender
mSharedUniformStateUpdater->setScreenRes(res.x(), res.y());
Stereo::Manager::instance().setMasterProjectionMatrix(mPerViewUniformStateUpdater->getProjectionMatrix());
}
else if (!mPostProcessor->isEnabled())
else
{
mSharedUniformStateUpdater->setScreenRes(width, height);
}

@ -21,6 +21,7 @@
#include "../mwbase/environment.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/world.hpp"
#include "postprocessor.hpp"
#include "util.hpp"
@ -102,24 +103,6 @@ namespace MWRender
int width = screenW - leftPadding * 2;
int height = screenH - topPadding * 2;
// Ensure we are reading from the resolved framebuffer and not the multisampled render buffer. Also ensure
// that the readbuffer is set correctly with rendeirng to FBO. glReadPixel() cannot read from multisampled
// targets
PostProcessor* postProcessor = dynamic_cast<PostProcessor*>(renderInfo.getCurrentCamera()->getUserData());
osg::GLExtensions* ext = osg::GLExtensions::Get(renderInfo.getContextID(), false);
if (ext)
{
size_t frameId = renderInfo.getState()->getFrameStamp()->getFrameNumber() % 2;
osg::FrameBufferObject* fbo = nullptr;
if (postProcessor && postProcessor->getFbo(PostProcessor::FBO_Primary, frameId))
fbo = postProcessor->getFbo(PostProcessor::FBO_Primary, frameId);
if (fbo)
fbo->apply(*renderInfo.getState(), osg::FrameBufferObject::READ_FRAMEBUFFER);
}
mImage->readPixels(leftPadding, topPadding, width, height, GL_RGB, GL_UNSIGNED_BYTE);
mImage->scaleImage(mWidth, mHeight, 1);
}
@ -145,7 +128,7 @@ namespace MWRender
void ScreenshotManager::screenshot(osg::Image* image, int w, int h)
{
osg::Camera* camera = mViewer->getCamera();
osg::Camera* camera = MWBase::Environment::get().getWorld()->getPostProcessor()->getHUDCamera();
osg::ref_ptr<osg::Drawable> tempDrw = new osg::Drawable;
tempDrw->setDrawCallback(new ReadImageFromFramebufferCallback(image, w, h));
tempDrw->setCullingActive(false);

Loading…
Cancel
Save