michael@0: #include "precompiled.h" michael@0: // michael@0: // Copyright (c) 2002-2013 The ANGLE Project Authors. All rights reserved. michael@0: // Use of this source code is governed by a BSD-style license that can be michael@0: // found in the LICENSE file. michael@0: // michael@0: michael@0: // Texture.cpp: Implements the gl::Texture class and its derived classes michael@0: // Texture2D and TextureCubeMap. Implements GL texture objects and related michael@0: // functionality. [OpenGL ES 2.0.24] section 3.7 page 63. michael@0: michael@0: #include "libGLESv2/Texture.h" michael@0: michael@0: #include "libGLESv2/main.h" michael@0: #include "libGLESv2/mathutil.h" michael@0: #include "libGLESv2/utilities.h" michael@0: #include "libGLESv2/renderer/Blit.h" michael@0: #include "libGLESv2/Renderbuffer.h" michael@0: #include "libGLESv2/renderer/Image.h" michael@0: #include "libGLESv2/renderer/Renderer.h" michael@0: #include "libGLESv2/renderer/TextureStorage.h" michael@0: #include "libEGL/Surface.h" michael@0: michael@0: namespace gl michael@0: { michael@0: michael@0: Texture::Texture(rx::Renderer *renderer, GLuint id) : RefCountObject(id) michael@0: { michael@0: mRenderer = renderer; michael@0: michael@0: mSamplerState.minFilter = GL_NEAREST_MIPMAP_LINEAR; michael@0: mSamplerState.magFilter = GL_LINEAR; michael@0: mSamplerState.wrapS = GL_REPEAT; michael@0: mSamplerState.wrapT = GL_REPEAT; michael@0: mSamplerState.maxAnisotropy = 1.0f; michael@0: mSamplerState.lodOffset = 0; michael@0: mUsage = GL_NONE; michael@0: michael@0: mDirtyImages = true; michael@0: michael@0: mImmutable = false; michael@0: } michael@0: michael@0: Texture::~Texture() michael@0: { michael@0: } michael@0: michael@0: // Returns true on successful filter state update (valid enum parameter) michael@0: bool Texture::setMinFilter(GLenum filter) michael@0: { michael@0: switch (filter) michael@0: { michael@0: case GL_NEAREST: michael@0: case GL_LINEAR: michael@0: case GL_NEAREST_MIPMAP_NEAREST: michael@0: case GL_LINEAR_MIPMAP_NEAREST: michael@0: case GL_NEAREST_MIPMAP_LINEAR: michael@0: case GL_LINEAR_MIPMAP_LINEAR: michael@0: mSamplerState.minFilter = filter; michael@0: return true; michael@0: default: michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: // Returns true on successful filter state update (valid enum parameter) michael@0: bool Texture::setMagFilter(GLenum filter) michael@0: { michael@0: switch (filter) michael@0: { michael@0: case GL_NEAREST: michael@0: case GL_LINEAR: michael@0: mSamplerState.magFilter = filter; michael@0: return true; michael@0: default: michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: // Returns true on successful wrap state update (valid enum parameter) michael@0: bool Texture::setWrapS(GLenum wrap) michael@0: { michael@0: switch (wrap) michael@0: { michael@0: case GL_REPEAT: michael@0: case GL_CLAMP_TO_EDGE: michael@0: case GL_MIRRORED_REPEAT: michael@0: mSamplerState.wrapS = wrap; michael@0: return true; michael@0: default: michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: // Returns true on successful wrap state update (valid enum parameter) michael@0: bool Texture::setWrapT(GLenum wrap) michael@0: { michael@0: switch (wrap) michael@0: { michael@0: case GL_REPEAT: michael@0: case GL_CLAMP_TO_EDGE: michael@0: case GL_MIRRORED_REPEAT: michael@0: mSamplerState.wrapT = wrap; michael@0: return true; michael@0: default: michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: // Returns true on successful max anisotropy update (valid anisotropy value) michael@0: bool Texture::setMaxAnisotropy(float textureMaxAnisotropy, float contextMaxAnisotropy) michael@0: { michael@0: textureMaxAnisotropy = std::min(textureMaxAnisotropy, contextMaxAnisotropy); michael@0: if (textureMaxAnisotropy < 1.0f) michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: mSamplerState.maxAnisotropy = textureMaxAnisotropy; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: // Returns true on successful usage state update (valid enum parameter) michael@0: bool Texture::setUsage(GLenum usage) michael@0: { michael@0: switch (usage) michael@0: { michael@0: case GL_NONE: michael@0: case GL_FRAMEBUFFER_ATTACHMENT_ANGLE: michael@0: mUsage = usage; michael@0: return true; michael@0: default: michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: GLenum Texture::getMinFilter() const michael@0: { michael@0: return mSamplerState.minFilter; michael@0: } michael@0: michael@0: GLenum Texture::getMagFilter() const michael@0: { michael@0: return mSamplerState.magFilter; michael@0: } michael@0: michael@0: GLenum Texture::getWrapS() const michael@0: { michael@0: return mSamplerState.wrapS; michael@0: } michael@0: michael@0: GLenum Texture::getWrapT() const michael@0: { michael@0: return mSamplerState.wrapT; michael@0: } michael@0: michael@0: float Texture::getMaxAnisotropy() const michael@0: { michael@0: return mSamplerState.maxAnisotropy; michael@0: } michael@0: michael@0: int Texture::getLodOffset() michael@0: { michael@0: rx::TextureStorageInterface *texture = getStorage(false); michael@0: return texture ? texture->getLodOffset() : 0; michael@0: } michael@0: michael@0: void Texture::getSamplerState(SamplerState *sampler) michael@0: { michael@0: *sampler = mSamplerState; michael@0: sampler->lodOffset = getLodOffset(); michael@0: } michael@0: michael@0: GLenum Texture::getUsage() const michael@0: { michael@0: return mUsage; michael@0: } michael@0: michael@0: bool Texture::isMipmapFiltered() const michael@0: { michael@0: switch (mSamplerState.minFilter) michael@0: { michael@0: case GL_NEAREST: michael@0: case GL_LINEAR: michael@0: return false; michael@0: case GL_NEAREST_MIPMAP_NEAREST: michael@0: case GL_LINEAR_MIPMAP_NEAREST: michael@0: case GL_NEAREST_MIPMAP_LINEAR: michael@0: case GL_LINEAR_MIPMAP_LINEAR: michael@0: return true; michael@0: default: UNREACHABLE(); michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: void Texture::setImage(GLint unpackAlignment, const void *pixels, rx::Image *image) michael@0: { michael@0: if (pixels != NULL) michael@0: { michael@0: image->loadData(0, 0, image->getWidth(), image->getHeight(), unpackAlignment, pixels); michael@0: mDirtyImages = true; michael@0: } michael@0: } michael@0: michael@0: void Texture::setCompressedImage(GLsizei imageSize, const void *pixels, rx::Image *image) michael@0: { michael@0: if (pixels != NULL) michael@0: { michael@0: image->loadCompressedData(0, 0, image->getWidth(), image->getHeight(), pixels); michael@0: mDirtyImages = true; michael@0: } michael@0: } michael@0: michael@0: bool Texture::subImage(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels, rx::Image *image) michael@0: { michael@0: if (pixels != NULL) michael@0: { michael@0: image->loadData(xoffset, yoffset, width, height, unpackAlignment, pixels); michael@0: mDirtyImages = true; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool Texture::subImageCompressed(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels, rx::Image *image) michael@0: { michael@0: if (pixels != NULL) michael@0: { michael@0: image->loadCompressedData(xoffset, yoffset, width, height, pixels); michael@0: mDirtyImages = true; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: rx::TextureStorageInterface *Texture::getNativeTexture() michael@0: { michael@0: // ensure the underlying texture is created michael@0: michael@0: rx::TextureStorageInterface *storage = getStorage(false); michael@0: if (storage) michael@0: { michael@0: updateTexture(); michael@0: } michael@0: michael@0: return storage; michael@0: } michael@0: michael@0: bool Texture::hasDirtyImages() const michael@0: { michael@0: return mDirtyImages; michael@0: } michael@0: michael@0: void Texture::resetDirty() michael@0: { michael@0: mDirtyImages = false; michael@0: } michael@0: michael@0: unsigned int Texture::getTextureSerial() michael@0: { michael@0: rx::TextureStorageInterface *texture = getStorage(false); michael@0: return texture ? texture->getTextureSerial() : 0; michael@0: } michael@0: michael@0: unsigned int Texture::getRenderTargetSerial(GLenum target) michael@0: { michael@0: rx::TextureStorageInterface *texture = getStorage(true); michael@0: return texture ? texture->getRenderTargetSerial(target) : 0; michael@0: } michael@0: michael@0: bool Texture::isImmutable() const michael@0: { michael@0: return mImmutable; michael@0: } michael@0: michael@0: GLint Texture::creationLevels(GLsizei width, GLsizei height) const michael@0: { michael@0: if ((isPow2(width) && isPow2(height)) || mRenderer->getNonPower2TextureSupport()) michael@0: { michael@0: return 0; // Maximum number of levels michael@0: } michael@0: else michael@0: { michael@0: // OpenGL ES 2.0 without GL_OES_texture_npot does not permit NPOT mipmaps. michael@0: return 1; michael@0: } michael@0: } michael@0: michael@0: GLint Texture::creationLevels(GLsizei size) const michael@0: { michael@0: return creationLevels(size, size); michael@0: } michael@0: michael@0: Texture2D::Texture2D(rx::Renderer *renderer, GLuint id) : Texture(renderer, id) michael@0: { michael@0: mTexStorage = NULL; michael@0: mSurface = NULL; michael@0: mColorbufferProxy = NULL; michael@0: mProxyRefs = 0; michael@0: michael@0: for (int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; ++i) michael@0: { michael@0: mImageArray[i] = renderer->createImage(); michael@0: } michael@0: } michael@0: michael@0: Texture2D::~Texture2D() michael@0: { michael@0: mColorbufferProxy = NULL; michael@0: michael@0: delete mTexStorage; michael@0: mTexStorage = NULL; michael@0: michael@0: if (mSurface) michael@0: { michael@0: mSurface->setBoundTexture(NULL); michael@0: mSurface = NULL; michael@0: } michael@0: michael@0: for (int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; ++i) michael@0: { michael@0: delete mImageArray[i]; michael@0: } michael@0: } michael@0: michael@0: // We need to maintain a count of references to renderbuffers acting as michael@0: // proxies for this texture, so that we do not attempt to use a pointer michael@0: // to a renderbuffer proxy which has been deleted. michael@0: void Texture2D::addProxyRef(const Renderbuffer *proxy) michael@0: { michael@0: mProxyRefs++; michael@0: } michael@0: michael@0: void Texture2D::releaseProxy(const Renderbuffer *proxy) michael@0: { michael@0: if (mProxyRefs > 0) michael@0: mProxyRefs--; michael@0: michael@0: if (mProxyRefs == 0) michael@0: mColorbufferProxy = NULL; michael@0: } michael@0: michael@0: GLenum Texture2D::getTarget() const michael@0: { michael@0: return GL_TEXTURE_2D; michael@0: } michael@0: michael@0: GLsizei Texture2D::getWidth(GLint level) const michael@0: { michael@0: if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS) michael@0: return mImageArray[level]->getWidth(); michael@0: else michael@0: return 0; michael@0: } michael@0: michael@0: GLsizei Texture2D::getHeight(GLint level) const michael@0: { michael@0: if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS) michael@0: return mImageArray[level]->getHeight(); michael@0: else michael@0: return 0; michael@0: } michael@0: michael@0: GLenum Texture2D::getInternalFormat(GLint level) const michael@0: { michael@0: if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS) michael@0: return mImageArray[level]->getInternalFormat(); michael@0: else michael@0: return GL_NONE; michael@0: } michael@0: michael@0: GLenum Texture2D::getActualFormat(GLint level) const michael@0: { michael@0: if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS) michael@0: return mImageArray[level]->getActualFormat(); michael@0: else michael@0: return D3DFMT_UNKNOWN; michael@0: } michael@0: michael@0: void Texture2D::redefineImage(GLint level, GLint internalformat, GLsizei width, GLsizei height) michael@0: { michael@0: releaseTexImage(); michael@0: michael@0: // If there currently is a corresponding storage texture image, it has these parameters michael@0: const int storageWidth = std::max(1, mImageArray[0]->getWidth() >> level); michael@0: const int storageHeight = std::max(1, mImageArray[0]->getHeight() >> level); michael@0: const int storageFormat = mImageArray[0]->getInternalFormat(); michael@0: michael@0: mImageArray[level]->redefine(mRenderer, internalformat, width, height, false); michael@0: michael@0: if (mTexStorage) michael@0: { michael@0: const int storageLevels = mTexStorage->levelCount(); michael@0: michael@0: if ((level >= storageLevels && storageLevels != 0) || michael@0: width != storageWidth || michael@0: height != storageHeight || michael@0: internalformat != storageFormat) // Discard mismatched storage michael@0: { michael@0: for (int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++) michael@0: { michael@0: mImageArray[i]->markDirty(); michael@0: } michael@0: michael@0: delete mTexStorage; michael@0: mTexStorage = NULL; michael@0: mDirtyImages = true; michael@0: } michael@0: } michael@0: } michael@0: michael@0: void Texture2D::setImage(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels) michael@0: { michael@0: GLint internalformat = ConvertSizedInternalFormat(format, type); michael@0: redefineImage(level, internalformat, width, height); michael@0: michael@0: Texture::setImage(unpackAlignment, pixels, mImageArray[level]); michael@0: } michael@0: michael@0: void Texture2D::bindTexImage(egl::Surface *surface) michael@0: { michael@0: releaseTexImage(); michael@0: michael@0: GLint internalformat = surface->getFormat(); michael@0: michael@0: mImageArray[0]->redefine(mRenderer, internalformat, surface->getWidth(), surface->getHeight(), true); michael@0: michael@0: delete mTexStorage; michael@0: mTexStorage = new rx::TextureStorageInterface2D(mRenderer, surface->getSwapChain()); michael@0: michael@0: mDirtyImages = true; michael@0: mSurface = surface; michael@0: mSurface->setBoundTexture(this); michael@0: } michael@0: michael@0: void Texture2D::releaseTexImage() michael@0: { michael@0: if (mSurface) michael@0: { michael@0: mSurface->setBoundTexture(NULL); michael@0: mSurface = NULL; michael@0: michael@0: if (mTexStorage) michael@0: { michael@0: delete mTexStorage; michael@0: mTexStorage = NULL; michael@0: } michael@0: michael@0: for (int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++) michael@0: { michael@0: mImageArray[i]->redefine(mRenderer, GL_NONE, 0, 0, true); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void Texture2D::setCompressedImage(GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels) michael@0: { michael@0: // compressed formats don't have separate sized internal formats-- we can just use the compressed format directly michael@0: redefineImage(level, format, width, height); michael@0: michael@0: Texture::setCompressedImage(imageSize, pixels, mImageArray[level]); michael@0: } michael@0: michael@0: void Texture2D::commitRect(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height) michael@0: { michael@0: if (level < levelCount()) michael@0: { michael@0: rx::Image *image = mImageArray[level]; michael@0: if (image->updateSurface(mTexStorage, level, xoffset, yoffset, width, height)) michael@0: { michael@0: image->markClean(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void Texture2D::subImage(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels) michael@0: { michael@0: if (Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, mImageArray[level])) michael@0: { michael@0: commitRect(level, xoffset, yoffset, width, height); michael@0: } michael@0: } michael@0: michael@0: void Texture2D::subImageCompressed(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels) michael@0: { michael@0: if (Texture::subImageCompressed(xoffset, yoffset, width, height, format, imageSize, pixels, mImageArray[level])) michael@0: { michael@0: commitRect(level, xoffset, yoffset, width, height); michael@0: } michael@0: } michael@0: michael@0: void Texture2D::copyImage(GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source) michael@0: { michael@0: GLint internalformat = ConvertSizedInternalFormat(format, GL_UNSIGNED_BYTE); michael@0: redefineImage(level, internalformat, width, height); michael@0: michael@0: if (!mImageArray[level]->isRenderableFormat()) michael@0: { michael@0: mImageArray[level]->copy(0, 0, x, y, width, height, source); michael@0: mDirtyImages = true; michael@0: } michael@0: else michael@0: { michael@0: if (!mTexStorage || !mTexStorage->isRenderTarget()) michael@0: { michael@0: convertToRenderTarget(); michael@0: } michael@0: michael@0: mImageArray[level]->markClean(); michael@0: michael@0: if (width != 0 && height != 0 && level < levelCount()) michael@0: { michael@0: gl::Rectangle sourceRect; michael@0: sourceRect.x = x; michael@0: sourceRect.width = width; michael@0: sourceRect.y = y; michael@0: sourceRect.height = height; michael@0: michael@0: mRenderer->copyImage(source, sourceRect, format, 0, 0, mTexStorage, level); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void Texture2D::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source) michael@0: { michael@0: if (xoffset + width > mImageArray[level]->getWidth() || yoffset + height > mImageArray[level]->getHeight()) michael@0: { michael@0: return gl::error(GL_INVALID_VALUE); michael@0: } michael@0: michael@0: if (!mImageArray[level]->isRenderableFormat() || (!mTexStorage && !isSamplerComplete())) michael@0: { michael@0: mImageArray[level]->copy(xoffset, yoffset, x, y, width, height, source); michael@0: mDirtyImages = true; michael@0: } michael@0: else michael@0: { michael@0: if (!mTexStorage || !mTexStorage->isRenderTarget()) michael@0: { michael@0: convertToRenderTarget(); michael@0: } michael@0: michael@0: updateTexture(); michael@0: michael@0: if (level < levelCount()) michael@0: { michael@0: gl::Rectangle sourceRect; michael@0: sourceRect.x = x; michael@0: sourceRect.width = width; michael@0: sourceRect.y = y; michael@0: sourceRect.height = height; michael@0: michael@0: mRenderer->copyImage(source, sourceRect, michael@0: gl::ExtractFormat(mImageArray[0]->getInternalFormat()), michael@0: xoffset, yoffset, mTexStorage, level); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void Texture2D::storage(GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) michael@0: { michael@0: delete mTexStorage; michael@0: mTexStorage = new rx::TextureStorageInterface2D(mRenderer, levels, internalformat, mUsage, false, width, height); michael@0: mImmutable = true; michael@0: michael@0: for (int level = 0; level < levels; level++) michael@0: { michael@0: mImageArray[level]->redefine(mRenderer, internalformat, width, height, true); michael@0: width = std::max(1, width >> 1); michael@0: height = std::max(1, height >> 1); michael@0: } michael@0: michael@0: for (int level = levels; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++) michael@0: { michael@0: mImageArray[level]->redefine(mRenderer, GL_NONE, 0, 0, true); michael@0: } michael@0: michael@0: if (mTexStorage->isManaged()) michael@0: { michael@0: int levels = levelCount(); michael@0: michael@0: for (int level = 0; level < levels; level++) michael@0: { michael@0: mImageArray[level]->setManagedSurface(mTexStorage, level); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Tests for 2D texture sampling completeness. [OpenGL ES 2.0.24] section 3.8.2 page 85. michael@0: bool Texture2D::isSamplerComplete() const michael@0: { michael@0: GLsizei width = mImageArray[0]->getWidth(); michael@0: GLsizei height = mImageArray[0]->getHeight(); michael@0: michael@0: if (width <= 0 || height <= 0) michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: bool mipmapping = isMipmapFiltered(); michael@0: bool filtering, renderable; michael@0: michael@0: if ((IsFloat32Format(getInternalFormat(0)) && !mRenderer->getFloat32TextureSupport(&filtering, &renderable)) || michael@0: (IsFloat16Format(getInternalFormat(0)) && !mRenderer->getFloat16TextureSupport(&filtering, &renderable))) michael@0: { michael@0: if (mSamplerState.magFilter != GL_NEAREST || michael@0: (mSamplerState.minFilter != GL_NEAREST && mSamplerState.minFilter != GL_NEAREST_MIPMAP_NEAREST)) michael@0: { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: bool npotSupport = mRenderer->getNonPower2TextureSupport(); michael@0: michael@0: if (!npotSupport) michael@0: { michael@0: if ((mSamplerState.wrapS != GL_CLAMP_TO_EDGE && !isPow2(width)) || michael@0: (mSamplerState.wrapT != GL_CLAMP_TO_EDGE && !isPow2(height))) michael@0: { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: if (mipmapping) michael@0: { michael@0: if (!npotSupport) michael@0: { michael@0: if (!isPow2(width) || !isPow2(height)) michael@0: { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: if (!isMipmapComplete()) michael@0: { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: // Tests for 2D texture (mipmap) completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81. michael@0: bool Texture2D::isMipmapComplete() const michael@0: { michael@0: if (isImmutable()) michael@0: { michael@0: return true; michael@0: } michael@0: michael@0: GLsizei width = mImageArray[0]->getWidth(); michael@0: GLsizei height = mImageArray[0]->getHeight(); michael@0: michael@0: if (width <= 0 || height <= 0) michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: int q = log2(std::max(width, height)); michael@0: michael@0: for (int level = 1; level <= q; level++) michael@0: { michael@0: if (mImageArray[level]->getInternalFormat() != mImageArray[0]->getInternalFormat()) michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: if (mImageArray[level]->getWidth() != std::max(1, width >> level)) michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: if (mImageArray[level]->getHeight() != std::max(1, height >> level)) michael@0: { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool Texture2D::isCompressed(GLint level) const michael@0: { michael@0: return IsCompressed(getInternalFormat(level)); michael@0: } michael@0: michael@0: bool Texture2D::isDepth(GLint level) const michael@0: { michael@0: return IsDepthTexture(getInternalFormat(level)); michael@0: } michael@0: michael@0: // Constructs a native texture resource from the texture images michael@0: void Texture2D::createTexture() michael@0: { michael@0: GLsizei width = mImageArray[0]->getWidth(); michael@0: GLsizei height = mImageArray[0]->getHeight(); michael@0: michael@0: if (!(width > 0 && height > 0)) michael@0: return; // do not attempt to create native textures for nonexistant data michael@0: michael@0: GLint levels = creationLevels(width, height); michael@0: GLenum internalformat = mImageArray[0]->getInternalFormat(); michael@0: michael@0: delete mTexStorage; michael@0: mTexStorage = new rx::TextureStorageInterface2D(mRenderer, levels, internalformat, mUsage, false, width, height); michael@0: michael@0: if (mTexStorage->isManaged()) michael@0: { michael@0: int levels = levelCount(); michael@0: michael@0: for (int level = 0; level < levels; level++) michael@0: { michael@0: mImageArray[level]->setManagedSurface(mTexStorage, level); michael@0: } michael@0: } michael@0: michael@0: mDirtyImages = true; michael@0: } michael@0: michael@0: void Texture2D::updateTexture() michael@0: { michael@0: bool mipmapping = (isMipmapFiltered() && isMipmapComplete()); michael@0: michael@0: int levels = (mipmapping ? levelCount() : 1); michael@0: michael@0: for (int level = 0; level < levels; level++) michael@0: { michael@0: rx::Image *image = mImageArray[level]; michael@0: michael@0: if (image->isDirty()) michael@0: { michael@0: commitRect(level, 0, 0, mImageArray[level]->getWidth(), mImageArray[level]->getHeight()); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void Texture2D::convertToRenderTarget() michael@0: { michael@0: rx::TextureStorageInterface2D *newTexStorage = NULL; michael@0: michael@0: if (mImageArray[0]->getWidth() != 0 && mImageArray[0]->getHeight() != 0) michael@0: { michael@0: GLsizei width = mImageArray[0]->getWidth(); michael@0: GLsizei height = mImageArray[0]->getHeight(); michael@0: GLint levels = mTexStorage != NULL ? mTexStorage->levelCount() : creationLevels(width, height); michael@0: GLenum internalformat = mImageArray[0]->getInternalFormat(); michael@0: michael@0: newTexStorage = new rx::TextureStorageInterface2D(mRenderer, levels, internalformat, GL_FRAMEBUFFER_ATTACHMENT_ANGLE, true, width, height); michael@0: michael@0: if (mTexStorage != NULL) michael@0: { michael@0: if (!mRenderer->copyToRenderTarget(newTexStorage, mTexStorage)) michael@0: { michael@0: delete newTexStorage; michael@0: return gl::error(GL_OUT_OF_MEMORY); michael@0: } michael@0: } michael@0: } michael@0: michael@0: delete mTexStorage; michael@0: mTexStorage = newTexStorage; michael@0: michael@0: mDirtyImages = true; michael@0: } michael@0: michael@0: void Texture2D::generateMipmaps() michael@0: { michael@0: if (!mRenderer->getNonPower2TextureSupport()) michael@0: { michael@0: if (!isPow2(mImageArray[0]->getWidth()) || !isPow2(mImageArray[0]->getHeight())) michael@0: { michael@0: return gl::error(GL_INVALID_OPERATION); michael@0: } michael@0: } michael@0: michael@0: // Purge array levels 1 through q and reset them to represent the generated mipmap levels. michael@0: unsigned int q = log2(std::max(mImageArray[0]->getWidth(), mImageArray[0]->getHeight())); michael@0: for (unsigned int i = 1; i <= q; i++) michael@0: { michael@0: redefineImage(i, mImageArray[0]->getInternalFormat(), michael@0: std::max(mImageArray[0]->getWidth() >> i, 1), michael@0: std::max(mImageArray[0]->getHeight() >> i, 1)); michael@0: } michael@0: michael@0: if (mTexStorage && mTexStorage->isRenderTarget()) michael@0: { michael@0: for (unsigned int i = 1; i <= q; i++) michael@0: { michael@0: mTexStorage->generateMipmap(i); michael@0: michael@0: mImageArray[i]->markClean(); michael@0: } michael@0: } michael@0: else michael@0: { michael@0: for (unsigned int i = 1; i <= q; i++) michael@0: { michael@0: mRenderer->generateMipmap(mImageArray[i], mImageArray[i - 1]); michael@0: } michael@0: } michael@0: } michael@0: michael@0: Renderbuffer *Texture2D::getRenderbuffer(GLenum target) michael@0: { michael@0: if (target != GL_TEXTURE_2D) michael@0: { michael@0: return gl::error(GL_INVALID_OPERATION, (Renderbuffer *)NULL); michael@0: } michael@0: michael@0: if (mColorbufferProxy == NULL) michael@0: { michael@0: mColorbufferProxy = new Renderbuffer(mRenderer, id(), new RenderbufferTexture2D(this, target)); michael@0: } michael@0: michael@0: return mColorbufferProxy; michael@0: } michael@0: michael@0: rx::RenderTarget *Texture2D::getRenderTarget(GLenum target) michael@0: { michael@0: ASSERT(target == GL_TEXTURE_2D); michael@0: michael@0: // ensure the underlying texture is created michael@0: if (getStorage(true) == NULL) michael@0: { michael@0: return NULL; michael@0: } michael@0: michael@0: updateTexture(); michael@0: michael@0: // ensure this is NOT a depth texture michael@0: if (isDepth(0)) michael@0: { michael@0: return NULL; michael@0: } michael@0: michael@0: return mTexStorage->getRenderTarget(); michael@0: } michael@0: michael@0: rx::RenderTarget *Texture2D::getDepthStencil(GLenum target) michael@0: { michael@0: ASSERT(target == GL_TEXTURE_2D); michael@0: michael@0: // ensure the underlying texture is created michael@0: if (getStorage(true) == NULL) michael@0: { michael@0: return NULL; michael@0: } michael@0: michael@0: updateTexture(); michael@0: michael@0: // ensure this is actually a depth texture michael@0: if (!isDepth(0)) michael@0: { michael@0: return NULL; michael@0: } michael@0: return mTexStorage->getRenderTarget(); michael@0: } michael@0: michael@0: int Texture2D::levelCount() michael@0: { michael@0: return mTexStorage ? mTexStorage->levelCount() : 0; michael@0: } michael@0: michael@0: rx::TextureStorageInterface *Texture2D::getStorage(bool renderTarget) michael@0: { michael@0: if (!mTexStorage || (renderTarget && !mTexStorage->isRenderTarget())) michael@0: { michael@0: if (renderTarget) michael@0: { michael@0: convertToRenderTarget(); michael@0: } michael@0: else michael@0: { michael@0: createTexture(); michael@0: } michael@0: } michael@0: michael@0: return mTexStorage; michael@0: } michael@0: michael@0: TextureCubeMap::TextureCubeMap(rx::Renderer *renderer, GLuint id) : Texture(renderer, id) michael@0: { michael@0: mTexStorage = NULL; michael@0: for (int i = 0; i < 6; i++) michael@0: { michael@0: mFaceProxies[i] = NULL; michael@0: mFaceProxyRefs[i] = 0; michael@0: michael@0: for (int j = 0; j < IMPLEMENTATION_MAX_TEXTURE_LEVELS; ++j) michael@0: { michael@0: mImageArray[i][j] = renderer->createImage(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: TextureCubeMap::~TextureCubeMap() michael@0: { michael@0: for (int i = 0; i < 6; i++) michael@0: { michael@0: mFaceProxies[i] = NULL; michael@0: michael@0: for (int j = 0; j < IMPLEMENTATION_MAX_TEXTURE_LEVELS; ++j) michael@0: { michael@0: delete mImageArray[i][j]; michael@0: } michael@0: } michael@0: michael@0: delete mTexStorage; michael@0: mTexStorage = NULL; michael@0: } michael@0: michael@0: // We need to maintain a count of references to renderbuffers acting as michael@0: // proxies for this texture, so that the texture is not deleted while michael@0: // proxy references still exist. If the reference count drops to zero, michael@0: // we set our proxy pointer NULL, so that a new attempt at referencing michael@0: // will cause recreation. michael@0: void TextureCubeMap::addProxyRef(const Renderbuffer *proxy) michael@0: { michael@0: for (int i = 0; i < 6; i++) michael@0: { michael@0: if (mFaceProxies[i] == proxy) michael@0: mFaceProxyRefs[i]++; michael@0: } michael@0: } michael@0: michael@0: void TextureCubeMap::releaseProxy(const Renderbuffer *proxy) michael@0: { michael@0: for (int i = 0; i < 6; i++) michael@0: { michael@0: if (mFaceProxies[i] == proxy) michael@0: { michael@0: if (mFaceProxyRefs[i] > 0) michael@0: mFaceProxyRefs[i]--; michael@0: michael@0: if (mFaceProxyRefs[i] == 0) michael@0: mFaceProxies[i] = NULL; michael@0: } michael@0: } michael@0: } michael@0: michael@0: GLenum TextureCubeMap::getTarget() const michael@0: { michael@0: return GL_TEXTURE_CUBE_MAP; michael@0: } michael@0: michael@0: GLsizei TextureCubeMap::getWidth(GLenum target, GLint level) const michael@0: { michael@0: if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS) michael@0: return mImageArray[faceIndex(target)][level]->getWidth(); michael@0: else michael@0: return 0; michael@0: } michael@0: michael@0: GLsizei TextureCubeMap::getHeight(GLenum target, GLint level) const michael@0: { michael@0: if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS) michael@0: return mImageArray[faceIndex(target)][level]->getHeight(); michael@0: else michael@0: return 0; michael@0: } michael@0: michael@0: GLenum TextureCubeMap::getInternalFormat(GLenum target, GLint level) const michael@0: { michael@0: if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS) michael@0: return mImageArray[faceIndex(target)][level]->getInternalFormat(); michael@0: else michael@0: return GL_NONE; michael@0: } michael@0: michael@0: GLenum TextureCubeMap::getActualFormat(GLenum target, GLint level) const michael@0: { michael@0: if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS) michael@0: return mImageArray[faceIndex(target)][level]->getActualFormat(); michael@0: else michael@0: return D3DFMT_UNKNOWN; michael@0: } michael@0: michael@0: void TextureCubeMap::setImagePosX(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels) michael@0: { michael@0: setImage(0, level, width, height, format, type, unpackAlignment, pixels); michael@0: } michael@0: michael@0: void TextureCubeMap::setImageNegX(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels) michael@0: { michael@0: setImage(1, level, width, height, format, type, unpackAlignment, pixels); michael@0: } michael@0: michael@0: void TextureCubeMap::setImagePosY(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels) michael@0: { michael@0: setImage(2, level, width, height, format, type, unpackAlignment, pixels); michael@0: } michael@0: michael@0: void TextureCubeMap::setImageNegY(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels) michael@0: { michael@0: setImage(3, level, width, height, format, type, unpackAlignment, pixels); michael@0: } michael@0: michael@0: void TextureCubeMap::setImagePosZ(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels) michael@0: { michael@0: setImage(4, level, width, height, format, type, unpackAlignment, pixels); michael@0: } michael@0: michael@0: void TextureCubeMap::setImageNegZ(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels) michael@0: { michael@0: setImage(5, level, width, height, format, type, unpackAlignment, pixels); michael@0: } michael@0: michael@0: void TextureCubeMap::setCompressedImage(GLenum face, GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels) michael@0: { michael@0: // compressed formats don't have separate sized internal formats-- we can just use the compressed format directly michael@0: redefineImage(faceIndex(face), level, format, width, height); michael@0: michael@0: Texture::setCompressedImage(imageSize, pixels, mImageArray[faceIndex(face)][level]); michael@0: } michael@0: michael@0: void TextureCubeMap::commitRect(int face, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height) michael@0: { michael@0: if (level < levelCount()) michael@0: { michael@0: rx::Image *image = mImageArray[face][level]; michael@0: if (image->updateSurface(mTexStorage, face, level, xoffset, yoffset, width, height)) michael@0: image->markClean(); michael@0: } michael@0: } michael@0: michael@0: void TextureCubeMap::subImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels) michael@0: { michael@0: if (Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, mImageArray[faceIndex(target)][level])) michael@0: { michael@0: commitRect(faceIndex(target), level, xoffset, yoffset, width, height); michael@0: } michael@0: } michael@0: michael@0: void TextureCubeMap::subImageCompressed(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels) michael@0: { michael@0: if (Texture::subImageCompressed(xoffset, yoffset, width, height, format, imageSize, pixels, mImageArray[faceIndex(target)][level])) michael@0: { michael@0: commitRect(faceIndex(target), level, xoffset, yoffset, width, height); michael@0: } michael@0: } michael@0: michael@0: // Tests for cube map sampling completeness. [OpenGL ES 2.0.24] section 3.8.2 page 86. michael@0: bool TextureCubeMap::isSamplerComplete() const michael@0: { michael@0: int size = mImageArray[0][0]->getWidth(); michael@0: michael@0: bool mipmapping = isMipmapFiltered(); michael@0: bool filtering, renderable; michael@0: michael@0: if ((gl::ExtractType(getInternalFormat(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0)) == GL_FLOAT && !mRenderer->getFloat32TextureSupport(&filtering, &renderable)) || michael@0: (gl::ExtractType(getInternalFormat(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0) == GL_HALF_FLOAT_OES) && !mRenderer->getFloat16TextureSupport(&filtering, &renderable))) michael@0: { michael@0: if (mSamplerState.magFilter != GL_NEAREST || michael@0: (mSamplerState.minFilter != GL_NEAREST && mSamplerState.minFilter != GL_NEAREST_MIPMAP_NEAREST)) michael@0: { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: if (!isPow2(size) && !mRenderer->getNonPower2TextureSupport()) michael@0: { michael@0: if (mSamplerState.wrapS != GL_CLAMP_TO_EDGE || mSamplerState.wrapT != GL_CLAMP_TO_EDGE || mipmapping) michael@0: { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: if (!mipmapping) michael@0: { michael@0: if (!isCubeComplete()) michael@0: { michael@0: return false; michael@0: } michael@0: } michael@0: else michael@0: { michael@0: if (!isMipmapCubeComplete()) // Also tests for isCubeComplete() michael@0: { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: // Tests for cube texture completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81. michael@0: bool TextureCubeMap::isCubeComplete() const michael@0: { michael@0: if (mImageArray[0][0]->getWidth() <= 0 || mImageArray[0][0]->getHeight() != mImageArray[0][0]->getWidth()) michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: for (unsigned int face = 1; face < 6; face++) michael@0: { michael@0: if (mImageArray[face][0]->getWidth() != mImageArray[0][0]->getWidth() || michael@0: mImageArray[face][0]->getWidth() != mImageArray[0][0]->getHeight() || michael@0: mImageArray[face][0]->getInternalFormat() != mImageArray[0][0]->getInternalFormat()) michael@0: { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool TextureCubeMap::isMipmapCubeComplete() const michael@0: { michael@0: if (isImmutable()) michael@0: { michael@0: return true; michael@0: } michael@0: michael@0: if (!isCubeComplete()) michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: GLsizei size = mImageArray[0][0]->getWidth(); michael@0: michael@0: int q = log2(size); michael@0: michael@0: for (int face = 0; face < 6; face++) michael@0: { michael@0: for (int level = 1; level <= q; level++) michael@0: { michael@0: if (mImageArray[face][level]->getInternalFormat() != mImageArray[0][0]->getInternalFormat()) michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: if (mImageArray[face][level]->getWidth() != std::max(1, size >> level)) michael@0: { michael@0: return false; michael@0: } michael@0: } michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool TextureCubeMap::isCompressed(GLenum target, GLint level) const michael@0: { michael@0: return IsCompressed(getInternalFormat(target, level)); michael@0: } michael@0: michael@0: // Constructs a native texture resource from the texture images, or returns an existing one michael@0: void TextureCubeMap::createTexture() michael@0: { michael@0: GLsizei size = mImageArray[0][0]->getWidth(); michael@0: michael@0: if (!(size > 0)) michael@0: return; // do not attempt to create native textures for nonexistant data michael@0: michael@0: GLint levels = creationLevels(size); michael@0: GLenum internalformat = mImageArray[0][0]->getInternalFormat(); michael@0: michael@0: delete mTexStorage; michael@0: mTexStorage = new rx::TextureStorageInterfaceCube(mRenderer, levels, internalformat, mUsage, false, size); michael@0: michael@0: if (mTexStorage->isManaged()) michael@0: { michael@0: int levels = levelCount(); michael@0: michael@0: for (int face = 0; face < 6; face++) michael@0: { michael@0: for (int level = 0; level < levels; level++) michael@0: { michael@0: mImageArray[face][level]->setManagedSurface(mTexStorage, face, level); michael@0: } michael@0: } michael@0: } michael@0: michael@0: mDirtyImages = true; michael@0: } michael@0: michael@0: void TextureCubeMap::updateTexture() michael@0: { michael@0: bool mipmapping = isMipmapFiltered() && isMipmapCubeComplete(); michael@0: michael@0: for (int face = 0; face < 6; face++) michael@0: { michael@0: int levels = (mipmapping ? levelCount() : 1); michael@0: michael@0: for (int level = 0; level < levels; level++) michael@0: { michael@0: rx::Image *image = mImageArray[face][level]; michael@0: michael@0: if (image->isDirty()) michael@0: { michael@0: commitRect(face, level, 0, 0, image->getWidth(), image->getHeight()); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: void TextureCubeMap::convertToRenderTarget() michael@0: { michael@0: rx::TextureStorageInterfaceCube *newTexStorage = NULL; michael@0: michael@0: if (mImageArray[0][0]->getWidth() != 0) michael@0: { michael@0: GLsizei size = mImageArray[0][0]->getWidth(); michael@0: GLint levels = mTexStorage != NULL ? mTexStorage->levelCount() : creationLevels(size); michael@0: GLenum internalformat = mImageArray[0][0]->getInternalFormat(); michael@0: michael@0: newTexStorage = new rx::TextureStorageInterfaceCube(mRenderer, levels, internalformat, GL_FRAMEBUFFER_ATTACHMENT_ANGLE, true, size); michael@0: michael@0: if (mTexStorage != NULL) michael@0: { michael@0: if (!mRenderer->copyToRenderTarget(newTexStorage, mTexStorage)) michael@0: { michael@0: delete newTexStorage; michael@0: return gl::error(GL_OUT_OF_MEMORY); michael@0: } michael@0: } michael@0: } michael@0: michael@0: delete mTexStorage; michael@0: mTexStorage = newTexStorage; michael@0: michael@0: mDirtyImages = true; michael@0: } michael@0: michael@0: void TextureCubeMap::setImage(int faceIndex, GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels) michael@0: { michael@0: GLint internalformat = ConvertSizedInternalFormat(format, type); michael@0: redefineImage(faceIndex, level, internalformat, width, height); michael@0: michael@0: Texture::setImage(unpackAlignment, pixels, mImageArray[faceIndex][level]); michael@0: } michael@0: michael@0: unsigned int TextureCubeMap::faceIndex(GLenum face) michael@0: { michael@0: META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_X - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 1); michael@0: META_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_Y - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 2); michael@0: META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 3); michael@0: META_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_Z - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 4); michael@0: META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 5); michael@0: michael@0: return face - GL_TEXTURE_CUBE_MAP_POSITIVE_X; michael@0: } michael@0: michael@0: void TextureCubeMap::redefineImage(int face, GLint level, GLint internalformat, GLsizei width, GLsizei height) michael@0: { michael@0: // If there currently is a corresponding storage texture image, it has these parameters michael@0: const int storageWidth = std::max(1, mImageArray[0][0]->getWidth() >> level); michael@0: const int storageHeight = std::max(1, mImageArray[0][0]->getHeight() >> level); michael@0: const int storageFormat = mImageArray[0][0]->getInternalFormat(); michael@0: michael@0: mImageArray[face][level]->redefine(mRenderer, internalformat, width, height, false); michael@0: michael@0: if (mTexStorage) michael@0: { michael@0: const int storageLevels = mTexStorage->levelCount(); michael@0: michael@0: if ((level >= storageLevels && storageLevels != 0) || michael@0: width != storageWidth || michael@0: height != storageHeight || michael@0: internalformat != storageFormat) // Discard mismatched storage michael@0: { michael@0: for (int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++) michael@0: { michael@0: for (int f = 0; f < 6; f++) michael@0: { michael@0: mImageArray[f][i]->markDirty(); michael@0: } michael@0: } michael@0: michael@0: delete mTexStorage; michael@0: mTexStorage = NULL; michael@0: michael@0: mDirtyImages = true; michael@0: } michael@0: } michael@0: } michael@0: michael@0: void TextureCubeMap::copyImage(GLenum target, GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source) michael@0: { michael@0: unsigned int faceindex = faceIndex(target); michael@0: GLint internalformat = gl::ConvertSizedInternalFormat(format, GL_UNSIGNED_BYTE); michael@0: redefineImage(faceindex, level, internalformat, width, height); michael@0: michael@0: if (!mImageArray[faceindex][level]->isRenderableFormat()) michael@0: { michael@0: mImageArray[faceindex][level]->copy(0, 0, x, y, width, height, source); michael@0: mDirtyImages = true; michael@0: } michael@0: else michael@0: { michael@0: if (!mTexStorage || !mTexStorage->isRenderTarget()) michael@0: { michael@0: convertToRenderTarget(); michael@0: } michael@0: michael@0: mImageArray[faceindex][level]->markClean(); michael@0: michael@0: ASSERT(width == height); michael@0: michael@0: if (width > 0 && level < levelCount()) michael@0: { michael@0: gl::Rectangle sourceRect; michael@0: sourceRect.x = x; michael@0: sourceRect.width = width; michael@0: sourceRect.y = y; michael@0: sourceRect.height = height; michael@0: michael@0: mRenderer->copyImage(source, sourceRect, format, 0, 0, mTexStorage, target, level); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void TextureCubeMap::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source) michael@0: { michael@0: GLsizei size = mImageArray[faceIndex(target)][level]->getWidth(); michael@0: michael@0: if (xoffset + width > size || yoffset + height > size) michael@0: { michael@0: return gl::error(GL_INVALID_VALUE); michael@0: } michael@0: michael@0: unsigned int faceindex = faceIndex(target); michael@0: michael@0: if (!mImageArray[faceindex][level]->isRenderableFormat() || (!mTexStorage && !isSamplerComplete())) michael@0: { michael@0: mImageArray[faceindex][level]->copy(0, 0, x, y, width, height, source); michael@0: mDirtyImages = true; michael@0: } michael@0: else michael@0: { michael@0: if (!mTexStorage || !mTexStorage->isRenderTarget()) michael@0: { michael@0: convertToRenderTarget(); michael@0: } michael@0: michael@0: updateTexture(); michael@0: michael@0: if (level < levelCount()) michael@0: { michael@0: gl::Rectangle sourceRect; michael@0: sourceRect.x = x; michael@0: sourceRect.width = width; michael@0: sourceRect.y = y; michael@0: sourceRect.height = height; michael@0: michael@0: mRenderer->copyImage(source, sourceRect, gl::ExtractFormat(mImageArray[0][0]->getInternalFormat()), michael@0: xoffset, yoffset, mTexStorage, target, level); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void TextureCubeMap::storage(GLsizei levels, GLenum internalformat, GLsizei size) michael@0: { michael@0: delete mTexStorage; michael@0: mTexStorage = new rx::TextureStorageInterfaceCube(mRenderer, levels, internalformat, mUsage, false, size); michael@0: mImmutable = true; michael@0: michael@0: for (int level = 0; level < levels; level++) michael@0: { michael@0: for (int face = 0; face < 6; face++) michael@0: { michael@0: mImageArray[face][level]->redefine(mRenderer, internalformat, size, size, true); michael@0: size = std::max(1, size >> 1); michael@0: } michael@0: } michael@0: michael@0: for (int level = levels; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++) michael@0: { michael@0: for (int face = 0; face < 6; face++) michael@0: { michael@0: mImageArray[face][level]->redefine(mRenderer, GL_NONE, 0, 0, true); michael@0: } michael@0: } michael@0: michael@0: if (mTexStorage->isManaged()) michael@0: { michael@0: int levels = levelCount(); michael@0: michael@0: for (int face = 0; face < 6; face++) michael@0: { michael@0: for (int level = 0; level < levels; level++) michael@0: { michael@0: mImageArray[face][level]->setManagedSurface(mTexStorage, face, level); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: void TextureCubeMap::generateMipmaps() michael@0: { michael@0: if (!isCubeComplete()) michael@0: { michael@0: return gl::error(GL_INVALID_OPERATION); michael@0: } michael@0: michael@0: if (!mRenderer->getNonPower2TextureSupport()) michael@0: { michael@0: if (!isPow2(mImageArray[0][0]->getWidth())) michael@0: { michael@0: return gl::error(GL_INVALID_OPERATION); michael@0: } michael@0: } michael@0: michael@0: // Purge array levels 1 through q and reset them to represent the generated mipmap levels. michael@0: unsigned int q = log2(mImageArray[0][0]->getWidth()); michael@0: for (unsigned int f = 0; f < 6; f++) michael@0: { michael@0: for (unsigned int i = 1; i <= q; i++) michael@0: { michael@0: redefineImage(f, i, mImageArray[f][0]->getInternalFormat(), michael@0: std::max(mImageArray[f][0]->getWidth() >> i, 1), michael@0: std::max(mImageArray[f][0]->getWidth() >> i, 1)); michael@0: } michael@0: } michael@0: michael@0: if (mTexStorage && mTexStorage->isRenderTarget()) michael@0: { michael@0: for (unsigned int f = 0; f < 6; f++) michael@0: { michael@0: for (unsigned int i = 1; i <= q; i++) michael@0: { michael@0: mTexStorage->generateMipmap(f, i); michael@0: michael@0: mImageArray[f][i]->markClean(); michael@0: } michael@0: } michael@0: } michael@0: else michael@0: { michael@0: for (unsigned int f = 0; f < 6; f++) michael@0: { michael@0: for (unsigned int i = 1; i <= q; i++) michael@0: { michael@0: mRenderer->generateMipmap(mImageArray[f][i], mImageArray[f][i - 1]); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: Renderbuffer *TextureCubeMap::getRenderbuffer(GLenum target) michael@0: { michael@0: if (!IsCubemapTextureTarget(target)) michael@0: { michael@0: return gl::error(GL_INVALID_OPERATION, (Renderbuffer *)NULL); michael@0: } michael@0: michael@0: unsigned int face = faceIndex(target); michael@0: michael@0: if (mFaceProxies[face] == NULL) michael@0: { michael@0: mFaceProxies[face] = new Renderbuffer(mRenderer, id(), new RenderbufferTextureCubeMap(this, target)); michael@0: } michael@0: michael@0: return mFaceProxies[face]; michael@0: } michael@0: michael@0: rx::RenderTarget *TextureCubeMap::getRenderTarget(GLenum target) michael@0: { michael@0: ASSERT(IsCubemapTextureTarget(target)); michael@0: michael@0: // ensure the underlying texture is created michael@0: if (getStorage(true) == NULL) michael@0: { michael@0: return NULL; michael@0: } michael@0: michael@0: updateTexture(); michael@0: michael@0: return mTexStorage->getRenderTarget(target); michael@0: } michael@0: michael@0: int TextureCubeMap::levelCount() michael@0: { michael@0: return mTexStorage ? mTexStorage->levelCount() - getLodOffset() : 0; michael@0: } michael@0: michael@0: rx::TextureStorageInterface *TextureCubeMap::getStorage(bool renderTarget) michael@0: { michael@0: if (!mTexStorage || (renderTarget && !mTexStorage->isRenderTarget())) michael@0: { michael@0: if (renderTarget) michael@0: { michael@0: convertToRenderTarget(); michael@0: } michael@0: else michael@0: { michael@0: createTexture(); michael@0: } michael@0: } michael@0: michael@0: return mTexStorage; michael@0: } michael@0: michael@0: }