diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index c026392a9f..73bd9db484 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include "../mwbase/world.hpp" #include "../mwworld/class.hpp" @@ -179,20 +180,6 @@ namespace MWRender camera->setComputeNearFarMode(osg::Camera::DO_NOT_COMPUTE_NEAR_FAR); SceneUtil::setCameraClearDepth(camera); -#ifdef OSG_HAS_MULTIVIEW - if (shouldDoTextureArray()) - { - auto* viewUniform = new osg::Uniform(osg::Uniform::FLOAT_MAT4, "viewMatrixMultiView", 2); - auto* projUniform = new osg::Uniform(osg::Uniform::FLOAT_MAT4, "projectionMatrixMultiView", 2); - viewUniform->setElement(0, osg::Matrix::identity()); - viewUniform->setElement(1, osg::Matrix::identity()); - projUniform->setElement(0, mPerspectiveMatrix); - projUniform->setElement(1, mPerspectiveMatrix); - mGroup->getOrCreateStateSet()->addUniform(viewUniform, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); - mGroup->getOrCreateStateSet()->addUniform(projUniform, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); - } -#endif - camera->setNodeMask(Mask_RenderToTexture); camera->addChild(mGroup); }; @@ -202,6 +189,9 @@ namespace MWRender if(mCameraStateset) camera->setStateSet(mCameraStateset); camera->setViewMatrix(mViewMatrix); + + if (shouldDoTextureArray()) + Stereo::setMultiviewMatrices(mGroup->getOrCreateStateSet(), { mPerspectiveMatrix, mPerspectiveMatrix }); }; void addChild(osg::Node* node) diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index ecfb2d6387..63784d2dae 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -702,16 +702,7 @@ void LocalMapRenderToTexture::setDefaults(osg::Camera* camera) stateset->addUniform(new osg::Uniform("projectionMatrix", static_cast(mProjectionMatrix)), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); if (Stereo::getMultiview()) - { - auto* viewUniform = new osg::Uniform(osg::Uniform::FLOAT_MAT4, "viewMatrixMultiView", 2); - auto* projUniform = new osg::Uniform(osg::Uniform::FLOAT_MAT4, "projectionMatrixMultiView", 2); - viewUniform->setElement(0, osg::Matrix::identity()); - viewUniform->setElement(1, osg::Matrix::identity()); - projUniform->setElement(0, mProjectionMatrix); - projUniform->setElement(1, mProjectionMatrix); - stateset->addUniform(viewUniform, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); - stateset->addUniform(projUniform, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); - } + Stereo::setMultiviewMatrices(stateset, { mProjectionMatrix, mProjectionMatrix }); // assign large value to effectively turn off fog // shaders don't respect glDisable(GL_FOG) diff --git a/apps/openmw/mwrender/pingpongcanvas.cpp b/apps/openmw/mwrender/pingpongcanvas.cpp index 581209eddb..f6b8e464fe 100644 --- a/apps/openmw/mwrender/pingpongcanvas.cpp +++ b/apps/openmw/mwrender/pingpongcanvas.cpp @@ -67,25 +67,8 @@ namespace MWRender static void attachCloneOfTemplate(osg::FrameBufferObject* fbo, osg::Camera::BufferComponent component, osg::Texture* tex) { - switch (tex->getTextureTarget()) - { - case GL_TEXTURE_2D: - { - auto* tex2d = new osg::Texture2D(*static_cast(tex)); - fbo->setAttachment(component, osg::FrameBufferAttachment(tex2d)); - } - break; - case GL_TEXTURE_2D_ARRAY: - { -#ifdef OSG_HAS_MULTIVIEW - auto* tex2dArray = new osg::Texture2DArray(*static_cast(tex)); - fbo->setAttachment(component, osg::FrameBufferAttachment(tex2dArray, osg::Camera::FACE_CONTROLLED_BY_MULTIVIEW_SHADER, 0)); -#endif - } - break; - default: - throw std::logic_error("Invalid texture type received"); - } + osg::ref_ptr clone = static_cast(tex->clone(osg::CopyOp::SHALLOW_COPY)); + fbo->setAttachment(component, Stereo::createMultiviewCompatibleAttachment(clone)); } void PingPongCanvas::drawImplementation(osg::RenderInfo& renderInfo) const diff --git a/apps/openmw/mwrender/postprocessor.cpp b/apps/openmw/mwrender/postprocessor.cpp index 8e5947ba89..0991ede7f2 100644 --- a/apps/openmw/mwrender/postprocessor.cpp +++ b/apps/openmw/mwrender/postprocessor.cpp @@ -9,10 +9,6 @@ #include #include -#ifdef OSG_HAS_MULTIVIEW -#include -#endif - #include #include #include @@ -75,53 +71,6 @@ namespace } }; - - static void setTextureSize(osg::Texture* tex, int w, int h) - { - switch (tex->getTextureTarget()) - { - case GL_TEXTURE_2D: - static_cast(tex)->setTextureSize(w, h); - break; - case GL_TEXTURE_2D_ARRAY: - static_cast(tex)->setTextureSize(w, h, 2); - break; - case GL_TEXTURE_2D_MULTISAMPLE: - static_cast(tex)->setTextureSize(w, h); - break; -#ifdef OSG_HAS_MULTIVIEW - case GL_TEXTURE_2D_MULTISAMPLE_ARRAY: - static_cast(tex)->setTextureSize(w, h, 2); - break; -#endif - default: - throw std::logic_error("Invalid texture type received"); - } - } - - static void setAttachment(osg::FrameBufferObject* fbo, osg::Camera::BufferComponent component, osg::Texture* tex) - { - switch (tex->getTextureTarget()) - { - case GL_TEXTURE_2D: - { - auto* tex2d = static_cast(tex); - fbo->setAttachment(component, osg::FrameBufferAttachment(tex2d)); - } - break; - case GL_TEXTURE_2D_ARRAY: - { -#ifdef OSG_HAS_MULTIVIEW - auto* tex2dArray = static_cast(tex); - fbo->setAttachment(component, osg::FrameBufferAttachment(tex2dArray, osg::Camera::FACE_CONTROLLED_BY_MULTIVIEW_SHADER, 0)); -#endif - } - break; - default: - throw std::logic_error("Invalid texture type received"); - } - } - enum class Usage { RENDER_BUFFER, @@ -136,35 +85,7 @@ namespace return osg::FrameBufferAttachment(attachment); } - osg::FrameBufferAttachment attachment; - -#ifdef OSG_HAS_MULTIVIEW - if (Stereo::getMultiview()) - { - if (samples > 1) - { - attachment = osg::FrameBufferAttachment(new osg::Texture2DMultisampleArray(), osg::Camera::FACE_CONTROLLED_BY_MULTIVIEW_SHADER, 0); - } - else - { - attachment = osg::FrameBufferAttachment(new osg::Texture2DArray(), osg::Camera::FACE_CONTROLLED_BY_MULTIVIEW_SHADER, 0); - } - } - else -#endif - { - if (samples > 1) - { - attachment = osg::FrameBufferAttachment(new osg::Texture2DMultisample()); - } - else - { - attachment = osg::FrameBufferAttachment(new osg::Texture2D()); - } - } - - osg::Texture* texture = attachment.getTexture(); - setTextureSize(texture, width, height); + auto texture = Stereo::createMultiviewCompatibleTexture(width, height, samples); texture->setSourceFormat(template_->getSourceFormat()); texture->setSourceType(template_->getSourceType()); texture->setInternalFormat(template_->getInternalFormat()); @@ -173,7 +94,7 @@ namespace 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 attachment; + return Stereo::createMultiviewCompatibleAttachment(texture); } } @@ -547,15 +468,15 @@ namespace MWRender if (!tex) continue; - setTextureSize(tex, width, height); + Stereo::setMultiviewCompatibleTextureSize(tex, width, height); tex->dirtyTextureObject(); } fbos[FBO_Primary] = new osg::FrameBufferObject; - setAttachment(fbos[FBO_Primary], osg::Camera::COLOR_BUFFER0, textures[Tex_Scene]); + fbos[FBO_Primary]->setAttachment(osg::Camera::COLOR_BUFFER0, Stereo::createMultiviewCompatibleAttachment(textures[Tex_Scene])); if (mNormals && mNormalsSupported) - setAttachment(fbos[FBO_Primary], osg::Camera::COLOR_BUFFER1, textures[Tex_Normal]); - setAttachment(fbos[FBO_Primary], osg::Camera::PACKED_DEPTH_STENCIL_BUFFER, textures[Tex_Depth]); + 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])); fbos[FBO_FirstPerson] = new osg::FrameBufferObject; @@ -581,20 +502,20 @@ namespace MWRender fbos[FBO_FirstPerson]->setAttachment(osg::FrameBufferObject::BufferComponent::COLOR_BUFFER0, colorRB); fbos[FBO_Intercept] = new osg::FrameBufferObject; - setAttachment(fbos[FBO_Intercept], osg::FrameBufferObject::BufferComponent::COLOR_BUFFER0, textures[Tex_Scene]); - setAttachment(fbos[FBO_Intercept], osg::FrameBufferObject::BufferComponent::COLOR_BUFFER1, textures[Tex_Normal]); + 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])); } else { - setAttachment(fbos[FBO_FirstPerson], osg::FrameBufferObject::BufferComponent::COLOR_BUFFER0, textures[Tex_Scene]); + fbos[FBO_FirstPerson]->setAttachment(osg::FrameBufferObject::BufferComponent::COLOR_BUFFER0, Stereo::createMultiviewCompatibleAttachment(textures[Tex_Scene])); if (mNormals && mNormalsSupported) - setAttachment(fbos[FBO_FirstPerson], osg::FrameBufferObject::BufferComponent::COLOR_BUFFER1, textures[Tex_Normal]); + fbos[FBO_FirstPerson]->setAttachment(osg::FrameBufferObject::BufferComponent::COLOR_BUFFER1, Stereo::createMultiviewCompatibleAttachment(textures[Tex_Normal])); } if (textures[Tex_OpaqueDepth]) { fbos[FBO_OpaqueDepth] = new osg::FrameBufferObject; - setAttachment(fbos[FBO_OpaqueDepth], osg::FrameBufferObject::BufferComponent::PACKED_DEPTH_STENCIL_BUFFER, textures[Tex_OpaqueDepth]); + fbos[FBO_OpaqueDepth]->setAttachment(osg::FrameBufferObject::BufferComponent::PACKED_DEPTH_STENCIL_BUFFER, Stereo::createMultiviewCompatibleAttachment(textures[Tex_OpaqueDepth])); } #ifdef __APPLE__ @@ -799,7 +720,7 @@ namespace MWRender else texture = new osg::Texture2D; } - setTextureSize(texture, width, height); + Stereo::setMultiviewCompatibleTextureSize(texture, width, height); texture->setSourceFormat(GL_RGBA); texture->setSourceType(GL_UNSIGNED_BYTE); texture->setInternalFormat(GL_RGBA); diff --git a/apps/openmw/mwrender/skyutil.cpp b/apps/openmw/mwrender/skyutil.cpp index 00bc2aef89..29a82f7eb0 100644 --- a/apps/openmw/mwrender/skyutil.cpp +++ b/apps/openmw/mwrender/skyutil.cpp @@ -615,9 +615,7 @@ namespace MWRender protected: void setDefaults(osg::StateSet* stateset) override { - if (Stereo::getMultiview()) - stateset->addUniform(new osg::Uniform(osg::Uniform::FLOAT_MAT4, "projectionMatrixMultiView", 2), osg::StateAttribute::OVERRIDE); - else + if (!Stereo::getMultiview()) stateset->addUniform(new osg::Uniform(osg::Uniform::FLOAT_MAT4, "projectionMatrix"), osg::StateAttribute::OVERRIDE); } @@ -626,7 +624,7 @@ namespace MWRender { if (Stereo::getMultiview()) { - auto* projectionMatrixMultiViewUniform = stateset->getUniform("projectionMatrixMultiView"); + std::array projectionMatrices; auto& sm = Stereo::Manager::instance(); for (int view : {0, 1}) @@ -636,8 +634,10 @@ namespace MWRender for (int col : {0, 1, 2}) viewOffsetMatrix(3, col) = 0; - projectionMatrixMultiViewUniform->setElement(view, viewOffsetMatrix * projectionMatrix); + projectionMatrices[view] = viewOffsetMatrix * projectionMatrix; } + + Stereo::setMultiviewMatrices(stateset, projectionMatrices); } } void applyLeft(osg::StateSet* stateset, osgUtil::CullVisitor* /*cv*/) override diff --git a/apps/openmw/mwrender/transparentpass.cpp b/apps/openmw/mwrender/transparentpass.cpp index 084eca1cdb..239a11821b 100644 --- a/apps/openmw/mwrender/transparentpass.cpp +++ b/apps/openmw/mwrender/transparentpass.cpp @@ -4,10 +4,6 @@ #include #include -#ifdef OSG_HAS_MULTIVIEW -#include -#endif - #include #include @@ -70,14 +66,11 @@ namespace MWRender if (Stereo::getMultiview()) { - if (!mMultiviewDepthResolveLeftSource[frameId]) - setupMultiviewDepthResolveBuffers(frameId); - mMultiviewDepthResolveLeftTarget[frameId]->apply(state, osg::FrameBufferObject::BindTarget::DRAW_FRAMEBUFFER); - mMultiviewDepthResolveLeftSource[frameId]->apply(state, osg::FrameBufferObject::BindTarget::READ_FRAMEBUFFER); - ext->glBlitFramebuffer(0, 0, tex->getTextureWidth(), tex->getTextureHeight(), 0, 0, tex->getTextureWidth(), tex->getTextureHeight(), GL_DEPTH_BUFFER_BIT, GL_NEAREST); - mMultiviewDepthResolveRightTarget[frameId]->apply(state, osg::FrameBufferObject::BindTarget::DRAW_FRAMEBUFFER); - mMultiviewDepthResolveRightSource[frameId]->apply(state, osg::FrameBufferObject::BindTarget::READ_FRAMEBUFFER); - ext->glBlitFramebuffer(0, 0, tex->getTextureWidth(), tex->getTextureHeight(), 0, 0, tex->getTextureWidth(), tex->getTextureHeight(), GL_DEPTH_BUFFER_BIT, GL_NEAREST); + if (!mMultiviewResolve[frameId]) + { + mMultiviewResolve[frameId] = std::make_unique(msaaFbo ? msaaFbo : fbo, opaqueFbo, GL_DEPTH_BUFFER_BIT); + } + mMultiviewResolve[frameId]->resolveImplementation(state); } else { @@ -107,42 +100,8 @@ namespace MWRender void TransparentDepthBinCallback::dirtyFrame(int frameId) { - mMultiviewDepthResolveLeftSource[frameId] = mMultiviewDepthResolveRightSource[frameId] = nullptr; - mMultiviewDepthResolveLeftTarget[frameId] = mMultiviewDepthResolveRightTarget[frameId] = nullptr; - } - - osg::FrameBufferAttachment makeSingleLayerAttachmentFromMultilayerAttachment(osg::FrameBufferAttachment attachment, int layer) - { - osg::Texture* tex = attachment.getTexture(); - - if (tex->getTextureTarget() == GL_TEXTURE_2D_ARRAY) - return osg::FrameBufferAttachment(static_cast(tex), layer, 0); - -#ifdef OSG_HAS_MULTIVIEW - if (tex->getTextureTarget() == GL_TEXTURE_2D_MULTISAMPLE_ARRAY) - return osg::FrameBufferAttachment(static_cast(tex), layer, 0); -#endif - - Log(Debug::Error) << "Attempted to extract a layer from an unlayered texture"; - - return osg::FrameBufferAttachment(); - } - - void TransparentDepthBinCallback::setupMultiviewDepthResolveBuffers(int frameId) - { - const osg::FrameBufferObject::BufferComponent component = osg::FrameBufferObject::BufferComponent::PACKED_DEPTH_STENCIL_BUFFER; - const auto& sourceFbo = mMsaaFbo[frameId] ? mMsaaFbo[frameId] : mFbo[frameId]; - const auto& sourceAttachment = sourceFbo->getAttachment(component); - mMultiviewDepthResolveLeftSource[frameId] = new osg::FrameBufferObject; - mMultiviewDepthResolveLeftSource[frameId]->setAttachment(component, makeSingleLayerAttachmentFromMultilayerAttachment(sourceAttachment, 0)); - mMultiviewDepthResolveRightSource[frameId] = new osg::FrameBufferObject; - mMultiviewDepthResolveRightSource[frameId]->setAttachment(component, makeSingleLayerAttachmentFromMultilayerAttachment(sourceAttachment, 1)); - const auto& targetFbo = mOpaqueFbo[frameId]; - const auto& targetAttachment = targetFbo->getAttachment(component); - mMultiviewDepthResolveLeftTarget[frameId] = new osg::FrameBufferObject; - mMultiviewDepthResolveLeftTarget[frameId]->setAttachment(component, makeSingleLayerAttachmentFromMultilayerAttachment(targetAttachment, 0)); - mMultiviewDepthResolveRightTarget[frameId] = new osg::FrameBufferObject; - mMultiviewDepthResolveRightTarget[frameId]->setAttachment(component, makeSingleLayerAttachmentFromMultilayerAttachment(targetAttachment, 1)); + if (mMultiviewResolve[frameId]) + mMultiviewResolve[frameId]->dirty(); } } diff --git a/apps/openmw/mwrender/transparentpass.hpp b/apps/openmw/mwrender/transparentpass.hpp index a9093acbf1..652fe8b8b7 100644 --- a/apps/openmw/mwrender/transparentpass.hpp +++ b/apps/openmw/mwrender/transparentpass.hpp @@ -15,6 +15,11 @@ namespace Shader class ShaderManager; } +namespace Stereo +{ + class MultiviewFramebufferResolve; +} + namespace MWRender { class TransparentDepthBinCallback : public osgUtil::RenderBin::DrawCallback @@ -24,16 +29,12 @@ namespace MWRender void drawImplementation(osgUtil::RenderBin* bin, osg::RenderInfo& renderInfo, osgUtil::RenderLeaf*& previous) override; void dirtyFrame(int frameId); - void setupMultiviewDepthResolveBuffers(int frameId); std::array, 2> mFbo; std::array, 2> mMsaaFbo; std::array, 2> mOpaqueFbo; - std::array, 2> mMultiviewDepthResolveLeftSource; - std::array, 2> mMultiviewDepthResolveRightSource; - std::array, 2> mMultiviewDepthResolveLeftTarget; - std::array, 2> mMultiviewDepthResolveRightTarget; + std::array, 2> mMultiviewResolve; private: osg::ref_ptr mStateSet; diff --git a/components/sceneutil/rtt.cpp b/components/sceneutil/rtt.cpp index 4ab1b4c92a..19daf2b560 100644 --- a/components/sceneutil/rtt.cpp +++ b/components/sceneutil/rtt.cpp @@ -200,21 +200,20 @@ namespace SceneUtil if (camera->getBufferAttachmentMap().count(osg::Camera::PACKED_DEPTH_STENCIL_BUFFER)) vdd->mDepthTexture = camera->getBufferAttachmentMap()[osg::Camera::PACKED_DEPTH_STENCIL_BUFFER]._texture; -#ifdef OSG_HAS_MULTIVIEW if (shouldDoTextureArray()) { // Create any buffer attachments not added in setDefaults if (camera->getBufferAttachmentMap().count(osg::Camera::COLOR_BUFFER) == 0) { vdd->mColorTexture = createTextureArray(mColorBufferInternalFormat); - camera->attach(osg::Camera::COLOR_BUFFER, vdd->mColorTexture, 0, osg::Camera::FACE_CONTROLLED_BY_MULTIVIEW_SHADER, mGenerateMipmaps, mSamples); - SceneUtil::attachAlphaToCoverageFriendlyFramebufferToCamera(camera, osg::Camera::COLOR_BUFFER, vdd->mColorTexture, 0, osg::Camera::FACE_CONTROLLED_BY_MULTIVIEW_SHADER, mGenerateMipmaps); + camera->attach(osg::Camera::COLOR_BUFFER, vdd->mColorTexture, 0, Stereo::osgFaceControlledByMultiviewShader(), mGenerateMipmaps, mSamples); + SceneUtil::attachAlphaToCoverageFriendlyFramebufferToCamera(camera, osg::Camera::COLOR_BUFFER, vdd->mColorTexture, 0, Stereo::osgFaceControlledByMultiviewShader(), mGenerateMipmaps); } if (camera->getBufferAttachmentMap().count(osg::Camera::PACKED_DEPTH_STENCIL_BUFFER) == 0) { vdd->mDepthTexture = createTextureArray(mDepthBufferInternalFormat); - camera->attach(osg::Camera::PACKED_DEPTH_STENCIL_BUFFER, vdd->mDepthTexture, 0, osg::Camera::FACE_CONTROLLED_BY_MULTIVIEW_SHADER, false, mSamples); + camera->attach(osg::Camera::PACKED_DEPTH_STENCIL_BUFFER, vdd->mDepthTexture, 0, Stereo::osgFaceControlledByMultiviewShader(), false, mSamples); } if (shouldDoTextureView()) @@ -226,7 +225,6 @@ namespace SceneUtil } } else -#endif { // Create any buffer attachments not added in setDefaults if (camera->getBufferAttachmentMap().count(osg::Camera::COLOR_BUFFER) == 0) diff --git a/components/stereo/frustum.cpp b/components/stereo/frustum.cpp index d9727e9ea6..40a355ed55 100644 --- a/components/stereo/frustum.cpp +++ b/components/stereo/frustum.cpp @@ -27,31 +27,32 @@ #include +#include + #include #include "frustum.hpp" namespace Stereo { -#ifdef OSG_HAS_MULTIVIEW - struct MultiviewFrustumCallback final : public osg::CullSettings::InitialFrustumCallback + struct MultiviewFrustumCallback final : public Stereo::InitialFrustumCallback { - MultiviewFrustumCallback(StereoFrustumManager* sfm) - : mSfm(sfm) + MultiviewFrustumCallback(StereoFrustumManager* sfm, osg::Camera* camera) + : Stereo::InitialFrustumCallback(camera) + , mSfm(sfm) { } - void setInitialFrustum(osg::CullStack& cullStack, osg::Polytope& frustum) const override + void setInitialFrustum(osg::CullStack& cullStack, osg::BoundingBoxd& bb, bool& nearCulling, bool& farCulling) const override { auto cm = cullStack.getCullingMode(); - bool nearCulling = !!(cm & osg::CullSettings::NEAR_PLANE_CULLING); - bool farCulling = !!(cm & osg::CullSettings::FAR_PLANE_CULLING); - frustum.setToBoundingBox(mSfm->boundingBox(), nearCulling, farCulling); + nearCulling = !!(cm & osg::CullSettings::NEAR_PLANE_CULLING); + farCulling = !!(cm & osg::CullSettings::FAR_PLANE_CULLING); + bb = mSfm->boundingBox(); } StereoFrustumManager* mSfm; }; -#endif struct ShadowFrustumCallback final : public SceneUtil::MWShadowTechnique::CustomFrustumCallback { @@ -95,14 +96,10 @@ namespace Stereo : mCamera(camera) , mShadowTechnique(nullptr) , mShadowFrustumCallback(nullptr) - , mMultiview(Stereo::getMultiview()) { - if (mMultiview) + if (Stereo::getMultiview()) { -#ifdef OSG_HAS_MULTIVIEW - mMultiviewFrustumCallback = new MultiviewFrustumCallback(this); - mCamera->setInitialFrustumCallback(mMultiviewFrustumCallback); -#endif + mMultiviewFrustumCallback = std::make_unique(this, camera); } if (Settings::Manager::getBool("shared shadow maps", "Stereo")) @@ -118,13 +115,6 @@ namespace Stereo StereoFrustumManager::~StereoFrustumManager() { - if (mMultiview) - { -#ifdef OSG_HAS_MULTIVIEW - mCamera->setInitialFrustumCallback(nullptr); -#endif - } - if (mShadowTechnique) mShadowTechnique->setCustomFrustumCallback(nullptr); } diff --git a/components/stereo/frustum.hpp b/components/stereo/frustum.hpp index 8d457c82b9..d6a624987d 100644 --- a/components/stereo/frustum.hpp +++ b/components/stereo/frustum.hpp @@ -38,10 +38,7 @@ namespace SceneUtil namespace Stereo { -#ifdef OSG_HAS_MULTIVIEW struct MultiviewFrustumCallback; -#endif - struct ShadowFrustumCallback; void joinBoundingBoxes(const osg::Matrix& masterProjection, const osg::Matrix& slaveProjection, osg::BoundingBoxd& bb); @@ -64,13 +61,10 @@ namespace Stereo osg::ref_ptr mCamera; osg::ref_ptr mShadowTechnique; osg::ref_ptr mShadowFrustumCallback; - bool mMultiview; std::map< osgUtil::CullVisitor*, osgUtil::CullVisitor*> mSharedFrustums; osg::BoundingBoxd mBoundingBox; -#ifdef OSG_HAS_MULTIVIEW - osg::ref_ptr mMultiviewFrustumCallback; -#endif + std::unique_ptr mMultiviewFrustumCallback; }; } diff --git a/components/stereo/multiview.cpp b/components/stereo/multiview.cpp index 0011b93687..e0cac44714 100644 --- a/components/stereo/multiview.cpp +++ b/components/stereo/multiview.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -337,6 +338,133 @@ namespace Stereo #endif } + void setMultiviewMatrices(osg::StateSet* stateset, const std::array& projection, bool createInverseMatrices) + { + auto* projUniform = stateset->getUniform("projectionMatrixMultiView"); + if (!projUniform) + { + projUniform = new osg::Uniform(osg::Uniform::FLOAT_MAT4, "projectionMatrixMultiView", 2); + stateset->addUniform(projUniform, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); + } + + projUniform->setElement(0, projection[0]); + projUniform->setElement(1, projection[1]); + + if (createInverseMatrices) + { + auto* invUniform = stateset->getUniform("invProjectionMatrixMultiView"); + if (!invUniform) + { + invUniform = new osg::Uniform(osg::Uniform::FLOAT_MAT4, "invProjectionMatrixMultiView", 2); + stateset->addUniform(invUniform, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); + } + + invUniform->setElement(0, osg::Matrix::inverse(projection[0])); + invUniform->setElement(1, osg::Matrix::inverse(projection[1])); + } + } + + void setMultiviewCompatibleTextureSize(osg::Texture* tex, int w, int h) + { + switch (tex->getTextureTarget()) + { + case GL_TEXTURE_2D: + static_cast(tex)->setTextureSize(w, h); + break; + case GL_TEXTURE_2D_ARRAY: + static_cast(tex)->setTextureSize(w, h, 2); + break; + case GL_TEXTURE_2D_MULTISAMPLE: + static_cast(tex)->setTextureSize(w, h); + break; +#ifdef OSG_HAS_MULTIVIEW + case GL_TEXTURE_2D_MULTISAMPLE_ARRAY: + static_cast(tex)->setTextureSize(w, h, 2); + break; +#endif + default: + throw std::logic_error("Invalid texture type received"); + } + } + + osg::ref_ptr createMultiviewCompatibleTexture(int width, int height, int samples) + { +#ifdef OSG_HAS_MULTIVIEW + if (Stereo::getMultiview()) + { + if (samples > 1) + { + auto tex = new osg::Texture2DMultisampleArray(); + tex->setTextureSize(width, height, 2); + tex->setNumSamples(samples); + return tex; + } + else + { + auto tex = new osg::Texture2DArray(); + tex->setTextureSize(width, height, 2); + return tex; + } + } + else +#endif + { + if (samples > 1) + { + auto tex = new osg::Texture2DMultisample(); + tex->setTextureSize(width, height); + tex->setNumSamples(samples); + return tex; + } + else + { + auto tex = new osg::Texture2D(); + tex->setTextureSize(width, height); + return tex; + } + } + } + + osg::FrameBufferAttachment createMultiviewCompatibleAttachment(osg::Texture* tex) + { + switch (tex->getTextureTarget()) + { + case GL_TEXTURE_2D: + { + auto* tex2d = static_cast(tex); + return osg::FrameBufferAttachment(tex2d); + } + case GL_TEXTURE_2D_MULTISAMPLE: + { + auto* tex2dMsaa = static_cast(tex); + return osg::FrameBufferAttachment(tex2dMsaa); + } +#ifdef OSG_HAS_MULTIVIEW + case GL_TEXTURE_2D_ARRAY: + { + auto* tex2dArray = static_cast(tex); + return osg::FrameBufferAttachment(tex2dArray, osg::Camera::FACE_CONTROLLED_BY_MULTIVIEW_SHADER, 0); + } + case GL_TEXTURE_2D_MULTISAMPLE_ARRAY: + { + auto* tex2dMsaaArray = static_cast(tex); + return osg::FrameBufferAttachment(tex2dMsaaArray, osg::Camera::FACE_CONTROLLED_BY_MULTIVIEW_SHADER, 0); + } +#endif + default: + throw std::logic_error("Invalid texture type received"); + } + } + + unsigned int osgFaceControlledByMultiviewShader() + { +#ifdef OSG_HAS_MULTIVIEW + return osg::Camera::FACE_CONTROLLED_BY_MULTIVIEW_SHADER; +#else + return 0; +#endif + } + class UpdateRenderStagesCallback : public SceneUtil::NodeCallback { public: @@ -558,4 +686,111 @@ namespace Stereo textureArray->setWrap(osg::Texture::WRAP_R, osg::Texture::CLAMP_TO_EDGE); return textureArray; } + + + osg::FrameBufferAttachment makeSingleLayerAttachmentFromMultilayerAttachment(osg::FrameBufferAttachment attachment, int layer) + { + osg::Texture* tex = attachment.getTexture(); + + if (tex->getTextureTarget() == GL_TEXTURE_2D_ARRAY) + return osg::FrameBufferAttachment(static_cast(tex), layer, 0); + +#ifdef OSG_HAS_MULTIVIEW + if (tex->getTextureTarget() == GL_TEXTURE_2D_MULTISAMPLE_ARRAY) + return osg::FrameBufferAttachment(static_cast(tex), layer, 0); +#endif + + Log(Debug::Error) << "Attempted to extract a layer from an unlayered texture"; + + return osg::FrameBufferAttachment(); + } + + MultiviewFramebufferResolve::MultiviewFramebufferResolve(osg::FrameBufferObject* msaaFbo, osg::FrameBufferObject* resolveFbo, GLbitfield blitMask) + : mResolveFbo(resolveFbo) + , mMsaaFbo(msaaFbo) + , mBlitMask(blitMask) + { + } + + void MultiviewFramebufferResolve::resolveImplementation(osg::State& state) + { + if (mDirtyLayers) + setupLayers(); + + osg::GLExtensions* ext = state.get(); + + for (int view : {0, 1}) + { + mResolveLayers[view]->apply(state, osg::FrameBufferObject::BindTarget::DRAW_FRAMEBUFFER); + mMsaaLayers[view]->apply(state, osg::FrameBufferObject::BindTarget::READ_FRAMEBUFFER); + ext->glBlitFramebuffer(0, 0, mWidth, mHeight, 0, 0, mWidth, mHeight, GL_DEPTH_BUFFER_BIT, GL_NEAREST); + } + } + void MultiviewFramebufferResolve::setupLayers() + { + mDirtyLayers = false; + std::vector components; + if (mBlitMask & GL_DEPTH_BUFFER_BIT) + components.push_back(osg::FrameBufferObject::BufferComponent::PACKED_DEPTH_STENCIL_BUFFER); + if (mBlitMask & GL_COLOR_BUFFER_BIT) + components.push_back(osg::FrameBufferObject::BufferComponent::COLOR_BUFFER); + + mMsaaLayers = { new osg::FrameBufferObject, new osg::FrameBufferObject }; + mResolveLayers = { new osg::FrameBufferObject, new osg::FrameBufferObject }; + for (auto component : components) + { + const auto& msaaAttachment = mMsaaFbo->getAttachment(component); + mMsaaLayers[0]->setAttachment(component, makeSingleLayerAttachmentFromMultilayerAttachment(msaaAttachment, 0)); + mMsaaLayers[1]->setAttachment(component, makeSingleLayerAttachmentFromMultilayerAttachment(msaaAttachment, 1)); + + const auto& resolveAttachment = mResolveFbo->getAttachment(component); + mResolveLayers[0]->setAttachment(component, makeSingleLayerAttachmentFromMultilayerAttachment(resolveAttachment, 0)); + mResolveLayers[1]->setAttachment(component, makeSingleLayerAttachmentFromMultilayerAttachment(resolveAttachment, 1)); + + mWidth = msaaAttachment.getTexture()->getTextureWidth(); + mHeight = msaaAttachment.getTexture()->getTextureHeight(); + } + } + +#ifdef OSG_HAS_MULTIVIEW + namespace + { + struct MultiviewFrustumCallback final : public osg::CullSettings::InitialFrustumCallback + { + MultiviewFrustumCallback(Stereo::InitialFrustumCallback* ifc) + : mIfc(ifc) + { + + } + + void setInitialFrustum(osg::CullStack& cullStack, osg::Polytope& frustum) const override + { + bool nearCulling = false; + bool farCulling = false; + osg::BoundingBoxd bb; + mIfc->setInitialFrustum(cullStack, bb, nearCulling, farCulling); + frustum.setToBoundingBox(bb, nearCulling, farCulling); + } + + Stereo::InitialFrustumCallback* mIfc; + }; + } +#endif + + InitialFrustumCallback::InitialFrustumCallback(osg::Camera* camera) + : mCamera(camera) + { +#ifdef OSG_HAS_MULTIVIEW + camera->setInitialFrustumCallback(new MultiviewFrustumCallback(this)); +#endif + } + + InitialFrustumCallback::~InitialFrustumCallback() + { +#ifdef OSG_HAS_MULTIVIEW + osg::ref_ptr camera; + if(mCamera.lock(camera)) + camera->setInitialFrustumCallback(nullptr); +#endif + } } diff --git a/components/stereo/multiview.hpp b/components/stereo/multiview.hpp index 3212ecc02c..418c69e159 100644 --- a/components/stereo/multiview.hpp +++ b/components/stereo/multiview.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -40,9 +41,6 @@ namespace Stereo //! Creates a Texture2D as a texture view into a Texture2DArray osg::ref_ptr createTextureView_Texture2DFromTexture2DArray(osg::Texture2DArray* textureArray, int layer); - //! Sets up a draw callback on the render stage that performs the MSAA resolve operation - void setMultiviewMSAAResolveCallback(osgUtil::RenderStage* renderStage); - //! Class that manages the specifics of GL_OVR_Multiview aware framebuffers, separating the layers into separate framebuffers, and disabling class MultiviewFramebuffer { @@ -86,6 +84,61 @@ namespace Stereo std::array, 2> mDepthTexture; }; + //! Sets up a draw callback on the render stage that performs the MSAA resolve operation + void setMultiviewMSAAResolveCallback(osgUtil::RenderStage* renderStage); + + //! Sets up or updates multiview matrices for the given stateset + void setMultiviewMatrices(osg::StateSet* stateset, const std::array& projection, bool createInverseMatrices = false); + + //! Sets the width/height of a texture by first down-casting it to the appropriate type. Sets depth to 2 always for Texture2DArray and Texture2DMultisampleArray. + void setMultiviewCompatibleTextureSize(osg::Texture* tex, int w, int h); + + //! Creates a texture (Texture2D, Texture2DMultisample, Texture2DArray, or Texture2DMultisampleArray) based on multiview settings and sample count. + osg::ref_ptr createMultiviewCompatibleTexture(int width, int height, int samples); + + //! Returns a framebuffer attachment from the texture, returning a multiview attachment if the texture is one of Texture2DArray or Texture2DMultisampleArray + osg::FrameBufferAttachment createMultiviewCompatibleAttachment(osg::Texture* tex); + + //! If OSG has multiview, returns the magic number used to tell OSG to create a multiview attachment. Otherwise returns 0. + unsigned int osgFaceControlledByMultiviewShader(); + + //! Implements resolving a multisamples multiview framebuffer. Does not automatically reflect changes to the fbo attachments, must call dirty() when the fbo attachments change. + class MultiviewFramebufferResolve + { + public: + MultiviewFramebufferResolve(osg::FrameBufferObject* msaaFbo, osg::FrameBufferObject* resolveFbo, GLbitfield blitMask); + + void resolveImplementation(osg::State& state); + + void dirty() { mDirtyLayers = true; } + + const osg::FrameBufferObject* resolveFbo() const { return mResolveFbo; }; + const osg::FrameBufferObject* msaaFbo() const { return mMsaaFbo; }; + + private: + void setupLayers(); + + osg::ref_ptr mResolveFbo; + std::array, 2> mResolveLayers{}; + osg::ref_ptr mMsaaFbo; + std::array, 2> mMsaaLayers{}; + + GLbitfield mBlitMask; + bool mDirtyLayers = true; + int mWidth = -1; + int mHeight = -1; + }; + + //! Wrapper for osg::CullSettings::InitialFrustumCallback, to avoid exposing osg multiview interfaces outside of multiview.cpp + struct InitialFrustumCallback + { + InitialFrustumCallback(osg::Camera* camera); + virtual ~InitialFrustumCallback(); + + virtual void setInitialFrustum(osg::CullStack& cullStack, osg::BoundingBoxd& bb, bool& nearCulling, bool& farCulling) const = 0; + + osg::observer_ptr mCamera; + }; } #endif diff --git a/components/stereo/stereomanager.cpp b/components/stereo/stereomanager.cpp index 7964c5c134..2c4b8a35e6 100644 --- a/components/stereo/stereomanager.cpp +++ b/components/stereo/stereomanager.cpp @@ -97,7 +97,6 @@ namespace Stereo protected: virtual void setDefaults(osg::StateSet* stateset) { - stateset->addUniform(new osg::Uniform(osg::Uniform::FLOAT_MAT4, "projectionMatrixMultiView", 2)); stateset->addUniform(new osg::Uniform(osg::Uniform::FLOAT_MAT4, "invProjectionMatrixMultiView", 2)); } @@ -195,8 +194,8 @@ namespace Stereo mEyeResolutionOverride = eyeResolution; mEyeResolutionOverriden = true; - if (mMultiviewFramebuffer) - updateStereoFramebuffer(); + //if (mMultiviewFramebuffer) + // updateStereoFramebuffer(); } void Manager::screenResolutionChanged() @@ -302,14 +301,14 @@ namespace Stereo void Manager::updateStereoFramebuffer() { //VR-TODO: in VR, still need to have this framebuffer attached before the postprocessor is created - auto samples = Settings::Manager::getInt("antialiasing", "Video"); - auto eyeRes = eyeResolution(); + //auto samples = Settings::Manager::getInt("antialiasing", "Video"); + //auto eyeRes = eyeResolution(); //if (mMultiviewFramebuffer) // mMultiviewFramebuffer->detachFrom(mMainCamera); - mMultiviewFramebuffer = std::make_shared(static_cast(eyeRes.x()), static_cast(eyeRes.y()), samples); - mMultiviewFramebuffer->attachColorComponent(SceneUtil::Color::colorSourceFormat(), SceneUtil::Color::colorSourceType(), SceneUtil::Color::colorInternalFormat()); - mMultiviewFramebuffer->attachDepthComponent(SceneUtil::AutoDepth::depthSourceFormat(), SceneUtil::AutoDepth::depthSourceType(), SceneUtil::AutoDepth::depthInternalFormat()); + //mMultiviewFramebuffer = std::make_shared(static_cast(eyeRes.x()), static_cast(eyeRes.y()), samples); + //mMultiviewFramebuffer->attachColorComponent(SceneUtil::Color::colorSourceFormat(), SceneUtil::Color::colorSourceType(), SceneUtil::Color::colorInternalFormat()); + //mMultiviewFramebuffer->attachDepthComponent(SceneUtil::AutoDepth::depthSourceFormat(), SceneUtil::AutoDepth::depthSourceType(), SceneUtil::AutoDepth::depthInternalFormat()); //mMultiviewFramebuffer->attachTo(mMainCamera); } @@ -376,17 +375,12 @@ namespace Stereo void Manager::updateMultiviewStateset(osg::StateSet* stateset) { - // Update stereo uniforms - auto * projectionMatrixMultiViewUniform = stateset->getUniform("projectionMatrixMultiView"); - auto * invProjectionMatrixMultiViewUniform = stateset->getUniform("invProjectionMatrixMultiView"); + std::array projectionMatrices; for (int view : {0, 1}) - { - auto projectionMatrix = computeEyeViewOffset(view) * computeEyeProjection(view, SceneUtil::AutoDepth::isReversed()); - auto invProjectionMatrix = osg::Matrix::inverse(projectionMatrix); - projectionMatrixMultiViewUniform->setElement(view, projectionMatrix); - invProjectionMatrixMultiViewUniform->setElement(view, invProjectionMatrix); - } + projectionMatrices[view] = computeEyeViewOffset(view) * computeEyeProjection(view, SceneUtil::AutoDepth::isReversed()); + + Stereo::setMultiviewMatrices(stateset, projectionMatrices, true); } void Manager::setUpdateViewCallback(std::shared_ptr cb)