/* ----------------------------------------------------------------------------- This source file is part of OGRE (Object-oriented Graphics Rendering Engine) For the latest info, see http://www.ogre3d.org/ Copyright (c) 2000-2005 The OGRE Team 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. ----------------------------------------------------------------------------- */ #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 256 /// Stencil and depth formats to be tried GLenum stencilFormats[]={ GL_NONE, // No stencil GL_STENCIL_INDEX1_EXT, GL_STENCIL_INDEX4_EXT, GL_STENCIL_INDEX8_EXT, GL_STENCIL_INDEX16_EXT }; size_t stencilBits[] = { 0, 1, 4, 8, 16 }; #define STENCILFORMAT_COUNT (sizeof(stencilFormats)/sizeof(GLenum)) GLenum depthFormats[]={ GL_NONE, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT24, // Prefer 24 bit depth GL_DEPTH_COMPONENT32, GL_DEPTH24_STENCIL8_EXT // packed depth / stencil }; 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, stencilRB; 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_NONE, 0); glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_NONE, 0); glDeleteRenderbuffersEXT(1, &depthRB); glDeleteRenderbuffersEXT(1, &stencilRB); 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; GLenum target = GL_TEXTURE_2D; 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::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; } } } }