diff options
Diffstat (limited to 'chromium/third_party/angle/src/libGLESv2/Texture.cpp')
-rw-r--r-- | chromium/third_party/angle/src/libGLESv2/Texture.cpp | 2460 |
1 files changed, 1892 insertions, 568 deletions
diff --git a/chromium/third_party/angle/src/libGLESv2/Texture.cpp b/chromium/third_party/angle/src/libGLESv2/Texture.cpp index cee9c57dacf..baa5bc68a96 100644 --- a/chromium/third_party/angle/src/libGLESv2/Texture.cpp +++ b/chromium/third_party/angle/src/libGLESv2/Texture.cpp @@ -12,19 +12,44 @@ #include "libGLESv2/Texture.h" #include "libGLESv2/main.h" -#include "libGLESv2/mathutil.h" -#include "libGLESv2/utilities.h" -#include "libGLESv2/renderer/Blit.h" +#include "common/mathutil.h" +#include "common/utilities.h" +#include "libGLESv2/formatutils.h" #include "libGLESv2/Renderbuffer.h" #include "libGLESv2/renderer/Image.h" #include "libGLESv2/renderer/Renderer.h" #include "libGLESv2/renderer/TextureStorage.h" #include "libEGL/Surface.h" +#include "libGLESv2/Buffer.h" +#include "libGLESv2/renderer/BufferStorage.h" +#include "libGLESv2/renderer/RenderTarget.h" namespace gl { -Texture::Texture(rx::Renderer *renderer, GLuint id) : RefCountObject(id) +bool IsMipmapFiltered(const SamplerState &samplerState) +{ + switch (samplerState.minFilter) + { + case GL_NEAREST: + case GL_LINEAR: + return false; + case GL_NEAREST_MIPMAP_NEAREST: + case GL_LINEAR_MIPMAP_NEAREST: + case GL_NEAREST_MIPMAP_LINEAR: + case GL_LINEAR_MIPMAP_LINEAR: + return true; + default: UNREACHABLE(); + return false; + } +} + +bool IsRenderTargetUsage(GLenum usage) +{ + return (usage == GL_FRAMEBUFFER_ATTACHMENT_ANGLE); +} + +Texture::Texture(rx::Renderer *renderer, GLuint id, GLenum target) : RefCountObject(id) { mRenderer = renderer; @@ -32,107 +57,129 @@ Texture::Texture(rx::Renderer *renderer, GLuint id) : RefCountObject(id) mSamplerState.magFilter = GL_LINEAR; mSamplerState.wrapS = GL_REPEAT; mSamplerState.wrapT = GL_REPEAT; + mSamplerState.wrapR = GL_REPEAT; mSamplerState.maxAnisotropy = 1.0f; - mSamplerState.lodOffset = 0; + mSamplerState.baseLevel = 0; + mSamplerState.maxLevel = 1000; + mSamplerState.minLod = -1000.0f; + mSamplerState.maxLod = 1000.0f; + mSamplerState.compareMode = GL_NONE; + mSamplerState.compareFunc = GL_LEQUAL; + mSamplerState.swizzleRed = GL_RED; + mSamplerState.swizzleGreen = GL_GREEN; + mSamplerState.swizzleBlue = GL_BLUE; + mSamplerState.swizzleAlpha = GL_ALPHA; mUsage = GL_NONE; - + mDirtyImages = true; mImmutable = false; + + mTarget = target; } Texture::~Texture() { } -// Returns true on successful filter state update (valid enum parameter) -bool Texture::setMinFilter(GLenum filter) +GLenum Texture::getTarget() const { - switch (filter) - { - case GL_NEAREST: - case GL_LINEAR: - case GL_NEAREST_MIPMAP_NEAREST: - case GL_LINEAR_MIPMAP_NEAREST: - case GL_NEAREST_MIPMAP_LINEAR: - case GL_LINEAR_MIPMAP_LINEAR: - mSamplerState.minFilter = filter; - return true; - default: - return false; - } + return mTarget; } -// Returns true on successful filter state update (valid enum parameter) -bool Texture::setMagFilter(GLenum filter) +void Texture::addProxyRef(const FramebufferAttachment *proxy) { - switch (filter) - { - case GL_NEAREST: - case GL_LINEAR: - mSamplerState.magFilter = filter; - return true; - default: - return false; - } + mRenderbufferProxies.addRef(proxy); } -// Returns true on successful wrap state update (valid enum parameter) -bool Texture::setWrapS(GLenum wrap) +void Texture::releaseProxy(const FramebufferAttachment *proxy) { - switch (wrap) - { - case GL_REPEAT: - case GL_CLAMP_TO_EDGE: - case GL_MIRRORED_REPEAT: - mSamplerState.wrapS = wrap; - return true; - default: - return false; - } + mRenderbufferProxies.release(proxy); } -// Returns true on successful wrap state update (valid enum parameter) -bool Texture::setWrapT(GLenum wrap) +void Texture::setMinFilter(GLenum filter) { - switch (wrap) - { - case GL_REPEAT: - case GL_CLAMP_TO_EDGE: - case GL_MIRRORED_REPEAT: - mSamplerState.wrapT = wrap; - return true; - default: - return false; - } + mSamplerState.minFilter = filter; } -// Returns true on successful max anisotropy update (valid anisotropy value) -bool Texture::setMaxAnisotropy(float textureMaxAnisotropy, float contextMaxAnisotropy) +void Texture::setMagFilter(GLenum filter) { - textureMaxAnisotropy = std::min(textureMaxAnisotropy, contextMaxAnisotropy); - if (textureMaxAnisotropy < 1.0f) - { - return false; - } + mSamplerState.magFilter = filter; +} - mSamplerState.maxAnisotropy = textureMaxAnisotropy; +void Texture::setWrapS(GLenum wrap) +{ + mSamplerState.wrapS = wrap; +} - return true; +void Texture::setWrapT(GLenum wrap) +{ + mSamplerState.wrapT = wrap; } -// Returns true on successful usage state update (valid enum parameter) -bool Texture::setUsage(GLenum usage) +void Texture::setWrapR(GLenum wrap) { - switch (usage) - { - case GL_NONE: - case GL_FRAMEBUFFER_ATTACHMENT_ANGLE: - mUsage = usage; - return true; - default: - return false; - } + mSamplerState.wrapR = wrap; +} + +void Texture::setMaxAnisotropy(float textureMaxAnisotropy, float contextMaxAnisotropy) +{ + mSamplerState.maxAnisotropy = std::min(textureMaxAnisotropy, contextMaxAnisotropy); +} + +void Texture::setCompareMode(GLenum mode) +{ + mSamplerState.compareMode = mode; +} + +void Texture::setCompareFunc(GLenum func) +{ + mSamplerState.compareFunc = func; +} + +void Texture::setSwizzleRed(GLenum swizzle) +{ + mSamplerState.swizzleRed = swizzle; +} + +void Texture::setSwizzleGreen(GLenum swizzle) +{ + mSamplerState.swizzleGreen = swizzle; +} + +void Texture::setSwizzleBlue(GLenum swizzle) +{ + mSamplerState.swizzleBlue = swizzle; +} + +void Texture::setSwizzleAlpha(GLenum swizzle) +{ + mSamplerState.swizzleAlpha = swizzle; +} + +void Texture::setBaseLevel(GLint baseLevel) +{ + mSamplerState.baseLevel = baseLevel; +} + +void Texture::setMaxLevel(GLint maxLevel) +{ + mSamplerState.maxLevel = maxLevel; +} + +void Texture::setMinLod(GLfloat minLod) +{ + mSamplerState.minLod = minLod; +} + +void Texture::setMaxLod(GLfloat maxLod) +{ + mSamplerState.maxLod = maxLod; +} + +void Texture::setUsage(GLenum usage) +{ + mUsage = usage; } GLenum Texture::getMinFilter() const @@ -155,21 +202,72 @@ GLenum Texture::getWrapT() const return mSamplerState.wrapT; } +GLenum Texture::getWrapR() const +{ + return mSamplerState.wrapR; +} + float Texture::getMaxAnisotropy() const { return mSamplerState.maxAnisotropy; } -int Texture::getLodOffset() +GLenum Texture::getSwizzleRed() const +{ + return mSamplerState.swizzleRed; +} + +GLenum Texture::getSwizzleGreen() const +{ + return mSamplerState.swizzleGreen; +} + +GLenum Texture::getSwizzleBlue() const +{ + return mSamplerState.swizzleBlue; +} + +GLenum Texture::getSwizzleAlpha() const { - rx::TextureStorageInterface *texture = getStorage(false); - return texture ? texture->getLodOffset() : 0; + return mSamplerState.swizzleAlpha; +} + +GLint Texture::getBaseLevel() const +{ + return mSamplerState.baseLevel; +} + +GLint Texture::getMaxLevel() const +{ + return mSamplerState.maxLevel; +} + +GLfloat Texture::getMinLod() const +{ + return mSamplerState.minLod; +} + +GLfloat Texture::getMaxLod() const +{ + return mSamplerState.maxLod; +} + +bool Texture::isSwizzled() const +{ + return mSamplerState.swizzleRed != GL_RED || + mSamplerState.swizzleGreen != GL_GREEN || + mSamplerState.swizzleBlue != GL_BLUE || + mSamplerState.swizzleAlpha != GL_ALPHA; } void Texture::getSamplerState(SamplerState *sampler) { *sampler = mSamplerState; - sampler->lodOffset = getLodOffset(); + + // Offset the effective base level by the texture storage's top level + rx::TextureStorageInterface *texture = getNativeTexture(); + int topLevel = texture ? texture->getTopLevel() : 0; + sampler->baseLevel = topLevel + mSamplerState.baseLevel; } GLenum Texture::getUsage() const @@ -177,57 +275,121 @@ GLenum Texture::getUsage() const return mUsage; } -bool Texture::isMipmapFiltered() const +GLint Texture::getBaseLevelWidth() const { - switch (mSamplerState.minFilter) + const rx::Image *baseImage = getBaseLevelImage(); + return (baseImage ? baseImage->getWidth() : 0); +} + +GLint Texture::getBaseLevelHeight() const +{ + const rx::Image *baseImage = getBaseLevelImage(); + return (baseImage ? baseImage->getHeight() : 0); +} + +GLint Texture::getBaseLevelDepth() const +{ + const rx::Image *baseImage = getBaseLevelImage(); + return (baseImage ? baseImage->getDepth() : 0); +} + +// Note: "base level image" is loosely defined to be any image from the base level, +// where in the base of 2D array textures and cube maps there are several. Don't use +// the base level image for anything except querying texture format and size. +GLenum Texture::getBaseLevelInternalFormat() const +{ + const rx::Image *baseImage = getBaseLevelImage(); + return (baseImage ? baseImage->getInternalFormat() : GL_NONE); +} + +void Texture::setImage(const PixelUnpackState &unpack, GLenum type, const void *pixels, rx::Image *image) +{ + // No-op + if (image->getWidth() == 0 || image->getHeight() == 0 || image->getDepth() == 0) { - case GL_NEAREST: - case GL_LINEAR: - return false; - case GL_NEAREST_MIPMAP_NEAREST: - case GL_LINEAR_MIPMAP_NEAREST: - case GL_NEAREST_MIPMAP_LINEAR: - case GL_LINEAR_MIPMAP_LINEAR: - return true; - default: UNREACHABLE(); - return false; + return; + } + + // We no longer need the "GLenum format" parameter to TexImage to determine what data format "pixels" contains. + // From our image internal format we know how many channels to expect, and "type" gives the format of pixel's components. + const void *pixelData = pixels; + + if (unpack.pixelBuffer.id() != 0) + { + // Do a CPU readback here, if we have an unpack buffer bound and the fast GPU path is not supported + Buffer *pixelBuffer = unpack.pixelBuffer.get(); + ptrdiff_t offset = reinterpret_cast<ptrdiff_t>(pixels); + const void *bufferData = pixelBuffer->getStorage()->getData(); + pixelData = static_cast<const unsigned char *>(bufferData) + offset; + } + + if (pixelData != NULL) + { + image->loadData(0, 0, 0, image->getWidth(), image->getHeight(), image->getDepth(), unpack.alignment, type, pixelData); + mDirtyImages = true; } } -void Texture::setImage(GLint unpackAlignment, const void *pixels, rx::Image *image) +bool Texture::isFastUnpackable(const PixelUnpackState &unpack, GLenum sizedInternalFormat) { - if (pixels != NULL) + return unpack.pixelBuffer.id() != 0 && mRenderer->supportsFastCopyBufferToTexture(sizedInternalFormat); +} + +bool Texture::fastUnpackPixels(const PixelUnpackState &unpack, const void *pixels, const Box &destArea, + GLenum sizedInternalFormat, GLenum type, rx::RenderTarget *destRenderTarget) +{ + if (destArea.width <= 0 && destArea.height <= 0 && destArea.depth <= 0) { - image->loadData(0, 0, image->getWidth(), image->getHeight(), unpackAlignment, pixels); - mDirtyImages = true; + return true; } + + // In order to perform the fast copy through the shader, we must have the right format, and be able + // to create a render target. + ASSERT(mRenderer->supportsFastCopyBufferToTexture(sizedInternalFormat)); + + unsigned int offset = reinterpret_cast<unsigned int>(pixels); + + return mRenderer->fastCopyBufferToTexture(unpack, offset, destRenderTarget, sizedInternalFormat, type, destArea); } void Texture::setCompressedImage(GLsizei imageSize, const void *pixels, rx::Image *image) { if (pixels != NULL) { - image->loadCompressedData(0, 0, image->getWidth(), image->getHeight(), pixels); + image->loadCompressedData(0, 0, 0, image->getWidth(), image->getHeight(), image->getDepth(), pixels); mDirtyImages = true; } } -bool Texture::subImage(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels, rx::Image *image) +bool Texture::subImage(GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, + GLenum format, GLenum type, const PixelUnpackState &unpack, const void *pixels, rx::Image *image) { - if (pixels != NULL) + const void *pixelData = pixels; + + // CPU readback & copy where direct GPU copy is not supported + if (unpack.pixelBuffer.id() != 0) { - image->loadData(xoffset, yoffset, width, height, unpackAlignment, pixels); + Buffer *pixelBuffer = unpack.pixelBuffer.get(); + unsigned int offset = reinterpret_cast<unsigned int>(pixels); + const void *bufferData = pixelBuffer->getStorage()->getData(); + pixelData = static_cast<const unsigned char *>(bufferData) + offset; + } + + if (pixelData != NULL) + { + image->loadData(xoffset, yoffset, zoffset, width, height, depth, unpack.alignment, type, pixelData); mDirtyImages = true; } return true; } -bool Texture::subImageCompressed(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels, rx::Image *image) +bool Texture::subImageCompressed(GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, + GLenum format, GLsizei imageSize, const void *pixels, rx::Image *image) { if (pixels != NULL) { - image->loadCompressedData(xoffset, yoffset, width, height, pixels); + image->loadCompressedData(xoffset, yoffset, zoffset, width, height, depth, pixels); mDirtyImages = true; } @@ -237,11 +399,12 @@ bool Texture::subImageCompressed(GLint xoffset, GLint yoffset, GLsizei width, GL rx::TextureStorageInterface *Texture::getNativeTexture() { // ensure the underlying texture is created + initializeStorage(false); - rx::TextureStorageInterface *storage = getStorage(false); + rx::TextureStorageInterface *storage = getBaseLevelStorage(); if (storage) { - updateTexture(); + updateStorage(); } return storage; @@ -259,26 +422,26 @@ void Texture::resetDirty() unsigned int Texture::getTextureSerial() { - rx::TextureStorageInterface *texture = getStorage(false); + rx::TextureStorageInterface *texture = getNativeTexture(); return texture ? texture->getTextureSerial() : 0; } -unsigned int Texture::getRenderTargetSerial(GLenum target) +bool Texture::isImmutable() const { - rx::TextureStorageInterface *texture = getStorage(true); - return texture ? texture->getRenderTargetSerial(target) : 0; + return mImmutable; } -bool Texture::isImmutable() const +int Texture::immutableLevelCount() { - return mImmutable; + return (mImmutable ? getNativeTexture()->getStorageInstance()->getLevelCount() : 0); } -GLint Texture::creationLevels(GLsizei width, GLsizei height) const +GLint Texture::creationLevels(GLsizei width, GLsizei height, GLsizei depth) const { - if ((isPow2(width) && isPow2(height)) || mRenderer->getNonPower2TextureSupport()) + if ((isPow2(width) && isPow2(height) && isPow2(depth)) || mRenderer->getNonPower2TextureSupport()) { - return 0; // Maximum number of levels + // Maximum number of levels + return log2(std::max(std::max(width, height), depth)) + 1; } else { @@ -287,17 +450,15 @@ GLint Texture::creationLevels(GLsizei width, GLsizei height) const } } -GLint Texture::creationLevels(GLsizei size) const +int Texture::mipLevels() const { - return creationLevels(size, size); + return log2(std::max(std::max(getBaseLevelWidth(), getBaseLevelHeight()), getBaseLevelDepth())) + 1; } -Texture2D::Texture2D(rx::Renderer *renderer, GLuint id) : Texture(renderer, id) +Texture2D::Texture2D(rx::Renderer *renderer, GLuint id) : Texture(renderer, id, GL_TEXTURE_2D) { mTexStorage = NULL; mSurface = NULL; - mColorbufferProxy = NULL; - mProxyRefs = 0; for (int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; ++i) { @@ -307,8 +468,6 @@ Texture2D::Texture2D(rx::Renderer *renderer, GLuint id) : Texture(renderer, id) Texture2D::~Texture2D() { - mColorbufferProxy = NULL; - delete mTexStorage; mTexStorage = NULL; @@ -324,28 +483,6 @@ Texture2D::~Texture2D() } } -// We need to maintain a count of references to renderbuffers acting as -// proxies for this texture, so that we do not attempt to use a pointer -// to a renderbuffer proxy which has been deleted. -void Texture2D::addProxyRef(const Renderbuffer *proxy) -{ - mProxyRefs++; -} - -void Texture2D::releaseProxy(const Renderbuffer *proxy) -{ - if (mProxyRefs > 0) - mProxyRefs--; - - if (mProxyRefs == 0) - mColorbufferProxy = NULL; -} - -GLenum Texture2D::getTarget() const -{ - return GL_TEXTURE_2D; -} - GLsizei Texture2D::getWidth(GLint level) const { if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS) @@ -375,24 +512,24 @@ GLenum Texture2D::getActualFormat(GLint level) const if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS) return mImageArray[level]->getActualFormat(); else - return D3DFMT_UNKNOWN; + return GL_NONE; } -void Texture2D::redefineImage(GLint level, GLint internalformat, GLsizei width, GLsizei height) +void Texture2D::redefineImage(GLint level, GLenum internalformat, GLsizei width, GLsizei height) { releaseTexImage(); // If there currently is a corresponding storage texture image, it has these parameters - const int storageWidth = std::max(1, mImageArray[0]->getWidth() >> level); - const int storageHeight = std::max(1, mImageArray[0]->getHeight() >> level); - const int storageFormat = mImageArray[0]->getInternalFormat(); + const int storageWidth = std::max(1, getBaseLevelWidth() >> level); + const int storageHeight = std::max(1, getBaseLevelHeight() >> level); + const GLenum storageFormat = getBaseLevelInternalFormat(); - mImageArray[level]->redefine(mRenderer, internalformat, width, height, false); + mImageArray[level]->redefine(mRenderer, GL_TEXTURE_2D, internalformat, width, height, 1, false); if (mTexStorage) { - const int storageLevels = mTexStorage->levelCount(); - + const int storageLevels = mTexStorage->getLevelCount(); + if ((level >= storageLevels && storageLevels != 0) || width != storageWidth || height != storageHeight || @@ -410,21 +547,44 @@ void Texture2D::redefineImage(GLint level, GLint internalformat, GLsizei width, } } -void Texture2D::setImage(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels) +void Texture2D::setImage(GLint level, GLsizei width, GLsizei height, GLenum internalFormat, GLenum format, GLenum type, const PixelUnpackState &unpack, const void *pixels) { - GLint internalformat = ConvertSizedInternalFormat(format, type); - redefineImage(level, internalformat, width, height); + GLuint clientVersion = mRenderer->getCurrentClientVersion(); + GLenum sizedInternalFormat = IsSizedInternalFormat(internalFormat, clientVersion) ? internalFormat + : GetSizedInternalFormat(format, type, clientVersion); + redefineImage(level, sizedInternalFormat, width, height); + + bool fastUnpacked = false; - Texture::setImage(unpackAlignment, pixels, mImageArray[level]); + // Attempt a fast gpu copy of the pixel data to the surface + if (isFastUnpackable(unpack, sizedInternalFormat) && isLevelComplete(level)) + { + // Will try to create RT storage if it does not exist + rx::RenderTarget *destRenderTarget = getRenderTarget(level); + Box destArea(0, 0, 0, getWidth(level), getHeight(level), 1); + + if (destRenderTarget && fastUnpackPixels(unpack, pixels, destArea, sizedInternalFormat, type, destRenderTarget)) + { + // Ensure we don't overwrite our newly initialized data + mImageArray[level]->markClean(); + + fastUnpacked = true; + } + } + + if (!fastUnpacked) + { + Texture::setImage(unpack, type, pixels, mImageArray[level]); + } } void Texture2D::bindTexImage(egl::Surface *surface) { releaseTexImage(); - GLint internalformat = surface->getFormat(); + GLenum internalformat = surface->getFormat(); - mImageArray[0]->redefine(mRenderer, internalformat, surface->getWidth(), surface->getHeight(), true); + mImageArray[0]->redefine(mRenderer, GL_TEXTURE_2D, internalformat, surface->getWidth(), surface->getHeight(), 1, true); delete mTexStorage; mTexStorage = new rx::TextureStorageInterface2D(mRenderer, surface->getSwapChain()); @@ -449,7 +609,7 @@ void Texture2D::releaseTexImage() for (int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++) { - mImageArray[i]->redefine(mRenderer, GL_NONE, 0, 0, true); + mImageArray[i]->redefine(mRenderer, GL_TEXTURE_2D, GL_NONE, 0, 0, 0, true); } } } @@ -464,19 +624,35 @@ void Texture2D::setCompressedImage(GLint level, GLenum format, GLsizei width, GL void Texture2D::commitRect(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height) { - if (level < levelCount()) + if (isValidLevel(level)) { rx::Image *image = mImageArray[level]; - if (image->updateSurface(mTexStorage, level, xoffset, yoffset, width, height)) + if (image->copyToStorage(mTexStorage, level, xoffset, yoffset, width, height)) { image->markClean(); } } } -void Texture2D::subImage(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels) +void Texture2D::subImage(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const PixelUnpackState &unpack, const void *pixels) { - if (Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, mImageArray[level])) + bool fastUnpacked = false; + + if (isFastUnpackable(unpack, getInternalFormat(level)) && isLevelComplete(level)) + { + rx::RenderTarget *renderTarget = getRenderTarget(level); + Box destArea(xoffset, yoffset, 0, width, height, 1); + + if (renderTarget && fastUnpackPixels(unpack, pixels, destArea, getInternalFormat(level), type, renderTarget)) + { + // Ensure we don't overwrite our newly initialized data + mImageArray[level]->markClean(); + + fastUnpacked = true; + } + } + + if (!fastUnpacked && Texture::subImage(xoffset, yoffset, 0, width, height, 1, format, type, unpack, pixels, mImageArray[level])) { commitRect(level, xoffset, yoffset, width, height); } @@ -484,7 +660,7 @@ void Texture2D::subImage(GLint level, GLint xoffset, GLint yoffset, GLsizei widt void Texture2D::subImageCompressed(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels) { - if (Texture::subImageCompressed(xoffset, yoffset, width, height, format, imageSize, pixels, mImageArray[level])) + if (Texture::subImageCompressed(xoffset, yoffset, 0, width, height, 1, format, imageSize, pixels, mImageArray[level])) { commitRect(level, xoffset, yoffset, width, height); } @@ -492,24 +668,22 @@ void Texture2D::subImageCompressed(GLint level, GLint xoffset, GLint yoffset, GL void Texture2D::copyImage(GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source) { - GLint internalformat = ConvertSizedInternalFormat(format, GL_UNSIGNED_BYTE); - redefineImage(level, internalformat, width, height); + GLuint clientVersion = mRenderer->getCurrentClientVersion(); + GLenum sizedInternalFormat = IsSizedInternalFormat(format, clientVersion) ? format + : GetSizedInternalFormat(format, GL_UNSIGNED_BYTE, clientVersion); + redefineImage(level, sizedInternalFormat, width, height); if (!mImageArray[level]->isRenderableFormat()) { - mImageArray[level]->copy(0, 0, x, y, width, height, source); + mImageArray[level]->copy(0, 0, 0, x, y, width, height, source); mDirtyImages = true; } else { - if (!mTexStorage || !mTexStorage->isRenderTarget()) - { - convertToRenderTarget(); - } - + ensureRenderTarget(); mImageArray[level]->markClean(); - if (width != 0 && height != 0 && level < levelCount()) + if (width != 0 && height != 0 && isValidLevel(level)) { gl::Rectangle sourceRect; sourceRect.x = x; @@ -522,37 +696,35 @@ void Texture2D::copyImage(GLint level, GLenum format, GLint x, GLint y, GLsizei } } -void Texture2D::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source) +void Texture2D::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source) { - if (xoffset + width > mImageArray[level]->getWidth() || yoffset + height > mImageArray[level]->getHeight()) - { - return gl::error(GL_INVALID_VALUE); - } + // can only make our texture storage to a render target if level 0 is defined (with a width & height) and + // the current level we're copying to is defined (with appropriate format, width & height) + bool canCreateRenderTarget = isLevelComplete(level) && isLevelComplete(0); - if (!mImageArray[level]->isRenderableFormat() || (!mTexStorage && !isSamplerComplete())) + if (!mImageArray[level]->isRenderableFormat() || (!mTexStorage && !canCreateRenderTarget)) { - mImageArray[level]->copy(xoffset, yoffset, x, y, width, height, source); + mImageArray[level]->copy(xoffset, yoffset, 0, x, y, width, height, source); mDirtyImages = true; } else { - if (!mTexStorage || !mTexStorage->isRenderTarget()) - { - convertToRenderTarget(); - } + ensureRenderTarget(); - updateTexture(); - - if (level < levelCount()) + if (isValidLevel(level)) { + updateStorageLevel(level); + + GLuint clientVersion = mRenderer->getCurrentClientVersion(); + gl::Rectangle sourceRect; sourceRect.x = x; sourceRect.width = width; sourceRect.y = y; sourceRect.height = height; - mRenderer->copyImage(source, sourceRect, - gl::ExtractFormat(mImageArray[0]->getInternalFormat()), + mRenderer->copyImage(source, sourceRect, + gl::GetFormat(getBaseLevelInternalFormat(), clientVersion), xoffset, yoffset, mTexStorage, level); } } @@ -560,52 +732,54 @@ void Texture2D::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yo void Texture2D::storage(GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) { - delete mTexStorage; - mTexStorage = new rx::TextureStorageInterface2D(mRenderer, levels, internalformat, mUsage, false, width, height); - mImmutable = true; - for (int level = 0; level < levels; level++) { - mImageArray[level]->redefine(mRenderer, internalformat, width, height, true); - width = std::max(1, width >> 1); - height = std::max(1, height >> 1); + GLsizei levelWidth = std::max(1, width >> level); + GLsizei levelHeight = std::max(1, height >> level); + mImageArray[level]->redefine(mRenderer, GL_TEXTURE_2D, internalformat, levelWidth, levelHeight, 1, true); } for (int level = levels; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++) { - mImageArray[level]->redefine(mRenderer, GL_NONE, 0, 0, true); + mImageArray[level]->redefine(mRenderer, GL_TEXTURE_2D, GL_NONE, 0, 0, 0, true); } - if (mTexStorage->isManaged()) - { - int levels = levelCount(); + mImmutable = true; + + setCompleteTexStorage(new rx::TextureStorageInterface2D(mRenderer, internalformat, IsRenderTargetUsage(mUsage), width, height, levels)); +} + +void Texture2D::setCompleteTexStorage(rx::TextureStorageInterface2D *newCompleteTexStorage) +{ + SafeDelete(mTexStorage); + mTexStorage = newCompleteTexStorage; - for (int level = 0; level < levels; level++) + if (mTexStorage && mTexStorage->isManaged()) + { + for (int level = 0; level < mTexStorage->getLevelCount(); level++) { mImageArray[level]->setManagedSurface(mTexStorage, level); } } + + mDirtyImages = true; } // Tests for 2D texture sampling completeness. [OpenGL ES 2.0.24] section 3.8.2 page 85. -bool Texture2D::isSamplerComplete() const +bool Texture2D::isSamplerComplete(const SamplerState &samplerState) const { - GLsizei width = mImageArray[0]->getWidth(); - GLsizei height = mImageArray[0]->getHeight(); + GLsizei width = getBaseLevelWidth(); + GLsizei height = getBaseLevelHeight(); if (width <= 0 || height <= 0) { return false; } - bool mipmapping = isMipmapFiltered(); - bool filtering, renderable; - - if ((IsFloat32Format(getInternalFormat(0)) && !mRenderer->getFloat32TextureSupport(&filtering, &renderable)) || - (IsFloat16Format(getInternalFormat(0)) && !mRenderer->getFloat16TextureSupport(&filtering, &renderable))) + if (!IsTextureFilteringSupported(getInternalFormat(0), mRenderer)) { - if (mSamplerState.magFilter != GL_NEAREST || - (mSamplerState.minFilter != GL_NEAREST && mSamplerState.minFilter != GL_NEAREST_MIPMAP_NEAREST)) + if (samplerState.magFilter != GL_NEAREST || + (samplerState.minFilter != GL_NEAREST && samplerState.minFilter != GL_NEAREST_MIPMAP_NEAREST)) { return false; } @@ -615,14 +789,14 @@ bool Texture2D::isSamplerComplete() const if (!npotSupport) { - if ((mSamplerState.wrapS != GL_CLAMP_TO_EDGE && !isPow2(width)) || - (mSamplerState.wrapT != GL_CLAMP_TO_EDGE && !isPow2(height))) + if ((samplerState.wrapS != GL_CLAMP_TO_EDGE && !isPow2(width)) || + (samplerState.wrapT != GL_CLAMP_TO_EDGE && !isPow2(height))) { return false; } } - if (mipmapping) + if (IsMipmapFiltered(samplerState)) { if (!npotSupport) { @@ -638,43 +812,82 @@ bool Texture2D::isSamplerComplete() const } } + // OpenGLES 3.0.2 spec section 3.8.13 states that a texture is not mipmap complete if: + // The internalformat specified for the texture arrays is a sized internal depth or + // depth and stencil format (see table 3.13), the value of TEXTURE_COMPARE_- + // MODE is NONE, and either the magnification filter is not NEAREST or the mini- + // fication filter is neither NEAREST nor NEAREST_MIPMAP_NEAREST. + if (gl::GetDepthBits(getInternalFormat(0), mRenderer->getCurrentClientVersion()) > 0 && + mRenderer->getCurrentClientVersion() > 2) + { + if (mSamplerState.compareMode == GL_NONE) + { + if ((mSamplerState.minFilter != GL_NEAREST && mSamplerState.minFilter != GL_NEAREST_MIPMAP_NEAREST) || + mSamplerState.magFilter != GL_NEAREST) + { + return false; + } + } + } + return true; } // Tests for 2D texture (mipmap) completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81. bool Texture2D::isMipmapComplete() const { + int levelCount = mipLevels(); + + for (int level = 0; level < levelCount; level++) + { + if (!isLevelComplete(level)) + { + return false; + } + } + + return true; +} + +bool Texture2D::isLevelComplete(int level) const +{ if (isImmutable()) { return true; } - GLsizei width = mImageArray[0]->getWidth(); - GLsizei height = mImageArray[0]->getHeight(); + const rx::Image *baseImage = getBaseLevelImage(); + + GLsizei width = baseImage->getWidth(); + GLsizei height = baseImage->getHeight(); if (width <= 0 || height <= 0) { return false; } - int q = log2(std::max(width, height)); + // The base image level is complete if the width and height are positive + if (level == 0) + { + return true; + } + + ASSERT(level >= 1 && level <= (int)ArraySize(mImageArray) && mImageArray[level] != NULL); + rx::Image *image = mImageArray[level]; - for (int level = 1; level <= q; level++) + if (image->getInternalFormat() != baseImage->getInternalFormat()) { - if (mImageArray[level]->getInternalFormat() != mImageArray[0]->getInternalFormat()) - { - return false; - } + return false; + } - if (mImageArray[level]->getWidth() != std::max(1, width >> level)) - { - return false; - } + if (image->getWidth() != std::max(1, width >> level)) + { + return false; + } - if (mImageArray[level]->getHeight() != std::max(1, height >> level)) - { - return false; - } + if (image->getHeight() != std::max(1, height >> level)) + { + return false; } return true; @@ -682,211 +895,203 @@ bool Texture2D::isMipmapComplete() const bool Texture2D::isCompressed(GLint level) const { - return IsCompressed(getInternalFormat(level)); + return IsFormatCompressed(getInternalFormat(level), mRenderer->getCurrentClientVersion()); } bool Texture2D::isDepth(GLint level) const { - return IsDepthTexture(getInternalFormat(level)); + return GetDepthBits(getInternalFormat(level), mRenderer->getCurrentClientVersion()) > 0; } // Constructs a native texture resource from the texture images -void Texture2D::createTexture() +void Texture2D::initializeStorage(bool renderTarget) { - GLsizei width = mImageArray[0]->getWidth(); - GLsizei height = mImageArray[0]->getHeight(); - - if (!(width > 0 && height > 0)) - return; // do not attempt to create native textures for nonexistant data - - GLint levels = creationLevels(width, height); - GLenum internalformat = mImageArray[0]->getInternalFormat(); - - delete mTexStorage; - mTexStorage = new rx::TextureStorageInterface2D(mRenderer, levels, internalformat, mUsage, false, width, height); - - if (mTexStorage->isManaged()) + // Only initialize the first time this texture is used as a render target or shader resource + if (mTexStorage) { - int levels = levelCount(); + return; + } - for (int level = 0; level < levels; level++) - { - mImageArray[level]->setManagedSurface(mTexStorage, level); - } + // do not attempt to create storage for nonexistant data + if (!isLevelComplete(0)) + { + return; } - mDirtyImages = true; + bool createRenderTarget = (renderTarget || IsRenderTargetUsage(mUsage)); + + setCompleteTexStorage(createCompleteStorage(createRenderTarget)); + ASSERT(mTexStorage); + + // flush image data to the storage + updateStorage(); } -void Texture2D::updateTexture() +rx::TextureStorageInterface2D *Texture2D::createCompleteStorage(bool renderTarget) const { - bool mipmapping = (isMipmapFiltered() && isMipmapComplete()); + GLsizei width = getBaseLevelWidth(); + GLsizei height = getBaseLevelHeight(); - int levels = (mipmapping ? levelCount() : 1); + ASSERT(width > 0 && height > 0); - for (int level = 0; level < levels; level++) - { - rx::Image *image = mImageArray[level]; + // use existing storage level count, when previously specified by TexStorage*D + GLint levels = (mTexStorage ? mTexStorage->getLevelCount() : creationLevels(width, height, 1)); - if (image->isDirty()) + return new rx::TextureStorageInterface2D(mRenderer, getBaseLevelInternalFormat(), renderTarget, width, height, levels); +} + +void Texture2D::updateStorage() +{ + ASSERT(mTexStorage != NULL); + GLint storageLevels = mTexStorage->getLevelCount(); + for (int level = 0; level < storageLevels; level++) + { + if (mImageArray[level]->isDirty() && isLevelComplete(level)) { - commitRect(level, 0, 0, mImageArray[level]->getWidth(), mImageArray[level]->getHeight()); + updateStorageLevel(level); } } } -void Texture2D::convertToRenderTarget() +void Texture2D::updateStorageLevel(int level) { - rx::TextureStorageInterface2D *newTexStorage = NULL; + ASSERT(level <= (int)ArraySize(mImageArray) && mImageArray[level] != NULL); + ASSERT(isLevelComplete(level)); - if (mImageArray[0]->getWidth() != 0 && mImageArray[0]->getHeight() != 0) + if (mImageArray[level]->isDirty()) { - GLsizei width = mImageArray[0]->getWidth(); - GLsizei height = mImageArray[0]->getHeight(); - GLint levels = mTexStorage != NULL ? mTexStorage->levelCount() : creationLevels(width, height); - GLenum internalformat = mImageArray[0]->getInternalFormat(); + commitRect(level, 0, 0, getWidth(level), getHeight(level)); + } +} - newTexStorage = new rx::TextureStorageInterface2D(mRenderer, levels, internalformat, GL_FRAMEBUFFER_ATTACHMENT_ANGLE, true, width, height); +bool Texture2D::ensureRenderTarget() +{ + initializeStorage(true); - if (mTexStorage != NULL) + if (getBaseLevelWidth() > 0 && getBaseLevelHeight() > 0) + { + ASSERT(mTexStorage); + if (!mTexStorage->isRenderTarget()) { - if (!mRenderer->copyToRenderTarget(newTexStorage, mTexStorage)) - { - delete newTexStorage; - return gl::error(GL_OUT_OF_MEMORY); + rx::TextureStorageInterface2D *newRenderTargetStorage = createCompleteStorage(true); + + if (!mRenderer->copyToRenderTarget(newRenderTargetStorage, mTexStorage)) + { + delete newRenderTargetStorage; + return gl::error(GL_OUT_OF_MEMORY, false); } + + setCompleteTexStorage(newRenderTargetStorage); } } - delete mTexStorage; - mTexStorage = newTexStorage; - - mDirtyImages = true; + return (mTexStorage && mTexStorage->isRenderTarget()); } void Texture2D::generateMipmaps() { - if (!mRenderer->getNonPower2TextureSupport()) - { - if (!isPow2(mImageArray[0]->getWidth()) || !isPow2(mImageArray[0]->getHeight())) - { - return gl::error(GL_INVALID_OPERATION); - } - } - // Purge array levels 1 through q and reset them to represent the generated mipmap levels. - unsigned int q = log2(std::max(mImageArray[0]->getWidth(), mImageArray[0]->getHeight())); - for (unsigned int i = 1; i <= q; i++) + int levelCount = mipLevels(); + for (int level = 1; level < levelCount; level++) { - redefineImage(i, mImageArray[0]->getInternalFormat(), - std::max(mImageArray[0]->getWidth() >> i, 1), - std::max(mImageArray[0]->getHeight() >> i, 1)); + redefineImage(level, getBaseLevelInternalFormat(), + std::max(getBaseLevelWidth() >> level, 1), + std::max(getBaseLevelHeight() >> level, 1)); } if (mTexStorage && mTexStorage->isRenderTarget()) { - for (unsigned int i = 1; i <= q; i++) + for (int level = 1; level < levelCount; level++) { - mTexStorage->generateMipmap(i); + mTexStorage->generateMipmap(level); - mImageArray[i]->markClean(); + mImageArray[level]->markClean(); } } else { - for (unsigned int i = 1; i <= q; i++) + for (int level = 1; level < levelCount; level++) { - mRenderer->generateMipmap(mImageArray[i], mImageArray[i - 1]); + mRenderer->generateMipmap(mImageArray[level], mImageArray[level - 1]); } } } -Renderbuffer *Texture2D::getRenderbuffer(GLenum target) +const rx::Image *Texture2D::getBaseLevelImage() const { - if (target != GL_TEXTURE_2D) - { - return gl::error(GL_INVALID_OPERATION, (Renderbuffer *)NULL); - } + return mImageArray[0]; +} - if (mColorbufferProxy == NULL) +rx::TextureStorageInterface *Texture2D::getBaseLevelStorage() +{ + return mTexStorage; +} + +FramebufferAttachment *Texture2D::getAttachment(GLint level) +{ + FramebufferAttachment *attachment = mRenderbufferProxies.get(level, 0); + if (!attachment) { - mColorbufferProxy = new Renderbuffer(mRenderer, id(), new RenderbufferTexture2D(this, target)); + attachment = new FramebufferAttachment(mRenderer, id(), new Texture2DAttachment(this, level)); + mRenderbufferProxies.add(level, 0, attachment); } - return mColorbufferProxy; + return attachment; } -rx::RenderTarget *Texture2D::getRenderTarget(GLenum target) +unsigned int Texture2D::getRenderTargetSerial(GLint level) { - ASSERT(target == GL_TEXTURE_2D); + return (ensureRenderTarget() ? mTexStorage->getRenderTargetSerial(level) : 0); +} +rx::RenderTarget *Texture2D::getRenderTarget(GLint level) +{ // ensure the underlying texture is created - if (getStorage(true) == NULL) + if (!ensureRenderTarget()) { return NULL; } - updateTexture(); - + updateStorageLevel(level); + // ensure this is NOT a depth texture - if (isDepth(0)) + if (isDepth(level)) { return NULL; } - return mTexStorage->getRenderTarget(); + return mTexStorage->getRenderTarget(level); } -rx::RenderTarget *Texture2D::getDepthStencil(GLenum target) +rx::RenderTarget *Texture2D::getDepthSencil(GLint level) { - ASSERT(target == GL_TEXTURE_2D); - // ensure the underlying texture is created - if (getStorage(true) == NULL) + if (!ensureRenderTarget()) { return NULL; } - updateTexture(); + updateStorageLevel(level); // ensure this is actually a depth texture - if (!isDepth(0)) + if (!isDepth(level)) { return NULL; } - return mTexStorage->getRenderTarget(); -} -int Texture2D::levelCount() -{ - return mTexStorage ? mTexStorage->levelCount() : 0; + return mTexStorage->getRenderTarget(level); } -rx::TextureStorageInterface *Texture2D::getStorage(bool renderTarget) +bool Texture2D::isValidLevel(int level) const { - if (!mTexStorage || (renderTarget && !mTexStorage->isRenderTarget())) - { - if (renderTarget) - { - convertToRenderTarget(); - } - else - { - createTexture(); - } - } - - return mTexStorage; + return (mTexStorage ? (level >= 0 && level < mTexStorage->getLevelCount()) : false); } -TextureCubeMap::TextureCubeMap(rx::Renderer *renderer, GLuint id) : Texture(renderer, id) +TextureCubeMap::TextureCubeMap(rx::Renderer *renderer, GLuint id) : Texture(renderer, id, GL_TEXTURE_CUBE_MAP) { mTexStorage = NULL; for (int i = 0; i < 6; i++) { - mFaceProxies[i] = NULL; - mFaceProxyRefs[i] = 0; - for (int j = 0; j < IMPLEMENTATION_MAX_TEXTURE_LEVELS; ++j) { mImageArray[i][j] = renderer->createImage(); @@ -898,8 +1103,6 @@ TextureCubeMap::~TextureCubeMap() { for (int i = 0; i < 6; i++) { - mFaceProxies[i] = NULL; - for (int j = 0; j < IMPLEMENTATION_MAX_TEXTURE_LEVELS; ++j) { delete mImageArray[i][j]; @@ -910,44 +1113,10 @@ TextureCubeMap::~TextureCubeMap() mTexStorage = NULL; } -// We need to maintain a count of references to renderbuffers acting as -// proxies for this texture, so that the texture is not deleted while -// proxy references still exist. If the reference count drops to zero, -// we set our proxy pointer NULL, so that a new attempt at referencing -// will cause recreation. -void TextureCubeMap::addProxyRef(const Renderbuffer *proxy) -{ - for (int i = 0; i < 6; i++) - { - if (mFaceProxies[i] == proxy) - mFaceProxyRefs[i]++; - } -} - -void TextureCubeMap::releaseProxy(const Renderbuffer *proxy) -{ - for (int i = 0; i < 6; i++) - { - if (mFaceProxies[i] == proxy) - { - if (mFaceProxyRefs[i] > 0) - mFaceProxyRefs[i]--; - - if (mFaceProxyRefs[i] == 0) - mFaceProxies[i] = NULL; - } - } -} - -GLenum TextureCubeMap::getTarget() const -{ - return GL_TEXTURE_CUBE_MAP; -} - GLsizei TextureCubeMap::getWidth(GLenum target, GLint level) const { if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS) - return mImageArray[faceIndex(target)][level]->getWidth(); + return mImageArray[targetToIndex(target)][level]->getWidth(); else return 0; } @@ -955,7 +1124,7 @@ GLsizei TextureCubeMap::getWidth(GLenum target, GLint level) const GLsizei TextureCubeMap::getHeight(GLenum target, GLint level) const { if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS) - return mImageArray[faceIndex(target)][level]->getHeight(); + return mImageArray[targetToIndex(target)][level]->getHeight(); else return 0; } @@ -963,7 +1132,7 @@ GLsizei TextureCubeMap::getHeight(GLenum target, GLint level) const GLenum TextureCubeMap::getInternalFormat(GLenum target, GLint level) const { if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS) - return mImageArray[faceIndex(target)][level]->getInternalFormat(); + return mImageArray[targetToIndex(target)][level]->getInternalFormat(); else return GL_NONE; } @@ -971,88 +1140,89 @@ GLenum TextureCubeMap::getInternalFormat(GLenum target, GLint level) const GLenum TextureCubeMap::getActualFormat(GLenum target, GLint level) const { if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS) - return mImageArray[faceIndex(target)][level]->getActualFormat(); + return mImageArray[targetToIndex(target)][level]->getActualFormat(); else - return D3DFMT_UNKNOWN; + return GL_NONE; } -void TextureCubeMap::setImagePosX(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels) +void TextureCubeMap::setImagePosX(GLint level, GLsizei width, GLsizei height, GLenum internalFormat, GLenum format, GLenum type, const PixelUnpackState &unpack, const void *pixels) { - setImage(0, level, width, height, format, type, unpackAlignment, pixels); + setImage(0, level, width, height, internalFormat, format, type, unpack, pixels); } -void TextureCubeMap::setImageNegX(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels) +void TextureCubeMap::setImageNegX(GLint level, GLsizei width, GLsizei height, GLenum internalFormat, GLenum format, GLenum type, const PixelUnpackState &unpack, const void *pixels) { - setImage(1, level, width, height, format, type, unpackAlignment, pixels); + setImage(1, level, width, height, internalFormat, format, type, unpack, pixels); } -void TextureCubeMap::setImagePosY(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels) +void TextureCubeMap::setImagePosY(GLint level, GLsizei width, GLsizei height, GLenum internalFormat, GLenum format, GLenum type, const PixelUnpackState &unpack, const void *pixels) { - setImage(2, level, width, height, format, type, unpackAlignment, pixels); + setImage(2, level, width, height, internalFormat, format, type, unpack, pixels); } -void TextureCubeMap::setImageNegY(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels) +void TextureCubeMap::setImageNegY(GLint level, GLsizei width, GLsizei height, GLenum internalFormat, GLenum format, GLenum type, const PixelUnpackState &unpack, const void *pixels) { - setImage(3, level, width, height, format, type, unpackAlignment, pixels); + setImage(3, level, width, height, internalFormat, format, type, unpack, pixels); } -void TextureCubeMap::setImagePosZ(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels) +void TextureCubeMap::setImagePosZ(GLint level, GLsizei width, GLsizei height, GLenum internalFormat, GLenum format, GLenum type, const PixelUnpackState &unpack, const void *pixels) { - setImage(4, level, width, height, format, type, unpackAlignment, pixels); + setImage(4, level, width, height, internalFormat, format, type, unpack, pixels); } -void TextureCubeMap::setImageNegZ(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels) +void TextureCubeMap::setImageNegZ(GLint level, GLsizei width, GLsizei height, GLenum internalFormat, GLenum format, GLenum type, const PixelUnpackState &unpack, const void *pixels) { - setImage(5, level, width, height, format, type, unpackAlignment, pixels); + setImage(5, level, width, height, internalFormat, format, type, unpack, pixels); } -void TextureCubeMap::setCompressedImage(GLenum face, GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels) +void TextureCubeMap::setCompressedImage(GLenum target, GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels) { // compressed formats don't have separate sized internal formats-- we can just use the compressed format directly - redefineImage(faceIndex(face), level, format, width, height); + int faceIndex = targetToIndex(target); + redefineImage(faceIndex, level, format, width, height); - Texture::setCompressedImage(imageSize, pixels, mImageArray[faceIndex(face)][level]); + Texture::setCompressedImage(imageSize, pixels, mImageArray[faceIndex][level]); } -void TextureCubeMap::commitRect(int face, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height) +void TextureCubeMap::commitRect(int faceIndex, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height) { - if (level < levelCount()) + if (isValidFaceLevel(faceIndex, level)) { - rx::Image *image = mImageArray[face][level]; - if (image->updateSurface(mTexStorage, face, level, xoffset, yoffset, width, height)) + rx::Image *image = mImageArray[faceIndex][level]; + if (image->copyToStorage(mTexStorage, faceIndex, level, xoffset, yoffset, width, height)) image->markClean(); } } -void TextureCubeMap::subImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels) +void TextureCubeMap::subImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const PixelUnpackState &unpack, const void *pixels) { - if (Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, mImageArray[faceIndex(target)][level])) + int faceIndex = targetToIndex(target); + if (Texture::subImage(xoffset, yoffset, 0, width, height, 1, format, type, unpack, pixels, mImageArray[faceIndex][level])) { - commitRect(faceIndex(target), level, xoffset, yoffset, width, height); + commitRect(faceIndex, level, xoffset, yoffset, width, height); } } void TextureCubeMap::subImageCompressed(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels) { - if (Texture::subImageCompressed(xoffset, yoffset, width, height, format, imageSize, pixels, mImageArray[faceIndex(target)][level])) + int faceIndex = targetToIndex(target); + if (Texture::subImageCompressed(xoffset, yoffset, 0, width, height, 1, format, imageSize, pixels, mImageArray[faceIndex][level])) { - commitRect(faceIndex(target), level, xoffset, yoffset, width, height); + commitRect(faceIndex, level, xoffset, yoffset, width, height); } } // Tests for cube map sampling completeness. [OpenGL ES 2.0.24] section 3.8.2 page 86. -bool TextureCubeMap::isSamplerComplete() const +bool TextureCubeMap::isSamplerComplete(const SamplerState &samplerState) const { - int size = mImageArray[0][0]->getWidth(); + int size = getBaseLevelWidth(); - bool mipmapping = isMipmapFiltered(); - bool filtering, renderable; + bool mipmapping = IsMipmapFiltered(samplerState); - if ((gl::ExtractType(getInternalFormat(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0)) == GL_FLOAT && !mRenderer->getFloat32TextureSupport(&filtering, &renderable)) || - (gl::ExtractType(getInternalFormat(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0) == GL_HALF_FLOAT_OES) && !mRenderer->getFloat16TextureSupport(&filtering, &renderable))) + if (!IsTextureFilteringSupported(getInternalFormat(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0), mRenderer)) { - if (mSamplerState.magFilter != GL_NEAREST || - (mSamplerState.minFilter != GL_NEAREST && mSamplerState.minFilter != GL_NEAREST_MIPMAP_NEAREST)) + if (samplerState.magFilter != GL_NEAREST || + (samplerState.minFilter != GL_NEAREST && samplerState.minFilter != GL_NEAREST_MIPMAP_NEAREST)) { return false; } @@ -1060,7 +1230,7 @@ bool TextureCubeMap::isSamplerComplete() const if (!isPow2(size) && !mRenderer->getNonPower2TextureSupport()) { - if (mSamplerState.wrapS != GL_CLAMP_TO_EDGE || mSamplerState.wrapT != GL_CLAMP_TO_EDGE || mipmapping) + if (samplerState.wrapS != GL_CLAMP_TO_EDGE || samplerState.wrapT != GL_CLAMP_TO_EDGE || mipmapping) { return false; } @@ -1087,16 +1257,22 @@ bool TextureCubeMap::isSamplerComplete() const // Tests for cube texture completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81. bool TextureCubeMap::isCubeComplete() const { - if (mImageArray[0][0]->getWidth() <= 0 || mImageArray[0][0]->getHeight() != mImageArray[0][0]->getWidth()) + int baseWidth = getBaseLevelWidth(); + int baseHeight = getBaseLevelHeight(); + GLenum baseFormat = getBaseLevelInternalFormat(); + + if (baseWidth <= 0 || baseWidth != baseHeight) { return false; } - for (unsigned int face = 1; face < 6; face++) + for (int faceIndex = 1; faceIndex < 6; faceIndex++) { - if (mImageArray[face][0]->getWidth() != mImageArray[0][0]->getWidth() || - mImageArray[face][0]->getWidth() != mImageArray[0][0]->getHeight() || - mImageArray[face][0]->getInternalFormat() != mImageArray[0][0]->getInternalFormat()) + const rx::Image &faceBaseImage = *mImageArray[faceIndex][0]; + + if (faceBaseImage.getWidth() != baseWidth || + faceBaseImage.getHeight() != baseHeight || + faceBaseImage.getInternalFormat() != baseFormat ) { return false; } @@ -1117,20 +1293,13 @@ bool TextureCubeMap::isMipmapCubeComplete() const return false; } - GLsizei size = mImageArray[0][0]->getWidth(); - - int q = log2(size); + int levelCount = mipLevels(); for (int face = 0; face < 6; face++) { - for (int level = 1; level <= q; level++) + for (int level = 1; level < levelCount; level++) { - if (mImageArray[face][level]->getInternalFormat() != mImageArray[0][0]->getInternalFormat()) - { - return false; - } - - if (mImageArray[face][level]->getWidth() != std::max(1, size >> level)) + if (!isFaceLevelComplete(face, level)) { return false; } @@ -1140,34 +1309,103 @@ bool TextureCubeMap::isMipmapCubeComplete() const return true; } +bool TextureCubeMap::isFaceLevelComplete(int faceIndex, int level) const +{ + ASSERT(level >= 0 && faceIndex < 6 && level < (int)ArraySize(mImageArray[faceIndex]) && mImageArray[faceIndex][level] != NULL); + + if (isImmutable()) + { + return true; + } + + int baseSize = getBaseLevelWidth(); + + if (baseSize <= 0) + { + return false; + } + + // "isCubeComplete" checks for base level completeness and we must call that + // to determine if any face at level 0 is complete. We omit that check here + // to avoid re-checking cube-completeness for every face at level 0. + if (level == 0) + { + return true; + } + + // Check that non-zero levels are consistent with the base level. + const rx::Image *faceLevelImage = mImageArray[faceIndex][level]; + + if (faceLevelImage->getInternalFormat() != getBaseLevelInternalFormat()) + { + return false; + } + + if (faceLevelImage->getWidth() != std::max(1, baseSize >> level)) + { + return false; + } + + return true; +} + bool TextureCubeMap::isCompressed(GLenum target, GLint level) const { - return IsCompressed(getInternalFormat(target, level)); + return IsFormatCompressed(getInternalFormat(target, level), mRenderer->getCurrentClientVersion()); +} + +bool TextureCubeMap::isDepth(GLenum target, GLint level) const +{ + return GetDepthBits(getInternalFormat(target, level), mRenderer->getCurrentClientVersion()) > 0; +} + +void TextureCubeMap::initializeStorage(bool renderTarget) +{ + // Only initialize the first time this texture is used as a render target or shader resource + if (mTexStorage) + { + return; + } + + // do not attempt to create storage for nonexistant data + if (!isFaceLevelComplete(0, 0)) + { + return; + } + + bool createRenderTarget = (renderTarget || IsRenderTargetUsage(mUsage)); + + setCompleteTexStorage(createCompleteStorage(createRenderTarget)); + ASSERT(mTexStorage); + + // flush image data to the storage + updateStorage(); } -// Constructs a native texture resource from the texture images, or returns an existing one -void TextureCubeMap::createTexture() +rx::TextureStorageInterfaceCube *TextureCubeMap::createCompleteStorage(bool renderTarget) const { - GLsizei size = mImageArray[0][0]->getWidth(); + GLsizei size = getBaseLevelWidth(); - if (!(size > 0)) - return; // do not attempt to create native textures for nonexistant data + ASSERT(size > 0); - GLint levels = creationLevels(size); - GLenum internalformat = mImageArray[0][0]->getInternalFormat(); + // use existing storage level count, when previously specified by TexStorage*D + GLint levels = (mTexStorage ? mTexStorage->getLevelCount() : creationLevels(size, size, 1)); - delete mTexStorage; - mTexStorage = new rx::TextureStorageInterfaceCube(mRenderer, levels, internalformat, mUsage, false, size); + return new rx::TextureStorageInterfaceCube(mRenderer, getBaseLevelInternalFormat(), renderTarget, size, levels); +} - if (mTexStorage->isManaged()) - { - int levels = levelCount(); +void TextureCubeMap::setCompleteTexStorage(rx::TextureStorageInterfaceCube *newCompleteTexStorage) +{ + SafeDelete(mTexStorage); + mTexStorage = newCompleteTexStorage; - for (int face = 0; face < 6; face++) + if (mTexStorage && mTexStorage->isManaged()) + { + for (int faceIndex = 0; faceIndex < 6; faceIndex++) { - for (int level = 0; level < levels; level++) + for (int level = 0; level < mTexStorage->getLevelCount(); level++) { - mImageArray[face][level]->setManagedSurface(mTexStorage, face, level); + mImageArray[faceIndex][level]->setManagedSurface(mTexStorage, faceIndex, level); } } } @@ -1175,63 +1413,69 @@ void TextureCubeMap::createTexture() mDirtyImages = true; } -void TextureCubeMap::updateTexture() +void TextureCubeMap::updateStorage() { - bool mipmapping = isMipmapFiltered() && isMipmapCubeComplete(); - + ASSERT(mTexStorage != NULL); + GLint storageLevels = mTexStorage->getLevelCount(); for (int face = 0; face < 6; face++) { - int levels = (mipmapping ? levelCount() : 1); - - for (int level = 0; level < levels; level++) + for (int level = 0; level < storageLevels; level++) { - rx::Image *image = mImageArray[face][level]; - - if (image->isDirty()) + if (mImageArray[face][level]->isDirty() && isFaceLevelComplete(face, level)) { - commitRect(face, level, 0, 0, image->getWidth(), image->getHeight()); + updateStorageFaceLevel(face, level); } } } } -void TextureCubeMap::convertToRenderTarget() +void TextureCubeMap::updateStorageFaceLevel(int faceIndex, int level) { - rx::TextureStorageInterfaceCube *newTexStorage = NULL; + ASSERT(level >= 0 && faceIndex < 6 && level < (int)ArraySize(mImageArray[faceIndex]) && mImageArray[faceIndex][level] != NULL); + rx::Image *image = mImageArray[faceIndex][level]; - if (mImageArray[0][0]->getWidth() != 0) + if (image->isDirty()) { - GLsizei size = mImageArray[0][0]->getWidth(); - GLint levels = mTexStorage != NULL ? mTexStorage->levelCount() : creationLevels(size); - GLenum internalformat = mImageArray[0][0]->getInternalFormat(); + commitRect(faceIndex, level, 0, 0, image->getWidth(), image->getHeight()); + } +} - newTexStorage = new rx::TextureStorageInterfaceCube(mRenderer, levels, internalformat, GL_FRAMEBUFFER_ATTACHMENT_ANGLE, true, size); +bool TextureCubeMap::ensureRenderTarget() +{ + initializeStorage(true); - if (mTexStorage != NULL) + if (getBaseLevelWidth() > 0) + { + ASSERT(mTexStorage); + if (!mTexStorage->isRenderTarget()) { - if (!mRenderer->copyToRenderTarget(newTexStorage, mTexStorage)) + rx::TextureStorageInterfaceCube *newRenderTargetStorage = createCompleteStorage(true); + + if (!mRenderer->copyToRenderTarget(newRenderTargetStorage, mTexStorage)) { - delete newTexStorage; - return gl::error(GL_OUT_OF_MEMORY); + delete newRenderTargetStorage; + return gl::error(GL_OUT_OF_MEMORY, false); } + + setCompleteTexStorage(newRenderTargetStorage); } } - delete mTexStorage; - mTexStorage = newTexStorage; - - mDirtyImages = true; + return (mTexStorage && mTexStorage->isRenderTarget()); } -void TextureCubeMap::setImage(int faceIndex, GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels) +void TextureCubeMap::setImage(int faceIndex, GLint level, GLsizei width, GLsizei height, GLenum internalFormat, GLenum format, GLenum type, const PixelUnpackState &unpack, const void *pixels) { - GLint internalformat = ConvertSizedInternalFormat(format, type); - redefineImage(faceIndex, level, internalformat, width, height); + GLuint clientVersion = mRenderer->getCurrentClientVersion(); + GLenum sizedInternalFormat = IsSizedInternalFormat(internalFormat, clientVersion) ? internalFormat + : GetSizedInternalFormat(format, type, clientVersion); - Texture::setImage(unpackAlignment, pixels, mImageArray[faceIndex][level]); + redefineImage(faceIndex, level, sizedInternalFormat, width, height); + + Texture::setImage(unpack, type, pixels, mImageArray[faceIndex][level]); } -unsigned int TextureCubeMap::faceIndex(GLenum face) +int TextureCubeMap::targetToIndex(GLenum target) { META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_X - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 1); META_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_Y - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 2); @@ -1239,32 +1483,32 @@ unsigned int TextureCubeMap::faceIndex(GLenum face) META_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_Z - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 4); META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 5); - return face - GL_TEXTURE_CUBE_MAP_POSITIVE_X; + return target - GL_TEXTURE_CUBE_MAP_POSITIVE_X; } -void TextureCubeMap::redefineImage(int face, GLint level, GLint internalformat, GLsizei width, GLsizei height) +void TextureCubeMap::redefineImage(int faceIndex, GLint level, GLenum internalformat, GLsizei width, GLsizei height) { // If there currently is a corresponding storage texture image, it has these parameters - const int storageWidth = std::max(1, mImageArray[0][0]->getWidth() >> level); - const int storageHeight = std::max(1, mImageArray[0][0]->getHeight() >> level); - const int storageFormat = mImageArray[0][0]->getInternalFormat(); + const int storageWidth = std::max(1, getBaseLevelWidth() >> level); + const int storageHeight = std::max(1, getBaseLevelHeight() >> level); + const GLenum storageFormat = getBaseLevelInternalFormat(); - mImageArray[face][level]->redefine(mRenderer, internalformat, width, height, false); + mImageArray[faceIndex][level]->redefine(mRenderer, GL_TEXTURE_CUBE_MAP, internalformat, width, height, 1, false); if (mTexStorage) { - const int storageLevels = mTexStorage->levelCount(); - + const int storageLevels = mTexStorage->getLevelCount(); + if ((level >= storageLevels && storageLevels != 0) || width != storageWidth || height != storageHeight || internalformat != storageFormat) // Discard mismatched storage { - for (int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++) + for (int level = 0; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++) { - for (int f = 0; f < 6; f++) + for (int faceIndex = 0; faceIndex < 6; faceIndex++) { - mImageArray[f][i]->markDirty(); + mImageArray[faceIndex][level]->markDirty(); } } @@ -1278,27 +1522,25 @@ void TextureCubeMap::redefineImage(int face, GLint level, GLint internalformat, void TextureCubeMap::copyImage(GLenum target, GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source) { - unsigned int faceindex = faceIndex(target); - GLint internalformat = gl::ConvertSizedInternalFormat(format, GL_UNSIGNED_BYTE); - redefineImage(faceindex, level, internalformat, width, height); + int faceIndex = targetToIndex(target); + GLuint clientVersion = mRenderer->getCurrentClientVersion(); + GLenum sizedInternalFormat = IsSizedInternalFormat(format, clientVersion) ? format + : GetSizedInternalFormat(format, GL_UNSIGNED_BYTE, clientVersion); + redefineImage(faceIndex, level, sizedInternalFormat, width, height); - if (!mImageArray[faceindex][level]->isRenderableFormat()) + if (!mImageArray[faceIndex][level]->isRenderableFormat()) { - mImageArray[faceindex][level]->copy(0, 0, x, y, width, height, source); + mImageArray[faceIndex][level]->copy(0, 0, 0, x, y, width, height, source); mDirtyImages = true; } else { - if (!mTexStorage || !mTexStorage->isRenderTarget()) - { - convertToRenderTarget(); - } - - mImageArray[faceindex][level]->markClean(); + ensureRenderTarget(); + mImageArray[faceIndex][level]->markClean(); ASSERT(width == height); - if (width > 0 && level < levelCount()) + if (width > 0 && isValidFaceLevel(faceIndex, level)) { gl::Rectangle sourceRect; sourceRect.x = x; @@ -1311,40 +1553,37 @@ void TextureCubeMap::copyImage(GLenum target, GLint level, GLenum format, GLint } } -void TextureCubeMap::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source) +void TextureCubeMap::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source) { - GLsizei size = mImageArray[faceIndex(target)][level]->getWidth(); + int faceIndex = targetToIndex(target); - if (xoffset + width > size || yoffset + height > size) - { - return gl::error(GL_INVALID_VALUE); - } - - unsigned int faceindex = faceIndex(target); + // We can only make our texture storage to a render target if the level we're copying *to* is complete + // and the base level is cube-complete. The base level must be cube complete (common case) because we cannot + // rely on the "getBaseLevel*" methods reliably otherwise. + bool canCreateRenderTarget = isFaceLevelComplete(faceIndex, level) && isCubeComplete(); - if (!mImageArray[faceindex][level]->isRenderableFormat() || (!mTexStorage && !isSamplerComplete())) + if (!mImageArray[faceIndex][level]->isRenderableFormat() || (!mTexStorage && !canCreateRenderTarget)) { - mImageArray[faceindex][level]->copy(0, 0, x, y, width, height, source); + mImageArray[faceIndex][level]->copy(0, 0, 0, x, y, width, height, source); mDirtyImages = true; } else { - if (!mTexStorage || !mTexStorage->isRenderTarget()) - { - convertToRenderTarget(); - } + ensureRenderTarget(); - updateTexture(); - - if (level < levelCount()) + if (isValidFaceLevel(faceIndex, level)) { + updateStorageFaceLevel(faceIndex, level); + + GLuint clientVersion = mRenderer->getCurrentClientVersion(); + gl::Rectangle sourceRect; sourceRect.x = x; sourceRect.width = width; sourceRect.y = y; sourceRect.height = height; - mRenderer->copyImage(source, sourceRect, gl::ExtractFormat(mImageArray[0][0]->getInternalFormat()), + mRenderer->copyImage(source, sourceRect, gl::GetFormat(getBaseLevelInternalFormat(), clientVersion), xoffset, yoffset, mTexStorage, target, level); } } @@ -1352,144 +1591,1229 @@ void TextureCubeMap::copySubImage(GLenum target, GLint level, GLint xoffset, GLi void TextureCubeMap::storage(GLsizei levels, GLenum internalformat, GLsizei size) { - delete mTexStorage; - mTexStorage = new rx::TextureStorageInterfaceCube(mRenderer, levels, internalformat, mUsage, false, size); - mImmutable = true; - for (int level = 0; level < levels; level++) { GLsizei mipSize = std::max(1, size >> level); - for (int face = 0; face < 6; face++) + for (int faceIndex = 0; faceIndex < 6; faceIndex++) { - mImageArray[face][level]->redefine(mRenderer, internalformat, mipSize, mipSize, true); + mImageArray[faceIndex][level]->redefine(mRenderer, GL_TEXTURE_CUBE_MAP, internalformat, mipSize, mipSize, 1, true); } } for (int level = levels; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++) { - for (int face = 0; face < 6; face++) + for (int faceIndex = 0; faceIndex < 6; faceIndex++) { - mImageArray[face][level]->redefine(mRenderer, GL_NONE, 0, 0, true); + mImageArray[faceIndex][level]->redefine(mRenderer, GL_TEXTURE_CUBE_MAP, GL_NONE, 0, 0, 0, true); } } - if (mTexStorage->isManaged()) + mImmutable = true; + + setCompleteTexStorage(new rx::TextureStorageInterfaceCube(mRenderer, internalformat, IsRenderTargetUsage(mUsage), size, levels)); +} + +void TextureCubeMap::generateMipmaps() +{ + // Purge array levels 1 through q and reset them to represent the generated mipmap levels. + int levelCount = mipLevels(); + for (int faceIndex = 0; faceIndex < 6; faceIndex++) { - int levels = levelCount(); + for (int level = 1; level < levelCount; level++) + { + int faceLevelSize = (std::max(mImageArray[faceIndex][0]->getWidth() >> level, 1)); + redefineImage(faceIndex, level, mImageArray[faceIndex][0]->getInternalFormat(), faceLevelSize, faceLevelSize); + } + } - for (int face = 0; face < 6; face++) + if (mTexStorage && mTexStorage->isRenderTarget()) + { + for (int faceIndex = 0; faceIndex < 6; faceIndex++) { - for (int level = 0; level < levels; level++) + for (int level = 1; level < levelCount; level++) { - mImageArray[face][level]->setManagedSurface(mTexStorage, face, level); + mTexStorage->generateMipmap(faceIndex, level); + + mImageArray[faceIndex][level]->markClean(); + } + } + } + else + { + for (int faceIndex = 0; faceIndex < 6; faceIndex++) + { + for (int level = 1; level < levelCount; level++) + { + mRenderer->generateMipmap(mImageArray[faceIndex][level], mImageArray[faceIndex][level - 1]); } } } } -void TextureCubeMap::generateMipmaps() +const rx::Image *TextureCubeMap::getBaseLevelImage() const { - if (!isCubeComplete()) + // Note: if we are not cube-complete, there is no single base level image that can describe all + // cube faces, so this method is only well-defined for a cube-complete base level. + return mImageArray[0][0]; +} + +rx::TextureStorageInterface *TextureCubeMap::getBaseLevelStorage() +{ + return mTexStorage; +} + +FramebufferAttachment *TextureCubeMap::getAttachment(GLenum target, GLint level) +{ + ASSERT(!IsCubemapTextureTarget(target)); + int faceIndex = targetToIndex(target); + + FramebufferAttachment *attachment = mRenderbufferProxies.get(level, faceIndex); + if (!attachment) + { + attachment = new FramebufferAttachment(mRenderer, id(), new TextureCubeMapAttachment(this, target, level)); + mRenderbufferProxies.add(level, faceIndex, attachment); + } + + return attachment; +} + +unsigned int TextureCubeMap::getRenderTargetSerial(GLenum target, GLint level) +{ + return (ensureRenderTarget() ? mTexStorage->getRenderTargetSerial(target, level) : 0); +} + +rx::RenderTarget *TextureCubeMap::getRenderTarget(GLenum target, GLint level) +{ + ASSERT(IsCubemapTextureTarget(target)); + + // ensure the underlying texture is created + if (!ensureRenderTarget()) { - return gl::error(GL_INVALID_OPERATION); + return NULL; } - if (!mRenderer->getNonPower2TextureSupport()) + updateStorageFaceLevel(targetToIndex(target), level); + + // ensure this is NOT a depth texture + if (isDepth(target, level)) + { + return NULL; + } + + return mTexStorage->getRenderTarget(target, level); +} + +rx::RenderTarget *TextureCubeMap::getDepthStencil(GLenum target, GLint level) +{ + ASSERT(IsCubemapTextureTarget(target)); + + // ensure the underlying texture is created + if (!ensureRenderTarget()) + { + return NULL; + } + + updateStorageFaceLevel(targetToIndex(target), level); + + // ensure this is a depth texture + if (!isDepth(target, level)) { - if (!isPow2(mImageArray[0][0]->getWidth())) + return NULL; + } + + return mTexStorage->getRenderTarget(target, level); +} + +bool TextureCubeMap::isValidFaceLevel(int faceIndex, int level) const +{ + return (mTexStorage ? (level >= 0 && level < mTexStorage->getLevelCount()) : 0); +} + +Texture3D::Texture3D(rx::Renderer *renderer, GLuint id) : Texture(renderer, id, GL_TEXTURE_3D) +{ + mTexStorage = NULL; + + for (int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; ++i) + { + mImageArray[i] = renderer->createImage(); + } +} + +Texture3D::~Texture3D() +{ + delete mTexStorage; + mTexStorage = NULL; + + for (int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; ++i) + { + delete mImageArray[i]; + } +} + +GLsizei Texture3D::getWidth(GLint level) const +{ + return (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS) ? mImageArray[level]->getWidth() : 0; +} + +GLsizei Texture3D::getHeight(GLint level) const +{ + return (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS) ? mImageArray[level]->getHeight() : 0; +} + +GLsizei Texture3D::getDepth(GLint level) const +{ + return (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS) ? mImageArray[level]->getDepth() : 0; +} + +GLenum Texture3D::getInternalFormat(GLint level) const +{ + return (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS) ? mImageArray[level]->getInternalFormat() : GL_NONE; +} + +GLenum Texture3D::getActualFormat(GLint level) const +{ + return (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS) ? mImageArray[level]->getActualFormat() : GL_NONE; +} + +bool Texture3D::isCompressed(GLint level) const +{ + return IsFormatCompressed(getInternalFormat(level), mRenderer->getCurrentClientVersion()); +} + +bool Texture3D::isDepth(GLint level) const +{ + return GetDepthBits(getInternalFormat(level), mRenderer->getCurrentClientVersion()) > 0; +} + +void Texture3D::setImage(GLint level, GLsizei width, GLsizei height, GLsizei depth, GLenum internalFormat, GLenum format, GLenum type, const PixelUnpackState &unpack, const void *pixels) +{ + GLuint clientVersion = mRenderer->getCurrentClientVersion(); + GLenum sizedInternalFormat = IsSizedInternalFormat(internalFormat, clientVersion) ? internalFormat + : GetSizedInternalFormat(format, type, clientVersion); + redefineImage(level, sizedInternalFormat, width, height, depth); + + bool fastUnpacked = false; + + // Attempt a fast gpu copy of the pixel data to the surface if the app bound an unpack buffer + if (isFastUnpackable(unpack, sizedInternalFormat)) + { + // Will try to create RT storage if it does not exist + rx::RenderTarget *destRenderTarget = getRenderTarget(level); + Box destArea(0, 0, 0, getWidth(level), getHeight(level), getDepth(level)); + + if (destRenderTarget && fastUnpackPixels(unpack, pixels, destArea, sizedInternalFormat, type, destRenderTarget)) { - return gl::error(GL_INVALID_OPERATION); + // Ensure we don't overwrite our newly initialized data + mImageArray[level]->markClean(); + + fastUnpacked = true; } } - // Purge array levels 1 through q and reset them to represent the generated mipmap levels. - unsigned int q = log2(mImageArray[0][0]->getWidth()); - for (unsigned int f = 0; f < 6; f++) + if (!fastUnpacked) { - for (unsigned int i = 1; i <= q; i++) + Texture::setImage(unpack, type, pixels, mImageArray[level]); + } +} + +void Texture3D::setCompressedImage(GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei depth, GLsizei imageSize, const void *pixels) +{ + // compressed formats don't have separate sized internal formats-- we can just use the compressed format directly + redefineImage(level, format, width, height, depth); + + Texture::setCompressedImage(imageSize, pixels, mImageArray[level]); +} + +void Texture3D::subImage(GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const PixelUnpackState &unpack, const void *pixels) +{ + bool fastUnpacked = false; + + // Attempt a fast gpu copy of the pixel data to the surface if the app bound an unpack buffer + if (isFastUnpackable(unpack, getInternalFormat(level))) + { + rx::RenderTarget *destRenderTarget = getRenderTarget(level); + Box destArea(xoffset, yoffset, zoffset, width, height, depth); + + if (destRenderTarget && fastUnpackPixels(unpack, pixels, destArea, getInternalFormat(level), type, destRenderTarget)) { - redefineImage(f, i, mImageArray[f][0]->getInternalFormat(), - std::max(mImageArray[f][0]->getWidth() >> i, 1), - std::max(mImageArray[f][0]->getWidth() >> i, 1)); + // Ensure we don't overwrite our newly initialized data + mImageArray[level]->markClean(); + + fastUnpacked = true; } } + if (!fastUnpacked && Texture::subImage(xoffset, yoffset, zoffset, width, height, depth, format, type, unpack, pixels, mImageArray[level])) + { + commitRect(level, xoffset, yoffset, zoffset, width, height, depth); + } +} + +void Texture3D::subImageCompressed(GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *pixels) +{ + if (Texture::subImageCompressed(xoffset, yoffset, zoffset, width, height, depth, format, imageSize, pixels, mImageArray[level])) + { + commitRect(level, xoffset, yoffset, zoffset, width, height, depth); + } +} + +void Texture3D::storage(GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth) +{ + for (int level = 0; level < levels; level++) + { + GLsizei levelWidth = std::max(1, width >> level); + GLsizei levelHeight = std::max(1, height >> level); + GLsizei levelDepth = std::max(1, depth >> level); + mImageArray[level]->redefine(mRenderer, GL_TEXTURE_3D, internalformat, levelWidth, levelHeight, levelDepth, true); + } + + for (int level = levels; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++) + { + mImageArray[level]->redefine(mRenderer, GL_TEXTURE_3D, GL_NONE, 0, 0, 0, true); + } + + mImmutable = true; + + setCompleteTexStorage(new rx::TextureStorageInterface3D(mRenderer, internalformat, IsRenderTargetUsage(mUsage), width, height, depth, levels)); +} + +void Texture3D::generateMipmaps() +{ + // Purge array levels 1 through q and reset them to represent the generated mipmap levels. + int levelCount = mipLevels(); + for (int level = 1; level < levelCount; level++) + { + redefineImage(level, getBaseLevelInternalFormat(), + std::max(getBaseLevelWidth() >> level, 1), + std::max(getBaseLevelHeight() >> level, 1), + std::max(getBaseLevelDepth() >> level, 1)); + } + if (mTexStorage && mTexStorage->isRenderTarget()) { - for (unsigned int f = 0; f < 6; f++) + for (int level = 1; level < levelCount; level++) + { + mTexStorage->generateMipmap(level); + + mImageArray[level]->markClean(); + } + } + else + { + for (int level = 1; level < levelCount; level++) + { + mRenderer->generateMipmap(mImageArray[level], mImageArray[level - 1]); + } + } +} + +const rx::Image *Texture3D::getBaseLevelImage() const +{ + return mImageArray[0]; +} + +rx::TextureStorageInterface *Texture3D::getBaseLevelStorage() +{ + return mTexStorage; +} + +void Texture3D::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source) +{ + // can only make our texture storage to a render target if level 0 is defined (with a width & height) and + // the current level we're copying to is defined (with appropriate format, width & height) + bool canCreateRenderTarget = isLevelComplete(level) && isLevelComplete(0); + + if (!mImageArray[level]->isRenderableFormat() || (!mTexStorage && !canCreateRenderTarget)) + { + mImageArray[level]->copy(xoffset, yoffset, zoffset, x, y, width, height, source); + mDirtyImages = true; + } + else + { + ensureRenderTarget(); + + if (isValidLevel(level)) + { + updateStorageLevel(level); + + gl::Rectangle sourceRect; + sourceRect.x = x; + sourceRect.width = width; + sourceRect.y = y; + sourceRect.height = height; + + GLuint clientVersion = mRenderer->getCurrentClientVersion(); + + mRenderer->copyImage(source, sourceRect, + gl::GetFormat(getBaseLevelInternalFormat(), clientVersion), + xoffset, yoffset, zoffset, mTexStorage, level); + } + } +} + +bool Texture3D::isSamplerComplete(const SamplerState &samplerState) const +{ + GLsizei width = getBaseLevelWidth(); + GLsizei height = getBaseLevelHeight(); + GLsizei depth = getBaseLevelDepth(); + + if (width <= 0 || height <= 0 || depth <= 0) + { + return false; + } + + if (!IsTextureFilteringSupported(getInternalFormat(0), mRenderer)) + { + if (samplerState.magFilter != GL_NEAREST || + (samplerState.minFilter != GL_NEAREST && samplerState.minFilter != GL_NEAREST_MIPMAP_NEAREST)) + { + return false; + } + } + + if (IsMipmapFiltered(samplerState) && !isMipmapComplete()) + { + return false; + } + + return true; +} + +bool Texture3D::isMipmapComplete() const +{ + int levelCount = mipLevels(); + + for (int level = 0; level < levelCount; level++) + { + if (!isLevelComplete(level)) + { + return false; + } + } + + return true; +} + +bool Texture3D::isLevelComplete(int level) const +{ + ASSERT(level >= 0 && level < (int)ArraySize(mImageArray) && mImageArray[level] != NULL); + + if (isImmutable()) + { + return true; + } + + GLsizei width = getBaseLevelWidth(); + GLsizei height = getBaseLevelHeight(); + GLsizei depth = getBaseLevelDepth(); + + if (width <= 0 || height <= 0 || depth <= 0) + { + return false; + } + + if (level == 0) + { + return true; + } + + rx::Image *levelImage = mImageArray[level]; + + if (levelImage->getInternalFormat() != getBaseLevelInternalFormat()) + { + return false; + } + + if (levelImage->getWidth() != std::max(1, width >> level)) + { + return false; + } + + if (levelImage->getHeight() != std::max(1, height >> level)) + { + return false; + } + + if (levelImage->getDepth() != std::max(1, depth >> level)) + { + return false; + } + + return true; +} + +FramebufferAttachment *Texture3D::getAttachment(GLint level, GLint layer) +{ + FramebufferAttachment *attachment = mRenderbufferProxies.get(level, layer); + if (!attachment) + { + attachment = new FramebufferAttachment(mRenderer, id(), new Texture3DAttachment(this, level, layer)); + mRenderbufferProxies.add(level, 0, attachment); + } + + return attachment; +} + +unsigned int Texture3D::getRenderTargetSerial(GLint level, GLint layer) +{ + return (ensureRenderTarget() ? mTexStorage->getRenderTargetSerial(level, layer) : 0); +} + +bool Texture3D::isValidLevel(int level) const +{ + return (mTexStorage ? (level >= 0 && level < mTexStorage->getLevelCount()) : 0); +} + +void Texture3D::initializeStorage(bool renderTarget) +{ + // Only initialize the first time this texture is used as a render target or shader resource + if (mTexStorage) + { + return; + } + + // do not attempt to create storage for nonexistant data + if (!isLevelComplete(0)) + { + return; + } + + bool createRenderTarget = (renderTarget || mUsage == GL_FRAMEBUFFER_ATTACHMENT_ANGLE); + + setCompleteTexStorage(createCompleteStorage(createRenderTarget)); + ASSERT(mTexStorage); + + // flush image data to the storage + updateStorage(); +} + +rx::TextureStorageInterface3D *Texture3D::createCompleteStorage(bool renderTarget) const +{ + GLsizei width = getBaseLevelWidth(); + GLsizei height = getBaseLevelHeight(); + GLsizei depth = getBaseLevelDepth(); + + ASSERT(width > 0 && height > 0 && depth > 0); + + // use existing storage level count, when previously specified by TexStorage*D + GLint levels = (mTexStorage ? mTexStorage->getLevelCount() : creationLevels(width, height, depth)); + + return new rx::TextureStorageInterface3D(mRenderer, getBaseLevelInternalFormat(), renderTarget, width, height, depth, levels); +} + +void Texture3D::setCompleteTexStorage(rx::TextureStorageInterface3D *newCompleteTexStorage) +{ + SafeDelete(mTexStorage); + mTexStorage = newCompleteTexStorage; + mDirtyImages = true; + + // We do not support managed 3D storage, as that is D3D9/ES2-only + ASSERT(!mTexStorage->isManaged()); +} + +void Texture3D::updateStorage() +{ + ASSERT(mTexStorage != NULL); + GLint storageLevels = mTexStorage->getLevelCount(); + for (int level = 0; level < storageLevels; level++) + { + if (mImageArray[level]->isDirty() && isLevelComplete(level)) + { + updateStorageLevel(level); + } + } +} + +void Texture3D::updateStorageLevel(int level) +{ + ASSERT(level >= 0 && level < (int)ArraySize(mImageArray) && mImageArray[level] != NULL); + ASSERT(isLevelComplete(level)); + + if (mImageArray[level]->isDirty()) + { + commitRect(level, 0, 0, 0, getWidth(level), getHeight(level), getDepth(level)); + } +} + +bool Texture3D::ensureRenderTarget() +{ + initializeStorage(true); + + if (getBaseLevelWidth() > 0 && getBaseLevelHeight() > 0 && getBaseLevelDepth() > 0) + { + ASSERT(mTexStorage); + if (!mTexStorage->isRenderTarget()) + { + rx::TextureStorageInterface3D *newRenderTargetStorage = createCompleteStorage(true); + + if (!mRenderer->copyToRenderTarget(newRenderTargetStorage, mTexStorage)) + { + delete newRenderTargetStorage; + return gl::error(GL_OUT_OF_MEMORY, false); + } + + setCompleteTexStorage(newRenderTargetStorage); + } + } + + return (mTexStorage && mTexStorage->isRenderTarget()); +} + +rx::RenderTarget *Texture3D::getRenderTarget(GLint level) +{ + // ensure the underlying texture is created + if (!ensureRenderTarget()) + { + return NULL; + } + + updateStorageLevel(level); + + // ensure this is NOT a depth texture + if (isDepth(level)) + { + return NULL; + } + + return mTexStorage->getRenderTarget(level); +} + +rx::RenderTarget *Texture3D::getRenderTarget(GLint level, GLint layer) +{ + // ensure the underlying texture is created + if (!ensureRenderTarget()) + { + return NULL; + } + + updateStorage(); + + // ensure this is NOT a depth texture + if (isDepth(level)) + { + return NULL; + } + + return mTexStorage->getRenderTarget(level, layer); +} + +rx::RenderTarget *Texture3D::getDepthStencil(GLint level, GLint layer) +{ + // ensure the underlying texture is created + if (!ensureRenderTarget()) + { + return NULL; + } + + updateStorageLevel(level); + + // ensure this is a depth texture + if (!isDepth(level)) + { + return NULL; + } + + return mTexStorage->getRenderTarget(level, layer); +} + +void Texture3D::redefineImage(GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth) +{ + // If there currently is a corresponding storage texture image, it has these parameters + const int storageWidth = std::max(1, getBaseLevelWidth() >> level); + const int storageHeight = std::max(1, getBaseLevelHeight() >> level); + const int storageDepth = std::max(1, getBaseLevelDepth() >> level); + const GLenum storageFormat = getBaseLevelInternalFormat(); + + mImageArray[level]->redefine(mRenderer, GL_TEXTURE_3D, internalformat, width, height, depth, false); + + if (mTexStorage) + { + const int storageLevels = mTexStorage->getLevelCount(); + + if ((level >= storageLevels && storageLevels != 0) || + width != storageWidth || + height != storageHeight || + depth != storageDepth || + internalformat != storageFormat) // Discard mismatched storage + { + for (int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++) + { + mImageArray[i]->markDirty(); + } + + delete mTexStorage; + mTexStorage = NULL; + mDirtyImages = true; + } + } +} + +void Texture3D::commitRect(GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth) +{ + if (isValidLevel(level)) + { + rx::Image *image = mImageArray[level]; + if (image->copyToStorage(mTexStorage, level, xoffset, yoffset, zoffset, width, height, depth)) + { + image->markClean(); + } + } +} + +Texture2DArray::Texture2DArray(rx::Renderer *renderer, GLuint id) : Texture(renderer, id, GL_TEXTURE_2D_ARRAY) +{ + mTexStorage = NULL; + + for (int level = 0; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; ++level) + { + mLayerCounts[level] = 0; + mImageArray[level] = NULL; + } +} + +Texture2DArray::~Texture2DArray() +{ + delete mTexStorage; + mTexStorage = NULL; + + deleteImages(); +} + +void Texture2DArray::deleteImages() +{ + for (int level = 0; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; ++level) + { + for (int layer = 0; layer < mLayerCounts[level]; ++layer) + { + delete mImageArray[level][layer]; + } + delete[] mImageArray[level]; + mImageArray[level] = NULL; + mLayerCounts[level] = 0; + } +} + +GLsizei Texture2DArray::getWidth(GLint level) const +{ + return (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS && mLayerCounts[level] > 0) ? mImageArray[level][0]->getWidth() : 0; +} + +GLsizei Texture2DArray::getHeight(GLint level) const +{ + return (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS && mLayerCounts[level] > 0) ? mImageArray[level][0]->getHeight() : 0; +} + +GLsizei Texture2DArray::getLayers(GLint level) const +{ + return (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS && mLayerCounts[level] > 0) ? mLayerCounts[level] : 0; +} + +GLenum Texture2DArray::getInternalFormat(GLint level) const +{ + return (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS && mLayerCounts[level] > 0) ? mImageArray[level][0]->getInternalFormat() : GL_NONE; +} + +GLenum Texture2DArray::getActualFormat(GLint level) const +{ + return (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS && mLayerCounts[level] > 0) ? mImageArray[level][0]->getActualFormat() : GL_NONE; +} + +bool Texture2DArray::isCompressed(GLint level) const +{ + return IsFormatCompressed(getInternalFormat(level), mRenderer->getCurrentClientVersion()); +} + +bool Texture2DArray::isDepth(GLint level) const +{ + return GetDepthBits(getInternalFormat(level), mRenderer->getCurrentClientVersion()) > 0; +} + +void Texture2DArray::setImage(GLint level, GLsizei width, GLsizei height, GLsizei depth, GLenum internalFormat, GLenum format, GLenum type, const PixelUnpackState &unpack, const void *pixels) +{ + GLuint clientVersion = mRenderer->getCurrentClientVersion(); + GLenum sizedInternalFormat = IsSizedInternalFormat(internalFormat, clientVersion) ? internalFormat + : GetSizedInternalFormat(format, type, clientVersion); + redefineImage(level, sizedInternalFormat, width, height, depth); + + GLsizei inputDepthPitch = gl::GetDepthPitch(sizedInternalFormat, type, clientVersion, width, height, unpack.alignment); + + for (int i = 0; i < depth; i++) + { + const void *layerPixels = pixels ? (reinterpret_cast<const unsigned char*>(pixels) + (inputDepthPitch * i)) : NULL; + Texture::setImage(unpack, type, layerPixels, mImageArray[level][i]); + } +} + +void Texture2DArray::setCompressedImage(GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei depth, GLsizei imageSize, const void *pixels) +{ + // compressed formats don't have separate sized internal formats-- we can just use the compressed format directly + redefineImage(level, format, width, height, depth); + + GLuint clientVersion = mRenderer->getCurrentClientVersion(); + GLsizei inputDepthPitch = gl::GetDepthPitch(format, GL_UNSIGNED_BYTE, clientVersion, width, height, 1); + + for (int i = 0; i < depth; i++) + { + const void *layerPixels = pixels ? (reinterpret_cast<const unsigned char*>(pixels) + (inputDepthPitch * i)) : NULL; + Texture::setCompressedImage(imageSize, layerPixels, mImageArray[level][i]); + } +} + +void Texture2DArray::subImage(GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const PixelUnpackState &unpack, const void *pixels) +{ + GLenum internalformat = getInternalFormat(level); + GLuint clientVersion = mRenderer->getCurrentClientVersion(); + GLsizei inputDepthPitch = gl::GetDepthPitch(internalformat, type, clientVersion, width, height, unpack.alignment); + + for (int i = 0; i < depth; i++) + { + int layer = zoffset + i; + const void *layerPixels = pixels ? (reinterpret_cast<const unsigned char*>(pixels) + (inputDepthPitch * i)) : NULL; + + if (Texture::subImage(xoffset, yoffset, zoffset, width, height, 1, format, type, unpack, layerPixels, mImageArray[level][layer])) { - for (unsigned int i = 1; i <= q; i++) + commitRect(level, xoffset, yoffset, layer, width, height); + } + } +} + +void Texture2DArray::subImageCompressed(GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *pixels) +{ + GLuint clientVersion = mRenderer->getCurrentClientVersion(); + GLsizei inputDepthPitch = gl::GetDepthPitch(format, GL_UNSIGNED_BYTE, clientVersion, width, height, 1); + + for (int i = 0; i < depth; i++) + { + int layer = zoffset + i; + const void *layerPixels = pixels ? (reinterpret_cast<const unsigned char*>(pixels) + (inputDepthPitch * i)) : NULL; + + if (Texture::subImageCompressed(xoffset, yoffset, zoffset, width, height, 1, format, imageSize, layerPixels, mImageArray[level][layer])) + { + commitRect(level, xoffset, yoffset, layer, width, height); + } + } +} + +void Texture2DArray::storage(GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth) +{ + deleteImages(); + + for (int level = 0; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++) + { + GLsizei levelWidth = std::max(1, width >> level); + GLsizei levelHeight = std::max(1, height >> level); + + mLayerCounts[level] = (level < levels ? depth : 0); + + if (mLayerCounts[level] > 0) + { + // Create new images for this level + mImageArray[level] = new rx::Image*[mLayerCounts[level]]; + + for (int layer = 0; layer < mLayerCounts[level]; layer++) { - mTexStorage->generateMipmap(f, i); + mImageArray[level][layer] = mRenderer->createImage(); + mImageArray[level][layer]->redefine(mRenderer, GL_TEXTURE_2D_ARRAY, internalformat, levelWidth, + levelHeight, 1, true); + } + } + } + + mImmutable = true; + setCompleteTexStorage(new rx::TextureStorageInterface2DArray(mRenderer, internalformat, IsRenderTargetUsage(mUsage), width, height, depth, levels)); +} + +void Texture2DArray::generateMipmaps() +{ + int baseWidth = getBaseLevelWidth(); + int baseHeight = getBaseLevelHeight(); + int baseDepth = getBaseLevelDepth(); + GLenum baseFormat = getBaseLevelInternalFormat(); + + // Purge array levels 1 through q and reset them to represent the generated mipmap levels. + int levelCount = mipLevels(); + for (int level = 1; level < levelCount; level++) + { + redefineImage(level, baseFormat, std::max(baseWidth >> level, 1), std::max(baseHeight >> level, 1), baseDepth); + } + + if (mTexStorage && mTexStorage->isRenderTarget()) + { + for (int level = 1; level < levelCount; level++) + { + mTexStorage->generateMipmap(level); - mImageArray[f][i]->markClean(); + for (int layer = 0; layer < mLayerCounts[level]; layer++) + { + mImageArray[level][layer]->markClean(); } } } else { - for (unsigned int f = 0; f < 6; f++) + for (int level = 1; level < levelCount; level++) { - for (unsigned int i = 1; i <= q; i++) + for (int layer = 0; layer < mLayerCounts[level]; layer++) { - mRenderer->generateMipmap(mImageArray[f][i], mImageArray[f][i - 1]); + mRenderer->generateMipmap(mImageArray[level][layer], mImageArray[level - 1][layer]); } } } } -Renderbuffer *TextureCubeMap::getRenderbuffer(GLenum target) +const rx::Image *Texture2DArray::getBaseLevelImage() const +{ + return (mLayerCounts[0] > 0 ? mImageArray[0][0] : NULL); +} + +rx::TextureStorageInterface *Texture2DArray::getBaseLevelStorage() +{ + return mTexStorage; +} + +void Texture2DArray::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source) +{ + // can only make our texture storage to a render target if level 0 is defined (with a width & height) and + // the current level we're copying to is defined (with appropriate format, width & height) + bool canCreateRenderTarget = isLevelComplete(level) && isLevelComplete(0); + + if (!mImageArray[level][0]->isRenderableFormat() || (!mTexStorage && !canCreateRenderTarget)) + { + mImageArray[level][zoffset]->copy(xoffset, yoffset, 0, x, y, width, height, source); + mDirtyImages = true; + } + else + { + ensureRenderTarget(); + + if (isValidLevel(level)) + { + updateStorageLevel(level); + + GLuint clientVersion = mRenderer->getCurrentClientVersion(); + + gl::Rectangle sourceRect; + sourceRect.x = x; + sourceRect.width = width; + sourceRect.y = y; + sourceRect.height = height; + + mRenderer->copyImage(source, sourceRect, gl::GetFormat(getInternalFormat(0), clientVersion), + xoffset, yoffset, zoffset, mTexStorage, level); + } + } +} + +bool Texture2DArray::isSamplerComplete(const SamplerState &samplerState) const { - if (!IsCubemapTextureTarget(target)) + GLsizei width = getBaseLevelWidth(); + GLsizei height = getBaseLevelHeight(); + GLsizei depth = getLayers(0); + + if (width <= 0 || height <= 0 || depth <= 0) { - return gl::error(GL_INVALID_OPERATION, (Renderbuffer *)NULL); + return false; } - unsigned int face = faceIndex(target); + if (!IsTextureFilteringSupported(getBaseLevelInternalFormat(), mRenderer)) + { + if (samplerState.magFilter != GL_NEAREST || + (samplerState.minFilter != GL_NEAREST && samplerState.minFilter != GL_NEAREST_MIPMAP_NEAREST)) + { + return false; + } + } - if (mFaceProxies[face] == NULL) + if (IsMipmapFiltered(samplerState) && !isMipmapComplete()) { - mFaceProxies[face] = new Renderbuffer(mRenderer, id(), new RenderbufferTextureCubeMap(this, target)); + return false; } - return mFaceProxies[face]; + return true; } -rx::RenderTarget *TextureCubeMap::getRenderTarget(GLenum target) +bool Texture2DArray::isMipmapComplete() const { - ASSERT(IsCubemapTextureTarget(target)); + int levelCount = mipLevels(); + + for (int level = 1; level < levelCount; level++) + { + if (!isLevelComplete(level)) + { + return false; + } + } + + return true; +} + +bool Texture2DArray::isLevelComplete(int level) const +{ + ASSERT(level >= 0 && level < (int)ArraySize(mImageArray)); + + if (isImmutable()) + { + return true; + } + + GLsizei width = getBaseLevelWidth(); + GLsizei height = getBaseLevelHeight(); + GLsizei layers = getLayers(0); + if (width <= 0 || height <= 0 || layers <= 0) + { + return false; + } + + if (level == 0) + { + return true; + } + + if (getInternalFormat(level) != getInternalFormat(0)) + { + return false; + } + + if (getWidth(level) != std::max(1, width >> level)) + { + return false; + } + + if (getHeight(level) != std::max(1, height >> level)) + { + return false; + } + + if (getLayers(level) != layers) + { + return false; + } + + return true; +} + +FramebufferAttachment *Texture2DArray::getAttachment(GLint level, GLint layer) +{ + FramebufferAttachment *attachment = mRenderbufferProxies.get(level, layer); + if (!attachment) + { + attachment = new FramebufferAttachment(mRenderer, id(), new Texture2DArrayAttachment(this, level, layer)); + mRenderbufferProxies.add(level, 0, attachment); + } + + return attachment; +} + +unsigned int Texture2DArray::getRenderTargetSerial(GLint level, GLint layer) +{ + return (ensureRenderTarget() ? mTexStorage->getRenderTargetSerial(level, layer) : 0); +} + +bool Texture2DArray::isValidLevel(int level) const +{ + return (mTexStorage ? (level >= 0 && level < mTexStorage->getLevelCount()) : 0); +} + +void Texture2DArray::initializeStorage(bool renderTarget) +{ + // Only initialize the first time this texture is used as a render target or shader resource + if (mTexStorage) + { + return; + } + + // do not attempt to create storage for nonexistant data + if (!isLevelComplete(0)) + { + return; + } + + bool createRenderTarget = (renderTarget || mUsage == GL_FRAMEBUFFER_ATTACHMENT_ANGLE); + + setCompleteTexStorage(createCompleteStorage(createRenderTarget)); + ASSERT(mTexStorage); + + // flush image data to the storage + updateStorage(); +} + +rx::TextureStorageInterface2DArray *Texture2DArray::createCompleteStorage(bool renderTarget) const +{ + GLsizei width = getBaseLevelWidth(); + GLsizei height = getBaseLevelHeight(); + GLsizei depth = getLayers(0); + + ASSERT(width > 0 && height > 0 && depth > 0); + + // use existing storage level count, when previously specified by TexStorage*D + GLint levels = (mTexStorage ? mTexStorage->getLevelCount() : creationLevels(width, height, 1)); + + return new rx::TextureStorageInterface2DArray(mRenderer, getBaseLevelInternalFormat(), renderTarget, width, height, depth, levels); +} + +void Texture2DArray::setCompleteTexStorage(rx::TextureStorageInterface2DArray *newCompleteTexStorage) +{ + SafeDelete(mTexStorage); + mTexStorage = newCompleteTexStorage; + mDirtyImages = true; + + // We do not support managed 2D array storage, as managed storage is ES2/D3D9 only + ASSERT(!mTexStorage->isManaged()); +} + +void Texture2DArray::updateStorage() +{ + ASSERT(mTexStorage != NULL); + GLint storageLevels = mTexStorage->getLevelCount(); + for (int level = 0; level < storageLevels; level++) + { + if (isLevelComplete(level)) + { + updateStorageLevel(level); + } + } +} + +void Texture2DArray::updateStorageLevel(int level) +{ + ASSERT(level >= 0 && level < (int)ArraySize(mLayerCounts)); + ASSERT(isLevelComplete(level)); + + for (int layer = 0; layer < mLayerCounts[level]; layer++) + { + ASSERT(mImageArray[level] != NULL && mImageArray[level][layer] != NULL); + if (mImageArray[level][layer]->isDirty()) + { + commitRect(level, 0, 0, layer, getWidth(level), getHeight(level)); + } + } +} + +bool Texture2DArray::ensureRenderTarget() +{ + initializeStorage(true); + + if (getBaseLevelWidth() > 0 && getBaseLevelHeight() > 0 && getLayers(0) > 0) + { + ASSERT(mTexStorage); + if (!mTexStorage->isRenderTarget()) + { + rx::TextureStorageInterface2DArray *newRenderTargetStorage = createCompleteStorage(true); + + if (!mRenderer->copyToRenderTarget(newRenderTargetStorage, mTexStorage)) + { + delete newRenderTargetStorage; + return gl::error(GL_OUT_OF_MEMORY, false); + } + + setCompleteTexStorage(newRenderTargetStorage); + } + } + + return (mTexStorage && mTexStorage->isRenderTarget()); +} + +rx::RenderTarget *Texture2DArray::getRenderTarget(GLint level, GLint layer) +{ // ensure the underlying texture is created - if (getStorage(true) == NULL) + if (!ensureRenderTarget()) { return NULL; } - updateTexture(); - - return mTexStorage->getRenderTarget(target); + updateStorageLevel(level); + + // ensure this is NOT a depth texture + if (isDepth(level)) + { + return NULL; + } + + return mTexStorage->getRenderTarget(level, layer); } -int TextureCubeMap::levelCount() +rx::RenderTarget *Texture2DArray::getDepthStencil(GLint level, GLint layer) { - return mTexStorage ? mTexStorage->levelCount() - getLodOffset() : 0; + // ensure the underlying texture is created + if (!ensureRenderTarget()) + { + return NULL; + } + + updateStorageLevel(level); + + // ensure this is a depth texture + if (!isDepth(level)) + { + return NULL; + } + + return mTexStorage->getRenderTarget(level, layer); } -rx::TextureStorageInterface *TextureCubeMap::getStorage(bool renderTarget) +void Texture2DArray::redefineImage(GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth) { - if (!mTexStorage || (renderTarget && !mTexStorage->isRenderTarget())) + // If there currently is a corresponding storage texture image, it has these parameters + const int storageWidth = std::max(1, getBaseLevelWidth() >> level); + const int storageHeight = std::max(1, getBaseLevelHeight() >> level); + const int storageDepth = getLayers(0); + const GLenum storageFormat = getBaseLevelInternalFormat(); + + for (int layer = 0; layer < mLayerCounts[level]; layer++) { - if (renderTarget) + delete mImageArray[level][layer]; + } + delete[] mImageArray[level]; + mImageArray[level] = NULL; + mLayerCounts[level] = depth; + + if (depth > 0) + { + mImageArray[level] = new rx::Image*[depth](); + + for (int layer = 0; layer < mLayerCounts[level]; layer++) { - convertToRenderTarget(); + mImageArray[level][layer] = mRenderer->createImage(); + mImageArray[level][layer]->redefine(mRenderer, GL_TEXTURE_2D_ARRAY, internalformat, width, height, 1, false); } - else + } + + if (mTexStorage) + { + const int storageLevels = mTexStorage->getLevelCount(); + + if ((level >= storageLevels && storageLevels != 0) || + width != storageWidth || + height != storageHeight || + depth != storageDepth || + internalformat != storageFormat) // Discard mismatched storage { - createTexture(); + for (int level = 0; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++) + { + for (int layer = 0; layer < mLayerCounts[level]; layer++) + { + mImageArray[level][layer]->markDirty(); + } + } + + delete mTexStorage; + mTexStorage = NULL; + mDirtyImages = true; } } +} - return mTexStorage; +void Texture2DArray::commitRect(GLint level, GLint xoffset, GLint yoffset, GLint layerTarget, GLsizei width, GLsizei height) +{ + if (isValidLevel(level) && layerTarget < getLayers(level)) + { + rx::Image *image = mImageArray[level][layerTarget]; + if (image->copyToStorage(mTexStorage, level, xoffset, yoffset, layerTarget, width, height)) + { + image->markClean(); + } + } } } |