OSDN Git Service

Make Blitter part of Renderer.
[android-x86/external-swiftshader.git] / src / OpenGL / libGLES_CM / Texture.cpp
index 19ea8ab..494439a 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 and TextureCubeMap. Implements GL texture objects and related\r
-// 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 es1\r
-{\r
-\r
-Texture::Texture(GLuint id) : RefCountObject(id)\r
-{\r
-    mMinFilter = GL_NEAREST_MIPMAP_LINEAR;\r
-    mMagFilter = GL_LINEAR;\r
-    mWrapS = GL_REPEAT;\r
-    mWrapT = GL_REPEAT;\r
-       mMaxAnisotropy = 1.0f;\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_OES:\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_OES:\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 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
-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
-GLfloat Texture::getMaxAnisotropy() const\r
-{\r
-    return mMaxAnisotropy;\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, GLint unpackAlignment, const void *pixels, egl::Image *image)\r
-{\r
-    if(pixels && image)\r
-    {\r
-               image->loadImageData(0, 0, image->getWidth(), image->getHeight(), format, type, unpackAlignment, pixels);\r
-    }\r
-}\r
-\r
-void Texture::setCompressedImage(GLsizei imageSize, const void *pixels, egl::Image *image)\r
-{\r
-    if(pixels && image)\r
-    {\r
-               image->loadCompressedData(0, 0, image->getWidth(), image->getHeight(), imageSize, pixels);\r
-    }\r
-}\r
-\r
-void Texture::subImage(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, 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())\r
-    {\r
-        return error(GL_INVALID_VALUE);\r
-    }\r
-\r
-    if(IsCompressed(image->getFormat()))\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, width, height, format, type, unpackAlignment, pixels);\r
-    }\r
-}\r
-\r
-void Texture::subImageCompressed(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, 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())\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, width, height, imageSize, pixels);\r
-    }\r
-}\r
-\r
-bool Texture::copy(egl::Image *source, const sw::Rect &sourceRect, GLenum destFormat, GLint xoffset, GLint yoffset, egl::Image *dest)\r
-{\r
-    Device *device = getDevice();\r
-       \r
-    sw::Rect destRect = {xoffset, yoffset, xoffset + (sourceRect.x1 - sourceRect.x0), yoffset + (sourceRect.y1 - sourceRect.y0)};\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();\r
-    }\r
-\r
-       return false;\r
-}\r
-\r
-Texture2D::Texture2D(GLuint id) : Texture(id)\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();\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, GLint unpackAlignment, const void *pixels)\r
-{\r
-       if(image[level])\r
-       {\r
-               image[level]->unbind();\r
-       }\r
-\r
-       image[level] = new 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, unpackAlignment, 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_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();\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();\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();\r
-       }\r
-\r
-       image[level] = new 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, GLint unpackAlignment, const void *pixels)\r
-{\r
-       Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, 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, width, height, 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();\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();\r
-       }\r
-\r
-       image[level] = new 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
-               sw::Rect sourceRect = {x, y, x + width, y + height};\r
-               sourceRect.clip(0, 0, source->getColorbuffer()->getWidth(), source->getColorbuffer()->getHeight());\r
-\r
-        copy(renderTarget, sourceRect, format, 0, 0, image[level]);\r
-    }\r
-\r
-       renderTarget->release();\r
-}\r
-\r
-void Texture2D::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, 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())\r
-    {\r
-        return error(GL_INVALID_VALUE);\r
-    }\r
-\r
-    egl::Image *renderTarget = source->getRenderTarget();\r
-\r
-    if(!renderTarget)\r
-    {\r
-        ERR("Failed to retrieve the render target.");\r
-        return error(GL_OUT_OF_MEMORY);\r
-    }\r
-\r
-       sw::Rect sourceRect = {x, y, x + width, y + height};\r
-       sourceRect.clip(0, 0, source->getColorbuffer()->getWidth(), source->getColorbuffer()->getHeight());\r
-\r
-       copy(renderTarget, sourceRect, image[level]->getFormat(), xoffset, yoffset, image[level]);\r
-\r
-       renderTarget->release();\r
-}\r
-\r
-void Texture2D::setImage(egl::Image *sharedImage)\r
-{\r
-       sharedImage->addRef();\r
-\r
-    if(image[0])\r
-    {\r
-        image[0]->unbind();\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));\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();\r
-               }\r
-\r
-               image[i] = new 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)\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(id(), new RenderbufferTexture2D(this));\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
-TextureExternal::TextureExternal(GLuint id) : Texture2D(id)\r
-{\r
-    mMinFilter = GL_LINEAR;\r
-    mMagFilter = GL_LINEAR;\r
-    mWrapS = GL_CLAMP_TO_EDGE;\r
-    mWrapT = 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
-// Exported functions for use by EGL\r
-extern "C"\r
-{\r
-       egl::Image *createBackBuffer(int width, int height, const egl::Config *config)\r
-       {\r
-               if(config)\r
-               {\r
-                       return new es1::Image(0, width, height, config->mAlphaSize ? GL_RGBA : GL_RGB, GL_UNSIGNED_BYTE);\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");\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();\r
-               }\r
-\r
-               es1::Image *surface = new es1::Image(0, 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
-}\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 and TextureCubeMap. 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 es1
+{
+
+Texture::Texture(GLuint name) : egl::Texture(name)
+{
+       mMinFilter = GL_NEAREST_MIPMAP_LINEAR;
+       mMagFilter = GL_LINEAR;
+       mWrapS = GL_REPEAT;
+       mWrapT = GL_REPEAT;
+       mMaxAnisotropy = 1.0f;
+       generateMipmap = GL_FALSE;
+       cropRectU = 0;
+       cropRectV = 0;
+       cropRectW = 0;
+       cropRectH = 0;
+
+       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_OES:
+               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_OES:
+               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 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;
+}
+
+void Texture::setGenerateMipmap(GLboolean enable)
+{
+       generateMipmap = enable;
+}
+
+void Texture::setCropRect(GLint u, GLint v, GLint w, GLint h)
+{
+       cropRectU = u;
+       cropRectV = v;
+       cropRectW = w;
+       cropRectH = h;
+}
+
+GLenum Texture::getMinFilter() const
+{
+       return mMinFilter;
+}
+
+GLenum Texture::getMagFilter() const
+{
+       return mMagFilter;
+}
+
+GLenum Texture::getWrapS() const
+{
+       return mWrapS;
+}
+
+GLenum Texture::getWrapT() const
+{
+       return mWrapT;
+}
+
+GLfloat Texture::getMaxAnisotropy() const
+{
+       return mMaxAnisotropy;
+}
+
+GLboolean Texture::getGenerateMipmap() const
+{
+       return generateMipmap;
+}
+
+GLint Texture::getCropRectU() const
+{
+       return cropRectU;
+}
+
+GLint Texture::getCropRectV() const
+{
+       return cropRectV;
+}
+
+GLint Texture::getCropRectW() const
+{
+       return cropRectW;
+}
+
+GLint Texture::getCropRectH() const
+{
+       return cropRectH;
+}
+
+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, GLint unpackAlignment, const void *pixels, egl::Image *image)
+{
+       if(pixels && image)
+       {
+               egl::Image::UnpackInfo unpackInfo;
+               unpackInfo.alignment = unpackAlignment;
+               image->loadImageData(context, 0, 0, 0, image->getWidth(), image->getHeight(), 1, format, type, unpackInfo, pixels);
+       }
+}
+
+void Texture::setCompressedImage(GLsizei imageSize, const void *pixels, egl::Image *image)
+{
+       if(pixels && image)
+       {
+               image->loadCompressedData(0, 0, 0, image->getWidth(), image->getHeight(), 1, imageSize, pixels);
+       }
+}
+
+void Texture::subImage(egl::Context *context, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels, egl::Image *image)
+{
+       if(!image)
+       {
+               return error(GL_INVALID_OPERATION);
+       }
+
+       if(width + xoffset > image->getWidth() || height + yoffset > image->getHeight())
+       {
+               return error(GL_INVALID_VALUE);
+       }
+
+       if(IsCompressed(image->getFormat()))
+       {
+               return error(GL_INVALID_OPERATION);
+       }
+
+       if(format != image->getFormat())
+       {
+               return error(GL_INVALID_OPERATION);
+       }
+
+       if(pixels)
+       {
+               egl::Image::UnpackInfo unpackInfo;
+               unpackInfo.alignment = unpackAlignment;
+               image->loadImageData(context, xoffset, yoffset, 0, width, height, 1, format, type, unpackInfo, pixels);
+       }
+}
+
+void Texture::subImageCompressed(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, 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())
+       {
+               return error(GL_INVALID_VALUE);
+       }
+
+       if(format != image->getFormat())
+       {
+               return error(GL_INVALID_OPERATION);
+       }
+
+       if(pixels)
+       {
+               image->loadCompressedData(xoffset, yoffset, 0, width, height, 1, imageSize, pixels);
+       }
+}
+
+bool Texture::copy(egl::Image *source, const sw::Rect &sourceRect, GLenum destFormat, GLint xoffset, GLint yoffset, egl::Image *dest)
+{
+       Device *device = getDevice();
+
+       sw::SliceRect destRect(xoffset, yoffset, xoffset + (sourceRect.x1 - sourceRect.x0), yoffset + (sourceRect.y1 - sourceRect.y0), 0);
+       sw::SliceRect sourceSliceRect(sourceRect);
+       bool success = device->stretchRect(source, &sourceSliceRect, dest, &destRect, false);
+
+       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, GLint unpackAlignment, 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, unpackAlignment, pixels, image[level]);
+}
+
+void Texture2D::bindTexImage(gl::Surface *surface)
+{
+       GLenum format;
+
+       switch(surface->getInternalFormat())
+       {
+       case sw::FORMAT_A8R8G8B8:
+               format = GL_BGRA_EXT;
+               break;
+       case sw::FORMAT_A8B8G8R8:
+               format = GL_RGBA;
+               break;
+       case sw::FORMAT_X8B8G8R8:
+       case sw::FORMAT_X8R8G8B8:
+               format = GL_RGB;
+               break;
+       default:
+               UNIMPLEMENTED();
+               return;
+       }
+
+       for(int level = 0; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++)
+       {
+               if(image[level])
+               {
+                       image[level]->release();
+                       image[level] = nullptr;
+               }
+       }
+
+       image[0] = surface->getRenderTarget();
+
+       mSurface = surface;
+       mSurface->setBoundTexture(this);
+}
+
+void 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();
+       }
+
+       image[level] = egl::Image::create(this, width, height, format, 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, GLint unpackAlignment, const void *pixels)
+{
+       Texture::subImage(context, xoffset, yoffset, width, height, format, type, unpackAlignment, 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, width, height, 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();
+
+       if(!renderTarget)
+       {
+               ERR("Failed to retrieve the render target.");
+               return error(GL_OUT_OF_MEMORY);
+       }
+
+       if(image[level])
+       {
+               image[level]->release();
+       }
+
+       image[level] = egl::Image::create(this, width, height, format, GL_UNSIGNED_BYTE);
+
+       if(!image[level])
+       {
+               return error(GL_OUT_OF_MEMORY);
+       }
+
+       if(width != 0 && height != 0)
+       {
+               sw::Rect sourceRect = {x, y, x + width, y + height};
+               sourceRect.clip(0, 0, source->getColorbuffer()->getWidth(), source->getColorbuffer()->getHeight());
+
+               copy(renderTarget, sourceRect, format, 0, 0, image[level]);
+       }
+
+       renderTarget->release();
+}
+
+void Texture2D::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, 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())
+       {
+               return error(GL_INVALID_VALUE);
+       }
+
+       egl::Image *renderTarget = source->getRenderTarget();
+
+       if(!renderTarget)
+       {
+               ERR("Failed to retrieve the render target.");
+               return error(GL_OUT_OF_MEMORY);
+       }
+
+       sw::Rect sourceRect = {x, y, x + width, y + height};
+       sourceRect.clip(0, 0, source->getColorbuffer()->getWidth(), source->getColorbuffer()->getHeight());
+
+       copy(renderTarget, sourceRect, image[level]->getFormat(), xoffset, yoffset, image[level]);
+
+       renderTarget->release();
+}
+
+void Texture2D::setSharedImage(egl::Image *sharedImage)
+{
+       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(!generateMipmap && !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[0]->getWidth();
+       GLsizei height = image[0]->getHeight();
+
+       int q = log2(std::max(width, height));
+
+       for(int level = 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));
+}
+
+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, true);
+       }
+}
+
+void Texture2D::autoGenerateMipmaps()
+{
+       if(generateMipmap && image[0]->hasDirtyMipmaps())
+       {
+               generateMipmaps();
+               image[0]->cleanMipmaps();
+       }
+}
+
+egl::Image *Texture2D::getImage(unsigned int level)
+{
+       return image[level];
+}
+
+Renderbuffer *Texture2D::getRenderbuffer(GLenum target)
+{
+       if(target != GL_TEXTURE_2D)
+       {
+               return error(GL_INVALID_OPERATION, (Renderbuffer*)nullptr);
+       }
+
+       if(!mColorbufferProxy)
+       {
+               mColorbufferProxy = new Renderbuffer(name, new RenderbufferTexture2D(this));
+       }
+
+       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();
+}
+
+TextureExternal::TextureExternal(GLuint name) : Texture2D(name)
+{
+       mMinFilter = GL_LINEAR;
+       mMagFilter = GL_LINEAR;
+       mWrapS = GL_CLAMP_TO_EDGE;
+       mWrapT = 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(height > sw::OUTLINE_RESOLUTION)
+       {
+               ERR("Invalid parameters: %dx%d", width, height);
+               return 0;
+       }
+
+       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;
+}