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 <sstream> 32 33 using namespace glw; 34 using tcu::TestLog; 35 using tcu::TextureFormat; 36 using tcu::NotSupportedError; 37 using glu::TransferFormat; 38 using glu::mapGLInternalFormat; 39 using glu::mapGLTransferFormat; 40 using glu::getPixelFormatName; 41 using glu::getTypeName; 42 using glu::getFramebufferTargetName; 43 using glu::getFramebufferAttachmentName; 44 using glu::getFramebufferAttachmentTypeName; 45 using glu::getTextureTargetName; 46 using glu::getTransferFormat; 47 using glu::ContextInfo; 48 using glu::ContextType; 49 using glu::RenderContext; 50 using de::UniquePtr; 51 using de::toString; 52 using std::set; 53 using std::vector; 54 using std::string; 55 using std::istringstream; 56 using std::istream_iterator; 57 58 namespace deqp 59 { 60 namespace gls 61 { 62 63 namespace FboUtil 64 { 65 66 67 void FormatDB::addFormat (ImageFormat format, FormatFlags newFlags) 68 { 69 FormatFlags& flags = m_map[format]; 70 flags = FormatFlags(flags | newFlags); 71 } 72 73 // Not too fast at the moment, might consider indexing? 74 Formats FormatDB::getFormats (FormatFlags requirements) const 75 { 76 Formats ret; 77 for (FormatMap::const_iterator it = m_map.begin(); it != m_map.end(); it++) 78 { 79 if ((it->second & requirements) == requirements) 80 ret.insert(it->first); 81 } 82 return ret; 83 } 84 85 FormatFlags FormatDB::getFormatInfo (ImageFormat format, FormatFlags fallback) const 86 { 87 return lookupDefault(m_map, format, fallback); 88 } 89 90 void addFormats (FormatDB& db, FormatEntries stdFmts) 91 { 92 for (const FormatEntry* it = stdFmts.begin(); it != stdFmts.end(); it++) 93 { 94 for (const FormatKey* it2 = it->second.begin(); it2 != it->second.end(); it2++) 95 db.addFormat(formatKeyInfo(*it2), it->first); 96 } 97 } 98 99 void addExtFormats (FormatDB& db, FormatExtEntries extFmts, const RenderContext* ctx) 100 { 101 const UniquePtr<ContextInfo> ctxInfo(ctx != DE_NULL ? ContextInfo::create(*ctx) : DE_NULL); 102 for (const FormatExtEntry* it = extFmts.begin(); it != extFmts.end(); it++) 103 { 104 bool supported = true; 105 if (ctxInfo) 106 { 107 istringstream tokenStream(string(it->extensions)); 108 istream_iterator<string> tokens((tokenStream)), end; 109 110 while (tokens != end) 111 { 112 if (!ctxInfo->isExtensionSupported(tokens->c_str())) 113 { 114 supported = false; 115 break; 116 } 117 ++tokens; 118 } 119 } 120 if (supported) 121 for (const FormatKey* i2 = it->formats.begin(); i2 != it->formats.end(); i2++) 122 db.addFormat(formatKeyInfo(*i2), FormatFlags(it->flags)); 123 } 124 } 125 126 FormatFlags formatFlag (GLenum context) 127 { 128 switch (context) 129 { 130 case GL_NONE: 131 return FormatFlags(0); 132 case GL_RENDERBUFFER: 133 return RENDERBUFFER_VALID; 134 case GL_TEXTURE: 135 return TEXTURE_VALID; 136 case GL_STENCIL_ATTACHMENT: 137 return STENCIL_RENDERABLE; 138 case GL_DEPTH_ATTACHMENT: 139 return DEPTH_RENDERABLE; 140 default: 141 DE_ASSERT(context >= GL_COLOR_ATTACHMENT0 && context <= GL_COLOR_ATTACHMENT15); 142 return COLOR_RENDERABLE; 143 } 144 } 145 146 namespace config { 147 148 GLsizei imageNumSamples (const Image& img) 149 { 150 if (const Renderbuffer* rbo = dynamic_cast<const Renderbuffer*>(&img)) 151 return rbo->numSamples; 152 return 0; 153 } 154 155 static GLenum glTarget (const Image& img) 156 { 157 if (dynamic_cast<const Renderbuffer*>(&img) != DE_NULL) 158 return GL_RENDERBUFFER; 159 if (dynamic_cast<const Texture2D*>(&img) != DE_NULL) 160 return GL_TEXTURE_2D; 161 if (dynamic_cast<const TextureCubeMap*>(&img) != DE_NULL) 162 return GL_TEXTURE_CUBE_MAP; 163 if (dynamic_cast<const Texture3D*>(&img) != DE_NULL) 164 return GL_TEXTURE_3D; 165 if (dynamic_cast<const Texture2DArray*>(&img) != DE_NULL) 166 return GL_TEXTURE_2D_ARRAY; 167 168 DE_ASSERT(!"Impossible image type"); 169 return GL_NONE; 170 } 171 172 static void glInitFlat (const TextureFlat& cfg, GLenum target, const glw::Functions& gl) 173 { 174 const TransferFormat format = transferImageFormat(cfg.internalFormat); 175 GLint w = cfg.width; 176 GLint h = cfg.height; 177 for (GLint level = 0; level < cfg.numLevels; level++) 178 { 179 gl.texImage2D(target, level, cfg.internalFormat.format, w, h, 0, 180 format.format, format.dataType, DE_NULL); 181 w = de::max(1, w / 2); 182 h = de::max(1, h / 2); 183 } 184 } 185 186 static void glInitLayered (const TextureLayered& cfg, 187 GLint depth_divider, const glw::Functions& gl) 188 { 189 const TransferFormat format = transferImageFormat(cfg.internalFormat); 190 GLint w = cfg.width; 191 GLint h = cfg.height; 192 GLint depth = cfg.numLayers; 193 for (GLint level = 0; level < cfg.numLevels; level++) 194 { 195 gl.texImage3D(glTarget(cfg), level, cfg.internalFormat.format, w, h, depth, 0, 196 format.format, format.dataType, DE_NULL); 197 w = de::max(1, w / 2); 198 h = de::max(1, h / 2); 199 depth = de::max(1, depth / depth_divider); 200 } 201 } 202 203 static void glInit (const Texture& cfg, const glw::Functions& gl) 204 { 205 if (const Texture2D* t2d = dynamic_cast<const Texture2D*>(&cfg)) 206 glInitFlat(*t2d, glTarget(*t2d), gl); 207 else if (const TextureCubeMap* tcm = dynamic_cast<const TextureCubeMap*>(&cfg)) 208 { 209 // \todo [2013-12-05 lauri] 210 // move this to glu or someplace sensible (this array is already 211 // present in duplicates) 212 static const GLenum s_cubeMapFaces[] = 213 { 214 GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 215 GL_TEXTURE_CUBE_MAP_POSITIVE_X, 216 GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 217 GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 218 GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 219 GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 220 }; 221 const Range<GLenum> range = GLS_ARRAY_RANGE(s_cubeMapFaces); 222 for (const GLenum* it = range.begin(); it != range.end(); it++) 223 glInitFlat(*tcm, *it, gl); 224 } 225 else if (const Texture3D* t3d = dynamic_cast<const Texture3D*>(&cfg)) 226 glInitLayered(*t3d, 2, gl); 227 else if (const Texture2DArray* t2a = dynamic_cast<const Texture2DArray*>(&cfg)) 228 glInitLayered(*t2a, 1, gl); 229 } 230 231 static GLuint glCreate (const Image& cfg, const glw::Functions& gl) 232 { 233 GLuint ret = 0; 234 if (const Renderbuffer* const rbo = dynamic_cast<const Renderbuffer*>(&cfg)) 235 { 236 gl.genRenderbuffers(1, &ret); 237 gl.bindRenderbuffer(GL_RENDERBUFFER, ret); 238 239 if (rbo->numSamples == 0) 240 gl.renderbufferStorage(GL_RENDERBUFFER, rbo->internalFormat.format, 241 rbo->width, rbo->height); 242 else 243 gl.renderbufferStorageMultisample( 244 GL_RENDERBUFFER, rbo->numSamples, rbo->internalFormat.format, 245 rbo->width, rbo->height); 246 247 gl.bindRenderbuffer(GL_RENDERBUFFER, 0); 248 } 249 else if (const Texture* const tex = dynamic_cast<const Texture*>(&cfg)) 250 { 251 gl.genTextures(1, &ret); 252 gl.bindTexture(glTarget(*tex), ret); 253 glInit(*tex, gl); 254 gl.bindTexture(glTarget(*tex), 0); 255 } 256 else 257 DE_ASSERT(!"Impossible image type"); 258 return ret; 259 } 260 261 static void glDelete (const Image& cfg, GLuint img, const glw::Functions& gl) 262 { 263 if (dynamic_cast<const Renderbuffer*>(&cfg) != DE_NULL) 264 gl.deleteRenderbuffers(1, &img); 265 else if (dynamic_cast<const Texture*>(&cfg) != DE_NULL) 266 gl.deleteTextures(1, &img); 267 else 268 DE_ASSERT(!"Impossible image type"); 269 } 270 271 static void attachAttachment (const Attachment& att, GLenum attPoint, 272 const glw::Functions& gl) 273 { 274 if (const RenderbufferAttachment* const rAtt = 275 dynamic_cast<const RenderbufferAttachment*>(&att)) 276 gl.framebufferRenderbuffer(rAtt->target, attPoint, 277 rAtt->renderbufferTarget, rAtt->imageName); 278 else if (const TextureFlatAttachment* const fAtt = 279 dynamic_cast<const TextureFlatAttachment*>(&att)) 280 gl.framebufferTexture2D(fAtt->target, attPoint, 281 fAtt->texTarget, fAtt->imageName, fAtt->level); 282 else if (const TextureLayerAttachment* const lAtt = 283 dynamic_cast<const TextureLayerAttachment*>(&att)) 284 gl.framebufferTextureLayer(lAtt->target, attPoint, 285 lAtt->imageName, lAtt->level, lAtt->layer); 286 else 287 DE_ASSERT(!"Impossible attachment type"); 288 } 289 290 GLenum attachmentType (const Attachment& att) 291 { 292 if (dynamic_cast<const RenderbufferAttachment*>(&att) != DE_NULL) 293 return GL_RENDERBUFFER; 294 else if (dynamic_cast<const TextureAttachment*>(&att) != DE_NULL) 295 return GL_TEXTURE; 296 297 DE_ASSERT(!"Impossible attachment type"); 298 return GL_NONE; 299 } 300 301 static GLsizei textureLayer (const TextureAttachment& tAtt) 302 { 303 if (dynamic_cast<const TextureFlatAttachment*>(&tAtt) != DE_NULL) 304 return 0; 305 else if (const TextureLayerAttachment* const lAtt = 306 dynamic_cast<const TextureLayerAttachment*>(&tAtt)) 307 return lAtt->layer; 308 309 DE_ASSERT(!"Impossible attachment type"); 310 return 0; 311 } 312 313 static void checkAttachmentCompleteness (Checker& cctx, const Attachment& attachment, 314 GLenum attPoint, const Image* image, 315 const FormatDB& db) 316 { 317 // GLES2 4.4.5 / GLES3 4.4.4, "Framebuffer attachment completeness" 318 319 if (const TextureAttachment* const texAtt = 320 dynamic_cast<const TextureAttachment*>(&attachment)) 321 if (const TextureLayered* const ltex = dynamic_cast<const TextureLayered*>(image)) 322 { 323 // GLES3: "If the value of FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is 324 // TEXTURE and the value of FRAMEBUFFER_ATTACHMENT_OBJECT_NAME names a 325 // three-dimensional texture, then the value of 326 // FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER must be smaller than the depth 327 // of the texture. 328 // 329 // GLES3: "If the value of FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is 330 // TEXTURE and the value of FRAMEBUFFER_ATTACHMENT_OBJECT_NAME names a 331 // two-dimensional array texture, then the value of 332 // FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER must be smaller than the 333 // number of layers in the texture. 334 335 cctx.require(textureLayer(*texAtt) < ltex->numLayers, 336 GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT); 337 } 338 339 // "The width and height of image are non-zero." 340 cctx.require(image->width > 0 && image->height > 0, GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT); 341 342 // Check for renderability 343 FormatFlags flags = db.getFormatInfo(image->internalFormat, ANY_FORMAT); 344 // If the format does not have the proper renderability flag, the 345 // completeness check _must_ fail. 346 cctx.require((flags & formatFlag(attPoint)) != 0, GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT); 347 // If the format is only optionally renderable, the completeness check _can_ fail. 348 cctx.canRequire((flags & REQUIRED_RENDERABLE) != 0, 349 GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT); 350 } 351 352 } // namespace config 353 354 using namespace config; 355 356 void Checker::require (bool condition, GLenum error) 357 { 358 if (!condition) 359 { 360 m_statusCodes.erase(GL_FRAMEBUFFER_COMPLETE); 361 m_statusCodes.insert(error); 362 } 363 } 364 365 void Checker::canRequire (bool condition, GLenum error) 366 { 367 if (!condition) 368 m_statusCodes.insert(error); 369 } 370 371 FboVerifier::FboVerifier (const FormatDB& formats, CheckerFactory& factory) 372 : m_formats (formats) 373 , m_factory (factory) 374 { 375 } 376 377 /*--------------------------------------------------------------------*//*! 378 * \brief Return acceptable framebuffer status codes. 379 * 380 * This function examines the framebuffer configuration descriptor `fboConfig` 381 * and returns the set of status codes that `glCheckFramebufferStatus` is 382 * allowed to return on a conforming implementation when given a framebuffer 383 * whose configuration adheres to `fboConfig`. 384 * 385 * The returned set is guaranteed to be non-empty, but it may contain multiple 386 * INCOMPLETE statuses (if there are multiple errors in the spec), or or a mix 387 * of COMPLETE and INCOMPLETE statuses (if supporting a FBO with this spec is 388 * optional). Furthermore, the statuses may contain GL error codes, which 389 * indicate that trying to create a framebuffer configuration like this could 390 * have failed with an error (if one was checked for) even before 391 * `glCheckFramebufferStatus` was ever called. 392 * 393 *//*--------------------------------------------------------------------*/ 394 StatusCodes FboVerifier::validStatusCodes (const Framebuffer& fboConfig) const 395 { 396 const AttachmentMap& atts = fboConfig.attachments; 397 const UniquePtr<Checker> cctx(m_factory.createChecker()); 398 399 for (TextureMap::const_iterator it = fboConfig.textures.begin(); 400 it != fboConfig.textures.end(); it++) 401 { 402 const FormatFlags flags = 403 m_formats.getFormatInfo(it->second->internalFormat, ANY_FORMAT); 404 cctx->require((flags & TEXTURE_VALID) != 0, GL_INVALID_ENUM); 405 cctx->require((flags & TEXTURE_VALID) != 0, GL_INVALID_OPERATION); 406 cctx->require((flags & TEXTURE_VALID) != 0, GL_INVALID_VALUE); 407 } 408 409 for (RboMap::const_iterator it = fboConfig.rbos.begin(); it != fboConfig.rbos.end(); it++) 410 { 411 const FormatFlags flags = 412 m_formats.getFormatInfo(it->second->internalFormat, ANY_FORMAT); 413 cctx->require((flags & RENDERBUFFER_VALID) != 0, GL_INVALID_ENUM); 414 } 415 416 // "There is at least one image attached to the framebuffer." 417 // TODO: support XXX_framebuffer_no_attachments 418 cctx->require(!atts.empty(), GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT); 419 420 for (AttachmentMap::const_iterator it = atts.begin(); it != atts.end(); it++) 421 { 422 const GLenum attPoint = it->first; 423 const Attachment& att = *it->second; 424 const Image* const image = fboConfig.getImage(attachmentType(att), att.imageName); 425 checkAttachmentCompleteness(*cctx, att, attPoint, image, m_formats); 426 cctx->check(it->first, *it->second, image); 427 } 428 429 return cctx->getStatusCodes(); 430 } 431 432 433 void Framebuffer::attach (glw::GLenum attPoint, const Attachment* att) 434 { 435 if (att == DE_NULL) 436 attachments.erase(attPoint); 437 else 438 attachments[attPoint] = att; 439 } 440 441 const Image* Framebuffer::getImage (GLenum type, glw::GLuint imgName) const 442 { 443 switch (type) 444 { 445 case GL_TEXTURE: 446 return lookupDefault(textures, imgName, DE_NULL); 447 case GL_RENDERBUFFER: 448 return lookupDefault(rbos, imgName, DE_NULL); 449 default: 450 DE_ASSERT(!"Bad image type"); 451 } 452 return DE_NULL; // shut up compiler warning 453 } 454 455 void Framebuffer::setTexture (glw::GLuint texName, const Texture& texCfg) 456 { 457 textures[texName] = &texCfg; 458 } 459 460 void Framebuffer::setRbo (glw::GLuint rbName, const Renderbuffer& rbCfg) 461 { 462 rbos[rbName] = &rbCfg; 463 } 464 465 static void logField (TestLog& log, const string& field, const string& value) 466 { 467 log << TestLog::Message << field << ": " << value << TestLog::EndMessage; 468 } 469 470 static void logImage (const Image& img, TestLog& log, bool useType) 471 { 472 const GLenum type = img.internalFormat.unsizedType; 473 logField(log, "Internal format", getPixelFormatName(img.internalFormat.format)); 474 if (useType && type != GL_NONE) 475 logField(log, "Format type", getTypeName(type)); 476 logField(log, "Width", toString(img.width)); 477 logField(log, "Height", toString(img.height)); 478 } 479 480 static void logRenderbuffer (const Renderbuffer& rbo, TestLog& log) 481 { 482 logImage(rbo, log, false); 483 logField(log, "Samples", toString(rbo.numSamples)); 484 } 485 486 static void logTexture (const Texture& tex, TestLog& log) 487 { 488 logField(log, "Type", glu::getTextureTargetName(glTarget(tex))); 489 logImage(tex, log, true); 490 logField(log, "Levels", toString(tex.numLevels)); 491 if (const TextureLayered* const lTex = dynamic_cast<const TextureLayered*>(&tex)) 492 logField(log, "Layers", toString(lTex->numLayers)); 493 } 494 495 static void logAttachment (const Attachment& att, TestLog& log) 496 { 497 logField(log, "Target", getFramebufferTargetName(att.target)); 498 logField(log, "Type", getFramebufferAttachmentTypeName(attachmentType(att))); 499 logField(log, "Image Name", toString(att.imageName)); 500 if (const RenderbufferAttachment* const rAtt 501 = dynamic_cast<const RenderbufferAttachment*>(&att)) 502 { 503 DE_UNREF(rAtt); // To shut up compiler during optimized builds. 504 DE_ASSERT(rAtt->renderbufferTarget == GL_RENDERBUFFER); 505 logField(log, "Renderbuffer Target", "GL_RENDERBUFFER"); 506 } 507 else if (const TextureAttachment* const tAtt = dynamic_cast<const TextureAttachment*>(&att)) 508 { 509 logField(log, "Mipmap Level", toString(tAtt->level)); 510 if (const TextureFlatAttachment* const fAtt = 511 dynamic_cast<const TextureFlatAttachment*>(tAtt)) 512 logField(log, "Texture Target", getTextureTargetName(fAtt->texTarget)); 513 else if (const TextureLayerAttachment* const lAtt = 514 dynamic_cast<const TextureLayerAttachment*>(tAtt)) 515 logField(log, "Layer", toString(lAtt->level)); 516 } 517 } 518 519 void logFramebufferConfig (const Framebuffer& cfg, TestLog& log) 520 { 521 log << TestLog::Section("Framebuffer", "Framebuffer configuration"); 522 523 const string rboDesc = cfg.rbos.empty() 524 ? "No renderbuffers were created" 525 : "Renderbuffers created"; 526 log << TestLog::Section("Renderbuffers", rboDesc); 527 for (RboMap::const_iterator it = cfg.rbos.begin(); it != cfg.rbos.end(); ++it) 528 { 529 const string num = toString(it->first); 530 log << TestLog::Section(num, "Renderbuffer " + num); 531 logRenderbuffer(*it->second, log); 532 log << TestLog::EndSection; 533 } 534 log << TestLog::EndSection; // Renderbuffers 535 536 const string texDesc = cfg.textures.empty() 537 ? "No textures were created" 538 : "Textures created"; 539 log << TestLog::Section("Textures", texDesc); 540 for (TextureMap::const_iterator it = cfg.textures.begin(); 541 it != cfg.textures.end(); ++it) 542 { 543 const string num = toString(it->first); 544 log << TestLog::Section(num, "Texture " + num); 545 logTexture(*it->second, log); 546 log << TestLog::EndSection; 547 } 548 log << TestLog::EndSection; // Textures 549 550 const string attDesc = cfg.attachments.empty() 551 ? "Framebuffer has no attachments" 552 : "Framebuffer attachments"; 553 log << TestLog::Section("Attachments", attDesc); 554 for (AttachmentMap::const_iterator it = cfg.attachments.begin(); 555 it != cfg.attachments.end(); it++) 556 { 557 const string attPointName = getFramebufferAttachmentName(it->first); 558 log << TestLog::Section(attPointName, "Attachment point " + attPointName); 559 logAttachment(*it->second, log); 560 log << TestLog::EndSection; 561 } 562 log << TestLog::EndSection; // Attachments 563 564 log << TestLog::EndSection; // Framebuffer 565 } 566 567 FboBuilder::FboBuilder (GLuint fbo, GLenum target, const glw::Functions& gl) 568 : m_error (GL_NO_ERROR) 569 , m_target (target) 570 , m_gl (gl) 571 { 572 m_gl.bindFramebuffer(m_target, fbo); 573 } 574 575 FboBuilder::~FboBuilder (void) 576 { 577 for (TextureMap::const_iterator it = textures.begin(); it != textures.end(); it++) 578 { 579 glDelete(*it->second, it->first, m_gl); 580 } 581 for (RboMap::const_iterator it = rbos.begin(); it != rbos.end(); it++) 582 { 583 glDelete(*it->second, it->first, m_gl); 584 } 585 m_gl.bindFramebuffer(m_target, 0); 586 for (Configs::const_iterator it = m_configs.begin(); it != m_configs.end(); it++) 587 { 588 delete *it; 589 } 590 } 591 592 void FboBuilder::checkError (void) 593 { 594 const GLenum error = m_gl.getError(); 595 if (error != GL_NO_ERROR && m_error == GL_NO_ERROR) 596 m_error = error; 597 } 598 599 void FboBuilder::glAttach (GLenum attPoint, const Attachment* att) 600 { 601 if (att == NULL) 602 m_gl.framebufferRenderbuffer(m_target, attPoint, GL_RENDERBUFFER, 0); 603 else 604 attachAttachment(*att, attPoint, m_gl); 605 checkError(); 606 attach(attPoint, att); 607 } 608 609 GLuint FboBuilder::glCreateTexture (const Texture& texCfg) 610 { 611 const GLuint texName = glCreate(texCfg, m_gl); 612 checkError(); 613 setTexture(texName, texCfg); 614 return texName; 615 } 616 617 GLuint FboBuilder::glCreateRbo (const Renderbuffer& rbCfg) 618 { 619 const GLuint rbName = glCreate(rbCfg, m_gl); 620 checkError(); 621 setRbo(rbName, rbCfg); 622 return rbName; 623 } 624 625 TransferFormat transferImageFormat (const ImageFormat& imgFormat) 626 { 627 if (imgFormat.unsizedType == GL_NONE) 628 return getTransferFormat(mapGLInternalFormat(imgFormat.format)); 629 else 630 return TransferFormat(imgFormat.format, imgFormat.unsizedType); 631 } 632 633 } // FboUtil 634 } // gls 635 } // deqp 636