Support redefinition of textures through glTexImage2D.

TRAC #11316.
Keep a dirty data flag, recreate the texture when it is set.
Texture now owns getTexture() and calls a virtual
createTexture method when the derived classes need to recreate.

Signed-off-by: Daniel Koch

Author:    Andrew Lewycky <andrew.lewycky@transgaming.com>

git-svn-id: https://angleproject.googlecode.com/svn/trunk@12 736b8ea6-26fd-11df-bfd4-992fa37f6226
diff --git a/libGLESv2/Texture.cpp b/libGLESv2/Texture.cpp
index 1ea1ed3..dcea3c0 100644
--- a/libGLESv2/Texture.cpp
+++ b/libGLESv2/Texture.cpp
@@ -24,6 +24,8 @@
     mMagFilter = GL_LINEAR;
     mWrapS = GL_REPEAT;
     mWrapT = GL_REPEAT;
+
+    mDirtyImageData = true;
 }
 
 Texture::~Texture()
@@ -113,24 +115,24 @@
 }
 
 // Copies an Image into an already locked Direct3D 9 surface, performing format conversions as necessary
-void Texture::copyImage(D3DLOCKED_RECT &lock, D3DFORMAT format, Image &image)
+void Texture::copyImage(const D3DLOCKED_RECT &lock, D3DFORMAT format, Image *image)
 {
-    if (lock.pBits && image.pixels)
+    if (lock.pBits && !image->pixels.empty())
     {
-        for (int y = 0; y < image.height; y++)
+        for (int y = 0; y < image->height; y++)
         {
-            unsigned char *source = (unsigned char*)image.pixels + y * image.width * pixelSize(image);
+            unsigned char *source = &image->pixels[0] + y * image->width * pixelSize(*image);
             unsigned short *source16 = (unsigned short*)source;
             unsigned char *dest = (unsigned char*)lock.pBits + y * lock.Pitch;
 
-            for (int x = 0; x < image.width; x++)
+            for (int x = 0; x < image->width; x++)
             {
                 unsigned char r;
                 unsigned char g;
                 unsigned char b;
                 unsigned char a;
 
-                switch (image.format)
+                switch (image->format)
                 {
                   case GL_ALPHA:
                     UNIMPLEMENTED();
@@ -142,7 +144,7 @@
                     UNIMPLEMENTED();
                     break;
                   case GL_RGB:
-                    switch (image.type)
+                    switch (image->type)
                     {
                       case GL_UNSIGNED_BYTE:          UNIMPLEMENTED(); break;
                       case GL_UNSIGNED_SHORT_5_6_5:
@@ -159,7 +161,7 @@
                     }
                     break;
                   case GL_RGBA:
-                    switch (image.type)
+                    switch (image->type)
                     {
                       case GL_UNSIGNED_BYTE:
                         r = source[x * 4 + 0];
@@ -262,23 +264,52 @@
     return 0;
 }
 
-Texture2D::Texture2D()
+void Texture::setImage(GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels, Image *img)
 {
-    for (int level = 0; level < MAX_TEXTURE_LEVELS; level++)
+    mDirtyImageData = true;
+
+    img->internalFormat = internalFormat;
+    img->width = width;
+    img->height = height;
+    img->format = format;
+    img->type = type;
+
+    size_t imageSize = pixelSize(*img) * width * height;
+
+    std::vector<unsigned char> storage(imageSize);
+
+    if (pixels)
     {
-        mImageArray[level].pixels = NULL;
+        memcpy(&storage[0], pixels, imageSize);
     }
 
+    img->pixels.swap(storage);
+}
+
+IDirect3DBaseTexture9 *Texture::getTexture()
+{
+    if (!isComplete())
+    {
+        return NULL;
+    }
+
+    if (mDirtyImageData)
+    {
+        mBaseTexture = createTexture();
+
+        mDirtyImageData = false;
+    }
+
+    return mBaseTexture;
+}
+
+Texture2D::Texture2D()
+{
     mTexture = NULL;
 }
 
 Texture2D::~Texture2D()
 {
-    for (int level = 0; level < MAX_TEXTURE_LEVELS; level++)
-    {
-        delete[] mImageArray[level].pixels;
-    }
-
     if (mTexture)
     {
         mTexture->Release();
@@ -298,22 +329,7 @@
         return;
     }
 
-    mImageArray[level].internalFormat = internalFormat;
-    mImageArray[level].width = width;
-    mImageArray[level].height = height;
-    mImageArray[level].format = format;
-    mImageArray[level].type = type;
-
-    delete[] mImageArray[level].pixels;
-    mImageArray[level].pixels = NULL;
-
-    int imageSize = pixelSize(mImageArray[level]) * width * height;
-    mImageArray[level].pixels = new unsigned char[imageSize];
-
-    if (pixels)
-    {
-        memcpy(mImageArray[level].pixels, pixels, imageSize);
-    }
+    Texture::setImage(internalFormat, width, height, format, type, pixels, &mImageArray[level]);
 
     if (level == 0)
     {
@@ -384,63 +400,63 @@
 }
 
 // Constructs a Direct3D 9 texture resource from the texture images, or returns an existing one
-IDirect3DBaseTexture9 *Texture2D::getTexture()
+IDirect3DBaseTexture9 *Texture2D::createTexture()
 {
-    if (!isComplete())
+    IDirect3DTexture9 *texture;
+
+    IDirect3DDevice9 *device = getDevice();
+    D3DFORMAT format = selectFormat(mImageArray[0]);
+
+    HRESULT result = device->CreateTexture(mWidth, mHeight, 0, D3DUSAGE_RENDERTARGET, format, D3DPOOL_DEFAULT, &texture, NULL);
+
+    if (result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY)
     {
-        return NULL;
+        return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
     }
 
-    if (!mTexture)   // FIXME: Recreate when changed (same for getRenderTarget)
+    ASSERT(SUCCEEDED(result));
+
+    IDirect3DTexture9 *lockableTexture;
+    result = device->CreateTexture(mWidth, mHeight, 0, D3DUSAGE_DYNAMIC, format, D3DPOOL_SYSTEMMEM, &lockableTexture, NULL);
+
+    if (result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY)
     {
-        IDirect3DDevice9 *device = getDevice();
-        D3DFORMAT format = selectFormat(mImageArray[0]);
-
-        HRESULT result = device->CreateTexture(mWidth, mHeight, 0, D3DUSAGE_RENDERTARGET, format, D3DPOOL_DEFAULT, &mTexture, NULL);
-
-        if (result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY)
-        {
-            error(GL_OUT_OF_MEMORY, 0);
-        }
-
-        ASSERT(SUCCEEDED(result));
-
-        IDirect3DTexture9 *lockableTexture;
-        result = device->CreateTexture(mWidth, mHeight, 0, D3DUSAGE_DYNAMIC, format, D3DPOOL_SYSTEMMEM, &lockableTexture, NULL);
-
-        if (result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY)
-        {
-            error(GL_OUT_OF_MEMORY,(IDirect3DBaseTexture9*)NULL);
-        }
-
-        ASSERT(SUCCEEDED(result));
-
-        if (mTexture)   // FIXME: Handle failure
-        {
-            int levelCount = mTexture->GetLevelCount();
-
-            for (int level = 0; level < levelCount; level++)
-            {
-                D3DLOCKED_RECT lock = {0};
-                lockableTexture->LockRect(level, &lock, NULL, 0);
-
-                copyImage(lock, format, mImageArray[level]);
-
-                lockableTexture->UnlockRect(level);
-            }
-
-            device->UpdateTexture(lockableTexture, mTexture);
-            lockableTexture->Release();
-        }
+        texture->Release();
+        return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
     }
 
-    return mTexture;
+    ASSERT(SUCCEEDED(result));
+
+    int levelCount = texture->GetLevelCount();
+
+    for (int level = 0; level < levelCount; level++)
+    {
+        D3DLOCKED_RECT lock = {0};
+        lockableTexture->LockRect(level, &lock, NULL, 0);
+
+        copyImage(lock, format, &mImageArray[level]);
+
+        lockableTexture->UnlockRect(level);
+    }
+
+    device->UpdateTexture(lockableTexture, texture);
+    lockableTexture->Release();
+
+    if (mTexture) mTexture->Release();
+    mTexture = texture;
+    return texture;
 }
 
 // Returns the top-level texture surface as a render target
 IDirect3DSurface9 *Texture2D::getRenderTarget()
 {
-    if (!mRenderTarget && getTexture())
+    if (mDirtyImageData && mRenderTarget)
+    {
+        mRenderTarget->Release();
+        mRenderTarget = NULL;
+    }
+
+    if (!mRenderTarget && getTexture()) // FIXME: getTexture fails for incomplete textures. Check spec.
     {
         mTexture->GetSurfaceLevel(0, &mRenderTarget);
     }
@@ -450,27 +466,11 @@
 
 TextureCubeMap::TextureCubeMap()
 {
-    for (int face = 0; face < 6; face++)
-    {
-        for (int level = 0; level < MAX_TEXTURE_LEVELS; level++)
-        {
-            mImageArray[face][level].pixels = NULL;
-        }
-    }
-
     mTexture = NULL;
 }
 
 TextureCubeMap::~TextureCubeMap()
 {
-    for (int face = 0; face < 6; face++)
-    {
-        for (int level = 0; level < MAX_TEXTURE_LEVELS; level++)
-        {
-            delete[] mImageArray[face][level].pixels;
-        }
-    }
-
     if (mTexture)
     {
         mTexture->Release();
@@ -586,57 +586,52 @@
 }
 
 // Constructs a Direct3D 9 texture resource from the texture images, or returns an existing one
-IDirect3DBaseTexture9 *TextureCubeMap::getTexture()
+IDirect3DBaseTexture9 *TextureCubeMap::createTexture()
 {
-    if (!isComplete())
+    IDirect3DDevice9 *device = getDevice();
+    D3DFORMAT format = selectFormat(mImageArray[0][0]);
+
+    IDirect3DCubeTexture9 *texture;
+
+    HRESULT result = device->CreateCubeTexture(mWidth, 0, D3DUSAGE_RENDERTARGET, format, D3DPOOL_DEFAULT, &texture, NULL);
+
+    if (result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY)
     {
-        return NULL;
+        return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
     }
 
-    if (!mTexture)   // FIXME: Recreate when changed (same for getRenderTarget)
+    ASSERT(SUCCEEDED(result));
+
+    IDirect3DCubeTexture9 *lockableTexture;
+    result = device->CreateCubeTexture(mWidth, 0, D3DUSAGE_DYNAMIC, format, D3DPOOL_SYSTEMMEM, &lockableTexture, NULL);
+
+    if (result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY)
     {
-        IDirect3DDevice9 *device = getDevice();
-        D3DFORMAT format = selectFormat(mImageArray[0][0]);
+        texture->Release();
+        return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
+    }
 
-        HRESULT result = device->CreateCubeTexture(mWidth, 0, D3DUSAGE_RENDERTARGET, format, D3DPOOL_DEFAULT, &mTexture, NULL);
+    ASSERT(SUCCEEDED(result));
 
-        if (result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY)
+    for (int face = 0; face < 6; face++)
+    {
+        for (int level = 0; level < MAX_TEXTURE_LEVELS; level++)
         {
-            error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
-        }
+            D3DLOCKED_RECT lock = {0};
+            lockableTexture->LockRect((D3DCUBEMAP_FACES)face, level, &lock, NULL, 0);
 
-        ASSERT(SUCCEEDED(result));
+            copyImage(lock, format, &mImageArray[face][level]);
 
-        IDirect3DCubeTexture9 *lockableTexture;
-        result = device->CreateCubeTexture(mWidth, 0, D3DUSAGE_DYNAMIC, format, D3DPOOL_SYSTEMMEM, &lockableTexture, NULL);
-
-        if (result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY)
-        {
-            return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
-        }
-
-        ASSERT(SUCCEEDED(result));
-
-        if (mTexture)
-        {
-            for (int face = 0; face < 6; face++)
-            {
-                for (int level = 0; level < MAX_TEXTURE_LEVELS; level++)
-                {
-                    D3DLOCKED_RECT lock = {0};
-                    lockableTexture->LockRect((D3DCUBEMAP_FACES)face, level, &lock, NULL, 0);
-
-                    copyImage(lock, format, mImageArray[face][level]);
-
-                    lockableTexture->UnlockRect((D3DCUBEMAP_FACES)face, level);
-                }
-            }
-
-            device->UpdateTexture(lockableTexture, mTexture);
-            lockableTexture->Release();
+            lockableTexture->UnlockRect((D3DCUBEMAP_FACES)face, level);
         }
     }
 
+    device->UpdateTexture(lockableTexture, texture);
+    lockableTexture->Release();
+
+    if (mTexture) mTexture->Release();
+
+    mTexture = texture;
     return mTexture;
 }
 
@@ -647,22 +642,7 @@
         return;
     }
 
-    mImageArray[face][level].internalFormat = internalFormat;
-    mImageArray[face][level].width = width;
-    mImageArray[face][level].height = height;
-    mImageArray[face][level].format = format;
-    mImageArray[face][level].type = type;
-
-    delete[] mImageArray[face][level].pixels;
-    mImageArray[face][level].pixels = NULL;
-
-    int imageSize = pixelSize(mImageArray[face][level]) * width * height;
-    mImageArray[face][level].pixels = new unsigned char[imageSize];
-
-    if (pixels)
-    {
-        memcpy(mImageArray[face][level].pixels, pixels, imageSize);
-    }
+    Texture::setImage(internalFormat, width, height, format, type, pixels, &mImageArray[face][level]);
 
     if (face == 0 && level == 0)
     {
diff --git a/libGLESv2/Texture.h b/libGLESv2/Texture.h
index b6e604f..f492ddb 100644
--- a/libGLESv2/Texture.h
+++ b/libGLESv2/Texture.h
@@ -17,6 +17,8 @@
 #include <GLES2/gl2.h>
 #include <d3d9.h>
 
+#include <vector>
+
 namespace gl
 {
 enum
@@ -27,17 +29,6 @@
     MAX_TEXTURE_LEVELS = 11   // log2 of MAX_TEXTURE_SIZE
 };
 
-// Helper structure representing a single image layer
-struct Image
-{
-    GLenum internalFormat;
-    GLsizei width;
-    GLsizei height;
-    GLenum format;
-    GLenum type;
-    void *pixels;
-};
-
 class Texture : public Colorbuffer
 {
   public:
@@ -58,10 +49,22 @@
     GLenum getWrapT();
 
     virtual bool isComplete() = 0;
-    virtual IDirect3DBaseTexture9 *getTexture() = 0;
+    IDirect3DBaseTexture9 *getTexture();
 
   protected:
-    void copyImage(D3DLOCKED_RECT &lock, D3DFORMAT format, Image &image);
+    // Helper structure representing a single image layer
+    struct Image
+    {
+        GLenum internalFormat;
+        GLsizei width;
+        GLsizei height;
+        GLenum format;
+        GLenum type;
+
+        std::vector<unsigned char> pixels;
+    };
+
+    void copyImage(const D3DLOCKED_RECT &lock, D3DFORMAT format, Image *image);
 
     static D3DFORMAT selectFormat(const Image &image);
     static int pixelSize(const Image &image);
@@ -71,8 +74,17 @@
     GLenum mWrapS;
     GLenum mWrapT;
 
+    void setImage(GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels, Image *img);
+
+    // The pointer returned is weak and it is assumed the derived class will keep a strong pointer until the next createTexture() call.
+    virtual IDirect3DBaseTexture9 *createTexture() = 0;
+
+    bool mDirtyImageData; // FIXME: would be private but getRenderTarget is still implemented through the derived classes and they need it.
+
   private:
     DISALLOW_COPY_AND_ASSIGN(Texture);
+
+    IDirect3DBaseTexture9 *mBaseTexture; // This is a weak pointer. The derived class is assumed to own a strong pointer.
 };
 
 class Texture2D : public Texture
@@ -97,12 +109,13 @@
     GLenum getWrapT();
 
     bool isComplete();
-    IDirect3DBaseTexture9 *getTexture();
     IDirect3DSurface9 *getRenderTarget();
 
   private:
     DISALLOW_COPY_AND_ASSIGN(Texture2D);
 
+    virtual IDirect3DBaseTexture9 *createTexture();
+
     Image mImageArray[MAX_TEXTURE_LEVELS];
 
     IDirect3DTexture9 *mTexture;
@@ -125,11 +138,12 @@
     void setImageNegZ(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels);
 
     bool isComplete();
-    IDirect3DBaseTexture9 *getTexture();
 
   private:
     DISALLOW_COPY_AND_ASSIGN(TextureCubeMap);
 
+    virtual IDirect3DBaseTexture9 *createTexture();
+
     void setImage(int face, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels);
 
     Image mImageArray[6][MAX_TEXTURE_LEVELS];