| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505 |
- /*
- -----------------------------------------------------------------------------
- This source file is part of OGRE
- (Object-oriented Graphics Rendering Engine)
- For the latest info, see http://www.ogre3d.org/
- Copyright (c) 2000-2011 Torus Knot Software Ltd
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- THE SOFTWARE.
- -----------------------------------------------------------------------------
- */
- #include "CmGLFBORenderTexture.h"
- #include "CmGLPixelFormat.h"
- #include "OgreStringConverter.h"
- #include "CmGLHardwarePixelBuffer.h"
- #include "CmGLFBOMultiRenderTarget.h"
- namespace CamelotEngine {
- //-----------------------------------------------------------------------------
- GLFBORenderTexture::GLFBORenderTexture(GLFBOManager *manager, const String &name,
- const GLSurfaceDesc &target, bool writeGamma, UINT32 fsaa):
- GLRenderTexture(name, target, writeGamma, fsaa),
- mFB(manager, fsaa)
- {
- // 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<GLFrameBufferObject **>(pData) = &mFB;
- }
- else if (name == "GL_FBOID")
- {
- *static_cast<GLuint*>(pData) = mFB.getGLFBOID();
- }
- else if (name == "GL_MULTISAMPLEFBOID")
- {
- *static_cast<GLuint*>(pData) = mFB.getGLMultisampleFBOID();
- }
- }
- void GLFBORenderTexture::swapBuffers(bool waitForVSync)
- {
- mFB.swapBuffers();
- }
-
- /// 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())
- {
- // TODO LOG PORT - Log this somewhere
- //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 = 0;
- 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 = 0, tid = 0;
- GLint old_drawbuffer = 0, old_readbuffer = 0;
- GLenum target = GL_TEXTURE_2D;
- glGetIntegerv (GL_DRAW_BUFFER, &old_drawbuffer);
- glGetIntegerv (GL_READ_BUFFER, &old_readbuffer);
- for(size_t x=0; x<PF_COUNT; ++x)
- {
- mProps[x].valid = false;
- // Fetch GL format token
- GLenum fmt = GLPixelUtil::getGLInternalFormat((PixelFormat)x);
- if(fmt == GL_NONE && x!=0)
- continue;
- // No test for compressed formats
- if(PixelUtil::isCompressed((PixelFormat)x))
- continue;
- // Buggy ATI cards *crash* on non-RGB(A) formats
- int depths[4];
- PixelUtil::getBitDepths((PixelFormat)x, depths);
- if(fmt!=GL_NONE && mATIMode && (!depths[0] || !depths[1] || !depths[2]))
- continue;
- // Create and attach framebuffer
- glGenFramebuffersEXT(1, &fb);
- glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb);
- if (fmt!=GL_NONE)
- {
- // Create and attach texture
- glGenTextures(1, &tid);
- glBindTexture(target, tid);
-
- // Set some default parameters so it won't fail on NVidia cards
- if (GLEW_VERSION_1_2)
- glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, 0);
- glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-
- glTexImage2D(target, 0, fmt, PROBE_SIZE, PROBE_SIZE, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
- glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
- target, tid, 0);
- }
- else
- {
- // Draw to nowhere -- stencil/depth only
- glDrawBuffer(GL_NONE);
- glReadBuffer(GL_NONE);
- }
- // Check status
- GLuint status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
- // Ignore status in case of fmt==GL_NONE, because no implementation will accept
- // a buffer without *any* attachment. Buffers with only stencil and depth attachment
- // might still be supported, so we must continue probing.
- if(fmt == GL_NONE || status == GL_FRAMEBUFFER_COMPLETE_EXT)
- {
- mProps[x].valid = true;
- StringUtil::StrStreamType str;
- str << "FBO " << PixelUtil::getFormatName((PixelFormat)x)
- << " depth/stencil support: ";
- // For each depth/stencil formats
- for (size_t depth = 0; depth < DEPTHFORMAT_COUNT; ++depth)
- {
- if (depthFormats[depth] != GL_DEPTH24_STENCIL8_EXT)
- {
- // General depth/stencil combination
- for (size_t stencil = 0; stencil < STENCILFORMAT_COUNT; ++stencil)
- {
- //StringUtil::StrStreamType l;
- //l << "Trying " << PixelUtil::getFormatName((PixelFormat)x)
- // << " D" << depthBits[depth]
- // << "S" << stencilBits[stencil];
- //LogManager::getSingleton().logMessage(l.str());
- if (_tryFormat(depthFormats[depth], stencilFormats[stencil]))
- {
- /// Add mode to allowed modes
- str << "D" << depthBits[depth] << "S" << stencilBits[stencil] << " ";
- FormatProperties::Mode mode;
- mode.depth = depth;
- mode.stencil = stencil;
- mProps[x].modes.push_back(mode);
- }
- }
- }
- else
- {
- // Packed depth/stencil format
- // #if OGRE_PLATFORM == OGRE_PLATFORM_LINUX
- // It now seems as if this workaround now *breaks* nvidia cards on Linux with the 169.12 drivers on Linux
- #if 0
- // Only query packed depth/stencil formats for 32-bit
- // non-floating point formats (ie not R32!)
- // Linux nVidia driver segfaults if you query others
- if (PixelUtil::getNumElemBits((PixelFormat)x) != 32 ||
- PixelUtil::isFloatingPoint((PixelFormat)x))
- {
- continue;
- }
- #endif
- if (_tryPackedFormat(depthFormats[depth]))
- {
- /// Add mode to allowed modes
- str << "Packed-D" << depthBits[depth] << "S" << 8 << " ";
- FormatProperties::Mode mode;
- mode.depth = depth;
- mode.stencil = 0; // unuse
- mProps[x].modes.push_back(mode);
- }
- }
- }
- }
- // Delete texture and framebuffer
- glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
- glDeleteFramebuffersEXT(1, &fb);
-
- // Workaround for NVIDIA / Linux 169.21 driver problem
- // see http://www.ogre3d.org/phpBB2/viewtopic.php?t=38037&start=25
- glFinish();
-
- if (fmt!=GL_NONE)
- glDeleteTextures(1, &tid);
- }
- // It seems a bug in nVidia driver: glBindFramebufferEXT should restore
- // draw and read buffers, but in some unclear circumstances it won't.
- glDrawBuffer(old_drawbuffer);
- glReadBuffer(old_readbuffer);
- String fmtstring = "";
- for(size_t x=0; x<PF_COUNT; ++x)
- {
- if(mProps[x].valid)
- fmtstring += PixelUtil::getFormatName((PixelFormat)x)+" ";
- }
- }
- void GLFBOManager::getBestDepthStencil(GLenum internalFormat, GLenum *depthFormat, GLenum *stencilFormat)
- {
- const FormatProperties &props = mProps[internalFormat];
- /// Decide what stencil and depth formats to use
- /// [best supported for internal format]
- size_t bestmode=0;
- int bestscore=-1;
- for(size_t mode=0; mode<props.modes.size(); mode++)
- {
- #if 0
- /// Always prefer D24S8
- if(stencilBits[props.modes[mode].stencil]==8 &&
- depthBits[props.modes[mode].depth]==24)
- {
- bestmode = mode;
- break;
- }
- #endif
- int desirability = 0;
- /// Find most desirable mode
- /// desirability == 0 if no depth, no stencil
- /// desirability == 1000...2000 if no depth, stencil
- /// desirability == 2000...3000 if depth, no stencil
- /// desirability == 3000+ if depth and stencil
- /// beyond this, the total numer of bits (stencil+depth) is maximised
- if(props.modes[mode].stencil)
- desirability += 1000;
- if(props.modes[mode].depth)
- desirability += 2000;
- if(depthBits[props.modes[mode].depth]==24) // Prefer 24 bit for now
- desirability += 500;
- if(depthFormats[props.modes[mode].depth]==GL_DEPTH24_STENCIL8_EXT) // Prefer 24/8 packed
- desirability += 5000;
- desirability += stencilBits[props.modes[mode].stencil] + depthBits[props.modes[mode].depth];
-
- if(desirability>bestscore)
- {
- 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, bool writeGamma, UINT32 fsaa)
- {
- GLFBORenderTexture *retval = new GLFBORenderTexture(this, name, target, writeGamma, fsaa);
- return retval;
- }
- MultiRenderTarget *GLFBOManager::createMultiRenderTarget(const String & name)
- {
- return new GLFBOMultiRenderTarget(this, name);
- }
- 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, UINT32 fsaa)
- {
- GLSurfaceDesc retval;
- retval.buffer = 0; // Return 0 buffer if GL_NONE is requested
- if(format != GL_NONE)
- {
- RBFormat key(format, width, height, fsaa);
- RenderBufferMap::iterator it = mRenderBufferMap.find(key);
- if(it != mRenderBufferMap.end())
- {
- retval.buffer = it->second.buffer;
- retval.zoffset = 0;
- retval.numSamples = fsaa;
- // Increase refcount
- ++it->second.refcount;
- }
- else
- {
- // New one
- GLRenderBuffer *rb = new GLRenderBuffer(format, width, height, fsaa);
- mRenderBufferMap[key] = RBRef(rb);
- retval.buffer = rb;
- retval.zoffset = 0;
- retval.numSamples = fsaa;
- }
- }
- //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(), surface.numSamples);
- 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(), surface.numSamples);
- 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;
- }
- }
- }
- }
|