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