/*========================================================================= Program: Visualization Toolkit Module: vtkOpenGLFramebufferObject.h Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. See Copyright.txt or http://www.kitware.com/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ /** * @class vtkOpenGLFramebufferObject * @brief Internal class which encapsulates OpenGL FramebufferObject * * Before delving into this class it is best to have some background * in some OpenGL terms. OpenGL has a notion of a currently * bound Framebuffers for drawing and reading. It can be the default * framebuffer such as created with a standard window/context or * it can be a user created Framebuffer objects. When draw and read * commands are invoked, they apply to the current draw and/or read * framebuffers. * * A framebuffer consists of color buffers and an optional depth buffer. * The FramebufferObject does not hold the memory for these buffers, it * just keeps track of what buffers are attached to it. The buffers themselves * hold the storage for the pixels/depths. * * In the context of this discussion a buffer can be either a * vtkTextureObject (both 2D or a slice of a 3D texture) or * a vtkRenderbuffer. In some cases a renderbuffer may be faster * or more lightweight but you cannot pass a renderbuffer into * a shader for sampling in a later pass like you can a texture. * * You attach these buffers to the Framebuffer using methods * such as AddColorAttachment or AddDepthAttachment * In normal usage a buffer is Attached to a FramebufferObject * and then some or all of the attached buffers are activated for drawing * or reading. * * When you have a framebuffer bound along with some buffers attached to it * you can then activate specific buffers for drawing or reading. So you * have draw and read framebuffer objects (bindings) and then for the currently * bound FramebufferObjects you have active draw and read buffers. * * A single FramebufferObject can be bound to both Draw and Read. You * cannot assign and activate a TextureObject for drawing on the FO and * at the same time pass it in as a Texture to the shader program. That * type of operation is very common and must be done in two steps. * - Render to the FO with the Texture attached as an active buffer * - deactivate the texture obj and then render with the texture obj * as a texture passed into the shader * * Typical use cases: * The simplest example *\code{.cpp} * fbo->SetContext(renWin); * fbo->SaveCurrentBindingsAndBuffers(); * fbo->PopulateFramebuffer(width, height); * * ... * * fbo->RestorePreviousBindingsAndBuffers(); *\endcode * * If you wish to use a texture you created * *\code{.cpp} * fbo->SetContext(renWin); * fbo->SaveCurrentBindingsAndBuffers(); * fbo->Bind(); * fbo->AddColorAttachment(0, vtkTextureObj); * fbo->AddDepthAttachment(); // auto create depth buffer * fbo->ActivateBuffer(0); * * ... * * fbo->RestorePreviousBindingsAndBuffers(); *\endcode * * If you will be using a FO repeatedly then it is best to create it * attach the buffers and then use as needed for example * * Typical use case: *\code{.cpp} * // setup the FBO once * fbo->SetContext(renWin); * fbo->SaveCurrentBindingsAndBuffers(); * fbo->AddColorAttachment(0, vtkTextureObj); * fbo->AddDepthAttachment(); // auto create depth buffer * fbo->RestorePreviousBindingsAndBuffers(); * * // use it many times * fbo->SaveCurrentBindingsAndBuffers(); * fbo->Bind(); * fbo->ActivateBuffer(0); * ... // render here etc * fbo->RestorePreviousBindingsAndBuffers(); *\endcode * * If you with to only bind the framebuffer for drawing or reading there * are mode specific versions of some methods that only apply to the * mode specified Draw/Read/Both. The mode argument uses OpenGL constants * so this class provides convenience methods to return them named * GetDrawMode() GetReadMode() and GetBothMode() so that your code * does not need to be polluted with OpenGL headers/constants. * * This class replaces both vtkFrameBufferObject and vtkFrameBufferObject2 * and contins methods from both of them. Most methods from FO2 should * work with this class. Just rename FBO2 to FBO and make sure to Save and * Restore the bindings and buffers. * If you have been using the old FO class, which had comments * in the header saying not to use it. Then you are in for a bit more of * a conversion but generally it should still be easy. Use the code * samples above (or any of the classes in OpenGL2 that currently use FBOs) * to guide you. They have all been converted to this class. Where previously * a DepthBuffer was automatically created for you, you now need to do it * explicitly using AddDepthAttachment(). * * Note the capitalization of FramebufferObject * * @sa * vtkTextureObject, vtkRenderbufferObject */ #ifndef vtkOpenGLFramebufferObject_h #define vtkOpenGLFramebufferObject_h /* Dec 2018 this class has been cleaned up such that * AddColorAttachment and AddDepthAttachment no longer * take a mode argument. The mode is determined by how * the framebuffer is bound. If you are using these methods * and need to support both the old and new signatures you * can check for the following define in your code. */ #define VTK_UPDATED_FRAMEBUFFER /** * A variant of vtkErrorMacro that is used to verify framebuffer * object completeness. It's provided so that reporting may include * the file and line number of the offending code. In release mode * the macro does nothing. */ #ifdef NDEBUG #define vtkCheckFrameBufferStatusMacro(mode) #define vtkStaticCheckFrameBufferStatusMacro(mode) #else #define vtkCheckFrameBufferStatusMacroImpl(macro, mode) \ { \ const char* eStr; \ bool ok = vtkOpenGLFramebufferObject::GetFrameBufferStatus(mode, eStr); \ if (!ok) \ { \ macro(<< "OpenGL ERROR. The FBO is incomplete : " << eStr); \ } \ } #define vtkCheckFrameBufferStatusMacro(mode) vtkCheckFrameBufferStatusMacroImpl(vtkErrorMacro, mode) #define vtkStaticCheckFrameBufferStatusMacro(mode) \ vtkCheckFrameBufferStatusMacroImpl(vtkGenericWarningMacro, mode) #endif #include "vtkFrameBufferObjectBase.h" #include "vtkRenderingOpenGL2Module.h" // For export macro #include // for the maps #include // for the lists of logical buffers. class vtkFOInfo; class vtkGenericOpenGLResourceFreeCallback; class vtkOpenGLRenderWindow; class vtkOpenGLVertexArrayObject; class vtkPixelBufferObject; class vtkRenderWindow; class vtkRenderbuffer; class vtkShaderProgram; class vtkTextureObject; class vtkWindow; class VTKRENDERINGOPENGL2_EXPORT vtkOpenGLFramebufferObject : public vtkFrameBufferObjectBase { public: static vtkOpenGLFramebufferObject* New(); vtkTypeMacro(vtkOpenGLFramebufferObject, vtkFrameBufferObjectBase); void PrintSelf(ostream& os, vtkIndent indent) override; ///@{ /** * Get/Set the context. Context must be a vtkOpenGLRenderWindow. * This does not increase the reference count of the * context to avoid reference loops. * SetContext() may raise an error if the OpenGL context does not support the * required OpenGL extensions. */ void SetContext(vtkRenderWindow* context); vtkOpenGLRenderWindow* GetContext(); ///@} /** * Make the draw frame buffer active. */ void Bind(); void Bind(unsigned int mode); /** * Unbind this buffer */ void UnBind(); void UnBind(unsigned int mode); ///@{ /** * Store/Restore the current framebuffer bindings and buffers. */ void SaveCurrentBindingsAndBuffers(); void SaveCurrentBindingsAndBuffers(unsigned int mode); void RestorePreviousBindingsAndBuffers(); void RestorePreviousBindingsAndBuffers(unsigned int mode); ///@} ///@{ /** * User must take care that width/height match the dimensions of * the user defined texture attachments. * This method makes the "active buffers" the buffers that will get drawn * into by subsequent drawing calls. * Note that this does not clear the render buffers i.e. no glClear() calls * are made by either of these methods. It's up to the caller to clear the * buffers if needed. */ bool Start(int width, int height); bool StartNonOrtho(int width, int height); ///@} /** * Set up ortho viewport with scissor, lighting, blend, and depth * disabled. The method affects the current bound FBO. */ void InitializeViewport(int width, int height); ///@{ // activate deactivate draw/read buffers (color buffers) void ActivateDrawBuffers(unsigned int n); void ActivateDrawBuffers(unsigned int* ids, int n); void ActivateDrawBuffer(unsigned int id); void ActivateReadBuffer(unsigned int id); void ActivateBuffer(unsigned int id) { this->ActivateDrawBuffer(id); this->ActivateReadBuffer(id); } void DeactivateDrawBuffers(); void DeactivateReadBuffer(); ///@} vtkGetMacro(ActiveReadBuffer, unsigned int); unsigned int GetActiveDrawBuffer(unsigned int id); /** * Renders a quad at the given location with pixel coordinates. This method * is provided as a convenience, since we often render quads in a FBO. * \pre positive_minX: minX>=0 * \pre increasing_x: minX<=maxX * \pre valid_maxX: maxX=0 * \pre increasing_y: minY<=maxY * \pre valid_maxY: maxYGetClassName() << " (" << this << "): returning LastSize pointer " << this->LastSize); return this->LastSize; } void GetLastSize(int& _arg1, int& _arg2) override { _arg1 = this->LastSize[0]; _arg2 = this->LastSize[1]; vtkDebugMacro(<< this->GetClassName() << " (" << this << "): returning LastSize (" << _arg1 << "," << _arg2 << ")"); } void GetLastSize(int _arg[2]) override { this->GetLastSize(_arg[0], _arg[1]); } ///@} /** * Returns if the context supports the required extensions. * Extension will be loaded when the context is set. */ static bool IsSupported(vtkOpenGLRenderWindow*) { return true; } /** * Validate the current FBO configuration (attachments, formats, etc) * prints detected errors to vtkErrorMacro. */ int CheckFrameBufferStatus(unsigned int mode); /** * Deactivate and UnBind */ virtual void ReleaseGraphicsResources(vtkWindow* win); /** * Validate the current FBO configuration (attachments, formats, etc) * return false if the FBO is incomplete. Assigns description a literal * containing a description of the status. * Low level api. */ static bool GetFrameBufferStatus(unsigned int mode, const char*& desc); vtkGetMacro(FBOIndex, unsigned int); /** * Copy from the currently bound READ FBO to the currently * bound DRAW FBO. The method is static so that one doesn't * need to ccreate an instance when transferring between attachments * in the default FBO. */ static int Blit( const int srcExt[4], const int destExt[4], unsigned int bits, unsigned int mapping); /** * Download data from the read color attachment of the currently * bound FBO into the returned PBO. The PBO must be free'd when * you are finished with it. The number of components in the * PBO is the same as in the name of the specific download function. * When downloading a single color channel, the channel must be * identified by index, 1->red, 2->green, 3-> blue. */ vtkPixelBufferObject* DownloadColor1(int extent[4], int vtkType, int channel); vtkPixelBufferObject* DownloadColor3(int extent[4], int vtkType); vtkPixelBufferObject* DownloadColor4(int extent[4], int vtkType); /** * Download data from the depth attachment of the currently * bound FBO. The returned PBO must be Delete'd by the caller. * The returned PBO has one component. */ vtkPixelBufferObject* DownloadDepth(int extent[4], int vtkType); /** * Download data from the read buffer of the current FBO. These * are low level methods. In the static variant a PBO must be * passed in since we don't have access to a context. The static * method is provided so that one may download from the default * FBO. */ vtkPixelBufferObject* Download( int extent[4], int vtkType, int nComps, int oglType, int oglFormat); static void Download( int extent[4], int vtkType, int nComps, int oglType, int oglFormat, vtkPixelBufferObject* pbo); // returns the mode values for draw/read/both // Can be used in cases where you do not // want to have OpenGL code mixed in. static unsigned int GetDrawMode(); static unsigned int GetReadMode(); static unsigned int GetBothMode(); /** * Resize all FO attachments */ void Resize(int width, int height); int GetMultiSamples(); protected: /** * Attach a specific buffer */ void AttachColorBuffer(unsigned int index); void AttachDepthBuffer(); /** * Load all necessary extensions. */ static bool LoadRequiredExtensions(vtkOpenGLRenderWindow*) { return true; } vtkGenericOpenGLResourceFreeCallback* ResourceCallback; // gen buffer (occurs when context is set) void CreateFBO(); // delete buffer (occurs during destruction or context switch) void DestroyFBO(); // detach and delete our reference(s) void DestroyDepthBuffer(vtkWindow* win); void DestroyColorBuffers(vtkWindow* win); // glDrawBuffers void ActivateBuffers(); // examine attachments to see if they have the same size void UpdateSize(); /** * Display all the attachments of the current framebuffer object. */ void DisplayFrameBufferAttachments(); /** * Display a given attachment for the current framebuffer object. */ void DisplayFrameBufferAttachment(unsigned int uattachment); /** * Display the draw buffers. */ void DisplayDrawBuffers(); /** * Display the read buffer. */ void DisplayReadBuffer(); /** * Display any buffer (convert value into string). */ void DisplayBuffer(int value); /** * Given a vtk type get a compatible open gl type. */ int GetOpenGLType(int vtkType); vtkOpenGLFramebufferObject(); ~vtkOpenGLFramebufferObject() override; vtkOpenGLRenderWindow* Context; unsigned int FBOIndex; bool DrawBindingSaved; bool ReadBindingSaved; bool DrawBufferSaved; bool ReadBufferSaved; int LastSize[2]; std::vector ActiveBuffers; unsigned int ActiveReadBuffer; vtkFOInfo* DepthBuffer; std::map ColorBuffers; private: vtkOpenGLFramebufferObject(const vtkOpenGLFramebufferObject&) = delete; void operator=(const vtkOpenGLFramebufferObject&) = delete; }; #endif