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