| // Copyright 2016 The SwiftShader Authors. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| // Texture.cpp: Implements the Texture class and its derived classes |
| // Texture2D, TextureCubeMap, Texture3D and Texture2DArray. Implements GL texture objects |
| // and related functionality. [OpenGL ES 2.0.24] section 3.7 page 63. |
| |
| #include "Texture.h" |
| |
| #include "main.h" |
| #include "mathutil.h" |
| #include "Framebuffer.h" |
| #include "Device.hpp" |
| #include "libEGL/Display.h" |
| #include "common/Surface.hpp" |
| #include "common/debug.h" |
| |
| #include <algorithm> |
| |
| namespace es2 |
| { |
| |
| Texture::Texture(GLuint name) : egl::Texture(name) |
| { |
| mMinFilter = GL_NEAREST_MIPMAP_LINEAR; |
| mMagFilter = GL_LINEAR; |
| mWrapS = GL_REPEAT; |
| mWrapT = GL_REPEAT; |
| mWrapR = GL_REPEAT; |
| mMaxAnisotropy = 1.0f; |
| mBaseLevel = 0; |
| mCompareFunc = GL_LEQUAL; |
| mCompareMode = GL_NONE; |
| mImmutableFormat = GL_FALSE; |
| mImmutableLevels = 0; |
| mMaxLevel = 1000; |
| mMaxLOD = 1000; |
| mMinLOD = -1000; |
| mSwizzleR = GL_RED; |
| mSwizzleG = GL_GREEN; |
| mSwizzleB = GL_BLUE; |
| mSwizzleA = GL_ALPHA; |
| |
| resource = new sw::Resource(0); |
| } |
| |
| Texture::~Texture() |
| { |
| resource->destruct(); |
| } |
| |
| sw::Resource *Texture::getResource() const |
| { |
| return resource; |
| } |
| |
| // Returns true on successful filter state update (valid enum parameter) |
| bool Texture::setMinFilter(GLenum filter) |
| { |
| switch(filter) |
| { |
| case GL_NEAREST_MIPMAP_NEAREST: |
| case GL_LINEAR_MIPMAP_NEAREST: |
| case GL_NEAREST_MIPMAP_LINEAR: |
| case GL_LINEAR_MIPMAP_LINEAR: |
| if(getTarget() == GL_TEXTURE_EXTERNAL_OES) |
| { |
| return false; |
| } |
| // Fall through |
| case GL_NEAREST: |
| case GL_LINEAR: |
| mMinFilter = filter; |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| // Returns true on successful filter state update (valid enum parameter) |
| bool Texture::setMagFilter(GLenum filter) |
| { |
| switch(filter) |
| { |
| case GL_NEAREST: |
| case GL_LINEAR: |
| mMagFilter = filter; |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| // Returns true on successful wrap state update (valid enum parameter) |
| bool Texture::setWrapS(GLenum wrap) |
| { |
| switch(wrap) |
| { |
| case GL_REPEAT: |
| case GL_MIRRORED_REPEAT: |
| if(getTarget() == GL_TEXTURE_EXTERNAL_OES) |
| { |
| return false; |
| } |
| // Fall through |
| case GL_CLAMP_TO_EDGE: |
| mWrapS = wrap; |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| // Returns true on successful wrap state update (valid enum parameter) |
| bool Texture::setWrapT(GLenum wrap) |
| { |
| switch(wrap) |
| { |
| case GL_REPEAT: |
| case GL_MIRRORED_REPEAT: |
| if(getTarget() == GL_TEXTURE_EXTERNAL_OES) |
| { |
| return false; |
| } |
| // Fall through |
| case GL_CLAMP_TO_EDGE: |
| mWrapT = wrap; |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| // Returns true on successful wrap state update (valid enum parameter) |
| bool Texture::setWrapR(GLenum wrap) |
| { |
| switch(wrap) |
| { |
| case GL_REPEAT: |
| case GL_MIRRORED_REPEAT: |
| if(getTarget() == GL_TEXTURE_EXTERNAL_OES) |
| { |
| return false; |
| } |
| // Fall through |
| case GL_CLAMP_TO_EDGE: |
| mWrapR = wrap; |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| // Returns true on successful max anisotropy update (valid anisotropy value) |
| bool Texture::setMaxAnisotropy(float textureMaxAnisotropy) |
| { |
| textureMaxAnisotropy = std::min(textureMaxAnisotropy, MAX_TEXTURE_MAX_ANISOTROPY); |
| |
| if(textureMaxAnisotropy < 1.0f) |
| { |
| return false; |
| } |
| |
| if(mMaxAnisotropy != textureMaxAnisotropy) |
| { |
| mMaxAnisotropy = textureMaxAnisotropy; |
| } |
| |
| return true; |
| } |
| |
| bool Texture::setBaseLevel(GLint baseLevel) |
| { |
| mBaseLevel = baseLevel; |
| return true; |
| } |
| |
| bool Texture::setCompareFunc(GLenum compareFunc) |
| { |
| switch(compareFunc) |
| { |
| case GL_LEQUAL: |
| case GL_GEQUAL: |
| case GL_LESS: |
| case GL_GREATER: |
| case GL_EQUAL: |
| case GL_NOTEQUAL: |
| case GL_ALWAYS: |
| case GL_NEVER: |
| mCompareFunc = compareFunc; |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| bool Texture::setCompareMode(GLenum compareMode) |
| { |
| switch(compareMode) |
| { |
| case GL_COMPARE_REF_TO_TEXTURE: |
| case GL_NONE: |
| mCompareMode = compareMode; |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| void Texture::makeImmutable(GLsizei levels) |
| { |
| mImmutableFormat = GL_TRUE; |
| mImmutableLevels = levels; |
| } |
| |
| bool Texture::setMaxLevel(GLint maxLevel) |
| { |
| mMaxLevel = maxLevel; |
| return true; |
| } |
| |
| bool Texture::setMaxLOD(GLfloat maxLOD) |
| { |
| mMaxLOD = maxLOD; |
| return true; |
| } |
| |
| bool Texture::setMinLOD(GLfloat minLOD) |
| { |
| mMinLOD = minLOD; |
| return true; |
| } |
| |
| bool Texture::setSwizzleR(GLenum swizzleR) |
| { |
| switch(swizzleR) |
| { |
| case GL_RED: |
| case GL_GREEN: |
| case GL_BLUE: |
| case GL_ALPHA: |
| case GL_ZERO: |
| case GL_ONE: |
| mSwizzleR = swizzleR; |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| bool Texture::setSwizzleG(GLenum swizzleG) |
| { |
| switch(swizzleG) |
| { |
| case GL_RED: |
| case GL_GREEN: |
| case GL_BLUE: |
| case GL_ALPHA: |
| case GL_ZERO: |
| case GL_ONE: |
| mSwizzleG = swizzleG; |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| bool Texture::setSwizzleB(GLenum swizzleB) |
| { |
| switch(swizzleB) |
| { |
| case GL_RED: |
| case GL_GREEN: |
| case GL_BLUE: |
| case GL_ALPHA: |
| case GL_ZERO: |
| case GL_ONE: |
| mSwizzleB = swizzleB; |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| bool Texture::setSwizzleA(GLenum swizzleA) |
| { |
| switch(swizzleA) |
| { |
| case GL_RED: |
| case GL_GREEN: |
| case GL_BLUE: |
| case GL_ALPHA: |
| case GL_ZERO: |
| case GL_ONE: |
| mSwizzleA = swizzleA; |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| GLenum Texture::getMinFilter() const |
| { |
| return mMinFilter; |
| } |
| |
| GLenum Texture::getMagFilter() const |
| { |
| return mMagFilter; |
| } |
| |
| GLenum Texture::getWrapS() const |
| { |
| return mWrapS; |
| } |
| |
| GLenum Texture::getWrapT() const |
| { |
| return mWrapT; |
| } |
| |
| GLenum Texture::getWrapR() const |
| { |
| return mWrapR; |
| } |
| |
| GLfloat Texture::getMaxAnisotropy() const |
| { |
| return mMaxAnisotropy; |
| } |
| |
| GLint Texture::getBaseLevel() const |
| { |
| return mBaseLevel; |
| } |
| GLenum Texture::getCompareFunc() const |
| { |
| return mCompareFunc; |
| } |
| GLenum Texture::getCompareMode() const |
| { |
| return mCompareMode; |
| } |
| GLboolean Texture::getImmutableFormat() const |
| { |
| return mImmutableFormat; |
| } |
| GLsizei Texture::getImmutableLevels() const |
| { |
| return mImmutableLevels; |
| } |
| GLint Texture::getMaxLevel() const |
| { |
| return mMaxLevel; |
| } |
| GLfloat Texture::getMaxLOD() const |
| { |
| return mMaxLOD; |
| } |
| GLfloat Texture::getMinLOD() const |
| { |
| return mMinLOD; |
| } |
| GLenum Texture::getSwizzleR() const |
| { |
| return mSwizzleR; |
| } |
| GLenum Texture::getSwizzleG() const |
| { |
| return mSwizzleG; |
| } |
| GLenum Texture::getSwizzleB() const |
| { |
| return mSwizzleB; |
| } |
| GLenum Texture::getSwizzleA() const |
| { |
| return mSwizzleA; |
| } |
| |
| GLsizei Texture::getDepth(GLenum target, GLint level) const |
| { |
| return 1; |
| } |
| |
| egl::Image *Texture::createSharedImage(GLenum target, unsigned int level) |
| { |
| egl::Image *image = getRenderTarget(target, level); // Increments reference count |
| |
| if(image) |
| { |
| image->markShared(); |
| } |
| |
| return image; |
| } |
| |
| void Texture::setImage(GLenum format, GLenum type, const egl::Image::UnpackInfo& unpackInfo, const void *pixels, egl::Image *image) |
| { |
| if(pixels && image) |
| { |
| GLsizei depth = (getTarget() == GL_TEXTURE_3D_OES || getTarget() == GL_TEXTURE_2D_ARRAY) ? image->getDepth() : 1; |
| image->loadImageData(0, 0, 0, image->getWidth(), image->getHeight(), depth, format, type, unpackInfo, pixels); |
| } |
| } |
| |
| void Texture::setCompressedImage(GLsizei imageSize, const void *pixels, egl::Image *image) |
| { |
| if(pixels && image) |
| { |
| GLsizei depth = (getTarget() == GL_TEXTURE_3D_OES || getTarget() == GL_TEXTURE_2D_ARRAY) ? image->getDepth() : 1; |
| image->loadCompressedData(0, 0, 0, image->getWidth(), image->getHeight(), depth, imageSize, pixels); |
| } |
| } |
| |
| void Texture::subImage(GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const egl::Image::UnpackInfo& unpackInfo, const void *pixels, egl::Image *image) |
| { |
| if(!image) |
| { |
| return error(GL_INVALID_OPERATION); |
| } |
| |
| if(width + xoffset > image->getWidth() || height + yoffset > image->getHeight() || depth + zoffset > image->getDepth()) |
| { |
| return error(GL_INVALID_VALUE); |
| } |
| |
| if(IsCompressed(image->getFormat(), egl::getClientVersion())) |
| { |
| return error(GL_INVALID_OPERATION); |
| } |
| |
| if(format != image->getFormat()) |
| { |
| return error(GL_INVALID_OPERATION); |
| } |
| |
| if(pixels) |
| { |
| image->loadImageData(xoffset, yoffset, zoffset, width, height, depth, format, type, unpackInfo, pixels); |
| } |
| } |
| |
| void Texture::subImageCompressed(GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *pixels, egl::Image *image) |
| { |
| if(!image) |
| { |
| return error(GL_INVALID_OPERATION); |
| } |
| |
| if(width + xoffset > image->getWidth() || height + yoffset > image->getHeight() || depth + zoffset > image->getDepth()) |
| { |
| return error(GL_INVALID_VALUE); |
| } |
| |
| if(format != image->getFormat()) |
| { |
| return error(GL_INVALID_OPERATION); |
| } |
| |
| if(pixels) |
| { |
| image->loadCompressedData(xoffset, yoffset, zoffset, width, height, depth, imageSize, pixels); |
| } |
| } |
| |
| bool Texture::copy(egl::Image *source, const sw::SliceRect &sourceRect, GLenum destFormat, GLint xoffset, GLint yoffset, GLint zoffset, egl::Image *dest) |
| { |
| Device *device = getDevice(); |
| |
| sw::SliceRect destRect(xoffset, yoffset, xoffset + (sourceRect.x1 - sourceRect.x0), yoffset + (sourceRect.y1 - sourceRect.y0), zoffset); |
| bool success = device->stretchRect(source, &sourceRect, dest, &destRect, Device::ALL_BUFFERS); |
| |
| if(!success) |
| { |
| return error(GL_OUT_OF_MEMORY, false); |
| } |
| |
| return true; |
| } |
| |
| bool Texture::isMipmapFiltered() const |
| { |
| switch(mMinFilter) |
| { |
| 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(mMinFilter); |
| } |
| |
| return false; |
| } |
| |
| Texture2D::Texture2D(GLuint name) : Texture(name) |
| { |
| for(int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++) |
| { |
| image[i] = nullptr; |
| } |
| |
| mSurface = nullptr; |
| |
| mColorbufferProxy = nullptr; |
| mProxyRefs = 0; |
| } |
| |
| Texture2D::~Texture2D() |
| { |
| for(int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++) |
| { |
| if(image[i]) |
| { |
| image[i]->unbind(this); |
| image[i] = nullptr; |
| } |
| } |
| |
| if(mSurface) |
| { |
| mSurface->setBoundTexture(nullptr); |
| mSurface = nullptr; |
| } |
| |
| mColorbufferProxy = nullptr; |
| } |
| |
| // 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 = nullptr; |
| } |
| } |
| |
| void Texture2D::sweep() |
| { |
| int imageCount = 0; |
| |
| for(int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++) |
| { |
| if(image[i] && image[i]->isChildOf(this)) |
| { |
| if(!image[i]->hasSingleReference()) |
| { |
| return; |
| } |
| |
| imageCount++; |
| } |
| } |
| |
| if(imageCount == referenceCount) |
| { |
| destroy(); |
| } |
| } |
| |
| GLenum Texture2D::getTarget() const |
| { |
| return GL_TEXTURE_2D; |
| } |
| |
| GLsizei Texture2D::getWidth(GLenum target, GLint level) const |
| { |
| ASSERT(target == GL_TEXTURE_2D); |
| return image[level] ? image[level]->getWidth() : 0; |
| } |
| |
| GLsizei Texture2D::getHeight(GLenum target, GLint level) const |
| { |
| ASSERT(target == GL_TEXTURE_2D); |
| return image[level] ? image[level]->getHeight() : 0; |
| } |
| |
| GLenum Texture2D::getFormat(GLenum target, GLint level) const |
| { |
| ASSERT(target == GL_TEXTURE_2D); |
| return image[level] ? image[level]->getFormat() : GL_NONE; |
| } |
| |
| GLenum Texture2D::getType(GLenum target, GLint level) const |
| { |
| ASSERT(target == GL_TEXTURE_2D); |
| return image[level] ? image[level]->getType() : GL_NONE; |
| } |
| |
| sw::Format Texture2D::getInternalFormat(GLenum target, GLint level) const |
| { |
| ASSERT(target == GL_TEXTURE_2D); |
| return image[level] ? image[level]->getInternalFormat() : sw::FORMAT_NULL; |
| } |
| |
| int Texture2D::getLevelCount() const |
| { |
| ASSERT(isSamplerComplete()); |
| int levels = 0; |
| |
| while(levels < IMPLEMENTATION_MAX_TEXTURE_LEVELS && image[levels]) |
| { |
| levels++; |
| } |
| |
| return levels; |
| } |
| |
| void Texture2D::setImage(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, const egl::Image::UnpackInfo& unpackInfo, const void *pixels) |
| { |
| if(image[level]) |
| { |
| image[level]->release(); |
| } |
| |
| image[level] = egl::Image::create(this, width, height, format, type); |
| |
| if(!image[level]) |
| { |
| return error(GL_OUT_OF_MEMORY); |
| } |
| |
| Texture::setImage(format, type, unpackInfo, pixels, image[level]); |
| } |
| |
| void Texture2D::bindTexImage(gl::Surface *surface) |
| { |
| GLenum format; |
| |
| switch(surface->getInternalFormat()) |
| { |
| case sw::FORMAT_A8R8G8B8: |
| case sw::FORMAT_SRGB8_A8: |
| format = GL_BGRA_EXT; |
| break; |
| case sw::FORMAT_A8B8G8R8: |
| format = GL_RGBA; |
| break; |
| case sw::FORMAT_X8B8G8R8: |
| case sw::FORMAT_X8R8G8B8: |
| case sw::FORMAT_SRGB8_X8: |
| format = GL_RGB; |
| break; |
| default: |
| UNIMPLEMENTED(); |
| return; |
| } |
| |
| for(int level = 0; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++) |
| { |
| if(image[level]) |
| { |
| image[level]->release(); |
| image[level] = nullptr; |
| } |
| } |
| |
| image[0] = surface->getRenderTarget(); |
| |
| mSurface = surface; |
| mSurface->setBoundTexture(this); |
| } |
| |
| void Texture2D::releaseTexImage() |
| { |
| for(int level = 0; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++) |
| { |
| if(image[level]) |
| { |
| image[level]->release(); |
| image[level] = nullptr; |
| } |
| } |
| } |
| |
| void Texture2D::setCompressedImage(GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels) |
| { |
| if(image[level]) |
| { |
| image[level]->release(); |
| } |
| |
| GLenum sizedInternalFormat = GetSizedInternalFormat(format, GL_UNSIGNED_BYTE); |
| image[level] = egl::Image::create(this, width, height, sizedInternalFormat, GL_UNSIGNED_BYTE); |
| |
| if(!image[level]) |
| { |
| return error(GL_OUT_OF_MEMORY); |
| } |
| |
| Texture::setCompressedImage(imageSize, pixels, image[level]); |
| } |
| |
| void Texture2D::subImage(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const egl::Image::UnpackInfo& unpackInfo, const void *pixels) |
| { |
| Texture::subImage(xoffset, yoffset, 0, width, height, 1, format, type, unpackInfo, pixels, image[level]); |
| } |
| |
| void Texture2D::subImageCompressed(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels) |
| { |
| Texture::subImageCompressed(xoffset, yoffset, 0, width, height, 1, format, imageSize, pixels, image[level]); |
| } |
| |
| void Texture2D::copyImage(GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source) |
| { |
| egl::Image *renderTarget = source->getRenderTarget(0); |
| |
| if(!renderTarget) |
| { |
| ERR("Failed to retrieve the render target."); |
| return error(GL_OUT_OF_MEMORY); |
| } |
| |
| if(image[level]) |
| { |
| image[level]->release(); |
| } |
| |
| GLenum sizedInternalFormat = GetSizedInternalFormat(format, GL_UNSIGNED_BYTE); |
| image[level] = egl::Image::create(this, width, height, sizedInternalFormat, GL_UNSIGNED_BYTE); |
| |
| if(!image[level]) |
| { |
| return error(GL_OUT_OF_MEMORY); |
| } |
| |
| if(width != 0 && height != 0) |
| { |
| Renderbuffer* renderbuffer = source->getReadColorbuffer(); |
| |
| if(!renderbuffer) |
| { |
| ERR("Failed to retrieve the source colorbuffer."); |
| return; |
| } |
| |
| sw::SliceRect sourceRect(x, y, x + width, y + height, 0); |
| sourceRect.clip(0, 0, renderbuffer->getWidth(), renderbuffer->getHeight()); |
| |
| copy(renderTarget, sourceRect, sizedInternalFormat, 0, 0, 0, image[level]); |
| } |
| |
| renderTarget->release(); |
| } |
| |
| void Texture2D::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source) |
| { |
| if(!image[level]) |
| { |
| return error(GL_INVALID_OPERATION); |
| } |
| |
| if(xoffset + width > image[level]->getWidth() || yoffset + height > image[level]->getHeight() || zoffset != 0) |
| { |
| return error(GL_INVALID_VALUE); |
| } |
| |
| egl::Image *renderTarget = source->getRenderTarget(0); |
| |
| if(!renderTarget) |
| { |
| ERR("Failed to retrieve the render target."); |
| return error(GL_OUT_OF_MEMORY); |
| } |
| |
| Renderbuffer* renderbuffer = source->getReadColorbuffer(); |
| |
| if(!renderbuffer) |
| { |
| ERR("Failed to retrieve the source colorbuffer."); |
| return; |
| } |
| |
| sw::SliceRect sourceRect(x, y, x + width, y + height, 0); |
| sourceRect.clip(0, 0, renderbuffer->getWidth(), renderbuffer->getHeight()); |
| |
| copy(renderTarget, sourceRect, image[level]->getFormat(), xoffset, yoffset, zoffset, image[level]); |
| |
| renderTarget->release(); |
| } |
| |
| void Texture2D::setSharedImage(egl::Image *sharedImage) |
| { |
| if(sharedImage == image[0]) |
| { |
| return; |
| } |
| |
| sharedImage->addRef(); |
| |
| if(image[0]) |
| { |
| image[0]->release(); |
| } |
| |
| image[0] = sharedImage; |
| } |
| |
| // Tests for 2D texture sampling completeness. [OpenGL ES 2.0.24] section 3.8.2 page 85. |
| bool Texture2D::isSamplerComplete() const |
| { |
| if(!image[0]) |
| { |
| return false; |
| } |
| |
| GLsizei width = image[0]->getWidth(); |
| GLsizei height = image[0]->getHeight(); |
| |
| if(width <= 0 || height <= 0) |
| { |
| return false; |
| } |
| |
| if(isMipmapFiltered()) |
| { |
| if(!isMipmapComplete()) |
| { |
| 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 |
| { |
| GLsizei width = image[mBaseLevel]->getWidth(); |
| GLsizei height = image[mBaseLevel]->getHeight(); |
| |
| int q = std::min(log2(std::max(width, height)), mMaxLevel); |
| |
| for(int level = mBaseLevel + 1; level <= q; level++) |
| { |
| if(!image[level]) |
| { |
| return false; |
| } |
| |
| if(image[level]->getFormat() != image[0]->getFormat()) |
| { |
| return false; |
| } |
| |
| if(image[level]->getType() != image[0]->getType()) |
| { |
| return false; |
| } |
| |
| if(image[level]->getWidth() != std::max(1, width >> level)) |
| { |
| return false; |
| } |
| |
| if(image[level]->getHeight() != std::max(1, height >> level)) |
| { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| bool Texture2D::isCompressed(GLenum target, GLint level) const |
| { |
| return IsCompressed(getFormat(target, level), egl::getClientVersion()); |
| } |
| |
| bool Texture2D::isDepth(GLenum target, GLint level) const |
| { |
| return IsDepthTexture(getFormat(target, level)); |
| } |
| |
| void Texture2D::generateMipmaps() |
| { |
| if(!image[0]) |
| { |
| return; // FIXME: error? |
| } |
| |
| unsigned int q = log2(std::max(image[0]->getWidth(), image[0]->getHeight())); |
| |
| for(unsigned int i = 1; i <= q; i++) |
| { |
| if(image[i]) |
| { |
| image[i]->release(); |
| } |
| |
| image[i] = egl::Image::create(this, std::max(image[0]->getWidth() >> i, 1), std::max(image[0]->getHeight() >> i, 1), image[0]->getFormat(), image[0]->getType()); |
| |
| if(!image[i]) |
| { |
| return error(GL_OUT_OF_MEMORY); |
| } |
| |
| getDevice()->stretchRect(image[i - 1], 0, image[i], 0, Device::ALL_BUFFERS | Device::USE_FILTER); |
| } |
| } |
| |
| egl::Image *Texture2D::getImage(unsigned int level) |
| { |
| return image[level]; |
| } |
| |
| Renderbuffer *Texture2D::getRenderbuffer(GLenum target, GLint level, GLint layer) |
| { |
| if(target != GL_TEXTURE_2D) |
| { |
| return error(GL_INVALID_OPERATION, (Renderbuffer*)nullptr); |
| } |
| |
| if(!mColorbufferProxy) |
| { |
| mColorbufferProxy = new Renderbuffer(name, new RenderbufferTexture2D(this, level)); |
| } |
| else |
| { |
| mColorbufferProxy->setLevel(level); |
| } |
| |
| return mColorbufferProxy; |
| } |
| |
| egl::Image *Texture2D::getRenderTarget(GLenum target, unsigned int level) |
| { |
| ASSERT(target == GL_TEXTURE_2D); |
| ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS); |
| |
| if(image[level]) |
| { |
| image[level]->addRef(); |
| } |
| |
| return image[level]; |
| } |
| |
| bool Texture2D::isShared(GLenum target, unsigned int level) const |
| { |
| ASSERT(target == GL_TEXTURE_2D); |
| ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS); |
| |
| if(mSurface) // Bound to an EGLSurface |
| { |
| return true; |
| } |
| |
| if(!image[level]) |
| { |
| return false; |
| } |
| |
| return image[level]->isShared(); |
| } |
| |
| TextureCubeMap::TextureCubeMap(GLuint name) : Texture(name) |
| { |
| for(int f = 0; f < 6; f++) |
| { |
| for(int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++) |
| { |
| image[f][i] = nullptr; |
| } |
| } |
| |
| for(int f = 0; f < 6; f++) |
| { |
| mFaceProxies[f] = nullptr; |
| mFaceProxyRefs[f] = 0; |
| } |
| } |
| |
| TextureCubeMap::~TextureCubeMap() |
| { |
| for(int f = 0; f < 6; f++) |
| { |
| for(int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++) |
| { |
| if(image[f][i]) |
| { |
| image[f][i]->unbind(this); |
| image[f][i] = nullptr; |
| } |
| } |
| } |
| |
| for(int i = 0; i < 6; i++) |
| { |
| mFaceProxies[i] = nullptr; |
| } |
| } |
| |
| // 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 f = 0; f < 6; f++) |
| { |
| if(mFaceProxies[f] == proxy) |
| { |
| mFaceProxyRefs[f]++; |
| } |
| } |
| } |
| |
| void TextureCubeMap::releaseProxy(const Renderbuffer *proxy) |
| { |
| for(int f = 0; f < 6; f++) |
| { |
| if(mFaceProxies[f] == proxy) |
| { |
| if(mFaceProxyRefs[f] > 0) |
| { |
| mFaceProxyRefs[f]--; |
| } |
| |
| if(mFaceProxyRefs[f] == 0) |
| { |
| mFaceProxies[f] = nullptr; |
| } |
| } |
| } |
| } |
| |
| void TextureCubeMap::sweep() |
| { |
| int imageCount = 0; |
| |
| for(int f = 0; f < 6; f++) |
| { |
| for(int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++) |
| { |
| if(image[f][i] && image[f][i]->isChildOf(this)) |
| { |
| if(!image[f][i]->hasSingleReference()) |
| { |
| return; |
| } |
| |
| imageCount++; |
| } |
| } |
| } |
| |
| if(imageCount == referenceCount) |
| { |
| destroy(); |
| } |
| } |
| |
| GLenum TextureCubeMap::getTarget() const |
| { |
| return GL_TEXTURE_CUBE_MAP; |
| } |
| |
| GLsizei TextureCubeMap::getWidth(GLenum target, GLint level) const |
| { |
| int face = CubeFaceIndex(target); |
| return image[face][level] ? image[face][level]->getWidth() : 0; |
| } |
| |
| GLsizei TextureCubeMap::getHeight(GLenum target, GLint level) const |
| { |
| int face = CubeFaceIndex(target); |
| return image[face][level] ? image[face][level]->getHeight() : 0; |
| } |
| |
| GLenum TextureCubeMap::getFormat(GLenum target, GLint level) const |
| { |
| int face = CubeFaceIndex(target); |
| return image[face][level] ? image[face][level]->getFormat() : 0; |
| } |
| |
| GLenum TextureCubeMap::getType(GLenum target, GLint level) const |
| { |
| int face = CubeFaceIndex(target); |
| return image[face][level] ? image[face][level]->getType() : 0; |
| } |
| |
| sw::Format TextureCubeMap::getInternalFormat(GLenum target, GLint level) const |
| { |
| int face = CubeFaceIndex(target); |
| return image[face][level] ? image[face][level]->getInternalFormat() : sw::FORMAT_NULL; |
| } |
| |
| int TextureCubeMap::getLevelCount() const |
| { |
| ASSERT(isSamplerComplete()); |
| int levels = 0; |
| |
| while(levels < IMPLEMENTATION_MAX_TEXTURE_LEVELS && image[0][levels]) |
| { |
| levels++; |
| } |
| |
| return levels; |
| } |
| |
| void TextureCubeMap::setCompressedImage(GLenum target, GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels) |
| { |
| int face = CubeFaceIndex(target); |
| |
| if(image[face][level]) |
| { |
| image[face][level]->release(); |
| } |
| |
| GLenum sizedInternalFormat = GetSizedInternalFormat(format, GL_UNSIGNED_BYTE); |
| image[face][level] = egl::Image::create(this, width, height, sizedInternalFormat, GL_UNSIGNED_BYTE); |
| |
| if(!image[face][level]) |
| { |
| return error(GL_OUT_OF_MEMORY); |
| } |
| |
| Texture::setCompressedImage(imageSize, pixels, image[face][level]); |
| } |
| |
| void TextureCubeMap::subImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const egl::Image::UnpackInfo& unpackInfo, const void *pixels) |
| { |
| Texture::subImage(xoffset, yoffset, 0, width, height, 1, format, type, unpackInfo, pixels, image[CubeFaceIndex(target)][level]); |
| } |
| |
| void TextureCubeMap::subImageCompressed(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels) |
| { |
| Texture::subImageCompressed(xoffset, yoffset, 0, width, height, 1, format, imageSize, pixels, image[CubeFaceIndex(target)][level]); |
| } |
| |
| // Tests for cube map sampling completeness. [OpenGL ES 2.0.24] section 3.8.2 page 86. |
| bool TextureCubeMap::isSamplerComplete() const |
| { |
| for(int face = 0; face < 6; face++) |
| { |
| if(!image[face][0]) |
| { |
| return false; |
| } |
| } |
| |
| int size = image[0][0]->getWidth(); |
| |
| if(size <= 0) |
| { |
| return false; |
| } |
| |
| if(!isMipmapFiltered()) |
| { |
| if(!isCubeComplete()) |
| { |
| return false; |
| } |
| } |
| else |
| { |
| if(!isMipmapCubeComplete()) // Also tests for isCubeComplete() |
| { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| // Tests for cube texture completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81. |
| bool TextureCubeMap::isCubeComplete() const |
| { |
| if(image[0][mBaseLevel]->getWidth() <= 0 || image[0][mBaseLevel]->getHeight() != image[0][mBaseLevel]->getWidth()) |
| { |
| return false; |
| } |
| |
| for(unsigned int face = 1; face < 6; face++) |
| { |
| if(image[face][mBaseLevel]->getWidth() != image[0][mBaseLevel]->getWidth() || |
| image[face][mBaseLevel]->getWidth() != image[0][mBaseLevel]->getHeight() || |
| image[face][mBaseLevel]->getFormat() != image[0][mBaseLevel]->getFormat() || |
| image[face][mBaseLevel]->getType() != image[0][mBaseLevel]->getType()) |
| { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| bool TextureCubeMap::isMipmapCubeComplete() const |
| { |
| if(!isCubeComplete()) |
| { |
| return false; |
| } |
| |
| GLsizei size = image[0][mBaseLevel]->getWidth(); |
| int q = std::min(log2(size), mMaxLevel); |
| |
| for(int face = 0; face < 6; face++) |
| { |
| for(int level = mBaseLevel + 1; level <= q; level++) |
| { |
| if(!image[face][level]) |
| { |
| return false; |
| } |
| |
| if(image[face][level]->getFormat() != image[0][mBaseLevel]->getFormat()) |
| { |
| return false; |
| } |
| |
| if(image[face][level]->getType() != image[0][mBaseLevel]->getType()) |
| { |
| return false; |
| } |
| |
| if(image[face][level]->getWidth() != std::max(1, size >> level)) |
| { |
| return false; |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| bool TextureCubeMap::isCompressed(GLenum target, GLint level) const |
| { |
| return IsCompressed(getFormat(target, level), egl::getClientVersion()); |
| } |
| |
| bool TextureCubeMap::isDepth(GLenum target, GLint level) const |
| { |
| return IsDepthTexture(getFormat(target, level)); |
| } |
| |
| void TextureCubeMap::releaseTexImage() |
| { |
| UNREACHABLE(0); // Cube maps cannot have an EGL surface bound as an image |
| } |
| |
| void TextureCubeMap::setImage(GLenum target, GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, const egl::Image::UnpackInfo& unpackInfo, const void *pixels) |
| { |
| int face = CubeFaceIndex(target); |
| |
| if(image[face][level]) |
| { |
| image[face][level]->release(); |
| } |
| |
| image[face][level] = egl::Image::create(this, width, height, format, type); |
| |
| if(!image[face][level]) |
| { |
| return error(GL_OUT_OF_MEMORY); |
| } |
| |
| Texture::setImage(format, type, unpackInfo, pixels, image[face][level]); |
| } |
| |
| void TextureCubeMap::copyImage(GLenum target, GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source) |
| { |
| egl::Image *renderTarget = source->getRenderTarget(0); |
| |
| if(!renderTarget) |
| { |
| ERR("Failed to retrieve the render target."); |
| return error(GL_OUT_OF_MEMORY); |
| } |
| |
| int face = CubeFaceIndex(target); |
| |
| if(image[face][level]) |
| { |
| image[face][level]->release(); |
| } |
| |
| GLenum sizedInternalFormat = GetSizedInternalFormat(format, GL_UNSIGNED_BYTE); |
| image[face][level] = egl::Image::create(this, width, height, sizedInternalFormat, GL_UNSIGNED_BYTE); |
| |
| if(!image[face][level]) |
| { |
| return error(GL_OUT_OF_MEMORY); |
| } |
| |
| if(width != 0 && height != 0) |
| { |
| Renderbuffer* renderbuffer = source->getReadColorbuffer(); |
| |
| if(!renderbuffer) |
| { |
| ERR("Failed to retrieve the source colorbuffer."); |
| return; |
| } |
| |
| sw::SliceRect sourceRect(x, y, x + width, y + height, 0); |
| sourceRect.clip(0, 0, renderbuffer->getWidth(), renderbuffer->getHeight()); |
| |
| copy(renderTarget, sourceRect, sizedInternalFormat, 0, 0, 0, image[face][level]); |
| } |
| |
| renderTarget->release(); |
| } |
| |
| egl::Image *TextureCubeMap::getImage(int face, unsigned int level) |
| { |
| return image[face][level]; |
| } |
| |
| egl::Image *TextureCubeMap::getImage(GLenum face, unsigned int level) |
| { |
| return image[CubeFaceIndex(face)][level]; |
| } |
| |
| void TextureCubeMap::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source) |
| { |
| int face = CubeFaceIndex(target); |
| |
| if(!image[face][level]) |
| { |
| return error(GL_INVALID_OPERATION); |
| } |
| |
| GLsizei size = image[face][level]->getWidth(); |
| |
| if(xoffset + width > size || yoffset + height > size || zoffset != 0) |
| { |
| return error(GL_INVALID_VALUE); |
| } |
| |
| egl::Image *renderTarget = source->getRenderTarget(0); |
| |
| if(!renderTarget) |
| { |
| ERR("Failed to retrieve the render target."); |
| return error(GL_OUT_OF_MEMORY); |
| } |
| |
| Renderbuffer* renderbuffer = source->getReadColorbuffer(); |
| |
| if(!renderbuffer) |
| { |
| ERR("Failed to retrieve the source colorbuffer."); |
| return; |
| } |
| |
| sw::SliceRect sourceRect(x, y, x + width, y + height, 0); |
| sourceRect.clip(0, 0, renderbuffer->getWidth(), renderbuffer->getHeight()); |
| |
| copy(renderTarget, sourceRect, image[face][level]->getFormat(), xoffset, yoffset, zoffset, image[face][level]); |
| |
| renderTarget->release(); |
| } |
| |
| void TextureCubeMap::generateMipmaps() |
| { |
| if(!isCubeComplete()) |
| { |
| return error(GL_INVALID_OPERATION); |
| } |
| |
| unsigned int q = log2(image[0][0]->getWidth()); |
| |
| for(unsigned int f = 0; f < 6; f++) |
| { |
| for(unsigned int i = 1; i <= q; i++) |
| { |
| if(image[f][i]) |
| { |
| image[f][i]->release(); |
| } |
| |
| image[f][i] = egl::Image::create(this, std::max(image[0][0]->getWidth() >> i, 1), std::max(image[0][0]->getHeight() >> i, 1), image[0][0]->getFormat(), image[0][0]->getType()); |
| |
| if(!image[f][i]) |
| { |
| return error(GL_OUT_OF_MEMORY); |
| } |
| |
| getDevice()->stretchRect(image[f][i - 1], 0, image[f][i], 0, Device::ALL_BUFFERS | Device::USE_FILTER); |
| } |
| } |
| } |
| |
| Renderbuffer *TextureCubeMap::getRenderbuffer(GLenum target, GLint level, GLint layer) |
| { |
| if(!IsCubemapTextureTarget(target)) |
| { |
| return error(GL_INVALID_OPERATION, (Renderbuffer*)nullptr); |
| } |
| |
| int face = CubeFaceIndex(target); |
| |
| if(!mFaceProxies[face]) |
| { |
| mFaceProxies[face] = new Renderbuffer(name, new RenderbufferTextureCubeMap(this, target, level)); |
| } |
| else |
| { |
| mFaceProxies[face]->setLevel(level); |
| } |
| |
| return mFaceProxies[face]; |
| } |
| |
| egl::Image *TextureCubeMap::getRenderTarget(GLenum target, unsigned int level) |
| { |
| ASSERT(IsCubemapTextureTarget(target)); |
| ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS); |
| |
| int face = CubeFaceIndex(target); |
| |
| if(image[face][level]) |
| { |
| image[face][level]->addRef(); |
| } |
| |
| return image[face][level]; |
| } |
| |
| bool TextureCubeMap::isShared(GLenum target, unsigned int level) const |
| { |
| ASSERT(IsCubemapTextureTarget(target)); |
| ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS); |
| |
| int face = CubeFaceIndex(target); |
| |
| if(!image[face][level]) |
| { |
| return false; |
| } |
| |
| return image[face][level]->isShared(); |
| } |
| |
| Texture3D::Texture3D(GLuint name) : Texture(name) |
| { |
| for(int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++) |
| { |
| image[i] = nullptr; |
| } |
| |
| mSurface = nullptr; |
| |
| mColorbufferProxy = nullptr; |
| mProxyRefs = 0; |
| } |
| |
| Texture3D::~Texture3D() |
| { |
| for(int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++) |
| { |
| if(image[i]) |
| { |
| image[i]->unbind(this); |
| image[i] = nullptr; |
| } |
| } |
| |
| if(mSurface) |
| { |
| mSurface->setBoundTexture(nullptr); |
| mSurface = nullptr; |
| } |
| |
| mColorbufferProxy = nullptr; |
| } |
| |
| // 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 Texture3D::addProxyRef(const Renderbuffer *proxy) |
| { |
| mProxyRefs++; |
| } |
| |
| void Texture3D::releaseProxy(const Renderbuffer *proxy) |
| { |
| if(mProxyRefs > 0) |
| { |
| mProxyRefs--; |
| } |
| |
| if(mProxyRefs == 0) |
| { |
| mColorbufferProxy = nullptr; |
| } |
| } |
| |
| void Texture3D::sweep() |
| { |
| int imageCount = 0; |
| |
| for(int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++) |
| { |
| if(image[i] && image[i]->isChildOf(this)) |
| { |
| if(!image[i]->hasSingleReference()) |
| { |
| return; |
| } |
| |
| imageCount++; |
| } |
| } |
| |
| if(imageCount == referenceCount) |
| { |
| destroy(); |
| } |
| } |
| |
| GLenum Texture3D::getTarget() const |
| { |
| return GL_TEXTURE_3D_OES; |
| } |
| |
| GLsizei Texture3D::getWidth(GLenum target, GLint level) const |
| { |
| ASSERT(target == getTarget()); |
| return image[level] ? image[level]->getWidth() : 0; |
| } |
| |
| GLsizei Texture3D::getHeight(GLenum target, GLint level) const |
| { |
| ASSERT(target == getTarget()); |
| return image[level] ? image[level]->getHeight() : 0; |
| } |
| |
| GLsizei Texture3D::getDepth(GLenum target, GLint level) const |
| { |
| ASSERT(target == getTarget()); |
| return image[level] ? image[level]->getDepth() : 0; |
| } |
| |
| GLenum Texture3D::getFormat(GLenum target, GLint level) const |
| { |
| ASSERT(target == getTarget()); |
| return image[level] ? image[level]->getFormat() : GL_NONE; |
| } |
| |
| GLenum Texture3D::getType(GLenum target, GLint level) const |
| { |
| ASSERT(target == getTarget()); |
| return image[level] ? image[level]->getType() : GL_NONE; |
| } |
| |
| sw::Format Texture3D::getInternalFormat(GLenum target, GLint level) const |
| { |
| ASSERT(target == getTarget()); |
| return image[level] ? image[level]->getInternalFormat() : sw::FORMAT_NULL; |
| } |
| |
| int Texture3D::getLevelCount() const |
| { |
| ASSERT(isSamplerComplete()); |
| int levels = 0; |
| |
| while(levels < IMPLEMENTATION_MAX_TEXTURE_LEVELS && image[levels]) |
| { |
| levels++; |
| } |
| |
| return levels; |
| } |
| |
| void Texture3D::setImage(GLint level, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const egl::Image::UnpackInfo& unpackInfo, const void *pixels) |
| { |
| if(image[level]) |
| { |
| image[level]->release(); |
| } |
| |
| image[level] = egl::Image::create(this, width, height, depth, format, type); |
| |
| if(!image[level]) |
| { |
| return error(GL_OUT_OF_MEMORY); |
| } |
| |
| Texture::setImage(format, type, unpackInfo, pixels, image[level]); |
| } |
| |
| void Texture3D::bindTexImage(gl::Surface *surface) |
| { |
| GLenum format; |
| |
| switch(surface->getInternalFormat()) |
| { |
| case sw::FORMAT_A8R8G8B8: |
| format = GL_RGBA; |
| break; |
| case sw::FORMAT_X8R8G8B8: |
| format = GL_RGB; |
| break; |
| default: |
| UNIMPLEMENTED(); |
| return; |
| } |
| |
| for(int level = 0; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++) |
| { |
| if(image[level]) |
| { |
| image[level]->release(); |
| image[level] = nullptr; |
| } |
| } |
| |
| image[0] = surface->getRenderTarget(); |
| |
| mSurface = surface; |
| mSurface->setBoundTexture(this); |
| } |
| |
| void Texture3D::releaseTexImage() |
| { |
| for(int level = 0; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++) |
| { |
| if(image[level]) |
| { |
| image[level]->release(); |
| image[level] = nullptr; |
| } |
| } |
| } |
| |
| void Texture3D::setCompressedImage(GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei depth, GLsizei imageSize, const void *pixels) |
| { |
| if(image[level]) |
| { |
| image[level]->release(); |
| } |
| |
| GLenum sizedInternalFormat = GetSizedInternalFormat(format, GL_UNSIGNED_BYTE); |
| image[level] = egl::Image::create(this, width, height, depth, sizedInternalFormat, GL_UNSIGNED_BYTE); |
| |
| if(!image[level]) |
| { |
| return error(GL_OUT_OF_MEMORY); |
| } |
| |
| Texture::setCompressedImage(imageSize, pixels, image[level]); |
| } |
| |
| void Texture3D::subImage(GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const egl::Image::UnpackInfo& unpackInfo, const void *pixels) |
| { |
| Texture::subImage(xoffset, yoffset, zoffset, width, height, depth, format, type, unpackInfo, pixels, image[level]); |
| } |
| |
| void Texture3D::subImageCompressed(GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *pixels) |
| { |
| Texture::subImageCompressed(xoffset, yoffset, zoffset, width, height, depth, format, imageSize, pixels, image[level]); |
| } |
| |
| void Texture3D::copyImage(GLint level, GLenum format, GLint x, GLint y, GLint z, GLsizei width, GLsizei height, GLsizei depth, Framebuffer *source) |
| { |
| egl::Image *renderTarget = source->getRenderTarget(0); |
| |
| if(!renderTarget) |
| { |
| ERR("Failed to retrieve the render target."); |
| return error(GL_OUT_OF_MEMORY); |
| } |
| |
| if(image[level]) |
| { |
| image[level]->release(); |
| } |
| |
| GLenum sizedInternalFormat = GetSizedInternalFormat(format, GL_UNSIGNED_BYTE); |
| image[level] = egl::Image::create(this, width, height, depth, sizedInternalFormat, GL_UNSIGNED_BYTE); |
| |
| if(!image[level]) |
| { |
| return error(GL_OUT_OF_MEMORY); |
| } |
| |
| if(width != 0 && height != 0 && depth != 0) |
| { |
| Renderbuffer* renderbuffer = source->getReadColorbuffer(); |
| |
| if(!renderbuffer) |
| { |
| ERR("Failed to retrieve the source colorbuffer."); |
| return; |
| } |
| |
| sw::SliceRect sourceRect(x, y, x + width, y + height, z); |
| sourceRect.clip(0, 0, renderbuffer->getWidth(), renderbuffer->getHeight()); |
| for(GLint sliceZ = 0; sliceZ < depth; ++sliceZ, ++sourceRect.slice) |
| { |
| copy(renderTarget, sourceRect, sizedInternalFormat, 0, 0, sliceZ, image[level]); |
| } |
| } |
| |
| renderTarget->release(); |
| } |
| |
| void Texture3D::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source) |
| { |
| if(!image[level]) |
| { |
| return error(GL_INVALID_OPERATION); |
| } |
| |
| if(xoffset + width > image[level]->getWidth() || yoffset + height > image[level]->getHeight() || zoffset >= image[level]->getDepth()) |
| { |
| return error(GL_INVALID_VALUE); |
| } |
| |
| egl::Image *renderTarget = source->getRenderTarget(0); |
| |
| if(!renderTarget) |
| { |
| ERR("Failed to retrieve the render target."); |
| return error(GL_OUT_OF_MEMORY); |
| } |
| |
| Renderbuffer* renderbuffer = source->getReadColorbuffer(); |
| |
| if(!renderbuffer) |
| { |
| ERR("Failed to retrieve the source colorbuffer."); |
| return; |
| } |
| |
| sw::SliceRect sourceRect = {x, y, x + width, y + height, 0}; |
| sourceRect.clip(0, 0, renderbuffer->getWidth(), renderbuffer->getHeight()); |
| |
| copy(renderTarget, sourceRect, image[level]->getFormat(), xoffset, yoffset, zoffset, image[level]); |
| |
| renderTarget->release(); |
| } |
| |
| void Texture3D::setSharedImage(egl::Image *sharedImage) |
| { |
| sharedImage->addRef(); |
| |
| if(image[0]) |
| { |
| image[0]->release(); |
| } |
| |
| image[0] = sharedImage; |
| } |
| |
| // Tests for 3D texture sampling completeness. [OpenGL ES 2.0.24] section 3.8.2 page 85. |
| bool Texture3D::isSamplerComplete() const |
| { |
| if(!image[0]) |
| { |
| return false; |
| } |
| |
| GLsizei width = image[0]->getWidth(); |
| GLsizei height = image[0]->getHeight(); |
| GLsizei depth = image[0]->getDepth(); |
| |
| if(width <= 0 || height <= 0 || depth <= 0) |
| { |
| return false; |
| } |
| |
| if(isMipmapFiltered()) |
| { |
| if(!isMipmapComplete()) |
| { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| // Tests for 3D texture (mipmap) completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81. |
| bool Texture3D::isMipmapComplete() const |
| { |
| GLsizei width = image[mBaseLevel]->getWidth(); |
| GLsizei height = image[mBaseLevel]->getHeight(); |
| GLsizei depth = image[mBaseLevel]->getDepth(); |
| bool isTexture2DArray = getTarget() == GL_TEXTURE_2D_ARRAY; |
| |
| int q = isTexture2DArray ? std::min(log2(std::max(width, height)), mMaxLevel) : |
| std::min(log2(std::max(std::max(width, height), depth)), mMaxLevel); |
| |
| for(int level = mBaseLevel + 1; level <= q; level++) |
| { |
| if(!image[level]) |
| { |
| return false; |
| } |
| |
| if(image[level]->getFormat() != image[0]->getFormat()) |
| { |
| return false; |
| } |
| |
| if(image[level]->getType() != image[0]->getType()) |
| { |
| return false; |
| } |
| |
| if(image[level]->getWidth() != std::max(1, width >> level)) |
| { |
| return false; |
| } |
| |
| if(image[level]->getHeight() != std::max(1, height >> level)) |
| { |
| return false; |
| } |
| |
| int levelDepth = isTexture2DArray ? depth : std::max(1, depth >> level); |
| if(image[level]->getDepth() != levelDepth) |
| { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| bool Texture3D::isCompressed(GLenum target, GLint level) const |
| { |
| return IsCompressed(getFormat(target, level), egl::getClientVersion()); |
| } |
| |
| bool Texture3D::isDepth(GLenum target, GLint level) const |
| { |
| return IsDepthTexture(getFormat(target, level)); |
| } |
| |
| void Texture3D::generateMipmaps() |
| { |
| if(!image[0]) |
| { |
| return; // FIXME: error? |
| } |
| |
| unsigned int q = log2(std::max(std::max(image[0]->getWidth(), image[0]->getHeight()), image[0]->getDepth())); |
| |
| for(unsigned int i = 1; i <= q; i++) |
| { |
| if(image[i]) |
| { |
| image[i]->release(); |
| } |
| |
| image[i] = egl::Image::create(this, std::max(image[0]->getWidth() >> i, 1), std::max(image[0]->getHeight() >> i, 1), std::max(image[0]->getDepth() >> i, 1), image[0]->getFormat(), image[0]->getType()); |
| |
| if(!image[i]) |
| { |
| return error(GL_OUT_OF_MEMORY); |
| } |
| |
| getDevice()->stretchCube(image[i - 1], image[i]); |
| } |
| } |
| |
| egl::Image *Texture3D::getImage(unsigned int level) |
| { |
| return image[level]; |
| } |
| |
| Renderbuffer *Texture3D::getRenderbuffer(GLenum target, GLint level, GLint layer) |
| { |
| if(target != getTarget()) |
| { |
| return error(GL_INVALID_OPERATION, (Renderbuffer*)nullptr); |
| } |
| |
| if(!mColorbufferProxy) |
| { |
| mColorbufferProxy = new Renderbuffer(name, new RenderbufferTexture3D(this, level, layer)); |
| } |
| else |
| { |
| mColorbufferProxy->setLevel(level); |
| mColorbufferProxy->setLayer(layer); |
| } |
| |
| return mColorbufferProxy; |
| } |
| |
| egl::Image *Texture3D::getRenderTarget(GLenum target, unsigned int level) |
| { |
| ASSERT(target == getTarget()); |
| ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS); |
| |
| if(image[level]) |
| { |
| image[level]->addRef(); |
| } |
| |
| return image[level]; |
| } |
| |
| bool Texture3D::isShared(GLenum target, unsigned int level) const |
| { |
| ASSERT(target == getTarget()); |
| ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS); |
| |
| if(mSurface) // Bound to an EGLSurface |
| { |
| return true; |
| } |
| |
| if(!image[level]) |
| { |
| return false; |
| } |
| |
| return image[level]->isShared(); |
| } |
| |
| Texture2DArray::Texture2DArray(GLuint name) : Texture3D(name) |
| { |
| } |
| |
| Texture2DArray::~Texture2DArray() |
| { |
| } |
| |
| GLenum Texture2DArray::getTarget() const |
| { |
| return GL_TEXTURE_2D_ARRAY; |
| } |
| |
| void Texture2DArray::generateMipmaps() |
| { |
| int depth = image[0] ? image[0]->getDepth() : 0; |
| if(!depth) |
| { |
| return; // FIXME: error? |
| } |
| |
| unsigned int q = log2(std::max(image[0]->getWidth(), image[0]->getHeight())); |
| |
| for(unsigned int i = 1; i <= q; i++) |
| { |
| if(image[i]) |
| { |
| image[i]->release(); |
| } |
| |
| GLsizei w = std::max(image[0]->getWidth() >> i, 1); |
| GLsizei h = std::max(image[0]->getHeight() >> i, 1); |
| image[i] = egl::Image::create(this, w, h, depth, image[0]->getFormat(), image[0]->getType()); |
| |
| if(!image[i]) |
| { |
| return error(GL_OUT_OF_MEMORY); |
| } |
| |
| GLsizei srcw = image[i - 1]->getWidth(); |
| GLsizei srch = image[i - 1]->getHeight(); |
| for(int z = 0; z < depth; ++z) |
| { |
| sw::SliceRect srcRect(0, 0, srcw, srch, z); |
| sw::SliceRect dstRect(0, 0, w, h, z); |
| getDevice()->stretchRect(image[i - 1], &srcRect, image[i], &dstRect, Device::ALL_BUFFERS | Device::USE_FILTER); |
| } |
| } |
| } |
| |
| TextureExternal::TextureExternal(GLuint name) : Texture2D(name) |
| { |
| mMinFilter = GL_LINEAR; |
| mMagFilter = GL_LINEAR; |
| mWrapS = GL_CLAMP_TO_EDGE; |
| mWrapT = GL_CLAMP_TO_EDGE; |
| mWrapR = GL_CLAMP_TO_EDGE; |
| } |
| |
| TextureExternal::~TextureExternal() |
| { |
| } |
| |
| GLenum TextureExternal::getTarget() const |
| { |
| return GL_TEXTURE_EXTERNAL_OES; |
| } |
| |
| } |
| |
| egl::Image *createBackBuffer(int width, int height, const egl::Config *config) |
| { |
| if(config) |
| { |
| return egl::Image::create(width, height, config->mRenderTargetFormat, config->mSamples, false); |
| } |
| |
| return nullptr; |
| } |
| |
| egl::Image *createDepthStencil(unsigned int width, unsigned int height, sw::Format format, int multiSampleDepth, bool discard) |
| { |
| if(width == 0 || height == 0 || height > sw::OUTLINE_RESOLUTION) |
| { |
| ERR("Invalid parameters: %dx%d", width, height); |
| return nullptr; |
| } |
| |
| bool lockable = true; |
| |
| switch(format) |
| { |
| // case sw::FORMAT_D15S1: |
| case sw::FORMAT_D24S8: |
| case sw::FORMAT_D24X8: |
| // case sw::FORMAT_D24X4S4: |
| case sw::FORMAT_D24FS8: |
| case sw::FORMAT_D32: |
| case sw::FORMAT_D16: |
| lockable = false; |
| break; |
| // case sw::FORMAT_S8_LOCKABLE: |
| // case sw::FORMAT_D16_LOCKABLE: |
| case sw::FORMAT_D32F_LOCKABLE: |
| // case sw::FORMAT_D32_LOCKABLE: |
| case sw::FORMAT_DF24S8: |
| case sw::FORMAT_DF16S8: |
| lockable = true; |
| break; |
| default: |
| UNREACHABLE(format); |
| } |
| |
| egl::Image *surface = egl::Image::create(width, height, format, multiSampleDepth, lockable); |
| |
| if(!surface) |
| { |
| ERR("Out of memory"); |
| return nullptr; |
| } |
| |
| return surface; |
| } |