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
This commit is contained in:
daniel@transgaming.com 2010-03-21 04:31:03 +00:00
Родитель bbb6cd0c6d
Коммит 842f7a4ebf
2 изменённых файлов: 180 добавлений и 85 удалений

Просмотреть файл

@ -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 @@ Texture::Texture() : Colorbuffer(0)
mWrapS = GL_REPEAT;
mWrapT = GL_REPEAT;
mDirtyImageData = true;
mDirtyMetaData = true;
}
@ -115,31 +125,8 @@ GLenum Texture::getWrapT() const
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 @@ void Texture::loadImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei
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::loadImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei
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 @@ 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);
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 @@ IDirect3DBaseTexture9 *Texture::getTexture()
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::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)
{
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 @@ IDirect3DBaseTexture9 *Texture2D::createTexture()
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 @@ IDirect3DBaseTexture9 *Texture2D::createTexture()
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 @@ IDirect3DSurface9 *Texture2D::getRenderTarget()
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 @@ void TextureCubeMap::setImageNegZ(GLint level, GLenum internalFormat, GLsizei wi
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 @@ bool TextureCubeMap::isComplete() const
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 @@ IDirect3DBaseTexture9 *TextureCubeMap::createTexture()
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 @@ unsigned int TextureCubeMap::faceIndex(GLenum face)
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
// Helper structure representing a single image layer
struct Image
{
Image();
~Image();
GLsizei width;
GLsizei height;
GLenum format;
std::vector<unsigned char> pixels;
bool dirty;
IDirect3DSurface9 *surface;
};
void copyImage(const D3DLOCKED_RECT &lock, D3DFORMAT format, const Image &image);
static D3DFORMAT selectFormat(const Image &image);
static D3DFORMAT selectFormat(GLenum format);
static int pixelSize(GLenum format, GLenum type);
int imagePitch(const Image& img) const;
@ -80,6 +83,8 @@ class Texture : public Colorbuffer
virtual IDirect3DBaseTexture9 *createTexture() = 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.
private:
@ -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.
bool mDirtyImageData;
void loadImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type,
const void *input, std::size_t outputPitch, void *output) const;
};
@ -114,6 +117,10 @@ class Texture2D : public Texture
virtual IDirect3DBaseTexture9 *createTexture();
virtual void updateTexture();
virtual bool dirtyImageData() const;
void commitRect(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height);
Image mImageArray[MAX_TEXTURE_LEVELS];
IDirect3DTexture9 *mTexture;
@ -145,9 +152,12 @@ class TextureCubeMap : public Texture
virtual IDirect3DBaseTexture9 *createTexture();
virtual void updateTexture();
virtual bool dirtyImageData() const;
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 commitRect(GLenum faceTarget, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height);
Image mImageArray[6][MAX_TEXTURE_LEVELS];