Merge vk-gl-cts/vulkan-cts-1.0.0 into vk-gl-cts/vulkan-cts-1.0.1
[platform/upstream/VK-GL-CTS.git] / modules / glshared / glsFboUtil.cpp
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)
68 static bool isFramebufferStatus (glw::GLenum fboStatus)
69 {
70         return glu::getFramebufferStatusName(fboStatus) != DE_NULL;
71 }
72
73 static bool isErrorCode (glw::GLenum errorCode)
74 {
75         return glu::getErrorName(errorCode) != DE_NULL;
76 }
77 #endif
78
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
93 void FormatDB::addCoreFormat (ImageFormat format, FormatFlags newFlags)
94 {
95         FormatFlags& flags = m_formatFlags[format];
96         flags = FormatFlags(flags | newFlags);
97 }
98
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?
121 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
132 bool FormatDB::isKnownFormat (ImageFormat format) const
133 {
134         return de::contains(m_formatFlags, format);
135 }
136
137 FormatFlags FormatDB::getFormatInfo (ImageFormat format) const
138 {
139         DE_ASSERT(de::contains(m_formatFlags, format));
140         return de::lookup(m_formatFlags, format);
141 }
142
143 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
159 bool FormatDB::ExtensionInfo::operator< (const ExtensionInfo& other) const
160 {
161         return (requiredExtensions < other.requiredExtensions) ||
162                    ((requiredExtensions == other.requiredExtensions) && (flags < other.flags));
163 }
164
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
521 Checker::Checker (const glu::RenderContext& ctx)
522         : m_renderCtx(ctx)
523 {
524         m_statusCodes.setAllowComplete(true);
525 }
526
527 void Checker::addGLError (glw::GLenum error, const char* description)
528 {
529         m_statusCodes.addErrorCode(error, description);
530         m_statusCodes.setAllowComplete(false);
531 }
532
533 void Checker::addPotentialGLError (glw::GLenum error, const char* description)
534 {
535         m_statusCodes.addErrorCode(error, description);
536 }
537
538 void Checker::addFBOStatus (GLenum status, const char* description)
539 {
540         m_statusCodes.addFBOErrorStatus(status, description);
541         m_statusCodes.setAllowComplete(false);
542 }
543
544 void Checker::addPotentialFBOStatus (GLenum status, const char* description)
545 {
546         m_statusCodes.addFBOErrorStatus(status, description);
547 }
548
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  *//*--------------------------------------------------------------------*/
573 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
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
654 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
668 void Framebuffer::setTexture (glw::GLuint texName, const Texture& texCfg)
669 {
670         textures[texName] = &texCfg;
671 }
672
673 void Framebuffer::setRbo (glw::GLuint rbName, const Renderbuffer& rbCfg)
674 {
675         rbos[rbName] = &rbCfg;
676 }
677
678 static void logField (TestLog& log, const string& field, const string& value)
679 {
680         log << TestLog::Message << field << ": " << value << TestLog::EndMessage;
681 }
682
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
693 static void logRenderbuffer (const Renderbuffer& rbo, TestLog& log)
694 {
695         logImage(rbo, log, false);
696         logField(log, "Samples",                        toString(rbo.numSamples));
697 }
698
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
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
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
770 ValidStatusCodes::ValidStatusCodes (void)
771         : m_allowComplete(false)
772 {
773 }
774
775 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
791 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
800 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
816 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
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
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
839 void ValidStatusCodes::setAllowComplete (bool b)
840 {
841         m_allowComplete = b;
842 }
843
844 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
875 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
893 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
907 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
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
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
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
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
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
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
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