/* ----------------------------------------------------------------------------- This source file is part of OGRE (Object-oriented Graphics Rendering Engine) For the latest info, see http://www.ogre3d.org/ Copyright (c) 2000-2006 Torus Knot Software Ltd Also see acknowledgements in Readme.html This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA, or go to http://www.gnu.org/copyleft/lesser.txt. You may alternatively use this source under the terms of a specific version of the OGRE Unrestricted License provided you have obtained such a license from Torus Knot Software Ltd. ----------------------------------------------------------------------------- */ #include "OgreGLFBORenderTexture.h" #include "OgreGLPixelFormat.h" #include "OgreLogManager.h" #include "OgreStringConverter.h" #include "OgreRoot.h" #include "OgreGLHardwarePixelBuffer.h" #include "OgreGLFBOMultiRenderTarget.h" namespace Ogre { //----------------------------------------------------------------------------- GLFBORenderTexture::GLFBORenderTexture(GLFBOManager *manager, const String &name, const GLSurfaceDesc &target): GLRenderTexture(name, target), mFB(manager) { // Bind target to surface 0 and initialise mFB.bindSurface(0, target); // Get attributes mWidth = mFB.getWidth(); mHeight = mFB.getHeight(); } void GLFBORenderTexture::getCustomAttribute(const String& name, void* pData) { if(name=="FBO") { *static_cast(pData) = &mFB; } } /// Size of probe texture #define PROBE_SIZE 16 /// Stencil and depth formats to be tried static const GLenum stencilFormats[] = { GL_NONE, // No stencil GL_STENCIL_INDEX1_EXT, GL_STENCIL_INDEX4_EXT, GL_STENCIL_INDEX8_EXT, GL_STENCIL_INDEX16_EXT }; static const size_t stencilBits[] = { 0, 1, 4, 8, 16 }; #define STENCILFORMAT_COUNT (sizeof(stencilFormats)/sizeof(GLenum)) static const GLenum depthFormats[] = { GL_NONE, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT24, // Prefer 24 bit depth GL_DEPTH_COMPONENT32, GL_DEPTH24_STENCIL8_EXT // packed depth / stencil }; static const size_t depthBits[] = { 0,16,24,32,24 }; #define DEPTHFORMAT_COUNT (sizeof(depthFormats)/sizeof(GLenum)) GLFBOManager::GLFBOManager(bool atimode): mATIMode(atimode) { detectFBOFormats(); glGenFramebuffersEXT(1, &mTempFBO); } GLFBOManager::~GLFBOManager() { if(!mRenderBufferMap.empty()) { LogManager::getSingleton().logMessage("GL: Warning! GLFBOManager destructor called, but not all renderbuffers were released."); } glDeleteFramebuffersEXT(1, &mTempFBO); } /** Try a certain FBO format, and return the status. Also sets mDepthRB and mStencilRB. @returns true if this combo is supported false if this combo is not supported */ GLuint GLFBOManager::_tryFormat(GLenum depthFormat, GLenum stencilFormat) { GLuint status, depthRB = 0, stencilRB = 0; bool failed = false; // flag on GL errors if(depthFormat != GL_NONE) { /// Generate depth renderbuffer glGenRenderbuffersEXT(1, &depthRB); /// Bind it to FBO glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthRB); /// Allocate storage for depth buffer glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, depthFormat, PROBE_SIZE, PROBE_SIZE); /// Attach depth glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depthRB); } if(stencilFormat != GL_NONE) { /// Generate stencil renderbuffer glGenRenderbuffersEXT(1, &stencilRB); /// Bind it to FBO glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, stencilRB); glGetError(); // NV hack /// Allocate storage for stencil buffer glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, stencilFormat, PROBE_SIZE, PROBE_SIZE); if(glGetError() != GL_NO_ERROR) // NV hack failed = true; /// Attach stencil glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, stencilRB); if(glGetError() != GL_NO_ERROR) // NV hack failed = true; } status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); /// If status is negative, clean up // Detach and destroy glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, 0); glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, 0); if (depthRB) glDeleteRenderbuffersEXT(1, &depthRB); if (stencilRB) glDeleteRenderbuffersEXT(1, &stencilRB); return status == GL_FRAMEBUFFER_COMPLETE_EXT && !failed; } /** Try a certain packed depth/stencil format, and return the status. @returns true if this combo is supported false if this combo is not supported */ bool GLFBOManager::_tryPackedFormat(GLenum packedFormat) { GLuint packedRB; bool failed = false; // flag on GL errors /// Generate renderbuffer glGenRenderbuffersEXT(1, &packedRB); /// Bind it to FBO glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, packedRB); /// Allocate storage for buffer glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, packedFormat, PROBE_SIZE, PROBE_SIZE); glGetError(); // NV hack /// Attach depth glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, packedRB); if(glGetError() != GL_NO_ERROR) // NV hack failed = true; /// Attach stencil glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, packedRB); if(glGetError() != GL_NO_ERROR) // NV hack failed = true; GLuint status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); /// Detach and destroy glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, 0); glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, 0); glDeleteRenderbuffersEXT(1, &packedRB); return status == GL_FRAMEBUFFER_COMPLETE_EXT && !failed; } /** Detect which internal formats are allowed as RTT Also detect what combinations of stencil and depth are allowed with this internal format. */ void GLFBOManager::detectFBOFormats() { // Try all formats, and report which ones work as target GLuint fb, tid; GLint old_drawbuffer, old_readbuffer; GLenum target = GL_TEXTURE_2D; glGetIntegerv (GL_DRAW_BUFFER, &old_drawbuffer); glGetIntegerv (GL_READ_BUFFER, &old_readbuffer); for(size_t x=0; xbestscore) { bestscore = desirability; bestmode = mode; } } *depthFormat = depthFormats[props.modes[bestmode].depth]; *stencilFormat = stencilFormats[props.modes[bestmode].stencil]; } GLFBORenderTexture *GLFBOManager::createRenderTexture(const String &name, const GLSurfaceDesc &target) { GLFBORenderTexture *retval = new GLFBORenderTexture(this, name, target); return retval; } MultiRenderTarget *GLFBOManager::createMultiRenderTarget(const String & name) { return new GLFBOMultiRenderTarget(this, name); } GLFrameBufferObject *GLFBOManager::createFrameBufferObject() { return new GLFrameBufferObject(this); } void GLFBOManager::destroyFrameBufferObject(GLFrameBufferObject * x) { delete x; } void GLFBOManager::bind(RenderTarget *target) { /// Check if the render target is in the rendertarget->FBO map GLFrameBufferObject *fbo = 0; target->getCustomAttribute("FBO", &fbo); if(fbo) fbo->bind(); else // Old style context (window/pbuffer) or copying render texture glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); } GLSurfaceDesc GLFBOManager::requestRenderBuffer(GLenum format, size_t width, size_t height) { GLSurfaceDesc retval; retval.buffer = 0; // Return 0 buffer if GL_NONE is requested if(format != GL_NONE) { RBFormat key(format, width, height); RenderBufferMap::iterator it = mRenderBufferMap.find(key); if(it != mRenderBufferMap.end()) { retval.buffer = it->second.buffer; retval.zoffset = 0; // Increase refcount ++it->second.refcount; } else { // New one GLRenderBuffer *rb = new GLRenderBuffer(format, width, height); mRenderBufferMap[key] = RBRef(rb); retval.buffer = rb; retval.zoffset = 0; } } //std::cerr << "Requested renderbuffer with format " << std::hex << format << std::dec << " of " << width << "x" << height << " :" << retval.buffer << std::endl; return retval; } //----------------------------------------------------------------------- void GLFBOManager::requestRenderBuffer(const GLSurfaceDesc &surface) { if(surface.buffer == 0) return; RBFormat key(surface.buffer->getGLFormat(), surface.buffer->getWidth(), surface.buffer->getHeight()); RenderBufferMap::iterator it = mRenderBufferMap.find(key); assert(it != mRenderBufferMap.end()); if (it != mRenderBufferMap.end()) // Just in case { assert(it->second.buffer == surface.buffer); // Increase refcount ++it->second.refcount; } } //----------------------------------------------------------------------- void GLFBOManager::releaseRenderBuffer(const GLSurfaceDesc &surface) { if(surface.buffer == 0) return; RBFormat key(surface.buffer->getGLFormat(), surface.buffer->getWidth(), surface.buffer->getHeight()); RenderBufferMap::iterator it = mRenderBufferMap.find(key); if(it != mRenderBufferMap.end()) { // Decrease refcount --it->second.refcount; if(it->second.refcount==0) { // If refcount reaches zero, delete buffer and remove from map delete it->second.buffer; mRenderBufferMap.erase(it); //std::cerr << "Destroyed renderbuffer of format " << std::hex << key.format << std::dec // << " of " << key.width << "x" << key.height << std::endl; } } } }