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
165static 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
186static 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
201bool 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
207std::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
222void 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
231void 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
270FormatFlags 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
290static 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
303namespace config {
304
305GLsizei	imageNumSamples	(const Image& img)
306{
307	if (const Renderbuffer* rbo = dynamic_cast<const Renderbuffer*>(&img))
308		return rbo->numSamples;
309	return 0;
310}
311
312static 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_ASSERT(!"Impossible image type");
326	return GL_NONE;
327}
328
329static 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
343static 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
360static 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
388static 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_ASSERT(!"Impossible image type");
415	return ret;
416}
417
418static 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_ASSERT(!"Impossible image type");
426}
427
428static 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_ASSERT(!"Impossible attachment type");
445}
446
447GLenum 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_ASSERT(!"Impossible attachment type");
455	return GL_NONE;
456}
457
458static 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_ASSERT(!"Impossible attachment type");
467	return 0;
468}
469
470static 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
519using namespace config;
520
521Checker::Checker (const glu::RenderContext& ctx)
522	: m_renderCtx(ctx)
523{
524	m_statusCodes.setAllowComplete(true);
525}
526
527void Checker::addGLError (glw::GLenum error, const char* description)
528{
529	m_statusCodes.addErrorCode(error, description);
530	m_statusCodes.setAllowComplete(false);
531}
532
533void Checker::addPotentialGLError (glw::GLenum error, const char* description)
534{
535	m_statusCodes.addErrorCode(error, description);
536}
537
538void Checker::addFBOStatus (GLenum status, const char* description)
539{
540	m_statusCodes.addFBOErrorStatus(status, description);
541	m_statusCodes.setAllowComplete(false);
542}
543
544void Checker::addPotentialFBOStatus (GLenum status, const char* description)
545{
546	m_statusCodes.addFBOErrorStatus(status, description);
547}
548
549FboVerifier::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 *//*--------------------------------------------------------------------*/
573ValidStatusCodes 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
646void 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
654const 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_ASSERT(!"Bad image type");
664	}
665	return DE_NULL; // shut up compiler warning
666}
667
668void Framebuffer::setTexture (glw::GLuint texName, const Texture& texCfg)
669{
670	textures[texName] = &texCfg;
671}
672
673void Framebuffer::setRbo (glw::GLuint rbName, const Renderbuffer& rbCfg)
674{
675	rbos[rbName] = &rbCfg;
676}
677
678static void logField (TestLog& log, const string& field, const string& value)
679{
680	log << TestLog::Message << field << ": " << value << TestLog::EndMessage;
681}
682
683static void logImage (const Image& img, TestLog& log, bool useType)
684{
685	const GLenum type = img.internalFormat.unsizedType;
686	logField(log, "Internal format",	getPixelFormatName(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
693static void logRenderbuffer (const Renderbuffer& rbo, TestLog& log)
694{
695	logImage(rbo, log, false);
696	logField(log, "Samples",			toString(rbo.numSamples));
697}
698
699static 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
708static 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
732void 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
770ValidStatusCodes::ValidStatusCodes (void)
771	: m_allowComplete(false)
772{
773}
774
775bool 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
791bool 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
800bool 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
816bool 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
825void 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
832void 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
839void ValidStatusCodes::setAllowComplete (bool b)
840{
841	m_allowComplete = b;
842}
843
844void 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
875void 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
893void 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
907void 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
930FboBuilder::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
938FboBuilder::~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
955void 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
962void 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
972GLuint 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
980GLuint 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
988TransferFormat 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