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)
isFramebufferStatus(glw::GLenum fboStatus)68 static bool isFramebufferStatus (glw::GLenum fboStatus)
69 {
70 	return glu::getFramebufferStatusName(fboStatus) != DE_NULL;
71 }
72 
isErrorCode(glw::GLenum errorCode)73 static bool isErrorCode (glw::GLenum errorCode)
74 {
75 	return glu::getErrorName(errorCode) != DE_NULL;
76 }
77 #endif
78 
operator <<(std::ostream & stream,const ImageFormat & format)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 
addCoreFormat(ImageFormat format,FormatFlags newFlags)93 void FormatDB::addCoreFormat (ImageFormat format, FormatFlags newFlags)
94 {
95 	FormatFlags& flags = m_formatFlags[format];
96 	flags = FormatFlags(flags | newFlags);
97 }
98 
addExtensionFormat(ImageFormat format,FormatFlags newFlags,const std::set<std::string> & requiredExtensions)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?
getFormats(FormatFlags requirements) const121 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 
isKnownFormat(ImageFormat format) const132 bool FormatDB::isKnownFormat (ImageFormat format) const
133 {
134 	return de::contains(m_formatFlags, format);
135 }
136 
getFormatInfo(ImageFormat format) const137 FormatFlags FormatDB::getFormatInfo (ImageFormat format) const
138 {
139 	DE_ASSERT(de::contains(m_formatFlags, format));
140 	return de::lookup(m_formatFlags, format);
141 }
142 
getFormatFeatureExtensions(ImageFormat format,FormatFlags requirements) const143 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 
operator <(const ExtensionInfo & other) const159 bool FormatDB::ExtensionInfo::operator< (const ExtensionInfo& other) const
160 {
161 	return (requiredExtensions < other.requiredExtensions) ||
162 		   ((requiredExtensions == other.requiredExtensions) && (flags < other.flags));
163 }
164 
detectGLESCompatibleContext(const RenderContext & ctx,int requiredMajor,int requiredMinor)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 
checkExtensionSupport(const ContextInfo & ctxInfo,const RenderContext & ctx,const std::string & extension)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 
checkExtensionSupport(const RenderContext & ctx,const std::string & extension)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 
getExtensionDescription(const std::string & extension)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 
addFormats(FormatDB & db,FormatEntries stdFmts)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 
addExtFormats(FormatDB & db,FormatExtEntries extFmts,const RenderContext * ctx)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 
formatFlag(GLenum context)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 
getAttachmentRenderabilityFlag(GLenum attachment)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 
imageNumSamples(const Image & img)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 
glTarget(const Image & img)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 
glInitFlat(const TextureFlat & cfg,GLenum target,const glw::Functions & gl)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 
glInitLayered(const TextureLayered & cfg,GLint depth_divider,const glw::Functions & gl)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 
glInit(const Texture & cfg,const glw::Functions & gl)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 
glCreate(const Image & cfg,const glw::Functions & gl)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 
glDelete(const Image & cfg,GLuint img,const glw::Functions & gl)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 
attachAttachment(const Attachment & att,GLenum attPoint,const glw::Functions & gl)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 
attachmentType(const Attachment & att)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 
textureLayer(const TextureAttachment & tAtt)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 
checkAttachmentCompleteness(Checker & cctx,const Attachment & attachment,GLenum attPoint,const Image * image,const FormatDB & db)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 
Checker(const glu::RenderContext & ctx)521 Checker::Checker (const glu::RenderContext& ctx)
522 	: m_renderCtx(ctx)
523 {
524 	m_statusCodes.setAllowComplete(true);
525 }
526 
addGLError(glw::GLenum error,const char * description)527 void Checker::addGLError (glw::GLenum error, const char* description)
528 {
529 	m_statusCodes.addErrorCode(error, description);
530 	m_statusCodes.setAllowComplete(false);
531 }
532 
addPotentialGLError(glw::GLenum error,const char * description)533 void Checker::addPotentialGLError (glw::GLenum error, const char* description)
534 {
535 	m_statusCodes.addErrorCode(error, description);
536 }
537 
addFBOStatus(GLenum status,const char * description)538 void Checker::addFBOStatus (GLenum status, const char* description)
539 {
540 	m_statusCodes.addFBOErrorStatus(status, description);
541 	m_statusCodes.setAllowComplete(false);
542 }
543 
addPotentialFBOStatus(GLenum status,const char * description)544 void Checker::addPotentialFBOStatus (GLenum status, const char* description)
545 {
546 	m_statusCodes.addFBOErrorStatus(status, description);
547 }
548 
FboVerifier(const FormatDB & formats,CheckerFactory & factory,const glu::RenderContext & renderCtx)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  *//*--------------------------------------------------------------------*/
validStatusCodes(const Framebuffer & fboConfig) const573 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 
attach(glw::GLenum attPoint,const Attachment * att)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 
getImage(GLenum type,glw::GLuint imgName) const654 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 
setTexture(glw::GLuint texName,const Texture & texCfg)668 void Framebuffer::setTexture (glw::GLuint texName, const Texture& texCfg)
669 {
670 	textures[texName] = &texCfg;
671 }
672 
setRbo(glw::GLuint rbName,const Renderbuffer & rbCfg)673 void Framebuffer::setRbo (glw::GLuint rbName, const Renderbuffer& rbCfg)
674 {
675 	rbos[rbName] = &rbCfg;
676 }
677 
logField(TestLog & log,const string & field,const string & value)678 static void logField (TestLog& log, const string& field, const string& value)
679 {
680 	log << TestLog::Message << field << ": " << value << TestLog::EndMessage;
681 }
682 
logImage(const Image & img,TestLog & log,bool useType)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 
logRenderbuffer(const Renderbuffer & rbo,TestLog & log)693 static void logRenderbuffer (const Renderbuffer& rbo, TestLog& log)
694 {
695 	logImage(rbo, log, false);
696 	logField(log, "Samples",			toString(rbo.numSamples));
697 }
698 
logTexture(const Texture & tex,TestLog & log)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 
logAttachment(const Attachment & att,TestLog & log)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 
logFramebufferConfig(const Framebuffer & cfg,TestLog & log)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 
ValidStatusCodes(void)770 ValidStatusCodes::ValidStatusCodes (void)
771 	: m_allowComplete(false)
772 {
773 }
774 
isFBOStatusValid(glw::GLenum fboStatus) const775 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 
isFBOStatusRequired(glw::GLenum fboStatus) const791 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 
isErrorCodeValid(glw::GLenum errorCode) const800 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 
isErrorCodeRequired(glw::GLenum errorCode) const816 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 
addErrorCode(glw::GLenum error,const char * description)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 
addFBOErrorStatus(glw::GLenum status,const char * description)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 
setAllowComplete(bool b)839 void ValidStatusCodes::setAllowComplete (bool b)
840 {
841 	m_allowComplete = b;
842 }
843 
logLegalResults(tcu::TestLog & log) const844 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 
logRules(tcu::TestLog & log) const875 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 
logRule(tcu::TestLog & log,const std::string & ruleName,const std::set<std::string> & rules) const893 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 
addViolation(std::vector<RuleViolation> & dst,glw::GLenum code,const char * description) const907 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 
FboBuilder(GLuint fbo,GLenum target,const glw::Functions & gl)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 
~FboBuilder(void)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 
checkError(void)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 
glAttach(GLenum attPoint,const Attachment * att)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 
glCreateTexture(const Texture & texCfg)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 
glCreateRbo(const Renderbuffer & rbCfg)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 
transferImageFormat(const ImageFormat & imgFormat)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