Partial TexSubImage after FBO rendering overwrites entire image.
TRAC #11439
* Store texel data in IDirect3DSurface9 rather than client memory.
* TexSubImage uploads new data immediately.
* Fix 5551 texture format conversion.
Author: Andrew Lewycky
Signed-off-by: Nicolas Capens
Signed-off-by: Daniel Koch
git-svn-id: https://angleproject.googlecode.com/svn/trunk@53 736b8ea6-26fd-11df-bfd4-992fa37f6226
diff --git a/src/libGLESv2/Texture.cpp b/src/libGLESv2/Texture.cpp
index f7dffcc..d727128 100644
--- a/src/libGLESv2/Texture.cpp
+++ b/src/libGLESv2/Texture.cpp
@@ -18,6 +18,17 @@
namespace gl
{
+
+Texture::Image::Image()
+ : dirty(false), surface(NULL)
+{
+}
+
+Texture::Image::~Image()
+{
+ if (surface) surface->Release();
+}
+
Texture::Texture() : Colorbuffer(0)
{
mMinFilter = GL_NEAREST_MIPMAP_LINEAR;
@@ -25,7 +36,6 @@
mWrapS = GL_REPEAT;
mWrapT = GL_REPEAT;
- mDirtyImageData = true;
mDirtyMetaData = true;
}
@@ -115,31 +125,8 @@
return mWrapT;
}
-// Copies an Image into an already locked Direct3D 9 surface, performing format conversions as necessary
-void Texture::copyImage(const D3DLOCKED_RECT &lock, D3DFORMAT format, const Image &image)
-{
- ASSERT(format == D3DFMT_A8R8G8B8);
-
- std::size_t sourcePitch = imagePitch(image);
-
- if (lock.pBits && !image.pixels.empty())
- {
- if (lock.Pitch == sourcePitch)
- {
- memcpy(lock.pBits, &image.pixels[0], lock.Pitch * image.height);
- }
- else
- {
- for (int y = 0; y < image.height; y++)
- {
- memcpy(static_cast<unsigned char*>(lock.pBits) + y * lock.Pitch, &image.pixels[0] + y * sourcePitch, sourcePitch);
- }
- }
- }
-}
-
// Selects an internal Direct3D 9 format for storing an Image
-D3DFORMAT Texture::selectFormat(const Image &image)
+D3DFORMAT Texture::selectFormat(GLenum format)
{
return D3DFMT_A8R8G8B8;
}
@@ -269,9 +256,9 @@
unsigned short rgba = source16[x];
a = (rgba & 0x0001) ? 0xFF : 0;
- b = ((rgba & 0x003E) << 2) | ((rgba & 0x903E) >> 3);
+ b = ((rgba & 0x003E) << 2) | ((rgba & 0x003E) >> 3);
g = ((rgba & 0x07C0) >> 3) | ((rgba & 0x07C0) >> 8);
- r = ((rgba & 0xF800) >> 8) | ((rgba & 0x07C0) >> 13);
+ r = ((rgba & 0xF800) >> 8) | ((rgba & 0xF800) >> 13);
}
break;
@@ -291,22 +278,38 @@
void Texture::setImage(GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels, Image *img)
{
+ IDirect3DSurface9 *newSurface = NULL;
+ HRESULT result = getDevice()->CreateOffscreenPlainSurface(width, height, selectFormat(format), D3DPOOL_SYSTEMMEM, &newSurface, NULL);
+
+ if (FAILED(result))
+ {
+ ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
+ return error(GL_OUT_OF_MEMORY);
+ }
+
+ if (img->surface) img->surface->Release();
+ img->surface = newSurface;
+
img->width = width;
img->height = height;
img->format = format;
- size_t imageSize = imagePitch(*img) * img->height;
-
- std::vector<unsigned char> storage(imageSize);
-
- if (pixels)
+ if (pixels != NULL)
{
- loadImageData(0, 0, width, height, format, type, pixels, imagePitch(*img), &storage[0]);
+ D3DLOCKED_RECT locked;
+ HRESULT result = newSurface->LockRect(&locked, NULL, 0);
+
+ ASSERT(SUCCEEDED(result));
+
+ if (SUCCEEDED(result))
+ {
+ loadImageData(0, 0, width, height, format, type, pixels, locked.Pitch, locked.pBits);
+ newSurface->UnlockRect();
+ }
+
+ img->dirty = true;
}
- img->pixels.swap(storage);
-
- mDirtyImageData = true;
mDirtyMetaData = true;
}
@@ -314,9 +317,18 @@
{
if (width + xoffset > img->width || height + yoffset > img->height) return error(GL_INVALID_VALUE);
- loadImageData(xoffset, yoffset, width, height, format, type, pixels, imagePitch(*img), &img->pixels[0]);
+ D3DLOCKED_RECT locked;
+ HRESULT result = img->surface->LockRect(&locked, NULL, 0);
- mDirtyImageData = true;
+ ASSERT(SUCCEEDED(result));
+
+ if (SUCCEEDED(result))
+ {
+ loadImageData(xoffset, yoffset, width, height, format, type, pixels, locked.Pitch, locked.pBits);
+ img->surface->UnlockRect();
+ }
+
+ img->dirty = true;
}
IDirect3DBaseTexture9 *Texture::getTexture()
@@ -328,18 +340,16 @@
if (mDirtyMetaData)
{
- ASSERT(mDirtyImageData);
-
mBaseTexture = createTexture();
}
- if (mDirtyImageData)
+ if (mDirtyMetaData || dirtyImageData())
{
updateTexture();
}
mDirtyMetaData = false;
- mDirtyImageData = false;
+ ASSERT(!dirtyImageData());
return mBaseTexture;
}
@@ -374,9 +384,39 @@
}
}
+void Texture2D::commitRect(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
+{
+ ASSERT(mImageArray[level].surface != NULL);
+
+ if (mTexture != NULL)
+ {
+ IDirect3DSurface9 *destLevel = NULL;
+ mTexture->GetSurfaceLevel(level, &destLevel);
+
+ 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;
+
+ getDevice()->UpdateSurface(img->surface, &sourceRect, destLevel, &destPoint);
+
+ destLevel->Release();
+
+ img->dirty = false;
+ }
+}
+
void Texture2D::subImage(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels)
{
Texture::subImage(xoffset, yoffset, width, height, format, type, pixels, &mImageArray[level]);
+ commitRect(level, xoffset, yoffset, width, height);
}
// Tests for GL texture object completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.
@@ -438,7 +478,7 @@
IDirect3DTexture9 *texture;
IDirect3DDevice9 *device = getDevice();
- D3DFORMAT format = selectFormat(mImageArray[0]);
+ D3DFORMAT format = selectFormat(mImageArray[0].format);
HRESULT result = device->CreateTexture(mWidth, mHeight, 0, D3DUSAGE_RENDERTARGET, format, D3DPOOL_DEFAULT, &texture, NULL);
@@ -455,30 +495,23 @@
void Texture2D::updateTexture()
{
IDirect3DDevice9 *device = getDevice();
- D3DFORMAT format = selectFormat(mImageArray[0]);
-
- IDirect3DTexture9 *lockableTexture;
- HRESULT result = device->CreateTexture(mWidth, mHeight, 0, D3DUSAGE_DYNAMIC, format, D3DPOOL_SYSTEMMEM, &lockableTexture, NULL);
-
- if (result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY)
- {
- return error(GL_OUT_OF_MEMORY);
- }
int levelCount = mTexture->GetLevelCount();
for (int level = 0; level < levelCount; level++)
{
- D3DLOCKED_RECT lock = {0};
- lockableTexture->LockRect(level, &lock, NULL, 0);
+ if (mImageArray[level].dirty)
+ {
+ IDirect3DSurface9 *levelSurface = NULL;
+ mTexture->GetSurfaceLevel(level, &levelSurface);
- copyImage(lock, format, mImageArray[level]);
+ device->UpdateSurface(mImageArray[level].surface, NULL, levelSurface, NULL);
- lockableTexture->UnlockRect(level);
+ levelSurface->Release();
+
+ mImageArray[level].dirty = false;
+ }
}
-
- device->UpdateTexture(lockableTexture, mTexture);
- lockableTexture->Release();
}
// Returns the top-level texture surface as a render target
@@ -498,6 +531,18 @@
return mRenderTarget;
}
+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;
+}
+
TextureCubeMap::TextureCubeMap()
{
mTexture = NULL;
@@ -547,9 +592,41 @@
setImage(5, level, internalFormat, width, height, format, type, pixels);
}
+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 (mTexture != NULL)
+ {
+ IDirect3DSurface9 *destLevel = NULL;
+ mTexture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(face), level, &destLevel);
+
+ 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;
+
+ getDevice()->UpdateSurface(img->surface, &sourceRect, destLevel, &destPoint);
+
+ destLevel->Release();
+
+ img->dirty = false;
+ }
+}
+
void TextureCubeMap::subImage(GLenum face, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels)
{
Texture::subImage(xoffset, yoffset, width, height, format, type, pixels, &mImageArray[faceIndex(face)][level]);
+ commitRect(face, level, xoffset, yoffset, width, height);
}
// Tests for GL texture object completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.
@@ -618,7 +695,7 @@
IDirect3DBaseTexture9 *TextureCubeMap::createTexture()
{
IDirect3DDevice9 *device = getDevice();
- D3DFORMAT format = selectFormat(mImageArray[0][0]);
+ D3DFORMAT format = selectFormat(mImageArray[0][0].format);
IDirect3DCubeTexture9 *texture;
@@ -638,33 +715,26 @@
void TextureCubeMap::updateTexture()
{
IDirect3DDevice9 *device = getDevice();
- D3DFORMAT format = selectFormat(mImageArray[0][0]);
-
- IDirect3DCubeTexture9 *lockableTexture;
- HRESULT 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);
- }
-
- ASSERT(SUCCEEDED(result));
for (int face = 0; face < 6; face++)
{
- for (int level = 0; level < MAX_TEXTURE_LEVELS; level++)
+ for (int level = 0; level <= log2(mWidth); level++)
{
- D3DLOCKED_RECT lock = {0};
- lockableTexture->LockRect((D3DCUBEMAP_FACES)face, level, &lock, NULL, 0);
+ Image *img = &mImageArray[face][level];
- copyImage(lock, format, mImageArray[face][level]);
+ if (img->dirty)
+ {
+ IDirect3DSurface9 *levelSurface = NULL;
+ mTexture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(face), level, &levelSurface);
- lockableTexture->UnlockRect((D3DCUBEMAP_FACES)face, level);
+ device->UpdateSurface(img->surface, NULL, levelSurface, NULL);
+
+ levelSurface->Release();
+
+ img->dirty = false;
+ }
}
}
-
- device->UpdateTexture(lockableTexture, mTexture);
- lockableTexture->Release();
}
void TextureCubeMap::setImage(int face, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels)
@@ -689,4 +759,19 @@
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;
+}
+
}