| // 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; | 
 | } | 
 |  | 
 | } |