GLES2FBOManager::GLES2FBOManager() : mMaxFSAASamples(0) { detectFBOFormats(); OGRE_CHECK_GL_ERROR(glGenFramebuffers(1, &mTempFBO)); // Check multisampling if supported if(getGLES2RenderSystem()->hasMinGLVersion(3, 0)) { // Check samples supported OGRE_CHECK_GL_ERROR(glGetIntegerv(GL_MAX_SAMPLES_APPLE, &mMaxFSAASamples)); } }
//----------------------------------------------------------------------------- GLES2FrameBufferObject::GLES2FrameBufferObject(GLES2FBOManager *manager, uint fsaa): GLFrameBufferObjectCommon(fsaa), mManager(manager) { #if OGRE_PLATFORM == OGRE_PLATFORM_APPLE_IOS GLint oldfb = 0; OGRE_CHECK_GL_ERROR(glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldfb)); #endif GLES2RenderSystem* rs = getGLES2RenderSystem(); mContext = rs->_getCurrentContext(); // Generate framebuffer object OGRE_CHECK_GL_ERROR(glGenFramebuffers(1, &mFB)); if(rs->getCapabilities()->hasCapability(RSC_DEBUG)) { #if OGRE_PLATFORM == OGRE_PLATFORM_APPLE_IOS OGRE_CHECK_GL_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, mFB)); // to avoid GL_INVALID_OPERATION in glLabelObjectEXT(GL_FRAMEBUFFER,...) on iOS #endif OGRE_CHECK_GL_ERROR(glLabelObjectEXT(GL_FRAMEBUFFER, mFB, 0, ("FBO #" + StringConverter::toString(mFB)).c_str())); } mNumSamples = std::min(mNumSamples, manager->getMaxFSAASamples()); // Will we need a second FBO to do multisampling? if (mNumSamples) { OGRE_CHECK_GL_ERROR(glGenFramebuffers(1, &mMultisampleFB)); if(rs->getCapabilities()->hasCapability(RSC_DEBUG)) { #if OGRE_PLATFORM == OGRE_PLATFORM_APPLE_IOS OGRE_CHECK_GL_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, mMultisampleFB)); // to avoid GL_INVALID_OPERATION in glLabelObjectEXT(GL_FRAMEBUFFER,...) on iOS #endif OGRE_CHECK_GL_ERROR(glLabelObjectEXT(GL_FRAMEBUFFER, mMultisampleFB, 0, ("MSAA FBO #" + StringConverter::toString(mMultisampleFB)).c_str())); } } else { mMultisampleFB = 0; } #if OGRE_PLATFORM == OGRE_PLATFORM_APPLE_IOS OGRE_CHECK_GL_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, oldfb)); #endif }
void GLES2FrameBufferObject::initialise() { GLES2RenderSystem* rs = getGLES2RenderSystem(); assert(mContext == rs->_getCurrentContext()); // Release depth and stencil, if they were bound mManager->releaseRenderBuffer(mDepth); mManager->releaseRenderBuffer(mStencil); mManager->releaseRenderBuffer(mMultisampleColourBuffer); // First buffer must be bound if(!mColour[0].buffer) { OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Attachment 0 must have surface attached", "GLES2FrameBufferObject::initialise"); } // If we're doing multisampling, then we need another FBO which contains a // renderbuffer which is set up to multisample, and we'll blit it to the final // FBO afterwards to perform the multisample resolve. In that case, the // mMultisampleFB is bound during rendering and is the one with a depth/stencil // Store basic stats uint32 width = mColour[0].buffer->getWidth(); uint32 height = mColour[0].buffer->getHeight(); GLuint format = mColour[0].buffer->getGLFormat(); ushort maxSupportedMRTs = rs->getCapabilities()->getNumMultiRenderTargets(); // Bind simple buffer to add colour attachments OGRE_CHECK_GL_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, mFB)); bool isDepth = PixelUtil::isDepth(getFormat()); // Bind all attachment points to frame buffer for(unsigned int x = 0; x < maxSupportedMRTs; ++x) { if(mColour[x].buffer) { if(mColour[x].buffer->getWidth() != width || mColour[x].buffer->getHeight() != height) { StringStream ss; ss << "Attachment " << x << " has incompatible size "; ss << mColour[x].buffer->getWidth() << "x" << mColour[x].buffer->getHeight(); ss << ". It must be of the same as the size of surface 0, "; ss << width << "x" << height; ss << "."; OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, ss.str(), "GLES2FrameBufferObject::initialise"); } if(mColour[x].buffer->getGLFormat() != format) { StringStream ss; ss << "Attachment " << x << " has incompatible format."; OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, ss.str(), "GLES2FrameBufferObject::initialise"); } mColour[x].buffer->bindToFramebuffer( isDepth ? GL_DEPTH_ATTACHMENT : (GL_COLOR_ATTACHMENT0 + x), mColour[x].zoffset); } else { // Detach OGRE_CHECK_GL_ERROR(glFramebufferRenderbuffer(GL_FRAMEBUFFER, static_cast<GLenum>(GL_COLOR_ATTACHMENT0+x), GL_RENDERBUFFER, 0)); } } // Now deal with depth / stencil if (mMultisampleFB) { // Bind multisample buffer OGRE_CHECK_GL_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, mMultisampleFB)); // Create AA render buffer (colour) // note, this can be shared too because we blit it to the final FBO // right after the render is finished mMultisampleColourBuffer = mManager->requestRenderBuffer(format, width, height, mNumSamples); // Attach it, because we won't be attaching below and non-multisample has // actually been attached to other FBO mMultisampleColourBuffer.buffer->bindToFramebuffer(GL_COLOR_ATTACHMENT0, mMultisampleColourBuffer.zoffset); // depth & stencil will be dealt with below } // Depth buffer is not handled here anymore. // See GLES2FrameBufferObject::attachDepthBuffer() & RenderSystem::setDepthBufferFor() if(rs->hasMinGLVersion(3, 0) && OGRE_PLATFORM != OGRE_PLATFORM_EMSCRIPTEN) // ED on Emscripten { GLenum bufs[OGRE_MAX_MULTIPLE_RENDER_TARGETS]; GLsizei n=0; for(unsigned int x=0; x<maxSupportedMRTs; ++x) { // Fill attached colour buffers if(mColour[x].buffer) { bufs[x] = isDepth ? GL_DEPTH_ATTACHMENT : (GL_COLOR_ATTACHMENT0 + x); // Keep highest used buffer + 1 n = x+1; } else { bufs[x] = GL_NONE; } } // Drawbuffer extension supported, use it if(!isDepth) OGRE_CHECK_GL_ERROR(glDrawBuffers(n, bufs)); if (mMultisampleFB) { // we need a read buffer because we'll be blitting to mFB OGRE_CHECK_GL_ERROR(glReadBuffer(bufs[0])); } else { // No read buffer, by default, if we want to read anyway we must not forget to set this. OGRE_CHECK_GL_ERROR(glReadBuffer(GL_NONE)); } } // Check status GLuint status; OGRE_CHECK_GL_ERROR(status = glCheckFramebufferStatus(GL_FRAMEBUFFER)); // Bind main buffer #if OGRE_PLATFORM == OGRE_PLATFORM_APPLE_IOS // The screen buffer is 1 on iOS OGRE_CHECK_GL_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, 1)); #else OGRE_CHECK_GL_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, 0)); #endif switch(status) { case GL_FRAMEBUFFER_COMPLETE: // All is good break; case GL_FRAMEBUFFER_UNSUPPORTED: OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "All framebuffer formats with this texture internal format unsupported", "GLES2FrameBufferObject::initialise"); default: OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Framebuffer incomplete or other FBO status error", "GLES2FrameBufferObject::initialise"); } }
/** Detect which internal formats are allowed as RTT Also detect what combinations of stencil and depth are allowed with this internal format. */ void GLES2FBOManager::detectFBOFormats() { #if OGRE_PLATFORM == OGRE_PLATFORM_EMSCRIPTEN memset(mProps, 0, sizeof(mProps)); // TODO: Fix that probing all formats slows down startup not just on the web also on Android / iOS mProps[PF_A8B8G8R8].valid = true; FormatProperties::Mode mode = {1, 0}; mProps[PF_A8B8G8R8].modes.push_back(mode); LogManager::getSingleton().logMessage("[GLES2] : detectFBOFormats is disabled on this platform (due performance reasons)"); #else // Try all formats, and report which ones work as target GLES2RenderSystem* rs = getGLES2RenderSystem(); GLuint fb = 0, tid = 0; bool hasGLES3 = rs->hasMinGLVersion(3, 0); const size_t depthCount = hasGLES3 ? DEPTHFORMAT_COUNT : DEPTHFORMAT_COUNT - 1; // 32_8 is not available on GLES2 const size_t stencilStep = hasGLES3 ? 3 : 1; // 1 and 4 bit not available on GLES3 for(size_t x = 0; x < PF_COUNT; ++x) { mProps[x].valid = false; // Fetch GL format token GLint internalFormat = GLES2PixelUtil::getGLInternalFormat((PixelFormat)x); GLenum fmt = GLES2PixelUtil::getGLOriginFormat((PixelFormat)x); GLenum type = GLES2PixelUtil::getGLOriginDataType((PixelFormat)x); // Note: letting PF_UNKNOWN pass here is for pure depth/ stencil formats // however there are reports that this crashes some unspecified android devices if((internalFormat == GL_NONE || fmt == GL_NONE || type == GL_NONE) && (x != 0)) continue; // not color-renderable in GLES if(fmt == GL_BGRA_EXT) continue; // No test for compressed formats if(PixelUtil::isCompressed((PixelFormat)x)) continue; // Create and attach framebuffer _createTempFramebuffer((PixelFormat)x, internalFormat, fmt, type, fb, tid); // 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(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE) { mProps[x].valid = true; StringStream str; str << "FBO " << PixelUtil::getFormatName((PixelFormat)x) << " depth/stencil support: "; // For each depth/stencil formats for (size_t depth = 0; depth < depthCount; ++depth) { if (depthFormats[depth] != GL_DEPTH24_STENCIL8 && depthFormats[depth] != GL_DEPTH32F_STENCIL8) { // General depth/stencil combination for (size_t stencil = 0; stencil < STENCILFORMAT_COUNT; stencil += stencilStep) { // StringStream 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 { // There is a small edge case that FBO is trashed during the test // on some drivers resulting in undefined behavior glBindFramebuffer(GL_FRAMEBUFFER, 0); glDeleteFramebuffers(1, &fb); _createTempFramebuffer((PixelFormat)x, internalFormat, fmt, type, fb, tid); } } } else if(hasGLES3 || rs->checkExtension("GL_OES_packed_depth_stencil") ) { // Packed depth/stencil format 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); } else { // There is a small edge case that FBO is trashed during the test // on some drivers resulting in undefined behavior glBindFramebuffer(GL_FRAMEBUFFER, 0); glDeleteFramebuffers(1, &fb); _createTempFramebuffer((PixelFormat)x, internalFormat, fmt, type, fb, tid); } } } LogManager::getSingleton().logMessage(str.str()); } // Delete texture and framebuffer glBindFramebuffer(GL_FRAMEBUFFER, 0); glDeleteFramebuffers(1, &fb); if (internalFormat != GL_NONE) { glDeleteTextures(1, &tid); tid = 0; } } // Clear any errors glGetError(); #endif String fmtstring; for(size_t x = 0; x < PF_COUNT; ++x) { if(mProps[x].valid) fmtstring += PixelUtil::getFormatName((PixelFormat)x)+" "; } LogManager::getSingleton().logMessage("[GLES2] : Valid FBO targets " + fmtstring); }