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 999