From f7284fe424c974ae1430b94a526b2c4b654407c6 Mon Sep 17 00:00:00 2001 From: Karl Tomlinson Date: Fri, 16 Jul 2010 09:08:09 +1200 Subject: [PATCH] b=573409 expire unused cached double-buffering back surfaces r=vlad sr=roc --- gfx/layers/basic/BasicLayers.cpp | 34 ++----- gfx/layers/basic/BasicLayers.h | 4 +- gfx/thebes/Makefile.in | 2 + gfx/thebes/gfxCachedTempSurface.cpp | 139 ++++++++++++++++++++++++++++ gfx/thebes/gfxCachedTempSurface.h | 90 ++++++++++++++++++ 5 files changed, 239 insertions(+), 30 deletions(-) create mode 100644 gfx/thebes/gfxCachedTempSurface.cpp create mode 100644 gfx/thebes/gfxCachedTempSurface.h diff --git a/gfx/layers/basic/BasicLayers.cpp b/gfx/layers/basic/BasicLayers.cpp index 0c1ab57fc01b..1c4cc63bf9da 100644 --- a/gfx/layers/basic/BasicLayers.cpp +++ b/gfx/layers/basic/BasicLayers.cpp @@ -750,32 +750,10 @@ BasicLayerManager::PushGroupWithCachedSurface(gfxContext *aTarget, gfxRect clip = aTarget->GetClipExtents(); clip.RoundOut(); - if (mCachedSurface) { - /* Verify the current buffer is valid for this purpose */ - if (mCachedSurface->GetContentType() != aContent) { - mCachedSurface = nsnull; - } else { - /* bufferClip should always be {0,0,width,height} of the buffer surface */ - if (clip.size.width > mCachedSurfaceSize.width || - clip.size.height > mCachedSurfaceSize.height) { - mCachedSurface = nsnull; - } - } - } - nsRefPtr ctx; - if (mCachedSurface) { - ctx = new gfxContext(mCachedSurface); - ctx->SetOperator(gfxContext::OPERATOR_CLEAR); - ctx->Paint(); - ctx->SetOperator(gfxContext::OPERATOR_OVER); - } else { - mCachedSurfaceSize = gfxIntSize(clip.size.width, clip.size.height); - mCachedSurface = currentSurf->CreateSimilarSurface(aContent, - mCachedSurfaceSize); - if (!mCachedSurface) - return nsnull; - ctx = new gfxContext(mCachedSurface); - } + nsRefPtr ctx = + mCachedSurface.Get(aContent, + gfxIntSize(clip.size.width, clip.size.height), + currentSurf); /* Align our buffer for the original surface */ ctx->Translate(-clip.pos); *aSavedOffset = clip.pos; @@ -787,13 +765,13 @@ void BasicLayerManager::PopGroupWithCachedSurface(gfxContext *aTarget, const gfxPoint& aSavedOffset) { - if (!mCachedSurface) + if (!mTarget) return; gfxContextMatrixAutoSaveRestore saveMatrix(aTarget); aTarget->IdentityMatrix(); - aTarget->SetSource(mCachedSurface, aSavedOffset); + aTarget->SetSource(mTarget->OriginalSurface(), aSavedOffset); aTarget->Paint(); } diff --git a/gfx/layers/basic/BasicLayers.h b/gfx/layers/basic/BasicLayers.h index 0a7bf54519c4..9976d1f92799 100644 --- a/gfx/layers/basic/BasicLayers.h +++ b/gfx/layers/basic/BasicLayers.h @@ -41,6 +41,7 @@ #include "Layers.h" #include "gfxContext.h" +#include "gfxCachedTempSurface.h" #include "nsAutoRef.h" #include "nsThreadUtils.h" @@ -133,8 +134,7 @@ private: nsRefPtr mTarget; // Cached surface for double buffering - nsRefPtr mCachedSurface; - gfxIntSize mCachedSurfaceSize; + gfxCachedTempSurface mCachedSurface; #ifdef DEBUG enum TransactionPhase { PHASE_NONE, PHASE_CONSTRUCTION, PHASE_DRAWING }; diff --git a/gfx/thebes/Makefile.in b/gfx/thebes/Makefile.in index e2be1f4af6e3..a30168c47ad1 100644 --- a/gfx/thebes/Makefile.in +++ b/gfx/thebes/Makefile.in @@ -16,6 +16,7 @@ EXPORTS = \ gfxASurface.h \ gfxAlphaRecovery.h \ gfxBlur.h \ + gfxCachedTempSurface.h \ gfxColor.h \ gfxContext.h \ gfxFont.h \ @@ -162,6 +163,7 @@ CPPSRCS = \ gfxASurface.cpp \ gfxAlphaRecovery.cpp \ gfxBlur.cpp \ + gfxCachedTempSurface.cpp \ gfxContext.cpp \ gfxImageSurface.cpp \ gfxFont.cpp \ diff --git a/gfx/thebes/gfxCachedTempSurface.cpp b/gfx/thebes/gfxCachedTempSurface.cpp new file mode 100644 index 000000000000..6847ca046d8f --- /dev/null +++ b/gfx/thebes/gfxCachedTempSurface.cpp @@ -0,0 +1,139 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Corporation code. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Karl Tomlinson + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "gfxCachedTempSurface.h" +#include "gfxContext.h" + +class CachedSurfaceExpirationTracker : + public nsExpirationTracker { + +public: + // With K = 2, this means that surfaces will be released when they are not + // used for 1-2 seconds. + enum { TIMEOUT_MS = 1000 }; + CachedSurfaceExpirationTracker() + : nsExpirationTracker(TIMEOUT_MS) {} + + ~CachedSurfaceExpirationTracker() { + AgeAllGenerations(); + } + + virtual void NotifyExpired(gfxCachedTempSurface* aSurface) { + RemoveObject(aSurface); + aSurface->Expire(); + } + + static void MarkSurfaceUsed(gfxCachedTempSurface* aSurface) + { + if (aSurface->GetExpirationState()->IsTracked()) { + sExpirationTracker->MarkUsed(aSurface); + return; + } + + if (!sExpirationTracker) { + sExpirationTracker = new CachedSurfaceExpirationTracker(); + } + sExpirationTracker->AddObject(aSurface); + } + + static void RemoveSurface(gfxCachedTempSurface* aSurface) + { + if (!sExpirationTracker) + return; + + if (aSurface->GetExpirationState()->IsTracked()) { + sExpirationTracker->RemoveObject(aSurface); + } + if (sExpirationTracker->IsEmpty()) { + delete sExpirationTracker; + sExpirationTracker = nsnull; + } + } + +private: + static CachedSurfaceExpirationTracker* sExpirationTracker; +}; + +CachedSurfaceExpirationTracker* +CachedSurfaceExpirationTracker::sExpirationTracker = nsnull; + +gfxCachedTempSurface::~gfxCachedTempSurface() +{ + CachedSurfaceExpirationTracker::RemoveSurface(this); +} + +already_AddRefed +gfxCachedTempSurface::Get(gfxASurface::gfxContentType aContentType, + const gfxIntSize& aSize, + gfxASurface* aSimilarTo) +{ + if (mSurface) { + /* Verify the current buffer is valid for this purpose */ + if (mSize.width < aSize.width || mSize.height < aSize.height + || (mSurface->GetContentType() != aContentType + && mSurface->AreSimilarSurfacesSensitiveToContentType())) { + mSurface = nsnull; + } else { + NS_ASSERTION(mType == aSimilarTo->GetType(), + "Unexpected surface type change"); + } + } + + PRBool cleared = PR_FALSE; + if (!mSurface) { + mSize = aSize; + mSurface = aSimilarTo->CreateSimilarSurface(aContentType, aSize); + if (!mSurface) + return nsnull; + + cleared = PR_TRUE; +#ifdef DEBUG + mType = aSimilarTo->GetType(); +#endif + } + + nsRefPtr ctx = new gfxContext(mSurface); + if (!cleared && aContentType != gfxASurface::CONTENT_COLOR) { + ctx->SetOperator(gfxContext::OPERATOR_CLEAR); + ctx->Paint(); + ctx->SetOperator(gfxContext::OPERATOR_OVER); + } + + CachedSurfaceExpirationTracker::MarkSurfaceUsed(this); + + return ctx.forget(); +} diff --git a/gfx/thebes/gfxCachedTempSurface.h b/gfx/thebes/gfxCachedTempSurface.h new file mode 100644 index 000000000000..b3dbce37f397 --- /dev/null +++ b/gfx/thebes/gfxCachedTempSurface.h @@ -0,0 +1,90 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Corporation code. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Karl Tomlinson + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef GFX_CACHED_TEMP_SURFACE_H +#define GFX_CACHED_TEMP_SURFACE_H + +#include "gfxASurface.h" +#include "nsExpirationTracker.h" + +class gfxContext; + +/** + * This class can be used to cache double-buffering back surfaces. + * + * Large resource allocations may have an overhead that can be avoided by + * caching. Caching also alows the system to use history in deciding whether + * to manage the surfaces in video or system memory. + * + * However, because we don't want to set aside megabytes of unused resources + * unncessarily, these surfaces are released on a timer. + */ + +class THEBES_API gfxCachedTempSurface { +public: + /** + * Returns a context for a surface that can be efficiently copied to + * |aSimilarTo|. + * + * When |aContentType| has an alpha component, the surface will be cleared. + * For opaque surfaces, the initial surface contents are undefined. + * When |aContentType| differs in different invocations this is handled + * appropriately, creating a new surface if necessary. + * + * |aSimilarTo| should be of the same gfxSurfaceType in each invocation. + * Because the cached surface may have been created during a previous + * invocation, this will not be efficient if the new |aSimilarTo| has a + * different format. + */ + already_AddRefed Get(gfxASurface::gfxContentType aContentType, + const gfxIntSize& aSize, + gfxASurface* aSimilarTo); + + void Expire() { mSurface = nsnull; } + nsExpirationState* GetExpirationState() { return &mExpirationState; } + ~gfxCachedTempSurface(); + +private: + nsRefPtr mSurface; + gfxIntSize mSize; + nsExpirationState mExpirationState; +#ifdef DEBUG + gfxASurface::gfxSurfaceType mType; +#endif +}; + +#endif /* GFX_CACHED_TEMP_SURFACE_H */