/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield * * This library is open source and may be redistributed and/or modified under * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or * (at your option) any later version. The full license is in LICENSE file * included with this distribution, and on the openscenegraph.org website. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * OpenSceneGraph Public License for more details. */ // initial FBO support written by Marco Jez, June 2005. #ifndef OSG_FRAMEBUFFEROBJECT #define OSG_FRAMEBUFFEROBJECT 1 #include #include #include #include #ifndef GL_EXT_framebuffer_object #define GL_EXT_framebuffer_object 1 #define GL_FRAMEBUFFER_EXT 0x8D40 #define GL_RENDERBUFFER_EXT 0x8D41 // #define GL_STENCIL_INDEX_EXT 0x8D45 // removed in rev. #114 of the spec #define GL_STENCIL_INDEX1_EXT 0x8D46 #define GL_STENCIL_INDEX4_EXT 0x8D47 #define GL_STENCIL_INDEX8_EXT 0x8D48 #define GL_STENCIL_INDEX16_EXT 0x8D49 #define GL_RENDERBUFFER_WIDTH_EXT 0x8D42 #define GL_RENDERBUFFER_HEIGHT_EXT 0x8D43 #define GL_RENDERBUFFER_INTERNAL_FORMAT_EXT 0x8D44 #define GL_RENDERBUFFER_RED_SIZE_EXT 0x8D50 #define GL_RENDERBUFFER_GREEN_SIZE_EXT 0x8D51 #define GL_RENDERBUFFER_BLUE_SIZE_EXT 0x8D52 #define GL_RENDERBUFFER_ALPHA_SIZE_EXT 0x8D53 #define GL_RENDERBUFFER_DEPTH_SIZE_EXT 0x8D54 #define GL_RENDERBUFFER_STENCIL_SIZE_EXT 0x8D55 #define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT 0x8CD0 #define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT 0x8CD1 #define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT 0x8CD2 #define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_EXT 0x8CD3 #define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT 0x8CD4 #define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER_EXT 0x8CD4 #define GL_COLOR_ATTACHMENT0_EXT 0x8CE0 #define GL_COLOR_ATTACHMENT1_EXT 0x8CE1 #define GL_COLOR_ATTACHMENT2_EXT 0x8CE2 #define GL_COLOR_ATTACHMENT3_EXT 0x8CE3 #define GL_COLOR_ATTACHMENT4_EXT 0x8CE4 #define GL_COLOR_ATTACHMENT5_EXT 0x8CE5 #define GL_COLOR_ATTACHMENT6_EXT 0x8CE6 #define GL_COLOR_ATTACHMENT7_EXT 0x8CE7 #define GL_COLOR_ATTACHMENT8_EXT 0x8CE8 #define GL_COLOR_ATTACHMENT9_EXT 0x8CE9 #define GL_COLOR_ATTACHMENT10_EXT 0x8CEA #define GL_COLOR_ATTACHMENT11_EXT 0x8CEB #define GL_COLOR_ATTACHMENT12_EXT 0x8CEC #define GL_COLOR_ATTACHMENT13_EXT 0x8CED #define GL_COLOR_ATTACHMENT14_EXT 0x8CEE #define GL_COLOR_ATTACHMENT15_EXT 0x8CEF #define GL_DEPTH_ATTACHMENT_EXT 0x8D00 #define GL_STENCIL_ATTACHMENT_EXT 0x8D20 #define GL_FRAMEBUFFER_COMPLETE_EXT 0x8CD5 #define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT 0x8CD6 #define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT 0x8CD7 #define GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT 0x8CD8 #define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT 0x8CD9 #define GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT 0x8CDA #define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT 0x8CDB #define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT 0x8CDC #define GL_FRAMEBUFFER_UNSUPPORTED_EXT 0x8CDD #define GL_FRAMEBUFFER_BINDING_EXT 0x8CA6 #define GL_RENDERBUFFER_BINDING_EXT 0x8CA7 #define GL_MAX_COLOR_ATTACHMENTS_EXT 0x8CDF #define GL_MAX_RENDERBUFFER_SIZE_EXT 0x84E8 #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_EXT_framebuffer_multisample #define GL_EXT_framebuffer_multisample 1 #define GL_RENDERBUFFER_SAMPLES_EXT 0x8CAB #define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT 0x8D56 #define GL_MAX_SAMPLES_EXT 0x8D57 #endif #ifndef GL_MAX_SAMPLES_EXT // Workaround for Centos 5 and other distros that define // GL_EXT_framebuffer_multisample but not GL_MAX_SAMPLES_EXT #define GL_MAX_SAMPLES_EXT 0x8D57 #endif #ifndef GL_NV_framebuffer_multisample_coverage #define GL_NV_framebuffer_multisample_coverage 1 #define GL_RENDERBUFFER_COVERAGE_SAMPLES_NV 0x8CAB #define GL_RENDERBUFFER_COLOR_SAMPLES_NV 0x8E10 #define GL_MAX_MULTISAMPLE_COVERAGE_MODES_NV 0x8E11 #define GL_MULTISAMPLE_COVERAGE_MODES_NV 0x8E12 #endif #ifndef GL_DEPTH_COMPONENT #define GL_DEPTH_COMPONENT 0x1902 #endif #ifndef GL_VERSION_1_4 #define GL_DEPTH_COMPONENT16 0x81A5 #define GL_DEPTH_COMPONENT24 0x81A6 #define GL_DEPTH_COMPONENT32 0x81A7 #endif #ifndef GL_DEPTH_COMPONENT32F #define GL_DEPTH_COMPONENT32F 0x8CAC #endif #ifndef GL_DEPTH_COMPONENT32F_NV #define GL_DEPTH_COMPONENT32F_NV 0x8DAB #endif #ifndef GL_EXT_packed_depth_stencil #define GL_EXT_packed_depth_stencil 1 #define GL_DEPTH_STENCIL_EXT 0x84F9 #define GL_UNSIGNED_INT_24_8_EXT 0x84FA #define GL_DEPTH24_STENCIL8_EXT 0x88F0 #define GL_TEXTURE_STENCIL_SIZE_EXT 0x88F1 #endif namespace osg { /************************************************************************** * FBOExtensions **************************************************************************/ class OSG_EXPORT FBOExtensions : public osg::Referenced { public: typedef void GL_APIENTRY TglBindRenderbuffer(GLenum, GLuint); typedef void GL_APIENTRY TglDeleteRenderbuffers(GLsizei n, const GLuint *renderbuffers); typedef void GL_APIENTRY TglGenRenderbuffers(GLsizei, GLuint *); typedef void GL_APIENTRY TglRenderbufferStorage(GLenum, GLenum, GLsizei, GLsizei); typedef void GL_APIENTRY TglRenderbufferStorageMultisample(GLenum, GLsizei, GLenum, GLsizei, GLsizei); typedef void GL_APIENTRY TglRenderbufferStorageMultisampleCoverageNV(GLenum, GLsizei, GLsizei, GLenum, GLsizei, GLsizei); typedef void GL_APIENTRY TglBindFramebuffer(GLenum, GLuint); typedef void GL_APIENTRY TglDeleteFramebuffers(GLsizei n, const GLuint *framebuffers); typedef void GL_APIENTRY TglGenFramebuffers(GLsizei, GLuint *); typedef GLenum GL_APIENTRY TglCheckFramebufferStatus(GLenum); typedef void GL_APIENTRY TglFramebufferTexture1D(GLenum, GLenum, GLenum, GLuint, GLint); typedef void GL_APIENTRY TglFramebufferTexture2D(GLenum, GLenum, GLenum, GLuint, GLint); typedef void GL_APIENTRY TglFramebufferTexture3D(GLenum, GLenum, GLenum, GLuint, GLint, GLint); typedef void GL_APIENTRY TglFramebufferTexture(GLenum, GLenum, GLint, GLint); typedef void GL_APIENTRY TglFramebufferTextureLayer(GLenum, GLenum, GLuint, GLint, GLint); typedef void GL_APIENTRY TglFramebufferRenderbuffer(GLenum, GLenum, GLenum, GLuint); typedef void GL_APIENTRY TglGenerateMipmap(GLenum); typedef void GL_APIENTRY TglBlitFramebuffer(GLint, GLint, GLint, GLint, GLint, GLint, GLint, GLint, GLbitfield, GLenum); typedef void GL_APIENTRY TglGetRenderbufferParameteriv(GLenum, GLenum, GLint*); TglBindRenderbuffer* glBindRenderbuffer; TglGenRenderbuffers* glGenRenderbuffers; TglDeleteRenderbuffers* glDeleteRenderbuffers; TglRenderbufferStorage* glRenderbufferStorage; TglRenderbufferStorageMultisample* glRenderbufferStorageMultisample; TglRenderbufferStorageMultisampleCoverageNV* glRenderbufferStorageMultisampleCoverageNV; TglBindFramebuffer* glBindFramebuffer; TglDeleteFramebuffers* glDeleteFramebuffers; TglGenFramebuffers* glGenFramebuffers; TglCheckFramebufferStatus* glCheckFramebufferStatus; TglFramebufferTexture1D* glFramebufferTexture1D; TglFramebufferTexture2D* glFramebufferTexture2D; TglFramebufferTexture3D* glFramebufferTexture3D; TglFramebufferTexture* glFramebufferTexture; TglFramebufferTextureLayer* glFramebufferTextureLayer; TglFramebufferRenderbuffer* glFramebufferRenderbuffer; TglGenerateMipmap* glGenerateMipmap; TglBlitFramebuffer* glBlitFramebuffer; TglGetRenderbufferParameteriv* glGetRenderbufferParameteriv; static FBOExtensions* instance(unsigned contextID, bool createIfNotInitalized); bool isSupported() const { return _supported; } bool isMultisampleSupported() const { return glRenderbufferStorageMultisample != 0; } bool isMultisampleCoverageSupported() const { return glRenderbufferStorageMultisampleCoverageNV != 0; } bool isPackedDepthStencilSupported() const { return _packed_depth_stencil_supported; } protected: FBOExtensions(unsigned int contextID); bool _supported; bool _packed_depth_stencil_supported; }; /************************************************************************** * RenderBuffer **************************************************************************/ class OSG_EXPORT RenderBuffer: public Object { public: RenderBuffer(); 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); inline int getWidth() const; inline int getHeight() const; inline void setWidth(int w); inline void setHeight(int h); 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; /** Mark internal RenderBuffer for deletion. * Deletion requests are queued until they can be executed * in the proper GL context. */ static void deleteRenderBuffer(unsigned int contextID, GLuint rb); /** flush all the cached RenderBuffers which need to be deleted * in the OpenGL context related to contextID.*/ static void flushDeletedRenderBuffers(unsigned int contextID,double currentTime, double& availableTime); /** discard all the cached RenderBuffers which need to be deleted in the OpenGL context related to contextID. * Note, unlike flush no OpenGL calls are made, instead the handles are all removed. * this call is useful for when an OpenGL context has been destroyed. */ static void discardDeletedRenderBuffers(unsigned int contextID); static int getMaxSamples(unsigned int contextID, const FBOExtensions *ext); protected: virtual ~RenderBuffer(); RenderBuffer &operator=(const RenderBuffer &) { return *this; } inline void dirtyAll() const; 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 inline int RenderBuffer::getWidth() const { return _width; } inline int RenderBuffer::getHeight() const { return _height; } inline void RenderBuffer::setWidth(int w) { _width = w; dirtyAll(); } inline void RenderBuffer::setHeight(int h) { _height = h; dirtyAll(); } inline void RenderBuffer::setSize(int w, int h) { _width = w; _height = h; dirtyAll(); } inline GLenum RenderBuffer::getInternalFormat() const { return _internalFormat; } inline void RenderBuffer::setInternalFormat(GLenum format) { _internalFormat = format; 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); } inline int RenderBuffer::compare(const RenderBuffer &rb) const { if (&rb == this) return 0; if (_internalFormat < rb._internalFormat) return -1; if (_internalFormat > rb._internalFormat) return 1; if (_width < rb._width) return -1; if (_width > rb._width) return 1; if (_height < rb._height) return -1; if (_height > rb._height) return 1; return 0; } /************************************************************************** * FrameBufferAttachement **************************************************************************/ class Texture1D; class Texture2D; class Texture2DMultisample; class Texture3D; class Texture2DArray; class TextureCubeMap; class TextureRectangle; class OSG_EXPORT FrameBufferAttachment { public: FrameBufferAttachment(); FrameBufferAttachment(const FrameBufferAttachment& copy); explicit FrameBufferAttachment(RenderBuffer* target); explicit FrameBufferAttachment(Texture1D* target, unsigned int level = 0); explicit FrameBufferAttachment(Texture2D* target, unsigned int level = 0); explicit FrameBufferAttachment(Texture2DMultisample* target, unsigned int level = 0); explicit FrameBufferAttachment(Texture3D* target, unsigned int zoffset, unsigned int level = 0); explicit FrameBufferAttachment(Texture2DArray* target, unsigned int layer, unsigned int level = 0); explicit FrameBufferAttachment(TextureCubeMap* target, unsigned int face, unsigned int level = 0); explicit FrameBufferAttachment(TextureRectangle* target); explicit FrameBufferAttachment(Camera::Attachment& attachment); ~FrameBufferAttachment(); FrameBufferAttachment&operator = (const FrameBufferAttachment& copy); bool isMultisample() const; void createRequiredTexturesAndApplyGenerateMipMap(State& state, const FBOExtensions* ext) const; void attach(State &state, GLenum target, GLenum attachment_point, const FBOExtensions* ext) const; int compare(const FrameBufferAttachment &fa) const; RenderBuffer* getRenderBuffer(); const RenderBuffer* getRenderBuffer() const; Texture* getTexture(); const Texture* getTexture() const; unsigned int getCubeMapFace() const; unsigned int getTextureLevel() const; unsigned int getTexture3DZOffset() const; unsigned int getTextureArrayLayer() const; private: // use the Pimpl idiom to avoid dependency from // all Texture* headers struct Pimpl; Pimpl* _ximpl; }; /************************************************************************** * FrameBufferObject **************************************************************************/ class OSG_EXPORT FrameBufferObject: public StateAttribute { public: typedef std::map AttachmentMap; typedef std::vector MultipleRenderingTargets; typedef Camera::BufferComponent BufferComponent; FrameBufferObject(); FrameBufferObject(const FrameBufferObject& copy, const CopyOp& copyop = CopyOp::SHALLOW_COPY); META_StateAttribute(osg, FrameBufferObject, (StateAttribute::Type)0x101010/*FrameBufferObject*/); inline const AttachmentMap& getAttachmentMap() const; void setAttachment(BufferComponent attachment_point, const FrameBufferAttachment &attachment); inline const FrameBufferAttachment& getAttachment(BufferComponent attachment_point) const; inline bool hasAttachment(BufferComponent attachment_point) const; 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. */ static void deleteFrameBufferObject(unsigned int contextID, GLuint program); /** flush all the cached FBOs which need to be deleted * in the OpenGL context related to contextID.*/ static void flushDeletedFrameBufferObjects(unsigned int contextID,double currentTime, double& availableTime); /** discard all the cached FBOs which need to be deleted * in the OpenGL context related to contextID.*/ static void discardDeletedFrameBufferObjects(unsigned int contextID); protected: virtual ~FrameBufferObject(); FrameBufferObject& operator = (const FrameBufferObject&) { return *this; } void updateDrawBuffers(); inline void dirtyAll(); GLenum convertBufferComponentToGLenum(BufferComponent attachment_point) const; private: AttachmentMap _attachments; // Buffers passed to glDrawBuffers when using multiple render targets. MultipleRenderingTargets _drawBuffers; mutable buffered_value _dirtyAttachmentList; mutable buffered_value _unsupported; mutable buffered_value _fboID; }; // INLINE METHODS inline const FrameBufferObject::AttachmentMap &FrameBufferObject::getAttachmentMap() const { return _attachments; } inline bool FrameBufferObject::hasAttachment(FrameBufferObject::BufferComponent attachment_point) const { return _attachments.find(attachment_point) != _attachments.end(); } inline const FrameBufferAttachment &FrameBufferObject::getAttachment(FrameBufferObject::BufferComponent attachment_point) const { return _attachments.find(attachment_point)->second; } inline void FrameBufferObject::dirtyAll() { _dirtyAttachmentList.setAllElementsTo(1); } } #endif