Bug 735378 - Cache temporary composite surfaces - r=bgirard
--- a/gfx/layers/basic/BasicLayers.cpp
+++ b/gfx/layers/basic/BasicLayers.cpp
@@ -1076,16 +1076,40 @@ protected:
nsRefPtr<gfxASurface> mSurface;
nsRefPtr<mozilla::gl::GLContext> mGLContext;
mozilla::RefPtr<mozilla::gfx::DrawTarget> mDrawTarget;
PRUint32 mCanvasFramebuffer;
bool mGLBufferIsPremultiplied;
bool mNeedsYFlip;
+
+ nsRefPtr<gfxImageSurface> mCachedTempSurface;
+ gfxIntSize mCachedSize;
+ gfxImageFormat mCachedFormat;
+
+ gfxImageSurface* GetTempSurface(const gfxIntSize& aSize, const gfxImageFormat aFormat)
+ {
+ if (!mCachedTempSurface ||
+ aSize.width != mCachedSize.width ||
+ aSize.height != mCachedSize.height ||
+ aFormat != mCachedFormat)
+ {
+ mCachedTempSurface = new gfxImageSurface(aSize, aFormat);
+ mCachedSize = aSize;
+ mCachedFormat = aFormat;
+ }
+
+ return mCachedTempSurface;
+ }
+
+ void DiscardTempSurface()
+ {
+ mCachedTempSurface = nsnull;
+ }
};
void
BasicCanvasLayer::Initialize(const Data& aData)
{
NS_ASSERTION(mSurface == nsnull, "BasicCanvasLayer::Initialize called twice!");
if (aData.mSurface) {
@@ -1134,34 +1158,40 @@ BasicCanvasLayer::UpdateSurface(gfxASurf
"Destination surface must be ImageSurface type");
return;
}
// We need to read from the GLContext
mGLContext->MakeCurrent();
#if defined (MOZ_X11) && defined (MOZ_EGL_XRENDER_COMPOSITE)
- mGLContext->fFinish();
+ mGLContext->GuaranteeResolve();
gfxASurface* offscreenSurface = mGLContext->GetOffscreenPixmapSurface();
// XRender can only blend premuliplied alpha, so only allow xrender
// path if we have premultiplied alpha or opaque content.
if (offscreenSurface && (mGLBufferIsPremultiplied || (GetContentFlags() & CONTENT_OPAQUE))) {
mSurface = offscreenSurface;
mNeedsYFlip = false;
+ return;
}
- else
#endif
- {
- nsRefPtr<gfxImageSurface> isurf = aDestSurface ?
- static_cast<gfxImageSurface*>(aDestSurface) :
- new gfxImageSurface(gfxIntSize(mBounds.width, mBounds.height),
- (GetContentFlags() & CONTENT_OPAQUE)
- ? gfxASurface::ImageFormatRGB24
- : gfxASurface::ImageFormatARGB32);
+ gfxImageSurface* isurf = nsnull;
+ if (aDestSurface) {
+ DiscardTempSurface();
+ isurf = static_cast<gfxImageSurface*>(aDestSurface);
+ } else {
+ nsIntSize size(mBounds.width, mBounds.height);
+ gfxImageFormat format = (GetContentFlags() & CONTENT_OPAQUE)
+ ? gfxASurface::ImageFormatRGB24
+ : gfxASurface::ImageFormatARGB32;
+
+ isurf = GetTempSurface(size, format);
+ }
+
if (!isurf || isurf->CairoStatus() != 0) {
return;
}
NS_ASSERTION(isurf->Stride() == mBounds.width * 4, "gfxImageSurface stride isn't what we expect!");
PRUint32 currentFramebuffer = 0;
@@ -1189,17 +1219,16 @@ BasicCanvasLayer::UpdateSurface(gfxASurf
gfxUtils::PremultiplyImageSurface(isurf);
// stick our surface into mSurface, so that the Paint() path is the same
if (!aDestSurface) {
mSurface = isurf;
}
}
}
-}
void
BasicCanvasLayer::Paint(gfxContext* aContext)
{
if (IsHidden())
return;
UpdateSurface();
FireDidTransactionCallback();
--- a/gfx/layers/d3d10/CanvasLayerD3D10.cpp
+++ b/gfx/layers/d3d10/CanvasLayerD3D10.cpp
@@ -167,20 +167,23 @@ CanvasLayerD3D10::UpdateSurface()
HRESULT hr = mTexture->Map(0, D3D10_MAP_WRITE_DISCARD, 0, &map);
if (FAILED(hr)) {
NS_WARNING("Failed to map CanvasLayer texture.");
return;
}
+ const bool stridesMatch = map.RowPitch == mBounds.width * 4;
+
PRUint8 *destination;
- if (map.RowPitch != mBounds.width * 4) {
- destination = new PRUint8[mBounds.width * mBounds.height * 4];
+ if (!stridesMatch) {
+ destination = GetTempBlob(mBounds.width * mBounds.height * 4);
} else {
+ DiscardTempBlob();
destination = (PRUint8*)map.pData;
}
mGLContext->MakeCurrent();
PRUint32 currentFramebuffer = 0;
mGLContext->fGetIntegerv(LOCAL_GL_FRAMEBUFFER_BINDING, (GLint*)¤tFramebuffer);
@@ -199,23 +202,22 @@ CanvasLayerD3D10::UpdateSurface()
mBounds.width, mBounds.height,
tmpSurface);
tmpSurface = nsnull;
// Put back the previous framebuffer binding.
if (currentFramebuffer != mCanvasFramebuffer)
mGLContext->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, currentFramebuffer);
- if (map.RowPitch != mBounds.width * 4) {
+ if (!stridesMatch) {
for (int y = 0; y < mBounds.height; y++) {
memcpy((PRUint8*)map.pData + map.RowPitch * y,
destination + mBounds.width * 4 * y,
mBounds.width * 4);
}
- delete [] destination;
}
mTexture->Unmap(0);
} else if (mSurface) {
RECT r;
r.left = 0;
r.top = 0;
r.right = mBounds.width;
r.bottom = mBounds.height;
--- a/gfx/layers/d3d10/CanvasLayerD3D10.h
+++ b/gfx/layers/d3d10/CanvasLayerD3D10.h
@@ -82,13 +82,31 @@ private:
PRUint32 mCanvasFramebuffer;
bool mDataIsPremultiplied;
bool mNeedsYFlip;
bool mIsD2DTexture;
bool mUsingSharedTexture;
bool mHasAlpha;
+
+ nsAutoArrayPtr<PRUint8> mCachedTempBlob;
+ PRUint32 mCachedTempBlob_Size;
+
+ PRUint8* GetTempBlob(const PRUint32 aSize)
+ {
+ if (!mCachedTempBlob || aSize != mCachedTempBlob_Size) {
+ mCachedTempBlob = new PRUint8[aSize];
+ mCachedTempBlob_Size = aSize;
+ }
+
+ return mCachedTempBlob;
+ }
+
+ void DiscardTempBlob()
+ {
+ mCachedTempBlob = nsnull;
+ }
};
} /* layers */
} /* mozilla */
#endif /* GFX_CANVASLAYERD3D10_H */
--- a/gfx/layers/d3d9/CanvasLayerD3D9.cpp
+++ b/gfx/layers/d3d9/CanvasLayerD3D9.cpp
@@ -108,20 +108,23 @@ CanvasLayerD3D9::UpdateSurface()
LockTextureRectD3D9 textureLock(mTexture);
if (!textureLock.HasLock()) {
NS_WARNING("Failed to lock CanvasLayer texture.");
return;
}
D3DLOCKED_RECT r = textureLock.GetLockRect();
+ const bool stridesMatch = r.Pitch == mBounds.width * 4;
+
PRUint8 *destination;
- if (r.Pitch != mBounds.width * 4) {
- destination = new PRUint8[mBounds.width * mBounds.height * 4];
+ if (!stridesMatch) {
+ destination = GetTempBlob(mBounds.width * mBounds.height * 4);
} else {
+ DiscardTempBlob();
destination = (PRUint8*)r.pBits;
}
mGLContext->MakeCurrent();
PRUint32 currentFramebuffer = 0;
mGLContext->fGetIntegerv(LOCAL_GL_FRAMEBUFFER_BINDING, (GLint*)¤tFramebuffer);
@@ -140,23 +143,22 @@ CanvasLayerD3D9::UpdateSurface()
mBounds.width, mBounds.height,
tmpSurface);
tmpSurface = nsnull;
// Put back the previous framebuffer binding.
if (currentFramebuffer != mCanvasFramebuffer)
mGLContext->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, currentFramebuffer);
- if (r.Pitch != mBounds.width * 4) {
+ if (!stridesMatch) {
for (int y = 0; y < mBounds.height; y++) {
memcpy((PRUint8*)r.pBits + r.Pitch * y,
destination + mBounds.width * 4 * y,
mBounds.width * 4);
}
- delete [] destination;
}
} else {
RECT r;
r.left = mBounds.x;
r.top = mBounds.y;
r.right = mBounds.XMost();
r.bottom = mBounds.YMost();
--- a/gfx/layers/d3d9/CanvasLayerD3D9.h
+++ b/gfx/layers/d3d9/CanvasLayerD3D9.h
@@ -87,16 +87,34 @@ protected:
nsRefPtr<IDirect3DTexture9> mTexture;
RefPtr<gfx::DrawTarget> mDrawTarget;
PRUint32 mCanvasFramebuffer;
bool mDataIsPremultiplied;
bool mNeedsYFlip;
bool mHasAlpha;
+
+ nsAutoArrayPtr<PRUint8> mCachedTempBlob;
+ PRUint32 mCachedTempBlob_Size;
+
+ PRUint8* GetTempBlob(const PRUint32 aSize)
+ {
+ if (!mCachedTempBlob || aSize != mCachedTempBlob_Size) {
+ mCachedTempBlob = new PRUint8[aSize];
+ mCachedTempBlob_Size = aSize;
+ }
+
+ return mCachedTempBlob;
+ }
+
+ void DiscardTempBlob()
+ {
+ mCachedTempBlob = nsnull;
+ }
};
// NB: eventually we'll have separate shadow canvas2d and shadow
// canvas3d layers, but currently they look the same from the
// perspective of the compositor process
class ShadowCanvasLayerD3D9 : public ShadowCanvasLayer,
public LayerD3D9
{
--- a/gfx/layers/opengl/CanvasLayerOGL.cpp
+++ b/gfx/layers/opengl/CanvasLayerOGL.cpp
@@ -173,52 +173,56 @@ CanvasLayerOGL::UpdateSurface()
if (mPixmap) {
return;
}
#endif
if (mCanvasGLContext &&
mCanvasGLContext->GetContextType() == gl()->GetContextType())
{
+ DiscardTempSurface();
+
// Can texture share, just make sure it's resolved first
mCanvasGLContext->MakeCurrent();
mCanvasGLContext->GuaranteeResolve();
if (gl()->BindOffscreenNeedsTexture(mCanvasGLContext) &&
mTexture == 0)
{
mOGLManager->MakeCurrent();
MakeTexture();
}
} else {
nsRefPtr<gfxASurface> updatedAreaSurface;
+
if (mDrawTarget) {
// TODO: This is suboptimal - We should have direct handling for the surface types instead of
// going via a gfxASurface.
updatedAreaSurface = gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(mDrawTarget);
} else if (mCanvasSurface) {
updatedAreaSurface = mCanvasSurface;
} else if (mCanvasGLContext) {
+ gfxIntSize size(mBounds.width, mBounds.height);
nsRefPtr<gfxImageSurface> updatedAreaImageSurface =
- new gfxImageSurface(gfxIntSize(mBounds.width, mBounds.height),
- gfxASurface::ImageFormatARGB32);
+ GetTempSurface(size, gfxASurface::ImageFormatARGB32);
+
mCanvasGLContext->ReadPixelsIntoImageSurface(0, 0,
mBounds.width,
mBounds.height,
updatedAreaImageSurface);
+
updatedAreaSurface = updatedAreaImageSurface;
}
mOGLManager->MakeCurrent();
- mLayerProgram =
- gl()->UploadSurfaceToTexture(updatedAreaSurface,
- mBounds,
- mTexture,
- false,
- nsIntPoint(0, 0));
+ mLayerProgram = gl()->UploadSurfaceToTexture(updatedAreaSurface,
+ mBounds,
+ mTexture,
+ false,
+ nsIntPoint(0, 0));
}
}
void
CanvasLayerOGL::RenderLayer(int aPreviousDestination,
const nsIntPoint& aOffset)
{
UpdateSurface();
--- a/gfx/layers/opengl/CanvasLayerOGL.h
+++ b/gfx/layers/opengl/CanvasLayerOGL.h
@@ -89,16 +89,40 @@ protected:
GLuint mTexture;
bool mDelayedUpdates;
bool mGLBufferIsPremultiplied;
bool mNeedsYFlip;
#if defined(MOZ_WIDGET_GTK2) && !defined(MOZ_PLATFORM_MAEMO)
GLXPixmap mPixmap;
#endif
+
+ nsRefPtr<gfxImageSurface> mCachedTempSurface;
+ gfxIntSize mCachedSize;
+ gfxImageFormat mCachedFormat;
+
+ gfxImageSurface* GetTempSurface(const gfxIntSize& aSize, const gfxImageFormat aFormat)
+ {
+ if (!mCachedTempSurface ||
+ aSize.width != mCachedSize.width ||
+ aSize.height != mCachedSize.height ||
+ aFormat != mCachedFormat)
+ {
+ mCachedTempSurface = new gfxImageSurface(aSize, aFormat);
+ mCachedSize = aSize;
+ mCachedFormat = aFormat;
+ }
+
+ return mCachedTempSurface;
+ }
+
+ void DiscardTempSurface()
+ {
+ mCachedTempSurface = nsnull;
+ }
};
// NB: eventually we'll have separate shadow canvas2d and shadow
// canvas3d layers, but currently they look the same from the
// perspective of the compositor process
class ShadowCanvasLayerOGL : public ShadowCanvasLayer,
public LayerOGL
{