Home | History | Annotate | Download | only in glshared
      1 /*-------------------------------------------------------------------------
      2  * drawElements Quality Program OpenGL (ES) Module
      3  * -----------------------------------------------
      4  *
      5  * Copyright 2014 The Android Open Source Project
      6  *
      7  * Licensed under the Apache License, Version 2.0 (the "License");
      8  * you may not use this file except in compliance with the License.
      9  * You may obtain a copy of the License at
     10  *
     11  *      http://www.apache.org/licenses/LICENSE-2.0
     12  *
     13  * Unless required by applicable law or agreed to in writing, software
     14  * distributed under the License is distributed on an "AS IS" BASIS,
     15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     16  * See the License for the specific language governing permissions and
     17  * limitations under the License.
     18  *
     19  *//*!
     20  * \file
     21  * \brief Utilities for framebuffer objects.
     22  *//*--------------------------------------------------------------------*/
     23 
     24 #include "glsFboUtil.hpp"
     25 
     26 #include "glwEnums.hpp"
     27 #include "deUniquePtr.hpp"
     28 #include "gluTextureUtil.hpp"
     29 #include "gluStrUtil.hpp"
     30 #include "deStringUtil.hpp"
     31 #include <sstream>
     32 
     33 using namespace glw;
     34 using tcu::TestLog;
     35 using tcu::TextureFormat;
     36 using tcu::NotSupportedError;
     37 using glu::TransferFormat;
     38 using glu::mapGLInternalFormat;
     39 using glu::mapGLTransferFormat;
     40 using glu::getPixelFormatName;
     41 using glu::getTypeName;
     42 using glu::getFramebufferTargetName;
     43 using glu::getFramebufferAttachmentName;
     44 using glu::getFramebufferAttachmentTypeName;
     45 using glu::getTextureTargetName;
     46 using glu::getTransferFormat;
     47 using glu::ContextInfo;
     48 using glu::ContextType;
     49 using glu::RenderContext;
     50 using de::UniquePtr;
     51 using de::toString;
     52 using std::set;
     53 using std::vector;
     54 using std::string;
     55 using std::istringstream;
     56 using std::istream_iterator;
     57 
     58 namespace deqp
     59 {
     60 namespace gls
     61 {
     62 
     63 namespace FboUtil
     64 {
     65 
     66 
     67 void FormatDB::addFormat (ImageFormat format, FormatFlags newFlags)
     68 {
     69 	FormatFlags& flags = m_map[format];
     70 	flags = FormatFlags(flags | newFlags);
     71 }
     72 
     73 // Not too fast at the moment, might consider indexing?
     74 Formats FormatDB::getFormats (FormatFlags requirements) const
     75 {
     76 	Formats ret;
     77 	for (FormatMap::const_iterator it = m_map.begin(); it != m_map.end(); it++)
     78 	{
     79 		if ((it->second & requirements) == requirements)
     80 			ret.insert(it->first);
     81 	}
     82 	return ret;
     83 }
     84 
     85 FormatFlags FormatDB::getFormatInfo (ImageFormat format, FormatFlags fallback) const
     86 {
     87 	return lookupDefault(m_map, format, fallback);
     88 }
     89 
     90 void addFormats (FormatDB& db, FormatEntries stdFmts)
     91 {
     92 	for (const FormatEntry* it = stdFmts.begin(); it != stdFmts.end(); it++)
     93 	{
     94 		for (const FormatKey* it2 = it->second.begin(); it2 != it->second.end(); it2++)
     95 			db.addFormat(formatKeyInfo(*it2), it->first);
     96 	}
     97 }
     98 
     99 void addExtFormats (FormatDB& db, FormatExtEntries extFmts, const RenderContext* ctx)
    100 {
    101 	const UniquePtr<ContextInfo> ctxInfo(ctx != DE_NULL ? ContextInfo::create(*ctx) : DE_NULL);
    102 	for (const FormatExtEntry* it = extFmts.begin(); it != extFmts.end(); it++)
    103 	{
    104 		bool supported = true;
    105 		if (ctxInfo)
    106 		{
    107 			istringstream tokenStream(string(it->extensions));
    108 			istream_iterator<string> tokens((tokenStream)), end;
    109 
    110 			while (tokens != end)
    111 			{
    112 				if (!ctxInfo->isExtensionSupported(tokens->c_str()))
    113 				{
    114 					supported = false;
    115 					break;
    116 				}
    117 				++tokens;
    118 			}
    119 		}
    120 		if (supported)
    121 			for (const FormatKey* i2 = it->formats.begin(); i2 != it->formats.end(); i2++)
    122 				db.addFormat(formatKeyInfo(*i2), FormatFlags(it->flags));
    123 	}
    124 }
    125 
    126 FormatFlags formatFlag (GLenum context)
    127 {
    128 	switch (context)
    129 	{
    130 		case GL_NONE:
    131 			return FormatFlags(0);
    132 		case GL_RENDERBUFFER:
    133 			return RENDERBUFFER_VALID;
    134 		case GL_TEXTURE:
    135 			return TEXTURE_VALID;
    136 		case GL_STENCIL_ATTACHMENT:
    137 			return STENCIL_RENDERABLE;
    138 		case GL_DEPTH_ATTACHMENT:
    139 			return DEPTH_RENDERABLE;
    140 		default:
    141 			DE_ASSERT(context >= GL_COLOR_ATTACHMENT0 && context <= GL_COLOR_ATTACHMENT15);
    142 			return COLOR_RENDERABLE;
    143 	}
    144 }
    145 
    146 namespace config {
    147 
    148 GLsizei	imageNumSamples	(const Image& img)
    149 {
    150 	if (const Renderbuffer* rbo = dynamic_cast<const Renderbuffer*>(&img))
    151 		return rbo->numSamples;
    152 	return 0;
    153 }
    154 
    155 static GLenum glTarget (const Image& img)
    156 {
    157 	if (dynamic_cast<const Renderbuffer*>(&img) != DE_NULL)
    158 		return GL_RENDERBUFFER;
    159 	if (dynamic_cast<const Texture2D*>(&img) != DE_NULL)
    160 		return GL_TEXTURE_2D;
    161 	if (dynamic_cast<const TextureCubeMap*>(&img) != DE_NULL)
    162 		return GL_TEXTURE_CUBE_MAP;
    163 	if (dynamic_cast<const Texture3D*>(&img) != DE_NULL)
    164 		return GL_TEXTURE_3D;
    165 	if (dynamic_cast<const Texture2DArray*>(&img) != DE_NULL)
    166 		return GL_TEXTURE_2D_ARRAY;
    167 
    168 	DE_ASSERT(!"Impossible image type");
    169 	return GL_NONE;
    170 }
    171 
    172 static void glInitFlat (const TextureFlat& cfg, GLenum target, const glw::Functions& gl)
    173 {
    174 	const TransferFormat format = transferImageFormat(cfg.internalFormat);
    175 	GLint w = cfg.width;
    176 	GLint h = cfg.height;
    177 	for (GLint level = 0; level < cfg.numLevels; level++)
    178 	{
    179 		gl.texImage2D(target, level, cfg.internalFormat.format, w, h, 0,
    180 					  format.format, format.dataType, DE_NULL);
    181 		w = de::max(1, w / 2);
    182 		h = de::max(1, h / 2);
    183 	}
    184 }
    185 
    186 static void glInitLayered (const TextureLayered& cfg,
    187 						   GLint depth_divider, const glw::Functions& gl)
    188 {
    189 	const TransferFormat format = transferImageFormat(cfg.internalFormat);
    190 	GLint w = cfg.width;
    191 	GLint h = cfg.height;
    192 	GLint depth = cfg.numLayers;
    193 	for (GLint level = 0; level < cfg.numLevels; level++)
    194 	{
    195 		gl.texImage3D(glTarget(cfg), level, cfg.internalFormat.format, w, h, depth, 0,
    196 					  format.format, format.dataType, DE_NULL);
    197 		w = de::max(1, w / 2);
    198 		h = de::max(1, h / 2);
    199 		depth = de::max(1, depth / depth_divider);
    200 	}
    201 }
    202 
    203 static void glInit (const Texture& cfg, const glw::Functions& gl)
    204 {
    205 	if (const Texture2D* t2d = dynamic_cast<const Texture2D*>(&cfg))
    206 		glInitFlat(*t2d, glTarget(*t2d), gl);
    207 	else if (const TextureCubeMap* tcm = dynamic_cast<const TextureCubeMap*>(&cfg))
    208 	{
    209 		// \todo [2013-12-05 lauri]
    210 		// move this to glu or someplace sensible (this array is already
    211 		// present in duplicates)
    212 		static const GLenum s_cubeMapFaces[] =
    213 			{
    214 				GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
    215 				GL_TEXTURE_CUBE_MAP_POSITIVE_X,
    216 				GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
    217 				GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
    218 				GL_TEXTURE_CUBE_MAP_NEGATIVE_Z,
    219 				GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
    220 			};
    221 		const Range<GLenum> range = GLS_ARRAY_RANGE(s_cubeMapFaces);
    222 		for (const GLenum* it = range.begin(); it != range.end(); it++)
    223 			glInitFlat(*tcm, *it, gl);
    224 	}
    225 	else if (const Texture3D* t3d = dynamic_cast<const Texture3D*>(&cfg))
    226 		glInitLayered(*t3d, 2, gl);
    227 	else if (const Texture2DArray* t2a = dynamic_cast<const Texture2DArray*>(&cfg))
    228 		glInitLayered(*t2a, 1, gl);
    229 }
    230 
    231 static GLuint glCreate (const Image& cfg, const glw::Functions& gl)
    232 {
    233 	GLuint ret = 0;
    234 	if (const Renderbuffer* const rbo = dynamic_cast<const Renderbuffer*>(&cfg))
    235 	{
    236 		gl.genRenderbuffers(1, &ret);
    237 		gl.bindRenderbuffer(GL_RENDERBUFFER, ret);
    238 
    239 		if (rbo->numSamples == 0)
    240 			gl.renderbufferStorage(GL_RENDERBUFFER, rbo->internalFormat.format,
    241 								   rbo->width, rbo->height);
    242 		else
    243 			gl.renderbufferStorageMultisample(
    244 				GL_RENDERBUFFER, rbo->numSamples, rbo->internalFormat.format,
    245 				rbo->width, rbo->height);
    246 
    247 		gl.bindRenderbuffer(GL_RENDERBUFFER, 0);
    248 	}
    249 	else if (const Texture* const tex = dynamic_cast<const Texture*>(&cfg))
    250 	{
    251 		gl.genTextures(1, &ret);
    252 		gl.bindTexture(glTarget(*tex), ret);
    253 		glInit(*tex, gl);
    254 		gl.bindTexture(glTarget(*tex), 0);
    255 	}
    256 	else
    257 		DE_ASSERT(!"Impossible image type");
    258 	return ret;
    259 }
    260 
    261 static void glDelete (const Image& cfg, GLuint img, const glw::Functions& gl)
    262 {
    263 	if (dynamic_cast<const Renderbuffer*>(&cfg) != DE_NULL)
    264 		gl.deleteRenderbuffers(1, &img);
    265 	else if (dynamic_cast<const Texture*>(&cfg) != DE_NULL)
    266 		gl.deleteTextures(1, &img);
    267 	else
    268 		DE_ASSERT(!"Impossible image type");
    269 }
    270 
    271 static void attachAttachment (const Attachment& att, GLenum attPoint,
    272 							  const glw::Functions& gl)
    273 {
    274 	if (const RenderbufferAttachment* const rAtt =
    275 		dynamic_cast<const RenderbufferAttachment*>(&att))
    276 		gl.framebufferRenderbuffer(rAtt->target, attPoint,
    277 								   rAtt->renderbufferTarget, rAtt->imageName);
    278 	else if (const TextureFlatAttachment* const fAtt =
    279 			 dynamic_cast<const TextureFlatAttachment*>(&att))
    280 		gl.framebufferTexture2D(fAtt->target, attPoint,
    281 								fAtt->texTarget, fAtt->imageName, fAtt->level);
    282 	else if (const TextureLayerAttachment* const lAtt =
    283 			 dynamic_cast<const TextureLayerAttachment*>(&att))
    284 		gl.framebufferTextureLayer(lAtt->target, attPoint,
    285 								   lAtt->imageName, lAtt->level, lAtt->layer);
    286 	else
    287 		DE_ASSERT(!"Impossible attachment type");
    288 }
    289 
    290 GLenum attachmentType (const Attachment& att)
    291 {
    292 	if (dynamic_cast<const RenderbufferAttachment*>(&att) != DE_NULL)
    293 		return GL_RENDERBUFFER;
    294 	else if (dynamic_cast<const TextureAttachment*>(&att) != DE_NULL)
    295 		return GL_TEXTURE;
    296 
    297 	DE_ASSERT(!"Impossible attachment type");
    298 	return GL_NONE;
    299 }
    300 
    301 static GLsizei textureLayer (const TextureAttachment& tAtt)
    302 {
    303 	if (dynamic_cast<const TextureFlatAttachment*>(&tAtt) != DE_NULL)
    304 		return 0;
    305 	else if (const TextureLayerAttachment* const lAtt =
    306 			 dynamic_cast<const TextureLayerAttachment*>(&tAtt))
    307 		return lAtt->layer;
    308 
    309 	DE_ASSERT(!"Impossible attachment type");
    310 	return 0;
    311 }
    312 
    313 static void checkAttachmentCompleteness (Checker& cctx, const Attachment& attachment,
    314 										 GLenum attPoint, const Image* image,
    315 										 const FormatDB& db)
    316 {
    317 	// GLES2 4.4.5 / GLES3 4.4.4, "Framebuffer attachment completeness"
    318 
    319 	if (const TextureAttachment* const texAtt =
    320 		dynamic_cast<const TextureAttachment*>(&attachment))
    321 		if (const TextureLayered* const ltex = dynamic_cast<const TextureLayered*>(image))
    322 		{
    323 			// GLES3: "If the value of FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is
    324 			// TEXTURE and the value of FRAMEBUFFER_ATTACHMENT_OBJECT_NAME names a
    325 			// three-dimensional texture, then the value of
    326 			// FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER must be smaller than the depth
    327 			// of the texture.
    328 			//
    329 			// GLES3: "If the value of FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is
    330 			// TEXTURE and the value of FRAMEBUFFER_ATTACHMENT_OBJECT_NAME names a
    331 			// two-dimensional array texture, then the value of
    332 			// FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER must be smaller than the
    333 			// number of layers in the texture.
    334 
    335 			cctx.require(textureLayer(*texAtt) < ltex->numLayers,
    336 						 GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT);
    337 		}
    338 
    339 	// "The width and height of image are non-zero."
    340 	cctx.require(image->width > 0 && image->height > 0, GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT);
    341 
    342 	// Check for renderability
    343 	FormatFlags flags = db.getFormatInfo(image->internalFormat, ANY_FORMAT);
    344 	// If the format does not have the proper renderability flag, the
    345 	// completeness check _must_ fail.
    346 	cctx.require((flags & formatFlag(attPoint)) != 0, GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT);
    347 	// If the format is only optionally renderable, the completeness check _can_ fail.
    348 	cctx.canRequire((flags & REQUIRED_RENDERABLE) != 0,
    349 					GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT);
    350 }
    351 
    352 } // namespace config
    353 
    354 using namespace config;
    355 
    356 void Checker::require (bool condition, GLenum error)
    357 {
    358 	if (!condition)
    359 	{
    360 		m_statusCodes.erase(GL_FRAMEBUFFER_COMPLETE);
    361 		m_statusCodes.insert(error);
    362 	}
    363 }
    364 
    365 void Checker::canRequire (bool condition, GLenum error)
    366 {
    367 	if (!condition)
    368 		m_statusCodes.insert(error);
    369 }
    370 
    371 FboVerifier::FboVerifier (const FormatDB& formats, CheckerFactory& factory)
    372 	: m_formats				(formats)
    373 	, m_factory				(factory)
    374 {
    375 }
    376 
    377 /*--------------------------------------------------------------------*//*!
    378  * \brief Return acceptable framebuffer status codes.
    379  *
    380  * This function examines the framebuffer configuration descriptor `fboConfig`
    381  * and returns the set of status codes that `glCheckFramebufferStatus` is
    382  * allowed to return on a conforming implementation when given a framebuffer
    383  * whose configuration adheres to `fboConfig`.
    384  *
    385  * The returned set is guaranteed to be non-empty, but it may contain multiple
    386  * INCOMPLETE statuses (if there are multiple errors in the spec), or or a mix
    387  * of COMPLETE and INCOMPLETE statuses (if supporting a FBO with this spec is
    388  * optional). Furthermore, the statuses may contain GL error codes, which
    389  * indicate that trying to create a framebuffer configuration like this could
    390  * have failed with an error (if one was checked for) even before
    391  * `glCheckFramebufferStatus` was ever called.
    392  *
    393  *//*--------------------------------------------------------------------*/
    394 StatusCodes FboVerifier::validStatusCodes (const Framebuffer& fboConfig) const
    395 {
    396 	const AttachmentMap& atts = fboConfig.attachments;
    397 	const UniquePtr<Checker> cctx(m_factory.createChecker());
    398 
    399 	for (TextureMap::const_iterator it = fboConfig.textures.begin();
    400 		 it != fboConfig.textures.end(); it++)
    401 	{
    402 		const FormatFlags flags =
    403 			m_formats.getFormatInfo(it->second->internalFormat, ANY_FORMAT);
    404 		cctx->require((flags & TEXTURE_VALID) != 0, GL_INVALID_ENUM);
    405 		cctx->require((flags & TEXTURE_VALID) != 0, GL_INVALID_OPERATION);
    406 		cctx->require((flags & TEXTURE_VALID) != 0, GL_INVALID_VALUE);
    407 	}
    408 
    409 	for (RboMap::const_iterator it = fboConfig.rbos.begin(); it != fboConfig.rbos.end(); it++)
    410 	{
    411 		const FormatFlags flags =
    412 			m_formats.getFormatInfo(it->second->internalFormat, ANY_FORMAT);
    413 		cctx->require((flags & RENDERBUFFER_VALID) != 0, GL_INVALID_ENUM);
    414 	}
    415 
    416 	// "There is at least one image attached to the framebuffer."
    417 	// TODO: support XXX_framebuffer_no_attachments
    418 	cctx->require(!atts.empty(), GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT);
    419 
    420 	for (AttachmentMap::const_iterator it = atts.begin(); it != atts.end(); it++)
    421 	{
    422 		const GLenum attPoint = it->first;
    423 		const Attachment& att = *it->second;
    424 		const Image* const image = fboConfig.getImage(attachmentType(att), att.imageName);
    425 		checkAttachmentCompleteness(*cctx, att, attPoint, image, m_formats);
    426 		cctx->check(it->first, *it->second, image);
    427 	}
    428 
    429 	return cctx->getStatusCodes();
    430 }
    431 
    432 
    433 void Framebuffer::attach (glw::GLenum attPoint, const Attachment* att)
    434 {
    435 	if (att == DE_NULL)
    436 		attachments.erase(attPoint);
    437 	else
    438 		attachments[attPoint] = att;
    439 }
    440 
    441 const Image* Framebuffer::getImage (GLenum type, glw::GLuint imgName) const
    442 {
    443 	switch (type)
    444 	{
    445 		case GL_TEXTURE:
    446 			return lookupDefault(textures, imgName, DE_NULL);
    447 		case GL_RENDERBUFFER:
    448 			return lookupDefault(rbos, imgName, DE_NULL);
    449 		default:
    450 			DE_ASSERT(!"Bad image type");
    451 	}
    452 	return DE_NULL; // shut up compiler warning
    453 }
    454 
    455 void Framebuffer::setTexture (glw::GLuint texName, const Texture& texCfg)
    456 {
    457 	textures[texName] = &texCfg;
    458 }
    459 
    460 void Framebuffer::setRbo (glw::GLuint rbName, const Renderbuffer& rbCfg)
    461 {
    462 	rbos[rbName] = &rbCfg;
    463 }
    464 
    465 static void logField (TestLog& log, const string& field, const string& value)
    466 {
    467 	log << TestLog::Message << field << ": " << value << TestLog::EndMessage;
    468 }
    469 
    470 static void logImage (const Image& img, TestLog& log, bool useType)
    471 {
    472 	const GLenum type = img.internalFormat.unsizedType;
    473 	logField(log, "Internal format",	getPixelFormatName(img.internalFormat.format));
    474 	if (useType && type != GL_NONE)
    475 		logField(log, "Format type",	getTypeName(type));
    476 	logField(log, "Width", 				toString(img.width));
    477 	logField(log, "Height",				toString(img.height));
    478 }
    479 
    480 static void logRenderbuffer (const Renderbuffer& rbo, TestLog& log)
    481 {
    482 	logImage(rbo, log, false);
    483 	logField(log, "Samples",			toString(rbo.numSamples));
    484 }
    485 
    486 static void logTexture (const Texture& tex, TestLog& log)
    487 {
    488 	logField(log, "Type",				glu::getTextureTargetName(glTarget(tex)));
    489 	logImage(tex, log, true);
    490 	logField(log, "Levels",				toString(tex.numLevels));
    491 	if (const TextureLayered* const lTex = dynamic_cast<const TextureLayered*>(&tex))
    492 		logField(log, "Layers",				toString(lTex->numLayers));
    493 }
    494 
    495 static void logAttachment (const Attachment& att, TestLog& log)
    496 {
    497 	logField(log, "Target",				getFramebufferTargetName(att.target));
    498 	logField(log, "Type",				getFramebufferAttachmentTypeName(attachmentType(att)));
    499 	logField(log, "Image Name",			toString(att.imageName));
    500 	if (const RenderbufferAttachment* const rAtt
    501 		= dynamic_cast<const RenderbufferAttachment*>(&att))
    502 	{
    503 		DE_UNREF(rAtt); // To shut up compiler during optimized builds.
    504 		DE_ASSERT(rAtt->renderbufferTarget == GL_RENDERBUFFER);
    505 		logField(log, "Renderbuffer Target",	"GL_RENDERBUFFER");
    506 	}
    507 	else if (const TextureAttachment* const tAtt = dynamic_cast<const TextureAttachment*>(&att))
    508 	{
    509 		logField(log, "Mipmap Level",		toString(tAtt->level));
    510 		if (const TextureFlatAttachment* const fAtt =
    511 			dynamic_cast<const TextureFlatAttachment*>(tAtt))
    512 			logField(log, "Texture Target",		getTextureTargetName(fAtt->texTarget));
    513 		else if (const TextureLayerAttachment* const lAtt =
    514 			dynamic_cast<const TextureLayerAttachment*>(tAtt))
    515 			logField(log, "Layer",				toString(lAtt->level));
    516 	}
    517 }
    518 
    519 void logFramebufferConfig (const Framebuffer& cfg, TestLog& log)
    520 {
    521 	log << TestLog::Section("Framebuffer", "Framebuffer configuration");
    522 
    523 	const string rboDesc = cfg.rbos.empty()
    524 		? "No renderbuffers were created"
    525 		: "Renderbuffers created";
    526 	log << TestLog::Section("Renderbuffers", rboDesc);
    527 	for (RboMap::const_iterator it = cfg.rbos.begin(); it != cfg.rbos.end(); ++it)
    528 	{
    529 		const string num = toString(it->first);
    530 		log << TestLog::Section(num, "Renderbuffer " + num);
    531 		logRenderbuffer(*it->second, log);
    532 		log << TestLog::EndSection;
    533 	}
    534 	log << TestLog::EndSection; // Renderbuffers
    535 
    536 	const string texDesc = cfg.textures.empty()
    537 		? "No textures were created"
    538 		: "Textures created";
    539 	log << TestLog::Section("Textures", texDesc);
    540 	for (TextureMap::const_iterator it = cfg.textures.begin();
    541 		 it != cfg.textures.end(); ++it)
    542 	{
    543 		const string num = toString(it->first);
    544 		log << TestLog::Section(num, "Texture " + num);
    545 		logTexture(*it->second, log);
    546 		log << TestLog::EndSection;
    547 	}
    548 	log << TestLog::EndSection; // Textures
    549 
    550 	const string attDesc = cfg.attachments.empty()
    551 		? "Framebuffer has no attachments"
    552 		: "Framebuffer attachments";
    553 	log << TestLog::Section("Attachments", attDesc);
    554 	for (AttachmentMap::const_iterator it = cfg.attachments.begin();
    555 		 it != cfg.attachments.end(); it++)
    556 	{
    557 		const string attPointName = getFramebufferAttachmentName(it->first);
    558 		log << TestLog::Section(attPointName, "Attachment point " + attPointName);
    559 		logAttachment(*it->second, log);
    560 		log << TestLog::EndSection;
    561 	}
    562 	log << TestLog::EndSection; // Attachments
    563 
    564 	log << TestLog::EndSection; // Framebuffer
    565 }
    566 
    567 FboBuilder::FboBuilder (GLuint fbo, GLenum target, const glw::Functions& gl)
    568 	: m_error	(GL_NO_ERROR)
    569 	, m_target	(target)
    570 	, m_gl		(gl)
    571 {
    572 	m_gl.bindFramebuffer(m_target, fbo);
    573 }
    574 
    575 FboBuilder::~FboBuilder (void)
    576 {
    577 	for (TextureMap::const_iterator it = textures.begin(); it != textures.end(); it++)
    578 	{
    579 		glDelete(*it->second, it->first, m_gl);
    580 	}
    581 	for (RboMap::const_iterator it = rbos.begin(); it != rbos.end(); it++)
    582 	{
    583 		glDelete(*it->second, it->first, m_gl);
    584 	}
    585 	m_gl.bindFramebuffer(m_target, 0);
    586 	for (Configs::const_iterator it = m_configs.begin(); it != m_configs.end(); it++)
    587 	{
    588 		delete *it;
    589 	}
    590 }
    591 
    592 void FboBuilder::checkError (void)
    593 {
    594 	const GLenum error = m_gl.getError();
    595 	if (error != GL_NO_ERROR && m_error == GL_NO_ERROR)
    596 		m_error = error;
    597 }
    598 
    599 void FboBuilder::glAttach (GLenum attPoint, const Attachment* att)
    600 {
    601 	if (att == NULL)
    602 		m_gl.framebufferRenderbuffer(m_target, attPoint, GL_RENDERBUFFER, 0);
    603 	else
    604 		attachAttachment(*att, attPoint, m_gl);
    605 	checkError();
    606 	attach(attPoint, att);
    607 }
    608 
    609 GLuint FboBuilder::glCreateTexture (const Texture& texCfg)
    610 {
    611 	const GLuint texName = glCreate(texCfg, m_gl);
    612 	checkError();
    613 	setTexture(texName, texCfg);
    614 	return texName;
    615 }
    616 
    617 GLuint FboBuilder::glCreateRbo (const Renderbuffer& rbCfg)
    618 {
    619 	const GLuint rbName = glCreate(rbCfg, m_gl);
    620 	checkError();
    621 	setRbo(rbName, rbCfg);
    622 	return rbName;
    623 }
    624 
    625 TransferFormat transferImageFormat (const ImageFormat& imgFormat)
    626 {
    627 	if (imgFormat.unsizedType == GL_NONE)
    628 		return getTransferFormat(mapGLInternalFormat(imgFormat.format));
    629 	else
    630 		return TransferFormat(imgFormat.format, imgFormat.unsizedType);
    631 }
    632 
    633 } // FboUtil
    634 } // gls
    635 } // deqp
    636