#include "BsGLFrameBufferObject.h" #include "BsGLPixelFormat.h" #include "BsGLPixelBuffer.h" #include "BsGLRenderTexture.h" #include "BsRenderStats.h" namespace BansheeEngine { GLFrameBufferObject::GLFrameBufferObject() { glGenFramebuffers(1, &mFB); for(UINT32 x = 0; x < BS_MAX_MULTIPLE_RENDER_TARGETS; ++x) mColor[x].buffer = nullptr; BS_INC_RENDER_STAT_CAT(ResCreated, RenderStatObject_FrameBufferObject); } GLFrameBufferObject::~GLFrameBufferObject() { glDeleteFramebuffers(1, &mFB); BS_INC_RENDER_STAT_CAT(ResDestroyed, RenderStatObject_FrameBufferObject); } void GLFrameBufferObject::bindSurface(UINT32 attachment, const GLSurfaceDesc &target) { assert(attachment < BS_MAX_MULTIPLE_RENDER_TARGETS); mColor[attachment] = target; if(mColor[0].buffer) rebuild(); } void GLFrameBufferObject::unbindSurface(UINT32 attachment) { assert(attachment < BS_MAX_MULTIPLE_RENDER_TARGETS); mColor[attachment].buffer = nullptr; if(mColor[0].buffer) rebuild(); } void GLFrameBufferObject::bindDepthStencil(GLPixelBufferPtr depthStencilBuffer) { mDepthStencilBuffer = depthStencilBuffer; if (mColor[0].buffer) rebuild(); } void GLFrameBufferObject::unbindDepthStencil() { mDepthStencilBuffer = nullptr; } void GLFrameBufferObject::rebuild() { // First buffer must be bound if(!mColor[0].buffer) BS_EXCEPT(InvalidParametersException, "Attachment 0 must have surface attached"); // Store basic stats UINT32 width = mColor[0].buffer->getWidth(); UINT32 height = mColor[0].buffer->getHeight(); GLuint glformat = mColor[0].buffer->getGLFormat(); PixelFormat format = mColor[0].buffer->getFormat(); UINT16 maxSupportedMRTs = BansheeEngine::RenderSystem::instancePtr()->getCapabilities()->getNumMultiRenderTargets(); // Bind simple buffer to add color attachments glBindFramebuffer(GL_FRAMEBUFFER, mFB); // Bind all attachment points to frame buffer for(UINT16 x = 0; x < maxSupportedMRTs; ++x) { if(mColor[x].buffer) { if(mColor[x].buffer->getWidth() != width || mColor[x].buffer->getHeight() != height) { StringStream ss; ss << "Attachment " << x << " has incompatible size "; ss << mColor[x].buffer->getWidth() << "x" << mColor[x].buffer->getHeight(); ss << ". It must be of the same as the size of surface 0, "; ss << width << "x" << height; ss << "."; BS_EXCEPT(InvalidParametersException, ss.str()); } if(mColor[x].buffer->getGLFormat() != glformat) { StringStream ss; ss << "Attachment " << x << " has incompatible format."; BS_EXCEPT(InvalidParametersException, ss.str()); } // Note: I'm attaching textures to FBO while renderbuffers might yield better performance if I // don't need to read from them mColor[x].buffer->bindToFramebuffer(GL_COLOR_ATTACHMENT0 + x, mColor[x].zoffset); } else { // Detach glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + x, 0, 0); } } if(mDepthStencilBuffer != nullptr) mDepthStencilBuffer->bindToFramebuffer(GL_DEPTH_STENCIL_ATTACHMENT, 0); // Do glDrawBuffer calls GLenum bufs[BS_MAX_MULTIPLE_RENDER_TARGETS]; GLsizei n = 0; for(UINT32 x = 0; x < BS_MAX_MULTIPLE_RENDER_TARGETS; ++x) { // Fill attached colour buffers if(mColor[x].buffer) { bufs[x] = GL_COLOR_ATTACHMENT0 + x; // Keep highest used buffer + 1 n = x+1; } else { bufs[x] = GL_NONE; } } glDrawBuffers(n, bufs); // No read buffer, by default, if we want to read anyway we must not forget to set this. glReadBuffer(GL_NONE); // Check status GLuint status; status = glCheckFramebufferStatus(GL_FRAMEBUFFER); // Bind main buffer glBindFramebuffer(GL_FRAMEBUFFER, 0); switch(status) { case GL_FRAMEBUFFER_COMPLETE: break; case GL_FRAMEBUFFER_UNSUPPORTED: BS_EXCEPT(InvalidParametersException, "All framebuffer formats with this texture internal format unsupported"); default: BS_EXCEPT(InvalidParametersException, "Framebuffer incomplete or other FBO status error"); } } void GLFrameBufferObject::bind() { glBindFramebuffer(GL_FRAMEBUFFER, mFB); } }