OSDN Git Service

Make Blitter part of Renderer.
[android-x86/external-swiftshader.git] / src / OpenGL / libGLESv2 / Texture.cpp
index de835c4..95cb8db 100644 (file)
-// SwiftShader Software Renderer\r
-//\r
-// Copyright(c) 2005-2013 TransGaming Inc.\r
-//\r
-// All rights reserved. No part of this software may be copied, distributed, transmitted,\r
-// transcribed, stored in a retrieval system, translated into any human or computer\r
-// language by any means, or disclosed to third parties without the explicit written\r
-// agreement of TransGaming Inc. Without such an agreement, no rights or licenses, express\r
-// or implied, including but not limited to any patent rights, are granted to you.\r
-//\r
-\r
-// Texture.cpp: Implements the Texture class and its derived classes\r
-// Texture2D, TextureCubeMap, Texture3D and Texture2DArray. Implements GL texture objects\r
-// and related functionality. [OpenGL ES 2.0.24] section 3.7 page 63.\r
-\r
-#include "Texture.h"\r
-\r
-#include "main.h"\r
-#include "mathutil.h"\r
-#include "Framebuffer.h"\r
-#include "Device.hpp"\r
-#include "libEGL/Display.h"\r
-#include "libEGL/Surface.h"\r
-#include "common/debug.h"\r
-\r
-#include <algorithm>\r
-\r
-namespace es2\r
-{\r
-\r
-Texture::Texture(GLuint name) : egl::Texture(name)\r
-{\r
-    mMinFilter = GL_NEAREST_MIPMAP_LINEAR;\r
-    mMagFilter = GL_LINEAR;\r
-    mWrapS = GL_REPEAT;\r
-    mWrapT = GL_REPEAT;\r
-       mWrapR = GL_REPEAT;\r
-       mMaxAnisotropy = 1.0f;\r
-       mBaseLevel = 0;\r
-       mCompareFunc = GL_LEQUAL;\r
-       mCompareMode = GL_NONE;\r
-       mImmutableFormat = GL_FALSE;\r
-       mMaxLevel = 1000;\r
-       mMaxLOD = 1000;\r
-       mMinLOD = -1000;\r
-       mSwizzleR = GL_RED;\r
-       mSwizzleG = GL_GREEN;\r
-       mSwizzleB = GL_BLUE;\r
-       mSwizzleA = GL_ALPHA;\r
-\r
-       resource = new sw::Resource(0);\r
-}\r
-\r
-Texture::~Texture()\r
-{\r
-       resource->destruct();\r
-}\r
-\r
-sw::Resource *Texture::getResource() const\r
-{\r
-       return resource;\r
-}\r
-\r
-// Returns true on successful filter state update (valid enum parameter)\r
-bool Texture::setMinFilter(GLenum filter)\r
-{\r
-    switch(filter)\r
-    {\r
-    case GL_NEAREST_MIPMAP_NEAREST:\r
-    case GL_LINEAR_MIPMAP_NEAREST:\r
-    case GL_NEAREST_MIPMAP_LINEAR:\r
-    case GL_LINEAR_MIPMAP_LINEAR:\r
-        if(getTarget() == GL_TEXTURE_EXTERNAL_OES)\r
-        {\r
-            return false;\r
-        }\r
-        // Fall through\r
-    case GL_NEAREST:\r
-    case GL_LINEAR:\r
-        mMinFilter = filter;\r
-        return true;\r
-    default:\r
-        return false;\r
-    }\r
-}\r
-\r
-// Returns true on successful filter state update (valid enum parameter)\r
-bool Texture::setMagFilter(GLenum filter)\r
-{\r
-    switch(filter)\r
-    {\r
-    case GL_NEAREST:\r
-    case GL_LINEAR:\r
-        mMagFilter = filter;\r
-        return true;\r
-    default:\r
-        return false;\r
-    }\r
-}\r
-\r
-// Returns true on successful wrap state update (valid enum parameter)\r
-bool Texture::setWrapS(GLenum wrap)\r
-{\r
-    switch(wrap)\r
-    {\r
-    case GL_REPEAT:\r
-    case GL_MIRRORED_REPEAT:\r
-        if(getTarget() == GL_TEXTURE_EXTERNAL_OES)\r
-        {\r
-            return false;\r
-        }\r
-        // Fall through\r
-    case GL_CLAMP_TO_EDGE:\r
-        mWrapS = wrap;\r
-        return true;\r
-    default:\r
-        return false;\r
-    }\r
-}\r
-\r
-// Returns true on successful wrap state update (valid enum parameter)\r
-bool Texture::setWrapT(GLenum wrap)\r
-{\r
-       switch(wrap)\r
-       {\r
-       case GL_REPEAT:\r
-       case GL_MIRRORED_REPEAT:\r
-               if(getTarget() == GL_TEXTURE_EXTERNAL_OES)\r
-               {\r
-                       return false;\r
-               }\r
-               // Fall through\r
-       case GL_CLAMP_TO_EDGE:\r
-               mWrapT = wrap;\r
-               return true;\r
-       default:\r
-               return false;\r
-       }\r
-}\r
-\r
-// Returns true on successful wrap state update (valid enum parameter)\r
-bool Texture::setWrapR(GLenum wrap)\r
-{\r
-       switch(wrap)\r
-       {\r
-       case GL_REPEAT:\r
-       case GL_MIRRORED_REPEAT:\r
-               if(getTarget() == GL_TEXTURE_EXTERNAL_OES)\r
-               {\r
-                       return false;\r
-               }\r
-               // Fall through\r
-       case GL_CLAMP_TO_EDGE:\r
-               mWrapR = wrap;\r
-               return true;\r
-       default:\r
-               return false;\r
-       }\r
-}\r
-\r
-// Returns true on successful max anisotropy update (valid anisotropy value)\r
-bool Texture::setMaxAnisotropy(float textureMaxAnisotropy)\r
-{\r
-    textureMaxAnisotropy = std::min(textureMaxAnisotropy, MAX_TEXTURE_MAX_ANISOTROPY);\r
-\r
-    if(textureMaxAnisotropy < 1.0f)\r
-    {\r
-        return false;\r
-    }\r
-    \r
-       if(mMaxAnisotropy != textureMaxAnisotropy)\r
-    {\r
-        mMaxAnisotropy = textureMaxAnisotropy;\r
-    }\r
-\r
-    return true;\r
-}\r
-\r
-bool Texture::setBaseLevel(GLint baseLevel)\r
-{\r
-       mBaseLevel = baseLevel;\r
-       return true;\r
-}\r
-\r
-bool Texture::setCompareFunc(GLenum compareFunc)\r
-{\r
-       switch(compareFunc)\r
-       {\r
-       case GL_LEQUAL:\r
-       case GL_GEQUAL:\r
-       case GL_LESS:\r
-       case GL_GREATER:\r
-       case GL_EQUAL:\r
-       case GL_NOTEQUAL:\r
-       case GL_ALWAYS:\r
-       case GL_NEVER:\r
-               mCompareFunc = compareFunc;\r
-               return true;\r
-       default:\r
-               return false;\r
-       }\r
-}\r
-\r
-bool Texture::setCompareMode(GLenum compareMode)\r
-{\r
-       switch(compareMode)\r
-       {\r
-       case GL_COMPARE_REF_TO_TEXTURE:\r
-       case GL_NONE:\r
-               mCompareMode = compareMode;\r
-               return true;\r
-       default:\r
-               return false;\r
-       }\r
-}\r
-\r
-bool Texture::setImmutableFormat(GLboolean immutableFormat)\r
-{\r
-       mImmutableFormat = immutableFormat;\r
-       return true;\r
-}\r
-\r
-bool Texture::setMaxLevel(GLint maxLevel)\r
-{\r
-       mMaxLevel = maxLevel;\r
-       return true;\r
-}\r
-\r
-bool Texture::setMaxLOD(GLfloat maxLOD)\r
-{\r
-       mMaxLOD = maxLOD;\r
-       return true;\r
-}\r
-\r
-bool Texture::setMinLOD(GLfloat minLOD)\r
-{\r
-       mMinLOD = minLOD;\r
-       return true;\r
-}\r
-\r
-bool Texture::setSwizzleR(GLenum swizzleR)\r
-{\r
-       switch(swizzleR)\r
-       {\r
-       case GL_RED:\r
-       case GL_GREEN:\r
-       case GL_BLUE:\r
-       case GL_ALPHA:\r
-       case GL_ZERO:\r
-       case GL_ONE:\r
-               mSwizzleR = swizzleR;\r
-               return true;\r
-       default:\r
-               return false;\r
-       }\r
-}\r
-\r
-bool Texture::setSwizzleG(GLenum swizzleG)\r
-{\r
-       switch(swizzleG)\r
-       {\r
-       case GL_RED:\r
-       case GL_GREEN:\r
-       case GL_BLUE:\r
-       case GL_ALPHA:\r
-       case GL_ZERO:\r
-       case GL_ONE:\r
-               mSwizzleG = swizzleG;\r
-               return true;\r
-       default:\r
-               return false;\r
-       }\r
-}\r
-\r
-bool Texture::setSwizzleB(GLenum swizzleB)\r
-{\r
-       switch(swizzleB)\r
-       {\r
-       case GL_RED:\r
-       case GL_GREEN:\r
-       case GL_BLUE:\r
-       case GL_ALPHA:\r
-       case GL_ZERO:\r
-       case GL_ONE:\r
-               mSwizzleB = swizzleB;\r
-               return true;\r
-       default:\r
-               return false;\r
-       }\r
-}\r
-\r
-bool Texture::setSwizzleA(GLenum swizzleA)\r
-{\r
-       switch(swizzleA)\r
-       {\r
-       case GL_RED:\r
-       case GL_GREEN:\r
-       case GL_BLUE:\r
-       case GL_ALPHA:\r
-       case GL_ZERO:\r
-       case GL_ONE:\r
-               mSwizzleA = swizzleA;\r
-               return true;\r
-       default:\r
-               return false;\r
-       }\r
-}\r
-\r
-GLenum Texture::getMinFilter() const\r
-{\r
-    return mMinFilter;\r
-}\r
-\r
-GLenum Texture::getMagFilter() const\r
-{\r
-    return mMagFilter;\r
-}\r
-\r
-GLenum Texture::getWrapS() const\r
-{\r
-    return mWrapS;\r
-}\r
-\r
-GLenum Texture::getWrapT() const\r
-{\r
-       return mWrapT;\r
-}\r
-\r
-GLenum Texture::getWrapR() const\r
-{\r
-       return mWrapR;\r
-}\r
-\r
-GLfloat Texture::getMaxAnisotropy() const\r
-{\r
-    return mMaxAnisotropy;\r
-}\r
-\r
-GLint Texture::getBaseLevel() const\r
-{\r
-       return mBaseLevel;\r
-}\r
-GLenum Texture::getCompareFunc() const\r
-{\r
-       return mCompareFunc;\r
-}\r
-GLenum Texture::getCompareMode() const\r
-{\r
-       return mCompareMode;\r
-}\r
-GLboolean Texture::getImmutableFormat() const\r
-{\r
-       return mImmutableFormat;\r
-}\r
-GLint Texture::getMaxLevel() const\r
-{\r
-       return mMaxLevel;\r
-}\r
-GLfloat Texture::getMaxLOD() const\r
-{\r
-       return mMaxLOD;\r
-}\r
-GLfloat Texture::getMinLOD() const\r
-{\r
-       return mMinLOD;\r
-}\r
-GLenum Texture::getSwizzleR() const\r
-{\r
-       return mSwizzleR;\r
-}\r
-GLenum Texture::getSwizzleG() const\r
-{\r
-       return mSwizzleG;\r
-}\r
-GLenum Texture::getSwizzleB() const\r
-{\r
-       return mSwizzleB;\r
-}\r
-GLenum Texture::getSwizzleA() const\r
-{\r
-       return mSwizzleA;\r
-}\r
-\r
-GLsizei Texture::getDepth(GLenum target, GLint level) const\r
-{\r
-       return 1;\r
-}\r
-\r
-egl::Image *Texture::createSharedImage(GLenum target, unsigned int level)\r
-{\r
-    egl::Image *image = getRenderTarget(target, level);   // Increments reference count\r
-\r
-    if(image)\r
-    {\r
-        image->markShared();\r
-    }\r
-\r
-    return image;\r
-}\r
-\r
-void Texture::setImage(GLenum format, GLenum type, const egl::Image::UnpackInfo& unpackInfo, const void *pixels, egl::Image *image)\r
-{\r
-    if(pixels && image)\r
-    {\r
-               GLsizei depth = (getTarget() == GL_TEXTURE_3D_OES || getTarget() == GL_TEXTURE_2D_ARRAY) ? image->getDepth() : 1;\r
-               image->loadImageData(0, 0, 0, image->getWidth(), image->getHeight(), depth, format, type, unpackInfo, pixels);\r
-    }\r
-}\r
-\r
-void Texture::setCompressedImage(GLsizei imageSize, const void *pixels, egl::Image *image)\r
-{\r
-    if(pixels && image)\r
-    {\r
-               GLsizei depth = (getTarget() == GL_TEXTURE_3D_OES || getTarget() == GL_TEXTURE_2D_ARRAY) ? image->getDepth() : 1;\r
-               image->loadCompressedData(0, 0, 0, image->getWidth(), image->getHeight(), depth, imageSize, pixels);\r
-    }\r
-}\r
-\r
-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)\r
-{\r
-       if(!image)\r
-       {\r
-               return error(GL_INVALID_OPERATION);\r
-       }\r
-\r
-       if(width + xoffset > image->getWidth() || height + yoffset > image->getHeight() || depth + zoffset > image->getDepth())\r
-    {\r
-        return error(GL_INVALID_VALUE);\r
-    }\r
-\r
-    if(IsCompressed(image->getFormat(), egl::getClientVersion()))\r
-    {\r
-        return error(GL_INVALID_OPERATION);\r
-    }\r
-\r
-    if(format != image->getFormat())\r
-    {\r
-        return error(GL_INVALID_OPERATION);\r
-    }\r
-\r
-    if(pixels)\r
-    {\r
-               image->loadImageData(xoffset, yoffset, zoffset, width, height, depth, format, type, unpackInfo, pixels);\r
-    }\r
-}\r
-\r
-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)\r
-{\r
-       if(!image)\r
-       {\r
-               return error(GL_INVALID_OPERATION);\r
-       }\r
-\r
-    if(width + xoffset > image->getWidth() || height + yoffset > image->getHeight() || depth + zoffset > image->getDepth())\r
-    {\r
-        return error(GL_INVALID_VALUE);\r
-    }\r
-\r
-    if(format != image->getFormat())\r
-    {\r
-        return error(GL_INVALID_OPERATION);\r
-    }\r
-\r
-    if(pixels)\r
-    {\r
-               image->loadCompressedData(xoffset, yoffset, zoffset, width, height, depth, imageSize, pixels);\r
-    }\r
-}\r
-\r
-bool Texture::copy(egl::Image *source, const sw::SliceRect &sourceRect, GLenum destFormat, GLint xoffset, GLint yoffset, GLint zoffset, egl::Image *dest)\r
-{\r
-    Device *device = getDevice();\r
-       \r
-    sw::SliceRect destRect(xoffset, yoffset, xoffset + (sourceRect.x1 - sourceRect.x0), yoffset + (sourceRect.y1 - sourceRect.y0), zoffset);\r
-    bool success = device->stretchRect(source, &sourceRect, dest, &destRect, false);\r
-\r
-    if(!success)\r
-    {\r
-        return error(GL_OUT_OF_MEMORY, false);\r
-    }\r
-\r
-    return true;\r
-}\r
-\r
-bool Texture::isMipmapFiltered() const\r
-{\r
-       switch(mMinFilter)\r
-    {\r
-    case GL_NEAREST:\r
-    case GL_LINEAR:\r
-        return false;\r
-    case GL_NEAREST_MIPMAP_NEAREST:\r
-    case GL_LINEAR_MIPMAP_NEAREST:\r
-    case GL_NEAREST_MIPMAP_LINEAR:\r
-    case GL_LINEAR_MIPMAP_LINEAR:\r
-        return true;\r
-    default: UNREACHABLE(mMinFilter);\r
-    }\r
-\r
-       return false;\r
-}\r
-\r
-Texture2D::Texture2D(GLuint name) : Texture(name)\r
-{\r
-       for(int i = 0; i < MIPMAP_LEVELS; i++)\r
-       {\r
-               image[i] = 0;\r
-       }\r
-\r
-    mSurface = NULL;\r
-\r
-       mColorbufferProxy = NULL;\r
-       mProxyRefs = 0;\r
-}\r
-\r
-Texture2D::~Texture2D()\r
-{\r
-       resource->lock(sw::DESTRUCT);\r
-\r
-       for(int i = 0; i < MIPMAP_LEVELS; i++)\r
-       {\r
-               if(image[i])\r
-               {\r
-                       image[i]->unbind(this);\r
-                       image[i] = 0;\r
-               }\r
-       }\r
-\r
-       resource->unlock();\r
-\r
-    if(mSurface)\r
-    {\r
-        mSurface->setBoundTexture(NULL);\r
-        mSurface = NULL;\r
-    }\r
-\r
-       mColorbufferProxy = NULL;\r
-}\r
-\r
-// We need to maintain a count of references to renderbuffers acting as \r
-// proxies for this texture, so that we do not attempt to use a pointer \r
-// to a renderbuffer proxy which has been deleted.\r
-void Texture2D::addProxyRef(const Renderbuffer *proxy)\r
-{\r
-    mProxyRefs++;\r
-}\r
-\r
-void Texture2D::releaseProxy(const Renderbuffer *proxy)\r
-{\r
-    if(mProxyRefs > 0)\r
-       {\r
-        mProxyRefs--;\r
-       }\r
-\r
-    if(mProxyRefs == 0)\r
-       {\r
-               mColorbufferProxy = NULL;\r
-       }\r
-}\r
-\r
-GLenum Texture2D::getTarget() const\r
-{\r
-    return GL_TEXTURE_2D;\r
-}\r
-\r
-GLsizei Texture2D::getWidth(GLenum target, GLint level) const\r
-{\r
-       ASSERT(target == GL_TEXTURE_2D);\r
-    return image[level] ? image[level]->getWidth() : 0;\r
-}\r
-\r
-GLsizei Texture2D::getHeight(GLenum target, GLint level) const\r
-{\r
-       ASSERT(target == GL_TEXTURE_2D);\r
-    return image[level] ? image[level]->getHeight() : 0;\r
-}\r
-\r
-GLenum Texture2D::getFormat(GLenum target, GLint level) const\r
-{\r
-       ASSERT(target == GL_TEXTURE_2D);\r
-    return image[level] ? image[level]->getFormat() : 0;\r
-}\r
-\r
-GLenum Texture2D::getType(GLenum target, GLint level) const\r
-{\r
-       ASSERT(target == GL_TEXTURE_2D);\r
-    return image[level] ? image[level]->getType() : 0;\r
-}\r
-\r
-sw::Format Texture2D::getInternalFormat(GLenum target, GLint level) const\r
-{\r
-       ASSERT(target == GL_TEXTURE_2D);\r
-       return image[level] ? image[level]->getInternalFormat() : sw::FORMAT_NULL;\r
-}\r
-\r
-int Texture2D::getLevelCount() const\r
-{\r
-       ASSERT(isSamplerComplete());\r
-       int levels = 0;\r
-\r
-       while(levels < MIPMAP_LEVELS && image[levels])\r
-       {\r
-               levels++;\r
-       }\r
-\r
-       return levels;\r
-}\r
-\r
-void Texture2D::setImage(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, const egl::Image::UnpackInfo& unpackInfo, const void *pixels)\r
-{\r
-       if(image[level])\r
-       {\r
-               image[level]->unbind(this);\r
-       }\r
-\r
-       image[level] = new egl::Image(this, width, height, format, type);\r
-\r
-       if(!image[level])\r
-       {\r
-               return error(GL_OUT_OF_MEMORY);\r
-       }\r
-\r
-       Texture::setImage(format, type, unpackInfo, pixels, image[level]);\r
-}\r
-\r
-void Texture2D::bindTexImage(egl::Surface *surface)\r
-{\r
-    GLenum format;\r
-\r
-    switch(surface->getInternalFormat())\r
-    {\r
-    case sw::FORMAT_A8R8G8B8:\r
-               format = GL_BGRA_EXT;\r
-        break;\r
-       case sw::FORMAT_A8B8G8R8:\r
-        format = GL_RGBA;\r
-        break;\r
-    case sw::FORMAT_X8B8G8R8:\r
-       case sw::FORMAT_X8R8G8B8:\r
-        format = GL_RGB;\r
-        break;\r
-    default:\r
-        UNIMPLEMENTED();\r
-        return;\r
-    }\r
-\r
-       for(int level = 0; level < MIPMAP_LEVELS; level++)\r
-       {\r
-               if(image[level])\r
-               {\r
-                       image[level]->unbind(this);\r
-                       image[level] = 0;\r
-               }\r
-       }\r
-\r
-       image[0] = surface->getRenderTarget();\r
-\r
-    mSurface = surface;\r
-    mSurface->setBoundTexture(this);\r
-}\r
-\r
-void Texture2D::releaseTexImage()\r
-{\r
-    for(int level = 0; level < MIPMAP_LEVELS; level++)\r
-       {\r
-               if(image[level])\r
-               {\r
-                       image[level]->unbind(this);\r
-                       image[level] = 0;\r
-               }\r
-       }\r
-}\r
-\r
-void Texture2D::setCompressedImage(GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels)\r
-{\r
-       if(image[level])\r
-       {\r
-               image[level]->unbind(this);\r
-       }\r
-\r
-       image[level] = new egl::Image(this, width, height, format, GL_UNSIGNED_BYTE);\r
-\r
-       if(!image[level])\r
-       {\r
-               return error(GL_OUT_OF_MEMORY);\r
-       }\r
-\r
-    Texture::setCompressedImage(imageSize, pixels, image[level]);\r
-}\r
-\r
-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)\r
-{\r
-       Texture::subImage(xoffset, yoffset, 0, width, height, 1, format, type, unpackInfo, pixels, image[level]);\r
-}\r
-\r
-void Texture2D::subImageCompressed(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels)\r
-{\r
-    Texture::subImageCompressed(xoffset, yoffset, 0, width, height, 1, format, imageSize, pixels, image[level]);\r
-}\r
-\r
-void Texture2D::copyImage(GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)\r
-{\r
-    egl::Image *renderTarget = source->getRenderTarget(0);\r
-\r
-    if(!renderTarget)\r
-    {\r
-        ERR("Failed to retrieve the render target.");\r
-        return error(GL_OUT_OF_MEMORY);\r
-    }\r
-\r
-       if(image[level])\r
-       {\r
-               image[level]->unbind(this);\r
-       }\r
-\r
-       image[level] = new egl::Image(this, width, height, format, GL_UNSIGNED_BYTE);\r
-\r
-       if(!image[level])\r
-       {\r
-               return error(GL_OUT_OF_MEMORY);\r
-       }\r
-\r
-    if(width != 0 && height != 0)\r
-    {\r
-               Renderbuffer* renderbuffer = source->getReadColorbuffer();\r
-\r
-               if(!renderbuffer)\r
-               {\r
-                       ERR("Failed to retrieve the source colorbuffer.");\r
-                       return;\r
-               }\r
-\r
-               sw::SliceRect sourceRect(x, y, x + width, y + height, 0);\r
-               sourceRect.clip(0, 0, renderbuffer->getWidth(), renderbuffer->getHeight());\r
-\r
-        copy(renderTarget, sourceRect, format, 0, 0, 0, image[level]);\r
-    }\r
-\r
-       renderTarget->release();\r
-}\r
-\r
-void Texture2D::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)\r
-{\r
-       if(!image[level])\r
-       {\r
-               return error(GL_INVALID_OPERATION);\r
-       }\r
-\r
-    if(xoffset + width > image[level]->getWidth() || yoffset + height > image[level]->getHeight() || zoffset != 0)\r
-    {\r
-        return error(GL_INVALID_VALUE);\r
-    }\r
-\r
-    egl::Image *renderTarget = source->getRenderTarget(0);\r
-\r
-    if(!renderTarget)\r
-    {\r
-        ERR("Failed to retrieve the render target.");\r
-        return error(GL_OUT_OF_MEMORY);\r
-    }\r
-\r
-       Renderbuffer* renderbuffer = source->getReadColorbuffer();\r
-\r
-       if(!renderbuffer)\r
-       {\r
-               ERR("Failed to retrieve the source colorbuffer.");\r
-               return;\r
-       }\r
-\r
-       sw::SliceRect sourceRect(x, y, x + width, y + height, 0);\r
-       sourceRect.clip(0, 0, renderbuffer->getWidth(), renderbuffer->getHeight());\r
-\r
-       copy(renderTarget, sourceRect, image[level]->getFormat(), xoffset, yoffset, zoffset, image[level]);\r
-\r
-       renderTarget->release();\r
-}\r
-\r
-void Texture2D::setImage(egl::Image *sharedImage)\r
-{\r
-       if (sharedImage == image[0])\r
-       {\r
-               return;\r
-       }\r
-\r
-       sharedImage->addRef();\r
-\r
-    if(image[0])\r
-    {\r
-        image[0]->unbind(this);\r
-    }\r
-\r
-    image[0] = sharedImage;\r
-}\r
-\r
-// Tests for 2D texture sampling completeness. [OpenGL ES 2.0.24] section 3.8.2 page 85.\r
-bool Texture2D::isSamplerComplete() const\r
-{\r
-       if(!image[0])\r
-       {\r
-               return false;\r
-       }\r
-\r
-    GLsizei width = image[0]->getWidth();\r
-    GLsizei height = image[0]->getHeight();\r
-\r
-    if(width <= 0 || height <= 0)\r
-    {\r
-        return false;\r
-    }\r
-\r
-    if(isMipmapFiltered())\r
-    {\r
-        if(!isMipmapComplete())\r
-        {\r
-            return false;\r
-        }\r
-    }\r
-\r
-    return true;\r
-}\r
-\r
-// Tests for 2D texture (mipmap) completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.\r
-bool Texture2D::isMipmapComplete() const\r
-{\r
-    GLsizei width = image[0]->getWidth();\r
-    GLsizei height = image[0]->getHeight();\r
-\r
-    int q = log2(std::max(width, height));\r
-\r
-    for(int level = 1; level <= q; level++)\r
-    {\r
-               if(!image[level])\r
-               {\r
-                       return false;\r
-               }\r
-\r
-        if(image[level]->getFormat() != image[0]->getFormat())\r
-        {\r
-            return false;\r
-        }\r
-\r
-        if(image[level]->getType() != image[0]->getType())\r
-        {\r
-            return false;\r
-        }\r
-\r
-        if(image[level]->getWidth() != std::max(1, width >> level))\r
-        {\r
-            return false;\r
-        }\r
-\r
-        if(image[level]->getHeight() != std::max(1, height >> level))\r
-        {\r
-            return false;\r
-        }\r
-    }\r
-\r
-    return true;\r
-}\r
-\r
-bool Texture2D::isCompressed(GLenum target, GLint level) const\r
-{\r
-    return IsCompressed(getFormat(target, level), egl::getClientVersion());\r
-}\r
-\r
-bool Texture2D::isDepth(GLenum target, GLint level) const\r
-{\r
-    return IsDepthTexture(getFormat(target, level));\r
-}\r
-\r
-void Texture2D::generateMipmaps()\r
-{\r
-       if(!image[0])\r
-       {\r
-               return;   // FIXME: error?\r
-       }\r
-\r
-    unsigned int q = log2(std::max(image[0]->getWidth(), image[0]->getHeight()));\r
-    \r
-       for(unsigned int i = 1; i <= q; i++)\r
-    {\r
-               if(image[i])\r
-               {\r
-                       image[i]->unbind(this);\r
-               }\r
-\r
-               image[i] = new egl::Image(this, std::max(image[0]->getWidth() >> i, 1), std::max(image[0]->getHeight() >> i, 1), image[0]->getFormat(), image[0]->getType());\r
-\r
-               if(!image[i])\r
-               {\r
-                       return error(GL_OUT_OF_MEMORY);\r
-               }\r
-\r
-               getDevice()->stretchRect(image[i - 1], 0, image[i], 0, true);\r
-    }\r
-}\r
-\r
-egl::Image *Texture2D::getImage(unsigned int level)\r
-{\r
-       return image[level];\r
-}\r
-\r
-Renderbuffer *Texture2D::getRenderbuffer(GLenum target, GLint level, GLint layer)\r
-{\r
-    if(target != GL_TEXTURE_2D)\r
-    {\r
-        return error(GL_INVALID_OPERATION, (Renderbuffer *)NULL);\r
-    }\r
-\r
-    if(mColorbufferProxy == NULL)\r
-    {\r
-        mColorbufferProxy = new Renderbuffer(name, new RenderbufferTexture2D(this, level));\r
-    }\r
-\r
-    return mColorbufferProxy;\r
-}\r
-\r
-egl::Image *Texture2D::getRenderTarget(GLenum target, unsigned int level)\r
-{\r
-    ASSERT(target == GL_TEXTURE_2D);\r
-       ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);\r
-\r
-       if(image[level])\r
-       {\r
-               image[level]->addRef();\r
-       }\r
-\r
-       return image[level];\r
-}\r
-\r
-bool Texture2D::isShared(GLenum target, unsigned int level) const\r
-{\r
-    ASSERT(target == GL_TEXTURE_2D);\r
-    ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);\r
-\r
-    if(mSurface)   // Bound to an EGLSurface\r
-    {\r
-        return true;\r
-    }\r
-\r
-    if(!image[level])\r
-    {\r
-        return false;\r
-    }\r
-\r
-    return image[level]->isShared();\r
-}\r
-\r
-TextureCubeMap::TextureCubeMap(GLuint name) : Texture(name)\r
-{\r
-       for(int f = 0; f < 6; f++)\r
-       {\r
-               for(int i = 0; i < MIPMAP_LEVELS; i++)\r
-               {\r
-                       image[f][i] = 0;\r
-               }\r
-       }\r
-\r
-       for(int f = 0; f < 6; f++)\r
-    {\r
-        mFaceProxies[f] = NULL;\r
-        mFaceProxyRefs[f] = 0;\r
-       }\r
-}\r
-\r
-TextureCubeMap::~TextureCubeMap()\r
-{\r
-       resource->lock(sw::DESTRUCT);\r
-\r
-       for(int f = 0; f < 6; f++)\r
-       {\r
-               for(int i = 0; i < MIPMAP_LEVELS; i++)\r
-               {\r
-                       if(image[f][i])\r
-                       {\r
-                               image[f][i]->unbind(this);\r
-                               image[f][i] = 0;\r
-                       }\r
-               }\r
-       }\r
-\r
-       resource->unlock();\r
-\r
-    for(int i = 0; i < 6; i++)\r
-    {\r
-        mFaceProxies[i] = NULL;\r
-    }\r
-}\r
-\r
-// We need to maintain a count of references to renderbuffers acting as \r
-// proxies for this texture, so that the texture is not deleted while \r
-// proxy references still exist. If the reference count drops to zero,\r
-// we set our proxy pointer NULL, so that a new attempt at referencing\r
-// will cause recreation.\r
-void TextureCubeMap::addProxyRef(const Renderbuffer *proxy)\r
-{\r
-    for(int f = 0; f < 6; f++)\r
-    {\r
-        if(mFaceProxies[f] == proxy)\r
-        {\r
-                       mFaceProxyRefs[f]++;\r
-               }\r
-       }\r
-}\r
-\r
-void TextureCubeMap::releaseProxy(const Renderbuffer *proxy)\r
-{\r
-    for(int f = 0; f < 6; f++)\r
-    {\r
-        if(mFaceProxies[f] == proxy)\r
-        {\r
-            if(mFaceProxyRefs[f] > 0)\r
-                       {\r
-                               mFaceProxyRefs[f]--;\r
-                       }\r
-\r
-            if(mFaceProxyRefs[f] == 0)\r
-                       {\r
-                               mFaceProxies[f] = NULL;\r
-                       }\r
-               }\r
-    }\r
-}\r
-\r
-GLenum TextureCubeMap::getTarget() const\r
-{\r
-    return GL_TEXTURE_CUBE_MAP;\r
-}\r
-\r
-GLsizei TextureCubeMap::getWidth(GLenum target, GLint level) const\r
-{\r
-       int face = CubeFaceIndex(target);\r
-    return image[face][level] ? image[face][level]->getWidth() : 0;\r
-}\r
-\r
-GLsizei TextureCubeMap::getHeight(GLenum target, GLint level) const\r
-{\r
-       int face = CubeFaceIndex(target);\r
-    return image[face][level] ? image[face][level]->getHeight() : 0;\r
-}\r
-\r
-GLenum TextureCubeMap::getFormat(GLenum target, GLint level) const\r
-{\r
-       int face = CubeFaceIndex(target);\r
-    return image[face][level] ? image[face][level]->getFormat() : 0;\r
-}\r
-\r
-GLenum TextureCubeMap::getType(GLenum target, GLint level) const\r
-{\r
-       int face = CubeFaceIndex(target);\r
-    return image[face][level] ? image[face][level]->getType() : 0;\r
-}\r
-\r
-sw::Format TextureCubeMap::getInternalFormat(GLenum target, GLint level) const\r
-{\r
-       int face = CubeFaceIndex(target);\r
-    return image[face][level] ? image[face][level]->getInternalFormat() : sw::FORMAT_NULL;\r
-}\r
-\r
-int TextureCubeMap::getLevelCount() const\r
-{\r
-       ASSERT(isSamplerComplete());\r
-       int levels = 0;\r
-\r
-       while(levels < MIPMAP_LEVELS && image[0][levels])\r
-       {\r
-               levels++;\r
-       }\r
-\r
-       return levels;\r
-}\r
-\r
-void TextureCubeMap::setCompressedImage(GLenum target, GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels)\r
-{\r
-       int face = CubeFaceIndex(target);\r
-\r
-       if(image[face][level])\r
-       {\r
-               image[face][level]->unbind(this);\r
-       }\r
-\r
-       image[face][level] = new egl::Image(this, width, height, format, GL_UNSIGNED_BYTE);\r
-\r
-       if(!image[face][level])\r
-       {\r
-               return error(GL_OUT_OF_MEMORY);\r
-       }\r
-\r
-    Texture::setCompressedImage(imageSize, pixels, image[face][level]);\r
-}\r
-\r
-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)\r
-{\r
-       Texture::subImage(xoffset, yoffset, 0, width, height, 1, format, type, unpackInfo, pixels, image[CubeFaceIndex(target)][level]);\r
-}\r
-\r
-void TextureCubeMap::subImageCompressed(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels)\r
-{\r
-    Texture::subImageCompressed(xoffset, yoffset, 0, width, height, 1, format, imageSize, pixels, image[CubeFaceIndex(target)][level]);\r
-}\r
-\r
-// Tests for cube map sampling completeness. [OpenGL ES 2.0.24] section 3.8.2 page 86.\r
-bool TextureCubeMap::isSamplerComplete() const\r
-{\r
-       for(int face = 0; face < 6; face++)\r
-    {\r
-               if(!image[face][0])\r
-               {\r
-                       return false;\r
-               }\r
-       }\r
-\r
-    int size = image[0][0]->getWidth();\r
-\r
-    if(size <= 0)\r
-    {\r
-        return false;\r
-    }\r
-\r
-    if(!isMipmapFiltered())\r
-    {\r
-        if(!isCubeComplete())\r
-        {\r
-            return false;\r
-        }\r
-    }\r
-    else\r
-    {\r
-        if(!isMipmapCubeComplete())   // Also tests for isCubeComplete()\r
-        {\r
-            return false;\r
-        }\r
-    }\r
-\r
-    return true;\r
-}\r
-\r
-// Tests for cube texture completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.\r
-bool TextureCubeMap::isCubeComplete() const\r
-{\r
-    if(image[0][0]->getWidth() <= 0 || image[0][0]->getHeight() != image[0][0]->getWidth())\r
-    {\r
-        return false;\r
-    }\r
-\r
-    for(unsigned int face = 1; face < 6; face++)\r
-    {\r
-        if(image[face][0]->getWidth()  != image[0][0]->getWidth() ||\r
-           image[face][0]->getWidth()  != image[0][0]->getHeight() ||\r
-           image[face][0]->getFormat() != image[0][0]->getFormat() ||\r
-           image[face][0]->getType()   != image[0][0]->getType())\r
-        {\r
-            return false;\r
-        }\r
-    }\r
-\r
-    return true;\r
-}\r
-\r
-bool TextureCubeMap::isMipmapCubeComplete() const\r
-{\r
-    if(!isCubeComplete())\r
-    {\r
-        return false;\r
-    }\r
-\r
-    GLsizei size = image[0][0]->getWidth();\r
-    int q = log2(size);\r
-\r
-    for(int face = 0; face < 6; face++)\r
-    {\r
-        for(int level = 1; level <= q; level++)\r
-        {\r
-                       if(!image[face][level])\r
-                       {\r
-                               return false;\r
-                       }\r
-\r
-            if(image[face][level]->getFormat() != image[0][0]->getFormat())\r
-            {\r
-                return false;\r
-            }\r
-\r
-            if(image[face][level]->getType() != image[0][0]->getType())\r
-            {\r
-                return false;\r
-            }\r
-\r
-            if(image[face][level]->getWidth() != std::max(1, size >> level))\r
-            {\r
-                return false;\r
-            }\r
-        }\r
-    }\r
-\r
-    return true;\r
-}\r
-\r
-bool TextureCubeMap::isCompressed(GLenum target, GLint level) const\r
-{\r
-    return IsCompressed(getFormat(target, level), egl::getClientVersion());\r
-}\r
-\r
-bool TextureCubeMap::isDepth(GLenum target, GLint level) const\r
-{\r
-    return IsDepthTexture(getFormat(target, level));\r
-}\r
-\r
-void TextureCubeMap::releaseTexImage()\r
-{\r
-    UNREACHABLE(0);   // Cube maps cannot have an EGL surface bound as an image\r
-}\r
-\r
-void TextureCubeMap::setImage(GLenum target, GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, const egl::Image::UnpackInfo& unpackInfo, const void *pixels)\r
-{\r
-       int face = CubeFaceIndex(target);\r
-\r
-       if(image[face][level])\r
-       {\r
-               image[face][level]->unbind(this);\r
-       }\r
-\r
-       image[face][level] = new egl::Image(this, width, height, format, type);\r
-\r
-       if(!image[face][level])\r
-       {\r
-               return error(GL_OUT_OF_MEMORY);\r
-       }\r
-\r
-       Texture::setImage(format, type, unpackInfo, pixels, image[face][level]);\r
-}\r
-\r
-void TextureCubeMap::copyImage(GLenum target, GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)\r
-{\r
-       egl::Image *renderTarget = source->getRenderTarget(0);\r
-\r
-    if(!renderTarget)\r
-    {\r
-        ERR("Failed to retrieve the render target.");\r
-        return error(GL_OUT_OF_MEMORY);\r
-    }\r
-\r
-       int face = CubeFaceIndex(target);\r
-\r
-       if(image[face][level])\r
-       {\r
-               image[face][level]->unbind(this);\r
-       }\r
-\r
-       image[face][level] = new egl::Image(this, width, height, format, GL_UNSIGNED_BYTE);\r
-\r
-       if(!image[face][level])\r
-       {\r
-               return error(GL_OUT_OF_MEMORY);\r
-       }\r
-\r
-    if(width != 0 && height != 0)\r
-    {\r
-               Renderbuffer* renderbuffer = source->getReadColorbuffer();\r
-\r
-               if(!renderbuffer)\r
-               {\r
-                       ERR("Failed to retrieve the source colorbuffer.");\r
-                       return;\r
-               }\r
-\r
-               sw::SliceRect sourceRect(x, y, x + width, y + height, 0);\r
-               sourceRect.clip(0, 0, renderbuffer->getWidth(), renderbuffer->getHeight());\r
-        \r
-        copy(renderTarget, sourceRect, format, 0, 0, 0, image[face][level]);\r
-    }\r
-\r
-       renderTarget->release();\r
-}\r
-\r
-egl::Image *TextureCubeMap::getImage(int face, unsigned int level)\r
-{\r
-       return image[face][level];\r
-}\r
-\r
-egl::Image *TextureCubeMap::getImage(GLenum face, unsigned int level)\r
-{\r
-    return image[CubeFaceIndex(face)][level];\r
-}\r
-\r
-void TextureCubeMap::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)\r
-{\r
-       int face = CubeFaceIndex(target);\r
-\r
-       if(!image[face][level])\r
-       {\r
-               return error(GL_INVALID_OPERATION);\r
-       }\r
-\r
-    GLsizei size = image[face][level]->getWidth();\r
-\r
-    if(xoffset + width > size || yoffset + height > size || zoffset != 0)\r
-    {\r
-        return error(GL_INVALID_VALUE);\r
-    }\r
-\r
-    egl::Image *renderTarget = source->getRenderTarget(0);\r
-\r
-    if(!renderTarget)\r
-    {\r
-        ERR("Failed to retrieve the render target.");\r
-        return error(GL_OUT_OF_MEMORY);\r
-    }\r
-\r
-       Renderbuffer* renderbuffer = source->getReadColorbuffer();\r
-\r
-       if(!renderbuffer)\r
-       {\r
-               ERR("Failed to retrieve the source colorbuffer.");\r
-               return;\r
-       }\r
-\r
-       sw::SliceRect sourceRect(x, y, x + width, y + height, 0);\r
-       sourceRect.clip(0, 0, renderbuffer->getWidth(), renderbuffer->getHeight());\r
-\r
-       copy(renderTarget, sourceRect, image[face][level]->getFormat(), xoffset, yoffset, zoffset, image[face][level]);\r
-\r
-       renderTarget->release();\r
-}\r
-\r
-void TextureCubeMap::generateMipmaps()\r
-{\r
-    if(!isCubeComplete())\r
-    {\r
-        return error(GL_INVALID_OPERATION);\r
-    }\r
-\r
-    unsigned int q = log2(image[0][0]->getWidth());\r
-\r
-       for(unsigned int f = 0; f < 6; f++)\r
-    {\r
-               for(unsigned int i = 1; i <= q; i++)\r
-               {\r
-                       if(image[f][i])\r
-                       {\r
-                               image[f][i]->unbind(this);\r
-                       }\r
-\r
-                       image[f][i] = new egl::Image(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());\r
-\r
-                       if(!image[f][i])\r
-                       {\r
-                               return error(GL_OUT_OF_MEMORY);\r
-                       }\r
-\r
-                       getDevice()->stretchRect(image[f][i - 1], 0, image[f][i], 0, true);\r
-               }\r
-       }\r
-}\r
-\r
-Renderbuffer *TextureCubeMap::getRenderbuffer(GLenum target, GLint level, GLint layer)\r
-{\r
-    if(!IsCubemapTextureTarget(target))\r
-    {\r
-        return error(GL_INVALID_OPERATION, (Renderbuffer *)NULL);\r
-    }\r
-\r
-    int face = CubeFaceIndex(target);\r
-\r
-    if(mFaceProxies[face] == NULL)\r
-    {\r
-        mFaceProxies[face] = new Renderbuffer(name, new RenderbufferTextureCubeMap(this, target, level));\r
-    }\r
-\r
-    return mFaceProxies[face];\r
-}\r
-\r
-egl::Image *TextureCubeMap::getRenderTarget(GLenum target, unsigned int level)\r
-{\r
-    ASSERT(IsCubemapTextureTarget(target));\r
-    ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);\r
-    \r
-       int face = CubeFaceIndex(target);\r
-\r
-       if(image[face][level])\r
-       {\r
-               image[face][level]->addRef();\r
-       }\r
-\r
-       return image[face][level];\r
-}\r
-\r
-bool TextureCubeMap::isShared(GLenum target, unsigned int level) const\r
-{\r
-    ASSERT(IsCubemapTextureTarget(target));\r
-    ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);\r
-\r
-    int face = CubeFaceIndex(target);\r
-\r
-    if(!image[face][level])\r
-    {\r
-        return false;\r
-    }\r
-\r
-    return image[face][level]->isShared();\r
-}\r
-\r
-Texture3D::Texture3D(GLuint name) : Texture(name)\r
-{\r
-       for(int i = 0; i < MIPMAP_LEVELS; i++)\r
-       {\r
-               image[i] = 0;\r
-       }\r
-\r
-       mSurface = NULL;\r
-\r
-       mColorbufferProxy = NULL;\r
-       mProxyRefs = 0;\r
-}\r
-\r
-Texture3D::~Texture3D()\r
-{\r
-       resource->lock(sw::DESTRUCT);\r
-\r
-       for(int i = 0; i < MIPMAP_LEVELS; i++)\r
-       {\r
-               if(image[i])\r
-               {\r
-                       image[i]->unbind(this);\r
-                       image[i] = 0;\r
-               }\r
-       }\r
-\r
-       resource->unlock();\r
-\r
-       if(mSurface)\r
-       {\r
-               mSurface->setBoundTexture(NULL);\r
-               mSurface = NULL;\r
-       }\r
-\r
-       mColorbufferProxy = NULL;\r
-}\r
-\r
-// We need to maintain a count of references to renderbuffers acting as \r
-// proxies for this texture, so that we do not attempt to use a pointer \r
-// to a renderbuffer proxy which has been deleted.\r
-void Texture3D::addProxyRef(const Renderbuffer *proxy)\r
-{\r
-       mProxyRefs++;\r
-}\r
-\r
-void Texture3D::releaseProxy(const Renderbuffer *proxy)\r
-{\r
-       if(mProxyRefs > 0)\r
-       {\r
-               mProxyRefs--;\r
-       }\r
-\r
-       if(mProxyRefs == 0)\r
-       {\r
-               mColorbufferProxy = NULL;\r
-       }\r
-}\r
-\r
-GLenum Texture3D::getTarget() const\r
-{\r
-       return GL_TEXTURE_3D_OES;\r
-}\r
-\r
-GLsizei Texture3D::getWidth(GLenum target, GLint level) const\r
-{\r
-       ASSERT(target == getTarget());\r
-       return image[level] ? image[level]->getWidth() : 0;\r
-}\r
-\r
-GLsizei Texture3D::getHeight(GLenum target, GLint level) const\r
-{\r
-       ASSERT(target == getTarget());\r
-       return image[level] ? image[level]->getHeight() : 0;\r
-}\r
-\r
-GLsizei Texture3D::getDepth(GLenum target, GLint level) const\r
-{\r
-       ASSERT(target == getTarget());\r
-       return image[level] ? image[level]->getDepth() : 0;\r
-}\r
-\r
-GLenum Texture3D::getFormat(GLenum target, GLint level) const\r
-{\r
-       ASSERT(target == getTarget());\r
-       return image[level] ? image[level]->getFormat() : 0;\r
-}\r
-\r
-GLenum Texture3D::getType(GLenum target, GLint level) const\r
-{\r
-       ASSERT(target == getTarget());\r
-       return image[level] ? image[level]->getType() : 0;\r
-}\r
-\r
-sw::Format Texture3D::getInternalFormat(GLenum target, GLint level) const\r
-{\r
-       ASSERT(target == getTarget());\r
-       return image[level] ? image[level]->getInternalFormat() : sw::FORMAT_NULL;\r
-}\r
-\r
-int Texture3D::getLevelCount() const\r
-{\r
-       ASSERT(isSamplerComplete());\r
-       int levels = 0;\r
-\r
-       while(levels < MIPMAP_LEVELS && image[levels])\r
-       {\r
-               levels++;\r
-       }\r
-\r
-       return levels;\r
-}\r
-\r
-void Texture3D::setImage(GLint level, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const egl::Image::UnpackInfo& unpackInfo, const void *pixels)\r
-{\r
-       if(image[level])\r
-       {\r
-               image[level]->unbind(this);\r
-       }\r
-\r
-       image[level] = new egl::Image(this, width, height, depth, format, type);\r
-\r
-       if(!image[level])\r
-       {\r
-               return error(GL_OUT_OF_MEMORY);\r
-       }\r
-\r
-       Texture::setImage(format, type, unpackInfo, pixels, image[level]);\r
-}\r
-\r
-void Texture3D::bindTexImage(egl::Surface *surface)\r
-{\r
-       GLenum format;\r
-\r
-       switch(surface->getInternalFormat())\r
-       {\r
-       case sw::FORMAT_A8R8G8B8:\r
-               format = GL_RGBA;\r
-               break;\r
-       case sw::FORMAT_X8R8G8B8:\r
-               format = GL_RGB;\r
-               break;\r
-       default:\r
-               UNIMPLEMENTED();\r
-               return;\r
-       }\r
-\r
-       for(int level = 0; level < MIPMAP_LEVELS; level++)\r
-       {\r
-               if(image[level])\r
-               {\r
-                       image[level]->unbind(this);\r
-                       image[level] = 0;\r
-               }\r
-       }\r
-\r
-       image[0] = surface->getRenderTarget();\r
-\r
-       mSurface = surface;\r
-       mSurface->setBoundTexture(this);\r
-}\r
-\r
-void Texture3D::releaseTexImage()\r
-{\r
-       for(int level = 0; level < MIPMAP_LEVELS; level++)\r
-       {\r
-               if(image[level])\r
-               {\r
-                       image[level]->unbind(this);\r
-                       image[level] = 0;\r
-               }\r
-       }\r
-}\r
-\r
-void Texture3D::setCompressedImage(GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei depth, GLsizei imageSize, const void *pixels)\r
-{\r
-       if(image[level])\r
-       {\r
-               image[level]->unbind(this);\r
-       }\r
-\r
-       image[level] = new egl::Image(this, width, height, depth, format, GL_UNSIGNED_BYTE);\r
-\r
-       if(!image[level])\r
-       {\r
-               return error(GL_OUT_OF_MEMORY);\r
-       }\r
-\r
-       Texture::setCompressedImage(imageSize, pixels, image[level]);\r
-}\r
-\r
-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)\r
-{\r
-       Texture::subImage(xoffset, yoffset, zoffset, width, height, format, depth, type, unpackInfo, pixels, image[level]);\r
-}\r
-\r
-void Texture3D::subImageCompressed(GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *pixels)\r
-{\r
-       Texture::subImageCompressed(xoffset, yoffset, zoffset, width, height, depth, format, imageSize, pixels, image[level]);\r
-}\r
-\r
-void Texture3D::copyImage(GLint level, GLenum format, GLint x, GLint y, GLint z, GLsizei width, GLsizei height, GLsizei depth, Framebuffer *source)\r
-{\r
-       egl::Image *renderTarget = source->getRenderTarget(0);\r
-\r
-       if(!renderTarget)\r
-       {\r
-               ERR("Failed to retrieve the render target.");\r
-               return error(GL_OUT_OF_MEMORY);\r
-       }\r
-\r
-       if(image[level])\r
-       {\r
-               image[level]->unbind(this);\r
-       }\r
-\r
-       image[level] = new egl::Image(this, width, height, depth, format, GL_UNSIGNED_BYTE);\r
-\r
-       if(!image[level])\r
-       {\r
-               return error(GL_OUT_OF_MEMORY);\r
-       }\r
-\r
-       if(width != 0 && height != 0 && depth != 0)\r
-       {\r
-               Renderbuffer* renderbuffer = source->getReadColorbuffer();\r
-\r
-               if(!renderbuffer)\r
-               {\r
-                       ERR("Failed to retrieve the source colorbuffer.");\r
-                       return;\r
-               }\r
-\r
-               sw::SliceRect sourceRect(x, y, x + width, y + height, z);\r
-               sourceRect.clip(0, 0, renderbuffer->getWidth(), renderbuffer->getHeight());\r
-               for(GLint sliceZ = 0; sliceZ < depth; ++sliceZ, ++sourceRect.slice)\r
-               {\r
-                       copy(renderTarget, sourceRect, format, 0, 0, sliceZ, image[level]);\r
-               }\r
-       }\r
-\r
-       renderTarget->release();\r
-}\r
-\r
-void Texture3D::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)\r
-{\r
-       if(!image[level])\r
-       {\r
-               return error(GL_INVALID_OPERATION);\r
-       }\r
-\r
-       if(xoffset + width > image[level]->getWidth() || yoffset + height > image[level]->getHeight() || zoffset >= image[level]->getDepth())\r
-       {\r
-               return error(GL_INVALID_VALUE);\r
-       }\r
-\r
-       egl::Image *renderTarget = source->getRenderTarget(0);\r
-\r
-       if(!renderTarget)\r
-       {\r
-               ERR("Failed to retrieve the render target.");\r
-               return error(GL_OUT_OF_MEMORY);\r
-       }\r
-\r
-       Renderbuffer* renderbuffer = source->getReadColorbuffer();\r
-\r
-       if(!renderbuffer)\r
-       {\r
-               ERR("Failed to retrieve the source colorbuffer.");\r
-               return;\r
-       }\r
-\r
-       sw::SliceRect sourceRect = {x, y, x + width, y + height, 0};\r
-       sourceRect.clip(0, 0, renderbuffer->getWidth(), renderbuffer->getHeight());\r
-\r
-       copy(renderTarget, sourceRect, image[level]->getFormat(), xoffset, yoffset, zoffset, image[level]);\r
-\r
-       renderTarget->release();\r
-}\r
-\r
-void Texture3D::setImage(egl::Image *sharedImage)\r
-{\r
-       sharedImage->addRef();\r
-\r
-       if(image[0])\r
-       {\r
-               image[0]->unbind(this);\r
-       }\r
-\r
-       image[0] = sharedImage;\r
-}\r
-\r
-// Tests for 3D texture sampling completeness. [OpenGL ES 2.0.24] section 3.8.2 page 85.\r
-bool Texture3D::isSamplerComplete() const\r
-{\r
-       if(!image[0])\r
-       {\r
-               return false;\r
-       }\r
-\r
-       GLsizei width = image[0]->getWidth();\r
-       GLsizei height = image[0]->getHeight();\r
-       GLsizei depth = image[0]->getDepth();\r
-\r
-       if(width <= 0 || height <= 0 || depth <= 0)\r
-       {\r
-               return false;\r
-       }\r
-\r
-       if(isMipmapFiltered())\r
-       {\r
-               if(!isMipmapComplete())\r
-               {\r
-                       return false;\r
-               }\r
-       }\r
-\r
-       return true;\r
-}\r
-\r
-// Tests for 3D texture (mipmap) completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.\r
-bool Texture3D::isMipmapComplete() const\r
-{\r
-       GLsizei width = image[0]->getWidth();\r
-       GLsizei height = image[0]->getHeight();\r
-       GLsizei depth = image[0]->getDepth();\r
-\r
-       int q = log2(std::max(std::max(width, height), depth));\r
-\r
-       for(int level = 1; level <= q; level++)\r
-       {\r
-               if(!image[level])\r
-               {\r
-                       return false;\r
-               }\r
-\r
-               if(image[level]->getFormat() != image[0]->getFormat())\r
-               {\r
-                       return false;\r
-               }\r
-\r
-               if(image[level]->getType() != image[0]->getType())\r
-               {\r
-                       return false;\r
-               }\r
-\r
-               if(image[level]->getWidth() != std::max(1, width >> level))\r
-               {\r
-                       return false;\r
-               }\r
-\r
-               if(image[level]->getHeight() != std::max(1, height >> level))\r
-               {\r
-                       return false;\r
-               }\r
-\r
-               if(image[level]->getDepth() != std::max(1, depth >> level))\r
-               {\r
-                       return false;\r
-               }\r
-       }\r
-\r
-       return true;\r
-}\r
-\r
-bool Texture3D::isCompressed(GLenum target, GLint level) const\r
-{\r
-       return IsCompressed(getFormat(target, level), egl::getClientVersion());\r
-}\r
-\r
-bool Texture3D::isDepth(GLenum target, GLint level) const\r
-{\r
-       return IsDepthTexture(getFormat(target, level));\r
-}\r
-\r
-void Texture3D::generateMipmaps()\r
-{\r
-       if(!image[0])\r
-       {\r
-               return;   // FIXME: error?\r
-       }\r
-\r
-       unsigned int q = log2(std::max(std::max(image[0]->getWidth(), image[0]->getHeight()), image[0]->getDepth()));\r
-\r
-       for(unsigned int i = 1; i <= q; i++)\r
-       {\r
-               if(image[i])\r
-               {\r
-                       image[i]->unbind(this);\r
-               }\r
-\r
-               image[i] = new egl::Image(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());\r
-\r
-               if(!image[i])\r
-               {\r
-                       return error(GL_OUT_OF_MEMORY);\r
-               }\r
-\r
-               getDevice()->stretchCube(image[i - 1], image[i]);\r
-       }\r
-}\r
-\r
-egl::Image *Texture3D::getImage(unsigned int level)\r
-{\r
-       return image[level];\r
-}\r
-\r
-Renderbuffer *Texture3D::getRenderbuffer(GLenum target, GLint level, GLint layer)\r
-{\r
-       if(target != getTarget())\r
-       {\r
-               return error(GL_INVALID_OPERATION, (Renderbuffer *)NULL);\r
-       }\r
-\r
-       if(mColorbufferProxy == NULL)\r
-       {\r
-               mColorbufferProxy = new Renderbuffer(name, new RenderbufferTexture3D(this, level, layer));\r
-       }\r
-\r
-       return mColorbufferProxy;\r
-}\r
-\r
-egl::Image *Texture3D::getRenderTarget(GLenum target, unsigned int level)\r
-{\r
-       ASSERT(target == getTarget());\r
-       ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);\r
-\r
-       if(image[level])\r
-       {\r
-               image[level]->addRef();\r
-       }\r
-\r
-       return image[level];\r
-}\r
-\r
-bool Texture3D::isShared(GLenum target, unsigned int level) const\r
-{\r
-       ASSERT(target == getTarget());\r
-       ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);\r
-\r
-       if(mSurface)   // Bound to an EGLSurface\r
-       {\r
-               return true;\r
-       }\r
-\r
-       if(!image[level])\r
-       {\r
-               return false;\r
-       }\r
-\r
-       return image[level]->isShared();\r
-}\r
-\r
-Texture2DArray::Texture2DArray(GLuint name) : Texture3D(name)\r
-{\r
-}\r
-\r
-Texture2DArray::~Texture2DArray()\r
-{\r
-}\r
-\r
-GLenum Texture2DArray::getTarget() const\r
-{\r
-       return GL_TEXTURE_2D_ARRAY;\r
-}\r
-\r
-void Texture2DArray::generateMipmaps()\r
-{\r
-       UNIMPLEMENTED();\r
-}\r
-\r
-TextureExternal::TextureExternal(GLuint name) : Texture2D(name)\r
-{\r
-    mMinFilter = GL_LINEAR;\r
-    mMagFilter = GL_LINEAR;\r
-    mWrapS = GL_CLAMP_TO_EDGE;\r
-       mWrapT = GL_CLAMP_TO_EDGE;\r
-       mWrapR = GL_CLAMP_TO_EDGE;\r
-}\r
-\r
-TextureExternal::~TextureExternal()\r
-{\r
-}\r
-\r
-GLenum TextureExternal::getTarget() const\r
-{\r
-    return GL_TEXTURE_EXTERNAL_OES;\r
-}\r
-\r
-}\r
-\r
-egl::Image *createBackBuffer(int width, int height, const egl::Config *config)\r
-{\r
-       if(config)\r
-       {\r
-               return new egl::Image(width, height, config->mRenderTargetFormat, 1, false, true);\r
-       }\r
-\r
-       return 0;\r
-}\r
-\r
-egl::Image *createDepthStencil(unsigned int width, unsigned int height, sw::Format format, int multiSampleDepth, bool discard)\r
-{\r
-       if(width == 0 || height == 0 || height > OUTLINE_RESOLUTION)\r
-       {\r
-               ERR("Invalid parameters: %dx%d", width, height);\r
-               return 0;\r
-       }\r
-               \r
-       bool lockable = true;\r
-\r
-       switch(format)\r
-       {\r
-//     case sw::FORMAT_D15S1:\r
-       case sw::FORMAT_D24S8:\r
-       case sw::FORMAT_D24X8:\r
-//     case sw::FORMAT_D24X4S4:\r
-       case sw::FORMAT_D24FS8:\r
-       case sw::FORMAT_D32:\r
-       case sw::FORMAT_D16:\r
-               lockable = false;\r
-               break;\r
-//     case sw::FORMAT_S8_LOCKABLE:\r
-//     case sw::FORMAT_D16_LOCKABLE:\r
-       case sw::FORMAT_D32F_LOCKABLE:\r
-//     case sw::FORMAT_D32_LOCKABLE:\r
-       case sw::FORMAT_DF24S8:\r
-       case sw::FORMAT_DF16S8:\r
-               lockable = true;\r
-               break;\r
-       default:\r
-               UNREACHABLE(format);\r
-       }\r
-\r
-       egl::Image *surface = new egl::Image(width, height, format, multiSampleDepth, lockable, true);\r
-\r
-       if(!surface)\r
-       {\r
-               ERR("Out of memory");\r
-               return 0;\r
-       }\r
-\r
-       return surface;\r
-}\r
+// 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(egl::Context *context, 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(context, 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(egl::Context *context, 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(context, 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(egl::Context *context, 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(context, 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(egl::Context *context, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const egl::Image::UnpackInfo& unpackInfo, const void *pixels)
+{
+       Texture::subImage(context, 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(egl::Context *context, 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(context, 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(egl::Context *context, 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(context, 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(egl::Context *context, 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(context, format, type, unpackInfo, 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();
+       }
+
+       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(egl::Context *context, 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(context, 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;
+}