image/src/SurfaceCache.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/image/src/SurfaceCache.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,545 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +/**
    1.10 + * SurfaceCache is a service for caching temporary surfaces in imagelib.
    1.11 + */
    1.12 +
    1.13 +#include "SurfaceCache.h"
    1.14 +
    1.15 +#include <algorithm>
    1.16 +#include "mozilla/Attributes.h"  // for MOZ_THIS_IN_INITIALIZER_LIST
    1.17 +#include "mozilla/DebugOnly.h"
    1.18 +#include "mozilla/Preferences.h"
    1.19 +#include "mozilla/RefPtr.h"
    1.20 +#include "mozilla/StaticPtr.h"
    1.21 +#include "nsIMemoryReporter.h"
    1.22 +#include "gfx2DGlue.h"
    1.23 +#include "gfxASurface.h"
    1.24 +#include "gfxPattern.h"  // Workaround for flaw in bug 921753 part 2.
    1.25 +#include "gfxDrawable.h"
    1.26 +#include "gfxPlatform.h"
    1.27 +#include "nsAutoPtr.h"
    1.28 +#include "nsExpirationTracker.h"
    1.29 +#include "nsHashKeys.h"
    1.30 +#include "nsRefPtrHashtable.h"
    1.31 +#include "nsSize.h"
    1.32 +#include "nsTArray.h"
    1.33 +#include "prsystem.h"
    1.34 +#include "SVGImageContext.h"
    1.35 +
    1.36 +using std::max;
    1.37 +using std::min;
    1.38 +using namespace mozilla::gfx;
    1.39 +
    1.40 +namespace mozilla {
    1.41 +namespace image {
    1.42 +
    1.43 +class CachedSurface;
    1.44 +class SurfaceCacheImpl;
    1.45 +
    1.46 +///////////////////////////////////////////////////////////////////////////////
    1.47 +// Static Data
    1.48 +///////////////////////////////////////////////////////////////////////////////
    1.49 +
    1.50 +// The single surface cache instance.
    1.51 +static StaticRefPtr<SurfaceCacheImpl> sInstance;
    1.52 +
    1.53 +
    1.54 +///////////////////////////////////////////////////////////////////////////////
    1.55 +// SurfaceCache Implementation
    1.56 +///////////////////////////////////////////////////////////////////////////////
    1.57 +
    1.58 +/*
    1.59 + * Cost models the cost of storing a surface in the cache. Right now, this is
    1.60 + * simply an estimate of the size of the surface in bytes, but in the future it
    1.61 + * may be worth taking into account the cost of rematerializing the surface as
    1.62 + * well.
    1.63 + */
    1.64 +typedef size_t Cost;
    1.65 +
    1.66 +static Cost ComputeCost(const IntSize& aSize)
    1.67 +{
    1.68 +  return aSize.width * aSize.height * 4;  // width * height * 4 bytes (32bpp)
    1.69 +}
    1.70 +
    1.71 +/*
    1.72 + * Since we want to be able to make eviction decisions based on cost, we need to
    1.73 + * be able to look up the CachedSurface which has a certain cost as well as the
    1.74 + * cost associated with a certain CachedSurface. To make this possible, in data
    1.75 + * structures we actually store a CostEntry, which contains a weak pointer to
    1.76 + * its associated surface.
    1.77 + *
    1.78 + * To make usage of the weak pointer safe, SurfaceCacheImpl always calls
    1.79 + * StartTracking after a surface is stored in the cache and StopTracking before
    1.80 + * it is removed.
    1.81 + */
    1.82 +class CostEntry
    1.83 +{
    1.84 +public:
    1.85 +  CostEntry(CachedSurface* aSurface, Cost aCost)
    1.86 +    : mSurface(aSurface)
    1.87 +    , mCost(aCost)
    1.88 +  {
    1.89 +    MOZ_ASSERT(aSurface, "Must have a surface");
    1.90 +  }
    1.91 +
    1.92 +  CachedSurface* GetSurface() const { return mSurface; }
    1.93 +  Cost GetCost() const { return mCost; }
    1.94 +
    1.95 +  bool operator==(const CostEntry& aOther) const
    1.96 +  {
    1.97 +    return mSurface == aOther.mSurface &&
    1.98 +           mCost == aOther.mCost;
    1.99 +  }
   1.100 +
   1.101 +  bool operator<(const CostEntry& aOther) const
   1.102 +  {
   1.103 +    return mCost < aOther.mCost ||
   1.104 +           (mCost == aOther.mCost && mSurface < aOther.mSurface);
   1.105 +  }
   1.106 +
   1.107 +private:
   1.108 +  CachedSurface* mSurface;
   1.109 +  Cost           mCost;
   1.110 +};
   1.111 +
   1.112 +/*
   1.113 + * A CachedSurface associates a surface with a key that uniquely identifies that
   1.114 + * surface.
   1.115 + */
   1.116 +class CachedSurface
   1.117 +{
   1.118 +public:
   1.119 +  NS_INLINE_DECL_REFCOUNTING(CachedSurface)
   1.120 +
   1.121 +  CachedSurface(DrawTarget*       aTarget,
   1.122 +                const IntSize     aTargetSize,
   1.123 +                const Cost        aCost,
   1.124 +                const ImageKey    aImageKey,
   1.125 +                const SurfaceKey& aSurfaceKey)
   1.126 +    : mTarget(aTarget)
   1.127 +    , mTargetSize(aTargetSize)
   1.128 +    , mCost(aCost)
   1.129 +    , mImageKey(aImageKey)
   1.130 +    , mSurfaceKey(aSurfaceKey)
   1.131 +  {
   1.132 +    MOZ_ASSERT(mTarget, "Must have a valid DrawTarget");
   1.133 +    MOZ_ASSERT(mImageKey, "Must have a valid image key");
   1.134 +  }
   1.135 +
   1.136 +  already_AddRefed<gfxDrawable> Drawable() const
   1.137 +  {
   1.138 +    nsRefPtr<gfxDrawable> drawable =
   1.139 +      new gfxSurfaceDrawable(mTarget, ThebesIntSize(mTargetSize));
   1.140 +    return drawable.forget();
   1.141 +  }
   1.142 +
   1.143 +  ImageKey GetImageKey() const { return mImageKey; }
   1.144 +  SurfaceKey GetSurfaceKey() const { return mSurfaceKey; }
   1.145 +  CostEntry GetCostEntry() { return image::CostEntry(this, mCost); }
   1.146 +  nsExpirationState* GetExpirationState() { return &mExpirationState; }
   1.147 +
   1.148 +private:
   1.149 +  nsExpirationState       mExpirationState;
   1.150 +  nsRefPtr<DrawTarget>    mTarget;
   1.151 +  const IntSize           mTargetSize;
   1.152 +  const Cost              mCost;
   1.153 +  const ImageKey          mImageKey;
   1.154 +  const SurfaceKey        mSurfaceKey;
   1.155 +};
   1.156 +
   1.157 +/*
   1.158 + * An ImageSurfaceCache is a per-image surface cache. For correctness we must be
   1.159 + * able to remove all surfaces associated with an image when the image is
   1.160 + * destroyed or invalidated. Since this will happen frequently, it makes sense
   1.161 + * to make it cheap by storing the surfaces for each image separately.
   1.162 + */
   1.163 +class ImageSurfaceCache
   1.164 +{
   1.165 +public:
   1.166 +  NS_INLINE_DECL_REFCOUNTING(ImageSurfaceCache)
   1.167 +
   1.168 +  typedef nsRefPtrHashtable<nsGenericHashKey<SurfaceKey>, CachedSurface> SurfaceTable;
   1.169 +
   1.170 +  bool IsEmpty() const { return mSurfaces.Count() == 0; }
   1.171 +  
   1.172 +  void Insert(const SurfaceKey& aKey, CachedSurface* aSurface)
   1.173 +  {
   1.174 +    MOZ_ASSERT(aSurface, "Should have a surface");
   1.175 +    mSurfaces.Put(aKey, aSurface);
   1.176 +  }
   1.177 +
   1.178 +  void Remove(CachedSurface* aSurface)
   1.179 +  {
   1.180 +    MOZ_ASSERT(aSurface, "Should have a surface");
   1.181 +    MOZ_ASSERT(mSurfaces.GetWeak(aSurface->GetSurfaceKey()),
   1.182 +        "Should not be removing a surface we don't have");
   1.183 +
   1.184 +    mSurfaces.Remove(aSurface->GetSurfaceKey());
   1.185 +  }
   1.186 +
   1.187 +  already_AddRefed<CachedSurface> Lookup(const SurfaceKey& aSurfaceKey)
   1.188 +  {
   1.189 +    nsRefPtr<CachedSurface> surface;
   1.190 +    mSurfaces.Get(aSurfaceKey, getter_AddRefs(surface));
   1.191 +    return surface.forget();
   1.192 +  }
   1.193 +
   1.194 +  void ForEach(SurfaceTable::EnumReadFunction aFunction, void* aData)
   1.195 +  {
   1.196 +    mSurfaces.EnumerateRead(aFunction, aData);
   1.197 +  }
   1.198 +
   1.199 +private:
   1.200 +  SurfaceTable mSurfaces;
   1.201 +};
   1.202 +
   1.203 +/*
   1.204 + * SurfaceCacheImpl is responsible for determining which surfaces will be cached
   1.205 + * and managing the surface cache data structures. Rather than interact with
   1.206 + * SurfaceCacheImpl directly, client code interacts with SurfaceCache, which
   1.207 + * maintains high-level invariants and encapsulates the details of the surface
   1.208 + * cache's implementation.
   1.209 + */
   1.210 +class SurfaceCacheImpl : public nsIMemoryReporter
   1.211 +{
   1.212 +public:
   1.213 +  NS_DECL_ISUPPORTS
   1.214 +
   1.215 +  SurfaceCacheImpl(uint32_t aSurfaceCacheExpirationTimeMS,
   1.216 +                   uint32_t aSurfaceCacheSize)
   1.217 +    : mExpirationTracker(MOZ_THIS_IN_INITIALIZER_LIST(),
   1.218 +                         aSurfaceCacheExpirationTimeMS)
   1.219 +    , mMemoryPressureObserver(new MemoryPressureObserver)
   1.220 +    , mMaxCost(aSurfaceCacheSize)
   1.221 +    , mAvailableCost(aSurfaceCacheSize)
   1.222 +  {
   1.223 +    nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
   1.224 +    if (os)
   1.225 +      os->AddObserver(mMemoryPressureObserver, "memory-pressure", false);
   1.226 +  }
   1.227 +
   1.228 +  virtual ~SurfaceCacheImpl()
   1.229 +  {
   1.230 +    nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
   1.231 +    if (os)
   1.232 +      os->RemoveObserver(mMemoryPressureObserver, "memory-pressure");
   1.233 +
   1.234 +    UnregisterWeakMemoryReporter(this);
   1.235 +  }
   1.236 +
   1.237 +  void InitMemoryReporter() {
   1.238 +    RegisterWeakMemoryReporter(this);
   1.239 +  }
   1.240 +
   1.241 +  void Insert(DrawTarget*       aTarget,
   1.242 +              IntSize           aTargetSize,
   1.243 +              const Cost        aCost,
   1.244 +              const ImageKey    aImageKey,
   1.245 +              const SurfaceKey& aSurfaceKey)
   1.246 +  {
   1.247 +    MOZ_ASSERT(!Lookup(aImageKey, aSurfaceKey).take(),
   1.248 +               "Inserting a duplicate drawable into the SurfaceCache");
   1.249 +
   1.250 +    // If this is bigger than the maximum cache size, refuse to cache it.
   1.251 +    if (!CanHold(aCost))
   1.252 +      return;
   1.253 +
   1.254 +    nsRefPtr<CachedSurface> surface =
   1.255 +      new CachedSurface(aTarget, aTargetSize, aCost, aImageKey, aSurfaceKey);
   1.256 +
   1.257 +    // Remove elements in order of cost until we can fit this in the cache.
   1.258 +    while (aCost > mAvailableCost) {
   1.259 +      MOZ_ASSERT(!mCosts.IsEmpty(), "Removed everything and it still won't fit");
   1.260 +      Remove(mCosts.LastElement().GetSurface());
   1.261 +    }
   1.262 +
   1.263 +    // Locate the appropriate per-image cache. If there's not an existing cache
   1.264 +    // for this image, create it.
   1.265 +    nsRefPtr<ImageSurfaceCache> cache = GetImageCache(aImageKey);
   1.266 +    if (!cache) {
   1.267 +      cache = new ImageSurfaceCache;
   1.268 +      mImageCaches.Put(aImageKey, cache);
   1.269 +    }
   1.270 +
   1.271 +    // Insert.
   1.272 +    MOZ_ASSERT(aCost <= mAvailableCost, "Inserting despite too large a cost");
   1.273 +    cache->Insert(aSurfaceKey, surface);
   1.274 +    StartTracking(surface);
   1.275 +  }
   1.276 +
   1.277 +  void Remove(CachedSurface* aSurface)
   1.278 +  {
   1.279 +    MOZ_ASSERT(aSurface, "Should have a surface");
   1.280 +    const ImageKey imageKey = aSurface->GetImageKey();
   1.281 +
   1.282 +    nsRefPtr<ImageSurfaceCache> cache = GetImageCache(imageKey);
   1.283 +    MOZ_ASSERT(cache, "Shouldn't try to remove a surface with no image cache");
   1.284 +
   1.285 +    StopTracking(aSurface);
   1.286 +    cache->Remove(aSurface);
   1.287 +
   1.288 +    // Remove the per-image cache if it's unneeded now.
   1.289 +    if (cache->IsEmpty()) {
   1.290 +      mImageCaches.Remove(imageKey);
   1.291 +    }
   1.292 +  }
   1.293 +
   1.294 +  void StartTracking(CachedSurface* aSurface)
   1.295 +  {
   1.296 +    CostEntry costEntry = aSurface->GetCostEntry();
   1.297 +    MOZ_ASSERT(costEntry.GetCost() <= mAvailableCost,
   1.298 +               "Cost too large and the caller didn't catch it");
   1.299 +
   1.300 +    mAvailableCost -= costEntry.GetCost();
   1.301 +    mCosts.InsertElementSorted(costEntry);
   1.302 +    mExpirationTracker.AddObject(aSurface);
   1.303 +  }
   1.304 +
   1.305 +  void StopTracking(CachedSurface* aSurface)
   1.306 +  {
   1.307 +    MOZ_ASSERT(aSurface, "Should have a surface");
   1.308 +    CostEntry costEntry = aSurface->GetCostEntry();
   1.309 +
   1.310 +    mExpirationTracker.RemoveObject(aSurface);
   1.311 +    DebugOnly<bool> foundInCosts = mCosts.RemoveElementSorted(costEntry);
   1.312 +    mAvailableCost += costEntry.GetCost();
   1.313 +
   1.314 +    MOZ_ASSERT(foundInCosts, "Lost track of costs for this surface");
   1.315 +    MOZ_ASSERT(mAvailableCost <= mMaxCost, "More available cost than we started with");
   1.316 +  }
   1.317 +
   1.318 +  already_AddRefed<gfxDrawable> Lookup(const ImageKey    aImageKey,
   1.319 +                                       const SurfaceKey& aSurfaceKey)
   1.320 +  {
   1.321 +    nsRefPtr<ImageSurfaceCache> cache = GetImageCache(aImageKey);
   1.322 +    if (!cache)
   1.323 +      return nullptr;  // No cached surfaces for this image.
   1.324 +    
   1.325 +    nsRefPtr<CachedSurface> surface = cache->Lookup(aSurfaceKey);
   1.326 +    if (!surface)
   1.327 +      return nullptr;  // Lookup in the per-image cache missed.
   1.328 +    
   1.329 +    mExpirationTracker.MarkUsed(surface);
   1.330 +    return surface->Drawable();
   1.331 +  }
   1.332 +
   1.333 +  bool CanHold(const Cost aCost) const
   1.334 +  {
   1.335 +    return aCost <= mMaxCost;
   1.336 +  }
   1.337 +
   1.338 +  void Discard(const ImageKey aImageKey)
   1.339 +  {
   1.340 +    nsRefPtr<ImageSurfaceCache> cache = GetImageCache(aImageKey);
   1.341 +    if (!cache)
   1.342 +      return;  // No cached surfaces for this image, so nothing to do.
   1.343 +
   1.344 +    // Discard all of the cached surfaces for this image.
   1.345 +    // XXX(seth): This is O(n^2) since for each item in the cache we are
   1.346 +    // removing an element from the costs array. Since n is expected to be
   1.347 +    // small, performance should be good, but if usage patterns change we should
   1.348 +    // change the data structure used for mCosts.
   1.349 +    cache->ForEach(DoStopTracking, this);
   1.350 +
   1.351 +    // The per-image cache isn't needed anymore, so remove it as well.
   1.352 +    mImageCaches.Remove(aImageKey);
   1.353 +  }
   1.354 +
   1.355 +  void DiscardAll()
   1.356 +  {
   1.357 +    // Remove in order of cost because mCosts is an array and the other data
   1.358 +    // structures are all hash tables.
   1.359 +    while (!mCosts.IsEmpty()) {
   1.360 +      Remove(mCosts.LastElement().GetSurface());
   1.361 +    }
   1.362 +  }
   1.363 +
   1.364 +  static PLDHashOperator DoStopTracking(const SurfaceKey&,
   1.365 +                                        CachedSurface*    aSurface,
   1.366 +                                        void*             aCache)
   1.367 +  {
   1.368 +    static_cast<SurfaceCacheImpl*>(aCache)->StopTracking(aSurface);
   1.369 +    return PL_DHASH_NEXT;
   1.370 +  }
   1.371 +
   1.372 +  NS_IMETHOD
   1.373 +  CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData)
   1.374 +  {
   1.375 +    return MOZ_COLLECT_REPORT(
   1.376 +      "imagelib-surface-cache", KIND_OTHER, UNITS_BYTES,
   1.377 +      SizeOfSurfacesEstimate(),
   1.378 +      "Memory used by the imagelib temporary surface cache.");
   1.379 +  }
   1.380 +
   1.381 +  // XXX(seth): This is currently only an estimate and, since we don't know
   1.382 +  // which surfaces are in GPU memory and which aren't, it's reported as
   1.383 +  // KIND_OTHER and will also show up in heap-unclassified. Bug 923302 will
   1.384 +  // make this nicer.
   1.385 +  Cost SizeOfSurfacesEstimate() const
   1.386 +  {
   1.387 +    return mMaxCost - mAvailableCost;
   1.388 +  }
   1.389 +
   1.390 +private:
   1.391 +  already_AddRefed<ImageSurfaceCache> GetImageCache(const ImageKey aImageKey)
   1.392 +  {
   1.393 +    nsRefPtr<ImageSurfaceCache> imageCache;
   1.394 +    mImageCaches.Get(aImageKey, getter_AddRefs(imageCache));
   1.395 +    return imageCache.forget();
   1.396 +  }
   1.397 +
   1.398 +  struct SurfaceTracker : public nsExpirationTracker<CachedSurface, 2>
   1.399 +  {
   1.400 +    SurfaceTracker(SurfaceCacheImpl* aCache, uint32_t aSurfaceCacheExpirationTimeMS)
   1.401 +      : nsExpirationTracker<CachedSurface, 2>(aSurfaceCacheExpirationTimeMS)
   1.402 +      , mCache(aCache)
   1.403 +    { }
   1.404 +
   1.405 +  protected:
   1.406 +    virtual void NotifyExpired(CachedSurface* aSurface) MOZ_OVERRIDE
   1.407 +    {
   1.408 +      if (mCache) {
   1.409 +        mCache->Remove(aSurface);
   1.410 +      }
   1.411 +    }
   1.412 +
   1.413 +  private:
   1.414 +    SurfaceCacheImpl* const mCache;  // Weak pointer to owner.
   1.415 +  };
   1.416 +
   1.417 +  struct MemoryPressureObserver : public nsIObserver
   1.418 +  {
   1.419 +    NS_DECL_ISUPPORTS
   1.420 +
   1.421 +    virtual ~MemoryPressureObserver() { }
   1.422 +
   1.423 +    NS_IMETHOD Observe(nsISupports*, const char* aTopic, const char16_t*)
   1.424 +    {
   1.425 +      if (sInstance && strcmp(aTopic, "memory-pressure") == 0) {
   1.426 +        sInstance->DiscardAll();
   1.427 +      }
   1.428 +      return NS_OK;
   1.429 +    }
   1.430 +  };
   1.431 +
   1.432 +
   1.433 +  nsTArray<CostEntry>                                       mCosts;
   1.434 +  nsRefPtrHashtable<nsPtrHashKey<Image>, ImageSurfaceCache> mImageCaches;
   1.435 +  SurfaceTracker                                            mExpirationTracker;
   1.436 +  nsRefPtr<MemoryPressureObserver>                          mMemoryPressureObserver;
   1.437 +  const Cost                                                mMaxCost;
   1.438 +  Cost                                                      mAvailableCost;
   1.439 +};
   1.440 +
   1.441 +NS_IMPL_ISUPPORTS(SurfaceCacheImpl, nsIMemoryReporter)
   1.442 +NS_IMPL_ISUPPORTS(SurfaceCacheImpl::MemoryPressureObserver, nsIObserver)
   1.443 +
   1.444 +///////////////////////////////////////////////////////////////////////////////
   1.445 +// Public API
   1.446 +///////////////////////////////////////////////////////////////////////////////
   1.447 +
   1.448 +/* static */ void
   1.449 +SurfaceCache::Initialize()
   1.450 +{
   1.451 +  // Initialize preferences.
   1.452 +  MOZ_ASSERT(!sInstance, "Shouldn't initialize more than once");
   1.453 +
   1.454 +  // Length of time before an unused surface is removed from the cache, in milliseconds.
   1.455 +  // The default value gives an expiration time of 1 minute.
   1.456 +  uint32_t surfaceCacheExpirationTimeMS =
   1.457 +    Preferences::GetUint("image.mem.surfacecache.min_expiration_ms", 60 * 1000);
   1.458 +
   1.459 +  // Maximum size of the surface cache, in kilobytes.
   1.460 +  // The default is 100MB. (But we may override this for e.g. B2G.)
   1.461 +  uint32_t surfaceCacheMaxSizeKB =
   1.462 +    Preferences::GetUint("image.mem.surfacecache.max_size_kb", 100 * 1024);
   1.463 +
   1.464 +  // A knob determining the actual size of the surface cache. Currently the
   1.465 +  // cache is (size of main memory) / (surface cache size factor) KB
   1.466 +  // or (surface cache max size) KB, whichever is smaller. The formula
   1.467 +  // may change in the future, though.
   1.468 +  // The default value is 64, which yields a 64MB cache on a 4GB machine.
   1.469 +  // The smallest machines we are likely to run this code on have 256MB
   1.470 +  // of memory, which would yield a 4MB cache on the default setting.
   1.471 +  uint32_t surfaceCacheSizeFactor =
   1.472 +    Preferences::GetUint("image.mem.surfacecache.size_factor", 64);
   1.473 +
   1.474 +  // Clamp to avoid division by zero below.
   1.475 +  surfaceCacheSizeFactor = max(surfaceCacheSizeFactor, 1u);
   1.476 +
   1.477 +  // Compute the size of the surface cache.
   1.478 +  uint32_t proposedSize = PR_GetPhysicalMemorySize() / surfaceCacheSizeFactor;
   1.479 +  uint32_t surfaceCacheSizeBytes = min(proposedSize, surfaceCacheMaxSizeKB * 1024);
   1.480 +
   1.481 +  // Create the surface cache singleton with the requested expiration time and
   1.482 +  // size. Note that the size is a limit that the cache may not grow beyond, but
   1.483 +  // we do not actually allocate any storage for surfaces at this time.
   1.484 +  sInstance = new SurfaceCacheImpl(surfaceCacheExpirationTimeMS,
   1.485 +                                   surfaceCacheSizeBytes);
   1.486 +  sInstance->InitMemoryReporter();
   1.487 +}
   1.488 +
   1.489 +/* static */ void
   1.490 +SurfaceCache::Shutdown()
   1.491 +{
   1.492 +  MOZ_ASSERT(sInstance, "No singleton - was Shutdown() called twice?");
   1.493 +  sInstance = nullptr;
   1.494 +}
   1.495 +
   1.496 +/* static */ already_AddRefed<gfxDrawable>
   1.497 +SurfaceCache::Lookup(const ImageKey    aImageKey,
   1.498 +                     const SurfaceKey& aSurfaceKey)
   1.499 +{
   1.500 +  MOZ_ASSERT(sInstance, "Should be initialized");
   1.501 +  MOZ_ASSERT(NS_IsMainThread());
   1.502 +
   1.503 +  return sInstance->Lookup(aImageKey, aSurfaceKey);
   1.504 +}
   1.505 +
   1.506 +/* static */ void
   1.507 +SurfaceCache::Insert(DrawTarget*       aTarget,
   1.508 +                     const ImageKey    aImageKey,
   1.509 +                     const SurfaceKey& aSurfaceKey)
   1.510 +{
   1.511 +  MOZ_ASSERT(sInstance, "Should be initialized");
   1.512 +  MOZ_ASSERT(NS_IsMainThread());
   1.513 +
   1.514 +  Cost cost = ComputeCost(aSurfaceKey.Size());
   1.515 +  return sInstance->Insert(aTarget, aSurfaceKey.Size(), cost, aImageKey,
   1.516 +                           aSurfaceKey);
   1.517 +}
   1.518 +
   1.519 +/* static */ bool
   1.520 +SurfaceCache::CanHold(const IntSize& aSize)
   1.521 +{
   1.522 +  MOZ_ASSERT(sInstance, "Should be initialized");
   1.523 +  MOZ_ASSERT(NS_IsMainThread());
   1.524 +
   1.525 +  Cost cost = ComputeCost(aSize);
   1.526 +  return sInstance->CanHold(cost);
   1.527 +}
   1.528 +
   1.529 +/* static */ void
   1.530 +SurfaceCache::Discard(Image* aImageKey)
   1.531 +{
   1.532 +  MOZ_ASSERT(sInstance, "Should be initialized");
   1.533 +  MOZ_ASSERT(NS_IsMainThread());
   1.534 +
   1.535 +  return sInstance->Discard(aImageKey);
   1.536 +}
   1.537 +
   1.538 +/* static */ void
   1.539 +SurfaceCache::DiscardAll()
   1.540 +{
   1.541 +  MOZ_ASSERT(sInstance, "Should be initialized");
   1.542 +  MOZ_ASSERT(NS_IsMainThread());
   1.543 +
   1.544 +  return sInstance->DiscardAll();
   1.545 +}
   1.546 +
   1.547 +} // namespace image
   1.548 +} // namespace mozilla

mercurial