diff --git a/include/osg/Camera b/include/osg/Camera index 38a2747db..b8ca447f3 100644 --- a/include/osg/Camera +++ b/include/osg/Camera @@ -332,14 +332,18 @@ class OSG_EXPORT Camera : public Transform, public CullSettings /** Attach a buffer with specified OpenGL internal format.*/ void attach(BufferComponent buffer, GLenum internalFormat); - /** Attach a Texture to specified buffer component. - * The level parameter controls the mip map level of the texture that is attached. - * The face parameter controls the face of texture cube map or z level of 3d texture. + /** Attach a Texture to specified buffer component. + * The level parameter controls the mip map level of the texture that is attached. + * The face parameter controls the face of texture cube map or z level of 3d texture. * The mipMapGeneration flag controls whether mipmap generation should be done for texture.*/ - void attach(BufferComponent buffer, osg::Texture* texture, unsigned int level = 0, unsigned int face=0, bool mipMapGeneration=false); + void attach(BufferComponent buffer, osg::Texture* texture, unsigned int level = 0, unsigned int face=0, bool mipMapGeneration=false, + unsigned int multisampleSamples = 0, + unsigned int multisampleColorSamples = 0); /** Attach a Image to specified buffer component.*/ - void attach(BufferComponent buffer, osg::Image* image); + void attach(BufferComponent buffer, osg::Image* image, + unsigned int multisampleSamples = 0, + unsigned int multisampleColorSamples = 0); /** Detach specified buffer component.*/ void detach(BufferComponent buffer); @@ -350,7 +354,9 @@ class OSG_EXPORT Camera : public Transform, public CullSettings _internalFormat(GL_NONE), _level(0), _face(0), - _mipMapGeneration(false) {} + _mipMapGeneration(false), + _multisampleSamples(0), + _multisampleColorSamples(0) {} int width() const { @@ -379,6 +385,8 @@ class OSG_EXPORT Camera : public Transform, public CullSettings unsigned int _level; unsigned int _face; bool _mipMapGeneration; + unsigned int _multisampleSamples; + unsigned int _multisampleColorSamples; }; typedef std::map< BufferComponent, Attachment> BufferAttachmentMap; @@ -388,8 +396,8 @@ class OSG_EXPORT Camera : public Transform, public CullSettings /** Get the const BufferAttachmentMap, used to configure frame buffer objects, pbuffers and texture reads.*/ const BufferAttachmentMap& getBufferAttachmentMap() const { return _bufferAttachmentMap; } - - + + /** Create a operation thread for this camera.*/ void createCameraThread(); @@ -442,11 +450,11 @@ class OSG_EXPORT Camera : public Transform, public CullSettings DrawCallback(const DrawCallback&,const CopyOp&) {} META_Object(osg,DrawCallback) - - /** Functor method called by rendering thread. Users will typically override this method to carry tasks such as screen capture.*/ + + /** Functor method called by rendering thread. Users will typically override this method to carry tasks such as screen capture.*/ virtual void operator () (osg::RenderInfo& renderInfo) const; - /** Functor method, provided for backwards compatibility, called by operator() (osg::RenderInfo& renderInfo) method.*/ + /** Functor method, provided for backwards compatibility, called by operator() (osg::RenderInfo& renderInfo) method.*/ virtual void operator () (const osg::Camera& /*camera*/) const {} }; diff --git a/include/osg/FrameBufferObject b/include/osg/FrameBufferObject index 7b681de01..4fb3c3074 100644 --- a/include/osg/FrameBufferObject +++ b/include/osg/FrameBufferObject @@ -79,6 +79,14 @@ #define GL_INVALID_FRAMEBUFFER_OPERATION_EXT 0x0506 #endif +#ifndef GL_EXT_framebuffer_blit +#define GL_EXT_framebuffer_blit 1 +#define GL_DRAW_FRAMEBUFFER_BINDING_EXT 0x8CA6 +#define GL_READ_FRAMEBUFFER_EXT 0x8CA8 +#define GL_DRAW_FRAMEBUFFER_EXT 0x8CA9 +#define GL_READ_FRAMEBUFFER_BINDING_EXT 0x8CAA +#endif + #ifndef GL_VERSION_1_4 #define GL_DEPTH_COMPONENT16 0x81A5 #define GL_DEPTH_COMPONENT24 0x81A6 @@ -99,6 +107,8 @@ namespace osg typedef void APIENTRY TglDeleteRenderbuffersEXT(GLsizei n, const GLuint *renderbuffers); typedef void APIENTRY TglGenRenderbuffersEXT(GLsizei, GLuint *); typedef void APIENTRY TglRenderbufferStorageEXT(GLenum, GLenum, GLsizei, GLsizei); + typedef void APIENTRY TglRenderbufferStorageMultisampleEXT(GLenum, GLsizei, GLenum, GLsizei, GLsizei); + typedef void APIENTRY TglRenderbufferStorageMultisampleCoverageNV(GLenum, GLsizei, GLsizei, GLenum, GLsizei, GLsizei); typedef void APIENTRY TglBindFramebufferEXT(GLenum, GLuint); typedef void APIENTRY TglDeleteFramebuffersEXT(GLsizei n, const GLuint *framebuffers); typedef void APIENTRY TglGenFramebuffersEXT(GLsizei, GLuint *); @@ -109,12 +119,13 @@ namespace osg typedef void APIENTRY TglFramebufferTextureLayerEXT(GLenum, GLenum, GLuint, GLint, GLint); typedef void APIENTRY TglFramebufferRenderbufferEXT(GLenum, GLenum, GLenum, GLuint); typedef void APIENTRY TglGenerateMipmapEXT(GLenum); - typedef void APIENTRY TglRenderbufferStorageMultisampleCoverageNV(GLenum, GLuint, GLuint, GLenum, GLuint, GLuint); - + typedef void APIENTRY TglBlitFramebufferEXT(GLint, GLint, GLint, GLint, GLint, GLint, GLint, GLint, GLbitfield, GLenum); + TglBindRenderbufferEXT* glBindRenderbufferEXT; TglGenRenderbuffersEXT* glGenRenderbuffersEXT; TglDeleteRenderbuffersEXT* glDeleteRenderbuffersEXT; TglRenderbufferStorageEXT* glRenderbufferStorageEXT; + TglRenderbufferStorageMultisampleEXT* glRenderbufferStorageMultisampleEXT; TglRenderbufferStorageMultisampleCoverageNV* glRenderbufferStorageMultisampleCoverageNV; TglBindFramebufferEXT* glBindFramebufferEXT; TglDeleteFramebuffersEXT* glDeleteFramebuffersEXT; @@ -126,10 +137,13 @@ namespace osg TglFramebufferTextureLayerEXT* glFramebufferTextureLayerEXT; TglFramebufferRenderbufferEXT* glFramebufferRenderbufferEXT; TglGenerateMipmapEXT* glGenerateMipmapEXT; + TglBlitFramebufferEXT* glBlitFramebufferEXT; static FBOExtensions* instance(unsigned contextID, bool createIfNotInitalized); bool isSupported() const { return _supported; } + bool isMultisampleSupported() const { return glRenderbufferStorageMultisampleEXT != 0; } + bool isMultisampleCoverageSupported() const { return glRenderbufferStorageMultisampleCoverageNV != 0; } protected: FBOExtensions(unsigned int contextID); @@ -146,7 +160,7 @@ namespace osg { public: RenderBuffer(); - RenderBuffer(int width, int height, GLenum internalFormat); + RenderBuffer(int width, int height, GLenum internalFormat, int samples=0, int colorSamples=0); RenderBuffer(const RenderBuffer& copy, const CopyOp& copyop = CopyOp::SHALLOW_COPY); META_Object(osg, RenderBuffer); @@ -158,6 +172,10 @@ namespace osg inline void setSize(int w, int h); inline GLenum getInternalFormat() const; inline void setInternalFormat(GLenum format); + inline int getSamples() const; + inline int getColorSamples() const; + inline void setSamples(int samples); + inline void setColorSamples(int colorSamples); GLuint getObjectID(unsigned int contextID, const FBOExtensions *ext) const; inline int compare(const RenderBuffer &rb) const; @@ -185,9 +203,14 @@ namespace osg private: mutable buffered_value _objectID; mutable buffered_value _dirty; + GLenum _internalFormat; int _width; int _height; + // "samples" in the framebuffer_multisample extension is equivalent to + // "coverageSamples" in the framebuffer_multisample_coverage extension. + int _samples; + int _colorSamples; }; // INLINE METHODS @@ -232,6 +255,28 @@ namespace osg dirtyAll(); } + inline int RenderBuffer::getSamples() const + { + return _samples; + } + + inline int RenderBuffer::getColorSamples() const + { + return _colorSamples; + } + + inline void RenderBuffer::setSamples(int samples) + { + _samples = samples; + dirtyAll(); + } + + inline void RenderBuffer::setColorSamples(int colorSamples) + { + _colorSamples = colorSamples; + dirtyAll(); + } + inline void RenderBuffer::dirtyAll() const { _dirty.setAllElementsTo(1); @@ -278,8 +323,9 @@ namespace osg FrameBufferAttachment&operator = (const FrameBufferAttachment& copy); + bool isMultisample() const; void createRequiredTexturesAndApplyGenerateMipMap(State& state, const FBOExtensions* ext) const; - void attach(State &state, GLenum attachment_point, const FBOExtensions* ext) const; + void attach(State &state, GLenum target, GLenum attachment_point, const FBOExtensions* ext) const; int compare(const FrameBufferAttachment &fa) const; private: @@ -323,10 +369,22 @@ namespace osg inline bool hasMultipleRenderingTargets() const { return !_drawBuffers.empty(); } inline const MultipleRenderingTargets& getMultipleRenderingTargets() const { return _drawBuffers; } + bool isMultisample() const; + int compare(const StateAttribute &sa) const; void apply(State &state) const; + enum BindTarget + { + READ_FRAMEBUFFER = GL_READ_FRAMEBUFFER_EXT, + DRAW_FRAMEBUFFER = GL_DRAW_FRAMEBUFFER_EXT, + READ_DRAW_FRAMEBUFFER = GL_FRAMEBUFFER_EXT + }; + + /** Bind the FBO as either the read or draw target, or both. */ + void apply(State &state, BindTarget target) const; + /** Mark internal FBO for deletion. * Deletion requests are queued until they can be executed * in the proper GL context. */ diff --git a/include/osgUtil/RenderStage b/include/osgUtil/RenderStage index 15c0d2fad..da7f8bd82 100644 --- a/include/osgUtil/RenderStage +++ b/include/osgUtil/RenderStage @@ -138,11 +138,28 @@ class OSGUTIL_EXPORT RenderStage : public RenderBin void setImageReadPixelDataType(GLenum type) { _imageReadPixelDataType = type; } GLenum getImageReadPixelDataType() const { return _imageReadPixelDataType; } - + /** Set a framebuffer object to render into. It is permissible for the + * framebuffer object to be multisampled, in which case you should also + * set a resolve framebuffer object - see setMultisampleResolveFramebufferObject(). */ void setFrameBufferObject(osg::FrameBufferObject* fbo) { _fbo = fbo; } osg::FrameBufferObject* getFrameBufferObject() { return _fbo.get(); } const osg::FrameBufferObject* getFrameBufferObject() const { return _fbo.get(); } - + + /** Sets the destination framebuffer object for glBlitFramebufferEXT to + * resolve a multisampled framebuffer object after the RenderStage is + * drawn. The resolve framebuffer object must not be multisampled. The + * resolve framebuffer object is only necessary if the primary framebuffer + * object is multisampled, if not then leave it set to null. */ + void setMultisampleResolveFramebufferObject(osg::FrameBufferObject* fbo); + osg::FrameBufferObject* getMultisampleResolveFramebufferObject() { return _resolveFbo.get(); } + const osg::FrameBufferObject* getMultisampleResolveFramebufferObject() const { return _resolveFbo.get(); } + + /** Set whether the framebuffer object should be unbound after + * rendering. By default this is set to true. Set it to false if the + * unbinding is not required. */ + void setDisableFboAfterRender(bool disable) {_disableFboAfterRender = disable;} + bool getDisableFboAfterRender() const {return _disableFboAfterRender;} + void setGraphicsContext(osg::GraphicsContext* context) { _graphicsContext = context; } osg::GraphicsContext* getGraphicsContext() { return _graphicsContext.get(); } const osg::GraphicsContext* getGraphicsContext() const { return _graphicsContext.get(); } @@ -247,7 +264,9 @@ class OSGUTIL_EXPORT RenderStage : public RenderBin std::map< osg::Camera::BufferComponent, Attachment> _bufferAttachmentMap; osg::ref_ptr _fbo; + osg::ref_ptr _resolveFbo; osg::ref_ptr _graphicsContext; + bool _disableFboAfterRender; mutable osg::Matrix _inheritedPositionalStateContainerMatrix; mutable osg::ref_ptr _inheritedPositionalStateContainer; diff --git a/src/osg/Camera.cpp b/src/osg/Camera.cpp index fc831445e..aa2cdb1a3 100644 --- a/src/osg/Camera.cpp +++ b/src/osg/Camera.cpp @@ -258,17 +258,25 @@ void Camera::attach(BufferComponent buffer, GLenum internalFormat) _bufferAttachmentMap[buffer]._internalFormat = internalFormat; } -void Camera::attach(BufferComponent buffer, osg::Texture* texture, unsigned int level, unsigned int face, bool mipMapGeneration) +void Camera::attach(BufferComponent buffer, osg::Texture* texture, unsigned int level, unsigned int face, bool mipMapGeneration, + unsigned int multisampleSamples, + unsigned int multisampleColorSamples) { _bufferAttachmentMap[buffer]._texture = texture; _bufferAttachmentMap[buffer]._level = level; _bufferAttachmentMap[buffer]._face = face; _bufferAttachmentMap[buffer]._mipMapGeneration = mipMapGeneration; + _bufferAttachmentMap[buffer]._multisampleSamples = multisampleSamples; + _bufferAttachmentMap[buffer]._multisampleColorSamples = multisampleColorSamples; } -void Camera::attach(BufferComponent buffer, osg::Image* image) +void Camera::attach(BufferComponent buffer, osg::Image* image, + unsigned int multisampleSamples, + unsigned int multisampleColorSamples) { _bufferAttachmentMap[buffer]._image = image; + _bufferAttachmentMap[buffer]._multisampleSamples = multisampleSamples; + _bufferAttachmentMap[buffer]._multisampleColorSamples = multisampleColorSamples; } void Camera::detach(BufferComponent buffer) diff --git a/src/osg/FrameBufferObject.cpp b/src/osg/FrameBufferObject.cpp index 9d10eb8a7..d3c014053 100644 --- a/src/osg/FrameBufferObject.cpp +++ b/src/osg/FrameBufferObject.cpp @@ -41,7 +41,24 @@ FBOExtensions* FBOExtensions::instance(unsigned contextID, bool createIfNotInita #define LOAD_FBO_EXT(name) setGLExtensionFuncPtr(name, (#name)) FBOExtensions::FBOExtensions(unsigned int contextID) -: _supported(false) +: _supported(false), + glBindRenderbufferEXT(0), + glGenRenderbuffersEXT(0), + glDeleteRenderbuffersEXT(0), + glRenderbufferStorageEXT(0), + glRenderbufferStorageMultisampleEXT(0), + glRenderbufferStorageMultisampleCoverageNV(0), + glBindFramebufferEXT(0), + glDeleteFramebuffersEXT(0), + glGenFramebuffersEXT(0), + glCheckFramebufferStatusEXT(0), + glFramebufferTexture1DEXT(0), + glFramebufferTexture2DEXT(0), + glFramebufferTexture3DEXT(0), + glFramebufferTextureLayerEXT(0), + glFramebufferRenderbufferEXT(0), + glGenerateMipmapEXT(0), + glBlitFramebufferEXT(0) { if (!isGLExtensionSupported(contextID, "GL_EXT_framebuffer_object")) return; @@ -75,6 +92,21 @@ FBOExtensions::FBOExtensions(unsigned int contextID) glFramebufferTexture3DEXT != 0 && glFramebufferRenderbufferEXT != 0 && glGenerateMipmapEXT != 0; + + if (!isGLExtensionSupported(contextID, "GL_EXT_framebuffer_blit")) + return; + + LOAD_FBO_EXT(glBlitFramebufferEXT); + + if (isGLExtensionSupported(contextID, "GL_EXT_framebuffer_multisample")) + { + LOAD_FBO_EXT(glRenderbufferStorageMultisampleEXT); + } + + if (isGLExtensionSupported(contextID, "GL_NV_framebuffer_multisample_coverage")) + { + LOAD_FBO_EXT(glRenderbufferStorageMultisampleCoverageNV); + } } @@ -144,15 +176,19 @@ RenderBuffer::RenderBuffer() : Object(), _internalFormat(GL_DEPTH_COMPONENT24), _width(512), - _height(512) + _height(512), + _samples(0), + _colorSamples(0) { } -RenderBuffer::RenderBuffer(int width, int height, GLenum internalFormat) +RenderBuffer::RenderBuffer(int width, int height, GLenum internalFormat, int samples, int colorSamples) : Object(), _internalFormat(internalFormat), _width(width), - _height(height) + _height(height), + _samples(samples), + _colorSamples(colorSamples) { } @@ -160,7 +196,9 @@ RenderBuffer::RenderBuffer(const RenderBuffer ©, const CopyOp ©op) : Object(copy, copyop), _internalFormat(copy._internalFormat), _width(copy._width), - _height(copy._height) + _height(copy._height), + _samples(copy._samples), + _colorSamples(copy._colorSamples) { } @@ -190,7 +228,30 @@ GLuint RenderBuffer::getObjectID(unsigned int contextID, const FBOExtensions *ex { // bind and configure ext->glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, objectID); - ext->glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, _internalFormat, _width, _height); + + // framebuffer_multisample_coverage specification requires that coverage + // samples must be >= color samples. + if (_samples < _colorSamples) + { + notify(WARN) << "Coverage samples must be greater than or equal to color samples." + " Setting coverage samples equal to color samples." << std::endl; + const_cast(this)->setSamples(_colorSamples); + } + + if (_samples > 0 && ext->isMultisampleCoverageSupported()) + { + ext->glRenderbufferStorageMultisampleCoverageNV(GL_RENDERBUFFER_EXT, + _samples, _colorSamples, _internalFormat, _width, _height); + } + else if (_samples > 0 && ext->isMultisampleSupported()) + { + ext->glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, + _samples, _internalFormat, _width, _height); + } + else + { + ext->glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, _internalFormat, _width, _height); + } dirty = 0; } @@ -391,6 +452,16 @@ FrameBufferAttachment &FrameBufferAttachment::operator = (const FrameBufferAttac return *this; } +bool FrameBufferAttachment::isMultisample() const +{ + if (_ximpl->renderbufferTarget.valid()) + { + return _ximpl->renderbufferTarget->getSamples() > 0; + } + + return false; +} + void FrameBufferAttachment::createRequiredTexturesAndApplyGenerateMipMap(State &state, const FBOExtensions* ext) const { unsigned int contextID = state.getContextID(); @@ -422,7 +493,7 @@ void FrameBufferAttachment::createRequiredTexturesAndApplyGenerateMipMap(State & } } -void FrameBufferAttachment::attach(State &state, GLenum attachment_point, const FBOExtensions* ext) const +void FrameBufferAttachment::attach(State &state, GLenum target, GLenum attachment_point, const FBOExtensions* ext) const { unsigned int contextID = state.getContextID(); @@ -444,25 +515,25 @@ void FrameBufferAttachment::attach(State &state, GLenum attachment_point, const { default: case Pimpl::RENDERBUFFER: - ext->glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, attachment_point, GL_RENDERBUFFER_EXT, _ximpl->renderbufferTarget->getObjectID(contextID, ext)); + ext->glFramebufferRenderbufferEXT(target, attachment_point, GL_RENDERBUFFER_EXT, _ximpl->renderbufferTarget->getObjectID(contextID, ext)); break; case Pimpl::TEXTURE1D: - ext->glFramebufferTexture1DEXT(GL_FRAMEBUFFER_EXT, attachment_point, GL_TEXTURE_1D, tobj->_id, _ximpl->level); + ext->glFramebufferTexture1DEXT(target, attachment_point, GL_TEXTURE_1D, tobj->_id, _ximpl->level); break; case Pimpl::TEXTURE2D: - ext->glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, attachment_point, GL_TEXTURE_2D, tobj->_id, _ximpl->level); + ext->glFramebufferTexture2DEXT(target, attachment_point, GL_TEXTURE_2D, tobj->_id, _ximpl->level); break; case Pimpl::TEXTURE3D: - ext->glFramebufferTexture3DEXT(GL_FRAMEBUFFER_EXT, attachment_point, GL_TEXTURE_3D, tobj->_id, _ximpl->level, _ximpl->zoffset); + ext->glFramebufferTexture3DEXT(target, attachment_point, GL_TEXTURE_3D, tobj->_id, _ximpl->level, _ximpl->zoffset); break; case Pimpl::TEXTURE2DARRAY: - ext->glFramebufferTextureLayerEXT(GL_FRAMEBUFFER_EXT, attachment_point, tobj->_id, _ximpl->level, _ximpl->zoffset); + ext->glFramebufferTextureLayerEXT(target, attachment_point, tobj->_id, _ximpl->level, _ximpl->zoffset); break; case Pimpl::TEXTURERECT: - ext->glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, attachment_point, GL_TEXTURE_RECTANGLE, tobj->_id, 0); + ext->glFramebufferTexture2DEXT(target, attachment_point, GL_TEXTURE_RECTANGLE, tobj->_id, 0); break; case Pimpl::TEXTURECUBE: - ext->glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, attachment_point, GL_TEXTURE_CUBE_MAP_POSITIVE_X + _ximpl->cubeMapFace, tobj->_id, _ximpl->level); + ext->glFramebufferTexture2DEXT(target, attachment_point, GL_TEXTURE_CUBE_MAP_POSITIVE_X + _ximpl->cubeMapFace, tobj->_id, _ximpl->level); break; } } @@ -622,6 +693,11 @@ void FrameBufferObject::updateDrawBuffers() } void FrameBufferObject::apply(State &state) const +{ + apply(state, READ_DRAW_FRAMEBUFFER); +} + +void FrameBufferObject::apply(State &state, BindTarget target) const { unsigned int contextID = state.getContextID(); @@ -639,7 +715,7 @@ void FrameBufferObject::apply(State &state) const if (_attachments.empty()) { - ext->glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + ext->glBindFramebufferEXT(target, 0); return; } @@ -678,7 +754,7 @@ void FrameBufferObject::apply(State &state) const } - ext->glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboID); + ext->glBindFramebufferEXT(target, fboID); // enable drawing buffers to render the result to fbo if (_drawBuffers.size() > 0) @@ -695,13 +771,26 @@ void FrameBufferObject::apply(State &state) const for (AttachmentMap::const_iterator i=_attachments.begin(); i!=_attachments.end(); ++i) { const FrameBufferAttachment &fa = i->second; - fa.attach(state, convertBufferComponentToGLenum(i->first), ext); + fa.attach(state, target, convertBufferComponentToGLenum(i->first), ext); } dirtyAttachmentList = 0; } } +bool FrameBufferObject::isMultisample() const +{ + if (_attachments.size()) + { + // If the FBO is correctly set up then all attachments will be either + // multisampled or single sampled. Therefore we can just return the + // result of the first attachment. + return _attachments.begin()->second.isMultisample(); + } + + return false; +} + int FrameBufferObject::compare(const StateAttribute &sa) const { COMPARE_StateAttribute_Types(FrameBufferObject, sa); diff --git a/src/osgUtil/RenderStage.cpp b/src/osgUtil/RenderStage.cpp index 0531657f2..7200d857f 100644 --- a/src/osgUtil/RenderStage.cpp +++ b/src/osgUtil/RenderStage.cpp @@ -33,7 +33,8 @@ using namespace osgUtil; //RegisterRenderBinProxy s_registerRenderStageProxy; RenderStage::RenderStage(): - RenderBin(getDefaultRenderBinSortMode()) + RenderBin(getDefaultRenderBinSortMode()), + _disableFboAfterRender(true) { // point RenderBin's _stage to this to ensure that references to // stage don't go tempted away to any other stage. @@ -59,7 +60,8 @@ RenderStage::RenderStage(): } RenderStage::RenderStage(SortMode mode): - RenderBin(mode) + RenderBin(mode), + _disableFboAfterRender(true) { // point RenderBin's _stage to this to ensure that references to // stage don't go tempted away to any other stage. @@ -104,7 +106,8 @@ RenderStage::RenderStage(const RenderStage& rhs,const osg::CopyOp& copyop): _face(rhs._face), _imageReadPixelFormat(rhs._imageReadPixelFormat), _imageReadPixelDataType(rhs._imageReadPixelDataType), - _renderStageLighting(rhs._renderStageLighting) + _renderStageLighting(rhs._renderStageLighting), + _disableFboAfterRender(rhs._disableFboAfterRender) { _stage = this; } @@ -327,10 +330,37 @@ void RenderStage::runCameraSetUp(osg::RenderInfo& renderInfo) OpenThreads::ScopedLock lock(*(_camera->getDataChangeMutex())); osg::ref_ptr fbo = new osg::FrameBufferObject; - + osg::ref_ptr fbo_multisample; + bool colorAttached = false; bool depthAttached = false; bool stencilAttached = false; + unsigned samples = 0; + unsigned colorSamples = 0; + + if (fbo_ext->isMultisampleSupported()) + { + for(osg::Camera::BufferAttachmentMap::iterator itr = bufferAttachments.begin(); + itr != bufferAttachments.end(); + ++itr) + { + osg::Camera::Attachment& attachment = itr->second; + samples = maximum(samples, attachment._multisampleSamples); + colorSamples = maximum(samples, attachment._multisampleColorSamples); + } + + if (colorSamples > samples) + { + osg::notify(WARN) << "Multisample color samples must be less than or " + "equal to samples. Setting color samples equal to samples." << std::endl; + colorSamples = samples; + } + + if (samples) + { + fbo_multisample = new osg::FrameBufferObject; + } + } for(osg::Camera::BufferAttachmentMap::iterator itr = bufferAttachments.begin(); itr != bufferAttachments.end(); @@ -344,6 +374,30 @@ void RenderStage::runCameraSetUp(osg::RenderInfo& renderInfo) fbo->setAttachment(buffer, osg::FrameBufferAttachment(attachment)); else fbo->setAttachment(buffer, osg::FrameBufferAttachment(new osg::RenderBuffer(width, height, attachment._internalFormat))); + + if (fbo_multisample.valid()) + { + GLenum internalFormat = attachment._internalFormat; + if (!internalFormat) + { + switch (buffer) + { + case Camera::DEPTH_BUFFER: + internalFormat = GL_DEPTH_COMPONENT24; + break; + case Camera::STENCIL_BUFFER: + internalFormat = GL_STENCIL_INDEX8_EXT; + break; + default: + internalFormat = GL_RGBA; + break; + } + } + fbo_multisample->setAttachment(buffer, + osg::FrameBufferAttachment(new osg::RenderBuffer( + width, height, internalFormat, + samples, colorSamples))); + } if (buffer==osg::Camera::DEPTH_BUFFER) depthAttached = true; else if (buffer==osg::Camera::STENCIL_BUFFER) stencilAttached = true; @@ -353,11 +407,23 @@ void RenderStage::runCameraSetUp(osg::RenderInfo& renderInfo) if (!depthAttached) { fbo->setAttachment(osg::Camera::DEPTH_BUFFER, osg::FrameBufferAttachment(new osg::RenderBuffer(width, height, GL_DEPTH_COMPONENT24))); + if (fbo_multisample.valid()) + { + fbo_multisample->setAttachment(osg::Camera::DEPTH_BUFFER, + osg::FrameBufferAttachment(new osg::RenderBuffer(width, + height, GL_DEPTH_COMPONENT24, samples, colorSamples))); + } } if (!colorAttached) { fbo->setAttachment(osg::Camera::COLOR_BUFFER, osg::FrameBufferAttachment(new osg::RenderBuffer(width, height, GL_RGB))); + if (fbo_multisample.valid()) + { + fbo_multisample->setAttachment(osg::Camera::COLOR_BUFFER, + osg::FrameBufferAttachment(new osg::RenderBuffer(width, + height, GL_RGB, samples, colorSamples))); + } } fbo->apply(state); @@ -386,6 +452,39 @@ void RenderStage::runCameraSetUp(osg::RenderInfo& renderInfo) setReadBuffer(GL_NONE); _fbo = fbo; + + if (fbo_multisample.valid()) + { + fbo_multisample->apply(state); + + GLenum status = fbo_ext->glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); + + if (status != GL_FRAMEBUFFER_COMPLETE_EXT) + { + notify(NOTICE) << "RenderStage::runCameraSetUp(), " + "multisample FBO setup failed, FBO status = 0x" + << std::hex << status << std::endl; + + fbo->apply(state); + fbo_multisample = 0; + _resolveFbo = 0; + + // clean up. + double availableTime = 100.0f; + double currentTime = state.getFrameStamp()?state.getFrameStamp()->getReferenceTime():0.0; + osg::RenderBuffer::flushDeletedRenderBuffers(state.getContextID(),currentTime,availableTime); + osg::FrameBufferObject::flushDeletedFrameBufferObjects(state.getContextID(),currentTime,availableTime); + } + else + { + _resolveFbo.swap(_fbo); + _fbo = fbo_multisample; + } + } + else + { + _resolveFbo = 0; + } } } @@ -680,6 +779,27 @@ void RenderStage::copyTexture(osg::RenderInfo& renderInfo) void RenderStage::drawInner(osg::RenderInfo& renderInfo,RenderLeaf*& previous, bool& doCopyTexture) { + struct SubFunc + { + static void applyReadFBO(bool& apply_read_fbo, + const FrameBufferObject* read_fbo, osg::State& state) + { + if (read_fbo->isMultisample()) + { + osg::notify(osg::WARN) << "Attempting to read from a" + " multisampled framebuffer object. Set a resolve" + " framebuffer on the RenderStage to fix this." << std::endl; + } + + if (apply_read_fbo) + { + // Bind the monosampled FBO to read from + read_fbo->apply(state, FrameBufferObject::READ_FRAMEBUFFER); + apply_read_fbo = false; + } + } + }; + osg::State& state = *renderInfo.getState(); osg::FBOExtensions* fbo_ext = _fbo.valid() ? osg::FBOExtensions::instance(state.getContextID(),true) : 0; @@ -722,67 +842,115 @@ void RenderStage::drawInner(osg::RenderInfo& renderInfo,RenderLeaf*& previous, b } } + const FrameBufferObject* read_fbo = _fbo.get(); + bool apply_read_fbo = false; + + if (fbo_supported && _resolveFbo.valid() && fbo_ext->glBlitFramebufferEXT) + { + GLbitfield blitMask = 0; + + //find which buffer types should be copied + for (FrameBufferObject::AttachmentMap::const_iterator + it = _resolveFbo->getAttachmentMap().begin(), + end =_resolveFbo->getAttachmentMap().end(); it != end; ++it) + { + switch (it->first) + { + case Camera::DEPTH_BUFFER: + blitMask |= GL_DEPTH_BUFFER_BIT; + break; + case Camera::STENCIL_BUFFER: + blitMask |= GL_STENCIL_BUFFER_BIT; + break; + default: + blitMask |= GL_COLOR_BUFFER_BIT; + break; + } + } + + // Bind the resolve framebuffer to blit into. + _resolveFbo->apply(state, FrameBufferObject::DRAW_FRAMEBUFFER); + + // Blit to the resolve framebuffer. + // Note that (with nvidia 175.16 windows drivers at least) if the read + // framebuffer is multisampled then the dimension arguments are ignored + // and the whole framebuffer is always copied. + fbo_ext->glBlitFramebufferEXT( + 0, 0, static_cast(_viewport->width()), static_cast(_viewport->height()), + 0, 0, static_cast(_viewport->width()), static_cast(_viewport->height()), + blitMask, GL_NEAREST); + + apply_read_fbo = true; + read_fbo = _resolveFbo.get(); + + using_multiple_render_targets = read_fbo->hasMultipleRenderingTargets(); + } + // now copy the rendered image to attached texture. if (doCopyTexture) { + SubFunc::applyReadFBO(apply_read_fbo, read_fbo, state); copyTexture(renderInfo); } - + std::map< osg::Camera::BufferComponent, Attachment>::const_iterator itr; for(itr = _bufferAttachmentMap.begin(); itr != _bufferAttachmentMap.end(); ++itr) { - if (itr->second._image.valid()) - { - if (using_multiple_render_targets) - { - int attachment=itr->first; - if (attachment==osg::Camera::DEPTH_BUFFER || attachment==osg::Camera::STENCIL_BUFFER) { - // assume first buffer rendered to is the one we want - glReadBuffer(_fbo->getMultipleRenderingTargets()[0]); - } else { - glReadBuffer(GL_COLOR_ATTACHMENT0_EXT + (attachment - osg::Camera::COLOR_BUFFER0)); - } - } else { - if (_readBuffer != GL_NONE) - { - glReadBuffer(_readBuffer); - } - } + if (itr->second._image.valid()) + { + SubFunc::applyReadFBO(apply_read_fbo, read_fbo, state); - GLenum pixelFormat = itr->second._image->getPixelFormat(); - if (pixelFormat==0) pixelFormat = _imageReadPixelFormat; - if (pixelFormat==0) pixelFormat = GL_RGB; + if (using_multiple_render_targets) + { + int attachment=itr->first; + if (attachment==osg::Camera::DEPTH_BUFFER || attachment==osg::Camera::STENCIL_BUFFER) { + // assume first buffer rendered to is the one we want + glReadBuffer(read_fbo->getMultipleRenderingTargets()[0]); + } else { + glReadBuffer(GL_COLOR_ATTACHMENT0_EXT + (attachment - osg::Camera::COLOR_BUFFER0)); + } + } else { + if (_readBuffer != GL_NONE) + { + glReadBuffer(_readBuffer); + } + } - GLenum dataType = itr->second._image->getDataType(); - if (dataType==0) dataType = _imageReadPixelDataType; - if (dataType==0) dataType = GL_UNSIGNED_BYTE; + GLenum pixelFormat = itr->second._image->getPixelFormat(); + if (pixelFormat==0) pixelFormat = _imageReadPixelFormat; + if (pixelFormat==0) pixelFormat = GL_RGB; - itr->second._image->readPixels(static_cast(_viewport->x()), - static_cast(_viewport->y()), - static_cast(_viewport->width()), - static_cast(_viewport->height()), - pixelFormat, dataType); + GLenum dataType = itr->second._image->getDataType(); + if (dataType==0) dataType = _imageReadPixelDataType; + if (dataType==0) dataType = GL_UNSIGNED_BYTE; + + itr->second._image->readPixels(static_cast(_viewport->x()), + static_cast(_viewport->y()), + static_cast(_viewport->width()), + static_cast(_viewport->height()), + pixelFormat, dataType); + } + } - } - } - - if (fbo_supported) { - // switch of the frame buffer object - fbo_ext->glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + if (getDisableFboAfterRender()) + { + // switch off the frame buffer object + fbo_ext->glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + } doCopyTexture = true; } - + if (fbo_supported && _camera) { // now generate mipmaps if they are required. - const osg::Camera::BufferAttachmentMap& bufferAttachements = _camera->getBufferAttachmentMap(); - for(osg::Camera::BufferAttachmentMap::const_iterator itr = bufferAttachements.begin(); - itr != bufferAttachements.end(); + const osg::Camera::BufferAttachmentMap& bufferAttachments = _camera->getBufferAttachmentMap(); + for(osg::Camera::BufferAttachmentMap::const_iterator itr = bufferAttachments.begin(); + itr != bufferAttachments.end(); ++itr) { if (itr->second._texture.valid() && itr->second._mipMapGeneration) @@ -1142,3 +1310,13 @@ unsigned int RenderStage::computeNumberOfDynamicRenderLeaves() const return count; } + +void osgUtil::RenderStage::setMultisampleResolveFramebufferObject(osg::FrameBufferObject* fbo) +{ + if (fbo && fbo->isMultisample()) + { + osg::notify(osg::WARN) << "Resolve framebuffer must not be" + " multisampled." << std::endl; + } + _resolveFbo = fbo; +}