Bug 1054079 (Part 3) - Store imgFrame objects, not SourceSurface objects, in the SurfaceCache. r=dholbert

This commit is contained in:
Seth Fowler 2014-09-14 15:22:45 -07:00
parent bb0bbb475a
commit 6cbe3de817
4 changed files with 82 additions and 69 deletions

View File

@ -12,14 +12,15 @@
#include <algorithm>
#include "mozilla/Attributes.h" // for MOZ_THIS_IN_INITIALIZER_LIST
#include "mozilla/DebugOnly.h"
#include "mozilla/Move.h"
#include "mozilla/Preferences.h"
#include "mozilla/RefPtr.h"
#include "mozilla/StaticPtr.h"
#include "nsIMemoryReporter.h"
#include "gfx2DGlue.h"
#include "gfxPattern.h" // Workaround for flaw in bug 921753 part 2.
#include "gfxDrawable.h"
#include "gfxPlatform.h"
#include "imgFrame.h"
#include "nsAutoPtr.h"
#include "nsExpirationTracker.h"
#include "nsHashKeys.h"
@ -117,7 +118,7 @@ class CachedSurface
public:
NS_INLINE_DECL_REFCOUNTING(CachedSurface)
CachedSurface(SourceSurface* aSurface,
CachedSurface(imgFrame* aSurface,
const IntSize aTargetSize,
const Cost aCost,
const ImageKey aImageKey,
@ -132,11 +133,9 @@ public:
MOZ_ASSERT(mImageKey, "Must have a valid image key");
}
already_AddRefed<gfxDrawable> Drawable() const
DrawableFrameRef DrawableRef() const
{
nsRefPtr<gfxDrawable> drawable =
new gfxSurfaceDrawable(mSurface, ThebesIntSize(mTargetSize));
return drawable.forget();
return mSurface->DrawableRef();
}
ImageKey GetImageKey() const { return mImageKey; }
@ -145,12 +144,12 @@ public:
nsExpirationState* GetExpirationState() { return &mExpirationState; }
private:
nsExpirationState mExpirationState;
nsRefPtr<SourceSurface> mSurface;
const IntSize mTargetSize;
const Cost mCost;
const ImageKey mImageKey;
const SurfaceKey mSurfaceKey;
nsExpirationState mExpirationState;
nsRefPtr<imgFrame> mSurface;
const IntSize mTargetSize;
const Cost mCost;
const ImageKey mImageKey;
const SurfaceKey mSurfaceKey;
};
/*
@ -240,14 +239,14 @@ public:
RegisterWeakMemoryReporter(this);
}
void Insert(SourceSurface* aSurface,
void Insert(imgFrame* aSurface,
IntSize aTargetSize,
const Cost aCost,
const ImageKey aImageKey,
const SurfaceKey& aSurfaceKey)
{
MOZ_ASSERT(!Lookup(aImageKey, aSurfaceKey).take(),
"Inserting a duplicate drawable into the SurfaceCache");
MOZ_ASSERT(!Lookup(aImageKey, aSurfaceKey),
"Inserting a duplicate surface into the SurfaceCache");
// If this is bigger than the maximum cache size, refuse to cache it.
if (!CanHold(aCost))
@ -317,19 +316,27 @@ public:
MOZ_ASSERT(mAvailableCost <= mMaxCost, "More available cost than we started with");
}
already_AddRefed<gfxDrawable> Lookup(const ImageKey aImageKey,
const SurfaceKey& aSurfaceKey)
DrawableFrameRef Lookup(const ImageKey aImageKey,
const SurfaceKey& aSurfaceKey)
{
nsRefPtr<ImageSurfaceCache> cache = GetImageCache(aImageKey);
if (!cache)
return nullptr; // No cached surfaces for this image.
return DrawableFrameRef(); // No cached surfaces for this image.
nsRefPtr<CachedSurface> surface = cache->Lookup(aSurfaceKey);
if (!surface)
return nullptr; // Lookup in the per-image cache missed.
return DrawableFrameRef(); // Lookup in the per-image cache missed.
DrawableFrameRef ref = surface->DrawableRef();
if (!ref) {
// The surface was released by the operating system. Remove the cache
// entry as well.
Remove(surface);
return DrawableFrameRef();
}
mExpirationTracker.MarkUsed(surface);
return surface->Drawable();
return ref;
}
bool CanHold(const Cost aCost) const
@ -497,7 +504,7 @@ SurfaceCache::Shutdown()
sInstance = nullptr;
}
/* static */ already_AddRefed<gfxDrawable>
/* static */ DrawableFrameRef
SurfaceCache::Lookup(const ImageKey aImageKey,
const SurfaceKey& aSurfaceKey)
{
@ -508,7 +515,7 @@ SurfaceCache::Lookup(const ImageKey aImageKey,
}
/* static */ void
SurfaceCache::Insert(SourceSurface* aSurface,
SurfaceCache::Insert(imgFrame* aSurface,
const ImageKey aImageKey,
const SurfaceKey& aSurfaceKey)
{

View File

@ -4,7 +4,8 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* SurfaceCache is a service for caching temporary surfaces in imagelib.
* SurfaceCache is a service for caching temporary surfaces and decoded image
* data in imagelib.
*/
#ifndef MOZILLA_IMAGELIB_SURFACECACHE_H_
@ -15,19 +16,15 @@
#include "gfxPoint.h" // for gfxSize
#include "nsCOMPtr.h" // for already_AddRefed
#include "mozilla/gfx/Point.h" // for mozilla::gfx::IntSize
#include "mozilla/gfx/2D.h" // for SourceSurface
#include "SVGImageContext.h" // for SVGImageContext
class gfxDrawable;
namespace mozilla {
namespace gfx {
class DrawTarget;
} // namespace gfx
namespace image {
class DrawableFrameRef;
class Image;
class imgFrame;
/*
* ImageKey contains the information we need to look up all cached surfaces for
@ -90,6 +87,11 @@ private:
* surfaces. Surfaces expire from the cache automatically if they go too long
* without being accessed.
*
* SurfaceCache does not hold surfaces directly; instead, it holds imgFrame
* objects, which hold surfaces but also layer on additional features specific
* to imagelib's needs like animation, padding support, and transparent support
* for volatile buffers.
*
* SurfaceCache is not thread-safe; it should only be accessed from the main
* thread.
*/
@ -108,27 +110,33 @@ struct SurfaceCache
static void Shutdown();
/*
* Look up a surface in the cache.
* Look up the imgFrame containing a surface in the cache and returns a
* drawable reference to that imgFrame.
*
* If the imgFrame was found in the cache, but had stored its surface in a
* volatile buffer which was discarded by the OS, then it is automatically
* removed from the cache and an empty DrawableFrameRef is returned.
*
* @param aImageKey Key data identifying which image the surface belongs to.
* @param aSurfaceKey Key data which uniquely identifies the requested surface.
*
* @return the requested surface, or nullptr if not found.
* @return a DrawableFrameRef to the imgFrame wrapping the requested surface,
* or an empty DrawableFrameRef if not found.
*/
static already_AddRefed<gfxDrawable> Lookup(const ImageKey aImageKey,
const SurfaceKey& aSurfaceKey);
static DrawableFrameRef Lookup(const ImageKey aImageKey,
const SurfaceKey& aSurfaceKey);
/*
* Insert a surface into the cache. It is an error to call this function
* without first calling Lookup to verify that the surface is not already in
* the cache.
*
* @param aTarget The new surface (in the form of a DrawTarget) to insert
* into the cache.
* @param aTarget The new surface (wrapped in an imgFrame) to insert into
* the cache.
* @param aImageKey Key data identifying which image the surface belongs to.
* @param aSurfaceKey Key data which uniquely identifies the requested surface.
*/
static void Insert(gfx::SourceSurface* aSurface,
static void Insert(imgFrame* aSurface,
const ImageKey aImageKey,
const SurfaceKey& aSurfaceKey);

View File

@ -11,6 +11,7 @@
#include "gfxPlatform.h"
#include "gfxUtils.h"
#include "imgDecoderObserver.h"
#include "imgFrame.h"
#include "mozilla/AutoRestore.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/dom/SVGSVGElement.h"
@ -842,27 +843,30 @@ VectorImage::Draw(gfxContext* aContext,
aSVGContext, animTime, aFlags);
if (aFlags & FLAG_BYPASS_SURFACE_CACHE) {
CreateDrawableAndShow(params);
CreateSurfaceAndShow(params);
return NS_OK;
}
nsRefPtr<gfxDrawable> drawable =
DrawableFrameRef frameRef =
SurfaceCache::Lookup(ImageKey(this),
SurfaceKey(params.size, aSVGContext,
animTime, aFlags));
// Draw.
if (drawable) {
Show(drawable, params);
if (frameRef) {
RefPtr<SourceSurface> surface = frameRef->GetSurface();
nsRefPtr<gfxDrawable> svgDrawable =
new gfxSurfaceDrawable(surface, ThebesIntSize(frameRef->GetSize()));
Show(svgDrawable, params);
} else {
CreateDrawableAndShow(params);
CreateSurfaceAndShow(params);
}
return NS_OK;
}
void
VectorImage::CreateDrawableAndShow(const SVGDrawingParameters& aParams)
VectorImage::CreateSurfaceAndShow(const SVGDrawingParameters& aParams)
{
mSVGDocumentWrapper->UpdateViewportBounds(aParams.viewportSize);
mSVGDocumentWrapper->FlushImageTransformInvalidation();
@ -885,38 +889,32 @@ VectorImage::CreateDrawableAndShow(const SVGDrawingParameters& aParams)
if (bypassCache)
return Show(svgDrawable, aParams);
// Try to create an offscreen surface.
RefPtr<gfx::DrawTarget> target =
gfxPlatform::GetPlatform()->
CreateOffscreenContentDrawTarget(aParams.size,
gfx::SurfaceFormat::B8G8R8A8);
// Try to create an imgFrame, initializing the surface it contains by drawing
// our gfxDrawable into it. (We use FILTER_NEAREST since we never scale here.)
nsRefPtr<imgFrame> frame = new imgFrame;
nsresult rv =
frame->InitWithDrawable(svgDrawable, ThebesIntSize(aParams.size),
SurfaceFormat::B8G8R8A8,
GraphicsFilter::FILTER_NEAREST, aParams.flags);
// If we couldn't create the draw target, it was probably because it would end
// If we couldn't create the frame, it was probably because it would end
// up way too big. Generally it also wouldn't fit in the cache, but the prefs
// could be set such that the cache isn't the limiting factor.
if (!target)
if (NS_FAILED(rv))
return Show(svgDrawable, aParams);
nsRefPtr<gfxContext> ctx = new gfxContext(target);
// Take a strong reference to the frame's surface and make sure it hasn't
// already been purged by the operating system.
RefPtr<SourceSurface> surface = frame->GetSurface();
if (!surface)
return Show(svgDrawable, aParams);
// Actually draw. (We use FILTER_NEAREST since we never scale here.)
nsIntRect imageRect(ThebesIntRect(aParams.imageRect));
gfxUtils::DrawPixelSnapped(ctx, svgDrawable,
ThebesIntSize(aParams.size),
ImageRegion::Create(imageRect),
SurfaceFormat::B8G8R8A8,
GraphicsFilter::FILTER_NEAREST, aParams.flags);
RefPtr<SourceSurface> surface = target->Snapshot();
// Attempt to cache the resulting surface.
SurfaceCache::Insert(surface, ImageKey(this),
// Attempt to cache the frame.
SurfaceCache::Insert(frame, ImageKey(this),
SurfaceKey(aParams.size, aParams.svgContext,
aParams.animationTime, aParams.flags));
// Draw. Note that if SurfaceCache::Insert failed for whatever reason,
// then |target| is all that is keeping the pixel data alive, so we have
// to draw before returning from this function.
// Draw.
nsRefPtr<gfxDrawable> drawable =
new gfxSurfaceDrawable(surface, ThebesIntSize(aParams.size));
Show(drawable, aParams);

View File

@ -86,7 +86,7 @@ protected:
virtual nsresult StopAnimation();
virtual bool ShouldAnimate();
void CreateDrawableAndShow(const SVGDrawingParameters& aParams);
void CreateSurfaceAndShow(const SVGDrawingParameters& aParams);
void Show(gfxDrawable* aDrawable, const SVGDrawingParameters& aParams);
private: