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