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 : #include "AnimationSurfaceProvider.h"
7 :
8 : #include "gfxPrefs.h"
9 : #include "nsProxyRelease.h"
10 :
11 : #include "Decoder.h"
12 :
13 : using namespace mozilla::gfx;
14 :
15 : namespace mozilla {
16 : namespace image {
17 :
18 2 : AnimationSurfaceProvider::AnimationSurfaceProvider(NotNull<RasterImage*> aImage,
19 : const SurfaceKey& aSurfaceKey,
20 2 : NotNull<Decoder*> aDecoder)
21 2 : : ISurfaceProvider(ImageKey(aImage.get()), aSurfaceKey,
22 : AvailabilityState::StartAsPlaceholder())
23 2 : , mImage(aImage.get())
24 : , mDecodingMutex("AnimationSurfaceProvider::mDecoder")
25 2 : , mDecoder(aDecoder.get())
26 8 : , mFramesMutex("AnimationSurfaceProvider::mFrames")
27 : {
28 2 : MOZ_ASSERT(!mDecoder->IsMetadataDecode(),
29 : "Use MetadataDecodingTask for metadata decodes");
30 2 : MOZ_ASSERT(!mDecoder->IsFirstFrameDecode(),
31 : "Use DecodedSurfaceProvider for single-frame image decodes");
32 2 : }
33 :
34 0 : AnimationSurfaceProvider::~AnimationSurfaceProvider()
35 : {
36 0 : DropImageReference();
37 0 : }
38 :
39 : void
40 2 : AnimationSurfaceProvider::DropImageReference()
41 : {
42 2 : if (!mImage) {
43 0 : return; // Nothing to do.
44 : }
45 :
46 : // RasterImage objects need to be destroyed on the main thread. We also need
47 : // to destroy them asynchronously, because if our surface cache entry is
48 : // destroyed and we were the only thing keeping |mImage| alive, RasterImage's
49 : // destructor may call into the surface cache while whatever code caused us to
50 : // get evicted is holding the surface cache lock, causing deadlock.
51 4 : RefPtr<RasterImage> image = mImage;
52 2 : mImage = nullptr;
53 2 : NS_ReleaseOnMainThreadSystemGroup(image.forget(), /* aAlwaysProxy = */ true);
54 : }
55 :
56 : DrawableFrameRef
57 156 : AnimationSurfaceProvider::DrawableRef(size_t aFrame)
58 : {
59 312 : MutexAutoLock lock(mFramesMutex);
60 :
61 156 : if (Availability().IsPlaceholder()) {
62 0 : MOZ_ASSERT_UNREACHABLE("Calling DrawableRef() on a placeholder");
63 : return DrawableFrameRef();
64 : }
65 :
66 156 : if (mFrames.IsEmpty()) {
67 0 : MOZ_ASSERT_UNREACHABLE("Calling DrawableRef() when we have no frames");
68 : return DrawableFrameRef();
69 : }
70 :
71 : // If we don't have that frame, return an empty frame ref.
72 156 : if (aFrame >= mFrames.Length()) {
73 0 : return DrawableFrameRef();
74 : }
75 :
76 : // We've got the requested frame. Return it.
77 156 : MOZ_ASSERT(mFrames[aFrame]);
78 156 : return mFrames[aFrame]->DrawableRef();
79 : }
80 :
81 : bool
82 0 : AnimationSurfaceProvider::IsFinished() const
83 : {
84 0 : MutexAutoLock lock(mFramesMutex);
85 :
86 0 : if (Availability().IsPlaceholder()) {
87 0 : MOZ_ASSERT_UNREACHABLE("Calling IsFinished() on a placeholder");
88 : return false;
89 : }
90 :
91 0 : if (mFrames.IsEmpty()) {
92 0 : MOZ_ASSERT_UNREACHABLE("Calling IsFinished() when we have no frames");
93 : return false;
94 : }
95 :
96 : // As long as we have at least one finished frame, we're finished.
97 0 : return mFrames[0]->IsFinished();
98 : }
99 :
100 : size_t
101 10 : AnimationSurfaceProvider::LogicalSizeInBytes() const
102 : {
103 : // When decoding animated images, we need at most three live surfaces: the
104 : // composited surface, the previous composited surface for
105 : // DisposalMethod::RESTORE_PREVIOUS, and the surface we're currently decoding
106 : // into. The composited surfaces are always BGRA. Although the surface we're
107 : // decoding into may be paletted, and may be smaller than the real size of the
108 : // image, we assume the worst case here.
109 : // XXX(seth): Note that this is actually not accurate yet; we're storing the
110 : // full sequence of frames, not just the three live surfaces mentioned above.
111 : // Unfortunately there's no way to know in advance how many frames an
112 : // animation has, so we really can't do better here. This will become correct
113 : // once bug 1289954 is complete.
114 10 : IntSize size = GetSurfaceKey().Size();
115 10 : return 3 * size.width * size.height * sizeof(uint32_t);
116 : }
117 :
118 : void
119 0 : AnimationSurfaceProvider::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
120 : size_t& aHeapSizeOut,
121 : size_t& aNonHeapSizeOut,
122 : size_t& aSharedHandlesOut)
123 : {
124 : // Note that the surface cache lock is already held here, and then we acquire
125 : // mFramesMutex. For this method, this ordering is unavoidable, which means
126 : // that we must be careful to always use the same ordering elsewhere.
127 0 : MutexAutoLock lock(mFramesMutex);
128 :
129 0 : for (const RawAccessFrameRef& frame : mFrames) {
130 0 : frame->AddSizeOfExcludingThis(aMallocSizeOf, aHeapSizeOut,
131 0 : aNonHeapSizeOut, aSharedHandlesOut);
132 : }
133 0 : }
134 :
135 : void
136 2 : AnimationSurfaceProvider::Run()
137 : {
138 4 : MutexAutoLock lock(mDecodingMutex);
139 :
140 2 : if (!mDecoder || !mImage) {
141 0 : MOZ_ASSERT_UNREACHABLE("Running after decoding finished?");
142 : return;
143 : }
144 :
145 : while (true) {
146 : // Run the decoder.
147 70 : LexerResult result = mDecoder->Decode(WrapNotNull(this));
148 :
149 36 : if (result.is<TerminalState>()) {
150 : // We may have a new frame now, but it's not guaranteed - a decoding
151 : // failure or truncated data may mean that no new frame got produced.
152 : // Since we're not sure, rather than call CheckForNewFrameAtYield() here
153 : // we call CheckForNewFrameAtTerminalState(), which handles both of these
154 : // possibilities.
155 2 : CheckForNewFrameAtTerminalState();
156 :
157 : // We're done!
158 2 : FinishDecoding();
159 2 : return;
160 : }
161 :
162 : // Notify for the progress we've made so far.
163 34 : if (mDecoder->HasProgress()) {
164 34 : NotifyProgress(WrapNotNull(mImage), WrapNotNull(mDecoder));
165 : }
166 :
167 34 : if (result == LexerResult(Yield::NEED_MORE_DATA)) {
168 : // We can't make any more progress right now. The decoder itself will ensure
169 : // that we get reenqueued when more data is available; just return for now.
170 0 : return;
171 : }
172 :
173 : // There's new output available - a new frame! Grab it.
174 34 : MOZ_ASSERT(result == LexerResult(Yield::OUTPUT_AVAILABLE));
175 34 : CheckForNewFrameAtYield();
176 34 : }
177 : }
178 :
179 : void
180 34 : AnimationSurfaceProvider::CheckForNewFrameAtYield()
181 : {
182 34 : mDecodingMutex.AssertCurrentThreadOwns();
183 34 : MOZ_ASSERT(mDecoder);
184 :
185 34 : bool justGotFirstFrame = false;
186 :
187 : {
188 68 : MutexAutoLock lock(mFramesMutex);
189 :
190 : // Try to get the new frame from the decoder.
191 68 : RawAccessFrameRef frame = mDecoder->GetCurrentFrameRef();
192 34 : if (!frame) {
193 0 : MOZ_ASSERT_UNREACHABLE("Decoder yielded but didn't produce a frame?");
194 : return;
195 : }
196 :
197 : // We should've gotten a different frame than last time.
198 34 : MOZ_ASSERT_IF(!mFrames.IsEmpty(),
199 : mFrames.LastElement().get() != frame.get());
200 :
201 : // Append the new frame to the list.
202 34 : mFrames.AppendElement(Move(frame));
203 :
204 34 : if (mFrames.Length() == 1) {
205 2 : justGotFirstFrame = true;
206 : }
207 : }
208 :
209 34 : if (justGotFirstFrame) {
210 2 : AnnounceSurfaceAvailable();
211 : }
212 34 : }
213 :
214 : void
215 2 : AnimationSurfaceProvider::CheckForNewFrameAtTerminalState()
216 : {
217 2 : mDecodingMutex.AssertCurrentThreadOwns();
218 2 : MOZ_ASSERT(mDecoder);
219 :
220 2 : bool justGotFirstFrame = false;
221 :
222 : {
223 4 : MutexAutoLock lock(mFramesMutex);
224 :
225 4 : RawAccessFrameRef frame = mDecoder->GetCurrentFrameRef();
226 2 : if (!frame) {
227 0 : return;
228 : }
229 :
230 2 : if (!mFrames.IsEmpty() && mFrames.LastElement().get() == frame.get()) {
231 0 : return; // We already have this one.
232 : }
233 :
234 : // Append the new frame to the list.
235 2 : mFrames.AppendElement(Move(frame));
236 :
237 2 : if (mFrames.Length() == 1) {
238 0 : justGotFirstFrame = true;
239 : }
240 : }
241 :
242 2 : if (justGotFirstFrame) {
243 0 : AnnounceSurfaceAvailable();
244 : }
245 : }
246 :
247 : void
248 2 : AnimationSurfaceProvider::AnnounceSurfaceAvailable()
249 : {
250 2 : mFramesMutex.AssertNotCurrentThreadOwns();
251 2 : MOZ_ASSERT(mImage);
252 :
253 : // We just got the first frame; let the surface cache know. We deliberately do
254 : // this outside of mFramesMutex to avoid a potential deadlock with
255 : // AddSizeOfExcludingThis(), since otherwise we'd be acquiring mFramesMutex
256 : // and then the surface cache lock, while the memory reporting code would
257 : // acquire the surface cache lock and then mFramesMutex.
258 2 : SurfaceCache::SurfaceAvailable(WrapNotNull(this));
259 2 : }
260 :
261 : void
262 2 : AnimationSurfaceProvider::FinishDecoding()
263 : {
264 2 : mDecodingMutex.AssertCurrentThreadOwns();
265 2 : MOZ_ASSERT(mImage);
266 2 : MOZ_ASSERT(mDecoder);
267 :
268 : // Send notifications.
269 2 : NotifyDecodeComplete(WrapNotNull(mImage), WrapNotNull(mDecoder));
270 :
271 : // Destroy our decoder; we don't need it anymore.
272 2 : mDecoder = nullptr;
273 :
274 : // We don't need a reference to our image anymore, either, and we don't want
275 : // one. We may be stored in the surface cache for a long time after decoding
276 : // finishes. If we don't drop our reference to the image, we'll end up
277 : // keeping it alive as long as we remain in the surface cache, which could
278 : // greatly extend the image's lifetime - in fact, if the image isn't
279 : // discardable, it'd result in a leak!
280 2 : DropImageReference();
281 2 : }
282 :
283 : bool
284 0 : AnimationSurfaceProvider::ShouldPreferSyncRun() const
285 : {
286 0 : MutexAutoLock lock(mDecodingMutex);
287 0 : MOZ_ASSERT(mDecoder);
288 :
289 0 : return mDecoder->ShouldSyncDecode(gfxPrefs::ImageMemDecodeBytesAtATime());
290 : }
291 :
292 : } // namespace image
293 : } // namespace mozilla
|