1 #include "precompiled.h" 2 // 3 // Copyright (c) 2002-2013 The ANGLE Project Authors. All rights reserved. 4 // Use of this source code is governed by a BSD-style license that can be 5 // found in the LICENSE file. 6 // 7 8 // Texture.cpp: Implements the gl::Texture class and its derived classes 9 // Texture2D and TextureCubeMap. Implements GL texture objects and related 10 // functionality. [OpenGL ES 2.0.24] section 3.7 page 63. 11 12 #include "libGLESv2/Texture.h" 13 14 #include "libGLESv2/main.h" 15 #include "libGLESv2/mathutil.h" 16 #include "libGLESv2/utilities.h" 17 #include "libGLESv2/renderer/Blit.h" 18 #include "libGLESv2/Renderbuffer.h" 19 #include "libGLESv2/renderer/Image.h" 20 #include "libGLESv2/renderer/Renderer.h" 21 #include "libGLESv2/renderer/TextureStorage.h" 22 #include "libEGL/Surface.h" 23 24 namespace gl 25 { 26 27 Texture::Texture(rx::Renderer *renderer, GLuint id) : RefCountObject(id) 28 { 29 mRenderer = renderer; 30 31 mSamplerState.minFilter = GL_NEAREST_MIPMAP_LINEAR; 32 mSamplerState.magFilter = GL_LINEAR; 33 mSamplerState.wrapS = GL_REPEAT; 34 mSamplerState.wrapT = GL_REPEAT; 35 mSamplerState.maxAnisotropy = 1.0f; 36 mSamplerState.lodOffset = 0; 37 mUsage = GL_NONE; 38 39 mDirtyImages = true; 40 41 mImmutable = false; 42 } 43 44 Texture::~Texture() 45 { 46 } 47 48 // Returns true on successful filter state update (valid enum parameter) 49 bool Texture::setMinFilter(GLenum filter) 50 { 51 switch (filter) 52 { 53 case GL_NEAREST: 54 case GL_LINEAR: 55 case GL_NEAREST_MIPMAP_NEAREST: 56 case GL_LINEAR_MIPMAP_NEAREST: 57 case GL_NEAREST_MIPMAP_LINEAR: 58 case GL_LINEAR_MIPMAP_LINEAR: 59 mSamplerState.minFilter = filter; 60 return true; 61 default: 62 return false; 63 } 64 } 65 66 // Returns true on successful filter state update (valid enum parameter) 67 bool Texture::setMagFilter(GLenum filter) 68 { 69 switch (filter) 70 { 71 case GL_NEAREST: 72 case GL_LINEAR: 73 mSamplerState.magFilter = filter; 74 return true; 75 default: 76 return false; 77 } 78 } 79 80 // Returns true on successful wrap state update (valid enum parameter) 81 bool Texture::setWrapS(GLenum wrap) 82 { 83 switch (wrap) 84 { 85 case GL_REPEAT: 86 case GL_CLAMP_TO_EDGE: 87 case GL_MIRRORED_REPEAT: 88 mSamplerState.wrapS = wrap; 89 return true; 90 default: 91 return false; 92 } 93 } 94 95 // Returns true on successful wrap state update (valid enum parameter) 96 bool Texture::setWrapT(GLenum wrap) 97 { 98 switch (wrap) 99 { 100 case GL_REPEAT: 101 case GL_CLAMP_TO_EDGE: 102 case GL_MIRRORED_REPEAT: 103 mSamplerState.wrapT = wrap; 104 return true; 105 default: 106 return false; 107 } 108 } 109 110 // Returns true on successful max anisotropy update (valid anisotropy value) 111 bool Texture::setMaxAnisotropy(float textureMaxAnisotropy, float contextMaxAnisotropy) 112 { 113 textureMaxAnisotropy = std::min(textureMaxAnisotropy, contextMaxAnisotropy); 114 if (textureMaxAnisotropy < 1.0f) 115 { 116 return false; 117 } 118 119 mSamplerState.maxAnisotropy = textureMaxAnisotropy; 120 121 return true; 122 } 123 124 // Returns true on successful usage state update (valid enum parameter) 125 bool Texture::setUsage(GLenum usage) 126 { 127 switch (usage) 128 { 129 case GL_NONE: 130 case GL_FRAMEBUFFER_ATTACHMENT_ANGLE: 131 mUsage = usage; 132 return true; 133 default: 134 return false; 135 } 136 } 137 138 GLenum Texture::getMinFilter() const 139 { 140 return mSamplerState.minFilter; 141 } 142 143 GLenum Texture::getMagFilter() const 144 { 145 return mSamplerState.magFilter; 146 } 147 148 GLenum Texture::getWrapS() const 149 { 150 return mSamplerState.wrapS; 151 } 152 153 GLenum Texture::getWrapT() const 154 { 155 return mSamplerState.wrapT; 156 } 157 158 float Texture::getMaxAnisotropy() const 159 { 160 return mSamplerState.maxAnisotropy; 161 } 162 163 int Texture::getLodOffset() 164 { 165 rx::TextureStorageInterface *texture = getStorage(false); 166 return texture ? texture->getLodOffset() : 0; 167 } 168 169 void Texture::getSamplerState(SamplerState *sampler) 170 { 171 *sampler = mSamplerState; 172 sampler->lodOffset = getLodOffset(); 173 } 174 175 GLenum Texture::getUsage() const 176 { 177 return mUsage; 178 } 179 180 bool Texture::isMipmapFiltered() const 181 { 182 switch (mSamplerState.minFilter) 183 { 184 case GL_NEAREST: 185 case GL_LINEAR: 186 return false; 187 case GL_NEAREST_MIPMAP_NEAREST: 188 case GL_LINEAR_MIPMAP_NEAREST: 189 case GL_NEAREST_MIPMAP_LINEAR: 190 case GL_LINEAR_MIPMAP_LINEAR: 191 return true; 192 default: UNREACHABLE(); 193 return false; 194 } 195 } 196 197 void Texture::setImage(GLint unpackAlignment, const void *pixels, rx::Image *image) 198 { 199 if (pixels != NULL) 200 { 201 image->loadData(0, 0, image->getWidth(), image->getHeight(), unpackAlignment, pixels); 202 mDirtyImages = true; 203 } 204 } 205 206 void Texture::setCompressedImage(GLsizei imageSize, const void *pixels, rx::Image *image) 207 { 208 if (pixels != NULL) 209 { 210 image->loadCompressedData(0, 0, image->getWidth(), image->getHeight(), pixels); 211 mDirtyImages = true; 212 } 213 } 214 215 bool Texture::subImage(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels, rx::Image *image) 216 { 217 if (pixels != NULL) 218 { 219 image->loadData(xoffset, yoffset, width, height, unpackAlignment, pixels); 220 mDirtyImages = true; 221 } 222 223 return true; 224 } 225 226 bool Texture::subImageCompressed(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels, rx::Image *image) 227 { 228 if (pixels != NULL) 229 { 230 image->loadCompressedData(xoffset, yoffset, width, height, pixels); 231 mDirtyImages = true; 232 } 233 234 return true; 235 } 236 237 rx::TextureStorageInterface *Texture::getNativeTexture() 238 { 239 // ensure the underlying texture is created 240 241 rx::TextureStorageInterface *storage = getStorage(false); 242 if (storage) 243 { 244 updateTexture(); 245 } 246 247 return storage; 248 } 249 250 bool Texture::hasDirtyImages() const 251 { 252 return mDirtyImages; 253 } 254 255 void Texture::resetDirty() 256 { 257 mDirtyImages = false; 258 } 259 260 unsigned int Texture::getTextureSerial() 261 { 262 rx::TextureStorageInterface *texture = getStorage(false); 263 return texture ? texture->getTextureSerial() : 0; 264 } 265 266 unsigned int Texture::getRenderTargetSerial(GLenum target) 267 { 268 rx::TextureStorageInterface *texture = getStorage(true); 269 return texture ? texture->getRenderTargetSerial(target) : 0; 270 } 271 272 bool Texture::isImmutable() const 273 { 274 return mImmutable; 275 } 276 277 GLint Texture::creationLevels(GLsizei width, GLsizei height) const 278 { 279 if ((isPow2(width) && isPow2(height)) || mRenderer->getNonPower2TextureSupport()) 280 { 281 return 0; // Maximum number of levels 282 } 283 else 284 { 285 // OpenGL ES 2.0 without GL_OES_texture_npot does not permit NPOT mipmaps. 286 return 1; 287 } 288 } 289 290 GLint Texture::creationLevels(GLsizei size) const 291 { 292 return creationLevels(size, size); 293 } 294 295 Texture2D::Texture2D(rx::Renderer *renderer, GLuint id) : Texture(renderer, id) 296 { 297 mTexStorage = NULL; 298 mSurface = NULL; 299 mColorbufferProxy = NULL; 300 mProxyRefs = 0; 301 302 for (int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; ++i) 303 { 304 mImageArray[i] = renderer->createImage(); 305 } 306 } 307 308 Texture2D::~Texture2D() 309 { 310 mColorbufferProxy = NULL; 311 312 delete mTexStorage; 313 mTexStorage = NULL; 314 315 if (mSurface) 316 { 317 mSurface->setBoundTexture(NULL); 318 mSurface = NULL; 319 } 320 321 for (int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; ++i) 322 { 323 delete mImageArray[i]; 324 } 325 } 326 327 // We need to maintain a count of references to renderbuffers acting as 328 // proxies for this texture, so that we do not attempt to use a pointer 329 // to a renderbuffer proxy which has been deleted. 330 void Texture2D::addProxyRef(const Renderbuffer *proxy) 331 { 332 mProxyRefs++; 333 } 334 335 void Texture2D::releaseProxy(const Renderbuffer *proxy) 336 { 337 if (mProxyRefs > 0) 338 mProxyRefs--; 339 340 if (mProxyRefs == 0) 341 mColorbufferProxy = NULL; 342 } 343 344 GLenum Texture2D::getTarget() const 345 { 346 return GL_TEXTURE_2D; 347 } 348 349 GLsizei Texture2D::getWidth(GLint level) const 350 { 351 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS) 352 return mImageArray[level]->getWidth(); 353 else 354 return 0; 355 } 356 357 GLsizei Texture2D::getHeight(GLint level) const 358 { 359 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS) 360 return mImageArray[level]->getHeight(); 361 else 362 return 0; 363 } 364 365 GLenum Texture2D::getInternalFormat(GLint level) const 366 { 367 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS) 368 return mImageArray[level]->getInternalFormat(); 369 else 370 return GL_NONE; 371 } 372 373 GLenum Texture2D::getActualFormat(GLint level) const 374 { 375 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS) 376 return mImageArray[level]->getActualFormat(); 377 else 378 return D3DFMT_UNKNOWN; 379 } 380 381 void Texture2D::redefineImage(GLint level, GLint internalformat, GLsizei width, GLsizei height) 382 { 383 releaseTexImage(); 384 385 // If there currently is a corresponding storage texture image, it has these parameters 386 const int storageWidth = std::max(1, mImageArray[0]->getWidth() >> level); 387 const int storageHeight = std::max(1, mImageArray[0]->getHeight() >> level); 388 const int storageFormat = mImageArray[0]->getInternalFormat(); 389 390 mImageArray[level]->redefine(mRenderer, internalformat, width, height, false); 391 392 if (mTexStorage) 393 { 394 const int storageLevels = mTexStorage->levelCount(); 395 396 if ((level >= storageLevels && storageLevels != 0) || 397 width != storageWidth || 398 height != storageHeight || 399 internalformat != storageFormat) // Discard mismatched storage 400 { 401 for (int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++) 402 { 403 mImageArray[i]->markDirty(); 404 } 405 406 delete mTexStorage; 407 mTexStorage = NULL; 408 mDirtyImages = true; 409 } 410 } 411 } 412 413 void Texture2D::setImage(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels) 414 { 415 GLint internalformat = ConvertSizedInternalFormat(format, type); 416 redefineImage(level, internalformat, width, height); 417 418 Texture::setImage(unpackAlignment, pixels, mImageArray[level]); 419 } 420 421 void Texture2D::bindTexImage(egl::Surface *surface) 422 { 423 releaseTexImage(); 424 425 GLint internalformat = surface->getFormat(); 426 427 mImageArray[0]->redefine(mRenderer, internalformat, surface->getWidth(), surface->getHeight(), true); 428 429 delete mTexStorage; 430 mTexStorage = new rx::TextureStorageInterface2D(mRenderer, surface->getSwapChain()); 431 432 mDirtyImages = true; 433 mSurface = surface; 434 mSurface->setBoundTexture(this); 435 } 436 437 void Texture2D::releaseTexImage() 438 { 439 if (mSurface) 440 { 441 mSurface->setBoundTexture(NULL); 442 mSurface = NULL; 443 444 if (mTexStorage) 445 { 446 delete mTexStorage; 447 mTexStorage = NULL; 448 } 449 450 for (int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++) 451 { 452 mImageArray[i]->redefine(mRenderer, GL_NONE, 0, 0, true); 453 } 454 } 455 } 456 457 void Texture2D::setCompressedImage(GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels) 458 { 459 // compressed formats don't have separate sized internal formats-- we can just use the compressed format directly 460 redefineImage(level, format, width, height); 461 462 Texture::setCompressedImage(imageSize, pixels, mImageArray[level]); 463 } 464 465 void Texture2D::commitRect(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height) 466 { 467 if (level < levelCount()) 468 { 469 rx::Image *image = mImageArray[level]; 470 if (image->updateSurface(mTexStorage, level, xoffset, yoffset, width, height)) 471 { 472 image->markClean(); 473 } 474 } 475 } 476 477 void Texture2D::subImage(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels) 478 { 479 if (Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, mImageArray[level])) 480 { 481 commitRect(level, xoffset, yoffset, width, height); 482 } 483 } 484 485 void Texture2D::subImageCompressed(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels) 486 { 487 if (Texture::subImageCompressed(xoffset, yoffset, width, height, format, imageSize, pixels, mImageArray[level])) 488 { 489 commitRect(level, xoffset, yoffset, width, height); 490 } 491 } 492 493 void Texture2D::copyImage(GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source) 494 { 495 GLint internalformat = ConvertSizedInternalFormat(format, GL_UNSIGNED_BYTE); 496 redefineImage(level, internalformat, width, height); 497 498 if (!mImageArray[level]->isRenderableFormat()) 499 { 500 mImageArray[level]->copy(0, 0, x, y, width, height, source); 501 mDirtyImages = true; 502 } 503 else 504 { 505 if (!mTexStorage || !mTexStorage->isRenderTarget()) 506 { 507 convertToRenderTarget(); 508 } 509 510 mImageArray[level]->markClean(); 511 512 if (width != 0 && height != 0 && level < levelCount()) 513 { 514 gl::Rectangle sourceRect; 515 sourceRect.x = x; 516 sourceRect.width = width; 517 sourceRect.y = y; 518 sourceRect.height = height; 519 520 mRenderer->copyImage(source, sourceRect, format, 0, 0, mTexStorage, level); 521 } 522 } 523 } 524 525 void Texture2D::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source) 526 { 527 if (xoffset + width > mImageArray[level]->getWidth() || yoffset + height > mImageArray[level]->getHeight()) 528 { 529 return gl::error(GL_INVALID_VALUE); 530 } 531 532 if (!mImageArray[level]->isRenderableFormat() || (!mTexStorage && !isSamplerComplete())) 533 { 534 mImageArray[level]->copy(xoffset, yoffset, x, y, width, height, source); 535 mDirtyImages = true; 536 } 537 else 538 { 539 if (!mTexStorage || !mTexStorage->isRenderTarget()) 540 { 541 convertToRenderTarget(); 542 } 543 544 updateTexture(); 545 546 if (level < levelCount()) 547 { 548 gl::Rectangle sourceRect; 549 sourceRect.x = x; 550 sourceRect.width = width; 551 sourceRect.y = y; 552 sourceRect.height = height; 553 554 mRenderer->copyImage(source, sourceRect, 555 gl::ExtractFormat(mImageArray[0]->getInternalFormat()), 556 xoffset, yoffset, mTexStorage, level); 557 } 558 } 559 } 560 561 void Texture2D::storage(GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) 562 { 563 delete mTexStorage; 564 mTexStorage = new rx::TextureStorageInterface2D(mRenderer, levels, internalformat, mUsage, false, width, height); 565 mImmutable = true; 566 567 for (int level = 0; level < levels; level++) 568 { 569 mImageArray[level]->redefine(mRenderer, internalformat, width, height, true); 570 width = std::max(1, width >> 1); 571 height = std::max(1, height >> 1); 572 } 573 574 for (int level = levels; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++) 575 { 576 mImageArray[level]->redefine(mRenderer, GL_NONE, 0, 0, true); 577 } 578 579 if (mTexStorage->isManaged()) 580 { 581 int levels = levelCount(); 582 583 for (int level = 0; level < levels; level++) 584 { 585 mImageArray[level]->setManagedSurface(mTexStorage, level); 586 } 587 } 588 } 589 590 // Tests for 2D texture sampling completeness. [OpenGL ES 2.0.24] section 3.8.2 page 85. 591 bool Texture2D::isSamplerComplete() const 592 { 593 GLsizei width = mImageArray[0]->getWidth(); 594 GLsizei height = mImageArray[0]->getHeight(); 595 596 if (width <= 0 || height <= 0) 597 { 598 return false; 599 } 600 601 bool mipmapping = isMipmapFiltered(); 602 bool filtering, renderable; 603 604 if ((IsFloat32Format(getInternalFormat(0)) && !mRenderer->getFloat32TextureSupport(&filtering, &renderable)) || 605 (IsFloat16Format(getInternalFormat(0)) && !mRenderer->getFloat16TextureSupport(&filtering, &renderable))) 606 { 607 if (mSamplerState.magFilter != GL_NEAREST || 608 (mSamplerState.minFilter != GL_NEAREST && mSamplerState.minFilter != GL_NEAREST_MIPMAP_NEAREST)) 609 { 610 return false; 611 } 612 } 613 614 bool npotSupport = mRenderer->getNonPower2TextureSupport(); 615 616 if (!npotSupport) 617 { 618 if ((mSamplerState.wrapS != GL_CLAMP_TO_EDGE && !isPow2(width)) || 619 (mSamplerState.wrapT != GL_CLAMP_TO_EDGE && !isPow2(height))) 620 { 621 return false; 622 } 623 } 624 625 if (mipmapping) 626 { 627 if (!npotSupport) 628 { 629 if (!isPow2(width) || !isPow2(height)) 630 { 631 return false; 632 } 633 } 634 635 if (!isMipmapComplete()) 636 { 637 return false; 638 } 639 } 640 641 return true; 642 } 643 644 // Tests for 2D texture (mipmap) completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81. 645 bool Texture2D::isMipmapComplete() const 646 { 647 if (isImmutable()) 648 { 649 return true; 650 } 651 652 GLsizei width = mImageArray[0]->getWidth(); 653 GLsizei height = mImageArray[0]->getHeight(); 654 655 if (width <= 0 || height <= 0) 656 { 657 return false; 658 } 659 660 int q = log2(std::max(width, height)); 661 662 for (int level = 1; level <= q; level++) 663 { 664 if (mImageArray[level]->getInternalFormat() != mImageArray[0]->getInternalFormat()) 665 { 666 return false; 667 } 668 669 if (mImageArray[level]->getWidth() != std::max(1, width >> level)) 670 { 671 return false; 672 } 673 674 if (mImageArray[level]->getHeight() != std::max(1, height >> level)) 675 { 676 return false; 677 } 678 } 679 680 return true; 681 } 682 683 bool Texture2D::isCompressed(GLint level) const 684 { 685 return IsCompressed(getInternalFormat(level)); 686 } 687 688 bool Texture2D::isDepth(GLint level) const 689 { 690 return IsDepthTexture(getInternalFormat(level)); 691 } 692 693 // Constructs a native texture resource from the texture images 694 void Texture2D::createTexture() 695 { 696 GLsizei width = mImageArray[0]->getWidth(); 697 GLsizei height = mImageArray[0]->getHeight(); 698 699 if (!(width > 0 && height > 0)) 700 return; // do not attempt to create native textures for nonexistant data 701 702 GLint levels = creationLevels(width, height); 703 GLenum internalformat = mImageArray[0]->getInternalFormat(); 704 705 delete mTexStorage; 706 mTexStorage = new rx::TextureStorageInterface2D(mRenderer, levels, internalformat, mUsage, false, width, height); 707 708 if (mTexStorage->isManaged()) 709 { 710 int levels = levelCount(); 711 712 for (int level = 0; level < levels; level++) 713 { 714 mImageArray[level]->setManagedSurface(mTexStorage, level); 715 } 716 } 717 718 mDirtyImages = true; 719 } 720 721 void Texture2D::updateTexture() 722 { 723 bool mipmapping = (isMipmapFiltered() && isMipmapComplete()); 724 725 int levels = (mipmapping ? levelCount() : 1); 726 727 for (int level = 0; level < levels; level++) 728 { 729 rx::Image *image = mImageArray[level]; 730 731 if (image->isDirty()) 732 { 733 commitRect(level, 0, 0, mImageArray[level]->getWidth(), mImageArray[level]->getHeight()); 734 } 735 } 736 } 737 738 void Texture2D::convertToRenderTarget() 739 { 740 rx::TextureStorageInterface2D *newTexStorage = NULL; 741 742 if (mImageArray[0]->getWidth() != 0 && mImageArray[0]->getHeight() != 0) 743 { 744 GLsizei width = mImageArray[0]->getWidth(); 745 GLsizei height = mImageArray[0]->getHeight(); 746 GLint levels = mTexStorage != NULL ? mTexStorage->levelCount() : creationLevels(width, height); 747 GLenum internalformat = mImageArray[0]->getInternalFormat(); 748 749 newTexStorage = new rx::TextureStorageInterface2D(mRenderer, levels, internalformat, GL_FRAMEBUFFER_ATTACHMENT_ANGLE, true, width, height); 750 751 if (mTexStorage != NULL) 752 { 753 if (!mRenderer->copyToRenderTarget(newTexStorage, mTexStorage)) 754 { 755 delete newTexStorage; 756 return gl::error(GL_OUT_OF_MEMORY); 757 } 758 } 759 } 760 761 delete mTexStorage; 762 mTexStorage = newTexStorage; 763 764 mDirtyImages = true; 765 } 766 767 void Texture2D::generateMipmaps() 768 { 769 if (!mRenderer->getNonPower2TextureSupport()) 770 { 771 if (!isPow2(mImageArray[0]->getWidth()) || !isPow2(mImageArray[0]->getHeight())) 772 { 773 return gl::error(GL_INVALID_OPERATION); 774 } 775 } 776 777 // Purge array levels 1 through q and reset them to represent the generated mipmap levels. 778 unsigned int q = log2(std::max(mImageArray[0]->getWidth(), mImageArray[0]->getHeight())); 779 for (unsigned int i = 1; i <= q; i++) 780 { 781 redefineImage(i, mImageArray[0]->getInternalFormat(), 782 std::max(mImageArray[0]->getWidth() >> i, 1), 783 std::max(mImageArray[0]->getHeight() >> i, 1)); 784 } 785 786 if (mTexStorage && mTexStorage->isRenderTarget()) 787 { 788 for (unsigned int i = 1; i <= q; i++) 789 { 790 mTexStorage->generateMipmap(i); 791 792 mImageArray[i]->markClean(); 793 } 794 } 795 else 796 { 797 for (unsigned int i = 1; i <= q; i++) 798 { 799 mRenderer->generateMipmap(mImageArray[i], mImageArray[i - 1]); 800 } 801 } 802 } 803 804 Renderbuffer *Texture2D::getRenderbuffer(GLenum target) 805 { 806 if (target != GL_TEXTURE_2D) 807 { 808 return gl::error(GL_INVALID_OPERATION, (Renderbuffer *)NULL); 809 } 810 811 if (mColorbufferProxy == NULL) 812 { 813 mColorbufferProxy = new Renderbuffer(mRenderer, id(), new RenderbufferTexture2D(this, target)); 814 } 815 816 return mColorbufferProxy; 817 } 818 819 rx::RenderTarget *Texture2D::getRenderTarget(GLenum target) 820 { 821 ASSERT(target == GL_TEXTURE_2D); 822 823 // ensure the underlying texture is created 824 if (getStorage(true) == NULL) 825 { 826 return NULL; 827 } 828 829 updateTexture(); 830 831 // ensure this is NOT a depth texture 832 if (isDepth(0)) 833 { 834 return NULL; 835 } 836 837 return mTexStorage->getRenderTarget(); 838 } 839 840 rx::RenderTarget *Texture2D::getDepthStencil(GLenum target) 841 { 842 ASSERT(target == GL_TEXTURE_2D); 843 844 // ensure the underlying texture is created 845 if (getStorage(true) == NULL) 846 { 847 return NULL; 848 } 849 850 updateTexture(); 851 852 // ensure this is actually a depth texture 853 if (!isDepth(0)) 854 { 855 return NULL; 856 } 857 return mTexStorage->getRenderTarget(); 858 } 859 860 int Texture2D::levelCount() 861 { 862 return mTexStorage ? mTexStorage->levelCount() : 0; 863 } 864 865 rx::TextureStorageInterface *Texture2D::getStorage(bool renderTarget) 866 { 867 if (!mTexStorage || (renderTarget && !mTexStorage->isRenderTarget())) 868 { 869 if (renderTarget) 870 { 871 convertToRenderTarget(); 872 } 873 else 874 { 875 createTexture(); 876 } 877 } 878 879 return mTexStorage; 880 } 881 882 TextureCubeMap::TextureCubeMap(rx::Renderer *renderer, GLuint id) : Texture(renderer, id) 883 { 884 mTexStorage = NULL; 885 for (int i = 0; i < 6; i++) 886 { 887 mFaceProxies[i] = NULL; 888 mFaceProxyRefs[i] = 0; 889 890 for (int j = 0; j < IMPLEMENTATION_MAX_TEXTURE_LEVELS; ++j) 891 { 892 mImageArray[i][j] = renderer->createImage(); 893 } 894 } 895 } 896 897 TextureCubeMap::~TextureCubeMap() 898 { 899 for (int i = 0; i < 6; i++) 900 { 901 mFaceProxies[i] = NULL; 902 903 for (int j = 0; j < IMPLEMENTATION_MAX_TEXTURE_LEVELS; ++j) 904 { 905 delete mImageArray[i][j]; 906 } 907 } 908 909 delete mTexStorage; 910 mTexStorage = NULL; 911 } 912 913 // We need to maintain a count of references to renderbuffers acting as 914 // proxies for this texture, so that the texture is not deleted while 915 // proxy references still exist. If the reference count drops to zero, 916 // we set our proxy pointer NULL, so that a new attempt at referencing 917 // will cause recreation. 918 void TextureCubeMap::addProxyRef(const Renderbuffer *proxy) 919 { 920 for (int i = 0; i < 6; i++) 921 { 922 if (mFaceProxies[i] == proxy) 923 mFaceProxyRefs[i]++; 924 } 925 } 926 927 void TextureCubeMap::releaseProxy(const Renderbuffer *proxy) 928 { 929 for (int i = 0; i < 6; i++) 930 { 931 if (mFaceProxies[i] == proxy) 932 { 933 if (mFaceProxyRefs[i] > 0) 934 mFaceProxyRefs[i]--; 935 936 if (mFaceProxyRefs[i] == 0) 937 mFaceProxies[i] = NULL; 938 } 939 } 940 } 941 942 GLenum TextureCubeMap::getTarget() const 943 { 944 return GL_TEXTURE_CUBE_MAP; 945 } 946 947 GLsizei TextureCubeMap::getWidth(GLenum target, GLint level) const 948 { 949 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS) 950 return mImageArray[faceIndex(target)][level]->getWidth(); 951 else 952 return 0; 953 } 954 955 GLsizei TextureCubeMap::getHeight(GLenum target, GLint level) const 956 { 957 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS) 958 return mImageArray[faceIndex(target)][level]->getHeight(); 959 else 960 return 0; 961 } 962 963 GLenum TextureCubeMap::getInternalFormat(GLenum target, GLint level) const 964 { 965 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS) 966 return mImageArray[faceIndex(target)][level]->getInternalFormat(); 967 else 968 return GL_NONE; 969 } 970 971 GLenum TextureCubeMap::getActualFormat(GLenum target, GLint level) const 972 { 973 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS) 974 return mImageArray[faceIndex(target)][level]->getActualFormat(); 975 else 976 return D3DFMT_UNKNOWN; 977 } 978 979 void TextureCubeMap::setImagePosX(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels) 980 { 981 setImage(0, level, width, height, format, type, unpackAlignment, pixels); 982 } 983 984 void TextureCubeMap::setImageNegX(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels) 985 { 986 setImage(1, level, width, height, format, type, unpackAlignment, pixels); 987 } 988 989 void TextureCubeMap::setImagePosY(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels) 990 { 991 setImage(2, level, width, height, format, type, unpackAlignment, pixels); 992 } 993 994 void TextureCubeMap::setImageNegY(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels) 995 { 996 setImage(3, level, width, height, format, type, unpackAlignment, pixels); 997 } 998 999 void TextureCubeMap::setImagePosZ(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels) 1000 { 1001 setImage(4, level, width, height, format, type, unpackAlignment, pixels); 1002 } 1003 1004 void TextureCubeMap::setImageNegZ(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels) 1005 { 1006 setImage(5, level, width, height, format, type, unpackAlignment, pixels); 1007 } 1008 1009 void TextureCubeMap::setCompressedImage(GLenum face, GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels) 1010 { 1011 // compressed formats don't have separate sized internal formats-- we can just use the compressed format directly 1012 redefineImage(faceIndex(face), level, format, width, height); 1013 1014 Texture::setCompressedImage(imageSize, pixels, mImageArray[faceIndex(face)][level]); 1015 } 1016 1017 void TextureCubeMap::commitRect(int face, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height) 1018 { 1019 if (level < levelCount()) 1020 { 1021 rx::Image *image = mImageArray[face][level]; 1022 if (image->updateSurface(mTexStorage, face, level, xoffset, yoffset, width, height)) 1023 image->markClean(); 1024 } 1025 } 1026 1027 void TextureCubeMap::subImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels) 1028 { 1029 if (Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, mImageArray[faceIndex(target)][level])) 1030 { 1031 commitRect(faceIndex(target), level, xoffset, yoffset, width, height); 1032 } 1033 } 1034 1035 void TextureCubeMap::subImageCompressed(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels) 1036 { 1037 if (Texture::subImageCompressed(xoffset, yoffset, width, height, format, imageSize, pixels, mImageArray[faceIndex(target)][level])) 1038 { 1039 commitRect(faceIndex(target), level, xoffset, yoffset, width, height); 1040 } 1041 } 1042 1043 // Tests for cube map sampling completeness. [OpenGL ES 2.0.24] section 3.8.2 page 86. 1044 bool TextureCubeMap::isSamplerComplete() const 1045 { 1046 int size = mImageArray[0][0]->getWidth(); 1047 1048 bool mipmapping = isMipmapFiltered(); 1049 bool filtering, renderable; 1050 1051 if ((gl::ExtractType(getInternalFormat(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0)) == GL_FLOAT && !mRenderer->getFloat32TextureSupport(&filtering, &renderable)) || 1052 (gl::ExtractType(getInternalFormat(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0) == GL_HALF_FLOAT_OES) && !mRenderer->getFloat16TextureSupport(&filtering, &renderable))) 1053 { 1054 if (mSamplerState.magFilter != GL_NEAREST || 1055 (mSamplerState.minFilter != GL_NEAREST && mSamplerState.minFilter != GL_NEAREST_MIPMAP_NEAREST)) 1056 { 1057 return false; 1058 } 1059 } 1060 1061 if (!isPow2(size) && !mRenderer->getNonPower2TextureSupport()) 1062 { 1063 if (mSamplerState.wrapS != GL_CLAMP_TO_EDGE || mSamplerState.wrapT != GL_CLAMP_TO_EDGE || mipmapping) 1064 { 1065 return false; 1066 } 1067 } 1068 1069 if (!mipmapping) 1070 { 1071 if (!isCubeComplete()) 1072 { 1073 return false; 1074 } 1075 } 1076 else 1077 { 1078 if (!isMipmapCubeComplete()) // Also tests for isCubeComplete() 1079 { 1080 return false; 1081 } 1082 } 1083 1084 return true; 1085 } 1086 1087 // Tests for cube texture completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81. 1088 bool TextureCubeMap::isCubeComplete() const 1089 { 1090 if (mImageArray[0][0]->getWidth() <= 0 || mImageArray[0][0]->getHeight() != mImageArray[0][0]->getWidth()) 1091 { 1092 return false; 1093 } 1094 1095 for (unsigned int face = 1; face < 6; face++) 1096 { 1097 if (mImageArray[face][0]->getWidth() != mImageArray[0][0]->getWidth() || 1098 mImageArray[face][0]->getWidth() != mImageArray[0][0]->getHeight() || 1099 mImageArray[face][0]->getInternalFormat() != mImageArray[0][0]->getInternalFormat()) 1100 { 1101 return false; 1102 } 1103 } 1104 1105 return true; 1106 } 1107 1108 bool TextureCubeMap::isMipmapCubeComplete() const 1109 { 1110 if (isImmutable()) 1111 { 1112 return true; 1113 } 1114 1115 if (!isCubeComplete()) 1116 { 1117 return false; 1118 } 1119 1120 GLsizei size = mImageArray[0][0]->getWidth(); 1121 1122 int q = log2(size); 1123 1124 for (int face = 0; face < 6; face++) 1125 { 1126 for (int level = 1; level <= q; level++) 1127 { 1128 if (mImageArray[face][level]->getInternalFormat() != mImageArray[0][0]->getInternalFormat()) 1129 { 1130 return false; 1131 } 1132 1133 if (mImageArray[face][level]->getWidth() != std::max(1, size >> level)) 1134 { 1135 return false; 1136 } 1137 } 1138 } 1139 1140 return true; 1141 } 1142 1143 bool TextureCubeMap::isCompressed(GLenum target, GLint level) const 1144 { 1145 return IsCompressed(getInternalFormat(target, level)); 1146 } 1147 1148 // Constructs a native texture resource from the texture images, or returns an existing one 1149 void TextureCubeMap::createTexture() 1150 { 1151 GLsizei size = mImageArray[0][0]->getWidth(); 1152 1153 if (!(size > 0)) 1154 return; // do not attempt to create native textures for nonexistant data 1155 1156 GLint levels = creationLevels(size); 1157 GLenum internalformat = mImageArray[0][0]->getInternalFormat(); 1158 1159 delete mTexStorage; 1160 mTexStorage = new rx::TextureStorageInterfaceCube(mRenderer, levels, internalformat, mUsage, false, size); 1161 1162 if (mTexStorage->isManaged()) 1163 { 1164 int levels = levelCount(); 1165 1166 for (int face = 0; face < 6; face++) 1167 { 1168 for (int level = 0; level < levels; level++) 1169 { 1170 mImageArray[face][level]->setManagedSurface(mTexStorage, face, level); 1171 } 1172 } 1173 } 1174 1175 mDirtyImages = true; 1176 } 1177 1178 void TextureCubeMap::updateTexture() 1179 { 1180 bool mipmapping = isMipmapFiltered() && isMipmapCubeComplete(); 1181 1182 for (int face = 0; face < 6; face++) 1183 { 1184 int levels = (mipmapping ? levelCount() : 1); 1185 1186 for (int level = 0; level < levels; level++) 1187 { 1188 rx::Image *image = mImageArray[face][level]; 1189 1190 if (image->isDirty()) 1191 { 1192 commitRect(face, level, 0, 0, image->getWidth(), image->getHeight()); 1193 } 1194 } 1195 } 1196 } 1197 1198 void TextureCubeMap::convertToRenderTarget() 1199 { 1200 rx::TextureStorageInterfaceCube *newTexStorage = NULL; 1201 1202 if (mImageArray[0][0]->getWidth() != 0) 1203 { 1204 GLsizei size = mImageArray[0][0]->getWidth(); 1205 GLint levels = mTexStorage != NULL ? mTexStorage->levelCount() : creationLevels(size); 1206 GLenum internalformat = mImageArray[0][0]->getInternalFormat(); 1207 1208 newTexStorage = new rx::TextureStorageInterfaceCube(mRenderer, levels, internalformat, GL_FRAMEBUFFER_ATTACHMENT_ANGLE, true, size); 1209 1210 if (mTexStorage != NULL) 1211 { 1212 if (!mRenderer->copyToRenderTarget(newTexStorage, mTexStorage)) 1213 { 1214 delete newTexStorage; 1215 return gl::error(GL_OUT_OF_MEMORY); 1216 } 1217 } 1218 } 1219 1220 delete mTexStorage; 1221 mTexStorage = newTexStorage; 1222 1223 mDirtyImages = true; 1224 } 1225 1226 void TextureCubeMap::setImage(int faceIndex, GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels) 1227 { 1228 GLint internalformat = ConvertSizedInternalFormat(format, type); 1229 redefineImage(faceIndex, level, internalformat, width, height); 1230 1231 Texture::setImage(unpackAlignment, pixels, mImageArray[faceIndex][level]); 1232 } 1233 1234 unsigned int TextureCubeMap::faceIndex(GLenum face) 1235 { 1236 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_X - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 1); 1237 META_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_Y - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 2); 1238 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 3); 1239 META_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_Z - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 4); 1240 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 5); 1241 1242 return face - GL_TEXTURE_CUBE_MAP_POSITIVE_X; 1243 } 1244 1245 void TextureCubeMap::redefineImage(int face, GLint level, GLint internalformat, GLsizei width, GLsizei height) 1246 { 1247 // If there currently is a corresponding storage texture image, it has these parameters 1248 const int storageWidth = std::max(1, mImageArray[0][0]->getWidth() >> level); 1249 const int storageHeight = std::max(1, mImageArray[0][0]->getHeight() >> level); 1250 const int storageFormat = mImageArray[0][0]->getInternalFormat(); 1251 1252 mImageArray[face][level]->redefine(mRenderer, internalformat, width, height, false); 1253 1254 if (mTexStorage) 1255 { 1256 const int storageLevels = mTexStorage->levelCount(); 1257 1258 if ((level >= storageLevels && storageLevels != 0) || 1259 width != storageWidth || 1260 height != storageHeight || 1261 internalformat != storageFormat) // Discard mismatched storage 1262 { 1263 for (int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++) 1264 { 1265 for (int f = 0; f < 6; f++) 1266 { 1267 mImageArray[f][i]->markDirty(); 1268 } 1269 } 1270 1271 delete mTexStorage; 1272 mTexStorage = NULL; 1273 1274 mDirtyImages = true; 1275 } 1276 } 1277 } 1278 1279 void TextureCubeMap::copyImage(GLenum target, GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source) 1280 { 1281 unsigned int faceindex = faceIndex(target); 1282 GLint internalformat = gl::ConvertSizedInternalFormat(format, GL_UNSIGNED_BYTE); 1283 redefineImage(faceindex, level, internalformat, width, height); 1284 1285 if (!mImageArray[faceindex][level]->isRenderableFormat()) 1286 { 1287 mImageArray[faceindex][level]->copy(0, 0, x, y, width, height, source); 1288 mDirtyImages = true; 1289 } 1290 else 1291 { 1292 if (!mTexStorage || !mTexStorage->isRenderTarget()) 1293 { 1294 convertToRenderTarget(); 1295 } 1296 1297 mImageArray[faceindex][level]->markClean(); 1298 1299 ASSERT(width == height); 1300 1301 if (width > 0 && level < levelCount()) 1302 { 1303 gl::Rectangle sourceRect; 1304 sourceRect.x = x; 1305 sourceRect.width = width; 1306 sourceRect.y = y; 1307 sourceRect.height = height; 1308 1309 mRenderer->copyImage(source, sourceRect, format, 0, 0, mTexStorage, target, level); 1310 } 1311 } 1312 } 1313 1314 void TextureCubeMap::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source) 1315 { 1316 GLsizei size = mImageArray[faceIndex(target)][level]->getWidth(); 1317 1318 if (xoffset + width > size || yoffset + height > size) 1319 { 1320 return gl::error(GL_INVALID_VALUE); 1321 } 1322 1323 unsigned int faceindex = faceIndex(target); 1324 1325 if (!mImageArray[faceindex][level]->isRenderableFormat() || (!mTexStorage && !isSamplerComplete())) 1326 { 1327 mImageArray[faceindex][level]->copy(0, 0, x, y, width, height, source); 1328 mDirtyImages = true; 1329 } 1330 else 1331 { 1332 if (!mTexStorage || !mTexStorage->isRenderTarget()) 1333 { 1334 convertToRenderTarget(); 1335 } 1336 1337 updateTexture(); 1338 1339 if (level < levelCount()) 1340 { 1341 gl::Rectangle sourceRect; 1342 sourceRect.x = x; 1343 sourceRect.width = width; 1344 sourceRect.y = y; 1345 sourceRect.height = height; 1346 1347 mRenderer->copyImage(source, sourceRect, gl::ExtractFormat(mImageArray[0][0]->getInternalFormat()), 1348 xoffset, yoffset, mTexStorage, target, level); 1349 } 1350 } 1351 } 1352 1353 void TextureCubeMap::storage(GLsizei levels, GLenum internalformat, GLsizei size) 1354 { 1355 delete mTexStorage; 1356 mTexStorage = new rx::TextureStorageInterfaceCube(mRenderer, levels, internalformat, mUsage, false, size); 1357 mImmutable = true; 1358 1359 for (int level = 0; level < levels; level++) 1360 { 1361 for (int face = 0; face < 6; face++) 1362 { 1363 mImageArray[face][level]->redefine(mRenderer, internalformat, size, size, true); 1364 size = std::max(1, size >> 1); 1365 } 1366 } 1367 1368 for (int level = levels; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++) 1369 { 1370 for (int face = 0; face < 6; face++) 1371 { 1372 mImageArray[face][level]->redefine(mRenderer, GL_NONE, 0, 0, true); 1373 } 1374 } 1375 1376 if (mTexStorage->isManaged()) 1377 { 1378 int levels = levelCount(); 1379 1380 for (int face = 0; face < 6; face++) 1381 { 1382 for (int level = 0; level < levels; level++) 1383 { 1384 mImageArray[face][level]->setManagedSurface(mTexStorage, face, level); 1385 } 1386 } 1387 } 1388 } 1389 1390 void TextureCubeMap::generateMipmaps() 1391 { 1392 if (!isCubeComplete()) 1393 { 1394 return gl::error(GL_INVALID_OPERATION); 1395 } 1396 1397 if (!mRenderer->getNonPower2TextureSupport()) 1398 { 1399 if (!isPow2(mImageArray[0][0]->getWidth())) 1400 { 1401 return gl::error(GL_INVALID_OPERATION); 1402 } 1403 } 1404 1405 // Purge array levels 1 through q and reset them to represent the generated mipmap levels. 1406 unsigned int q = log2(mImageArray[0][0]->getWidth()); 1407 for (unsigned int f = 0; f < 6; f++) 1408 { 1409 for (unsigned int i = 1; i <= q; i++) 1410 { 1411 redefineImage(f, i, mImageArray[f][0]->getInternalFormat(), 1412 std::max(mImageArray[f][0]->getWidth() >> i, 1), 1413 std::max(mImageArray[f][0]->getWidth() >> i, 1)); 1414 } 1415 } 1416 1417 if (mTexStorage && mTexStorage->isRenderTarget()) 1418 { 1419 for (unsigned int f = 0; f < 6; f++) 1420 { 1421 for (unsigned int i = 1; i <= q; i++) 1422 { 1423 mTexStorage->generateMipmap(f, i); 1424 1425 mImageArray[f][i]->markClean(); 1426 } 1427 } 1428 } 1429 else 1430 { 1431 for (unsigned int f = 0; f < 6; f++) 1432 { 1433 for (unsigned int i = 1; i <= q; i++) 1434 { 1435 mRenderer->generateMipmap(mImageArray[f][i], mImageArray[f][i - 1]); 1436 } 1437 } 1438 } 1439 } 1440 1441 Renderbuffer *TextureCubeMap::getRenderbuffer(GLenum target) 1442 { 1443 if (!IsCubemapTextureTarget(target)) 1444 { 1445 return gl::error(GL_INVALID_OPERATION, (Renderbuffer *)NULL); 1446 } 1447 1448 unsigned int face = faceIndex(target); 1449 1450 if (mFaceProxies[face] == NULL) 1451 { 1452 mFaceProxies[face] = new Renderbuffer(mRenderer, id(), new RenderbufferTextureCubeMap(this, target)); 1453 } 1454 1455 return mFaceProxies[face]; 1456 } 1457 1458 rx::RenderTarget *TextureCubeMap::getRenderTarget(GLenum target) 1459 { 1460 ASSERT(IsCubemapTextureTarget(target)); 1461 1462 // ensure the underlying texture is created 1463 if (getStorage(true) == NULL) 1464 { 1465 return NULL; 1466 } 1467 1468 updateTexture(); 1469 1470 return mTexStorage->getRenderTarget(target); 1471 } 1472 1473 int TextureCubeMap::levelCount() 1474 { 1475 return mTexStorage ? mTexStorage->levelCount() - getLodOffset() : 0; 1476 } 1477 1478 rx::TextureStorageInterface *TextureCubeMap::getStorage(bool renderTarget) 1479 { 1480 if (!mTexStorage || (renderTarget && !mTexStorage->isRenderTarget())) 1481 { 1482 if (renderTarget) 1483 { 1484 convertToRenderTarget(); 1485 } 1486 else 1487 { 1488 createTexture(); 1489 } 1490 } 1491 1492 return mTexStorage; 1493 } 1494 1495 } 1496