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