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 "deSTLUtil.hpp"
     32 #include <sstream>
     33 
     34 using namespace glw;
     35 using tcu::TestLog;
     36 using tcu::TextureFormat;
     37 using tcu::NotSupportedError;
     38 using glu::TransferFormat;
     39 using glu::mapGLInternalFormat;
     40 using glu::mapGLTransferFormat;
     41 using glu::getTextureFormatName;
     42 using glu::getTypeName;
     43 using glu::getFramebufferTargetName;
     44 using glu::getFramebufferAttachmentName;
     45 using glu::getFramebufferAttachmentTypeName;
     46 using glu::getTextureTargetName;
     47 using glu::getTransferFormat;
     48 using glu::ContextInfo;
     49 using glu::ContextType;
     50 using glu::RenderContext;
     51 using de::UniquePtr;
     52 using de::toString;
     53 using std::set;
     54 using std::vector;
     55 using std::string;
     56 using std::istringstream;
     57 using std::istream_iterator;
     58 
     59 namespace deqp
     60 {
     61 namespace gls
     62 {
     63 
     64 namespace FboUtil
     65 {
     66 
     67 #if defined(DE_DEBUG)
     68 static bool isFramebufferStatus (glw::GLenum fboStatus)
     69 {
     70 	return glu::getFramebufferStatusName(fboStatus) != DE_NULL;
     71 }
     72 
     73 static bool isErrorCode (glw::GLenum errorCode)
     74 {
     75 	return glu::getErrorName(errorCode) != DE_NULL;
     76 }
     77 #endif
     78 
     79 std::ostream& operator<< (std::ostream& stream, const ImageFormat& format)
     80 {
     81 	if (format.unsizedType == GL_NONE)
     82 	{
     83 		// sized format
     84 		return stream << glu::getTextureFormatStr(format.format);
     85 	}
     86 	else
     87 	{
     88 		// unsized format
     89 		return stream << "(format = " << glu::getTextureFormatStr(format.format) << ", type = " << glu::getTypeStr(format.unsizedType) << ")";
     90 	}
     91 }
     92 
     93 void FormatDB::addCoreFormat (ImageFormat format, FormatFlags newFlags)
     94 {
     95 	FormatFlags& flags = m_formatFlags[format];
     96 	flags = FormatFlags(flags | newFlags);
     97 }
     98 
     99 void FormatDB::addExtensionFormat (ImageFormat format, FormatFlags newFlags, const std::set<std::string>& requiredExtensions)
    100 {
    101 	DE_ASSERT(!requiredExtensions.empty());
    102 
    103 	{
    104 		FormatFlags& flags = m_formatFlags[format];
    105 		flags = FormatFlags(flags | newFlags);
    106 	}
    107 
    108 	{
    109 		std::set<ExtensionInfo>&	extensionInfo	= m_formatExtensions[format];
    110 		ExtensionInfo				extensionRecord;
    111 
    112 		extensionRecord.flags				= newFlags;
    113 		extensionRecord.requiredExtensions	= requiredExtensions;
    114 
    115 		DE_ASSERT(!de::contains(extensionInfo, extensionRecord)); // extensions specified only once
    116 		extensionInfo.insert(extensionRecord);
    117 	}
    118 }
    119 
    120 // Not too fast at the moment, might consider indexing?
    121 Formats FormatDB::getFormats (FormatFlags requirements) const
    122 {
    123 	Formats ret;
    124 	for (FormatMap::const_iterator it = m_formatFlags.begin(); it != m_formatFlags.end(); it++)
    125 	{
    126 		if ((it->second & requirements) == requirements)
    127 			ret.insert(it->first);
    128 	}
    129 	return ret;
    130 }
    131 
    132 bool FormatDB::isKnownFormat (ImageFormat format) const
    133 {
    134 	return de::contains(m_formatFlags, format);
    135 }
    136 
    137 FormatFlags FormatDB::getFormatInfo (ImageFormat format) const
    138 {
    139 	DE_ASSERT(de::contains(m_formatFlags, format));
    140 	return de::lookup(m_formatFlags, format);
    141 }
    142 
    143 std::set<std::set<std::string> > FormatDB::getFormatFeatureExtensions (ImageFormat format, FormatFlags requirements) const
    144 {
    145 	DE_ASSERT(de::contains(m_formatExtensions, format));
    146 
    147 	const std::set<ExtensionInfo>&		extensionInfo	= de::lookup(m_formatExtensions, format);
    148 	std::set<std::set<std::string> >	ret;
    149 
    150 	for (std::set<ExtensionInfo>::const_iterator it = extensionInfo.begin(); it != extensionInfo.end(); ++it)
    151 	{
    152 		if ((it->flags & requirements) == requirements)
    153 			ret.insert(it->requiredExtensions);
    154 	}
    155 
    156 	return ret;
    157 }
    158 
    159 bool FormatDB::ExtensionInfo::operator< (const ExtensionInfo& other) const
    160 {
    161 	return (requiredExtensions < other.requiredExtensions) ||
    162 		   ((requiredExtensions == other.requiredExtensions) && (flags < other.flags));
    163 }
    164 
    165 static bool detectGLESCompatibleContext (const RenderContext& ctx, int requiredMajor, int requiredMinor)
    166 {
    167 	const glw::Functions&	gl				= ctx.getFunctions();
    168 	glw::GLint				majorVersion	= 0;
    169 	glw::GLint				minorVersion	= 0;
    170 
    171 	// Detect compatible GLES context by querying GL_MAJOR_VERSION.
    172 	// This query does not exist on GLES2 so a failing query implies
    173 	// GLES2 context.
    174 
    175 	gl.getIntegerv(GL_MAJOR_VERSION, &majorVersion);
    176 	if (gl.getError() != GL_NO_ERROR)
    177 		majorVersion = 2;
    178 
    179 	gl.getIntegerv(GL_MINOR_VERSION, &minorVersion);
    180 	if (gl.getError() != GL_NO_ERROR)
    181 		minorVersion = 0;
    182 
    183 	return (majorVersion > requiredMajor) || (majorVersion == requiredMajor && minorVersion >= requiredMinor);
    184 }
    185 
    186 static bool checkExtensionSupport (const ContextInfo& ctxInfo, const RenderContext& ctx, const std::string& extension)
    187 {
    188 	if (de::beginsWith(extension, "GL_"))
    189 		return ctxInfo.isExtensionSupported(extension.c_str());
    190 	else if (extension == "DEQP_gles3_core_compatible")
    191 		return detectGLESCompatibleContext(ctx, 3, 0);
    192 	else if (extension == "DEQP_gles31_core_compatible")
    193 		return detectGLESCompatibleContext(ctx, 3, 1);
    194 	else
    195 	{
    196 		DE_ASSERT(false);
    197 		return false;
    198 	}
    199 }
    200 
    201 bool checkExtensionSupport (const RenderContext& ctx, const std::string& extension)
    202 {
    203 	const de::UniquePtr<ContextInfo> info(ContextInfo::create(ctx));
    204 	return checkExtensionSupport(*info, ctx, extension);
    205 }
    206 
    207 std::string getExtensionDescription (const std::string& extension)
    208 {
    209 	if (de::beginsWith(extension, "GL_"))
    210 		return extension;
    211 	else if (extension == "DEQP_gles3_core_compatible")
    212 		return "GLES3 compatible context";
    213 	else if (extension == "DEQP_gles31_core_compatible")
    214 		return "GLES3.1 compatible context";
    215 	else
    216 	{
    217 		DE_ASSERT(false);
    218 		return "";
    219 	}
    220 }
    221 
    222 void addFormats (FormatDB& db, FormatEntries stdFmts)
    223 {
    224 	for (const FormatEntry* it = stdFmts.begin(); it != stdFmts.end(); it++)
    225 	{
    226 		for (const FormatKey* it2 = it->second.begin(); it2 != it->second.end(); it2++)
    227 			db.addCoreFormat(formatKeyInfo(*it2), it->first);
    228 	}
    229 }
    230 
    231 void addExtFormats (FormatDB& db, FormatExtEntries extFmts, const RenderContext* ctx)
    232 {
    233 	const UniquePtr<ContextInfo> ctxInfo(ctx != DE_NULL ? ContextInfo::create(*ctx) : DE_NULL);
    234 	for (const FormatExtEntry* entryIt = extFmts.begin(); entryIt != extFmts.end(); entryIt++)
    235 	{
    236 		bool					supported			= true;
    237 		std::set<std::string>	requiredExtensions;
    238 
    239 		// parse required extensions
    240 		{
    241 			istringstream tokenStream(string(entryIt->extensions));
    242 			istream_iterator<string> tokens((tokenStream)), end;
    243 
    244 			while (tokens != end)
    245 			{
    246 				requiredExtensions.insert(*tokens);
    247 				++tokens;
    248 			}
    249 		}
    250 
    251 		// check support
    252 		if (ctxInfo)
    253 		{
    254 			for (std::set<std::string>::const_iterator extIt = requiredExtensions.begin(); extIt != requiredExtensions.end(); ++extIt)
    255 			{
    256 				if (!checkExtensionSupport(*ctxInfo, *ctx, *extIt))
    257 				{
    258 					supported = false;
    259 					break;
    260 				}
    261 			}
    262 		}
    263 
    264 		if (supported)
    265 			for (const FormatKey* i2 = entryIt->formats.begin(); i2 != entryIt->formats.end(); i2++)
    266 				db.addExtensionFormat(formatKeyInfo(*i2), FormatFlags(entryIt->flags), requiredExtensions);
    267 	}
    268 }
    269 
    270 FormatFlags formatFlag (GLenum context)
    271 {
    272 	switch (context)
    273 	{
    274 		case GL_NONE:
    275 			return FormatFlags(0);
    276 		case GL_RENDERBUFFER:
    277 			return RENDERBUFFER_VALID;
    278 		case GL_TEXTURE:
    279 			return TEXTURE_VALID;
    280 		case GL_STENCIL_ATTACHMENT:
    281 			return STENCIL_RENDERABLE;
    282 		case GL_DEPTH_ATTACHMENT:
    283 			return DEPTH_RENDERABLE;
    284 		default:
    285 			DE_ASSERT(context >= GL_COLOR_ATTACHMENT0 && context <= GL_COLOR_ATTACHMENT15);
    286 			return COLOR_RENDERABLE;
    287 	}
    288 }
    289 
    290 static FormatFlags getAttachmentRenderabilityFlag (GLenum attachment)
    291 {
    292 	switch (attachment)
    293 	{
    294 		case GL_STENCIL_ATTACHMENT:			return STENCIL_RENDERABLE;
    295 		case GL_DEPTH_ATTACHMENT:			return DEPTH_RENDERABLE;
    296 
    297 		default:
    298 			DE_ASSERT(attachment >= GL_COLOR_ATTACHMENT0 && attachment <= GL_COLOR_ATTACHMENT15);
    299 			return COLOR_RENDERABLE;
    300 	}
    301 }
    302 
    303 namespace config {
    304 
    305 GLsizei	imageNumSamples	(const Image& img)
    306 {
    307 	if (const Renderbuffer* rbo = dynamic_cast<const Renderbuffer*>(&img))
    308 		return rbo->numSamples;
    309 	return 0;
    310 }
    311 
    312 static GLenum glTarget (const Image& img)
    313 {
    314 	if (dynamic_cast<const Renderbuffer*>(&img) != DE_NULL)
    315 		return GL_RENDERBUFFER;
    316 	if (dynamic_cast<const Texture2D*>(&img) != DE_NULL)
    317 		return GL_TEXTURE_2D;
    318 	if (dynamic_cast<const TextureCubeMap*>(&img) != DE_NULL)
    319 		return GL_TEXTURE_CUBE_MAP;
    320 	if (dynamic_cast<const Texture3D*>(&img) != DE_NULL)
    321 		return GL_TEXTURE_3D;
    322 	if (dynamic_cast<const Texture2DArray*>(&img) != DE_NULL)
    323 		return GL_TEXTURE_2D_ARRAY;
    324 
    325 	DE_FATAL("Impossible image type");
    326 	return GL_NONE;
    327 }
    328 
    329 static void glInitFlat (const TextureFlat& cfg, GLenum target, const glw::Functions& gl)
    330 {
    331 	const TransferFormat format = transferImageFormat(cfg.internalFormat);
    332 	GLint w = cfg.width;
    333 	GLint h = cfg.height;
    334 	for (GLint level = 0; level < cfg.numLevels; level++)
    335 	{
    336 		gl.texImage2D(target, level, cfg.internalFormat.format, w, h, 0,
    337 					  format.format, format.dataType, DE_NULL);
    338 		w = de::max(1, w / 2);
    339 		h = de::max(1, h / 2);
    340 	}
    341 }
    342 
    343 static void glInitLayered (const TextureLayered& cfg,
    344 						   GLint depth_divider, const glw::Functions& gl)
    345 {
    346 	const TransferFormat format = transferImageFormat(cfg.internalFormat);
    347 	GLint w = cfg.width;
    348 	GLint h = cfg.height;
    349 	GLint depth = cfg.numLayers;
    350 	for (GLint level = 0; level < cfg.numLevels; level++)
    351 	{
    352 		gl.texImage3D(glTarget(cfg), level, cfg.internalFormat.format, w, h, depth, 0,
    353 					  format.format, format.dataType, DE_NULL);
    354 		w = de::max(1, w / 2);
    355 		h = de::max(1, h / 2);
    356 		depth = de::max(1, depth / depth_divider);
    357 	}
    358 }
    359 
    360 static void glInit (const Texture& cfg, const glw::Functions& gl)
    361 {
    362 	if (const Texture2D* t2d = dynamic_cast<const Texture2D*>(&cfg))
    363 		glInitFlat(*t2d, glTarget(*t2d), gl);
    364 	else if (const TextureCubeMap* tcm = dynamic_cast<const TextureCubeMap*>(&cfg))
    365 	{
    366 		// \todo [2013-12-05 lauri]
    367 		// move this to glu or someplace sensible (this array is already
    368 		// present in duplicates)
    369 		static const GLenum s_cubeMapFaces[] =
    370 			{
    371 				GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
    372 				GL_TEXTURE_CUBE_MAP_POSITIVE_X,
    373 				GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
    374 				GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
    375 				GL_TEXTURE_CUBE_MAP_NEGATIVE_Z,
    376 				GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
    377 			};
    378 		const Range<GLenum> range = GLS_ARRAY_RANGE(s_cubeMapFaces);
    379 		for (const GLenum* it = range.begin(); it != range.end(); it++)
    380 			glInitFlat(*tcm, *it, gl);
    381 	}
    382 	else if (const Texture3D* t3d = dynamic_cast<const Texture3D*>(&cfg))
    383 		glInitLayered(*t3d, 2, gl);
    384 	else if (const Texture2DArray* t2a = dynamic_cast<const Texture2DArray*>(&cfg))
    385 		glInitLayered(*t2a, 1, gl);
    386 }
    387 
    388 static GLuint glCreate (const Image& cfg, const glw::Functions& gl)
    389 {
    390 	GLuint ret = 0;
    391 	if (const Renderbuffer* const rbo = dynamic_cast<const Renderbuffer*>(&cfg))
    392 	{
    393 		gl.genRenderbuffers(1, &ret);
    394 		gl.bindRenderbuffer(GL_RENDERBUFFER, ret);
    395 
    396 		if (rbo->numSamples == 0)
    397 			gl.renderbufferStorage(GL_RENDERBUFFER, rbo->internalFormat.format,
    398 								   rbo->width, rbo->height);
    399 		else
    400 			gl.renderbufferStorageMultisample(
    401 				GL_RENDERBUFFER, rbo->numSamples, rbo->internalFormat.format,
    402 				rbo->width, rbo->height);
    403 
    404 		gl.bindRenderbuffer(GL_RENDERBUFFER, 0);
    405 	}
    406 	else if (const Texture* const tex = dynamic_cast<const Texture*>(&cfg))
    407 	{
    408 		gl.genTextures(1, &ret);
    409 		gl.bindTexture(glTarget(*tex), ret);
    410 		glInit(*tex, gl);
    411 		gl.bindTexture(glTarget(*tex), 0);
    412 	}
    413 	else
    414 		DE_FATAL("Impossible image type");
    415 	return ret;
    416 }
    417 
    418 static void glDelete (const Image& cfg, GLuint img, const glw::Functions& gl)
    419 {
    420 	if (dynamic_cast<const Renderbuffer*>(&cfg) != DE_NULL)
    421 		gl.deleteRenderbuffers(1, &img);
    422 	else if (dynamic_cast<const Texture*>(&cfg) != DE_NULL)
    423 		gl.deleteTextures(1, &img);
    424 	else
    425 		DE_FATAL("Impossible image type");
    426 }
    427 
    428 static void attachAttachment (const Attachment& att, GLenum attPoint,
    429 							  const glw::Functions& gl)
    430 {
    431 	if (const RenderbufferAttachment* const rAtt =
    432 		dynamic_cast<const RenderbufferAttachment*>(&att))
    433 		gl.framebufferRenderbuffer(rAtt->target, attPoint,
    434 								   rAtt->renderbufferTarget, rAtt->imageName);
    435 	else if (const TextureFlatAttachment* const fAtt =
    436 			 dynamic_cast<const TextureFlatAttachment*>(&att))
    437 		gl.framebufferTexture2D(fAtt->target, attPoint,
    438 								fAtt->texTarget, fAtt->imageName, fAtt->level);
    439 	else if (const TextureLayerAttachment* const lAtt =
    440 			 dynamic_cast<const TextureLayerAttachment*>(&att))
    441 		gl.framebufferTextureLayer(lAtt->target, attPoint,
    442 								   lAtt->imageName, lAtt->level, lAtt->layer);
    443 	else
    444 		DE_FATAL("Impossible attachment type");
    445 }
    446 
    447 GLenum attachmentType (const Attachment& att)
    448 {
    449 	if (dynamic_cast<const RenderbufferAttachment*>(&att) != DE_NULL)
    450 		return GL_RENDERBUFFER;
    451 	else if (dynamic_cast<const TextureAttachment*>(&att) != DE_NULL)
    452 		return GL_TEXTURE;
    453 
    454 	DE_FATAL("Impossible attachment type");
    455 	return GL_NONE;
    456 }
    457 
    458 static GLsizei textureLayer (const TextureAttachment& tAtt)
    459 {
    460 	if (dynamic_cast<const TextureFlatAttachment*>(&tAtt) != DE_NULL)
    461 		return 0;
    462 	else if (const TextureLayerAttachment* const lAtt =
    463 			 dynamic_cast<const TextureLayerAttachment*>(&tAtt))
    464 		return lAtt->layer;
    465 
    466 	DE_FATAL("Impossible attachment type");
    467 	return 0;
    468 }
    469 
    470 static void checkAttachmentCompleteness (Checker& cctx, const Attachment& attachment,
    471 										 GLenum attPoint, const Image* image,
    472 										 const FormatDB& db)
    473 {
    474 	// GLES2 4.4.5 / GLES3 4.4.4, "Framebuffer attachment completeness"
    475 
    476 	if (const TextureAttachment* const texAtt =
    477 		dynamic_cast<const TextureAttachment*>(&attachment))
    478 		if (const TextureLayered* const ltex = dynamic_cast<const TextureLayered*>(image))
    479 		{
    480 			// GLES3: "If the value of FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is
    481 			// TEXTURE and the value of FRAMEBUFFER_ATTACHMENT_OBJECT_NAME names a
    482 			// three-dimensional texture, then the value of
    483 			// FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER must be smaller than the depth
    484 			// of the texture.
    485 			//
    486 			// GLES3: "If the value of FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is
    487 			// TEXTURE and the value of FRAMEBUFFER_ATTACHMENT_OBJECT_NAME names a
    488 			// two-dimensional array texture, then the value of
    489 			// FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER must be smaller than the
    490 			// number of layers in the texture.
    491 
    492 			if (textureLayer(*texAtt) >= ltex->numLayers)
    493 				cctx.addFBOStatus(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT, "Attached layer index is larger than present");
    494 		}
    495 
    496 	// "The width and height of image are non-zero."
    497 	if (image->width == 0 || image->height == 0)
    498 		cctx.addFBOStatus(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT, "Width and height of an image are not non-zero");
    499 
    500 	// Check for renderability
    501 	if (db.isKnownFormat(image->internalFormat))
    502 	{
    503 		const FormatFlags flags = db.getFormatInfo(image->internalFormat);
    504 
    505 		// If the format does not have the proper renderability flag, the
    506 		// completeness check _must_ fail.
    507 		if ((flags & getAttachmentRenderabilityFlag(attPoint)) == 0)
    508 			cctx.addFBOStatus(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT, "Attachment format is not renderable in this attachment");
    509 		// If the format is only optionally renderable, the completeness check _can_ fail.
    510 		else if ((flags & REQUIRED_RENDERABLE) == 0)
    511 			cctx.addPotentialFBOStatus(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT, "Attachment format is not required renderable");
    512 	}
    513 	else
    514 		cctx.addFBOStatus(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT, "Attachment format is not legal");
    515 }
    516 
    517 } // namespace config
    518 
    519 using namespace config;
    520 
    521 Checker::Checker (const glu::RenderContext& ctx)
    522 	: m_renderCtx(ctx)
    523 {
    524 	m_statusCodes.setAllowComplete(true);
    525 }
    526 
    527 void Checker::addGLError (glw::GLenum error, const char* description)
    528 {
    529 	m_statusCodes.addErrorCode(error, description);
    530 	m_statusCodes.setAllowComplete(false);
    531 }
    532 
    533 void Checker::addPotentialGLError (glw::GLenum error, const char* description)
    534 {
    535 	m_statusCodes.addErrorCode(error, description);
    536 }
    537 
    538 void Checker::addFBOStatus (GLenum status, const char* description)
    539 {
    540 	m_statusCodes.addFBOErrorStatus(status, description);
    541 	m_statusCodes.setAllowComplete(false);
    542 }
    543 
    544 void Checker::addPotentialFBOStatus (GLenum status, const char* description)
    545 {
    546 	m_statusCodes.addFBOErrorStatus(status, description);
    547 }
    548 
    549 FboVerifier::FboVerifier (const FormatDB& formats, CheckerFactory& factory, const glu::RenderContext& renderCtx)
    550 	: m_formats		(formats)
    551 	, m_factory		(factory)
    552 	, m_renderCtx	(renderCtx)
    553 {
    554 }
    555 
    556 /*--------------------------------------------------------------------*//*!
    557  * \brief Return acceptable framebuffer status codes.
    558  *
    559  * This function examines the framebuffer configuration descriptor `fboConfig`
    560  * and returns the set of status codes that `glCheckFramebufferStatus` is
    561  * allowed to return on a conforming implementation when given a framebuffer
    562  * whose configuration adheres to `fboConfig`.
    563  *
    564  * The returned set is guaranteed to be non-empty, but it may contain multiple
    565  * INCOMPLETE statuses (if there are multiple errors in the spec), or or a mix
    566  * of COMPLETE and INCOMPLETE statuses (if supporting a FBO with this spec is
    567  * optional). Furthermore, the statuses may contain GL error codes, which
    568  * indicate that trying to create a framebuffer configuration like this could
    569  * have failed with an error (if one was checked for) even before
    570  * `glCheckFramebufferStatus` was ever called.
    571  *
    572  *//*--------------------------------------------------------------------*/
    573 ValidStatusCodes FboVerifier::validStatusCodes (const Framebuffer& fboConfig) const
    574 {
    575 	const AttachmentMap& atts = fboConfig.attachments;
    576 	const UniquePtr<Checker> cctx(m_factory.createChecker(m_renderCtx));
    577 
    578 	for (TextureMap::const_iterator it = fboConfig.textures.begin();
    579 		 it != fboConfig.textures.end(); it++)
    580 	{
    581 		std::string errorDescription;
    582 
    583 		if (m_formats.isKnownFormat(it->second->internalFormat))
    584 		{
    585 			const FormatFlags flags = m_formats.getFormatInfo(it->second->internalFormat);
    586 
    587 			if ((flags & TEXTURE_VALID) == 0)
    588 				errorDescription = "Format " + de::toString(it->second->internalFormat) + " is not a valid format for a texture";
    589 		}
    590 		else if (it->second->internalFormat.unsizedType == GL_NONE)
    591 		{
    592 			// sized format
    593 			errorDescription = "Format " + de::toString(it->second->internalFormat) + " does not exist";
    594 		}
    595 		else
    596 		{
    597 			// unsized type-format pair
    598 			errorDescription = "Format " + de::toString(it->second->internalFormat) + " is not a legal format";
    599 		}
    600 
    601 		if (!errorDescription.empty())
    602 		{
    603 			cctx->addGLError(GL_INVALID_ENUM,		errorDescription.c_str());
    604 			cctx->addGLError(GL_INVALID_OPERATION,	errorDescription.c_str());
    605 			cctx->addGLError(GL_INVALID_VALUE,		errorDescription.c_str());
    606 		}
    607 	}
    608 
    609 	for (RboMap::const_iterator it = fboConfig.rbos.begin(); it != fboConfig.rbos.end(); it++)
    610 	{
    611 		if (m_formats.isKnownFormat(it->second->internalFormat))
    612 		{
    613 			const FormatFlags flags = m_formats.getFormatInfo(it->second->internalFormat);
    614 			if ((flags & RENDERBUFFER_VALID) == 0)
    615 			{
    616 				const std::string reason = "Format " + de::toString(it->second->internalFormat) + " is not a valid format for a renderbuffer";
    617 				cctx->addGLError(GL_INVALID_ENUM, reason.c_str());
    618 			}
    619 		}
    620 		else
    621 		{
    622 			const std::string reason = "Internal format " + de::toString(it->second->internalFormat) + " does not exist";
    623 			cctx->addGLError(GL_INVALID_ENUM, reason.c_str());
    624 		}
    625 	}
    626 
    627 	// "There is at least one image attached to the framebuffer."
    628 	// \todo support XXX_framebuffer_no_attachments
    629 	if (atts.empty())
    630 		cctx->addFBOStatus(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT, "No images attached to the framebuffer");
    631 
    632 	for (AttachmentMap::const_iterator it = atts.begin(); it != atts.end(); it++)
    633 	{
    634 		const GLenum attPoint = it->first;
    635 		const Attachment& att = *it->second;
    636 		const Image* const image = fboConfig.getImage(attachmentType(att), att.imageName);
    637 
    638 		checkAttachmentCompleteness(*cctx, att, attPoint, image, m_formats);
    639 		cctx->check(it->first, *it->second, image);
    640 	}
    641 
    642 	return cctx->getStatusCodes();
    643 }
    644 
    645 
    646 void Framebuffer::attach (glw::GLenum attPoint, const Attachment* att)
    647 {
    648 	if (att == DE_NULL)
    649 		attachments.erase(attPoint);
    650 	else
    651 		attachments[attPoint] = att;
    652 }
    653 
    654 const Image* Framebuffer::getImage (GLenum type, glw::GLuint imgName) const
    655 {
    656 	switch (type)
    657 	{
    658 		case GL_TEXTURE:
    659 			return de::lookupDefault(textures, imgName, DE_NULL);
    660 		case GL_RENDERBUFFER:
    661 			return de::lookupDefault(rbos, imgName, DE_NULL);
    662 		default:
    663 			DE_FATAL("Bad image type");
    664 	}
    665 	return DE_NULL; // shut up compiler warning
    666 }
    667 
    668 void Framebuffer::setTexture (glw::GLuint texName, const Texture& texCfg)
    669 {
    670 	textures[texName] = &texCfg;
    671 }
    672 
    673 void Framebuffer::setRbo (glw::GLuint rbName, const Renderbuffer& rbCfg)
    674 {
    675 	rbos[rbName] = &rbCfg;
    676 }
    677 
    678 static void logField (TestLog& log, const string& field, const string& value)
    679 {
    680 	log << TestLog::Message << field << ": " << value << TestLog::EndMessage;
    681 }
    682 
    683 static void logImage (const Image& img, TestLog& log, bool useType)
    684 {
    685 	const GLenum type = img.internalFormat.unsizedType;
    686 	logField(log, "Internal format",	getTextureFormatName(img.internalFormat.format));
    687 	if (useType && type != GL_NONE)
    688 		logField(log, "Format type",	getTypeName(type));
    689 	logField(log, "Width",				toString(img.width));
    690 	logField(log, "Height",				toString(img.height));
    691 }
    692 
    693 static void logRenderbuffer (const Renderbuffer& rbo, TestLog& log)
    694 {
    695 	logImage(rbo, log, false);
    696 	logField(log, "Samples",			toString(rbo.numSamples));
    697 }
    698 
    699 static void logTexture (const Texture& tex, TestLog& log)
    700 {
    701 	logField(log, "Type",				glu::getTextureTargetName(glTarget(tex)));
    702 	logImage(tex, log, true);
    703 	logField(log, "Levels",				toString(tex.numLevels));
    704 	if (const TextureLayered* const lTex = dynamic_cast<const TextureLayered*>(&tex))
    705 		logField(log, "Layers",				toString(lTex->numLayers));
    706 }
    707 
    708 static void logAttachment (const Attachment& att, TestLog& log)
    709 {
    710 	logField(log, "Target",				getFramebufferTargetName(att.target));
    711 	logField(log, "Type",				getFramebufferAttachmentTypeName(attachmentType(att)));
    712 	logField(log, "Image Name",			toString(att.imageName));
    713 	if (const RenderbufferAttachment* const rAtt
    714 		= dynamic_cast<const RenderbufferAttachment*>(&att))
    715 	{
    716 		DE_UNREF(rAtt); // To shut up compiler during optimized builds.
    717 		DE_ASSERT(rAtt->renderbufferTarget == GL_RENDERBUFFER);
    718 		logField(log, "Renderbuffer Target",	"GL_RENDERBUFFER");
    719 	}
    720 	else if (const TextureAttachment* const tAtt = dynamic_cast<const TextureAttachment*>(&att))
    721 	{
    722 		logField(log, "Mipmap Level",		toString(tAtt->level));
    723 		if (const TextureFlatAttachment* const fAtt =
    724 			dynamic_cast<const TextureFlatAttachment*>(tAtt))
    725 			logField(log, "Texture Target",		getTextureTargetName(fAtt->texTarget));
    726 		else if (const TextureLayerAttachment* const lAtt =
    727 			dynamic_cast<const TextureLayerAttachment*>(tAtt))
    728 			logField(log, "Layer",				toString(lAtt->level));
    729 	}
    730 }
    731 
    732 void logFramebufferConfig (const Framebuffer& cfg, TestLog& log)
    733 {
    734 	log << TestLog::Section("Framebuffer", "Framebuffer configuration");
    735 
    736 	for (RboMap::const_iterator it = cfg.rbos.begin(); it != cfg.rbos.end(); ++it)
    737 	{
    738 		const string				num			= toString(it->first);
    739 		const tcu::ScopedLogSection	subsection	(log, num, "Renderbuffer " + num);
    740 
    741 		logRenderbuffer(*it->second, log);
    742 	}
    743 
    744 	for (TextureMap::const_iterator it = cfg.textures.begin();
    745 		it != cfg.textures.end(); ++it)
    746 	{
    747 		const string				num			= toString(it->first);
    748 		const tcu::ScopedLogSection	subsection	(log, num, "Texture " + num);
    749 
    750 		logTexture(*it->second, log);
    751 	}
    752 
    753 	const string attDesc = cfg.attachments.empty()
    754 		? "Framebuffer has no attachments"
    755 		: "Framebuffer attachments";
    756 	log << TestLog::Section("Attachments", attDesc);
    757 	for (AttachmentMap::const_iterator it = cfg.attachments.begin();
    758 		 it != cfg.attachments.end(); it++)
    759 	{
    760 		const string attPointName = getFramebufferAttachmentName(it->first);
    761 		log << TestLog::Section(attPointName, "Attachment point " + attPointName);
    762 		logAttachment(*it->second, log);
    763 		log << TestLog::EndSection;
    764 	}
    765 	log << TestLog::EndSection; // Attachments
    766 
    767 	log << TestLog::EndSection; // Framebuffer
    768 }
    769 
    770 ValidStatusCodes::ValidStatusCodes (void)
    771 	: m_allowComplete(false)
    772 {
    773 }
    774 
    775 bool ValidStatusCodes::isFBOStatusValid (glw::GLenum fboStatus) const
    776 {
    777 	if (fboStatus == GL_FRAMEBUFFER_COMPLETE)
    778 		return m_allowComplete;
    779 	else
    780 	{
    781 		// rule violation exists?
    782 		for (int ndx = 0; ndx < (int)m_errorStatuses.size(); ++ndx)
    783 		{
    784 			if (m_errorStatuses[ndx].errorCode == fboStatus)
    785 				return true;
    786 		}
    787 		return false;
    788 	}
    789 }
    790 
    791 bool ValidStatusCodes::isFBOStatusRequired (glw::GLenum fboStatus) const
    792 {
    793 	if (fboStatus == GL_FRAMEBUFFER_COMPLETE)
    794 		return m_allowComplete && m_errorStatuses.empty();
    795 	else
    796 		// fboStatus is the only allowed error status and succeeding is forbidden
    797 		return !m_allowComplete && m_errorStatuses.size() == 1 && m_errorStatuses.front().errorCode == fboStatus;
    798 }
    799 
    800 bool ValidStatusCodes::isErrorCodeValid (glw::GLenum errorCode) const
    801 {
    802 	if (errorCode == GL_NO_ERROR)
    803 		return m_errorCodes.empty();
    804 	else
    805 	{
    806 		// rule violation exists?
    807 		for (int ndx = 0; ndx < (int)m_errorCodes.size(); ++ndx)
    808 		{
    809 			if (m_errorCodes[ndx].errorCode == errorCode)
    810 				return true;
    811 		}
    812 		return false;
    813 	}
    814 }
    815 
    816 bool ValidStatusCodes::isErrorCodeRequired (glw::GLenum errorCode) const
    817 {
    818 	if (m_errorCodes.empty() && errorCode == GL_NO_ERROR)
    819 		return true;
    820 	else
    821 		// only this error code listed
    822 		return m_errorCodes.size() == 1 && m_errorCodes.front().errorCode == errorCode;
    823 }
    824 
    825 void ValidStatusCodes::addErrorCode (glw::GLenum error, const char* description)
    826 {
    827 	DE_ASSERT(isErrorCode(error));
    828 	DE_ASSERT(error != GL_NO_ERROR);
    829 	addViolation(m_errorCodes, error, description);
    830 }
    831 
    832 void ValidStatusCodes::addFBOErrorStatus (glw::GLenum status, const char* description)
    833 {
    834 	DE_ASSERT(isFramebufferStatus(status));
    835 	DE_ASSERT(status != GL_FRAMEBUFFER_COMPLETE);
    836 	addViolation(m_errorStatuses, status, description);
    837 }
    838 
    839 void ValidStatusCodes::setAllowComplete (bool b)
    840 {
    841 	m_allowComplete = b;
    842 }
    843 
    844 void ValidStatusCodes::logLegalResults (tcu::TestLog& log) const
    845 {
    846 	tcu::MessageBuilder			msg				(&log);
    847 	std::vector<std::string>	validResults;
    848 
    849 	for (int ndx = 0; ndx < (int)m_errorCodes.size(); ++ndx)
    850 		validResults.push_back(std::string(glu::getErrorName(m_errorCodes[ndx].errorCode)) + " (during FBO initialization)");
    851 
    852 	for (int ndx = 0; ndx < (int)m_errorStatuses.size(); ++ndx)
    853 		validResults.push_back(glu::getFramebufferStatusName(m_errorStatuses[ndx].errorCode));
    854 
    855 	if (m_allowComplete)
    856 		validResults.push_back("GL_FRAMEBUFFER_COMPLETE");
    857 
    858 	msg << "Expected ";
    859 	if (validResults.size() > 1)
    860 		msg << "one of ";
    861 
    862 	for (int ndx = 0; ndx < (int)validResults.size(); ++ndx)
    863 	{
    864 		const bool last			= ((ndx + 1) == (int)validResults.size());
    865 		const bool secondToLast	= ((ndx + 2) == (int)validResults.size());
    866 
    867 		msg << validResults[ndx];
    868 		if (!last)
    869 			msg << ((secondToLast) ? (" or ") : (", "));
    870 	}
    871 
    872 	msg << "." << TestLog::EndMessage;
    873 }
    874 
    875 void ValidStatusCodes::logRules (tcu::TestLog& log) const
    876 {
    877 	const tcu::ScopedLogSection section(log, "Rules", "Active rules");
    878 
    879 	for (int ndx = 0; ndx < (int)m_errorCodes.size(); ++ndx)
    880 		logRule(log, glu::getErrorName(m_errorCodes[ndx].errorCode), m_errorCodes[ndx].rules);
    881 
    882 	for (int ndx = 0; ndx < (int)m_errorStatuses.size(); ++ndx)
    883 		logRule(log, glu::getFramebufferStatusName(m_errorStatuses[ndx].errorCode), m_errorStatuses[ndx].rules);
    884 
    885 	if (m_allowComplete)
    886 	{
    887 		std::set<std::string> defaultRule;
    888 		defaultRule.insert("FBO is complete");
    889 		logRule(log, "GL_FRAMEBUFFER_COMPLETE", defaultRule);
    890 	}
    891 }
    892 
    893 void ValidStatusCodes::logRule (tcu::TestLog& log, const std::string& ruleName, const std::set<std::string>& rules) const
    894 {
    895 	if (!rules.empty())
    896 	{
    897 		const tcu::ScopedLogSection		section	(log, ruleName, ruleName);
    898 		tcu::MessageBuilder				msg		(&log);
    899 
    900 		msg << "Rules:\n";
    901 		for (std::set<std::string>::const_iterator it = rules.begin(); it != rules.end(); ++it)
    902 			msg << "\t * " << *it << "\n";
    903 		msg << TestLog::EndMessage;
    904 	}
    905 }
    906 
    907 void ValidStatusCodes::addViolation (std::vector<RuleViolation>& dst, glw::GLenum code, const char* description) const
    908 {
    909 	// rule violation already exists?
    910 	for (int ndx = 0; ndx < (int)dst.size(); ++ndx)
    911 	{
    912 		if (dst[ndx].errorCode == code)
    913 		{
    914 			dst[ndx].rules.insert(std::string(description));
    915 			return;
    916 		}
    917 	}
    918 
    919 	// new violation
    920 	{
    921 		RuleViolation violation;
    922 
    923 		violation.errorCode = code;
    924 		violation.rules.insert(std::string(description));
    925 
    926 		dst.push_back(violation);
    927 	}
    928 }
    929 
    930 FboBuilder::FboBuilder (GLuint fbo, GLenum target, const glw::Functions& gl)
    931 	: m_error	(GL_NO_ERROR)
    932 	, m_target	(target)
    933 	, m_gl		(gl)
    934 {
    935 	m_gl.bindFramebuffer(m_target, fbo);
    936 }
    937 
    938 FboBuilder::~FboBuilder (void)
    939 {
    940 	for (TextureMap::const_iterator it = textures.begin(); it != textures.end(); it++)
    941 	{
    942 		glDelete(*it->second, it->first, m_gl);
    943 	}
    944 	for (RboMap::const_iterator it = rbos.begin(); it != rbos.end(); it++)
    945 	{
    946 		glDelete(*it->second, it->first, m_gl);
    947 	}
    948 	m_gl.bindFramebuffer(m_target, 0);
    949 	for (Configs::const_iterator it = m_configs.begin(); it != m_configs.end(); it++)
    950 	{
    951 		delete *it;
    952 	}
    953 }
    954 
    955 void FboBuilder::checkError (void)
    956 {
    957 	const GLenum error = m_gl.getError();
    958 	if (error != GL_NO_ERROR && m_error == GL_NO_ERROR)
    959 		m_error = error;
    960 }
    961 
    962 void FboBuilder::glAttach (GLenum attPoint, const Attachment* att)
    963 {
    964 	if (att == NULL)
    965 		m_gl.framebufferRenderbuffer(m_target, attPoint, GL_RENDERBUFFER, 0);
    966 	else
    967 		attachAttachment(*att, attPoint, m_gl);
    968 	checkError();
    969 	attach(attPoint, att);
    970 }
    971 
    972 GLuint FboBuilder::glCreateTexture (const Texture& texCfg)
    973 {
    974 	const GLuint texName = glCreate(texCfg, m_gl);
    975 	checkError();
    976 	setTexture(texName, texCfg);
    977 	return texName;
    978 }
    979 
    980 GLuint FboBuilder::glCreateRbo (const Renderbuffer& rbCfg)
    981 {
    982 	const GLuint rbName = glCreate(rbCfg, m_gl);
    983 	checkError();
    984 	setRbo(rbName, rbCfg);
    985 	return rbName;
    986 }
    987 
    988 TransferFormat transferImageFormat (const ImageFormat& imgFormat)
    989 {
    990 	if (imgFormat.unsizedType == GL_NONE)
    991 		return getTransferFormat(mapGLInternalFormat(imgFormat.format));
    992 	else
    993 		return TransferFormat(imgFormat.format, imgFormat.unsizedType);
    994 }
    995 
    996 } // FboUtil
    997 } // gls
    998 } // deqp
    999