summaryrefslogtreecommitdiffstats
path: root/gfx/thebes/gfxGradientCache.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /gfx/thebes/gfxGradientCache.cpp
parentInitial commit. (diff)
downloadfirefox-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.cpp214
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