Edit

kc3-lang/angle/src/libGLESv2/Texture.cpp

Branch :

  • Show log

    Commit

  • Author : apatrick@chromium.org
    Date : 2010-11-23 22:05:41
    Hash : 9d1c9b4b
    Message : glGenerateMipmap now does not force textures to become render targets. For textures that are not already render targets, use the CPU to filter them and mark them as dirty. I tested it by modifying the "mip_map_2d" demo to call glGenerateMipmap instead of generating the mipmaps itself. I also used the debugger to force it to take the render target path and verified that it still worked. We've been having problems with the display being reset when video memory pressure is heavy with one of our demos. This patch made a noticable improvement on Vista but I don't think it will necessarily help on XP. Review URL: http://codereview.appspot.com/3262041 git-svn-id: https://angleproject.googlecode.com/svn/trunk@490 736b8ea6-26fd-11df-bfd4-992fa37f6226

  • src/libGLESv2/Texture.cpp
  • //
    // Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
    // Use of this source code is governed by a BSD-style license that can be
    // found in the LICENSE file.
    //
    
    // Texture.cpp: Implements the gl::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 "libGLESv2/Texture.h"
    
    #include <d3dx9tex.h>
    
    #include <algorithm>
    
    #include "common/debug.h"
    
    #include "libGLESv2/main.h"
    #include "libGLESv2/mathutil.h"
    #include "libGLESv2/utilities.h"
    #include "libGLESv2/Blit.h"
    
    namespace gl
    {
    
    Texture::Image::Image()
      : width(0), height(0), dirty(false), surface(NULL), format(GL_NONE)
    {
    }
    
    Texture::Image::~Image()
    {
      if (surface) surface->Release();
    }
    
    Texture::Texture(GLuint id) : RefCountObject(id)
    {
        mMinFilter = GL_NEAREST_MIPMAP_LINEAR;
        mMagFilter = GL_LINEAR;
        mWrapS = GL_REPEAT;
        mWrapT = GL_REPEAT;
    
        mWidth = 0;
        mHeight = 0;
    
        mDirtyMetaData = true;
        mDirty = true;
        mIsRenderable = false;
        mType = GL_UNSIGNED_BYTE;
        mBaseTexture = NULL;
    }
    
    Texture::~Texture()
    {
    }
    
    Blit *Texture::getBlitter()
    {
        Context *context = getContext();
        return context->getBlitter();
    }
    
    // Returns true on successful filter state update (valid enum parameter)
    bool Texture::setMinFilter(GLenum filter)
    {
        switch (filter)
        {
          case GL_NEAREST:
          case GL_LINEAR:
          case GL_NEAREST_MIPMAP_NEAREST:
          case GL_LINEAR_MIPMAP_NEAREST:
          case GL_NEAREST_MIPMAP_LINEAR:
          case GL_LINEAR_MIPMAP_LINEAR:
            {
                if (mMinFilter != filter)
                {
                    mMinFilter = filter;
                    mDirty = true;
                }
                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:
            {
                if (mMagFilter != filter)
                {
                    mMagFilter = filter;
                    mDirty = true;
                }
                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_CLAMP_TO_EDGE:
          case GL_MIRRORED_REPEAT:
            {
                if (mWrapS != wrap)
                {
                    mWrapS = wrap;
                    mDirty = true;
                }
                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_CLAMP_TO_EDGE:
          case GL_MIRRORED_REPEAT:
            {
                if (mWrapT != wrap)
                {
                    mWrapT = wrap;
                    mDirty = true;
                }
                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;
    }
    
    GLuint Texture::getWidth() const
    {
        return mWidth;
    }
    
    GLuint Texture::getHeight() const
    {
        return mHeight;
    }
    
    bool Texture::isFloatingPoint() const
    {
        return (mType == GL_FLOAT || mType == GL_HALF_FLOAT_OES);
    }
    
    bool Texture::isRenderableFormat() const
    {
        D3DFORMAT format = getD3DFormat();
        
        switch(format)
        {
          case D3DFMT_L8:
          case D3DFMT_A8L8:
          case D3DFMT_DXT1:
            return false;
          case D3DFMT_A8R8G8B8:
          case D3DFMT_X8R8G8B8:
          case D3DFMT_A16B16G16R16F:
          case D3DFMT_A32B32G32R32F:
            return true;
          default:
            UNREACHABLE();
        }
    
        return false;
    }
    
    // Selects an internal Direct3D 9 format for storing an Image
    D3DFORMAT Texture::selectFormat(GLenum format, GLenum type)
    {
        if (format == GL_COMPRESSED_RGB_S3TC_DXT1_EXT ||
            format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT)
        {
            return D3DFMT_DXT1;
        }
        else if (type == GL_FLOAT)
        {
            return D3DFMT_A32B32G32R32F;
        }
        else if (type == GL_HALF_FLOAT_OES)
        {
            return D3DFMT_A16B16G16R16F;
        }
        else if (type == GL_UNSIGNED_BYTE)
        {
            if (format == GL_LUMINANCE && getContext()->supportsLuminanceTextures())
            {
                return D3DFMT_L8;
            }
            else if (format == GL_LUMINANCE_ALPHA && getContext()->supportsLuminanceAlphaTextures())
            {
                return D3DFMT_A8L8;
            }
            else if (format == GL_RGB)
            {
                return D3DFMT_X8R8G8B8;
            }
    
            return D3DFMT_A8R8G8B8;
        }
    
        return D3DFMT_A8R8G8B8;
    }
    
    // Store the pixel rectangle designated by xoffset,yoffset,width,height with pixels stored as format/type at input
    // into the target pixel rectangle at output with outputPitch bytes in between each line.
    void Texture::loadImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type,
                                GLint unpackAlignment, const void *input, size_t outputPitch, void *output, D3DSURFACE_DESC *description) const
    {
        GLsizei inputPitch = ComputePitch(width, format, type, unpackAlignment);
    
        switch (type)
        {
          case GL_UNSIGNED_BYTE:
            switch (format)
            {
              case GL_ALPHA:
                loadAlphaImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
                break;
              case GL_LUMINANCE:
                loadLuminanceImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output, description->Format == D3DFMT_L8);
                break;
              case GL_LUMINANCE_ALPHA:
                loadLuminanceAlphaImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output, description->Format == D3DFMT_A8L8);
                break;
              case GL_RGB:
                loadRGBUByteImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
                break;
              case GL_RGBA:
                loadRGBAUByteImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
                break;
              case GL_BGRA_EXT:
                loadBGRAImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
                break;
              default: UNREACHABLE();
            }
            break;
          case GL_UNSIGNED_SHORT_5_6_5:
            switch (format)
            {
              case GL_RGB:
                loadRGB565ImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
                break;
              default: UNREACHABLE();
            }
            break;
          case GL_UNSIGNED_SHORT_4_4_4_4:
            switch (format)
            {
              case GL_RGBA:
                loadRGBA4444ImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
                break;
              default: UNREACHABLE();
            }
            break;
          case GL_UNSIGNED_SHORT_5_5_5_1:
            switch (format)
            {
              case GL_RGBA:
                loadRGBA5551ImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
                break;
              default: UNREACHABLE();
            }
            break;
          case GL_FLOAT:
            switch (format)
            {
              // float textures are converted to RGBA, not BGRA, as they're stored that way in D3D
              case GL_ALPHA:
                loadAlphaFloatImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
                break;
              case GL_LUMINANCE:
                loadLuminanceFloatImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
                break;
              case GL_LUMINANCE_ALPHA:
                loadLuminanceAlphaFloatImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
                break;
              case GL_RGB:
                loadRGBFloatImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
                break;
              case GL_RGBA:
                loadRGBAFloatImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
                break;
              default: UNREACHABLE();
            }
            break;
          case GL_HALF_FLOAT_OES:
            switch (format)
            {
              // float textures are converted to RGBA, not BGRA, as they're stored that way in D3D
              case GL_ALPHA:
                loadAlphaHalfFloatImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
                break;
              case GL_LUMINANCE:
                loadLuminanceHalfFloatImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
                break;
              case GL_LUMINANCE_ALPHA:
                loadLuminanceAlphaHalfFloatImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
                break;
              case GL_RGB:
                loadRGBHalfFloatImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
                break;
              case GL_RGBA:
                loadRGBAHalfFloatImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
                break;
              default: UNREACHABLE();
            }
            break;
          default: UNREACHABLE();
        }
    }
    
    void Texture::loadAlphaImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
                                     size_t inputPitch, const void *input, size_t outputPitch, void *output) const
    {
        const unsigned char *source = NULL;
        unsigned char *dest = NULL;
        
        for (int y = 0; y < height; y++)
        {
            source = static_cast<const unsigned char*>(input) + y * inputPitch;
            dest = static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 4;
            for (int x = 0; x < width; x++)
            {
                dest[4 * x + 0] = 0;
                dest[4 * x + 1] = 0;
                dest[4 * x + 2] = 0;
                dest[4 * x + 3] = source[x];
            }
        }
    }
    
    void Texture::loadAlphaFloatImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
                                          size_t inputPitch, const void *input, size_t outputPitch, void *output) const
    {
        const float *source = NULL;
        float *dest = NULL;
    
        for (int y = 0; y < height; y++)
        {
            source = reinterpret_cast<const float*>(static_cast<const unsigned char*>(input) + y * inputPitch);
            dest = reinterpret_cast<float*>(static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch  + xoffset * 16);
            for (int x = 0; x < width; x++)
            {
                dest[4 * x + 0] = 0;
                dest[4 * x + 1] = 0;
                dest[4 * x + 2] = 0;
                dest[4 * x + 3] = source[x];
            }
        }
    }
    
    void Texture::loadAlphaHalfFloatImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
                                              size_t inputPitch, const void *input, size_t outputPitch, void *output) const
    {
        const unsigned short *source = NULL;
        unsigned short *dest = NULL;
    
        for (int y = 0; y < height; y++)
        {
            source = reinterpret_cast<const unsigned short*>(static_cast<const unsigned char*>(input) + y * inputPitch);
            dest = reinterpret_cast<unsigned short*>(static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 8);
            for (int x = 0; x < width; x++)
            {
                dest[4 * x + 0] = 0;
                dest[4 * x + 1] = 0;
                dest[4 * x + 2] = 0;
                dest[4 * x + 3] = source[x];
            }
        }
    }
    
    void Texture::loadLuminanceImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
                                         size_t inputPitch, const void *input, size_t outputPitch, void *output, bool native) const
    {
        const unsigned char *source = NULL;
        unsigned char *dest = NULL;
    
        for (int y = 0; y < height; y++)
        {
            source = static_cast<const unsigned char*>(input) + y * inputPitch;
            dest = static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 4;
    
            if (!native)   // BGRA8 destination format
            {
                for (int x = 0; x < width; x++)
                {
                    dest[4 * x + 0] = source[x];
                    dest[4 * x + 1] = source[x];
                    dest[4 * x + 2] = source[x];
                    dest[4 * x + 3] = 0xFF;
                }
            }
            else   // L8 destination format
            {
                memcpy(dest, source, width);
            }
        }
    }
    
    void Texture::loadLuminanceFloatImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
                                              size_t inputPitch, const void *input, size_t outputPitch, void *output) const
    {
        const float *source = NULL;
        float *dest = NULL;
    
        for (int y = 0; y < height; y++)
        {
            source = reinterpret_cast<const float*>(static_cast<const unsigned char*>(input) + y * inputPitch);
            dest = reinterpret_cast<float*>(static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch  + xoffset * 16);
            for (int x = 0; x < width; x++)
            {
                dest[4 * x + 0] = source[x];
                dest[4 * x + 1] = source[x];
                dest[4 * x + 2] = source[x];
                dest[4 * x + 3] = 1.0f;
            }
        }
    }
    
    void Texture::loadLuminanceHalfFloatImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
                                                       size_t inputPitch, const void *input, size_t outputPitch, void *output) const
    {
        const unsigned short *source = NULL;
        unsigned short *dest = NULL;
    
        for (int y = 0; y < height; y++)
        {
            source = reinterpret_cast<const unsigned short*>(static_cast<const unsigned char*>(input) + y * inputPitch);
            dest = reinterpret_cast<unsigned short*>(static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 8);
            for (int x = 0; x < width; x++)
            {
                dest[4 * x + 0] = source[x];
                dest[4 * x + 1] = source[x];
                dest[4 * x + 2] = source[x];
                dest[4 * x + 3] = 0x3C00; // SEEEEEMMMMMMMMMM, S = 0, E = 15, M = 0: 16bit flpt representation of 1
            }
        }
    }
    
    void Texture::loadLuminanceAlphaImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
                                              size_t inputPitch, const void *input, size_t outputPitch, void *output, bool native) const
    {
        const unsigned char *source = NULL;
        unsigned char *dest = NULL;
    
        for (int y = 0; y < height; y++)
        {
            source = static_cast<const unsigned char*>(input) + y * inputPitch;
            dest = static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 4;
            
            if (!native)   // BGRA8 destination format
            {
                for (int x = 0; x < width; x++)
                {
                    dest[4 * x + 0] = source[2*x+0];
                    dest[4 * x + 1] = source[2*x+0];
                    dest[4 * x + 2] = source[2*x+0];
                    dest[4 * x + 3] = source[2*x+1];
                }
            }
            else
            {
                memcpy(dest, source, width * 2);
            }
        }
    }
    
    void Texture::loadLuminanceAlphaFloatImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
                                                   size_t inputPitch, const void *input, size_t outputPitch, void *output) const
    {
        const float *source = NULL;
        float *dest = NULL;
    
        for (int y = 0; y < height; y++)
        {
            source = reinterpret_cast<const float*>(static_cast<const unsigned char*>(input) + y * inputPitch);
            dest = reinterpret_cast<float*>(static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch  + xoffset * 16);
            for (int x = 0; x < width; x++)
            {
                dest[4 * x + 0] = source[2*x+0];
                dest[4 * x + 1] = source[2*x+0];
                dest[4 * x + 2] = source[2*x+0];
                dest[4 * x + 3] = source[2*x+1];
            }
        }
    }
    
    void Texture::loadLuminanceAlphaHalfFloatImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
                                                       size_t inputPitch, const void *input, size_t outputPitch, void *output) const
    {
        const unsigned short *source = NULL;
        unsigned short *dest = NULL;
    
        for (int y = 0; y < height; y++)
        {
            source = reinterpret_cast<const unsigned short*>(static_cast<const unsigned char*>(input) + y * inputPitch);
            dest = reinterpret_cast<unsigned short*>(static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 8);
            for (int x = 0; x < width; x++)
            {
                dest[4 * x + 0] = source[2*x+0];
                dest[4 * x + 1] = source[2*x+0];
                dest[4 * x + 2] = source[2*x+0];
                dest[4 * x + 3] = source[2*x+1];
            }
        }
    }
    
    void Texture::loadRGBUByteImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
                                        size_t inputPitch, const void *input, size_t outputPitch, void *output) const
    {
        const unsigned char *source = NULL;
        unsigned char *dest = NULL;
    
        for (int y = 0; y < height; y++)
        {
            source = static_cast<const unsigned char*>(input) + y * inputPitch;
            dest = static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 4;
            for (int x = 0; x < width; x++)
            {
                dest[4 * x + 0] = source[x * 3 + 2];
                dest[4 * x + 1] = source[x * 3 + 1];
                dest[4 * x + 2] = source[x * 3 + 0];
                dest[4 * x + 3] = 0xFF;
            }
        }
    }
    
    void Texture::loadRGB565ImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
                                      size_t inputPitch, const void *input, size_t outputPitch, void *output) const
    {
        const unsigned short *source = NULL;
        unsigned char *dest = NULL;
    
        for (int y = 0; y < height; y++)
        {
            source = reinterpret_cast<const unsigned short*>(static_cast<const unsigned char*>(input) + y * inputPitch);
            dest = static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 4;
            for (int x = 0; x < width; x++)
            {
                unsigned short rgba = source[x];
                dest[4 * x + 0] = ((rgba & 0x001F) << 3) | ((rgba & 0x001F) >> 2);
                dest[4 * x + 1] = ((rgba & 0x07E0) >> 3) | ((rgba & 0x07E0) >> 9);
                dest[4 * x + 2] = ((rgba & 0xF800) >> 8) | ((rgba & 0xF800) >> 13);
                dest[4 * x + 3] = 0xFF;
            }
        }
    }
    
    void Texture::loadRGBFloatImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
                                        size_t inputPitch, const void *input, size_t outputPitch, void *output) const
    {
        const float *source = NULL;
        float *dest = NULL;
    
        for (int y = 0; y < height; y++)
        {
            source = reinterpret_cast<const float*>(static_cast<const unsigned char*>(input) + y * inputPitch);
            dest = reinterpret_cast<float*>(static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch  + xoffset * 16);
            for (int x = 0; x < width; x++)
            {
                dest[4 * x + 0] = source[x * 3 + 0];
                dest[4 * x + 1] = source[x * 3 + 1];
                dest[4 * x + 2] = source[x * 3 + 2];
                dest[4 * x + 3] = 1.0f;
            }
        }
    }
    
    void Texture::loadRGBHalfFloatImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
                                            size_t inputPitch, const void *input, size_t outputPitch, void *output) const
    {
        const unsigned short *source = NULL;
        unsigned short *dest = NULL;
    
        for (int y = 0; y < height; y++)
        {
            source = reinterpret_cast<const unsigned short*>(static_cast<const unsigned char*>(input) + y * inputPitch);
            dest = reinterpret_cast<unsigned short*>(static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch  + xoffset * 8);
            for (int x = 0; x < width; x++)
            {
                dest[4 * x + 0] = source[x * 3 + 0];
                dest[4 * x + 1] = source[x * 3 + 1];
                dest[4 * x + 2] = source[x * 3 + 2];
                dest[4 * x + 3] = 0x3C00; // SEEEEEMMMMMMMMMM, S = 0, E = 15, M = 0: 16bit flpt representation of 1
            }
        }
    }
    
    void Texture::loadRGBAUByteImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
                                         size_t inputPitch, const void *input, size_t outputPitch, void *output) const
    {
        const unsigned char *source = NULL;
        unsigned char *dest = NULL;
    
        for (int y = 0; y < height; y++)
        {
            source = static_cast<const unsigned char*>(input) + y * inputPitch;
            dest = static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 4;
            for (int x = 0; x < width; x++)
            {
                dest[4 * x + 0] = source[x * 4 + 2];
                dest[4 * x + 1] = source[x * 4 + 1];
                dest[4 * x + 2] = source[x * 4 + 0];
                dest[4 * x + 3] = source[x * 4 + 3];
            }
        }
    }
    
    void Texture::loadRGBA4444ImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
                                        size_t inputPitch, const void *input, size_t outputPitch, void *output) const
    {
        const unsigned short *source = NULL;
        unsigned char *dest = NULL;
    
        for (int y = 0; y < height; y++)
        {
            source = reinterpret_cast<const unsigned short*>(static_cast<const unsigned char*>(input) + y * inputPitch);
            dest = static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 4;
            for (int x = 0; x < width; x++)
            {
                unsigned short rgba = source[x];
                dest[4 * x + 0] = ((rgba & 0x00F0) << 0) | ((rgba & 0x00F0) >> 4);
                dest[4 * x + 1] = ((rgba & 0x0F00) >> 4) | ((rgba & 0x0F00) >> 8);
                dest[4 * x + 2] = ((rgba & 0xF000) >> 8) | ((rgba & 0xF000) >> 12);
                dest[4 * x + 3] = ((rgba & 0x000F) << 4) | ((rgba & 0x000F) >> 0);
            }
        }
    }
    
    void Texture::loadRGBA5551ImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
                                        size_t inputPitch, const void *input, size_t outputPitch, void *output) const
    {
        const unsigned short *source = NULL;
        unsigned char *dest = NULL;
    
        for (int y = 0; y < height; y++)
        {
            source = reinterpret_cast<const unsigned short*>(static_cast<const unsigned char*>(input) + y * inputPitch);
            dest = static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 4;
            for (int x = 0; x < width; x++)
            {
                unsigned short rgba = source[x];
                dest[4 * x + 0] = ((rgba & 0x003E) << 2) | ((rgba & 0x003E) >> 3);
                dest[4 * x + 1] = ((rgba & 0x07C0) >> 3) | ((rgba & 0x07C0) >> 8);
                dest[4 * x + 2] = ((rgba & 0xF800) >> 8) | ((rgba & 0xF800) >> 13);
                dest[4 * x + 3] = (rgba & 0x0001) ? 0xFF : 0;
            }
        }
    }
    
    void Texture::loadRGBAFloatImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
                                         size_t inputPitch, const void *input, size_t outputPitch, void *output) const
    {
        const float *source = NULL;
        float *dest = NULL;
    
        for (int y = 0; y < height; y++)
        {
            source = reinterpret_cast<const float*>(static_cast<const unsigned char*>(input) + y * inputPitch);
            dest = reinterpret_cast<float*>(static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch  + xoffset * 16);
            memcpy(dest, source, width * 16);
        }
    }
    
    void Texture::loadRGBAHalfFloatImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
                                            size_t inputPitch, const void *input, size_t outputPitch, void *output) const
    {
        const unsigned char *source = NULL;
        unsigned char *dest = NULL;
    
        for (int y = 0; y < height; y++)
        {
            source = static_cast<const unsigned char*>(input) + y * inputPitch;
            dest = static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch  + xoffset * 8;
            memcpy(dest, source, width * 8);
        }
    }
    
    void Texture::loadBGRAImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
                                    size_t inputPitch, const void *input, size_t outputPitch, void *output) const
    {
        const unsigned char *source = NULL;
        unsigned char *dest = NULL;
    
        for (int y = 0; y < height; y++)
        {
            source = static_cast<const unsigned char*>(input) + y * inputPitch;
            dest = static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 4;
            memcpy(dest, source, width*4);
        }
    }
    
    void Texture::createSurface(GLsizei width, GLsizei height, GLenum format, GLenum type, Image *img)
    {
        IDirect3DTexture9 *newTexture = NULL;
        IDirect3DSurface9 *newSurface = NULL;
    
        if (width != 0 && height != 0)
        {
            int levelToFetch = 0;
            GLsizei requestWidth = width;
            GLsizei requestHeight = height;
            if (IsCompressed(format) && (width % 4 != 0 || height % 4 != 0))
            {
                bool isMult4 = false;
                int upsampleCount = 0;
                while (!isMult4)
                {
                    requestWidth <<= 1;
                    requestHeight <<= 1;
                    upsampleCount++;
                    if (requestWidth % 4 == 0 && requestHeight % 4 == 0)
                    {
                        isMult4 = true;
                    }
                }
                levelToFetch = upsampleCount;
            }
    
            HRESULT result = getDevice()->CreateTexture(requestWidth, requestHeight, levelToFetch + 1, NULL, selectFormat(format, type),
                                                        D3DPOOL_SYSTEMMEM, &newTexture, NULL);
    
            if (FAILED(result))
            {
                ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
                return error(GL_OUT_OF_MEMORY);
            }
    
            newTexture->GetSurfaceLevel(levelToFetch, &newSurface);
            newTexture->Release();
        }
    
        if (img->surface) img->surface->Release();
        img->surface = newSurface;
    
        img->width = width;
        img->height = height;
        img->format = format;
    }
    
    void Texture::setImage(GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels, Image *img)
    {
        createSurface(width, height, format, type, img);
    
        if (pixels != NULL && img->surface != NULL)
        {
            D3DSURFACE_DESC description;
            img->surface->GetDesc(&description);
    
            D3DLOCKED_RECT locked;
            HRESULT result = img->surface->LockRect(&locked, NULL, 0);
    
            ASSERT(SUCCEEDED(result));
    
            if (SUCCEEDED(result))
            {
                loadImageData(0, 0, width, height, format, type, unpackAlignment, pixels, locked.Pitch, locked.pBits, &description);
                img->surface->UnlockRect();
            }
    
            img->dirty = true;
        }
    
        mDirtyMetaData = true;
    }
    
    void Texture::setCompressedImage(GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels, Image *img)
    {
        createSurface(width, height, format, GL_UNSIGNED_BYTE, img);
    
        if (pixels != NULL && img->surface != NULL)
        {
            D3DLOCKED_RECT locked;
            HRESULT result = img->surface->LockRect(&locked, NULL, 0);
    
            ASSERT(SUCCEEDED(result));
    
            if (SUCCEEDED(result))
            {
                memcpy(locked.pBits, pixels, imageSize);
                img->surface->UnlockRect();
            }
    
            img->dirty = true;
        }
    
        mDirtyMetaData = true;
    }
    
    bool Texture::subImage(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels, Image *img)
    {
        if (width + xoffset > img->width || height + yoffset > img->height)
        {
            error(GL_INVALID_VALUE);
            return false;
        }
    
        if (!img->surface)
        {
            createSurface(img->width, img->height, format, type, img);
        }
    
        if (pixels != NULL && img->surface != NULL)
        {
            D3DSURFACE_DESC description;
            img->surface->GetDesc(&description);
    
            D3DLOCKED_RECT locked;
            HRESULT result = img->surface->LockRect(&locked, NULL, 0);
    
            ASSERT(SUCCEEDED(result));
    
            if (SUCCEEDED(result))
            {
                loadImageData(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, locked.Pitch, locked.pBits, &description);
                img->surface->UnlockRect();
            }
    
            img->dirty = true;
        }
    
        return true;
    }
    
    bool Texture::subImageCompressed(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels, Image *img)
    {
        if (width + xoffset > img->width || height + yoffset > img->height)
        {
            error(GL_INVALID_VALUE);
            return false;
        }
    
        if (format != getFormat())
        {
            error(GL_INVALID_OPERATION);
            return false;
        }
    
        if (!img->surface)
        {
            createSurface(img->width, img->height, format, GL_UNSIGNED_BYTE, img);
        }
    
        if (pixels != NULL && img->surface != NULL)
        {
            RECT updateRegion;
            updateRegion.left = xoffset;
            updateRegion.right = xoffset + width;
            updateRegion.bottom = yoffset + height;
            updateRegion.top = yoffset;
    
            D3DLOCKED_RECT locked;
            HRESULT result = img->surface->LockRect(&locked, &updateRegion, 0);
    
            ASSERT(SUCCEEDED(result));
    
            if (SUCCEEDED(result))
            {
                GLsizei inputPitch = ComputeCompressedPitch(width, format);
                int rows = imageSize / inputPitch;
                for (int i = 0; i < rows; ++i)
                {
                    memcpy((void*)((BYTE*)locked.pBits + i * locked.Pitch), (void*)((BYTE*)pixels + i * inputPitch), inputPitch);
                }
                img->surface->UnlockRect();
            }
    
            img->dirty = true;
        }
    
        return true;
    }
    
    // This implements glCopyTex[Sub]Image2D for non-renderable internal texture formats
    void Texture::copyNonRenderable(Image *image, GLenum internalFormat, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, IDirect3DSurface9 *renderTarget)
    {
        IDirect3DDevice9 *device = getDevice();
        IDirect3DSurface9 *surface = NULL;
        D3DSURFACE_DESC description;
        renderTarget->GetDesc(&description);
        
        HRESULT result = device->CreateOffscreenPlainSurface(description.Width, description.Height, description.Format, D3DPOOL_SYSTEMMEM, &surface, NULL);
    
        if (!SUCCEEDED(result))
        {
            ERR("Could not create matching destination surface.");
            return error(GL_OUT_OF_MEMORY);
        }
    
        result = device->GetRenderTargetData(renderTarget, surface);
    
        if (!SUCCEEDED(result))
        {
            ERR("GetRenderTargetData unexpectedly failed.");
            surface->Release();
            return error(GL_OUT_OF_MEMORY);
        }
    
        D3DLOCKED_RECT sourceLock = {0};
        RECT sourceRect = {x, y, x + width, y + height};
        result = surface->LockRect(&sourceLock, &sourceRect, 0);
    
        if (FAILED(result))
        {
            ERR("Failed to lock the source surface (rectangle might be invalid).");
            surface->UnlockRect();
            surface->Release();
            return error(GL_OUT_OF_MEMORY);
        }
    
        if (!image->surface)
        {
            createSurface(width, height, internalFormat, mType, image);
        }
    
        if (image->surface == NULL)
        {
            ERR("Failed to create an image surface.");
            surface->UnlockRect();
            surface->Release();
            return error(GL_OUT_OF_MEMORY); 
        }
    
        D3DLOCKED_RECT destLock = {0};
        RECT destRect = {xoffset, yoffset, xoffset + width, yoffset + height};
        result = image->surface->LockRect(&destLock, &destRect, 0);
        
        if (FAILED(result))
        {
            ERR("Failed to lock the destination surface (rectangle might be invalid).");
            surface->UnlockRect();
            surface->Release();
            return error(GL_OUT_OF_MEMORY);
        }
    
        if (destLock.pBits && sourceLock.pBits)
        {
            unsigned char *source = (unsigned char*)sourceLock.pBits;
            unsigned char *dest = (unsigned char*)destLock.pBits;
    
            switch (description.Format)
            {
              case D3DFMT_X8R8G8B8:
              case D3DFMT_A8R8G8B8:
                switch(getD3DFormat())
                {
                  case D3DFMT_L8:
                    for(int y = 0; y < height; y++)
                    {
                        for(int x = 0; x < height; x++)
                        {
                            dest[x] = source[x * 4 + 2];
                        }
    
                        source += sourceLock.Pitch;
                        dest += destLock.Pitch;
                    }
                    break;
                  case D3DFMT_A8L8:
                    for(int y = 0; y < height; y++)
                    {
                        for(int x = 0; x < height; x++)
                        {
                            dest[x * 2 + 0] = source[x * 4 + 2];
                            dest[x * 2 + 1] = source[x * 4 + 3];
                        }
    
                        source += sourceLock.Pitch;
                        dest += destLock.Pitch;
                    }
                    break;
                  default:
                    UNREACHABLE();
                }
                break;
              case D3DFMT_R5G6B5:
                switch(getD3DFormat())
                {
                  case D3DFMT_L8:
                    for(int y = 0; y < height; y++)
                    {
                        for(int x = 0; x < height; x++)
                        {
                            unsigned char red = source[x * 2 + 1] & 0xF8;
                            dest[x] = red | (red >> 5);
                        }
    
                        source += sourceLock.Pitch;
                        dest += destLock.Pitch;
                    }
                    break;
                  default:
                    UNREACHABLE();
                }
                break;
              default:
                UNREACHABLE();
            }
    
            image->dirty = true;
            mDirtyMetaData = true;    
        }
    
        image->surface->UnlockRect();
        surface->UnlockRect();
        surface->Release();
    }
    
    D3DFORMAT Texture::getD3DFormat() const
    {
        return selectFormat(getFormat(), mType);
    }
    
    IDirect3DBaseTexture9 *Texture::getTexture()
    {
        if (!isComplete())
        {
            return NULL;
        }
    
        if (mDirtyMetaData)
        {
            mBaseTexture = createTexture();
            mIsRenderable = false;
        }
    
        if (mDirtyMetaData || dirtyImageData())
        {
            updateTexture();
        }
    
        mDirtyMetaData = false;
        ASSERT(!dirtyImageData());
    
        return mBaseTexture;
    }
    
    bool Texture::isDirty() const
    {
        return (mDirty || mDirtyMetaData || dirtyImageData());
    }
    
    // Returns the top-level texture surface as a render target
    void Texture::needRenderTarget()
    {
        if (!mIsRenderable)
        {
            mBaseTexture = convertToRenderTarget();
            mIsRenderable = true;
        }
    
        if (dirtyImageData())
        {
            updateTexture();
        }
    
        mDirtyMetaData = false;
    }
    
    void Texture::dropTexture()
    {
        if (mBaseTexture)
        {
            mBaseTexture = NULL;
        }
    
        mIsRenderable = false;
    }
    
    void Texture::pushTexture(IDirect3DBaseTexture9 *newTexture, bool renderable)
    {
        mBaseTexture = newTexture;
        mDirtyMetaData = false;
        mIsRenderable = renderable;
        mDirty = true;
    }
    
    
    GLint Texture::creationLevels(GLsizei width, GLsizei height, GLint maxlevel) const
    {
        if (isPow2(width) && isPow2(height))
        {
            return maxlevel;
        }
        else
        {
            // OpenGL ES 2.0 without GL_OES_texture_npot does not permit NPOT mipmaps.
            return 1;
        }
    }
    
    GLint Texture::creationLevels(GLsizei size, GLint maxlevel) const
    {
        return creationLevels(size, size, maxlevel);
    }
    
    int Texture::levelCount() const
    {
        return mBaseTexture ? mBaseTexture->GetLevelCount() : 0;
    }
    
    bool Texture::isRenderable() const
    {
        return mIsRenderable;
    }
    
    Texture2D::Texture2D(GLuint id) : Texture(id)
    {
        mTexture = NULL;
    }
    
    Texture2D::~Texture2D()
    {
        mColorbufferProxy.set(NULL);
    
        if (mTexture)
        {
            mTexture->Release();
            mTexture = NULL;
        }
    }
    
    GLenum Texture2D::getTarget() const
    {
        return GL_TEXTURE_2D;
    }
    
    GLenum Texture2D::getFormat() const
    {
        return mImageArray[0].format;
    }
    
    // While OpenGL doesn't check texture consistency until draw-time, D3D9 requires a complete texture
    // for render-to-texture (such as CopyTexImage). We have no way of keeping individual inconsistent levels.
    // Call this when a particular level of the texture must be defined with a specific format, width and height.
    //
    // Returns true if the existing texture was unsuitable and had to be destroyed. If so, it will also set
    // a new height and width for the texture by working backwards from the given width and height.
    bool Texture2D::redefineTexture(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum type)
    {
        bool widthOkay = (mWidth >> level == width);
        bool heightOkay = (mHeight >> level == height);
    
        bool sizeOkay = ((widthOkay && heightOkay)
                         || (widthOkay && mHeight >> level == 0 && height == 1)
                         || (heightOkay && mWidth >> level == 0 && width == 1));
    
        bool typeOkay = (type == mType);
    
        bool textureOkay = (sizeOkay && typeOkay && internalFormat == mImageArray[0].format);
    
        if (!textureOkay)
        {
            TRACE("Redefining 2D texture (%d, 0x%04X, %d, %d => 0x%04X, %d, %d).", level,
                  mImageArray[0].format, mWidth, mHeight,
                  internalFormat, width, height);
    
            // Purge all the levels and the texture.
    
            for (int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
            {
                if (mImageArray[i].surface != NULL)
                {
                    mImageArray[i].dirty = false;
    
                    mImageArray[i].surface->Release();
                    mImageArray[i].surface = NULL;
                }
            }
    
            if (mTexture != NULL)
            {
                mTexture->Release();
                mTexture = NULL;
                dropTexture();
            }
    
            mWidth = width << level;
            mHeight = height << level;
            mImageArray[0].format = internalFormat;
            mType = type;
        }
    
        return !textureOkay;
    }
    
    void Texture2D::setImage(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
    {
        redefineTexture(level, internalFormat, width, height, type);
    
        Texture::setImage(width, height, format, type, unpackAlignment, pixels, &mImageArray[level]);
    }
    
    void Texture2D::setCompressedImage(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels)
    {
        redefineTexture(level, internalFormat, width, height, GL_UNSIGNED_BYTE);
    
        Texture::setCompressedImage(width, height, internalFormat, imageSize, pixels, &mImageArray[level]);
    }
    
    void Texture2D::commitRect(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
    {
        ASSERT(mImageArray[level].surface != NULL);
    
        if (level < levelCount())
        {
            IDirect3DSurface9 *destLevel = NULL;
            HRESULT result = mTexture->GetSurfaceLevel(level, &destLevel);
    
            ASSERT(SUCCEEDED(result));
    
            if (SUCCEEDED(result))
            {
                Image *img = &mImageArray[level];
    
                RECT sourceRect;
                sourceRect.left = xoffset;
                sourceRect.top = yoffset;
                sourceRect.right = xoffset + width;
                sourceRect.bottom = yoffset + height;
    
                POINT destPoint;
                destPoint.x = xoffset;
                destPoint.y = yoffset;
    
                result = getDevice()->UpdateSurface(img->surface, &sourceRect, destLevel, &destPoint);
                ASSERT(SUCCEEDED(result));
    
                destLevel->Release();
    
                img->dirty = false;
            }
        }
    }
    
    void Texture2D::subImage(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
    {
        if (Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, &mImageArray[level]))
        {
            commitRect(level, xoffset, yoffset, width, height);
        }
    }
    
    void Texture2D::subImageCompressed(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels)
    {
        if (Texture::subImageCompressed(xoffset, yoffset, width, height, format, imageSize, pixels, &mImageArray[level]))
        {
            commitRect(level, xoffset, yoffset, width, height);
        }
    }
    
    void Texture2D::copyImage(GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height, RenderbufferStorage *source)
    {
        IDirect3DSurface9 *renderTarget = source->getRenderTarget();
    
        if (!renderTarget)
        {
            ERR("Failed to retrieve the render target.");
            return error(GL_OUT_OF_MEMORY);
        }
    
        bool redefined = redefineTexture(level, internalFormat, width, height, mType);
       
        if (!isRenderableFormat())
        {
            copyNonRenderable(&mImageArray[level], internalFormat, 0, 0, x, y, width, height, renderTarget);
        }
        else
        {
            if (redefined)
            {
                convertToRenderTarget();
                pushTexture(mTexture, true);
            }
            else
            {
                needRenderTarget();
            }
    
            if (width != 0 && height != 0 && level < levelCount())
            {
                RECT sourceRect;
                sourceRect.left = x;
                sourceRect.right = x + width;
                sourceRect.top = y;
                sourceRect.bottom = y + height;
    
                IDirect3DSurface9 *dest;
                HRESULT hr = mTexture->GetSurfaceLevel(level, &dest);
    
                getBlitter()->formatConvert(source->getRenderTarget(), sourceRect, internalFormat, 0, 0, dest);
                dest->Release();
            }
        }
    
        mImageArray[level].width = width;
        mImageArray[level].height = height;
        mImageArray[level].format = internalFormat;
    }
    
    void Texture2D::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, RenderbufferStorage *source)
    {
        if (xoffset + width > mImageArray[level].width || yoffset + height > mImageArray[level].height)
        {
            return error(GL_INVALID_VALUE);
        }
    
        IDirect3DSurface9 *renderTarget = source->getRenderTarget();
    
        if (!renderTarget)
        {
            ERR("Failed to retrieve the render target.");
            return error(GL_OUT_OF_MEMORY);
        }
    
        bool redefined = redefineTexture(0, mImageArray[0].format, mImageArray[0].width, mImageArray[0].height, mType);
       
        if (!isRenderableFormat())
        {
            copyNonRenderable(&mImageArray[level], getFormat(), xoffset, yoffset, x, y, width, height, renderTarget);
        }
        else
        {
            if (redefined)
            {
                convertToRenderTarget();
                pushTexture(mTexture, true);
            }
            else
            {
                needRenderTarget();
            }
    
            if (level < levelCount())
            {
                RECT sourceRect;
                sourceRect.left = x;
                sourceRect.right = x + width;
                sourceRect.top = y;
                sourceRect.bottom = y + height;
    
                IDirect3DSurface9 *dest;
                HRESULT hr = mTexture->GetSurfaceLevel(level, &dest);
    
                getBlitter()->formatConvert(source->getRenderTarget(), sourceRect, mImageArray[0].format, xoffset, yoffset, dest);
                dest->Release();
            }
        }
    }
    
    // Tests for GL texture object completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.
    bool Texture2D::isComplete() const
    {
        GLsizei width = mImageArray[0].width;
        GLsizei height = mImageArray[0].height;
    
        if (width <= 0 || height <= 0)
        {
            return false;
        }
    
        bool mipmapping = false;
    
        switch (mMinFilter)
        {
          case GL_NEAREST:
          case GL_LINEAR:
            mipmapping = false;
            break;
          case GL_NEAREST_MIPMAP_NEAREST:
          case GL_LINEAR_MIPMAP_NEAREST:
          case GL_NEAREST_MIPMAP_LINEAR:
          case GL_LINEAR_MIPMAP_LINEAR:
            mipmapping = true;
            break;
         default: UNREACHABLE();
        }
    
        if ((getFormat() == GL_FLOAT && !getContext()->supportsFloatLinearFilter()) ||
            (getFormat() == GL_HALF_FLOAT_OES && !getContext()->supportsHalfFloatLinearFilter()))
        {
            if (mMagFilter != GL_NEAREST || (mMinFilter != GL_NEAREST && mMinFilter != GL_NEAREST_MIPMAP_NEAREST))
            {
                return false;
            }
        }
    
    
        if ((getWrapS() != GL_CLAMP_TO_EDGE && !isPow2(width))
            || (getWrapT() != GL_CLAMP_TO_EDGE && !isPow2(height)))
        {
            return false;
        }
    
        if (mipmapping)
        {
            if (!isPow2(width) || !isPow2(height))
            {
                return false;
            }
    
            int q = log2(std::max(width, height));
    
            for (int level = 1; level <= q; level++)
            {
                if (mImageArray[level].format != mImageArray[0].format)
                {
                    return false;
                }
    
                if (mImageArray[level].width != std::max(1, width >> level))
                {
                    return false;
                }
    
                if (mImageArray[level].height != std::max(1, height >> level))
                {
                    return false;
                }
            }
        }
    
        return true;
    }
    
    bool Texture2D::isCompressed() const
    {
        return IsCompressed(getFormat());
    }
    
    // Constructs a Direct3D 9 texture resource from the texture images, or returns an existing one
    IDirect3DBaseTexture9 *Texture2D::createTexture()
    {
        IDirect3DTexture9 *texture;
    
        IDirect3DDevice9 *device = getDevice();
        D3DFORMAT format = selectFormat(mImageArray[0].format, mType);
    
        HRESULT result = device->CreateTexture(mWidth, mHeight, creationLevels(mWidth, mHeight, 0), 0, format, D3DPOOL_DEFAULT, &texture, NULL);
    
        if (FAILED(result))
        {
            ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
            return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
        }
    
        if (mTexture) mTexture->Release();
        mTexture = texture;
        return texture;
    }
    
    void Texture2D::updateTexture()
    {
        IDirect3DDevice9 *device = getDevice();
    
        int levels = levelCount();
    
        for (int level = 0; level < levels; level++)
        {
            if (mImageArray[level].dirty)
            {
                IDirect3DSurface9 *levelSurface = NULL;
                HRESULT result = mTexture->GetSurfaceLevel(level, &levelSurface);
    
                ASSERT(SUCCEEDED(result));
    
                if (SUCCEEDED(result))
                {
                    result = device->UpdateSurface(mImageArray[level].surface, NULL, levelSurface, NULL);
                    ASSERT(SUCCEEDED(result));
    
                    levelSurface->Release();
    
                    mImageArray[level].dirty = false;
                }
            }
        }
    }
    
    IDirect3DBaseTexture9 *Texture2D::convertToRenderTarget()
    {
        IDirect3DTexture9 *texture = NULL;
    
        if (mWidth != 0 && mHeight != 0)
        {
            egl::Display *display = getDisplay();
            IDirect3DDevice9 *device = getDevice();
            D3DFORMAT format = selectFormat(mImageArray[0].format, mType);
    
            HRESULT result = device->CreateTexture(mWidth, mHeight, creationLevels(mWidth, mHeight, 0), D3DUSAGE_RENDERTARGET, format, D3DPOOL_DEFAULT, &texture, NULL);
    
            if (FAILED(result))
            {
                ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
                return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
            }
    
            if (mTexture != NULL)
            {
                int levels = levelCount();
                for (int i = 0; i < levels; i++)
                {
                    IDirect3DSurface9 *source;
                    result = mTexture->GetSurfaceLevel(i, &source);
    
                    if (FAILED(result))
                    {
                        ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
    
                        texture->Release();
    
                        return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
                    }
    
                    IDirect3DSurface9 *dest;
                    result = texture->GetSurfaceLevel(i, &dest);
    
                    if (FAILED(result))
                    {
                        ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
    
                        texture->Release();
                        source->Release();
    
                        return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
                    }
    
                    display->endScene();
                    result = device->StretchRect(source, NULL, dest, NULL, D3DTEXF_NONE);
    
                    if (FAILED(result))
                    {
                        ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
    
                        texture->Release();
                        source->Release();
                        dest->Release();
    
                        return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
                    }
    
                    source->Release();
                    dest->Release();
                }
            }
        }
    
        if (mTexture != NULL)
        {
            mTexture->Release();
        }
    
        mTexture = texture;
        return mTexture;
    }
    
    bool Texture2D::dirtyImageData() const
    {
        int q = log2(std::max(mWidth, mHeight));
    
        for (int i = 0; i <= q; i++)
        {
            if (mImageArray[i].dirty) return true;
        }
    
        return false;
    }
    
    void Texture2D::generateMipmaps()
    {
        if (!isPow2(mImageArray[0].width) || !isPow2(mImageArray[0].height))
        {
            return error(GL_INVALID_OPERATION);
        }
    
        // Purge array levels 1 through q and reset them to represent the generated mipmap levels.
        unsigned int q = log2(std::max(mWidth, mHeight));
        for (unsigned int i = 1; i <= q; i++)
        {
            if (mImageArray[i].surface != NULL)
            {
                mImageArray[i].surface->Release();
                mImageArray[i].surface = NULL;
            }
    
            mImageArray[i].dirty = false;
    
            mImageArray[i].format = mImageArray[0].format;
            mImageArray[i].width = std::max(mImageArray[0].width >> i, 1);
            mImageArray[i].height = std::max(mImageArray[0].height >> i, 1);
        }
    
        if (isRenderable())
        {
            if (mTexture == NULL)
            {
                ERR(" failed because mTexture was null.");
                return;
            }
    
            for (unsigned int i = 1; i <= q; i++)
            {
                IDirect3DSurface9 *upper = NULL;
                IDirect3DSurface9 *lower = NULL;
    
                mTexture->GetSurfaceLevel(i-1, &upper);
                mTexture->GetSurfaceLevel(i, &lower);
    
                if (upper != NULL && lower != NULL)
                {
                    getBlitter()->boxFilter(upper, lower);
                }
    
                if (upper != NULL) upper->Release();
                if (lower != NULL) lower->Release();
            }
        }
        else
        {
            for (unsigned int i = 1; i <= q; i++)
            {
                createSurface(mImageArray[i].width, mImageArray[i].height, mImageArray[i].format, mType, &mImageArray[i]);
                if (mImageArray[i].surface == NULL)
                {
                    return error(GL_OUT_OF_MEMORY);
                }
    
                if (FAILED(D3DXLoadSurfaceFromSurface(mImageArray[i].surface, NULL, NULL, mImageArray[i - 1].surface, NULL, NULL, D3DX_FILTER_BOX, 0)))
                {
                    ERR(" failed to load filter %d to %d.", i - 1, i);
                }
    
                mImageArray[i].dirty = true;
            }
    
            mDirtyMetaData = true;
        }
    }
    
    Renderbuffer *Texture2D::getColorbuffer(GLenum target)
    {
        if (target != GL_TEXTURE_2D)
        {
            return error(GL_INVALID_OPERATION, (Renderbuffer *)NULL);
        }
    
        if (mColorbufferProxy.get() == NULL)
        {
            mColorbufferProxy.set(new Renderbuffer(id(), new TextureColorbufferProxy(this, target)));
        }
    
        return mColorbufferProxy.get();
    }
    
    IDirect3DSurface9 *Texture2D::getRenderTarget(GLenum target)
    {
        ASSERT(target == GL_TEXTURE_2D);
    
        needRenderTarget();
    
        if (mTexture == NULL)
        {
            return NULL;
        }
        
        IDirect3DSurface9 *renderTarget = NULL;
        mTexture->GetSurfaceLevel(0, &renderTarget);
    
        return renderTarget;
    }
    
    TextureCubeMap::TextureCubeMap(GLuint id) : Texture(id)
    {
        mTexture = NULL;
    }
    
    TextureCubeMap::~TextureCubeMap()
    {
        for (int i = 0; i < 6; i++)
        {
            mFaceProxies[i].set(NULL);
        }
    
        if (mTexture)
        {
            mTexture->Release();
            mTexture = NULL;
        }
    }
    
    GLenum TextureCubeMap::getTarget() const
    {
        return GL_TEXTURE_CUBE_MAP;
    }
    
    GLenum TextureCubeMap::getFormat() const
    {
        return mImageArray[0][0].format;
    }
    
    void TextureCubeMap::setImagePosX(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
    {
        setImage(0, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
    }
    
    void TextureCubeMap::setImageNegX(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
    {
        setImage(1, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
    }
    
    void TextureCubeMap::setImagePosY(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
    {
        setImage(2, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
    }
    
    void TextureCubeMap::setImageNegY(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
    {
        setImage(3, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
    }
    
    void TextureCubeMap::setImagePosZ(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
    {
        setImage(4, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
    }
    
    void TextureCubeMap::setImageNegZ(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
    {
        setImage(5, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
    }
    
    void TextureCubeMap::setCompressedImage(GLenum face, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels)
    {
        redefineTexture(level, internalFormat, width);
    
        Texture::setCompressedImage(width, height, internalFormat, imageSize, pixels, &mImageArray[faceIndex(face)][level]);
    }
    
    void TextureCubeMap::commitRect(GLenum faceTarget, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
    {
        int face = faceIndex(faceTarget);
    
        ASSERT(mImageArray[face][level].surface != NULL);
    
        if (level < levelCount())
        {
            IDirect3DSurface9 *destLevel = getCubeMapSurface(face, level);
            ASSERT(destLevel != NULL);
    
            if (destLevel != NULL)
            {
                Image *img = &mImageArray[face][level];
    
                RECT sourceRect;
                sourceRect.left = xoffset;
                sourceRect.top = yoffset;
                sourceRect.right = xoffset + width;
                sourceRect.bottom = yoffset + height;
    
                POINT destPoint;
                destPoint.x = xoffset;
                destPoint.y = yoffset;
    
                HRESULT result = getDevice()->UpdateSurface(img->surface, &sourceRect, destLevel, &destPoint);
                ASSERT(SUCCEEDED(result));
    
                destLevel->Release();
    
                img->dirty = false;
            }
        }
    }
    
    void TextureCubeMap::subImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
    {
        if (Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, &mImageArray[faceIndex(target)][level]))
        {
            commitRect(target, level, xoffset, yoffset, width, height);
        }
    }
    
    void TextureCubeMap::subImageCompressed(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels)
    {
        if (Texture::subImageCompressed(xoffset, yoffset, width, height, format, imageSize, pixels, &mImageArray[faceIndex(target)][level]))
        {
            commitRect(target, level, xoffset, yoffset, width, height);
        }
    }
    
    // Tests for GL texture object completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.
    bool TextureCubeMap::isComplete() const
    {
        int size = mImageArray[0][0].width;
    
        if (size <= 0)
        {
            return false;
        }
    
        bool mipmapping;
    
        switch (mMinFilter)
        {
          case GL_NEAREST:
          case GL_LINEAR:
            mipmapping = false;
            break;
          case GL_NEAREST_MIPMAP_NEAREST:
          case GL_LINEAR_MIPMAP_NEAREST:
          case GL_NEAREST_MIPMAP_LINEAR:
          case GL_LINEAR_MIPMAP_LINEAR:
            mipmapping = true;
            break;
          default: UNREACHABLE();
        }
    
        for (int face = 0; face < 6; face++)
        {
            if (mImageArray[face][0].width != size || mImageArray[face][0].height != size)
            {
                return false;
            }
        }
    
        if ((getFormat() == GL_FLOAT && !getContext()->supportsFloatLinearFilter()) ||
            (getFormat() == GL_HALF_FLOAT_OES && !getContext()->supportsHalfFloatLinearFilter()))
        {
            if (mMagFilter != GL_NEAREST || (mMinFilter != GL_NEAREST && mMinFilter != GL_NEAREST_MIPMAP_NEAREST))
            {
                return false;
            }
        }
    
        if (mipmapping)
        {
            if (!isPow2(size) && (getWrapS() != GL_CLAMP_TO_EDGE || getWrapT() != GL_CLAMP_TO_EDGE))
            {
                return false;
            }
    
            int q = log2(size);
    
            for (int face = 0; face < 6; face++)
            {
                for (int level = 1; level <= q; level++)
                {
                    if (mImageArray[face][level].format != mImageArray[0][0].format)
                    {
                        return false;
                    }
    
                    if (mImageArray[face][level].width != std::max(1, size >> level))
                    {
                        return false;
                    }
    
                    ASSERT(mImageArray[face][level].height == mImageArray[face][level].width);
                }
            }
        }
    
        return true;
    }
    
    bool TextureCubeMap::isCompressed() const
    {
        return IsCompressed(getFormat());
    }
    
    // Constructs a Direct3D 9 texture resource from the texture images, or returns an existing one
    IDirect3DBaseTexture9 *TextureCubeMap::createTexture()
    {
        IDirect3DDevice9 *device = getDevice();
        D3DFORMAT format = selectFormat(mImageArray[0][0].format, mType);
    
        IDirect3DCubeTexture9 *texture;
    
        HRESULT result = device->CreateCubeTexture(mWidth, creationLevels(mWidth, 0), 0, format, D3DPOOL_DEFAULT, &texture, NULL);
    
        if (FAILED(result))
        {
            ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
            return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
        }
    
        if (mTexture) mTexture->Release();
    
        mTexture = texture;
        return mTexture;
    }
    
    void TextureCubeMap::updateTexture()
    {
        IDirect3DDevice9 *device = getDevice();
    
        for (int face = 0; face < 6; face++)
        {
            int levels = levelCount();
            for (int level = 0; level < levels; level++)
            {
                Image *img = &mImageArray[face][level];
    
                if (img->dirty)
                {
                    IDirect3DSurface9 *levelSurface = getCubeMapSurface(face, level);
                    ASSERT(levelSurface != NULL);
    
                    if (levelSurface != NULL)
                    {
                        HRESULT result = device->UpdateSurface(img->surface, NULL, levelSurface, NULL);
                        ASSERT(SUCCEEDED(result));
    
                        levelSurface->Release();
    
                        img->dirty = false;
                    }
                }
            }
        }
    }
    
    IDirect3DBaseTexture9 *TextureCubeMap::convertToRenderTarget()
    {
        IDirect3DCubeTexture9 *texture = NULL;
    
        if (mWidth != 0)
        {
            egl::Display *display = getDisplay();
            IDirect3DDevice9 *device = getDevice();
            D3DFORMAT format = selectFormat(mImageArray[0][0].format, mType);
    
            HRESULT result = device->CreateCubeTexture(mWidth, creationLevels(mWidth, 0), D3DUSAGE_RENDERTARGET, format, D3DPOOL_DEFAULT, &texture, NULL);
    
            if (FAILED(result))
            {
                ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
                return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
            }
    
            if (mTexture != NULL)
            {
                int levels = levelCount();
                for (int f = 0; f < 6; f++)
                {
                    for (int i = 0; i < levels; i++)
                    {
                        IDirect3DSurface9 *source;
                        result = mTexture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(f), i, &source);
    
                        if (FAILED(result))
                        {
                            ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
    
                            texture->Release();
    
                            return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
                        }
    
                        IDirect3DSurface9 *dest;
                        result = texture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(f), i, &dest);
    
                        if (FAILED(result))
                        {
                            ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
    
                            texture->Release();
                            source->Release();
    
                            return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
                        }
    
                        display->endScene();
                        result = device->StretchRect(source, NULL, dest, NULL, D3DTEXF_NONE);
    
                        if (FAILED(result))
                        {
                            ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
    
                            texture->Release();
                            source->Release();
                            dest->Release();
    
                            return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
                        }
                    }
                }
            }
        }
    
        if (mTexture != NULL)
        {
            mTexture->Release();
        }
    
        mTexture = texture;
        return mTexture;
    }
    
    void TextureCubeMap::setImage(int face, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
    {
        redefineTexture(level, internalFormat, width);
    
        Texture::setImage(width, height, format, type, unpackAlignment, pixels, &mImageArray[face][level]);
    }
    
    unsigned int TextureCubeMap::faceIndex(GLenum face)
    {
        META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_X - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 1);
        META_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_Y - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 2);
        META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 3);
        META_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_Z - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 4);
        META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 5);
    
        return face - GL_TEXTURE_CUBE_MAP_POSITIVE_X;
    }
    
    bool TextureCubeMap::dirtyImageData() const
    {
        int q = log2(mWidth);
    
        for (int f = 0; f < 6; f++)
        {
            for (int i = 0; i <= q; i++)
            {
                if (mImageArray[f][i].dirty) return true;
            }
        }
    
        return false;
    }
    
    // While OpenGL doesn't check texture consistency until draw-time, D3D9 requires a complete texture
    // for render-to-texture (such as CopyTexImage). We have no way of keeping individual inconsistent levels & faces.
    // Call this when a particular level of the texture must be defined with a specific format, width and height.
    //
    // Returns true if the existing texture was unsuitable had to be destroyed. If so, it will also set
    // a new size for the texture by working backwards from the given size.
    bool TextureCubeMap::redefineTexture(GLint level, GLenum internalFormat, GLsizei width)
    {
        // Are these settings compatible with level 0?
        bool sizeOkay = (mImageArray[0][0].width >> level == width);
    
        bool textureOkay = (sizeOkay && internalFormat == mImageArray[0][0].format);
    
        if (!textureOkay)
        {
            TRACE("Redefining cube texture (%d, 0x%04X, %d => 0x%04X, %d).", level,
                  mImageArray[0][0].format, mImageArray[0][0].width,
                  internalFormat, width);
    
            // Purge all the levels and the texture.
            for (int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
            {
                for (int f = 0; f < 6; f++)
                {
                    if (mImageArray[f][i].surface != NULL)
                    {
                        mImageArray[f][i].dirty = false;
    
                        mImageArray[f][i].surface->Release();
                        mImageArray[f][i].surface = NULL;
                    }
                }
            }
    
            if (mTexture != NULL)
            {
                mTexture->Release();
                mTexture = NULL;
                dropTexture();
            }
    
            mWidth = width << level;
            mImageArray[0][0].width = width << level;
            mHeight = width << level;
            mImageArray[0][0].height = width << level;
    
            mImageArray[0][0].format = internalFormat;
        }
    
        return !textureOkay;
    }
    
    void TextureCubeMap::copyImage(GLenum target, GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height, RenderbufferStorage *source)
    {
        IDirect3DSurface9 *renderTarget = source->getRenderTarget();
    
        if (!renderTarget)
        {
            ERR("Failed to retrieve the render target.");
            return error(GL_OUT_OF_MEMORY);
        }
    
        unsigned int faceindex = faceIndex(target);
        bool redefined = redefineTexture(level, internalFormat, width);
    
        if (!isRenderableFormat())
        {
            copyNonRenderable(&mImageArray[faceindex][level], internalFormat, 0, 0, x, y, width, height, renderTarget);
        }
        else
        {
            if (redefined)
            {
                convertToRenderTarget();
                pushTexture(mTexture, true);
            }
            else
            {
                needRenderTarget();
            }
    
            ASSERT(width == height);
    
            if (width > 0 && level < levelCount())
            {
                RECT sourceRect;
                sourceRect.left = x;
                sourceRect.right = x + width;
                sourceRect.top = y;
                sourceRect.bottom = y + height;
    
                IDirect3DSurface9 *dest = getCubeMapSurface(target, level);
    
                getBlitter()->formatConvert(source->getRenderTarget(), sourceRect, internalFormat, 0, 0, dest);
                dest->Release();
            }
        }
    
        mImageArray[faceindex][level].width = width;
        mImageArray[faceindex][level].height = height;
        mImageArray[faceindex][level].format = internalFormat;
    }
    
    IDirect3DSurface9 *TextureCubeMap::getCubeMapSurface(unsigned int faceIdentifier, unsigned int level)
    {
        unsigned int faceIndex;
    
        if (faceIdentifier < 6)
        {
            faceIndex = faceIdentifier;
        }
        else if (faceIdentifier >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && faceIdentifier <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z)
        {
            faceIndex = faceIdentifier - GL_TEXTURE_CUBE_MAP_POSITIVE_X;
        }
        else
        {
            UNREACHABLE();
            faceIndex = 0;
        }
    
        if (mTexture == NULL)
        {
            UNREACHABLE();
            return NULL;
        }
    
        IDirect3DSurface9 *surface = NULL;
    
        HRESULT hr = mTexture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(faceIndex), level, &surface);
    
        return (SUCCEEDED(hr)) ? surface : NULL;
    }
    
    void TextureCubeMap::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, RenderbufferStorage *source)
    {
        GLsizei size = mImageArray[faceIndex(target)][level].width;
    
        if (xoffset + width > size || yoffset + height > size)
        {
            return error(GL_INVALID_VALUE);
        }
    
        IDirect3DSurface9 *renderTarget = source->getRenderTarget();
    
        if (!renderTarget)
        {
            ERR("Failed to retrieve the render target.");
            return error(GL_OUT_OF_MEMORY);
        }
    
        unsigned int faceindex = faceIndex(target);
        bool redefined = redefineTexture(0, mImageArray[0][0].format, mImageArray[0][0].width);
       
        if (!isRenderableFormat())
        {
            copyNonRenderable(&mImageArray[faceindex][level], getFormat(), 0, 0, x, y, width, height, renderTarget);
        }
        else
        {
            if (redefined)
            {
                convertToRenderTarget();
                pushTexture(mTexture, true);
            }
            else
            {
                needRenderTarget();
            }
    
            if (level < levelCount())
            {
                RECT sourceRect;
                sourceRect.left = x;
                sourceRect.right = x + width;
                sourceRect.top = y;
                sourceRect.bottom = y + height;
    
                IDirect3DSurface9 *dest = getCubeMapSurface(target, level);
    
                getBlitter()->formatConvert(source->getRenderTarget(), sourceRect, mImageArray[0][0].format, xoffset, yoffset, dest);
                dest->Release();
            }
        }
    }
    
    bool TextureCubeMap::isCubeComplete() const
    {
        if (mImageArray[0][0].width == 0)
        {
            return false;
        }
    
        for (unsigned int f = 1; f < 6; f++)
        {
            if (mImageArray[f][0].width != mImageArray[0][0].width
                || mImageArray[f][0].format != mImageArray[0][0].format)
            {
                return false;
            }
        }
    
        return true;
    }
    
    void TextureCubeMap::generateMipmaps()
    {
        if (!isPow2(mImageArray[0][0].width) || !isCubeComplete())
        {
            return error(GL_INVALID_OPERATION);
        }
    
        // Purge array levels 1 through q and reset them to represent the generated mipmap levels.
        unsigned int q = log2(mImageArray[0][0].width);
        for (unsigned int f = 0; f < 6; f++)
        {
            for (unsigned int i = 1; i <= q; i++)
            {
                if (mImageArray[f][i].surface != NULL)
                {
                    mImageArray[f][i].surface->Release();
                    mImageArray[f][i].surface = NULL;
                }
    
                mImageArray[f][i].dirty = false;
    
                mImageArray[f][i].format = mImageArray[f][0].format;
                mImageArray[f][i].width = std::max(mImageArray[f][0].width >> i, 1);
                mImageArray[f][i].height = mImageArray[f][i].width;
            }
        }
    
        if (isRenderable())
        {
            if (mTexture == NULL)
            {
                return;
            }
    
            for (unsigned int f = 0; f < 6; f++)
            {
                for (unsigned int i = 1; i <= q; i++)
                {
                    IDirect3DSurface9 *upper = getCubeMapSurface(f, i-1);
                    IDirect3DSurface9 *lower = getCubeMapSurface(f, i);
    
                    if (upper != NULL && lower != NULL)
                    {
                        getBlitter()->boxFilter(upper, lower);
                    }
    
                    if (upper != NULL) upper->Release();
                    if (lower != NULL) lower->Release();
                }
            }
        }
        else
        {
            for (unsigned int f = 0; f < 6; f++)
            {
                for (unsigned int i = 1; i <= q; i++)
                {
                    createSurface(mImageArray[f][i].width, mImageArray[f][i].height, mImageArray[f][i].format, mType, &mImageArray[f][i]);
                    if (mImageArray[f][i].surface == NULL)
                    {
                        return error(GL_OUT_OF_MEMORY);
                    }
    
                    if (FAILED(D3DXLoadSurfaceFromSurface(mImageArray[f][i].surface, NULL, NULL, mImageArray[f][i - 1].surface, NULL, NULL, D3DX_FILTER_BOX, 0)))
                    {
                        ERR(" failed to load filter %d to %d.", i - 1, i);
                    }
    
                    mImageArray[f][i].dirty = true;
                }
            }
    
            mDirtyMetaData = true;
        }
    }
    
    Renderbuffer *TextureCubeMap::getColorbuffer(GLenum target)
    {
        if (!IsCubemapTextureTarget(target))
        {
            return error(GL_INVALID_OPERATION, (Renderbuffer *)NULL);
        }
    
        unsigned int face = faceIndex(target);
    
        if (mFaceProxies[face].get() == NULL)
        {
            mFaceProxies[face].set(new Renderbuffer(id(), new TextureColorbufferProxy(this, target)));
        }
    
        return mFaceProxies[face].get();
    }
    
    IDirect3DSurface9 *TextureCubeMap::getRenderTarget(GLenum target)
    {
        ASSERT(IsCubemapTextureTarget(target));
    
        needRenderTarget();
        
        if (mTexture == NULL)
        {
            return NULL;
        }
        
        IDirect3DSurface9 *renderTarget = NULL;
        mTexture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(faceIndex(target)), 0, &renderTarget);
    
        return renderTarget;
    }
    
    Texture::TextureColorbufferProxy::TextureColorbufferProxy(Texture *texture, GLenum target)
      : Colorbuffer(texture), mTexture(texture), mTarget(target)
    {
        ASSERT(IsTextureTarget(target));
    }
    
    void Texture::TextureColorbufferProxy::addRef() const
    {
        mTexture->addRef();
    }
    
    void Texture::TextureColorbufferProxy::release() const
    {
        mTexture->release();
    }
    
    IDirect3DSurface9 *Texture::TextureColorbufferProxy::getRenderTarget()
    {
        if (mRenderTarget) mRenderTarget->Release();
    
        mRenderTarget = mTexture->getRenderTarget(mTarget);
    
        return mRenderTarget;
    }
    
    int Texture::TextureColorbufferProxy::getWidth() const
    {
        return mTexture->getWidth();
    }
    
    int Texture::TextureColorbufferProxy::getHeight() const
    {
        return mTexture->getHeight();
    }
    
    GLenum Texture::TextureColorbufferProxy::getFormat() const
    {
        return mTexture->getFormat();
    }
    
    bool Texture::TextureColorbufferProxy::isFloatingPoint() const
    {
        return mTexture->isFloatingPoint();
    }
    
    }