diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /gfx/thebes/gfxGradientCache.cpp | |
parent | Initial commit. (diff) | |
download | firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'gfx/thebes/gfxGradientCache.cpp')
-rw-r--r-- | gfx/thebes/gfxGradientCache.cpp | 214 |
1 files changed, 214 insertions, 0 deletions
diff --git a/gfx/thebes/gfxGradientCache.cpp b/gfx/thebes/gfxGradientCache.cpp new file mode 100644 index 0000000000..2faf7b9a1f --- /dev/null +++ b/gfx/thebes/gfxGradientCache.cpp @@ -0,0 +1,214 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/gfx/2D.h" +#include "nsTArray.h" +#include "PLDHashTable.h" +#include "nsExpirationTracker.h" +#include "nsClassHashtable.h" +#include "gfxGradientCache.h" +#include <time.h> + +namespace mozilla { +namespace gfx { + +using namespace mozilla; + +struct GradientCacheKey : public PLDHashEntryHdr { + typedef const GradientCacheKey& KeyType; + typedef const GradientCacheKey* KeyTypePointer; + enum { ALLOW_MEMMOVE = true }; + const CopyableTArray<GradientStop> mStops; + ExtendMode mExtend; + BackendType mBackendType; + + GradientCacheKey(const nsTArray<GradientStop>& aStops, ExtendMode aExtend, + BackendType aBackendType) + : mStops(aStops), mExtend(aExtend), mBackendType(aBackendType) {} + + explicit GradientCacheKey(const GradientCacheKey* aOther) + : mStops(aOther->mStops), + mExtend(aOther->mExtend), + mBackendType(aOther->mBackendType) {} + + GradientCacheKey(GradientCacheKey&& aOther) = default; + + union FloatUint32 { + float f; + uint32_t u; + }; + + static PLDHashNumber HashKey(const KeyTypePointer aKey) { + PLDHashNumber hash = 0; + FloatUint32 convert; + hash = AddToHash(hash, int(aKey->mBackendType)); + hash = AddToHash(hash, int(aKey->mExtend)); + for (uint32_t i = 0; i < aKey->mStops.Length(); i++) { + hash = AddToHash(hash, aKey->mStops[i].color.ToABGR()); + // Use the float bits as hash, except for the cases of 0.0 and -0.0 which + // both map to 0 + convert.f = aKey->mStops[i].offset; + hash = AddToHash(hash, convert.f ? convert.u : 0); + } + return hash; + } + + bool KeyEquals(KeyTypePointer aKey) const { + bool sameStops = true; + if (aKey->mStops.Length() != mStops.Length()) { + sameStops = false; + } else { + for (uint32_t i = 0; i < mStops.Length(); i++) { + if (mStops[i].color.ToABGR() != aKey->mStops[i].color.ToABGR() || + mStops[i].offset != aKey->mStops[i].offset) { + sameStops = false; + break; + } + } + } + + return sameStops && (aKey->mBackendType == mBackendType) && + (aKey->mExtend == mExtend); + } + static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; } +}; + +/** + * This class is what is cached. It need to be allocated in an object separated + * to the cache entry to be able to be tracked by the nsExpirationTracker. + * */ +struct GradientCacheData { + GradientCacheData(GradientStops* aStops, GradientCacheKey&& aKey) + : mStops(aStops), mKey(std::move(aKey)) {} + + GradientCacheData(GradientCacheData&& aOther) = default; + + nsExpirationState* GetExpirationState() { return &mExpirationState; } + + nsExpirationState mExpirationState; + const RefPtr<GradientStops> mStops; + GradientCacheKey mKey; +}; + +/** + * This class implements a cache with no maximum size, that retains the + * gfxPatterns used to draw the gradients. + * + * The key is the nsStyleGradient that defines the gradient, and the size of the + * gradient. + * + * The value is the gfxPattern, and whether or not we perform an optimization + * based on the actual gradient property. + * + * An entry stays in the cache as long as it is used often. As long as a cache + * entry is in the cache, all the references it has are guaranteed to be valid: + * the nsStyleRect for the key, the gfxPattern for the value. + */ +class GradientCache final : public nsExpirationTracker<GradientCacheData, 4> { + public: + GradientCache() + : nsExpirationTracker<GradientCacheData, 4>(MAX_GENERATION_MS, + "GradientCache") { + srand(time(nullptr)); + } + + virtual void NotifyExpired(GradientCacheData* aObject) override { + // This will free the gfxPattern. + RemoveObject(aObject); + mHashEntries.Remove(aObject->mKey); + } + + GradientCacheData* Lookup(const nsTArray<GradientStop>& aStops, + ExtendMode aExtend, BackendType aBackendType) { + GradientCacheData* gradient = + mHashEntries.Get(GradientCacheKey(aStops, aExtend, aBackendType)); + + if (gradient) { + MarkUsed(gradient); + } + + return gradient; + } + + // Returns true if we successfully register the gradient in the cache, false + // otherwise. + bool RegisterEntry(GradientCacheData* aValue) { + nsresult rv = AddObject(aValue); + if (NS_FAILED(rv)) { + // We are OOM, and we cannot track this object. We don't want stall + // entries in the hash table (since the expiration tracker is responsible + // for removing the cache entries), so we avoid putting that entry in the + // table, which is a good things considering we are short on memory + // anyway, we probably don't want to retain things. + return false; + } + mHashEntries.Put(aValue->mKey, aValue); + return true; + } + + protected: + static const uint32_t MAX_GENERATION_MS = 10000; + /** + * FIXME use nsTHashtable to avoid duplicating the GradientCacheKey. + * https://bugzilla.mozilla.org/show_bug.cgi?id=761393#c47 + */ + nsClassHashtable<GradientCacheKey, GradientCacheData> mHashEntries; +}; + +static GradientCache* gGradientCache = nullptr; + +GradientStops* gfxGradientCache::GetGradientStops( + const DrawTarget* aDT, nsTArray<GradientStop>& aStops, ExtendMode aExtend) { + if (!gGradientCache) { + gGradientCache = new GradientCache(); + } + GradientCacheData* cached = + gGradientCache->Lookup(aStops, aExtend, aDT->GetBackendType()); + if (cached && cached->mStops) { + if (!cached->mStops->IsValid()) { + gGradientCache->NotifyExpired(cached); + } else { + return cached->mStops; + } + } + + return nullptr; +} + +already_AddRefed<GradientStops> gfxGradientCache::GetOrCreateGradientStops( + const DrawTarget* aDT, nsTArray<GradientStop>& aStops, ExtendMode aExtend) { + if (aDT->IsRecording()) { + return aDT->CreateGradientStops(aStops.Elements(), aStops.Length(), + aExtend); + } + + RefPtr<GradientStops> gs = GetGradientStops(aDT, aStops, aExtend); + if (!gs) { + gs = aDT->CreateGradientStops(aStops.Elements(), aStops.Length(), aExtend); + if (!gs) { + return nullptr; + } + GradientCacheData* cached = new GradientCacheData( + gs, GradientCacheKey(aStops, aExtend, aDT->GetBackendType())); + if (!gGradientCache->RegisterEntry(cached)) { + delete cached; + } + } + return gs.forget(); +} + +void gfxGradientCache::PurgeAllCaches() { + if (gGradientCache) { + gGradientCache->AgeAllGenerations(); + } +} + +void gfxGradientCache::Shutdown() { + delete gGradientCache; + gGradientCache = nullptr; +} + +} // namespace gfx +} // namespace mozilla |