Loading image/ISurfaceProvider.h +17 −7 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ #include "mozilla/gfx/2D.h" #include "imgFrame.h" #include "SurfaceCache.h" namespace mozilla { namespace image { Loading @@ -37,11 +38,6 @@ public: /// @return a drawable reference to a surface. virtual DrawableFrameRef DrawableRef() = 0; /// @return true if this ISurfaceProvider is acting as a placeholder, which is /// to say that it doesn't have a surface and hence can't return a /// non-empty DrawableFrameRef yet, but it will be able to in the future. virtual bool IsPlaceholder() const = 0; /// @return true if DrawableRef() will return a completely decoded surface. virtual bool IsFinished() const = 0; Loading @@ -57,8 +53,22 @@ public: /// overhead is ignored. virtual size_t LogicalSizeInBytes() const = 0; /// @return the availability state of this ISurfaceProvider, which indicates /// whether DrawableRef() could successfully return a surface. Should only be /// called from SurfaceCache code as it relies on SurfaceCache for /// synchronization. AvailabilityState& Availability() { return mAvailability; } const AvailabilityState& Availability() const { return mAvailability; } protected: explicit ISurfaceProvider(AvailabilityState aAvailability) : mAvailability(aAvailability) { } virtual ~ISurfaceProvider() { } private: AvailabilityState mAvailability; }; /** Loading @@ -70,11 +80,11 @@ public: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SimpleSurfaceProvider, override) explicit SimpleSurfaceProvider(NotNull<imgFrame*> aSurface) : mSurface(aSurface) : ISurfaceProvider(AvailabilityState::StartAvailable()) , mSurface(aSurface) { } DrawableFrameRef DrawableRef() override { return mSurface->DrawableRef(); } bool IsPlaceholder() const override { return false; } bool IsFinished() const override { return mSurface->IsFinished(); } bool IsLocked() const override { return bool(mLockRef); } Loading image/SurfaceCache.cpp +52 −6 Original line number Diff line number Diff line Loading @@ -71,6 +71,8 @@ typedef size_t Cost; // Placeholders do not have surfaces, but need to be given a trivial cost for // our invariants to hold. // XXX(seth): This is only true of old-style placeholders inserted via // InsertPlaceholder(). static const Cost sPlaceholderCost = 1; static Cost Loading Loading @@ -141,8 +143,8 @@ public: , mImageKey(aImageKey) , mSurfaceKey(aSurfaceKey) { MOZ_ASSERT(!IsPlaceholder() || mCost == sPlaceholderCost, "Placeholder should have trivial cost"); MOZ_ASSERT(aProvider || mCost == sPlaceholderCost, "Old-style placeholders should have trivial cost"); MOZ_ASSERT(mImageKey, "Must have a valid image key"); } Loading @@ -168,7 +170,11 @@ public: mProvider->SetLocked(aLocked); } bool IsPlaceholder() const { return !mProvider || mProvider->IsPlaceholder(); } bool IsPlaceholder() const { return !mProvider || mProvider->Availability().IsPlaceholder(); } bool IsLocked() const { return !IsPlaceholder() && mProvider->IsLocked(); } ImageKey GetImageKey() const { return mImageKey; } Loading Loading @@ -449,7 +455,8 @@ public: InsertOutcome Insert(ISurfaceProvider* aProvider, const Cost aCost, const ImageKey aImageKey, const SurfaceKey& aSurfaceKey) const SurfaceKey& aSurfaceKey, bool aSetAvailable) { // If this is a duplicate surface, refuse to replace the original. // XXX(seth): Calling Lookup() and then RemoveEntry() does the lookup Loading Loading @@ -490,6 +497,11 @@ public: mImageCaches.Put(aImageKey, cache); } // If we were asked to mark the cache entry available, do so. if (aSetAvailable) { aProvider->Availability().SetAvailable(); } RefPtr<CachedSurface> surface = new CachedSurface(aProvider, aCost, aImageKey, aSurfaceKey); Loading Loading @@ -667,6 +679,25 @@ public: return size_t(mMaxCost); } void SurfaceAvailable(NotNull<ISurfaceProvider*> aProvider, const ImageKey aImageKey, const SurfaceKey& aSurfaceKey) { if (!aProvider->Availability().IsPlaceholder()) { MOZ_ASSERT_UNREACHABLE("Calling SurfaceAvailable on non-placeholder"); return; } // Reinsert the provider, requesting that Insert() mark it available. This // may or may not succeed, depending on whether some other decoder has // beaten us to the punch and inserted a non-placeholder version of this // surface first, but it's fine either way. // XXX(seth): This could be implemented more efficiently; we should be able // to just update our data structures without reinserting. Cost cost = aProvider->LogicalSizeInBytes(); Insert(aProvider, cost, aImageKey, aSurfaceKey, /* aSetAvailable = */ true); } void LockImage(const ImageKey aImageKey) { RefPtr<ImageSurfaceCache> cache = GetImageCache(aImageKey); Loading Loading @@ -1039,7 +1070,8 @@ SurfaceCache::Insert(NotNull<ISurfaceProvider*> aProvider, MutexAutoLock lock(sInstance->GetMutex()); Cost cost = aProvider->LogicalSizeInBytes(); return sInstance->Insert(aProvider.get(), cost, aImageKey, aSurfaceKey); return sInstance->Insert(aProvider.get(), cost, aImageKey, aSurfaceKey, /* aSetAvailable = */ false); } /* static */ InsertOutcome Loading @@ -1051,7 +1083,8 @@ SurfaceCache::InsertPlaceholder(const ImageKey aImageKey, } MutexAutoLock lock(sInstance->GetMutex()); return sInstance->Insert(nullptr, sPlaceholderCost, aImageKey, aSurfaceKey); return sInstance->Insert(nullptr, sPlaceholderCost, aImageKey, aSurfaceKey, /* aSetAvailable = */ false); } /* static */ bool Loading @@ -1075,6 +1108,19 @@ SurfaceCache::CanHold(size_t aSize) return sInstance->CanHold(aSize); } /* static */ void SurfaceCache::SurfaceAvailable(NotNull<ISurfaceProvider*> aProvider, const ImageKey aImageKey, const SurfaceKey& aSurfaceKey) { if (!sInstance) { return; } MutexAutoLock lock(sInstance->GetMutex()); sInstance->SurfaceAvailable(aProvider, aImageKey, aSurfaceKey); } /* static */ void SurfaceCache::LockImage(Image* aImageKey) { Loading image/SurfaceCache.h +62 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ namespace image { class Image; class ISurfaceProvider; class LookupResult; class SurfaceCacheImpl; struct SurfaceMemoryCounter; /* Loading Loading @@ -118,6 +119,36 @@ VectorSurfaceKey(const gfx::IntSize& aSize, return SurfaceKey(aSize, aSVGContext, aAnimationTime, DefaultSurfaceFlags()); } /** * AvailabilityState is used to track whether an ISurfaceProvider has a surface * available or is just a placeholder. * * To ensure that availability changes are atomic (and especially that internal * SurfaceCache code doesn't have to deal with asynchronous availability * changes), an ISurfaceProvider which starts as a placeholder can only reveal * the fact that it now has a surface available via a call to * SurfaceCache::SurfaceAvailable(). */ class AvailabilityState { public: static AvailabilityState StartAvailable() { return AvailabilityState(true); } static AvailabilityState StartAsPlaceholder() { return AvailabilityState(false); } bool IsAvailable() const { return mIsAvailable; } bool IsPlaceholder() const { return !mIsAvailable; } private: friend class SurfaceCacheImpl; explicit AvailabilityState(bool aIsAvailable) : mIsAvailable(aIsAvailable) { } void SetAvailable() { mIsAvailable = true; } bool mIsAvailable; }; enum class InsertOutcome : uint8_t { SUCCESS, // Success (but see Insert documentation). FAILURE, // Couldn't insert (e.g., for capacity reasons). Loading Loading @@ -283,6 +314,37 @@ struct SurfaceCache static InsertOutcome InsertPlaceholder(const ImageKey aImageKey, const SurfaceKey& aSurfaceKey); /** * Mark the cache entry @aProvider as having an available surface. This turns * a placeholder cache entry into a normal cache entry. The cache entry * becomes locked if the associated image is locked; otherwise, it starts in * the unlocked state. * * If the cache entry containing @aProvider has already been evicted from the * surface cache, this function has no effect. * * It's illegal to call this function if @aProvider is not a placeholder; by * definition, non-placeholder ISurfaceProviders should have a surface * available already. * * XXX(seth): We're currently in a transitional state where two notions of * placeholder exist: the old one (placeholders are an "empty" cache entry * inserted via InsertPlaceholder(), which then gets replaced by inserting a * real cache entry with the same keys via Insert()) and the new one (where * the same cache entry, inserted via Insert(), starts in a placeholder state * and then transitions to being a normal cache entry via this function). The * old mechanism will be removed in bug 1292392. * * @param aProvider The cache entry that now has a surface available. * @param aImageKey Key data identifying which image the cache entry * belongs to. * @param aSurfaceKey Key data which uniquely identifies the requested * cache entry. */ static void SurfaceAvailable(NotNull<ISurfaceProvider*> aProvider, const ImageKey aImageKey, const SurfaceKey& aSurfaceKey); /** * Checks if a surface of a given size could possibly be stored in the cache. * If CanHold() returns false, Insert() will always fail to insert the Loading Loading
image/ISurfaceProvider.h +17 −7 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ #include "mozilla/gfx/2D.h" #include "imgFrame.h" #include "SurfaceCache.h" namespace mozilla { namespace image { Loading @@ -37,11 +38,6 @@ public: /// @return a drawable reference to a surface. virtual DrawableFrameRef DrawableRef() = 0; /// @return true if this ISurfaceProvider is acting as a placeholder, which is /// to say that it doesn't have a surface and hence can't return a /// non-empty DrawableFrameRef yet, but it will be able to in the future. virtual bool IsPlaceholder() const = 0; /// @return true if DrawableRef() will return a completely decoded surface. virtual bool IsFinished() const = 0; Loading @@ -57,8 +53,22 @@ public: /// overhead is ignored. virtual size_t LogicalSizeInBytes() const = 0; /// @return the availability state of this ISurfaceProvider, which indicates /// whether DrawableRef() could successfully return a surface. Should only be /// called from SurfaceCache code as it relies on SurfaceCache for /// synchronization. AvailabilityState& Availability() { return mAvailability; } const AvailabilityState& Availability() const { return mAvailability; } protected: explicit ISurfaceProvider(AvailabilityState aAvailability) : mAvailability(aAvailability) { } virtual ~ISurfaceProvider() { } private: AvailabilityState mAvailability; }; /** Loading @@ -70,11 +80,11 @@ public: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SimpleSurfaceProvider, override) explicit SimpleSurfaceProvider(NotNull<imgFrame*> aSurface) : mSurface(aSurface) : ISurfaceProvider(AvailabilityState::StartAvailable()) , mSurface(aSurface) { } DrawableFrameRef DrawableRef() override { return mSurface->DrawableRef(); } bool IsPlaceholder() const override { return false; } bool IsFinished() const override { return mSurface->IsFinished(); } bool IsLocked() const override { return bool(mLockRef); } Loading
image/SurfaceCache.cpp +52 −6 Original line number Diff line number Diff line Loading @@ -71,6 +71,8 @@ typedef size_t Cost; // Placeholders do not have surfaces, but need to be given a trivial cost for // our invariants to hold. // XXX(seth): This is only true of old-style placeholders inserted via // InsertPlaceholder(). static const Cost sPlaceholderCost = 1; static Cost Loading Loading @@ -141,8 +143,8 @@ public: , mImageKey(aImageKey) , mSurfaceKey(aSurfaceKey) { MOZ_ASSERT(!IsPlaceholder() || mCost == sPlaceholderCost, "Placeholder should have trivial cost"); MOZ_ASSERT(aProvider || mCost == sPlaceholderCost, "Old-style placeholders should have trivial cost"); MOZ_ASSERT(mImageKey, "Must have a valid image key"); } Loading @@ -168,7 +170,11 @@ public: mProvider->SetLocked(aLocked); } bool IsPlaceholder() const { return !mProvider || mProvider->IsPlaceholder(); } bool IsPlaceholder() const { return !mProvider || mProvider->Availability().IsPlaceholder(); } bool IsLocked() const { return !IsPlaceholder() && mProvider->IsLocked(); } ImageKey GetImageKey() const { return mImageKey; } Loading Loading @@ -449,7 +455,8 @@ public: InsertOutcome Insert(ISurfaceProvider* aProvider, const Cost aCost, const ImageKey aImageKey, const SurfaceKey& aSurfaceKey) const SurfaceKey& aSurfaceKey, bool aSetAvailable) { // If this is a duplicate surface, refuse to replace the original. // XXX(seth): Calling Lookup() and then RemoveEntry() does the lookup Loading Loading @@ -490,6 +497,11 @@ public: mImageCaches.Put(aImageKey, cache); } // If we were asked to mark the cache entry available, do so. if (aSetAvailable) { aProvider->Availability().SetAvailable(); } RefPtr<CachedSurface> surface = new CachedSurface(aProvider, aCost, aImageKey, aSurfaceKey); Loading Loading @@ -667,6 +679,25 @@ public: return size_t(mMaxCost); } void SurfaceAvailable(NotNull<ISurfaceProvider*> aProvider, const ImageKey aImageKey, const SurfaceKey& aSurfaceKey) { if (!aProvider->Availability().IsPlaceholder()) { MOZ_ASSERT_UNREACHABLE("Calling SurfaceAvailable on non-placeholder"); return; } // Reinsert the provider, requesting that Insert() mark it available. This // may or may not succeed, depending on whether some other decoder has // beaten us to the punch and inserted a non-placeholder version of this // surface first, but it's fine either way. // XXX(seth): This could be implemented more efficiently; we should be able // to just update our data structures without reinserting. Cost cost = aProvider->LogicalSizeInBytes(); Insert(aProvider, cost, aImageKey, aSurfaceKey, /* aSetAvailable = */ true); } void LockImage(const ImageKey aImageKey) { RefPtr<ImageSurfaceCache> cache = GetImageCache(aImageKey); Loading Loading @@ -1039,7 +1070,8 @@ SurfaceCache::Insert(NotNull<ISurfaceProvider*> aProvider, MutexAutoLock lock(sInstance->GetMutex()); Cost cost = aProvider->LogicalSizeInBytes(); return sInstance->Insert(aProvider.get(), cost, aImageKey, aSurfaceKey); return sInstance->Insert(aProvider.get(), cost, aImageKey, aSurfaceKey, /* aSetAvailable = */ false); } /* static */ InsertOutcome Loading @@ -1051,7 +1083,8 @@ SurfaceCache::InsertPlaceholder(const ImageKey aImageKey, } MutexAutoLock lock(sInstance->GetMutex()); return sInstance->Insert(nullptr, sPlaceholderCost, aImageKey, aSurfaceKey); return sInstance->Insert(nullptr, sPlaceholderCost, aImageKey, aSurfaceKey, /* aSetAvailable = */ false); } /* static */ bool Loading @@ -1075,6 +1108,19 @@ SurfaceCache::CanHold(size_t aSize) return sInstance->CanHold(aSize); } /* static */ void SurfaceCache::SurfaceAvailable(NotNull<ISurfaceProvider*> aProvider, const ImageKey aImageKey, const SurfaceKey& aSurfaceKey) { if (!sInstance) { return; } MutexAutoLock lock(sInstance->GetMutex()); sInstance->SurfaceAvailable(aProvider, aImageKey, aSurfaceKey); } /* static */ void SurfaceCache::LockImage(Image* aImageKey) { Loading
image/SurfaceCache.h +62 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ namespace image { class Image; class ISurfaceProvider; class LookupResult; class SurfaceCacheImpl; struct SurfaceMemoryCounter; /* Loading Loading @@ -118,6 +119,36 @@ VectorSurfaceKey(const gfx::IntSize& aSize, return SurfaceKey(aSize, aSVGContext, aAnimationTime, DefaultSurfaceFlags()); } /** * AvailabilityState is used to track whether an ISurfaceProvider has a surface * available or is just a placeholder. * * To ensure that availability changes are atomic (and especially that internal * SurfaceCache code doesn't have to deal with asynchronous availability * changes), an ISurfaceProvider which starts as a placeholder can only reveal * the fact that it now has a surface available via a call to * SurfaceCache::SurfaceAvailable(). */ class AvailabilityState { public: static AvailabilityState StartAvailable() { return AvailabilityState(true); } static AvailabilityState StartAsPlaceholder() { return AvailabilityState(false); } bool IsAvailable() const { return mIsAvailable; } bool IsPlaceholder() const { return !mIsAvailable; } private: friend class SurfaceCacheImpl; explicit AvailabilityState(bool aIsAvailable) : mIsAvailable(aIsAvailable) { } void SetAvailable() { mIsAvailable = true; } bool mIsAvailable; }; enum class InsertOutcome : uint8_t { SUCCESS, // Success (but see Insert documentation). FAILURE, // Couldn't insert (e.g., for capacity reasons). Loading Loading @@ -283,6 +314,37 @@ struct SurfaceCache static InsertOutcome InsertPlaceholder(const ImageKey aImageKey, const SurfaceKey& aSurfaceKey); /** * Mark the cache entry @aProvider as having an available surface. This turns * a placeholder cache entry into a normal cache entry. The cache entry * becomes locked if the associated image is locked; otherwise, it starts in * the unlocked state. * * If the cache entry containing @aProvider has already been evicted from the * surface cache, this function has no effect. * * It's illegal to call this function if @aProvider is not a placeholder; by * definition, non-placeholder ISurfaceProviders should have a surface * available already. * * XXX(seth): We're currently in a transitional state where two notions of * placeholder exist: the old one (placeholders are an "empty" cache entry * inserted via InsertPlaceholder(), which then gets replaced by inserting a * real cache entry with the same keys via Insert()) and the new one (where * the same cache entry, inserted via Insert(), starts in a placeholder state * and then transitions to being a normal cache entry via this function). The * old mechanism will be removed in bug 1292392. * * @param aProvider The cache entry that now has a surface available. * @param aImageKey Key data identifying which image the cache entry * belongs to. * @param aSurfaceKey Key data which uniquely identifies the requested * cache entry. */ static void SurfaceAvailable(NotNull<ISurfaceProvider*> aProvider, const ImageKey aImageKey, const SurfaceKey& aSurfaceKey); /** * Checks if a surface of a given size could possibly be stored in the cache. * If CanHold() returns false, Insert() will always fail to insert the Loading