xref: /aosp_15_r20/external/deqp/modules/glshared/glsFboUtil.cpp (revision 35238bce31c2a825756842865a792f8cf7f89930)
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