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