gfx/angle/src/libGLESv2/Texture.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     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 //
     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.
    12 #include "libGLESv2/Texture.h"
    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"
    24 namespace gl
    25 {
    27 Texture::Texture(rx::Renderer *renderer, GLuint id) : RefCountObject(id)
    28 {
    29     mRenderer = renderer;
    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;
    39     mDirtyImages = true;
    41     mImmutable = false;
    42 }
    44 Texture::~Texture()
    45 {
    46 }
    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 }
    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 }
    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 }
    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 }
   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     }
   119     mSamplerState.maxAnisotropy = textureMaxAnisotropy;
   121     return true;
   122 }
   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 }
   138 GLenum Texture::getMinFilter() const
   139 {
   140     return mSamplerState.minFilter;
   141 }
   143 GLenum Texture::getMagFilter() const
   144 {
   145     return mSamplerState.magFilter;
   146 }
   148 GLenum Texture::getWrapS() const
   149 {
   150     return mSamplerState.wrapS;
   151 }
   153 GLenum Texture::getWrapT() const
   154 {
   155     return mSamplerState.wrapT;
   156 }
   158 float Texture::getMaxAnisotropy() const
   159 {
   160     return mSamplerState.maxAnisotropy;
   161 }
   163 int Texture::getLodOffset()
   164 {
   165     rx::TextureStorageInterface *texture = getStorage(false);
   166     return texture ? texture->getLodOffset() : 0;
   167 }
   169 void Texture::getSamplerState(SamplerState *sampler)
   170 {
   171     *sampler = mSamplerState;
   172     sampler->lodOffset = getLodOffset();
   173 }
   175 GLenum Texture::getUsage() const
   176 {
   177     return mUsage;
   178 }
   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 }
   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 }
   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 }
   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     }
   223     return true;
   224 }
   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     }
   234     return true;
   235 }
   237 rx::TextureStorageInterface *Texture::getNativeTexture()
   238 {
   239     // ensure the underlying texture is created
   241     rx::TextureStorageInterface *storage = getStorage(false);
   242     if (storage)
   243     {
   244         updateTexture();
   245     }
   247     return storage;
   248 }
   250 bool Texture::hasDirtyImages() const
   251 {
   252     return mDirtyImages;
   253 }
   255 void Texture::resetDirty()
   256 {
   257     mDirtyImages = false;
   258 }
   260 unsigned int Texture::getTextureSerial()
   261 {
   262     rx::TextureStorageInterface *texture = getStorage(false);
   263     return texture ? texture->getTextureSerial() : 0;
   264 }
   266 unsigned int Texture::getRenderTargetSerial(GLenum target)
   267 {
   268     rx::TextureStorageInterface *texture = getStorage(true);
   269     return texture ? texture->getRenderTargetSerial(target) : 0;
   270 }
   272 bool Texture::isImmutable() const
   273 {
   274     return mImmutable;
   275 }
   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 }
   290 GLint Texture::creationLevels(GLsizei size) const
   291 {
   292     return creationLevels(size, size);
   293 }
   295 Texture2D::Texture2D(rx::Renderer *renderer, GLuint id) : Texture(renderer, id)
   296 {
   297     mTexStorage = NULL;
   298     mSurface = NULL;
   299     mColorbufferProxy = NULL;
   300     mProxyRefs = 0;
   302     for (int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; ++i)
   303     {
   304         mImageArray[i] = renderer->createImage();
   305     }
   306 }
   308 Texture2D::~Texture2D()
   309 {
   310     mColorbufferProxy = NULL;
   312     delete mTexStorage;
   313     mTexStorage = NULL;
   315     if (mSurface)
   316     {
   317         mSurface->setBoundTexture(NULL);
   318         mSurface = NULL;
   319     }
   321     for (int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; ++i)
   322     {
   323         delete mImageArray[i];
   324     }
   325 }
   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 }
   335 void Texture2D::releaseProxy(const Renderbuffer *proxy)
   336 {
   337     if (mProxyRefs > 0)
   338         mProxyRefs--;
   340     if (mProxyRefs == 0)
   341         mColorbufferProxy = NULL;
   342 }
   344 GLenum Texture2D::getTarget() const
   345 {
   346     return GL_TEXTURE_2D;
   347 }
   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 }
   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 }
   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 }
   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 }
   381 void Texture2D::redefineImage(GLint level, GLint internalformat, GLsizei width, GLsizei height)
   382 {
   383     releaseTexImage();
   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();
   390     mImageArray[level]->redefine(mRenderer, internalformat, width, height, false);
   392     if (mTexStorage)
   393     {
   394         const int storageLevels = mTexStorage->levelCount();
   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             }
   406             delete mTexStorage;
   407             mTexStorage = NULL;
   408             mDirtyImages = true;
   409         }
   410     }
   411 }
   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);
   418     Texture::setImage(unpackAlignment, pixels, mImageArray[level]);
   419 }
   421 void Texture2D::bindTexImage(egl::Surface *surface)
   422 {
   423     releaseTexImage();
   425     GLint internalformat = surface->getFormat();
   427     mImageArray[0]->redefine(mRenderer, internalformat, surface->getWidth(), surface->getHeight(), true);
   429     delete mTexStorage;
   430     mTexStorage = new rx::TextureStorageInterface2D(mRenderer, surface->getSwapChain());
   432     mDirtyImages = true;
   433     mSurface = surface;
   434     mSurface->setBoundTexture(this);
   435 }
   437 void Texture2D::releaseTexImage()
   438 {
   439     if (mSurface)
   440     {
   441         mSurface->setBoundTexture(NULL);
   442         mSurface = NULL;
   444         if (mTexStorage)
   445         {
   446             delete mTexStorage;
   447             mTexStorage = NULL;
   448         }
   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 }
   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);
   462     Texture::setCompressedImage(imageSize, pixels, mImageArray[level]);
   463 }
   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 }
   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 }
   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 }
   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);
   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         }
   510         mImageArray[level]->markClean();
   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;
   520             mRenderer->copyImage(source, sourceRect, format, 0, 0, mTexStorage, level);
   521         }
   522     }
   523 }
   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     }
   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         }
   544         updateTexture();
   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;
   554             mRenderer->copyImage(source, sourceRect, 
   555                                  gl::ExtractFormat(mImageArray[0]->getInternalFormat()),
   556                                  xoffset, yoffset, mTexStorage, level);
   557         }
   558     }
   559 }
   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;
   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     }
   574     for (int level = levels; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++)
   575     {
   576         mImageArray[level]->redefine(mRenderer, GL_NONE, 0, 0, true);
   577     }
   579     if (mTexStorage->isManaged())
   580     {
   581         int levels = levelCount();
   583         for (int level = 0; level < levels; level++)
   584         {
   585             mImageArray[level]->setManagedSurface(mTexStorage, level);
   586         }
   587     }
   588 }
   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();
   596     if (width <= 0 || height <= 0)
   597     {
   598         return false;
   599     }
   601     bool mipmapping = isMipmapFiltered();
   602     bool filtering, renderable;
   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     }
   614     bool npotSupport = mRenderer->getNonPower2TextureSupport();
   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     }
   625     if (mipmapping)
   626     {
   627         if (!npotSupport)
   628         {
   629             if (!isPow2(width) || !isPow2(height))
   630             {
   631                 return false;
   632             }
   633         }
   635         if (!isMipmapComplete())
   636         {
   637             return false;
   638         }
   639     }
   641     return true;
   642 }
   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     }
   652     GLsizei width = mImageArray[0]->getWidth();
   653     GLsizei height = mImageArray[0]->getHeight();
   655     if (width <= 0 || height <= 0)
   656     {
   657         return false;
   658     }
   660     int q = log2(std::max(width, height));
   662     for (int level = 1; level <= q; level++)
   663     {
   664         if (mImageArray[level]->getInternalFormat() != mImageArray[0]->getInternalFormat())
   665         {
   666             return false;
   667         }
   669         if (mImageArray[level]->getWidth() != std::max(1, width >> level))
   670         {
   671             return false;
   672         }
   674         if (mImageArray[level]->getHeight() != std::max(1, height >> level))
   675         {
   676             return false;
   677         }
   678     }
   680     return true;
   681 }
   683 bool Texture2D::isCompressed(GLint level) const
   684 {
   685     return IsCompressed(getInternalFormat(level));
   686 }
   688 bool Texture2D::isDepth(GLint level) const
   689 {
   690     return IsDepthTexture(getInternalFormat(level));
   691 }
   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();
   699     if (!(width > 0 && height > 0))
   700         return; // do not attempt to create native textures for nonexistant data
   702     GLint levels = creationLevels(width, height);
   703     GLenum internalformat = mImageArray[0]->getInternalFormat();
   705     delete mTexStorage;
   706     mTexStorage = new rx::TextureStorageInterface2D(mRenderer, levels, internalformat, mUsage, false, width, height);
   708     if (mTexStorage->isManaged())
   709     {
   710         int levels = levelCount();
   712         for (int level = 0; level < levels; level++)
   713         {
   714             mImageArray[level]->setManagedSurface(mTexStorage, level);
   715         }
   716     }
   718     mDirtyImages = true;
   719 }
   721 void Texture2D::updateTexture()
   722 {
   723     bool mipmapping = (isMipmapFiltered() && isMipmapComplete());
   725     int levels = (mipmapping ? levelCount() : 1);
   727     for (int level = 0; level < levels; level++)
   728     {
   729         rx::Image *image = mImageArray[level];
   731         if (image->isDirty())
   732         {
   733             commitRect(level, 0, 0, mImageArray[level]->getWidth(), mImageArray[level]->getHeight());
   734         }
   735     }
   736 }
   738 void Texture2D::convertToRenderTarget()
   739 {
   740     rx::TextureStorageInterface2D *newTexStorage = NULL;
   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();
   749         newTexStorage = new rx::TextureStorageInterface2D(mRenderer, levels, internalformat, GL_FRAMEBUFFER_ATTACHMENT_ANGLE, true, width, height);
   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     }
   761     delete mTexStorage;
   762     mTexStorage = newTexStorage;
   764     mDirtyImages = true;
   765 }
   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     }
   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     }
   786     if (mTexStorage && mTexStorage->isRenderTarget())
   787     {
   788         for (unsigned int i = 1; i <= q; i++)
   789         {
   790             mTexStorage->generateMipmap(i);
   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 }
   804 Renderbuffer *Texture2D::getRenderbuffer(GLenum target)
   805 {
   806     if (target != GL_TEXTURE_2D)
   807     {
   808         return gl::error(GL_INVALID_OPERATION, (Renderbuffer *)NULL);
   809     }
   811     if (mColorbufferProxy == NULL)
   812     {
   813         mColorbufferProxy = new Renderbuffer(mRenderer, id(), new RenderbufferTexture2D(this, target));
   814     }
   816     return mColorbufferProxy;
   817 }
   819 rx::RenderTarget *Texture2D::getRenderTarget(GLenum target)
   820 {
   821     ASSERT(target == GL_TEXTURE_2D);
   823     // ensure the underlying texture is created
   824     if (getStorage(true) == NULL)
   825     {
   826         return NULL;
   827     }
   829     updateTexture();
   831     // ensure this is NOT a depth texture
   832     if (isDepth(0))
   833     {
   834         return NULL;
   835     }
   837     return mTexStorage->getRenderTarget();
   838 }
   840 rx::RenderTarget *Texture2D::getDepthStencil(GLenum target)
   841 {
   842     ASSERT(target == GL_TEXTURE_2D);
   844     // ensure the underlying texture is created
   845     if (getStorage(true) == NULL)
   846     {
   847         return NULL;
   848     }
   850     updateTexture();
   852     // ensure this is actually a depth texture
   853     if (!isDepth(0))
   854     {
   855         return NULL;
   856     }
   857     return mTexStorage->getRenderTarget();
   858 }
   860 int Texture2D::levelCount()
   861 {
   862     return mTexStorage ? mTexStorage->levelCount() : 0;
   863 }
   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     }
   879     return mTexStorage;
   880 }
   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;
   890         for (int j = 0; j < IMPLEMENTATION_MAX_TEXTURE_LEVELS; ++j)
   891         {
   892             mImageArray[i][j] = renderer->createImage();
   893         }
   894     }
   895 }
   897 TextureCubeMap::~TextureCubeMap()
   898 {
   899     for (int i = 0; i < 6; i++)
   900     {
   901         mFaceProxies[i] = NULL;
   903         for (int j = 0; j < IMPLEMENTATION_MAX_TEXTURE_LEVELS; ++j)
   904         {
   905             delete mImageArray[i][j];
   906         }
   907     }
   909     delete mTexStorage;
   910     mTexStorage = NULL;
   911 }
   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 }
   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]--;
   936             if (mFaceProxyRefs[i] == 0)
   937                 mFaceProxies[i] = NULL;
   938         }
   939     }
   940 }
   942 GLenum TextureCubeMap::getTarget() const
   943 {
   944     return GL_TEXTURE_CUBE_MAP;
   945 }
   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 }
   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 }
   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 }
   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 }
   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 }
   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 }
   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 }
   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 }
   999 void TextureCubeMap::setImagePosZ(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
  1001     setImage(4, level, width, height, format, type, unpackAlignment, pixels);
  1004 void TextureCubeMap::setImageNegZ(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
  1006     setImage(5, level, width, height, format, type, unpackAlignment, pixels);
  1009 void TextureCubeMap::setCompressedImage(GLenum face, GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels)
  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);
  1014     Texture::setCompressedImage(imageSize, pixels, mImageArray[faceIndex(face)][level]);
  1017 void TextureCubeMap::commitRect(int face, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
  1019     if (level < levelCount())
  1021         rx::Image *image = mImageArray[face][level];
  1022         if (image->updateSurface(mTexStorage, face, level, xoffset, yoffset, width, height))
  1023             image->markClean();
  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)
  1029     if (Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, mImageArray[faceIndex(target)][level]))
  1031         commitRect(faceIndex(target), level, xoffset, yoffset, width, height);
  1035 void TextureCubeMap::subImageCompressed(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels)
  1037     if (Texture::subImageCompressed(xoffset, yoffset, width, height, format, imageSize, pixels, mImageArray[faceIndex(target)][level]))
  1039         commitRect(faceIndex(target), level, xoffset, yoffset, width, height);
  1043 // Tests for cube map sampling completeness. [OpenGL ES 2.0.24] section 3.8.2 page 86.
  1044 bool TextureCubeMap::isSamplerComplete() const
  1046     int size = mImageArray[0][0]->getWidth();
  1048     bool mipmapping = isMipmapFiltered();
  1049     bool filtering, renderable;
  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)))
  1054         if (mSamplerState.magFilter != GL_NEAREST ||
  1055             (mSamplerState.minFilter != GL_NEAREST && mSamplerState.minFilter != GL_NEAREST_MIPMAP_NEAREST))
  1057             return false;
  1061     if (!isPow2(size) && !mRenderer->getNonPower2TextureSupport())
  1063         if (mSamplerState.wrapS != GL_CLAMP_TO_EDGE || mSamplerState.wrapT != GL_CLAMP_TO_EDGE || mipmapping)
  1065             return false;
  1069     if (!mipmapping)
  1071         if (!isCubeComplete())
  1073             return false;
  1076     else
  1078         if (!isMipmapCubeComplete())   // Also tests for isCubeComplete()
  1080             return false;
  1084     return true;
  1087 // Tests for cube texture completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.
  1088 bool TextureCubeMap::isCubeComplete() const
  1090     if (mImageArray[0][0]->getWidth() <= 0 || mImageArray[0][0]->getHeight() != mImageArray[0][0]->getWidth())
  1092         return false;
  1095     for (unsigned int face = 1; face < 6; face++)
  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())
  1101             return false;
  1105     return true;
  1108 bool TextureCubeMap::isMipmapCubeComplete() const
  1110     if (isImmutable())
  1112         return true;
  1115     if (!isCubeComplete())
  1117         return false;
  1120     GLsizei size = mImageArray[0][0]->getWidth();
  1122     int q = log2(size);
  1124     for (int face = 0; face < 6; face++)
  1126         for (int level = 1; level <= q; level++)
  1128             if (mImageArray[face][level]->getInternalFormat() != mImageArray[0][0]->getInternalFormat())
  1130                 return false;
  1133             if (mImageArray[face][level]->getWidth() != std::max(1, size >> level))
  1135                 return false;
  1140     return true;
  1143 bool TextureCubeMap::isCompressed(GLenum target, GLint level) const
  1145     return IsCompressed(getInternalFormat(target, level));
  1148 // Constructs a native texture resource from the texture images, or returns an existing one
  1149 void TextureCubeMap::createTexture()
  1151     GLsizei size = mImageArray[0][0]->getWidth();
  1153     if (!(size > 0))
  1154         return; // do not attempt to create native textures for nonexistant data
  1156     GLint levels = creationLevels(size);
  1157     GLenum internalformat = mImageArray[0][0]->getInternalFormat();
  1159     delete mTexStorage;
  1160     mTexStorage = new rx::TextureStorageInterfaceCube(mRenderer, levels, internalformat, mUsage, false, size);
  1162     if (mTexStorage->isManaged())
  1164         int levels = levelCount();
  1166         for (int face = 0; face < 6; face++)
  1168             for (int level = 0; level < levels; level++)
  1170                 mImageArray[face][level]->setManagedSurface(mTexStorage, face, level);
  1175     mDirtyImages = true;
  1178 void TextureCubeMap::updateTexture()
  1180     bool mipmapping = isMipmapFiltered() && isMipmapCubeComplete();
  1182     for (int face = 0; face < 6; face++)
  1184         int levels = (mipmapping ? levelCount() : 1);
  1186         for (int level = 0; level < levels; level++)
  1188             rx::Image *image = mImageArray[face][level];
  1190             if (image->isDirty())
  1192                 commitRect(face, level, 0, 0, image->getWidth(), image->getHeight());
  1198 void TextureCubeMap::convertToRenderTarget()
  1200     rx::TextureStorageInterfaceCube *newTexStorage = NULL;
  1202     if (mImageArray[0][0]->getWidth() != 0)
  1204         GLsizei size = mImageArray[0][0]->getWidth();
  1205         GLint levels = mTexStorage != NULL ? mTexStorage->levelCount() : creationLevels(size);
  1206         GLenum internalformat = mImageArray[0][0]->getInternalFormat();
  1208         newTexStorage = new rx::TextureStorageInterfaceCube(mRenderer, levels, internalformat, GL_FRAMEBUFFER_ATTACHMENT_ANGLE, true, size);
  1210         if (mTexStorage != NULL)
  1212             if (!mRenderer->copyToRenderTarget(newTexStorage, mTexStorage))
  1214                 delete newTexStorage;
  1215                 return gl::error(GL_OUT_OF_MEMORY);
  1220     delete mTexStorage;
  1221     mTexStorage = newTexStorage;
  1223     mDirtyImages = true;
  1226 void TextureCubeMap::setImage(int faceIndex, GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
  1228     GLint internalformat = ConvertSizedInternalFormat(format, type);
  1229     redefineImage(faceIndex, level, internalformat, width, height);
  1231     Texture::setImage(unpackAlignment, pixels, mImageArray[faceIndex][level]);
  1234 unsigned int TextureCubeMap::faceIndex(GLenum face)
  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);
  1242     return face - GL_TEXTURE_CUBE_MAP_POSITIVE_X;
  1245 void TextureCubeMap::redefineImage(int face, GLint level, GLint internalformat, GLsizei width, GLsizei height)
  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();
  1252     mImageArray[face][level]->redefine(mRenderer, internalformat, width, height, false);
  1254     if (mTexStorage)
  1256         const int storageLevels = mTexStorage->levelCount();
  1258         if ((level >= storageLevels && storageLevels != 0) ||
  1259             width != storageWidth ||
  1260             height != storageHeight ||
  1261             internalformat != storageFormat)   // Discard mismatched storage
  1263             for (int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
  1265                 for (int f = 0; f < 6; f++)
  1267                     mImageArray[f][i]->markDirty();
  1271             delete mTexStorage;
  1272             mTexStorage = NULL;
  1274             mDirtyImages = true;
  1279 void TextureCubeMap::copyImage(GLenum target, GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
  1281     unsigned int faceindex = faceIndex(target);
  1282     GLint internalformat = gl::ConvertSizedInternalFormat(format, GL_UNSIGNED_BYTE);
  1283     redefineImage(faceindex, level, internalformat, width, height);
  1285     if (!mImageArray[faceindex][level]->isRenderableFormat())
  1287         mImageArray[faceindex][level]->copy(0, 0, x, y, width, height, source);
  1288         mDirtyImages = true;
  1290     else
  1292         if (!mTexStorage || !mTexStorage->isRenderTarget())
  1294             convertToRenderTarget();
  1297         mImageArray[faceindex][level]->markClean();
  1299         ASSERT(width == height);
  1301         if (width > 0 && level < levelCount())
  1303             gl::Rectangle sourceRect;
  1304             sourceRect.x = x;
  1305             sourceRect.width = width;
  1306             sourceRect.y = y;
  1307             sourceRect.height = height;
  1309             mRenderer->copyImage(source, sourceRect, format, 0, 0, mTexStorage, target, level);
  1314 void TextureCubeMap::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
  1316     GLsizei size = mImageArray[faceIndex(target)][level]->getWidth();
  1318     if (xoffset + width > size || yoffset + height > size)
  1320         return gl::error(GL_INVALID_VALUE);
  1323     unsigned int faceindex = faceIndex(target);
  1325     if (!mImageArray[faceindex][level]->isRenderableFormat() || (!mTexStorage && !isSamplerComplete()))
  1327         mImageArray[faceindex][level]->copy(0, 0, x, y, width, height, source);
  1328         mDirtyImages = true;
  1330     else
  1332         if (!mTexStorage || !mTexStorage->isRenderTarget())
  1334             convertToRenderTarget();
  1337         updateTexture();
  1339         if (level < levelCount())
  1341             gl::Rectangle sourceRect;
  1342             sourceRect.x = x;
  1343             sourceRect.width = width;
  1344             sourceRect.y = y;
  1345             sourceRect.height = height;
  1347             mRenderer->copyImage(source, sourceRect, gl::ExtractFormat(mImageArray[0][0]->getInternalFormat()), 
  1348                                  xoffset, yoffset, mTexStorage, target, level);
  1353 void TextureCubeMap::storage(GLsizei levels, GLenum internalformat, GLsizei size)
  1355     delete mTexStorage;
  1356     mTexStorage = new rx::TextureStorageInterfaceCube(mRenderer, levels, internalformat, mUsage, false, size);
  1357     mImmutable = true;
  1359     for (int level = 0; level < levels; level++)
  1361         for (int face = 0; face < 6; face++)
  1363             mImageArray[face][level]->redefine(mRenderer, internalformat, size, size, true);
  1364             size = std::max(1, size >> 1);
  1368     for (int level = levels; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++)
  1370         for (int face = 0; face < 6; face++)
  1372             mImageArray[face][level]->redefine(mRenderer, GL_NONE, 0, 0, true);
  1376     if (mTexStorage->isManaged())
  1378         int levels = levelCount();
  1380         for (int face = 0; face < 6; face++)
  1382             for (int level = 0; level < levels; level++)
  1384                 mImageArray[face][level]->setManagedSurface(mTexStorage, face, level);
  1390 void TextureCubeMap::generateMipmaps()
  1392     if (!isCubeComplete())
  1394         return gl::error(GL_INVALID_OPERATION);
  1397     if (!mRenderer->getNonPower2TextureSupport())
  1399         if (!isPow2(mImageArray[0][0]->getWidth()))
  1401             return gl::error(GL_INVALID_OPERATION);
  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++)
  1409         for (unsigned int i = 1; i <= q; i++)
  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));
  1417     if (mTexStorage && mTexStorage->isRenderTarget())
  1419         for (unsigned int f = 0; f < 6; f++)
  1421             for (unsigned int i = 1; i <= q; i++)
  1423                 mTexStorage->generateMipmap(f, i);
  1425                 mImageArray[f][i]->markClean();
  1429     else
  1431         for (unsigned int f = 0; f < 6; f++)
  1433             for (unsigned int i = 1; i <= q; i++)
  1435                 mRenderer->generateMipmap(mImageArray[f][i], mImageArray[f][i - 1]);
  1441 Renderbuffer *TextureCubeMap::getRenderbuffer(GLenum target)
  1443     if (!IsCubemapTextureTarget(target))
  1445         return gl::error(GL_INVALID_OPERATION, (Renderbuffer *)NULL);
  1448     unsigned int face = faceIndex(target);
  1450     if (mFaceProxies[face] == NULL)
  1452         mFaceProxies[face] = new Renderbuffer(mRenderer, id(), new RenderbufferTextureCubeMap(this, target));
  1455     return mFaceProxies[face];
  1458 rx::RenderTarget *TextureCubeMap::getRenderTarget(GLenum target)
  1460     ASSERT(IsCubemapTextureTarget(target));
  1462     // ensure the underlying texture is created
  1463     if (getStorage(true) == NULL)
  1465         return NULL;
  1468     updateTexture();
  1470     return mTexStorage->getRenderTarget(target);
  1473 int TextureCubeMap::levelCount()
  1475     return mTexStorage ? mTexStorage->levelCount() - getLodOffset() : 0;
  1478 rx::TextureStorageInterface *TextureCubeMap::getStorage(bool renderTarget)
  1480     if (!mTexStorage || (renderTarget && !mTexStorage->isRenderTarget()))
  1482         if (renderTarget)
  1484             convertToRenderTarget();
  1486         else
  1488             createTexture();
  1492     return mTexStorage;

mercurial