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
parent bbb6cd0c
...@@ -18,6 +18,17 @@ ...@@ -18,6 +18,17 @@
namespace gl namespace gl
{ {
Texture::Image::Image()
: dirty(false), surface(NULL)
{
}
Texture::Image::~Image()
{
if (surface) surface->Release();
}
Texture::Texture() : Colorbuffer(0) Texture::Texture() : Colorbuffer(0)
{ {
mMinFilter = GL_NEAREST_MIPMAP_LINEAR; mMinFilter = GL_NEAREST_MIPMAP_LINEAR;
...@@ -25,7 +36,6 @@ Texture::Texture() : Colorbuffer(0) ...@@ -25,7 +36,6 @@ Texture::Texture() : Colorbuffer(0)
mWrapS = GL_REPEAT; mWrapS = GL_REPEAT;
mWrapT = GL_REPEAT; mWrapT = GL_REPEAT;
mDirtyImageData = true;
mDirtyMetaData = true; mDirtyMetaData = true;
} }
...@@ -115,31 +125,8 @@ GLenum Texture::getWrapT() const ...@@ -115,31 +125,8 @@ GLenum Texture::getWrapT() const
return mWrapT; 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 // Selects an internal Direct3D 9 format for storing an Image
D3DFORMAT Texture::selectFormat(const Image &image) D3DFORMAT Texture::selectFormat(GLenum format)
{ {
return D3DFMT_A8R8G8B8; return D3DFMT_A8R8G8B8;
} }
...@@ -269,9 +256,9 @@ void Texture::loadImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei ...@@ -269,9 +256,9 @@ void Texture::loadImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei
unsigned short rgba = source16[x]; unsigned short rgba = source16[x];
a = (rgba & 0x0001) ? 0xFF : 0; 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); g = ((rgba & 0x07C0) >> 3) | ((rgba & 0x07C0) >> 8);
r = ((rgba & 0xF800) >> 8) | ((rgba & 0x07C0) >> 13); r = ((rgba & 0xF800) >> 8) | ((rgba & 0xF800) >> 13);
} }
break; break;
...@@ -291,22 +278,38 @@ void Texture::loadImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei ...@@ -291,22 +278,38 @@ void Texture::loadImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei
void Texture::setImage(GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels, Image *img) 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->width = width;
img->height = height; img->height = height;
img->format = format; img->format = format;
size_t imageSize = imagePitch(*img) * img->height; if (pixels != NULL)
{
D3DLOCKED_RECT locked;
HRESULT result = newSurface->LockRect(&locked, NULL, 0);
std::vector<unsigned char> storage(imageSize); ASSERT(SUCCEEDED(result));
if (pixels) if (SUCCEEDED(result))
{ {
loadImageData(0, 0, width, height, format, type, pixels, imagePitch(*img), &storage[0]); loadImageData(0, 0, width, height, format, type, pixels, locked.Pitch, locked.pBits);
} newSurface->UnlockRect();
}
img->pixels.swap(storage); img->dirty = true;
}
mDirtyImageData = true;
mDirtyMetaData = true; mDirtyMetaData = true;
} }
...@@ -314,9 +317,18 @@ void Texture::subImage(GLint xoffset, GLint yoffset, GLsizei width, GLsizei heig ...@@ -314,9 +317,18 @@ void Texture::subImage(GLint xoffset, GLint yoffset, GLsizei width, GLsizei heig
{ {
if (width + xoffset > img->width || height + yoffset > img->height) return error(GL_INVALID_VALUE); 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() IDirect3DBaseTexture9 *Texture::getTexture()
...@@ -328,18 +340,16 @@ IDirect3DBaseTexture9 *Texture::getTexture() ...@@ -328,18 +340,16 @@ IDirect3DBaseTexture9 *Texture::getTexture()
if (mDirtyMetaData) if (mDirtyMetaData)
{ {
ASSERT(mDirtyImageData);
mBaseTexture = createTexture(); mBaseTexture = createTexture();
} }
if (mDirtyImageData) if (mDirtyMetaData || dirtyImageData())
{ {
updateTexture(); updateTexture();
} }
mDirtyMetaData = false; mDirtyMetaData = false;
mDirtyImageData = false; ASSERT(!dirtyImageData());
return mBaseTexture; return mBaseTexture;
} }
...@@ -374,9 +384,39 @@ void Texture2D::setImage(GLint level, GLenum internalFormat, GLsizei width, GLsi ...@@ -374,9 +384,39 @@ void Texture2D::setImage(GLint level, GLenum internalFormat, GLsizei width, GLsi
} }
} }
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) 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]); 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. // Tests for GL texture object completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.
...@@ -438,7 +478,7 @@ IDirect3DBaseTexture9 *Texture2D::createTexture() ...@@ -438,7 +478,7 @@ IDirect3DBaseTexture9 *Texture2D::createTexture()
IDirect3DTexture9 *texture; IDirect3DTexture9 *texture;
IDirect3DDevice9 *device = getDevice(); 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); HRESULT result = device->CreateTexture(mWidth, mHeight, 0, D3DUSAGE_RENDERTARGET, format, D3DPOOL_DEFAULT, &texture, NULL);
...@@ -455,30 +495,23 @@ IDirect3DBaseTexture9 *Texture2D::createTexture() ...@@ -455,30 +495,23 @@ IDirect3DBaseTexture9 *Texture2D::createTexture()
void Texture2D::updateTexture() void Texture2D::updateTexture()
{ {
IDirect3DDevice9 *device = getDevice(); 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(); int levelCount = mTexture->GetLevelCount();
for (int level = 0; level < levelCount; level++) for (int level = 0; level < levelCount; level++)
{ {
D3DLOCKED_RECT lock = {0}; if (mImageArray[level].dirty)
lockableTexture->LockRect(level, &lock, NULL, 0); {
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();
}
device->UpdateTexture(lockableTexture, mTexture); mImageArray[level].dirty = false;
lockableTexture->Release(); }
}
} }
// Returns the top-level texture surface as a render target // Returns the top-level texture surface as a render target
...@@ -498,6 +531,18 @@ IDirect3DSurface9 *Texture2D::getRenderTarget() ...@@ -498,6 +531,18 @@ IDirect3DSurface9 *Texture2D::getRenderTarget()
return mRenderTarget; 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() TextureCubeMap::TextureCubeMap()
{ {
mTexture = NULL; mTexture = NULL;
...@@ -547,9 +592,41 @@ void TextureCubeMap::setImageNegZ(GLint level, GLenum internalFormat, GLsizei wi ...@@ -547,9 +592,41 @@ void TextureCubeMap::setImageNegZ(GLint level, GLenum internalFormat, GLsizei wi
setImage(5, level, internalFormat, width, height, format, type, pixels); 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) 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]); 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. // Tests for GL texture object completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.
...@@ -618,7 +695,7 @@ bool TextureCubeMap::isComplete() const ...@@ -618,7 +695,7 @@ bool TextureCubeMap::isComplete() const
IDirect3DBaseTexture9 *TextureCubeMap::createTexture() IDirect3DBaseTexture9 *TextureCubeMap::createTexture()
{ {
IDirect3DDevice9 *device = getDevice(); IDirect3DDevice9 *device = getDevice();
D3DFORMAT format = selectFormat(mImageArray[0][0]); D3DFORMAT format = selectFormat(mImageArray[0][0].format);
IDirect3DCubeTexture9 *texture; IDirect3DCubeTexture9 *texture;
...@@ -638,33 +715,26 @@ IDirect3DBaseTexture9 *TextureCubeMap::createTexture() ...@@ -638,33 +715,26 @@ IDirect3DBaseTexture9 *TextureCubeMap::createTexture()
void TextureCubeMap::updateTexture() void TextureCubeMap::updateTexture()
{ {
IDirect3DDevice9 *device = getDevice(); 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 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}; Image *img = &mImageArray[face][level];
lockableTexture->LockRect((D3DCUBEMAP_FACES)face, level, &lock, NULL, 0);
if (img->dirty)
{
IDirect3DSurface9 *levelSurface = NULL;
mTexture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(face), level, &levelSurface);
copyImage(lock, format, mImageArray[face][level]); device->UpdateSurface(img->surface, NULL, levelSurface, NULL);
lockableTexture->UnlockRect((D3DCUBEMAP_FACES)face, level); 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) void TextureCubeMap::setImage(int face, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels)
...@@ -689,4 +759,19 @@ unsigned int TextureCubeMap::faceIndex(GLenum face) ...@@ -689,4 +759,19 @@ unsigned int TextureCubeMap::faceIndex(GLenum face)
return face - GL_TEXTURE_CUBE_MAP_POSITIVE_X; 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;
}
} }
...@@ -55,16 +55,19 @@ class Texture : public Colorbuffer ...@@ -55,16 +55,19 @@ class Texture : public Colorbuffer
// Helper structure representing a single image layer // Helper structure representing a single image layer
struct Image struct Image
{ {
Image();
~Image();
GLsizei width; GLsizei width;
GLsizei height; GLsizei height;
GLenum format; GLenum format;
std::vector<unsigned char> pixels; bool dirty;
};
void copyImage(const D3DLOCKED_RECT &lock, D3DFORMAT format, const Image &image); IDirect3DSurface9 *surface;
};
static D3DFORMAT selectFormat(const Image &image); static D3DFORMAT selectFormat(GLenum format);
static int pixelSize(GLenum format, GLenum type); static int pixelSize(GLenum format, GLenum type);
int imagePitch(const Image& img) const; int imagePitch(const Image& img) const;
...@@ -80,6 +83,8 @@ class Texture : public Colorbuffer ...@@ -80,6 +83,8 @@ class Texture : public Colorbuffer
virtual IDirect3DBaseTexture9 *createTexture() = 0; virtual IDirect3DBaseTexture9 *createTexture() = 0;
virtual void updateTexture() = 0; virtual void updateTexture() = 0;
virtual bool dirtyImageData() const = 0;
bool mDirtyMetaData; // FIXME: would be private but getRenderTarget is still implemented through the derived classes and they need it. bool mDirtyMetaData; // FIXME: would be private but getRenderTarget is still implemented through the derived classes and they need it.
private: private:
...@@ -87,8 +92,6 @@ class Texture : public Colorbuffer ...@@ -87,8 +92,6 @@ class Texture : public Colorbuffer
IDirect3DBaseTexture9 *mBaseTexture; // This is a weak pointer. The derived class is assumed to own a strong pointer. IDirect3DBaseTexture9 *mBaseTexture; // This is a weak pointer. The derived class is assumed to own a strong pointer.
bool mDirtyImageData;
void loadImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, void loadImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type,
const void *input, std::size_t outputPitch, void *output) const; const void *input, std::size_t outputPitch, void *output) const;
}; };
...@@ -114,6 +117,10 @@ class Texture2D : public Texture ...@@ -114,6 +117,10 @@ class Texture2D : public Texture
virtual IDirect3DBaseTexture9 *createTexture(); virtual IDirect3DBaseTexture9 *createTexture();
virtual void updateTexture(); virtual void updateTexture();
virtual bool dirtyImageData() const;
void commitRect(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height);
Image mImageArray[MAX_TEXTURE_LEVELS]; Image mImageArray[MAX_TEXTURE_LEVELS];
IDirect3DTexture9 *mTexture; IDirect3DTexture9 *mTexture;
...@@ -145,9 +152,12 @@ class TextureCubeMap : public Texture ...@@ -145,9 +152,12 @@ class TextureCubeMap : public Texture
virtual IDirect3DBaseTexture9 *createTexture(); virtual IDirect3DBaseTexture9 *createTexture();
virtual void updateTexture(); virtual void updateTexture();
virtual bool dirtyImageData() const;
static unsigned int faceIndex(GLenum face); static unsigned int faceIndex(GLenum face);
void setImage(int face, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); void setImage(int face, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels);
void commitRect(GLenum faceTarget, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height);
Image mImageArray[6][MAX_TEXTURE_LEVELS]; Image mImageArray[6][MAX_TEXTURE_LEVELS];
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment