LCOV - code coverage report
Current view: top level - image - SurfaceCache.h (source / functions) Hit Total Coverage
Test: output.info Lines: 27 30 90.0 %
Date: 2017-07-14 16:53:18 Functions: 14 17 82.4 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       5             : 
       6             : /**
       7             :  * SurfaceCache is a service for caching temporary surfaces and decoded image
       8             :  * data in imagelib.
       9             :  */
      10             : 
      11             : #ifndef mozilla_image_SurfaceCache_h
      12             : #define mozilla_image_SurfaceCache_h
      13             : 
      14             : #include "mozilla/Maybe.h"           // for Maybe
      15             : #include "mozilla/NotNull.h"
      16             : #include "mozilla/MemoryReporting.h" // for MallocSizeOf
      17             : #include "mozilla/HashFunctions.h"   // for HashGeneric and AddToHash
      18             : #include "gfx2DGlue.h"
      19             : #include "gfxPoint.h"                // for gfxSize
      20             : #include "nsCOMPtr.h"                // for already_AddRefed
      21             : #include "mozilla/gfx/Point.h"       // for mozilla::gfx::IntSize
      22             : #include "mozilla/gfx/2D.h"          // for SourceSurface
      23             : #include "PlaybackType.h"
      24             : #include "SurfaceFlags.h"
      25             : #include "SVGImageContext.h"         // for SVGImageContext
      26             : 
      27             : namespace mozilla {
      28             : namespace image {
      29             : 
      30             : class Image;
      31             : class ISurfaceProvider;
      32             : class LookupResult;
      33             : class SurfaceCacheImpl;
      34             : struct SurfaceMemoryCounter;
      35             : 
      36             : /*
      37             :  * ImageKey contains the information we need to look up all SurfaceCache entries
      38             :  * for a particular image.
      39             :  */
      40             : typedef Image* ImageKey;
      41             : 
      42             : /*
      43             :  * SurfaceKey contains the information we need to look up a specific
      44             :  * SurfaceCache entry. Together with an ImageKey, this uniquely identifies the
      45             :  * surface.
      46             :  *
      47             :  * Callers should construct a SurfaceKey using the appropriate helper function
      48             :  * for their image type - either RasterSurfaceKey or VectorSurfaceKey.
      49             :  */
      50         645 : class SurfaceKey
      51             : {
      52             :   typedef gfx::IntSize IntSize;
      53             : 
      54             : public:
      55         284 :   bool operator==(const SurfaceKey& aOther) const
      56             :   {
      57         568 :     return aOther.mSize == mSize &&
      58         568 :            aOther.mSVGContext == mSVGContext &&
      59         852 :            aOther.mPlayback == mPlayback &&
      60         568 :            aOther.mFlags == mFlags;
      61             :   }
      62             : 
      63         217 :   PLDHashNumber Hash() const
      64             :   {
      65         217 :     PLDHashNumber hash = HashGeneric(mSize.width, mSize.height);
      66         217 :     hash = AddToHash(hash, mSVGContext.map(HashSIC).valueOr(0));
      67         217 :     hash = AddToHash(hash, uint8_t(mPlayback), uint32_t(mFlags));
      68         217 :     return hash;
      69             :   }
      70             : 
      71          88 :   const IntSize& Size() const { return mSize; }
      72           0 :   Maybe<SVGImageContext> SVGContext() const { return mSVGContext; }
      73           0 :   PlaybackType Playback() const { return mPlayback; }
      74           0 :   SurfaceFlags Flags() const { return mFlags; }
      75             : 
      76             : private:
      77         179 :   SurfaceKey(const IntSize& aSize,
      78             :              const Maybe<SVGImageContext>& aSVGContext,
      79             :              PlaybackType aPlayback,
      80             :              SurfaceFlags aFlags)
      81         179 :     : mSize(aSize)
      82             :     , mSVGContext(aSVGContext)
      83             :     , mPlayback(aPlayback)
      84         179 :     , mFlags(aFlags)
      85         179 :   { }
      86             : 
      87          73 :   static PLDHashNumber HashSIC(const SVGImageContext& aSIC) {
      88          73 :     return aSIC.Hash();
      89             :   }
      90             : 
      91             :   friend SurfaceKey RasterSurfaceKey(const IntSize&, SurfaceFlags, PlaybackType);
      92             :   friend SurfaceKey VectorSurfaceKey(const IntSize&,
      93             :                                      const Maybe<SVGImageContext>&);
      94             : 
      95             :   IntSize                mSize;
      96             :   Maybe<SVGImageContext> mSVGContext;
      97             :   PlaybackType           mPlayback;
      98             :   SurfaceFlags           mFlags;
      99             : };
     100             : 
     101             : inline SurfaceKey
     102          88 : RasterSurfaceKey(const gfx::IntSize& aSize,
     103             :                  SurfaceFlags aFlags,
     104             :                  PlaybackType aPlayback)
     105             : {
     106          88 :   return SurfaceKey(aSize, Nothing(), aPlayback, aFlags);
     107             : }
     108             : 
     109             : inline SurfaceKey
     110          91 : VectorSurfaceKey(const gfx::IntSize& aSize,
     111             :                  const Maybe<SVGImageContext>& aSVGContext)
     112             : {
     113             :   // We don't care about aFlags for VectorImage because none of the flags we
     114             :   // have right now influence VectorImage's rendering. If we add a new flag that
     115             :   // *does* affect how a VectorImage renders, we'll have to change this.
     116             :   // Similarly, we don't accept a PlaybackType parameter because we don't
     117             :   // currently cache frames of animated SVG images.
     118             :   return SurfaceKey(aSize, aSVGContext, PlaybackType::eStatic,
     119          91 :                     DefaultSurfaceFlags());
     120             : }
     121             : 
     122             : 
     123             : /**
     124             :  * AvailabilityState is used to track whether an ISurfaceProvider has a surface
     125             :  * available or is just a placeholder.
     126             :  *
     127             :  * To ensure that availability changes are atomic (and especially that internal
     128             :  * SurfaceCache code doesn't have to deal with asynchronous availability
     129             :  * changes), an ISurfaceProvider which starts as a placeholder can only reveal
     130             :  * the fact that it now has a surface available via a call to
     131             :  * SurfaceCache::SurfaceAvailable().
     132             :  */
     133             : class AvailabilityState
     134             : {
     135             : public:
     136          18 :   static AvailabilityState StartAvailable() { return AvailabilityState(true); }
     137          14 :   static AvailabilityState StartAsPlaceholder() { return AvailabilityState(false); }
     138             : 
     139             :   bool IsAvailable() const { return mIsAvailable; }
     140         971 :   bool IsPlaceholder() const { return !mIsAvailable; }
     141             : 
     142             : private:
     143             :   friend class SurfaceCacheImpl;
     144             : 
     145          32 :   explicit AvailabilityState(bool aIsAvailable) : mIsAvailable(aIsAvailable) { }
     146             : 
     147          14 :   void SetAvailable() { mIsAvailable = true; }
     148             : 
     149             :   bool mIsAvailable;
     150             : };
     151             : 
     152             : enum class InsertOutcome : uint8_t {
     153             :   SUCCESS,                 // Success (but see Insert documentation).
     154             :   FAILURE,                 // Couldn't insert (e.g., for capacity reasons).
     155             :   FAILURE_ALREADY_PRESENT  // A surface with the same key is already present.
     156             : };
     157             : 
     158             : /**
     159             :  * SurfaceCache is an ImageLib-global service that allows caching of decoded
     160             :  * image surfaces, temporary surfaces (e.g. for caching rotated or clipped
     161             :  * versions of images), or dynamically generated surfaces (e.g. for animations).
     162             :  * SurfaceCache entries normally expire from the cache automatically if they go
     163             :  * too long without being accessed.
     164             :  *
     165             :  * Because SurfaceCache must support both normal surfaces and dynamically
     166             :  * generated surfaces, it does not actually hold surfaces directly. Instead, it
     167             :  * holds ISurfaceProvider objects which can provide access to a surface when
     168             :  * requested; SurfaceCache doesn't care about the details of how this is
     169             :  * accomplished.
     170             :  *
     171             :  * Sometime it's useful to temporarily prevent entries from expiring from the
     172             :  * cache. This is most often because losing the data could harm the user
     173             :  * experience (for example, we often don't want to allow surfaces that are
     174             :  * currently visible to expire) or because it's not possible to rematerialize
     175             :  * the surface. SurfaceCache supports this through the use of image locking; see
     176             :  * the comments for Insert() and LockImage() for more details.
     177             :  *
     178             :  * Any image which stores surfaces in the SurfaceCache *must* ensure that it
     179             :  * calls RemoveImage() before it is destroyed. See the comments for
     180             :  * RemoveImage() for more details.
     181             :  */
     182             : struct SurfaceCache
     183             : {
     184             :   typedef gfx::IntSize IntSize;
     185             : 
     186             :   /**
     187             :    * Initialize static data. Called during imagelib module initialization.
     188             :    */
     189             :   static void Initialize();
     190             : 
     191             :   /**
     192             :    * Release static data. Called during imagelib module shutdown.
     193             :    */
     194             :   static void Shutdown();
     195             : 
     196             :   /**
     197             :    * Looks up the requested cache entry and returns a drawable reference to its
     198             :    * associated surface.
     199             :    *
     200             :    * If the image associated with the cache entry is locked, then the entry will
     201             :    * be locked before it is returned.
     202             :    *
     203             :    * If a matching ISurfaceProvider was found in the cache, but SurfaceCache
     204             :    * couldn't obtain a surface from it (e.g. because it had stored its surface
     205             :    * in a volatile buffer which was discarded by the OS) then it is
     206             :    * automatically removed from the cache and an empty LookupResult is returned.
     207             :    * Note that this will never happen to ISurfaceProviders associated with a
     208             :    * locked image; SurfaceCache tells such ISurfaceProviders to keep a strong
     209             :    * references to their data internally.
     210             :    *
     211             :    * @param aImageKey       Key data identifying which image the cache entry
     212             :    *                        belongs to.
     213             :    * @param aSurfaceKey     Key data which uniquely identifies the requested
     214             :    *                        cache entry.
     215             :    * @return                a LookupResult which will contain a DrawableSurface
     216             :    *                        if the cache entry was found.
     217             :    */
     218             :   static LookupResult Lookup(const ImageKey    aImageKey,
     219             :                              const SurfaceKey& aSurfaceKey);
     220             : 
     221             :   /**
     222             :    * Looks up the best matching cache entry and returns a drawable reference to
     223             :    * its associated surface.
     224             :    *
     225             :    * The result may vary from the requested cache entry only in terms of size.
     226             :    *
     227             :    * @param aImageKey       Key data identifying which image the cache entry
     228             :    *                        belongs to.
     229             :    * @param aSurfaceKey     Key data which uniquely identifies the requested
     230             :    *                        cache entry.
     231             :    * @return                a LookupResult which will contain a DrawableSurface
     232             :    *                        if a cache entry similar to the one the caller
     233             :    *                        requested could be found. Callers can use
     234             :    *                        LookupResult::IsExactMatch() to check whether the
     235             :    *                        returned surface exactly matches @aSurfaceKey.
     236             :    */
     237             :   static LookupResult LookupBestMatch(const ImageKey    aImageKey,
     238             :                                       const SurfaceKey& aSurfaceKey);
     239             : 
     240             :   /**
     241             :    * Insert an ISurfaceProvider into the cache. If an entry with the same
     242             :    * ImageKey and SurfaceKey is already in the cache, Insert returns
     243             :    * FAILURE_ALREADY_PRESENT. If a matching placeholder is already present, it
     244             :    * is replaced.
     245             :    *
     246             :    * Cache entries will never expire as long as they remain locked, but if they
     247             :    * become unlocked, they can expire either because the SurfaceCache runs out
     248             :    * of capacity or because they've gone too long without being used.  When it
     249             :    * is first inserted, a cache entry is locked if its associated image is
     250             :    * locked.  When that image is later unlocked, the cache entry becomes
     251             :    * unlocked too. To become locked again at that point, two things must happen:
     252             :    * the image must become locked again (via LockImage()), and the cache entry
     253             :    * must be touched again (via one of the Lookup() functions).
     254             :    *
     255             :    * All of this means that a very particular procedure has to be followed for
     256             :    * cache entries which cannot be rematerialized. First, they must be inserted
     257             :    * *after* the image is locked with LockImage(); if you use the other order,
     258             :    * the cache entry might expire before LockImage() gets called or before the
     259             :    * entry is touched again by Lookup(). Second, the image they are associated
     260             :    * with must never be unlocked.
     261             :    *
     262             :    * If a cache entry cannot be rematerialized, it may be important to know
     263             :    * whether it was inserted into the cache successfully. Insert() returns
     264             :    * FAILURE if it failed to insert the cache entry, which could happen because
     265             :    * of capacity reasons, or because it was already freed by the OS. If the
     266             :    * cache entry isn't associated with a locked image, checking for SUCCESS or
     267             :    * FAILURE is useless: the entry might expire immediately after being
     268             :    * inserted, even though Insert() returned SUCCESS. Thus, many callers do not
     269             :    * need to check the result of Insert() at all.
     270             :    *
     271             :    * @param aProvider    The new cache entry to insert into the cache.
     272             :    * @return SUCCESS if the cache entry was inserted successfully. (But see above
     273             :    *           for more information about when you should check this.)
     274             :    *         FAILURE if the cache entry could not be inserted, e.g. for capacity
     275             :    *           reasons. (But see above for more information about when you
     276             :    *           should check this.)
     277             :    *         FAILURE_ALREADY_PRESENT if an entry with the same ImageKey and
     278             :    *           SurfaceKey already exists in the cache.
     279             :    */
     280             :   static InsertOutcome Insert(NotNull<ISurfaceProvider*> aProvider);
     281             : 
     282             :   /**
     283             :    * Mark the cache entry @aProvider as having an available surface. This turns
     284             :    * a placeholder cache entry into a normal cache entry. The cache entry
     285             :    * becomes locked if the associated image is locked; otherwise, it starts in
     286             :    * the unlocked state.
     287             :    *
     288             :    * If the cache entry containing @aProvider has already been evicted from the
     289             :    * surface cache, this function has no effect.
     290             :    *
     291             :    * It's illegal to call this function if @aProvider is not a placeholder; by
     292             :    * definition, non-placeholder ISurfaceProviders should have a surface
     293             :    * available already.
     294             :    *
     295             :    * @param aProvider       The cache entry that now has a surface available.
     296             :    */
     297             :   static void SurfaceAvailable(NotNull<ISurfaceProvider*> aProvider);
     298             : 
     299             :   /**
     300             :    * Checks if a surface of a given size could possibly be stored in the cache.
     301             :    * If CanHold() returns false, Insert() will always fail to insert the
     302             :    * surface, but the inverse is not true: Insert() may take more information
     303             :    * into account than just image size when deciding whether to cache the
     304             :    * surface, so Insert() may still fail even if CanHold() returns true.
     305             :    *
     306             :    * Use CanHold() to avoid the need to create a temporary surface when we know
     307             :    * for sure the cache can't hold it.
     308             :    *
     309             :    * @param aSize  The dimensions of a surface in pixels.
     310             :    * @param aBytesPerPixel  How many bytes each pixel of the surface requires.
     311             :    *                        Defaults to 4, which is appropriate for RGBA or RGBX
     312             :    *                        images.
     313             :    *
     314             :    * @return false if the surface cache can't hold a surface of that size.
     315             :    */
     316             :   static bool CanHold(const IntSize& aSize, uint32_t aBytesPerPixel = 4);
     317             :   static bool CanHold(size_t aSize);
     318             : 
     319             :   /**
     320             :    * Locks an image. Any of the image's cache entries which are either inserted
     321             :    * or accessed while the image is locked will not expire.
     322             :    *
     323             :    * Locking an image does not automatically lock that image's existing cache
     324             :    * entries. A call to LockImage() guarantees that entries which are inserted
     325             :    * afterward will not expire before the next call to UnlockImage() or
     326             :    * UnlockSurfaces() for that image. Cache entries that are accessed via
     327             :    * Lookup() or LookupBestMatch() after a LockImage() call will also not expire
     328             :    * until the next UnlockImage() or UnlockSurfaces() call for that image. Any
     329             :    * other cache entries owned by the image may expire at any time.
     330             :    *
     331             :    * All of an image's cache entries are removed by RemoveImage(), whether the
     332             :    * image is locked or not.
     333             :    *
     334             :    * It's safe to call LockImage() on an image that's already locked; this has
     335             :    * no effect.
     336             :    *
     337             :    * You must always unlock any image you lock. You may do this explicitly by
     338             :    * calling UnlockImage(), or implicitly by calling RemoveImage(). Since you're
     339             :    * required to call RemoveImage() when you destroy an image, this doesn't
     340             :    * impose any additional requirements, but it's preferable to call
     341             :    * UnlockImage() earlier if it's possible.
     342             :    *
     343             :    * @param aImageKey    The image to lock.
     344             :    */
     345             :   static void LockImage(const ImageKey aImageKey);
     346             : 
     347             :   /**
     348             :    * Unlocks an image, allowing any of its cache entries to expire at any time.
     349             :    *
     350             :    * It's OK to call UnlockImage() on an image that's already unlocked; this has
     351             :    * no effect.
     352             :    *
     353             :    * @param aImageKey    The image to unlock.
     354             :    */
     355             :   static void UnlockImage(const ImageKey aImageKey);
     356             : 
     357             :   /**
     358             :    * Unlocks the existing cache entries of an image, allowing them to expire at
     359             :    * any time.
     360             :    *
     361             :    * This does not unlock the image itself, so accessing the cache entries via
     362             :    * Lookup() or LookupBestMatch() will lock them again, and prevent them from
     363             :    * expiring.
     364             :    *
     365             :    * This is intended to be used in situations where it's no longer clear that
     366             :    * all of the cache entries owned by an image are needed. Calling
     367             :    * UnlockSurfaces() and then taking some action that will cause Lookup() to
     368             :    * touch any cache entries that are still useful will permit the remaining
     369             :    * entries to expire from the cache.
     370             :    *
     371             :    * If the image is unlocked, this has no effect.
     372             :    *
     373             :    * @param aImageKey    The image which should have its existing cache entries
     374             :    *                     unlocked.
     375             :    */
     376             :   static void UnlockEntries(const ImageKey aImageKey);
     377             : 
     378             :   /**
     379             :    * Removes all cache entries (including placeholders) associated with the
     380             :    * given image from the cache.  If the image is locked, it is automatically
     381             :    * unlocked.
     382             :    *
     383             :    * This MUST be called, at a minimum, when an Image which could be storing
     384             :    * entries in the surface cache is destroyed. If another image were allocated
     385             :    * at the same address it could result in subtle, difficult-to-reproduce bugs.
     386             :    *
     387             :    * @param aImageKey  The image which should be removed from the cache.
     388             :    */
     389             :   static void RemoveImage(const ImageKey aImageKey);
     390             : 
     391             :   /**
     392             :    * Evicts all evictable entries from the cache.
     393             :    *
     394             :    * All entries are evictable except for entries associated with locked images.
     395             :    * Non-evictable entries can only be removed by RemoveImage().
     396             :    */
     397             :   static void DiscardAll();
     398             : 
     399             :   /**
     400             :    * Collects an accounting of the surfaces contained in the SurfaceCache for
     401             :    * the given image, along with their size and various other metadata.
     402             :    *
     403             :    * This is intended for use with memory reporting.
     404             :    *
     405             :    * @param aImageKey     The image to report memory usage for.
     406             :    * @param aCounters     An array into which the report for each surface will
     407             :    *                      be written.
     408             :    * @param aMallocSizeOf A fallback malloc memory reporting function.
     409             :    */
     410             :   static void CollectSizeOfSurfaces(const ImageKey    aImageKey,
     411             :                                     nsTArray<SurfaceMemoryCounter>& aCounters,
     412             :                                     MallocSizeOf      aMallocSizeOf);
     413             : 
     414             :   /**
     415             :    * @return maximum capacity of the SurfaceCache in bytes. This is only exposed
     416             :    * for use by tests; normal code should use CanHold() instead.
     417             :    */
     418             :   static size_t MaximumCapacity();
     419             : 
     420             : private:
     421             :   virtual ~SurfaceCache() = 0;  // Forbid instantiation.
     422             : };
     423             : 
     424             : } // namespace image
     425             : } // namespace mozilla
     426             : 
     427             : #endif // mozilla_image_SurfaceCache_h

Generated by: LCOV version 1.13