|  | // Copyright 2016 The SwiftShader Authors. All Rights Reserved. | 
|  | // | 
|  | // Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | // you may not use this file except in compliance with the License. | 
|  | // You may obtain a copy of the License at | 
|  | // | 
|  | //    http://www.apache.org/licenses/LICENSE-2.0 | 
|  | // | 
|  | // Unless required by applicable law or agreed to in writing, software | 
|  | // distributed under the License is distributed on an "AS IS" BASIS, | 
|  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | // See the License for the specific language governing permissions and | 
|  | // limitations under the License. | 
|  |  | 
|  | // Framebuffer.cpp: Implements the Framebuffer class. Implements GL framebuffer | 
|  | // objects and related functionality. | 
|  |  | 
|  | #include "Framebuffer.h" | 
|  |  | 
|  | #include "main.h" | 
|  | #include "Renderbuffer.h" | 
|  | #include "Texture.h" | 
|  | #include "utilities.h" | 
|  |  | 
|  | namespace gl | 
|  | { | 
|  |  | 
|  | Framebuffer::Framebuffer() | 
|  | { | 
|  | mColorbufferType = GL_NONE; | 
|  | mDepthbufferType = GL_NONE; | 
|  | mStencilbufferType = GL_NONE; | 
|  | } | 
|  |  | 
|  | Framebuffer::~Framebuffer() | 
|  | { | 
|  | mColorbufferPointer = nullptr; | 
|  | mDepthbufferPointer = nullptr; | 
|  | mStencilbufferPointer = nullptr; | 
|  | } | 
|  |  | 
|  | Renderbuffer *Framebuffer::lookupRenderbuffer(GLenum type, GLuint handle) const | 
|  | { | 
|  | Context *context = getContext(); | 
|  | Renderbuffer *buffer = nullptr; | 
|  |  | 
|  | if(type == GL_NONE) | 
|  | { | 
|  | buffer = nullptr; | 
|  | } | 
|  | else if(type == GL_RENDERBUFFER) | 
|  | { | 
|  | buffer = context->getRenderbuffer(handle); | 
|  | } | 
|  | else if(IsTextureTarget(type)) | 
|  | { | 
|  | buffer = context->getTexture(handle)->getRenderbuffer(type); | 
|  | } | 
|  | else UNREACHABLE(type); | 
|  |  | 
|  | return buffer; | 
|  | } | 
|  |  | 
|  | void Framebuffer::setColorbuffer(GLenum type, GLuint colorbuffer) | 
|  | { | 
|  | mColorbufferType = (colorbuffer != 0) ? type : GL_NONE; | 
|  | mColorbufferPointer = lookupRenderbuffer(type, colorbuffer); | 
|  | } | 
|  |  | 
|  | void Framebuffer::setDepthbuffer(GLenum type, GLuint depthbuffer) | 
|  | { | 
|  | mDepthbufferType = (depthbuffer != 0) ? type : GL_NONE; | 
|  | mDepthbufferPointer = lookupRenderbuffer(type, depthbuffer); | 
|  | } | 
|  |  | 
|  | void Framebuffer::setStencilbuffer(GLenum type, GLuint stencilbuffer) | 
|  | { | 
|  | mStencilbufferType = (stencilbuffer != 0) ? type : GL_NONE; | 
|  | mStencilbufferPointer = lookupRenderbuffer(type, stencilbuffer); | 
|  | } | 
|  |  | 
|  | void Framebuffer::detachTexture(GLuint texture) | 
|  | { | 
|  | if(mColorbufferPointer.name() == texture && IsTextureTarget(mColorbufferType)) | 
|  | { | 
|  | mColorbufferType = GL_NONE; | 
|  | mColorbufferPointer = nullptr; | 
|  | } | 
|  |  | 
|  | if(mDepthbufferPointer.name() == texture && IsTextureTarget(mDepthbufferType)) | 
|  | { | 
|  | mDepthbufferType = GL_NONE; | 
|  | mDepthbufferPointer = nullptr; | 
|  | } | 
|  |  | 
|  | if(mStencilbufferPointer.name() == texture && IsTextureTarget(mStencilbufferType)) | 
|  | { | 
|  | mStencilbufferType = GL_NONE; | 
|  | mStencilbufferPointer = nullptr; | 
|  | } | 
|  | } | 
|  |  | 
|  | void Framebuffer::detachRenderbuffer(GLuint renderbuffer) | 
|  | { | 
|  | if(mColorbufferPointer.name() == renderbuffer && mColorbufferType == GL_RENDERBUFFER) | 
|  | { | 
|  | mColorbufferType = GL_NONE; | 
|  | mColorbufferPointer = nullptr; | 
|  | } | 
|  |  | 
|  | if(mDepthbufferPointer.name() == renderbuffer && mDepthbufferType == GL_RENDERBUFFER) | 
|  | { | 
|  | mDepthbufferType = GL_NONE; | 
|  | mDepthbufferPointer = nullptr; | 
|  | } | 
|  |  | 
|  | if(mStencilbufferPointer.name() == renderbuffer && mStencilbufferType == GL_RENDERBUFFER) | 
|  | { | 
|  | mStencilbufferType = GL_NONE; | 
|  | mStencilbufferPointer = nullptr; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Increments refcount on surface. | 
|  | // caller must Release() the returned surface | 
|  | Image *Framebuffer::getRenderTarget() | 
|  | { | 
|  | Renderbuffer *colorbuffer = mColorbufferPointer; | 
|  |  | 
|  | if(colorbuffer) | 
|  | { | 
|  | return colorbuffer->getRenderTarget(); | 
|  | } | 
|  |  | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | // Increments refcount on surface. | 
|  | // caller must Release() the returned surface | 
|  | Image *Framebuffer::getDepthStencil() | 
|  | { | 
|  | Renderbuffer *depthstencilbuffer = mDepthbufferPointer; | 
|  |  | 
|  | if(!depthstencilbuffer) | 
|  | { | 
|  | depthstencilbuffer = mStencilbufferPointer; | 
|  | } | 
|  |  | 
|  | if(depthstencilbuffer) | 
|  | { | 
|  | return depthstencilbuffer->getRenderTarget(); | 
|  | } | 
|  |  | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | Renderbuffer *Framebuffer::getColorbuffer() | 
|  | { | 
|  | return mColorbufferPointer; | 
|  | } | 
|  |  | 
|  | Renderbuffer *Framebuffer::getDepthbuffer() | 
|  | { | 
|  | return mDepthbufferPointer; | 
|  | } | 
|  |  | 
|  | Renderbuffer *Framebuffer::getStencilbuffer() | 
|  | { | 
|  | return mStencilbufferPointer; | 
|  | } | 
|  |  | 
|  | GLenum Framebuffer::getColorbufferType() | 
|  | { | 
|  | return mColorbufferType; | 
|  | } | 
|  |  | 
|  | GLenum Framebuffer::getDepthbufferType() | 
|  | { | 
|  | return mDepthbufferType; | 
|  | } | 
|  |  | 
|  | GLenum Framebuffer::getStencilbufferType() | 
|  | { | 
|  | return mStencilbufferType; | 
|  | } | 
|  |  | 
|  | GLuint Framebuffer::getColorbufferName() | 
|  | { | 
|  | return mColorbufferPointer.name(); | 
|  | } | 
|  |  | 
|  | GLuint Framebuffer::getDepthbufferName() | 
|  | { | 
|  | return mDepthbufferPointer.name(); | 
|  | } | 
|  |  | 
|  | GLuint Framebuffer::getStencilbufferName() | 
|  | { | 
|  | return mStencilbufferPointer.name(); | 
|  | } | 
|  |  | 
|  | bool Framebuffer::hasStencil() | 
|  | { | 
|  | if(mStencilbufferType != GL_NONE) | 
|  | { | 
|  | Renderbuffer *stencilbufferObject = getStencilbuffer(); | 
|  |  | 
|  | if(stencilbufferObject) | 
|  | { | 
|  | return stencilbufferObject->getStencilSize() > 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | GLenum Framebuffer::completeness() | 
|  | { | 
|  | int width; | 
|  | int height; | 
|  | int samples; | 
|  |  | 
|  | return completeness(width, height, samples); | 
|  | } | 
|  |  | 
|  | GLenum Framebuffer::completeness(int &width, int &height, int &samples) | 
|  | { | 
|  | width = -1; | 
|  | height = -1; | 
|  | samples = -1; | 
|  |  | 
|  | if(mColorbufferType != GL_NONE) | 
|  | { | 
|  | Renderbuffer *colorbuffer = getColorbuffer(); | 
|  |  | 
|  | if(!colorbuffer) | 
|  | { | 
|  | return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; | 
|  | } | 
|  |  | 
|  | if(colorbuffer->getWidth() == 0 || colorbuffer->getHeight() == 0) | 
|  | { | 
|  | return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; | 
|  | } | 
|  |  | 
|  | if(mColorbufferType == GL_RENDERBUFFER) | 
|  | { | 
|  | if(!gl::IsColorRenderable(colorbuffer->getFormat())) | 
|  | { | 
|  | return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; | 
|  | } | 
|  | } | 
|  | else if(IsTextureTarget(mColorbufferType)) | 
|  | { | 
|  | GLenum format = colorbuffer->getFormat(); | 
|  |  | 
|  | if(IsCompressed(format) || | 
|  | format == GL_ALPHA || | 
|  | format == GL_LUMINANCE || | 
|  | format == GL_LUMINANCE_ALPHA) | 
|  | { | 
|  | return GL_FRAMEBUFFER_UNSUPPORTED; | 
|  | } | 
|  |  | 
|  | if(gl::IsDepthTexture(format) || gl::IsStencilTexture(format)) | 
|  | { | 
|  | return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | UNREACHABLE(mColorbufferType); | 
|  | return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; | 
|  | } | 
|  |  | 
|  | width = colorbuffer->getWidth(); | 
|  | height = colorbuffer->getHeight(); | 
|  | samples = colorbuffer->getSamples(); | 
|  | } | 
|  |  | 
|  | Renderbuffer *depthbuffer = nullptr; | 
|  | Renderbuffer *stencilbuffer = nullptr; | 
|  |  | 
|  | if(mDepthbufferType != GL_NONE) | 
|  | { | 
|  | depthbuffer = getDepthbuffer(); | 
|  |  | 
|  | if(!depthbuffer) | 
|  | { | 
|  | return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; | 
|  | } | 
|  |  | 
|  | if(depthbuffer->getWidth() == 0 || depthbuffer->getHeight() == 0) | 
|  | { | 
|  | return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; | 
|  | } | 
|  |  | 
|  | if(mDepthbufferType == GL_RENDERBUFFER) | 
|  | { | 
|  | if(!gl::IsDepthRenderable(depthbuffer->getFormat())) | 
|  | { | 
|  | return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; | 
|  | } | 
|  | } | 
|  | else if(IsTextureTarget(mDepthbufferType)) | 
|  | { | 
|  | if(!gl::IsDepthTexture(depthbuffer->getFormat())) | 
|  | { | 
|  | return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | UNREACHABLE(mDepthbufferType); | 
|  | return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; | 
|  | } | 
|  |  | 
|  | if(width == -1 || height == -1) | 
|  | { | 
|  | width = depthbuffer->getWidth(); | 
|  | height = depthbuffer->getHeight(); | 
|  | samples = depthbuffer->getSamples(); | 
|  | } | 
|  | else if(width != depthbuffer->getWidth() || height != depthbuffer->getHeight()) | 
|  | { | 
|  | return GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT; | 
|  | } | 
|  | else if(samples != depthbuffer->getSamples()) | 
|  | { | 
|  | return GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT; | 
|  | } | 
|  | } | 
|  |  | 
|  | if(mStencilbufferType != GL_NONE) | 
|  | { | 
|  | stencilbuffer = getStencilbuffer(); | 
|  |  | 
|  | if(!stencilbuffer) | 
|  | { | 
|  | return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; | 
|  | } | 
|  |  | 
|  | if(stencilbuffer->getWidth() == 0 || stencilbuffer->getHeight() == 0) | 
|  | { | 
|  | return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; | 
|  | } | 
|  |  | 
|  | if(mStencilbufferType == GL_RENDERBUFFER) | 
|  | { | 
|  | if(!gl::IsStencilRenderable(stencilbuffer->getFormat())) | 
|  | { | 
|  | return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; | 
|  | } | 
|  | } | 
|  | else if(IsTextureTarget(mStencilbufferType)) | 
|  | { | 
|  | GLenum internalformat = stencilbuffer->getFormat(); | 
|  |  | 
|  | if(!gl::IsStencilTexture(internalformat)) | 
|  | { | 
|  | return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | UNREACHABLE(mStencilbufferType); | 
|  | return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; | 
|  | } | 
|  |  | 
|  | if(width == -1 || height == -1) | 
|  | { | 
|  | width = stencilbuffer->getWidth(); | 
|  | height = stencilbuffer->getHeight(); | 
|  | samples = stencilbuffer->getSamples(); | 
|  | } | 
|  | else if(width != stencilbuffer->getWidth() || height != stencilbuffer->getHeight()) | 
|  | { | 
|  | return GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT; | 
|  | } | 
|  | else if(samples != stencilbuffer->getSamples()) | 
|  | { | 
|  | return GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT; | 
|  | } | 
|  | } | 
|  |  | 
|  | // If we have both a depth and stencil buffer, they must refer to the same object | 
|  | // since we only support packed_depth_stencil and not separate depth and stencil | 
|  | if(depthbuffer && stencilbuffer && (depthbuffer != stencilbuffer)) | 
|  | { | 
|  | return GL_FRAMEBUFFER_UNSUPPORTED; | 
|  | } | 
|  |  | 
|  | // We need to have at least one attachment to be complete | 
|  | if(width == -1 || height == -1) | 
|  | { | 
|  | return GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT; | 
|  | } | 
|  |  | 
|  | return GL_FRAMEBUFFER_COMPLETE; | 
|  | } | 
|  |  | 
|  | DefaultFramebuffer::DefaultFramebuffer(Colorbuffer *colorbuffer, DepthStencilbuffer *depthStencil) | 
|  | { | 
|  | mColorbufferPointer = new Renderbuffer(0, colorbuffer); | 
|  |  | 
|  | Renderbuffer *depthStencilRenderbuffer = new Renderbuffer(0, depthStencil); | 
|  | mDepthbufferPointer = depthStencilRenderbuffer; | 
|  | mStencilbufferPointer = depthStencilRenderbuffer; | 
|  |  | 
|  | mColorbufferType = GL_RENDERBUFFER; | 
|  | mDepthbufferType = (depthStencilRenderbuffer->getDepthSize() != 0) ? GL_RENDERBUFFER : GL_NONE; | 
|  | mStencilbufferType = (depthStencilRenderbuffer->getStencilSize() != 0) ? GL_RENDERBUFFER : GL_NONE; | 
|  | } | 
|  |  | 
|  | GLenum DefaultFramebuffer::completeness() | 
|  | { | 
|  | // The default framebuffer should always be complete | 
|  | ASSERT(Framebuffer::completeness() == GL_FRAMEBUFFER_COMPLETE); | 
|  |  | 
|  | return GL_FRAMEBUFFER_COMPLETE; | 
|  | } | 
|  |  | 
|  | } |