|  | // 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 "Sampler.h" | 
|  | #include "Shader.h" | 
|  | #include "libEGL/Display.h" | 
|  | #include "common/Surface.hpp" | 
|  | #include "common/debug.h" | 
|  |  | 
|  | #include <algorithm> | 
|  |  | 
|  | namespace es2 | 
|  | { | 
|  |  | 
|  | egl::Image*& ImageLevels::getNullImage() | 
|  | { | 
|  | static egl::Image* nullImage; | 
|  | nullImage = nullptr; | 
|  | return nullImage; | 
|  | } | 
|  |  | 
|  | 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) || (getTarget() == GL_TEXTURE_RECTANGLE_ARB)) | 
|  | { | 
|  | 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) || (getTarget() == GL_TEXTURE_RECTANGLE_ARB)) | 
|  | { | 
|  | 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) || (getTarget() == GL_TEXTURE_RECTANGLE_ARB)) | 
|  | { | 
|  | 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) || (getTarget() == GL_TEXTURE_RECTANGLE_ARB)) | 
|  | { | 
|  | 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) | 
|  | { | 
|  | if(baseLevel < 0) | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | 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; | 
|  | } | 
|  | } | 
|  |  | 
|  | 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 gl::PixelStorageModes &unpackParameters, 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, unpackParameters, pixels); | 
|  | } | 
|  | } | 
|  |  | 
|  | void Texture::setCompressedImage(GLsizei imageSize, const void *pixels, egl::Image *image) | 
|  | { | 
|  | if(pixels && image && (imageSize > 0)) // imageSize's correlation to width and height is already validated with gl::ComputeCompressedSize() at the API level | 
|  | { | 
|  | 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 gl::PixelStorageModes &unpackParameters, const void *pixels, egl::Image *image) | 
|  | { | 
|  | if(!image) | 
|  | { | 
|  | return error(GL_INVALID_OPERATION); | 
|  | } | 
|  |  | 
|  | if(pixels && width > 0 && height > 0 && depth > 0) | 
|  | { | 
|  | image->loadImageData(xoffset, yoffset, zoffset, width, height, depth, format, type, unpackParameters, 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(pixels && (imageSize > 0)) // imageSize's correlation to width and height is already validated with gl::ComputeCompressedSize() at the API level | 
|  | { | 
|  | image->loadCompressedData(xoffset, yoffset, zoffset, width, height, depth, imageSize, pixels); | 
|  | } | 
|  | } | 
|  |  | 
|  | bool Texture::copy(egl::Image *source, const sw::SliceRect &sourceRect, 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); | 
|  | sw::SliceRectF sourceRectF(static_cast<float>(sourceRect.x0), | 
|  | static_cast<float>(sourceRect.y0), | 
|  | static_cast<float>(sourceRect.x1), | 
|  | static_cast<float>(sourceRect.y1), | 
|  | sourceRect.slice); | 
|  | bool success = device->stretchRect(source, &sourceRectF, dest, &destRect, Device::ALL_BUFFERS); | 
|  |  | 
|  | if(!success) | 
|  | { | 
|  | return error(GL_OUT_OF_MEMORY, false); | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool Texture::isMipmapFiltered(Sampler *sampler) const | 
|  | { | 
|  | GLenum minFilter = sampler ? sampler->getMinFilter() : mMinFilter; | 
|  |  | 
|  | switch(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(minFilter); | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | Texture2D::Texture2D(GLuint name) : Texture(name) | 
|  | { | 
|  | mSurface = nullptr; | 
|  |  | 
|  | mColorbufferProxy = nullptr; | 
|  | mProxyRefs = 0; | 
|  | } | 
|  |  | 
|  | Texture2D::~Texture2D() | 
|  | { | 
|  | image.unbind(this); | 
|  |  | 
|  | 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 == getTarget()); | 
|  | return image[level] ? image[level]->getWidth() : 0; | 
|  | } | 
|  |  | 
|  | GLsizei Texture2D::getHeight(GLenum target, GLint level) const | 
|  | { | 
|  | ASSERT(target == getTarget()); | 
|  | return image[level] ? image[level]->getHeight() : 0; | 
|  | } | 
|  |  | 
|  | GLint Texture2D::getFormat(GLenum target, GLint level) const | 
|  | { | 
|  | ASSERT(target == getTarget()); | 
|  | return image[level] ? image[level]->getFormat() : GL_NONE; | 
|  | } | 
|  |  | 
|  | int Texture2D::getTopLevel() const | 
|  | { | 
|  | int level = mBaseLevel; | 
|  |  | 
|  | while(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS && image[level]) | 
|  | { | 
|  | level++; | 
|  | } | 
|  |  | 
|  | return level - 1; | 
|  | } | 
|  |  | 
|  | bool Texture2D::requiresSync() const | 
|  | { | 
|  | for(int level = 0; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++) | 
|  | { | 
|  | if(image[level] && image[level]->requiresSync()) | 
|  | { | 
|  | return true; | 
|  | } | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | void Texture2D::setImage(GLint level, GLsizei width, GLsizei height, GLint internalformat, GLenum format, GLenum type, const gl::PixelStorageModes &unpackParameters, const void *pixels) | 
|  | { | 
|  | if(image[level]) | 
|  | { | 
|  | image[level]->release(); | 
|  | } | 
|  |  | 
|  | image[level] = egl::Image::create(this, width, height, internalformat); | 
|  |  | 
|  | if(!image[level]) | 
|  | { | 
|  | return error(GL_OUT_OF_MEMORY); | 
|  | } | 
|  |  | 
|  | Texture::setImage(format, type, unpackParameters, pixels, image[level]); | 
|  | } | 
|  |  | 
|  | void Texture2D::bindTexImage(gl::Surface *surface) | 
|  | { | 
|  | image.release(); | 
|  |  | 
|  | image[0] = surface->getRenderTarget(); | 
|  |  | 
|  | assert(!mSurface); // eglBindTexImage called before eglReleaseTexImage | 
|  |  | 
|  | mSurface = surface; | 
|  | mSurface->setBoundTexture(this); | 
|  | } | 
|  |  | 
|  | void Texture2D::releaseTexImage() | 
|  | { | 
|  | image.release(); | 
|  |  | 
|  | if(mSurface) | 
|  | { | 
|  | mSurface->setBoundTexture(nullptr); | 
|  | mSurface = nullptr; | 
|  | } | 
|  | } | 
|  |  | 
|  | void Texture2D::setCompressedImage(GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels) | 
|  | { | 
|  | if(image[level]) | 
|  | { | 
|  | image[level]->release(); | 
|  | } | 
|  |  | 
|  | image[level] = egl::Image::create(this, width, height, format); | 
|  |  | 
|  | 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 gl::PixelStorageModes &unpackParameters, const void *pixels) | 
|  | { | 
|  | Texture::subImage(xoffset, yoffset, 0, width, height, 1, format, type, unpackParameters, 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 internalformat, GLint x, GLint y, GLsizei width, GLsizei height, Renderbuffer *source) | 
|  | { | 
|  | if(image[level]) | 
|  | { | 
|  | image[level]->release(); | 
|  | } | 
|  |  | 
|  | image[level] = egl::Image::create(this, width, height, internalformat); | 
|  |  | 
|  | if(!image[level]) | 
|  | { | 
|  | return error(GL_OUT_OF_MEMORY); | 
|  | } | 
|  |  | 
|  | if(width != 0 && height != 0) | 
|  | { | 
|  | egl::Image *renderTarget = source->getRenderTarget(); | 
|  |  | 
|  | if(!renderTarget) | 
|  | { | 
|  | ERR("Failed to retrieve the render target."); | 
|  | return error(GL_OUT_OF_MEMORY); | 
|  | } | 
|  |  | 
|  | sw::SliceRect sourceRect(x, y, x + width, y + height, 0); | 
|  | sourceRect.clip(0, 0, renderTarget->getWidth(), renderTarget->getHeight()); | 
|  |  | 
|  | copy(renderTarget, sourceRect, 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, Renderbuffer *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); | 
|  | } | 
|  |  | 
|  | if(width > 0 && height > 0) | 
|  | { | 
|  | egl::Image *renderTarget = source->getRenderTarget(); | 
|  |  | 
|  | if(!renderTarget) | 
|  | { | 
|  | ERR("Failed to retrieve the render target."); | 
|  | return error(GL_OUT_OF_MEMORY); | 
|  | } | 
|  |  | 
|  | sw::SliceRect sourceRect(x, y, x + width, y + height, 0); | 
|  | sourceRect.clip(0, 0, renderTarget->getWidth(), renderTarget->getHeight()); | 
|  |  | 
|  | copy(renderTarget, sourceRect, 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; | 
|  | } | 
|  |  | 
|  | bool Texture2D::isBaseLevelDefined() const | 
|  | { | 
|  | if(!image[mBaseLevel]) | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | GLsizei width = image[mBaseLevel]->getWidth(); | 
|  | GLsizei height = image[mBaseLevel]->getHeight(); | 
|  |  | 
|  | if(width <= 0 || height <= 0) | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Tests for 2D texture sampling completeness. [OpenGL ES 3.0.5] section 3.8.13 page 160. | 
|  | bool Texture2D::isSamplerComplete(Sampler *sampler) const | 
|  | { | 
|  | if(!isBaseLevelDefined()) | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if(isMipmapFiltered(sampler)) | 
|  | { | 
|  | if(!isMipmapComplete()) | 
|  | { | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Tests for 2D texture (mipmap) completeness. [OpenGL ES 3.0.5] section 3.8.13 page 160. | 
|  | bool Texture2D::isMipmapComplete() const | 
|  | { | 
|  | if(mBaseLevel > mMaxLevel) | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | GLsizei width = image[mBaseLevel]->getWidth(); | 
|  | GLsizei height = image[mBaseLevel]->getHeight(); | 
|  | int maxsize = std::max(width, height); | 
|  | int p = log2(maxsize) + mBaseLevel; | 
|  | int q = std::min(p, mMaxLevel); | 
|  |  | 
|  | for(int level = mBaseLevel + 1; level <= q; level++) | 
|  | { | 
|  | if(!image[level]) | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if(image[level]->getFormat() != image[mBaseLevel]->getFormat()) | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | int i = level - mBaseLevel; | 
|  |  | 
|  | if(image[level]->getWidth() != std::max(1, width >> i)) | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if(image[level]->getHeight() != std::max(1, height >> i)) | 
|  | { | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool Texture2D::isCompressed(GLenum target, GLint level) const | 
|  | { | 
|  | return IsCompressed(getFormat(target, level)); | 
|  | } | 
|  |  | 
|  | bool Texture2D::isDepth(GLenum target, GLint level) const | 
|  | { | 
|  | return IsDepthTexture(getFormat(target, level)); | 
|  | } | 
|  |  | 
|  | void Texture2D::generateMipmaps() | 
|  | { | 
|  | if(!image[mBaseLevel]) | 
|  | { | 
|  | return;   // Image unspecified. Not an error. | 
|  | } | 
|  |  | 
|  | if(image[mBaseLevel]->getWidth() == 0 || image[mBaseLevel]->getHeight() == 0) | 
|  | { | 
|  | return;   // Zero dimension. Not an error. | 
|  | } | 
|  |  | 
|  | int maxsize = std::max(image[mBaseLevel]->getWidth(), image[mBaseLevel]->getHeight()); | 
|  | int p = log2(maxsize) + mBaseLevel; | 
|  | int q = std::min(p, mMaxLevel); | 
|  |  | 
|  | for(int i = mBaseLevel + 1; i <= q; i++) | 
|  | { | 
|  | if(image[i]) | 
|  | { | 
|  | image[i]->release(); | 
|  | } | 
|  |  | 
|  | image[i] = egl::Image::create(this, std::max(image[mBaseLevel]->getWidth() >> i, 1), std::max(image[mBaseLevel]->getHeight() >> i, 1), image[mBaseLevel]->getFormat()); | 
|  |  | 
|  | 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) | 
|  | { | 
|  | if(target != getTarget()) | 
|  | { | 
|  | 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 == getTarget()); | 
|  | 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 == getTarget()); | 
|  | ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS); | 
|  |  | 
|  | if(mSurface)   // Bound to an EGLSurface | 
|  | { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | if(!image[level]) | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return image[level]->isShared(); | 
|  | } | 
|  |  | 
|  | Texture2DRect::Texture2DRect(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; | 
|  | } | 
|  |  | 
|  | GLenum Texture2DRect::getTarget() const | 
|  | { | 
|  | return GL_TEXTURE_RECTANGLE_ARB; | 
|  | } | 
|  |  | 
|  | Renderbuffer *Texture2DRect::getRenderbuffer(GLenum target, GLint level) | 
|  | { | 
|  | if((target != getTarget()) || (level != 0)) | 
|  | { | 
|  | return error(GL_INVALID_OPERATION, (Renderbuffer*)nullptr); | 
|  | } | 
|  |  | 
|  | if(!mColorbufferProxy) | 
|  | { | 
|  | mColorbufferProxy = new Renderbuffer(name, new RenderbufferTexture2DRect(this)); | 
|  | } | 
|  |  | 
|  | return mColorbufferProxy; | 
|  | } | 
|  |  | 
|  | TextureCubeMap::TextureCubeMap(GLuint name) : Texture(name) | 
|  | { | 
|  | for(int f = 0; f < 6; f++) | 
|  | { | 
|  | mFaceProxies[f] = nullptr; | 
|  | mFaceProxyRefs[f] = 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | TextureCubeMap::~TextureCubeMap() | 
|  | { | 
|  | for(int i = 0; i < 6; i++) | 
|  | { | 
|  | image[i].unbind(this); | 
|  | 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; | 
|  | } | 
|  |  | 
|  | GLint TextureCubeMap::getFormat(GLenum target, GLint level) const | 
|  | { | 
|  | int face = CubeFaceIndex(target); | 
|  | return image[face][level] ? image[face][level]->getFormat() : 0; | 
|  | } | 
|  |  | 
|  | int TextureCubeMap::getTopLevel() const | 
|  | { | 
|  | int level = mBaseLevel; | 
|  |  | 
|  | while(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS && image[0][level]) | 
|  | { | 
|  | level++; | 
|  | } | 
|  |  | 
|  | return level - 1; | 
|  | } | 
|  |  | 
|  | bool TextureCubeMap::requiresSync() const | 
|  | { | 
|  | for(int level = 0; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++) | 
|  | { | 
|  | for(int face = 0; face < 6; face++) | 
|  | { | 
|  | if(image[face][level] && image[face][level]->requiresSync()) | 
|  | { | 
|  | return true; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | 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(); | 
|  | } | 
|  |  | 
|  | image[face][level] = egl::Image::create(this, width, height, 1, 1, format); | 
|  |  | 
|  | 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 gl::PixelStorageModes &unpackParameters, const void *pixels) | 
|  | { | 
|  | Texture::subImage(xoffset, yoffset, 0, width, height, 1, format, type, unpackParameters, 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]); | 
|  | } | 
|  |  | 
|  | bool TextureCubeMap::isBaseLevelDefined() const | 
|  | { | 
|  | for(int face = 0; face < 6; face++) | 
|  | { | 
|  | if(!image[face][mBaseLevel]) | 
|  | { | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | int size = image[0][mBaseLevel]->getWidth(); | 
|  |  | 
|  | if(size <= 0) | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Tests for cube map sampling completeness. [OpenGL ES 3.0.5] section 3.8.13 page 161. | 
|  | bool TextureCubeMap::isSamplerComplete(Sampler *sampler) const | 
|  | { | 
|  | if(!isBaseLevelDefined()) | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if(!isMipmapFiltered(sampler)) | 
|  | { | 
|  | if(!isCubeComplete()) | 
|  | { | 
|  | return false; | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | if(!isMipmapCubeComplete())   // Also tests for isCubeComplete() | 
|  | { | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Tests for cube texture completeness. [OpenGL ES 3.0.5] section 3.8.13 page 160. | 
|  | bool TextureCubeMap::isCubeComplete() const | 
|  | { | 
|  | if(!isBaseLevelDefined()) | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | 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()) | 
|  | { | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool TextureCubeMap::isMipmapCubeComplete() const | 
|  | { | 
|  | if(mBaseLevel > mMaxLevel) | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if(!isCubeComplete()) | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | GLsizei size = image[0][mBaseLevel]->getWidth(); | 
|  | int p = log2(size) + mBaseLevel; | 
|  | int q = std::min(p, 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; | 
|  | } | 
|  |  | 
|  | int i = level - mBaseLevel; | 
|  |  | 
|  | if(image[face][level]->getWidth() != std::max(1, size >> i)) | 
|  | { | 
|  | return false; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void TextureCubeMap::updateBorders(int level) | 
|  | { | 
|  | egl::Image *posX = image[CubeFaceIndex(GL_TEXTURE_CUBE_MAP_POSITIVE_X)][level]; | 
|  | egl::Image *negX = image[CubeFaceIndex(GL_TEXTURE_CUBE_MAP_NEGATIVE_X)][level]; | 
|  | egl::Image *posY = image[CubeFaceIndex(GL_TEXTURE_CUBE_MAP_POSITIVE_Y)][level]; | 
|  | egl::Image *negY = image[CubeFaceIndex(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y)][level]; | 
|  | egl::Image *posZ = image[CubeFaceIndex(GL_TEXTURE_CUBE_MAP_POSITIVE_Z)][level]; | 
|  | egl::Image *negZ = image[CubeFaceIndex(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z)][level]; | 
|  |  | 
|  | if(!posX || !negX || !posY || !negY || !posZ || !negZ) | 
|  | { | 
|  | return; | 
|  | } | 
|  |  | 
|  | if(posX->getBorder() == 0)   // Non-seamless cube map. | 
|  | { | 
|  | return; | 
|  | } | 
|  |  | 
|  | if(!posX->hasDirtyContents() || !posY->hasDirtyContents() || !posZ->hasDirtyContents() || !negX->hasDirtyContents() || !negY->hasDirtyContents() || !negZ->hasDirtyContents()) | 
|  | { | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Copy top / bottom first. | 
|  | posX->copyCubeEdge(sw::Surface::BOTTOM, negY, sw::Surface::RIGHT); | 
|  | posY->copyCubeEdge(sw::Surface::BOTTOM, posZ, sw::Surface::TOP); | 
|  | posZ->copyCubeEdge(sw::Surface::BOTTOM, negY, sw::Surface::TOP); | 
|  | negX->copyCubeEdge(sw::Surface::BOTTOM, negY, sw::Surface::LEFT); | 
|  | negY->copyCubeEdge(sw::Surface::BOTTOM, negZ, sw::Surface::BOTTOM); | 
|  | negZ->copyCubeEdge(sw::Surface::BOTTOM, negY, sw::Surface::BOTTOM); | 
|  |  | 
|  | posX->copyCubeEdge(sw::Surface::TOP, posY, sw::Surface::RIGHT); | 
|  | posY->copyCubeEdge(sw::Surface::TOP, negZ, sw::Surface::TOP); | 
|  | posZ->copyCubeEdge(sw::Surface::TOP, posY, sw::Surface::BOTTOM); | 
|  | negX->copyCubeEdge(sw::Surface::TOP, posY, sw::Surface::LEFT); | 
|  | negY->copyCubeEdge(sw::Surface::TOP, posZ, sw::Surface::BOTTOM); | 
|  | negZ->copyCubeEdge(sw::Surface::TOP, posY, sw::Surface::TOP); | 
|  |  | 
|  | // Copy left / right after top and bottom are done. | 
|  | // The corner colors will be computed assuming top / bottom are already set. | 
|  | posX->copyCubeEdge(sw::Surface::RIGHT, negZ, sw::Surface::LEFT); | 
|  | posY->copyCubeEdge(sw::Surface::RIGHT, posX, sw::Surface::TOP); | 
|  | posZ->copyCubeEdge(sw::Surface::RIGHT, posX, sw::Surface::LEFT); | 
|  | negX->copyCubeEdge(sw::Surface::RIGHT, posZ, sw::Surface::LEFT); | 
|  | negY->copyCubeEdge(sw::Surface::RIGHT, posX, sw::Surface::BOTTOM); | 
|  | negZ->copyCubeEdge(sw::Surface::RIGHT, negX, sw::Surface::LEFT); | 
|  |  | 
|  | posX->copyCubeEdge(sw::Surface::LEFT, posZ, sw::Surface::RIGHT); | 
|  | posY->copyCubeEdge(sw::Surface::LEFT, negX, sw::Surface::TOP); | 
|  | posZ->copyCubeEdge(sw::Surface::LEFT, negX, sw::Surface::RIGHT); | 
|  | negX->copyCubeEdge(sw::Surface::LEFT, negZ, sw::Surface::RIGHT); | 
|  | negY->copyCubeEdge(sw::Surface::LEFT, negX, sw::Surface::BOTTOM); | 
|  | negZ->copyCubeEdge(sw::Surface::LEFT, posX, sw::Surface::RIGHT); | 
|  |  | 
|  | posX->markContentsClean(); | 
|  | posY->markContentsClean(); | 
|  | posZ->markContentsClean(); | 
|  | negX->markContentsClean(); | 
|  | negY->markContentsClean(); | 
|  | negZ->markContentsClean(); | 
|  | } | 
|  |  | 
|  | bool TextureCubeMap::isCompressed(GLenum target, GLint level) const | 
|  | { | 
|  | return IsCompressed(getFormat(target, level)); | 
|  | } | 
|  |  | 
|  | 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, GLint internalformat, GLenum format, GLenum type, const gl::PixelStorageModes &unpackParameters, const void *pixels) | 
|  | { | 
|  | int face = CubeFaceIndex(target); | 
|  |  | 
|  | if(image[face][level]) | 
|  | { | 
|  | image[face][level]->release(); | 
|  | } | 
|  |  | 
|  | image[face][level] = egl::Image::create(this, width, height, 1, 1, internalformat); | 
|  |  | 
|  | if(!image[face][level]) | 
|  | { | 
|  | return error(GL_OUT_OF_MEMORY); | 
|  | } | 
|  |  | 
|  | Texture::setImage(format, type, unpackParameters, pixels, image[face][level]); | 
|  | } | 
|  |  | 
|  | void TextureCubeMap::copyImage(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, Renderbuffer *source) | 
|  | { | 
|  | int face = CubeFaceIndex(target); | 
|  |  | 
|  | if(image[face][level]) | 
|  | { | 
|  | image[face][level]->release(); | 
|  | } | 
|  |  | 
|  | image[face][level] = egl::Image::create(this, width, height, 1, 1, internalformat); | 
|  |  | 
|  | if(!image[face][level]) | 
|  | { | 
|  | return error(GL_OUT_OF_MEMORY); | 
|  | } | 
|  |  | 
|  | if(width != 0 && height != 0) | 
|  | { | 
|  | egl::Image *renderTarget = source->getRenderTarget(); | 
|  |  | 
|  | if(!renderTarget) | 
|  | { | 
|  | ERR("Failed to retrieve the render target."); | 
|  | return error(GL_OUT_OF_MEMORY); | 
|  | } | 
|  |  | 
|  | sw::SliceRect sourceRect(x, y, x + width, y + height, 0); | 
|  | sourceRect.clip(0, 0, renderTarget->getWidth(), renderTarget->getHeight()); | 
|  |  | 
|  | copy(renderTarget, sourceRect, 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, Renderbuffer *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); | 
|  | } | 
|  |  | 
|  | if(width > 0 && height > 0) | 
|  | { | 
|  | egl::Image *renderTarget = source->getRenderTarget(); | 
|  |  | 
|  | if(!renderTarget) | 
|  | { | 
|  | ERR("Failed to retrieve the render target."); | 
|  | return error(GL_OUT_OF_MEMORY); | 
|  | } | 
|  |  | 
|  | sw::SliceRect sourceRect(x, y, x + width, y + height, 0); | 
|  | sourceRect.clip(0, 0, renderTarget->getWidth(), renderTarget->getHeight()); | 
|  |  | 
|  | copy(renderTarget, sourceRect, xoffset, yoffset, zoffset, image[face][level]); | 
|  |  | 
|  | renderTarget->release(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void TextureCubeMap::generateMipmaps() | 
|  | { | 
|  | if(!isCubeComplete()) | 
|  | { | 
|  | return error(GL_INVALID_OPERATION); | 
|  | } | 
|  |  | 
|  | int p = log2(image[0][mBaseLevel]->getWidth()) + mBaseLevel; | 
|  | int q = std::min(p, mMaxLevel); | 
|  |  | 
|  | for(int f = 0; f < 6; f++) | 
|  | { | 
|  | ASSERT(image[f][mBaseLevel]); | 
|  |  | 
|  | for(int i = mBaseLevel + 1; i <= q; i++) | 
|  | { | 
|  | if(image[f][i]) | 
|  | { | 
|  | image[f][i]->release(); | 
|  | } | 
|  |  | 
|  | image[f][i] = egl::Image::create(this, std::max(image[f][mBaseLevel]->getWidth() >> i, 1), std::max(image[f][mBaseLevel]->getHeight() >> i, 1), 1, 1, image[f][mBaseLevel]->getFormat()); | 
|  |  | 
|  | 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) | 
|  | { | 
|  | 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) | 
|  | { | 
|  | mSurface = nullptr; | 
|  |  | 
|  | mColorbufferProxy = nullptr; | 
|  | mProxyRefs = 0; | 
|  | } | 
|  |  | 
|  | Texture3D::~Texture3D() | 
|  | { | 
|  | image.unbind(this); | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | GLint Texture3D::getFormat(GLenum target, GLint level) const | 
|  | { | 
|  | ASSERT(target == getTarget()); | 
|  | return image[level] ? image[level]->getFormat() : GL_NONE; | 
|  | } | 
|  |  | 
|  | int Texture3D::getTopLevel() const | 
|  | { | 
|  | int level = mBaseLevel; | 
|  |  | 
|  | while(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS && image[level]) | 
|  | { | 
|  | level++; | 
|  | } | 
|  |  | 
|  | return level - 1; | 
|  | } | 
|  |  | 
|  | bool Texture3D::requiresSync() const | 
|  | { | 
|  | for(int level = 0; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++) | 
|  | { | 
|  | if(image[level] && image[level]->requiresSync()) | 
|  | { | 
|  | return true; | 
|  | } | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | void Texture3D::setImage(GLint level, GLsizei width, GLsizei height, GLsizei depth, GLint internalformat, GLenum format, GLenum type, const gl::PixelStorageModes &unpackParameters, const void *pixels) | 
|  | { | 
|  | if(image[level]) | 
|  | { | 
|  | image[level]->release(); | 
|  | } | 
|  |  | 
|  | image[level] = egl::Image::create(this, width, height, depth, 0, internalformat); | 
|  |  | 
|  | if(!image[level]) | 
|  | { | 
|  | return error(GL_OUT_OF_MEMORY); | 
|  | } | 
|  |  | 
|  | Texture::setImage(format, type, unpackParameters, pixels, image[level]); | 
|  | } | 
|  |  | 
|  | void Texture3D::releaseTexImage() | 
|  | { | 
|  | UNREACHABLE(0);   // 3D textures cannot have an EGL surface bound as an image | 
|  | } | 
|  |  | 
|  | void Texture3D::setCompressedImage(GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei depth, GLsizei imageSize, const void *pixels) | 
|  | { | 
|  | if(image[level]) | 
|  | { | 
|  | image[level]->release(); | 
|  | } | 
|  |  | 
|  | image[level] = egl::Image::create(this, width, height, depth, 0, format); | 
|  |  | 
|  | 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 gl::PixelStorageModes &unpackParameters, const void *pixels) | 
|  | { | 
|  | Texture::subImage(xoffset, yoffset, zoffset, width, height, depth, format, type, unpackParameters, 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 internalformat, GLint x, GLint y, GLint z, GLsizei width, GLsizei height, GLsizei depth, Renderbuffer *source) | 
|  | { | 
|  | if(image[level]) | 
|  | { | 
|  | image[level]->release(); | 
|  | } | 
|  |  | 
|  | image[level] = egl::Image::create(this, width, height, depth, 0, internalformat); | 
|  |  | 
|  | if(!image[level]) | 
|  | { | 
|  | return error(GL_OUT_OF_MEMORY); | 
|  | } | 
|  |  | 
|  | if(width != 0 && height != 0 && depth != 0) | 
|  | { | 
|  | egl::Image *renderTarget = source->getRenderTarget(); | 
|  |  | 
|  | if(!renderTarget) | 
|  | { | 
|  | ERR("Failed to retrieve the render target."); | 
|  | return error(GL_OUT_OF_MEMORY); | 
|  | } | 
|  |  | 
|  | sw::SliceRect sourceRect(x, y, x + width, y + height, z); | 
|  | sourceRect.clip(0, 0, renderTarget->getWidth(), renderTarget->getHeight()); | 
|  |  | 
|  | for(GLint sliceZ = 0; sliceZ < depth; sliceZ++, sourceRect.slice++) | 
|  | { | 
|  | copy(renderTarget, sourceRect, 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, Renderbuffer *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); | 
|  | } | 
|  |  | 
|  | if(width > 0 && height > 0) | 
|  | { | 
|  | egl::Image *renderTarget = source->getRenderTarget(); | 
|  |  | 
|  | if(!renderTarget) | 
|  | { | 
|  | ERR("Failed to retrieve the render target."); | 
|  | return error(GL_OUT_OF_MEMORY); | 
|  | } | 
|  |  | 
|  | sw::SliceRect sourceRect = {x, y, x + width, y + height, 0}; | 
|  | sourceRect.clip(0, 0, renderTarget->getWidth(), renderTarget->getHeight()); | 
|  |  | 
|  | copy(renderTarget, sourceRect, xoffset, yoffset, zoffset, image[level]); | 
|  |  | 
|  | renderTarget->release(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void Texture3D::setSharedImage(egl::Image *sharedImage) | 
|  | { | 
|  | sharedImage->addRef(); | 
|  |  | 
|  | if(image[0]) | 
|  | { | 
|  | image[0]->release(); | 
|  | } | 
|  |  | 
|  | image[0] = sharedImage; | 
|  | } | 
|  |  | 
|  | bool Texture3D::isBaseLevelDefined() const | 
|  | { | 
|  | if(!image[mBaseLevel]) | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | GLsizei width = image[mBaseLevel]->getWidth(); | 
|  | GLsizei height = image[mBaseLevel]->getHeight(); | 
|  | GLsizei depth = image[mBaseLevel]->getDepth(); | 
|  |  | 
|  | if(width <= 0 || height <= 0 || depth <= 0) | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Tests for 3D texture sampling completeness. [OpenGL ES 3.0.5] section 3.8.13 page 160. | 
|  | bool Texture3D::isSamplerComplete(Sampler *sampler) const | 
|  | { | 
|  | if(!isBaseLevelDefined()) | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if(isMipmapFiltered(sampler)) | 
|  | { | 
|  | if(!isMipmapComplete()) | 
|  | { | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Tests for 3D texture (mipmap) completeness. [OpenGL ES 3.0.5] section 3.8.13 page 160. | 
|  | bool Texture3D::isMipmapComplete() const | 
|  | { | 
|  | if(mBaseLevel > mMaxLevel) | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | GLsizei width = image[mBaseLevel]->getWidth(); | 
|  | GLsizei height = image[mBaseLevel]->getHeight(); | 
|  | GLsizei depth = image[mBaseLevel]->getDepth(); | 
|  | bool isTexture2DArray = getTarget() == GL_TEXTURE_2D_ARRAY; | 
|  |  | 
|  | int maxsize = isTexture2DArray ? std::max(width, height) : std::max(std::max(width, height), depth); | 
|  | int p = log2(maxsize) + mBaseLevel; | 
|  | int q = std::min(p, mMaxLevel); | 
|  |  | 
|  | for(int level = mBaseLevel + 1; level <= q; level++) | 
|  | { | 
|  | if(!image[level]) | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if(image[level]->getFormat() != image[mBaseLevel]->getFormat()) | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | int i = level - mBaseLevel; | 
|  |  | 
|  | if(image[level]->getWidth() != std::max(1, width >> i)) | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if(image[level]->getHeight() != std::max(1, height >> i)) | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | int levelDepth = isTexture2DArray ? depth : std::max(1, depth >> i); | 
|  | if(image[level]->getDepth() != levelDepth) | 
|  | { | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool Texture3D::isCompressed(GLenum target, GLint level) const | 
|  | { | 
|  | return IsCompressed(getFormat(target, level)); | 
|  | } | 
|  |  | 
|  | bool Texture3D::isDepth(GLenum target, GLint level) const | 
|  | { | 
|  | return IsDepthTexture(getFormat(target, level)); | 
|  | } | 
|  |  | 
|  | void Texture3D::generateMipmaps() | 
|  | { | 
|  | if(!image[mBaseLevel]) | 
|  | { | 
|  | return;   // Image unspecified. Not an error. | 
|  | } | 
|  |  | 
|  | if(image[mBaseLevel]->getWidth() == 0 || image[mBaseLevel]->getHeight() == 0 || image[mBaseLevel]->getDepth() == 0) | 
|  | { | 
|  | return;   // Zero dimension. Not an error. | 
|  | } | 
|  |  | 
|  | int maxsize = std::max(std::max(image[mBaseLevel]->getWidth(), image[mBaseLevel]->getHeight()), image[mBaseLevel]->getDepth()); | 
|  | int p = log2(maxsize) + mBaseLevel; | 
|  | int q = std::min(p, mMaxLevel); | 
|  |  | 
|  | for(int i = mBaseLevel + 1; i <= q; i++) | 
|  | { | 
|  | if(image[i]) | 
|  | { | 
|  | image[i]->release(); | 
|  | } | 
|  |  | 
|  | image[i] = egl::Image::create(this, std::max(image[mBaseLevel]->getWidth() >> i, 1), std::max(image[mBaseLevel]->getHeight() >> i, 1), std::max(image[mBaseLevel]->getDepth() >> i, 1), 0, image[mBaseLevel]->getFormat()); | 
|  |  | 
|  | 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) | 
|  | { | 
|  | if(target != getTarget()) | 
|  | { | 
|  | return error(GL_INVALID_OPERATION, (Renderbuffer*)nullptr); | 
|  | } | 
|  |  | 
|  | if(!mColorbufferProxy) | 
|  | { | 
|  | mColorbufferProxy = new Renderbuffer(name, new RenderbufferTexture3D(this, level)); | 
|  | } | 
|  | else | 
|  | { | 
|  | mColorbufferProxy->setLevel(level); | 
|  | } | 
|  |  | 
|  | 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() | 
|  | { | 
|  | if(!image[mBaseLevel]) | 
|  | { | 
|  | return;   // Image unspecified. Not an error. | 
|  | } | 
|  |  | 
|  | if(image[mBaseLevel]->getWidth() == 0 || image[mBaseLevel]->getHeight() == 0 || image[mBaseLevel]->getDepth() == 0) | 
|  | { | 
|  | return;   // Zero dimension. Not an error. | 
|  | } | 
|  |  | 
|  | int depth = image[mBaseLevel]->getDepth(); | 
|  | int maxsize = std::max(image[mBaseLevel]->getWidth(), image[mBaseLevel]->getHeight()); | 
|  | int p = log2(maxsize) + mBaseLevel; | 
|  | int q = std::min(p, mMaxLevel); | 
|  |  | 
|  | for(int i = mBaseLevel + 1; i <= q; i++) | 
|  | { | 
|  | if(image[i]) | 
|  | { | 
|  | image[i]->release(); | 
|  | } | 
|  |  | 
|  | GLsizei w = std::max(image[mBaseLevel]->getWidth() >> i, 1); | 
|  | GLsizei h = std::max(image[mBaseLevel]->getHeight() >> i, 1); | 
|  | image[i] = egl::Image::create(this, w, h, depth, 0, image[mBaseLevel]->getFormat()); | 
|  |  | 
|  | 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::SliceRectF srcRect(0.0f, 0.0f, static_cast<float>(srcw), static_cast<float>(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; | 
|  | } | 
|  |  | 
|  | } | 
|  |  | 
|  | NO_SANITIZE_FUNCTION egl::Image *createBackBuffer(int width, int height, sw::Format format, int multiSampleDepth) | 
|  | { | 
|  | if(width > es2::IMPLEMENTATION_MAX_RENDERBUFFER_SIZE || height > es2::IMPLEMENTATION_MAX_RENDERBUFFER_SIZE) | 
|  | { | 
|  | ERR("Invalid parameters: %dx%d", width, height); | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | GLenum internalformat = sw2es::ConvertBackBufferFormat(format); | 
|  |  | 
|  | return egl::Image::create(width, height, internalformat, multiSampleDepth, false); | 
|  | } | 
|  |  | 
|  | NO_SANITIZE_FUNCTION egl::Image *createBackBufferFromClientBuffer(const egl::ClientBuffer& clientBuffer) | 
|  | { | 
|  | if(clientBuffer.getWidth() > es2::IMPLEMENTATION_MAX_RENDERBUFFER_SIZE || | 
|  | clientBuffer.getHeight() > es2::IMPLEMENTATION_MAX_RENDERBUFFER_SIZE) | 
|  | { | 
|  | ERR("Invalid parameters: %dx%d", clientBuffer.getWidth(), clientBuffer.getHeight()); | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | return egl::Image::create(clientBuffer); | 
|  | } | 
|  |  | 
|  | NO_SANITIZE_FUNCTION egl::Image *createDepthStencil(int width, int height, sw::Format format, int multiSampleDepth) | 
|  | { | 
|  | if(width > es2::IMPLEMENTATION_MAX_RENDERBUFFER_SIZE || height > es2::IMPLEMENTATION_MAX_RENDERBUFFER_SIZE) | 
|  | { | 
|  | 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); | 
|  | } | 
|  |  | 
|  | GLenum internalformat = sw2es::ConvertDepthStencilFormat(format); | 
|  |  | 
|  | egl::Image *surface = egl::Image::create(width, height, internalformat, multiSampleDepth, lockable); | 
|  |  | 
|  | if(!surface) | 
|  | { | 
|  | ERR("Out of memory"); | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | return surface; | 
|  | } |