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