gfx/thebes/gfxGradientCache.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:71a752068efe
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 #include "mozilla/gfx/2D.h"
7 #include "nsTArray.h"
8 #include "pldhash.h"
9 #include "nsExpirationTracker.h"
10 #include "nsClassHashtable.h"
11 #include "mozilla/Telemetry.h"
12 #include "gfxGradientCache.h"
13 #include <time.h>
14
15 namespace mozilla {
16 namespace gfx {
17
18 using namespace mozilla;
19
20 struct GradientCacheKey : public PLDHashEntryHdr {
21 typedef const GradientCacheKey& KeyType;
22 typedef const GradientCacheKey* KeyTypePointer;
23 enum { ALLOW_MEMMOVE = true };
24 const nsTArray<GradientStop> mStops;
25 ExtendMode mExtend;
26 BackendType mBackendType;
27
28 GradientCacheKey(const nsTArray<GradientStop>& aStops, ExtendMode aExtend, BackendType aBackendType)
29 : mStops(aStops), mExtend(aExtend), mBackendType(aBackendType)
30 { }
31
32 GradientCacheKey(const GradientCacheKey* aOther)
33 : mStops(aOther->mStops), mExtend(aOther->mExtend), mBackendType(aOther->mBackendType)
34 { }
35
36 union FloatUint32
37 {
38 float f;
39 uint32_t u;
40 };
41
42 static PLDHashNumber
43 HashKey(const KeyTypePointer aKey)
44 {
45 PLDHashNumber hash = 0;
46 FloatUint32 convert;
47 hash = AddToHash(hash, int(aKey->mBackendType));
48 hash = AddToHash(hash, int(aKey->mExtend));
49 for (uint32_t i = 0; i < aKey->mStops.Length(); i++) {
50 hash = AddToHash(hash, aKey->mStops[i].color.ToABGR());
51 // Use the float bits as hash, except for the cases of 0.0 and -0.0 which both map to 0
52 convert.f = aKey->mStops[i].offset;
53 hash = AddToHash(hash, convert.f ? convert.u : 0);
54 }
55 return hash;
56 }
57
58 bool KeyEquals(KeyTypePointer aKey) const
59 {
60 bool sameStops = true;
61 if (aKey->mStops.Length() != mStops.Length()) {
62 sameStops = false;
63 } else {
64 for (uint32_t i = 0; i < mStops.Length(); i++) {
65 if (mStops[i].color.ToABGR() != aKey->mStops[i].color.ToABGR() ||
66 mStops[i].offset != aKey->mStops[i].offset) {
67 sameStops = false;
68 break;
69 }
70 }
71 }
72
73 return sameStops &&
74 (aKey->mBackendType == mBackendType) &&
75 (aKey->mExtend == mExtend);
76 }
77 static KeyTypePointer KeyToPointer(KeyType aKey)
78 {
79 return &aKey;
80 }
81 };
82
83 /**
84 * This class is what is cached. It need to be allocated in an object separated
85 * to the cache entry to be able to be tracked by the nsExpirationTracker.
86 * */
87 struct GradientCacheData {
88 GradientCacheData(GradientStops* aStops, const GradientCacheKey& aKey)
89 : mStops(aStops),
90 mKey(aKey)
91 {}
92
93 GradientCacheData(const GradientCacheData& aOther)
94 : mStops(aOther.mStops),
95 mKey(aOther.mKey)
96 { }
97
98 nsExpirationState *GetExpirationState() {
99 return &mExpirationState;
100 }
101
102 nsExpirationState mExpirationState;
103 const RefPtr<GradientStops> mStops;
104 GradientCacheKey mKey;
105 };
106
107 /**
108 * This class implements a cache with no maximum size, that retains the
109 * gfxPatterns used to draw the gradients.
110 *
111 * The key is the nsStyleGradient that defines the gradient, and the size of the
112 * gradient.
113 *
114 * The value is the gfxPattern, and whether or not we perform an optimization
115 * based on the actual gradient property.
116 *
117 * An entry stays in the cache as long as it is used often. As long as a cache
118 * entry is in the cache, all the references it has are guaranteed to be valid:
119 * the nsStyleRect for the key, the gfxPattern for the value.
120 */
121 class GradientCache MOZ_FINAL : public nsExpirationTracker<GradientCacheData,4>
122 {
123 public:
124 GradientCache()
125 : nsExpirationTracker<GradientCacheData, 4>(MAX_GENERATION_MS)
126 {
127 srand(time(nullptr));
128 mTimerPeriod = rand() % MAX_GENERATION_MS + 1;
129 Telemetry::Accumulate(Telemetry::GRADIENT_RETENTION_TIME, mTimerPeriod);
130 }
131
132 virtual void NotifyExpired(GradientCacheData* aObject)
133 {
134 // This will free the gfxPattern.
135 RemoveObject(aObject);
136 mHashEntries.Remove(aObject->mKey);
137 }
138
139 GradientCacheData* Lookup(const nsTArray<GradientStop>& aStops, ExtendMode aExtend, BackendType aBackendType)
140 {
141 GradientCacheData* gradient =
142 mHashEntries.Get(GradientCacheKey(aStops, aExtend, aBackendType));
143
144 if (gradient) {
145 MarkUsed(gradient);
146 }
147
148 return gradient;
149 }
150
151 // Returns true if we successfully register the gradient in the cache, false
152 // otherwise.
153 bool RegisterEntry(GradientCacheData* aValue)
154 {
155 nsresult rv = AddObject(aValue);
156 if (NS_FAILED(rv)) {
157 // We are OOM, and we cannot track this object. We don't want stall
158 // entries in the hash table (since the expiration tracker is responsible
159 // for removing the cache entries), so we avoid putting that entry in the
160 // table, which is a good things considering we are short on memory
161 // anyway, we probably don't want to retain things.
162 return false;
163 }
164 mHashEntries.Put(aValue->mKey, aValue);
165 return true;
166 }
167
168 protected:
169 uint32_t mTimerPeriod;
170 static const uint32_t MAX_GENERATION_MS = 10000;
171 /**
172 * FIXME use nsTHashtable to avoid duplicating the GradientCacheKey.
173 * https://bugzilla.mozilla.org/show_bug.cgi?id=761393#c47
174 */
175 nsClassHashtable<GradientCacheKey, GradientCacheData> mHashEntries;
176 };
177
178 static GradientCache* gGradientCache = nullptr;
179
180 GradientStops *
181 gfxGradientCache::GetGradientStops(DrawTarget *aDT, nsTArray<GradientStop>& aStops, ExtendMode aExtend)
182 {
183 if (!gGradientCache) {
184 gGradientCache = new GradientCache();
185 }
186 GradientCacheData* cached = gGradientCache->Lookup(aStops, aExtend, aDT->GetType());
187 return cached ? cached->mStops : nullptr;
188 }
189
190 GradientStops *
191 gfxGradientCache::GetOrCreateGradientStops(DrawTarget *aDT, nsTArray<GradientStop>& aStops, ExtendMode aExtend)
192 {
193 RefPtr<GradientStops> gs = GetGradientStops(aDT, aStops, aExtend);
194 if (!gs) {
195 gs = aDT->CreateGradientStops(aStops.Elements(), aStops.Length(), aExtend);
196 if (!gs) {
197 return nullptr;
198 }
199 GradientCacheData *cached = new GradientCacheData(gs, GradientCacheKey(aStops, aExtend, aDT->GetType()));
200 if (!gGradientCache->RegisterEntry(cached)) {
201 delete cached;
202 }
203 }
204 return gs;
205 }
206
207 void
208 gfxGradientCache::Shutdown()
209 {
210 delete gGradientCache;
211 gGradientCache = nullptr;
212 }
213
214 }
215 }

mercurial