/*========================================================================= Program: Visualization Toolkit Module: vtkFrameBufferObject.cxx 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. =========================================================================*/ #include "vtkFrameBufferObject.h" #include "vtkObjectFactory.h" #include "vtkOpenGLRenderWindow.h" #include "vtkTextureObject.h" #include "vtkRenderbuffer.h" #include "vtkPixelBufferObject.h" #include "vtkOpenGLExtensionManager.h" #include "vtkOpenGLError.h" #include "vtkgl.h" #include #include using std::vector; // #define VTK_FBO_DEBUG // display info on RenderQuad() //---------------------------------------------------------------------------- vtkStandardNewMacro(vtkFrameBufferObject); //---------------------------------------------------------------------------- vtkFrameBufferObject::vtkFrameBufferObject() { this->ColorBuffersDirty = true; this->DepthBufferNeeded = true; this->FBOIndex = 0; this->PreviousFBOIndex = -1; this->DepthBuffer = 0; this->LastSize[0] = this->LastSize[1] = -1; this->SetActiveBuffer(0); this->NumberOfRenderTargets = 1; } //---------------------------------------------------------------------------- vtkFrameBufferObject::~vtkFrameBufferObject() { this->DestroyFBO(); this->DestroyDepthBuffer(); this->DestroyColorBuffers(); } //---------------------------------------------------------------------------- void vtkFrameBufferObject::CreateFBO() { this->FBOIndex=0; GLuint temp; vtkgl::GenFramebuffersEXT(1,&temp); vtkOpenGLCheckErrorMacro("failed at glGenFramebuffers"); this->FBOIndex=temp; } //---------------------------------------------------------------------------- void vtkFrameBufferObject::DestroyFBO() { // because we don't hold a reference to the render // context we don't have any control on when it is // destroyed. In fact it may be destroyed before // we are(eg smart pointers), in which case we should // do nothing. if (this->Context && (this->FBOIndex!=0)) { GLuint fbo=static_cast(this->FBOIndex); vtkgl::DeleteFramebuffersEXT(1,&fbo); vtkOpenGLCheckErrorMacro("failed at glDeleteFramebuffers"); this->FBOIndex=0; } } //---------------------------------------------------------------------------- bool vtkFrameBufferObject::IsSupported(vtkRenderWindow *win) { vtkOpenGLRenderWindow *renWin=vtkOpenGLRenderWindow::SafeDownCast(win); if (renWin) { vtkOpenGLExtensionManager *mgr=renWin->GetExtensionManager(); bool gl12 = mgr->ExtensionSupported("GL_VERSION_1_2")==1; bool tex3D = gl12 || mgr->ExtensionSupported("GL_EXT_texture3D"); bool gl14 = mgr->ExtensionSupported("GL_VERSION_1_4")==1; bool depthTex = gl14 || mgr->ExtensionSupported("GL_ARB_depth_texture")==1; bool gl20 = mgr->ExtensionSupported("GL_VERSION_2_0")==1; bool drawBufs = gl20 || mgr->ExtensionSupported("GL_ARB_draw_buffers")==1; bool fbo = mgr->ExtensionSupported("GL_EXT_framebuffer_object")==1; bool fboBlit = mgr->ExtensionSupported("GL_EXT_framebuffer_blit")==1; // On Mesa 8.0.4 reporting OpenGL 1.4 with renderer // "Mesa DRI Intel(R) 945GME" shader fails to compile // "gl_FragData[1] = ..." with the error // 0:46(15): error: array index must be < 1 // Mesa 7 with renderer "Software Rasterizer // has a bug GL_ARB_draw_buffers that leaves the FBO // perpetually incomplete. bool driver = !(mgr->DriverIsMesa() && (mgr->DriverGLVersionIs(1,4) || (mgr->DriverVersionIs(7) && (mgr->DriverGLRendererIs("Software Rasterizer") || mgr->DriverGLRendererIs("Mesa X11"))))); return tex3D && depthTex && drawBufs && fbo && fboBlit && driver; } return false; } //---------------------------------------------------------------------------- bool vtkFrameBufferObject::LoadRequiredExtensions(vtkRenderWindow *win) { vtkOpenGLRenderWindow *oglRenWin = vtkOpenGLRenderWindow::SafeDownCast(win); vtkOpenGLExtensionManager *mgr = oglRenWin->GetExtensionManager(); bool gl12 = mgr->ExtensionSupported("GL_VERSION_1_2")==1; bool tex3D = gl12 || mgr->ExtensionSupported("GL_EXT_texture3D"); bool gl14 = mgr->ExtensionSupported("GL_VERSION_1_4")==1; bool depthTex = gl14 || mgr->ExtensionSupported("GL_ARB_depth_texture")==1; bool gl20 = mgr->ExtensionSupported("GL_VERSION_2_0")==1; bool drawBufs = gl20 || mgr->ExtensionSupported("GL_ARB_draw_buffers"); bool fbo = mgr->ExtensionSupported("GL_EXT_framebuffer_object")==1; bool fboBlit = mgr->ExtensionSupported("GL_EXT_framebuffer_blit")==1; bool supported = tex3D && depthTex && drawBufs && fbo && fboBlit; if(supported) { if(gl12) { mgr->LoadSupportedExtension("GL_VERSION_1_2"); } else { mgr->LoadCorePromotedExtension("GL_EXT_texture3D"); } if(gl14) { mgr->LoadSupportedExtension("GL_VERSION_1_4"); } else { mgr->LoadCorePromotedExtension("GL_ARB_depth_texture"); } if(gl20) { mgr->LoadSupportedExtension("GL_VERSION_2_0"); } else { mgr->LoadCorePromotedExtension("GL_ARB_draw_buffers"); } mgr->LoadSupportedExtension("GL_EXT_framebuffer_object"); mgr->LoadSupportedExtension("GL_EXT_framebuffer_blit"); } return supported; } //---------------------------------------------------------------------------- void vtkFrameBufferObject::SetContext(vtkRenderWindow *renWin) { // avoid pointless re-assignment if (this->Context==renWin) { return; } // free previous resources this->DestroyDepthBuffer(); this->DestroyColorBuffers(); this->DestroyFBO(); this->Context = NULL; this->Modified(); // all done if assigned null if (!renWin) { return; } // check for support vtkOpenGLRenderWindow *context = vtkOpenGLRenderWindow::SafeDownCast(renWin); if ( !context || !this->LoadRequiredExtensions(renWin)) { vtkErrorMacro("Context does not support the required extensions"); return; } // initialize this->Context=renWin; this->Context->MakeCurrent(); this->CreateFBO(); } //---------------------------------------------------------------------------- vtkRenderWindow *vtkFrameBufferObject::GetContext() { return this->Context; } //---------------------------------------------------------------------------- bool vtkFrameBufferObject::StartNonOrtho(int width, int height, bool shaderSupportsTextureInt) { this->Bind(); // If width/height changed since last render, we need to resize the // buffers. if (this->LastSize[0] != width || this->LastSize[1] != height || (this->DepthBuffer && !this->DepthBufferNeeded) || (this->DepthBufferNeeded && !this->DepthBuffer)) { this->DestroyDepthBuffer(); this->DestroyColorBuffers(); } if (this->LastSize[0] != width || this->LastSize[1] != height || this->ColorBuffersDirty || this->DepthBufferNeeded) { this->CreateDepthBuffer( width, height, vtkgl::DRAW_FRAMEBUFFER_EXT); this->CreateColorBuffers( width, height, vtkgl::DRAW_FRAMEBUFFER_EXT, shaderSupportsTextureInt); } this->LastSize[0] = width; this->LastSize[1] = height; this->ActivateBuffers(); GLenum status = vtkgl::CheckFramebufferStatusEXT(vtkgl::FRAMEBUFFER_EXT); if (status != vtkgl::FRAMEBUFFER_COMPLETE_EXT) { vtkErrorMacro("Frame buffer object was not initialized correctly."); this->CheckFrameBufferStatus(vtkgl::FRAMEBUFFER_EXT); this->DisplayFrameBufferAttachments(); this->DisplayDrawBuffers(); this->DisplayReadBuffer(); return false; } return true; } //---------------------------------------------------------------------------- bool vtkFrameBufferObject::Start(int width, int height, bool shaderSupportsTextureInt) { if (!this->StartNonOrtho(width, height, shaderSupportsTextureInt)) { return false; } glDisable(GL_ALPHA_TEST); glDisable(GL_BLEND); glDisable(GL_DEPTH_TEST); glDisable(GL_LIGHTING); glDisable(GL_SCISSOR_TEST); // Viewport transformation for 1:1 'pixel=texel=data' mapping. // Note this note enough for 1:1 mapping, because depending on the // primitive displayed (point,line,polygon), the rasterization rules // are different. glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0.0, width, 0.0, height, -1, 1); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glViewport(0, 0, width, height); return true; } //---------------------------------------------------------------------------- void vtkFrameBufferObject::SetActiveBuffers(int num, unsigned int indices[]) { this->ActiveBuffers.clear(); for (int cc=0; cc < num; cc++) { this->ActiveBuffers.push_back(indices[cc]); } this->Modified(); } //---------------------------------------------------------------------------- void vtkFrameBufferObject::ActivateBuffers() { GLint maxbuffers; glGetIntegerv(vtkgl::MAX_DRAW_BUFFERS, &maxbuffers); GLenum *buffers = new GLenum[maxbuffers]; GLint count=0; for(unsigned int cc=0; cc < this->ActiveBuffers.size() && count < maxbuffers; cc++) { buffers[cc] = vtkgl::COLOR_ATTACHMENT0_EXT + this->ActiveBuffers[cc]; count++; } vtkgl::DrawBuffers(count, buffers); vtkOpenGLCheckErrorMacro("failed at glDrawBuffers"); delete[] buffers; } //---------------------------------------------------------------------------- void vtkFrameBufferObject::Bind() { if(this->FBOIndex!=0 && this->PreviousFBOIndex==-1) { this->Context->MakeCurrent(); GLint framebufferBinding; glGetIntegerv(vtkgl::FRAMEBUFFER_BINDING_EXT,&framebufferBinding); this->PreviousFBOIndex=framebufferBinding; vtkgl::BindFramebufferEXT(vtkgl::FRAMEBUFFER_EXT, this->FBOIndex); } } //---------------------------------------------------------------------------- void vtkFrameBufferObject::UnBind() { if (this->FBOIndex!=0 && this->PreviousFBOIndex!=-1) { vtkgl::BindFramebufferEXT(vtkgl::FRAMEBUFFER_EXT,this->PreviousFBOIndex); this->PreviousFBOIndex=-1; } } //---------------------------------------------------------------------------- void vtkFrameBufferObject::CreateDepthBuffer( int width, int height, unsigned int mode) { this->DestroyDepthBuffer(); if (this->UserDepthBuffer) { // Attach the depth buffer to the FBO. vtkgl::FramebufferTexture2DEXT( (GLenum)mode, vtkgl::DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, this->UserDepthBuffer->GetHandle(), 0); vtkOpenGLCheckErrorMacro("failed at glFramebufferTexture2D"); } else if (this->DepthBufferNeeded) { // Create render buffers which are independent of render targets. GLuint temp; vtkgl::GenRenderbuffersEXT(1, &temp); vtkOpenGLCheckErrorMacro("failed at glGenRenderbuffers"); this->DepthBuffer = temp; vtkgl::BindRenderbufferEXT(vtkgl::RENDERBUFFER_EXT, this->DepthBuffer); vtkOpenGLCheckErrorMacro("failed at glBindRenderbuffer"); // Assign storage to this depth buffer. vtkgl::RenderbufferStorageEXT( vtkgl::RENDERBUFFER_EXT, vtkgl::DEPTH_COMPONENT24, width, height); vtkOpenGLCheckErrorMacro("failed at glRenderbufferStorage"); // Attach the depth buffer to the FBO. vtkgl::FramebufferRenderbufferEXT( (GLenum)mode, vtkgl::DEPTH_ATTACHMENT_EXT, vtkgl::RENDERBUFFER_EXT, this->DepthBuffer); vtkOpenGLCheckErrorMacro("failed at glFramebufferRenderbuffer"); } } //---------------------------------------------------------------------------- void vtkFrameBufferObject::DestroyDepthBuffer() { // because we don't hold a reference to the render // context we don't have any control on when it is // destroyed. In fact it may be destroyed before // we are(eg smart pointers), in which case we should // do nothing. if(this->Context && this->DepthBuffer) { GLuint temp = static_cast(this->DepthBuffer); vtkgl::DeleteRenderbuffersEXT(1, &temp); vtkOpenGLCheckErrorMacro("failed at glDeleteRenderbuffers"); this->DepthBuffer = 0; } } //---------------------------------------------------------------------------- void vtkFrameBufferObject::DestroyColorBuffers() { this->ColorBuffers.clear(); this->ColorBuffersDirty = true; } //---------------------------------------------------------------------------- void vtkFrameBufferObject::CreateColorBuffers( int iwidth, int iheight, unsigned int mode, bool shaderSupportsTextureInt) { unsigned int width = static_cast(iwidth); unsigned int height = static_cast (iheight); unsigned int nUserColorBuffers = static_cast(this->UserColorBuffers.size()); this->ColorBuffers.resize(this->NumberOfRenderTargets); unsigned int cc; for (cc=0; ccNumberOfRenderTargets && ccUserColorBuffers[cc]; if (userBuffer) { assert(userBuffer->GetWidth()==width); assert(userBuffer->GetHeight()==height); this->ColorBuffers[cc] = this->UserColorBuffers[cc]; } } for (cc=0; cc < this->NumberOfRenderTargets; cc++) { vtkSmartPointer colorBuffer = this->ColorBuffers[cc]; if (!colorBuffer) { // create a new color buffer for the user. colorBuffer = vtkSmartPointer::New(); colorBuffer->SetContext(this->Context); colorBuffer->SetMinificationFilter(vtkTextureObject::Nearest); colorBuffer->SetLinearMagnification(false); colorBuffer->SetWrapS(vtkTextureObject::Clamp); colorBuffer->SetWrapT(vtkTextureObject::Clamp); if (!colorBuffer->Create2D( width, height, 4, VTK_UNSIGNED_CHAR, shaderSupportsTextureInt)) { vtkErrorMacro("Failed to create texture for color buffer."); return; } } // attach the buffer if (colorBuffer->GetNumberOfDimensions() == 2) { vtkgl::FramebufferTexture2DEXT( (GLenum)mode, vtkgl::COLOR_ATTACHMENT0_EXT+cc, GL_TEXTURE_2D, colorBuffer->GetHandle(), 0); vtkOpenGLCheckErrorMacro("failed at glFramebufferTexture2D"); } else if (colorBuffer->GetNumberOfDimensions() == 3) { assert(this->UserZSlices[cc]GetDepth()); vtkgl::FramebufferTexture3DEXT( (GLenum)mode, vtkgl::COLOR_ATTACHMENT0_EXT+cc, vtkgl::TEXTURE_3D, colorBuffer->GetHandle(), 0, this->UserZSlices[cc]); vtkOpenGLCheckErrorMacro("failed at glFramebufferTexture3D"); } this->ColorBuffers[cc] = colorBuffer; } // unbind the remainder unsigned int attachments=this->GetMaximumNumberOfRenderTargets(); while(ccColorBuffersDirty = false; } //---------------------------------------------------------------------------- unsigned int vtkFrameBufferObject::GetMaximumNumberOfActiveTargets() { unsigned int result = 0; if (this->Context) { GLint maxbuffers; glGetIntegerv(vtkgl::MAX_DRAW_BUFFERS, &maxbuffers); result = static_cast(maxbuffers); } return result; } //---------------------------------------------------------------------------- unsigned int vtkFrameBufferObject::GetMaximumNumberOfRenderTargets() { unsigned int result = 0; if (this->Context) { GLint maxColorAttachments; glGetIntegerv(vtkgl::MAX_COLOR_ATTACHMENTS_EXT,&maxColorAttachments); result = static_cast(maxColorAttachments); } return result; } //---------------------------------------------------------------------------- void vtkFrameBufferObject::SetNumberOfRenderTargets(unsigned int num) { assert(num>0); this->NumberOfRenderTargets = num; this->ColorBuffersDirty = true; } //---------------------------------------------------------------------------- void vtkFrameBufferObject::SetDepthBuffer(vtkTextureObject* tex) { if (this->UserDepthBuffer != tex) { this->UserDepthBuffer = tex; this->DepthBufferDirty = true; } } //---------------------------------------------------------------------------- void vtkFrameBufferObject::RemoveDepthBuffer() { this->SetDepthBuffer(0); } //---------------------------------------------------------------------------- void vtkFrameBufferObject::SetColorBuffer( unsigned int index, vtkTextureObject* tex, unsigned int zslice/*=0*/) { if (this->UserColorBuffers.size() <= index) { this->UserColorBuffers.resize(index+1); this->UserZSlices.resize(index+1); } if (this->UserColorBuffers[index] != tex || this->UserZSlices[index] != zslice) { this->UserColorBuffers[index] = tex; this->UserZSlices[index] = zslice; this->ColorBuffersDirty = true; } } //---------------------------------------------------------------------------- vtkTextureObject* vtkFrameBufferObject::GetColorBuffer(unsigned int index) { assert(this->UserColorBuffers.size()>index); return this->UserColorBuffers[index]; } //---------------------------------------------------------------------------- void vtkFrameBufferObject::RemoveColorBuffer(unsigned int index) { if (index < this->UserColorBuffers.size()) { this->UserColorBuffers[index] = 0; this->UserZSlices[index] = 0; this->ColorBuffersDirty = true; } } //---------------------------------------------------------------------------- void vtkFrameBufferObject::RemoveAllColorBuffers() { this->UserColorBuffers.clear(); this->UserZSlices.clear(); this->ColorBuffersDirty = true; } // ---------------------------------------------------------------------------- // Description: // Display all the attachments of the current framebuffer object. void vtkFrameBufferObject::DisplayFrameBufferAttachments() { GLint framebufferBinding; glGetIntegerv(vtkgl::FRAMEBUFFER_BINDING_EXT,&framebufferBinding); vtkOpenGLCheckErrorMacro("after getting FRAMEBUFFER_BINDING_EXT"); if(framebufferBinding==0) { cout<<"Current framebuffer is bind to the system one"<DisplayFrameBufferAttachment(vtkgl::COLOR_ATTACHMENT0_EXT+i); ++i; } cout<<"depth attachement :"<DisplayFrameBufferAttachment(vtkgl::DEPTH_ATTACHMENT_EXT); cout<<"stencil attachement :"<DisplayFrameBufferAttachment(vtkgl::STENCIL_ATTACHMENT_EXT); } } // ---------------------------------------------------------------------------- // Description: // Display a given attachment for the current framebuffer object. void vtkFrameBufferObject::DisplayFrameBufferAttachment( unsigned int uattachment) { GLenum attachment=static_cast(uattachment); GLint params; vtkgl::GetFramebufferAttachmentParameterivEXT( vtkgl::FRAMEBUFFER_EXT,attachment, vtkgl::FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT,¶ms); vtkOpenGLCheckErrorMacro("after getting FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT"); switch(params) { case GL_NONE: cout<<" this attachment is empty"<PrintError("after getting FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT"); cout<<" this attachment is a renderbuffer with name: "<PrintError( // "after getting binding the current RENDERBUFFER_EXT to params"); vtkgl::GetRenderbufferParameterivEXT(vtkgl::RENDERBUFFER_EXT, vtkgl::RENDERBUFFER_WIDTH_EXT, ¶ms); // this->PrintError("after getting RENDERBUFFER_WIDTH_EXT"); cout<<" renderbuffer width="<PrintError("after getting RENDERBUFFER_HEIGHT_EXT"); cout<<" renderbuffer height="<PrintError("after getting RENDERBUFFER_INTERNAL_FORMAT_EXT"); cout<<" renderbuffer internal format=0x"<< std::hex<PrintError("after getting RENDERBUFFER_RED_SIZE_EXT"); cout<<" renderbuffer actual resolution for the red component="<PrintError("after getting RENDERBUFFER_GREEN_SIZE_EXT"); cout<<" renderbuffer actual resolution for the green component="<PrintError("after getting RENDERBUFFER_BLUE_SIZE_EXT"); cout<<" renderbuffer actual resolution for the blue component="<PrintError("after getting RENDERBUFFER_ALPHA_SIZE_EXT"); cout<<" renderbuffer actual resolution for the alpha component="<PrintError("after getting RENDERBUFFER_DEPTH_SIZE_EXT"); cout<<" renderbuffer actual resolution for the depth component="<PrintError("after getting RENDERBUFFER_STENCIL_SIZE_EXT"); cout<<" renderbuffer actual resolution for the stencil component=" <1) { cout<<"s"; } cout<<". "<=GL_AUX0) { int b=value-GL_AUX0; GLint ivalue; glGetIntegerv(GL_AUX_BUFFERS,&ivalue); if(b=0); assert("pre increasing_x" && minX<=maxX); assert("pre valid_maxX" && maxXLastSize[0]); assert("pre positive_minY" && minY>=0); assert("pre increasing_y" && minY<=maxY); assert("pre valid_maxY" && maxYLastSize[1]); #ifdef VTK_FBO_DEBUG cout<<"render quad: minX="<Superclass::PrintSelf(os, indent); os << indent << "LastSize : " << this->LastSize[0] << this->LastSize[1] << endl; os << indent << "DepthBufferNeeded:"; if(this->DepthBufferNeeded) { os << "true"; } else { os << "false"; } os <NumberOfRenderTargets << endl; } // Description: // Common switch for parsing fbo status return. #define vtkFBOStrErrorMacro(status, str, ok) \ ok = false; \ switch(status) \ { \ case vtkgl::FRAMEBUFFER_COMPLETE_EXT: \ str = "FBO complete"; \ ok = true; \ break; \ case vtkgl::FRAMEBUFFER_UNSUPPORTED_EXT: \ str = "FRAMEBUFFER_UNSUPPORTED"; \ break; \ case vtkgl::FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: \ str = "FRAMEBUFFER_INCOMPLETE_ATTACHMENT"; \ break; \ case vtkgl::FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT: \ str = "FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"; \ break; \ case vtkgl::FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT: \ str = "FRAMEBUFFER_INCOMPLETE_DIMENSIONS"; \ break; \ case vtkgl::FRAMEBUFFER_INCOMPLETE_FORMATS_EXT: \ str = "FRAMEBUFFER_INCOMPLETE_FORMATS"; \ break; \ case vtkgl::FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT: \ str = "FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER"; \ break; \ case vtkgl::FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT: \ str = "FRAMEBUFFER_INCOMPLETE_READ_BUFFER"; \ break; \ default: \ str = "Unknown status"; \ } // ---------------------------------------------------------------------------- int vtkFrameBufferObject::CheckFrameBufferStatus(unsigned int mode) { bool ok; const char *desc = "error"; GLenum status = vtkgl::CheckFramebufferStatusEXT((GLenum)mode); vtkOpenGLCheckErrorMacro("failed at glCheckFramebufferStatus"); vtkFBOStrErrorMacro(status, desc, ok); if (!ok) { vtkErrorMacro("The framebuffer is incomplete : " << desc); return 0; } return 1; }