зеркало из https://github.com/AvaloniaUI/angle.git
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:
Родитель
bbb6cd0c6d
Коммит
842f7a4ebf
|
@ -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];
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче