# include "multiview.hpp"
# include <osg/FrameBufferObject>
# include <osg/GLExtensions>
# include <osg/Texture2D>
# include <osg/Texture2DMultisample>
# include <osg/Texture2DArray>
# include <osgUtil/RenderStage>
# include <osgUtil/CullVisitor>
# ifdef OSG_HAS_MULTIVIEW
# include <osg/Texture2DMultisampleArray>
# endif
# include <components/sceneutil/nodecallback.hpp>
# include <components/settings/settings.hpp>
# include <components/debug/debuglog.hpp>
# include <components/stereo/stereomanager.hpp>
# include <algorithm>
namespace Stereo
{
namespace
{
bool getMultiviewSupportedImpl ( unsigned int contextID )
{
# ifdef OSG_HAS_MULTIVIEW
if ( ! osg : : isGLExtensionSupported ( contextID , " GL_OVR_multiview " ) )
{
Log ( Debug : : Verbose ) < < " Disabling Multiview (opengl extension \" GL_OVR_multiview \" not supported) " ;
return false ;
}
if ( ! osg : : isGLExtensionSupported ( contextID , " GL_OVR_multiview2 " ) )
{
Log ( Debug : : Verbose ) < < " Disabling Multiview (opengl extension \" GL_OVR_multiview2 \" not supported) " ;
return false ;
}
return true ;
# else
Log ( Debug : : Verbose ) < < " Disabling Multiview (OSG does not support multiview) " ;
return false ;
# endif
}
bool getMultiviewSupported ( unsigned int contextID )
{
static bool supported = getMultiviewSupportedImpl ( contextID ) ;
return supported ;
}
bool getTextureViewSupportedImpl ( unsigned int contextID )
{
if ( ! osg : : isGLExtensionOrVersionSupported ( contextID , " ARB_texture_view " , 4.3 ) )
{
Log ( Debug : : Verbose ) < < " Disabling texture views (opengl extension \" ARB_texture_view \" not supported) " ;
return false ;
}
return true ;
}
bool getTextureViewSupported ( unsigned int contextID )
{
static bool supported = getTextureViewSupportedImpl ( contextID ) ;
return supported ;
}
bool getMultiviewImpl ( unsigned int contextID )
{
if ( ! Stereo : : getStereo ( ) )
{
Log ( Debug : : Verbose ) < < " Disabling Multiview (disabled by config) " ;
return false ;
}
if ( ! Settings : : Manager : : getBool ( " multiview " , " Stereo " ) )
{
Log ( Debug : : Verbose ) < < " Disabling Multiview (disabled by config) " ;
return false ;
}
if ( ! getMultiviewSupported ( contextID ) )
{
return false ;
}
if ( ! getTextureViewSupported ( contextID ) )
{
Log ( Debug : : Verbose ) < < " Disabling Multiview (texture views not supported) " ;
return false ;
}
Log ( Debug : : Verbose ) < < " Enabling Multiview " ;
return true ;
}
bool getMultiview ( unsigned int contextID )
{
static bool multiView = getMultiviewImpl ( contextID ) ;
return multiView ;
}
}
bool getTextureViewSupported ( )
{
return getTextureViewSupported ( 0 ) ;
}
bool getMultiview ( )
{
return getMultiview ( 0 ) ;
}
void configureExtensions ( unsigned int contextID )
{
getTextureViewSupported ( contextID ) ;
getMultiviewSupported ( contextID ) ;
getMultiview ( contextID ) ;
}
void setVertexBufferHint ( )
{
if ( getStereo ( ) & & Settings : : Manager : : getBool ( " multiview " , " Stereo " ) )
{
auto * ds = osg : : DisplaySettings : : instance ( ) . get ( ) ;
if ( ! Settings : : Manager : : getBool ( " allow display lists for multiview " , " Stereo " )
& & ds - > getVertexBufferHint ( ) = = osg : : DisplaySettings : : VertexBufferHint : : NO_PREFERENCE )
{
// Note that this only works if this code is executed before realize() is called on the viewer.
// The hint is read by the state object only once, before the user realize operations are run.
// Therefore we have to set this hint without access to a graphics context to let us determine
// if multiview will actually be supported or not. So if the user has requested multiview, we
// will just have to set it regardless.
ds - > setVertexBufferHint ( osg : : DisplaySettings : : VertexBufferHint : : VERTEX_BUFFER_OBJECT ) ;
Log ( Debug : : Verbose ) < < " Disabling display lists " ;
}
}
}
class Texture2DViewSubloadCallback : public osg : : Texture2D : : SubloadCallback
{
public :
Texture2DViewSubloadCallback ( osg : : Texture2DArray * textureArray , int layer ) ;
void load ( const osg : : Texture2D & texture , osg : : State & state ) const override ;
void subload ( const osg : : Texture2D & texture , osg : : State & state ) const override ;
private :
osg : : ref_ptr < osg : : Texture2DArray > mTextureArray ;
int mLayer ;
} ;
Texture2DViewSubloadCallback : : Texture2DViewSubloadCallback ( osg : : Texture2DArray * textureArray , int layer )
: mTextureArray ( textureArray )
, mLayer ( layer )
{
}
void Texture2DViewSubloadCallback : : load ( const osg : : Texture2D & texture , osg : : State & state ) const
{
state . checkGLErrors ( " before Texture2DViewSubloadCallback::load() " ) ;
auto contextId = state . getContextID ( ) ;
auto * gl = osg : : GLExtensions : : Get ( contextId , false ) ;
mTextureArray - > apply ( state ) ;
auto sourceTextureObject = mTextureArray - > getTextureObject ( contextId ) ;
if ( ! sourceTextureObject )
{
Log ( Debug : : Error ) < < " Texture2DViewSubloadCallback: Texture2DArray did not have a texture object " ;
return ;
}
osg : : Texture : : TextureObject * const targetTextureObject = texture . getTextureObject ( contextId ) ;
if ( targetTextureObject = = nullptr )
{
Log ( Debug : : Error ) < < " Texture2DViewSubloadCallback: Texture2D did not have a texture object " ;
return ;
}
// OSG already bound this texture ID, giving it a target.
// Delete it and make a new texture ID.
glBindTexture ( GL_TEXTURE_2D , 0 ) ;
glDeleteTextures ( 1 , & targetTextureObject - > _id ) ;
glGenTextures ( 1 , & targetTextureObject - > _id ) ;
auto sourceId = sourceTextureObject - > _id ;
auto targetId = targetTextureObject - > _id ;
auto internalFormat = sourceTextureObject - > _profile . _internalFormat ;
auto levels = std : : max ( 1 , sourceTextureObject - > _profile . _numMipmapLevels ) ;
{
////// OSG BUG
// Texture views require immutable storage.
// OSG should always give immutable storage to sized internal formats, but does not do so for depth formats.
// Fortunately, we can just call glTexStorage3D here to make it immutable. This probably discards depth info for that frame, but whatever.
# ifndef GL_TEXTURE_IMMUTABLE_FORMAT
# define GL_TEXTURE_IMMUTABLE_FORMAT 0x912F
# endif
// Store any current binding and re-apply it after so i don't mess with state.
GLint oldBinding = 0 ;
glGetIntegerv ( GL_TEXTURE_BINDING_2D_ARRAY , & oldBinding ) ;
// Bind the source texture and check if it's immutable.
glBindTexture ( GL_TEXTURE_2D_ARRAY , sourceId ) ;
GLint immutable = 0 ;
glGetTexParameteriv ( GL_TEXTURE_2D_ARRAY , GL_TEXTURE_IMMUTABLE_FORMAT , & immutable ) ;
if ( ! immutable )
{
// It wasn't immutable, so make it immutable.
gl - > glTexStorage3D ( GL_TEXTURE_2D_ARRAY , 1 , internalFormat , sourceTextureObject - > _profile . _width , sourceTextureObject - > _profile . _height , 2 ) ;
state . checkGLErrors ( " after Texture2DViewSubloadCallback::load()::glTexStorage3D " ) ;
}
glBindTexture ( GL_TEXTURE_2D_ARRAY , oldBinding ) ;
}
gl - > glTextureView ( targetId , GL_TEXTURE_2D , sourceId , internalFormat , 0 , levels , mLayer , 1 ) ;
state . checkGLErrors ( " after Texture2DViewSubloadCallback::load()::glTextureView " ) ;
glBindTexture ( GL_TEXTURE_2D , targetId ) ;
}
void Texture2DViewSubloadCallback : : subload ( const osg : : Texture2D & texture , osg : : State & state ) const
{
// Nothing to do
}
osg : : ref_ptr < osg : : Texture2D > createTextureView_Texture2DFromTexture2DArray ( osg : : Texture2DArray * textureArray , int layer )
{
if ( ! getTextureViewSupported ( ) )
{
Log ( Debug : : Error ) < < " createTextureView_Texture2DFromTexture2DArray: Tried to use a texture view but glTextureView is not supported " ;
return nullptr ;
}
osg : : ref_ptr < osg : : Texture2D > texture2d = new osg : : Texture2D ;
texture2d - > setSubloadCallback ( new Texture2DViewSubloadCallback ( textureArray , layer ) ) ;
texture2d - > setTextureSize ( textureArray - > getTextureWidth ( ) , textureArray - > getTextureHeight ( ) ) ;
texture2d - > setBorderColor ( textureArray - > getBorderColor ( ) ) ;
texture2d - > setBorderWidth ( textureArray - > getBorderWidth ( ) ) ;
texture2d - > setLODBias ( textureArray - > getLODBias ( ) ) ;
texture2d - > setFilter ( osg : : Texture : : FilterParameter : : MAG_FILTER , textureArray - > getFilter ( osg : : Texture : : FilterParameter : : MAG_FILTER ) ) ;
texture2d - > setFilter ( osg : : Texture : : FilterParameter : : MIN_FILTER , textureArray - > getFilter ( osg : : Texture : : FilterParameter : : MIN_FILTER ) ) ;
texture2d - > setInternalFormat ( textureArray - > getInternalFormat ( ) ) ;
texture2d - > setNumMipmapLevels ( textureArray - > getNumMipmapLevels ( ) ) ;
return texture2d ;
}
# ifdef OSG_HAS_MULTIVIEW
//! Draw callback that, if set on a RenderStage, resolves MSAA after draw. Needed when using custom fbo/resolve fbos on renderstages in combination with multiview.
struct MultiviewMSAAResolveCallback : public osgUtil : : RenderBin : : DrawCallback
{
void drawImplementation ( osgUtil : : RenderBin * bin , osg : : RenderInfo & renderInfo , osgUtil : : RenderLeaf * & previous ) override
{
osgUtil : : RenderStage * stage = static_cast < osgUtil : : RenderStage * > ( bin ) ;
auto msaaFbo = stage - > getFrameBufferObject ( ) ;
auto resolveFbo = stage - > getMultisampleResolveFramebufferObject ( ) ;
if ( msaaFbo ! = mMsaaFbo )
{
mMsaaFbo = msaaFbo ;
setupMsaaLayers ( ) ;
}
if ( resolveFbo ! = mFbo )
{
mFbo = resolveFbo ;
setupLayers ( ) ;
}
// Null the resolve framebuffer to keep osg from doing redundant work.
stage - > setMultisampleResolveFramebufferObject ( nullptr ) ;
// Do the actual render work
bin - > drawImplementation ( renderInfo , previous ) ;
// Blit layers
osg : : State & state = * renderInfo . getState ( ) ;
osg : : GLExtensions * ext = state . get < osg : : GLExtensions > ( ) ;
for ( int i = 0 ; i < 2 ; i + + )
{
mLayers [ i ] - > apply ( state , osg : : FrameBufferObject : : DRAW_FRAMEBUFFER ) ;
mMsaaLayers [ i ] - > apply ( state , osg : : FrameBufferObject : : READ_FRAMEBUFFER ) ;
ext - > glBlitFramebuffer ( 0 , 0 , mWidth , mHeight , 0 , 0 , mWidth , mHeight , GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT , GL_NEAREST ) ;
}
msaaFbo - > apply ( state , osg : : FrameBufferObject : : READ_DRAW_FRAMEBUFFER ) ;
}
void setupLayers ( )
{
const auto & attachments = mFbo - > getAttachmentMap ( ) ;
for ( int i = 0 ; i < 2 ; i + + )
{
mLayers [ i ] = new osg : : FrameBufferObject ;
// Intentionally not using ref& so attachment can be non-const
for ( auto [ component , attachment ] : attachments )
{
osg : : Texture2DArray * texture = static_cast < osg : : Texture2DArray * > ( attachment . getTexture ( ) ) ;
mLayers [ i ] - > setAttachment ( component , osg : : FrameBufferAttachment ( texture , i ) ) ;
mWidth = texture - > getTextureWidth ( ) ;
mHeight = texture - > getTextureHeight ( ) ;
}
}
}
void setupMsaaLayers ( )
{
const auto & attachments = mMsaaFbo - > getAttachmentMap ( ) ;
for ( int i = 0 ; i < 2 ; i + + )
{
mMsaaLayers [ i ] = new osg : : FrameBufferObject ;
// Intentionally not using ref& so attachment can be non-const
for ( auto [ component , attachment ] : attachments )
{
osg : : Texture2DMultisampleArray * texture = static_cast < osg : : Texture2DMultisampleArray * > ( attachment . getTexture ( ) ) ;
mMsaaLayers [ i ] - > setAttachment ( component , osg : : FrameBufferAttachment ( texture , i ) ) ;
mWidth = texture - > getTextureWidth ( ) ;
mHeight = texture - > getTextureHeight ( ) ;
}
}
}
osg : : ref_ptr < osg : : FrameBufferObject > mFbo ;
osg : : ref_ptr < osg : : FrameBufferObject > mMsaaFbo ;
osg : : ref_ptr < osg : : FrameBufferObject > mLayers [ 2 ] ;
osg : : ref_ptr < osg : : FrameBufferObject > mMsaaLayers [ 2 ] ;
int mWidth ;
int mHeight ;
} ;
# endif
void setMultiviewMSAAResolveCallback ( osgUtil : : RenderStage * renderStage )
{
# ifdef OSG_HAS_MULTIVIEW
if ( Stereo : : getMultiview ( ) )
{
renderStage - > setDrawCallback ( new MultiviewMSAAResolveCallback ) ;
}
# endif
}
void setMultiviewMatrices ( osg : : StateSet * stateset , const std : : array < osg : : Matrix , 2 > & 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 < osg : : Texture2D * > ( tex ) - > setTextureSize ( w , h ) ;
break ;
case GL_TEXTURE_2D_ARRAY :
static_cast < osg : : Texture2DArray * > ( tex ) - > setTextureSize ( w , h , 2 ) ;
break ;
case GL_TEXTURE_2D_MULTISAMPLE :
static_cast < osg : : Texture2DMultisample * > ( tex ) - > setTextureSize ( w , h ) ;
break ;
# ifdef OSG_HAS_MULTIVIEW
case GL_TEXTURE_2D_MULTISAMPLE_ARRAY :
static_cast < osg : : Texture2DMultisampleArray * > ( tex ) - > setTextureSize ( w , h , 2 ) ;
break ;
# endif
default :
throw std : : logic_error ( " Invalid texture type received " ) ;
}
}
osg : : ref_ptr < osg : : Texture > 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 < osg : : Texture2D * > ( tex ) ;
return osg : : FrameBufferAttachment ( tex2d ) ;
}
case GL_TEXTURE_2D_MULTISAMPLE :
{
auto * tex2dMsaa = static_cast < osg : : Texture2DMultisample * > ( tex ) ;
return osg : : FrameBufferAttachment ( tex2dMsaa ) ;
}
# ifdef OSG_HAS_MULTIVIEW
case GL_TEXTURE_2D_ARRAY :
{
auto * tex2dArray = static_cast < osg : : Texture2DArray * > ( tex ) ;
return osg : : FrameBufferAttachment ( tex2dArray , osg : : Camera : : FACE_CONTROLLED_BY_MULTIVIEW_SHADER , 0 ) ;
}
case GL_TEXTURE_2D_MULTISAMPLE_ARRAY :
{
auto * tex2dMsaaArray = static_cast < osg : : Texture2DMultisampleArray * > ( 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 < UpdateRenderStagesCallback , osg : : Node * , osgUtil : : CullVisitor * >
{
public :
UpdateRenderStagesCallback ( Stereo : : MultiviewFramebuffer * multiviewFramebuffer )
: mMultiviewFramebuffer ( multiviewFramebuffer )
{
mViewport = new osg : : Viewport ( 0 , 0 , multiviewFramebuffer - > width ( ) , multiviewFramebuffer - > height ( ) ) ;
mViewportStateset = new osg : : StateSet ( ) ;
mViewportStateset - > setAttribute ( mViewport . get ( ) ) ;
}
void operator ( ) ( osg : : Node * node , osgUtil : : CullVisitor * cv )
{
osgUtil : : RenderStage * renderStage = cv - > getCurrentRenderStage ( ) ;
bool msaa = mMultiviewFramebuffer - > samples ( ) > 1 ;
if ( ! Stereo : : getMultiview ( ) )
{
auto eye = static_cast < int > ( Stereo : : Manager : : instance ( ) . getEye ( cv ) ) ;
if ( msaa )
{
renderStage - > setFrameBufferObject ( mMultiviewFramebuffer - > layerMsaaFbo ( eye ) ) ;
renderStage - > setMultisampleResolveFramebufferObject ( mMultiviewFramebuffer - > layerFbo ( eye ) ) ;
}
else
{
renderStage - > setFrameBufferObject ( mMultiviewFramebuffer - > layerFbo ( eye ) ) ;
}
}
// OSG tries to do a horizontal split, but we want to render to separate framebuffers instead.
renderStage - > setViewport ( mViewport ) ;
cv - > pushStateSet ( mViewportStateset . get ( ) ) ;
traverse ( node , cv ) ;
cv - > popStateSet ( ) ;
}
private :
Stereo : : MultiviewFramebuffer * mMultiviewFramebuffer ;
osg : : ref_ptr < osg : : Viewport > mViewport ;
osg : : ref_ptr < osg : : StateSet > mViewportStateset ;
} ;
MultiviewFramebuffer : : MultiviewFramebuffer ( int width , int height , int samples )
: mWidth ( width )
, mHeight ( height )
, mSamples ( samples )
, mMultiview ( getMultiview ( ) )
, mMultiviewFbo { new osg : : FrameBufferObject }
, mLayerFbo { new osg : : FrameBufferObject , new osg : : FrameBufferObject }
, mLayerMsaaFbo { new osg : : FrameBufferObject , new osg : : FrameBufferObject }
{
}
MultiviewFramebuffer : : ~ MultiviewFramebuffer ( )
{
}
void MultiviewFramebuffer : : attachColorComponent ( GLint sourceFormat , GLint sourceType , GLint internalFormat )
{
if ( mMultiview )
{
# ifdef OSG_HAS_MULTIVIEW
mMultiviewColorTexture = createTextureArray ( sourceFormat , sourceType , internalFormat ) ;
mMultiviewFbo - > setAttachment ( osg : : Camera : : COLOR_BUFFER , osg : : FrameBufferAttachment ( mMultiviewColorTexture , osg : : Camera : : FACE_CONTROLLED_BY_MULTIVIEW_SHADER , 0 ) ) ;
for ( unsigned i = 0 ; i < 2 ; i + + )
{
mColorTexture [ i ] = createTextureView_Texture2DFromTexture2DArray ( mMultiviewColorTexture . get ( ) , i ) ;
mLayerFbo [ i ] - > setAttachment ( osg : : Camera : : COLOR_BUFFER , osg : : FrameBufferAttachment ( mColorTexture [ i ] ) ) ;
}
# endif
}
else
{
for ( unsigned i = 0 ; i < 2 ; i + + )
{
if ( mSamples > 1 )
mLayerMsaaFbo [ i ] - > setAttachment ( osg : : Camera : : COLOR_BUFFER , osg : : FrameBufferAttachment ( new osg : : RenderBuffer ( mWidth , mHeight , internalFormat , mSamples ) ) ) ;
mColorTexture [ i ] = createTexture ( sourceFormat , sourceType , internalFormat ) ;
mLayerFbo [ i ] - > setAttachment ( osg : : Camera : : COLOR_BUFFER , osg : : FrameBufferAttachment ( mColorTexture [ i ] ) ) ;
}
}
}
void MultiviewFramebuffer : : attachDepthComponent ( GLint sourceFormat , GLint sourceType , GLint internalFormat )
{
if ( mMultiview )
{
# ifdef OSG_HAS_MULTIVIEW
mMultiviewDepthTexture = createTextureArray ( sourceFormat , sourceType , internalFormat ) ;
mMultiviewFbo - > setAttachment ( osg : : Camera : : PACKED_DEPTH_STENCIL_BUFFER , osg : : FrameBufferAttachment ( mMultiviewDepthTexture , osg : : Camera : : FACE_CONTROLLED_BY_MULTIVIEW_SHADER , 0 ) ) ;
for ( unsigned i = 0 ; i < 2 ; i + + )
{
mDepthTexture [ i ] = createTextureView_Texture2DFromTexture2DArray ( mMultiviewDepthTexture . get ( ) , i ) ;
mLayerFbo [ i ] - > setAttachment ( osg : : Camera : : PACKED_DEPTH_STENCIL_BUFFER , osg : : FrameBufferAttachment ( mDepthTexture [ i ] ) ) ;
}
# endif
}
else
{
for ( unsigned i = 0 ; i < 2 ; i + + )
{
if ( mSamples > 1 )
mLayerMsaaFbo [ i ] - > setAttachment ( osg : : Camera : : PACKED_DEPTH_STENCIL_BUFFER , osg : : FrameBufferAttachment ( new osg : : RenderBuffer ( mWidth , mHeight , internalFormat , mSamples ) ) ) ;
mDepthTexture [ i ] = createTexture ( sourceFormat , sourceType , internalFormat ) ;
mLayerFbo [ i ] - > setAttachment ( osg : : Camera : : PACKED_DEPTH_STENCIL_BUFFER , osg : : FrameBufferAttachment ( mDepthTexture [ i ] ) ) ;
}
}
}
osg : : FrameBufferObject * MultiviewFramebuffer : : multiviewFbo ( )
{
return mMultiviewFbo ;
}
osg : : FrameBufferObject * MultiviewFramebuffer : : layerFbo ( int i )
{
return mLayerFbo [ i ] ;
}
osg : : FrameBufferObject * MultiviewFramebuffer : : layerMsaaFbo ( int i )
{
return mLayerMsaaFbo [ i ] ;
}
osg : : Texture2DArray * MultiviewFramebuffer : : multiviewColorBuffer ( )
{
return mMultiviewColorTexture ;
}
osg : : Texture2DArray * MultiviewFramebuffer : : multiviewDepthBuffer ( )
{
return mMultiviewDepthTexture ;
}
osg : : Texture2D * MultiviewFramebuffer : : layerColorBuffer ( int i )
{
return mColorTexture [ i ] ;
}
osg : : Texture2D * MultiviewFramebuffer : : layerDepthBuffer ( int i )
{
return mDepthTexture [ i ] ;
}
void MultiviewFramebuffer : : attachTo ( osg : : Camera * camera )
{
# ifdef OSG_HAS_MULTIVIEW
if ( mMultiview )
{
if ( mMultiviewColorTexture )
{
camera - > attach ( osg : : Camera : : COLOR_BUFFER , mMultiviewColorTexture , 0 , osg : : Camera : : FACE_CONTROLLED_BY_MULTIVIEW_SHADER , false , mSamples ) ;
camera - > getBufferAttachmentMap ( ) [ osg : : Camera : : COLOR_BUFFER ] . _internalFormat = mMultiviewColorTexture - > getInternalFormat ( ) ;
camera - > getBufferAttachmentMap ( ) [ osg : : Camera : : COLOR_BUFFER ] . _mipMapGeneration = false ;
}
if ( mMultiviewDepthTexture )
{
camera - > attach ( osg : : Camera : : PACKED_DEPTH_STENCIL_BUFFER , mMultiviewDepthTexture , 0 , osg : : Camera : : FACE_CONTROLLED_BY_MULTIVIEW_SHADER , false , mSamples ) ;
camera - > getBufferAttachmentMap ( ) [ osg : : Camera : : PACKED_DEPTH_STENCIL_BUFFER ] . _internalFormat = mMultiviewDepthTexture - > getInternalFormat ( ) ;
camera - > getBufferAttachmentMap ( ) [ osg : : Camera : : PACKED_DEPTH_STENCIL_BUFFER ] . _mipMapGeneration = false ;
}
}
# endif
camera - > setRenderTargetImplementation ( osg : : Camera : : FRAME_BUFFER_OBJECT ) ;
if ( ! mCullCallback )
mCullCallback = new UpdateRenderStagesCallback ( this ) ;
camera - > addCullCallback ( mCullCallback ) ;
}
void MultiviewFramebuffer : : detachFrom ( osg : : Camera * camera )
{
# ifdef OSG_HAS_MULTIVIEW
if ( mMultiview )
{
if ( mMultiviewColorTexture )
{
camera - > detach ( osg : : Camera : : COLOR_BUFFER ) ;
}
if ( mMultiviewDepthTexture )
{
camera - > detach ( osg : : Camera : : PACKED_DEPTH_STENCIL_BUFFER ) ;
}
}
# endif
camera - > setRenderTargetImplementation ( osg : : Camera : : FRAME_BUFFER ) ;
if ( mCullCallback )
camera - > removeCullCallback ( mCullCallback ) ;
}
osg : : Texture2D * MultiviewFramebuffer : : createTexture ( GLint sourceFormat , GLint sourceType , GLint internalFormat )
{
osg : : Texture2D * texture = new osg : : Texture2D ;
texture - > setTextureSize ( mWidth , mHeight ) ;
texture - > setSourceFormat ( sourceFormat ) ;
texture - > setSourceType ( sourceType ) ;
texture - > setInternalFormat ( internalFormat ) ;
texture - > setFilter ( osg : : Texture : : MIN_FILTER , osg : : Texture : : LINEAR ) ;
texture - > setFilter ( osg : : Texture : : 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 - > setWrap ( osg : : Texture : : WRAP_R , osg : : Texture : : CLAMP_TO_EDGE ) ;
return texture ;
}
osg : : Texture2DArray * MultiviewFramebuffer : : createTextureArray ( GLint sourceFormat , GLint sourceType , GLint internalFormat )
{
osg : : Texture2DArray * textureArray = new osg : : Texture2DArray ;
textureArray - > setTextureSize ( mWidth , mHeight , 2 ) ;
textureArray - > setSourceFormat ( sourceFormat ) ;
textureArray - > setSourceType ( sourceType ) ;
textureArray - > setInternalFormat ( internalFormat ) ;
textureArray - > setFilter ( osg : : Texture : : MIN_FILTER , osg : : Texture : : LINEAR ) ;
textureArray - > setFilter ( osg : : Texture : : MAG_FILTER , osg : : Texture : : LINEAR ) ;
textureArray - > setWrap ( osg : : Texture : : WRAP_S , osg : : Texture : : CLAMP_TO_EDGE ) ;
textureArray - > setWrap ( osg : : Texture : : WRAP_T , osg : : Texture : : CLAMP_TO_EDGE ) ;
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 < osg : : Texture2DArray * > ( tex ) , layer , 0 ) ;
# ifdef OSG_HAS_MULTIVIEW
if ( tex - > getTextureTarget ( ) = = GL_TEXTURE_2D_MULTISAMPLE_ARRAY )
return osg : : FrameBufferAttachment ( static_cast < osg : : Texture2DMultisampleArray * > ( 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 < osg : : GLExtensions > ( ) ;
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 < osg : : FrameBufferObject : : BufferComponent > 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 < osg : : Camera > camera ;
if ( mCamera . lock ( camera ) )
camera - > setInitialFrustumCallback ( nullptr ) ;
# endif
}
}