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 "DecodedSurfaceProvider.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 12 : DecodedSurfaceProvider::DecodedSurfaceProvider(NotNull<RasterImage*> aImage,
19 : const SurfaceKey& aSurfaceKey,
20 12 : NotNull<Decoder*> aDecoder)
21 12 : : ISurfaceProvider(ImageKey(aImage.get()), aSurfaceKey,
22 : AvailabilityState::StartAsPlaceholder())
23 12 : , mImage(aImage.get())
24 : , mMutex("mozilla::image::DecodedSurfaceProvider")
25 36 : , mDecoder(aDecoder.get())
26 : {
27 12 : MOZ_ASSERT(!mDecoder->IsMetadataDecode(),
28 : "Use MetadataDecodingTask for metadata decodes");
29 12 : MOZ_ASSERT(mDecoder->IsFirstFrameDecode(),
30 : "Use AnimationSurfaceProvider for animation decodes");
31 12 : }
32 :
33 0 : DecodedSurfaceProvider::~DecodedSurfaceProvider()
34 : {
35 0 : DropImageReference();
36 0 : }
37 :
38 : void
39 12 : DecodedSurfaceProvider::DropImageReference()
40 : {
41 12 : if (!mImage) {
42 0 : return; // Nothing to do.
43 : }
44 :
45 : // RasterImage objects need to be destroyed on the main thread. We also need
46 : // to destroy them asynchronously, because if our surface cache entry is
47 : // destroyed and we were the only thing keeping |mImage| alive, RasterImage's
48 : // destructor may call into the surface cache while whatever code caused us to
49 : // get evicted is holding the surface cache lock, causing deadlock.
50 24 : RefPtr<RasterImage> image = mImage;
51 12 : mImage = nullptr;
52 12 : NS_ReleaseOnMainThreadSystemGroup(image.forget(), /* aAlwaysProxy = */ true);
53 : }
54 :
55 : DrawableFrameRef
56 35 : DecodedSurfaceProvider::DrawableRef(size_t aFrame)
57 : {
58 35 : MOZ_ASSERT(aFrame == 0,
59 : "Requesting an animation frame from a DecodedSurfaceProvider?");
60 :
61 : // We depend on SurfaceCache::SurfaceAvailable() to provide synchronization
62 : // for methods that touch |mSurface|; after SurfaceAvailable() is called,
63 : // |mSurface| should be non-null and shouldn't be mutated further until we get
64 : // destroyed. That means that the assertions below are very important; we'll
65 : // end up with data races if these assumptions are violated.
66 35 : if (Availability().IsPlaceholder()) {
67 0 : MOZ_ASSERT_UNREACHABLE("Calling DrawableRef() on a placeholder");
68 : return DrawableFrameRef();
69 : }
70 :
71 35 : if (!mSurface) {
72 0 : MOZ_ASSERT_UNREACHABLE("Calling DrawableRef() when we have no surface");
73 : return DrawableFrameRef();
74 : }
75 :
76 35 : return mSurface->DrawableRef();
77 : }
78 :
79 : bool
80 24 : DecodedSurfaceProvider::IsFinished() const
81 : {
82 : // See DrawableRef() for commentary on these assertions.
83 24 : if (Availability().IsPlaceholder()) {
84 0 : MOZ_ASSERT_UNREACHABLE("Calling IsFinished() on a placeholder");
85 : return false;
86 : }
87 :
88 24 : if (!mSurface) {
89 0 : MOZ_ASSERT_UNREACHABLE("Calling IsFinished() when we have no surface");
90 : return false;
91 : }
92 :
93 24 : return mSurface->IsFinished();
94 : }
95 :
96 : void
97 12 : DecodedSurfaceProvider::SetLocked(bool aLocked)
98 : {
99 : // See DrawableRef() for commentary on these assertions.
100 12 : if (Availability().IsPlaceholder()) {
101 0 : MOZ_ASSERT_UNREACHABLE("Calling SetLocked() on a placeholder");
102 : return;
103 : }
104 :
105 12 : if (!mSurface) {
106 0 : MOZ_ASSERT_UNREACHABLE("Calling SetLocked() when we have no surface");
107 : return;
108 : }
109 :
110 12 : if (aLocked == IsLocked()) {
111 0 : return; // Nothing to do.
112 : }
113 :
114 : // If we're locked, hold a DrawableFrameRef to |mSurface|, which will keep any
115 : // volatile buffer it owns in memory.
116 24 : mLockRef = aLocked ? mSurface->DrawableRef()
117 12 : : DrawableFrameRef();
118 : }
119 :
120 : size_t
121 60 : DecodedSurfaceProvider::LogicalSizeInBytes() const
122 : {
123 : // Single frame images are always 32bpp.
124 60 : IntSize size = GetSurfaceKey().Size();
125 60 : return size.width * size.height * sizeof(uint32_t);
126 : }
127 :
128 : void
129 12 : DecodedSurfaceProvider::Run()
130 : {
131 12 : MutexAutoLock lock(mMutex);
132 :
133 12 : if (!mDecoder || !mImage) {
134 0 : MOZ_ASSERT_UNREACHABLE("Running after decoding finished?");
135 : return;
136 : }
137 :
138 : // Run the decoder.
139 12 : LexerResult result = mDecoder->Decode(WrapNotNull(this));
140 :
141 : // If there's a new surface available, announce it to the surface cache.
142 12 : CheckForNewSurface();
143 :
144 12 : if (result.is<TerminalState>()) {
145 12 : FinishDecoding();
146 12 : return; // We're done.
147 : }
148 :
149 : // Notify for the progress we've made so far.
150 0 : if (mDecoder->HasProgress()) {
151 0 : NotifyProgress(WrapNotNull(mImage), WrapNotNull(mDecoder));
152 : }
153 :
154 0 : MOZ_ASSERT(result.is<Yield>());
155 :
156 0 : if (result == LexerResult(Yield::NEED_MORE_DATA)) {
157 : // We can't make any more progress right now. The decoder itself will ensure
158 : // that we get reenqueued when more data is available; just return for now.
159 0 : return;
160 : }
161 :
162 : // Single-frame images shouldn't yield for any reason except NEED_MORE_DATA.
163 0 : MOZ_ASSERT_UNREACHABLE("Unexpected yield for single-frame image");
164 : mDecoder->TerminateFailure();
165 : FinishDecoding();
166 : }
167 :
168 : void
169 12 : DecodedSurfaceProvider::CheckForNewSurface()
170 : {
171 12 : mMutex.AssertCurrentThreadOwns();
172 12 : MOZ_ASSERT(mDecoder);
173 :
174 12 : if (mSurface) {
175 : // Single-frame images should produce no more than one surface, so if we
176 : // have one, it should be the same one the decoder is working on.
177 0 : MOZ_ASSERT(mSurface.get() == mDecoder->GetCurrentFrameRef().get(),
178 : "DecodedSurfaceProvider and Decoder have different surfaces?");
179 0 : return;
180 : }
181 :
182 : // We don't have a surface yet; try to get one from the decoder.
183 12 : mSurface = mDecoder->GetCurrentFrameRef().get();
184 12 : if (!mSurface) {
185 0 : return; // No surface yet.
186 : }
187 :
188 : // We just got a surface for the first time; let the surface cache know.
189 12 : MOZ_ASSERT(mImage);
190 12 : SurfaceCache::SurfaceAvailable(WrapNotNull(this));
191 : }
192 :
193 : void
194 12 : DecodedSurfaceProvider::FinishDecoding()
195 : {
196 12 : mMutex.AssertCurrentThreadOwns();
197 12 : MOZ_ASSERT(mImage);
198 12 : MOZ_ASSERT(mDecoder);
199 :
200 : // Send notifications.
201 12 : NotifyDecodeComplete(WrapNotNull(mImage), WrapNotNull(mDecoder));
202 :
203 : // Destroy our decoder; we don't need it anymore. (And if we don't destroy it,
204 : // our surface can never be optimized, because the decoder has a
205 : // RawAccessFrameRef to it.)
206 12 : mDecoder = nullptr;
207 :
208 : // We don't need a reference to our image anymore, either, and we don't want
209 : // one. We may be stored in the surface cache for a long time after decoding
210 : // finishes. If we don't drop our reference to the image, we'll end up
211 : // keeping it alive as long as we remain in the surface cache, which could
212 : // greatly extend the image's lifetime - in fact, if the image isn't
213 : // discardable, it'd result in a leak!
214 12 : DropImageReference();
215 12 : }
216 :
217 : bool
218 3 : DecodedSurfaceProvider::ShouldPreferSyncRun() const
219 : {
220 3 : return mDecoder->ShouldSyncDecode(gfxPrefs::ImageMemDecodeBytesAtATime());
221 : }
222 :
223 : } // namespace image
224 : } // namespace mozilla
|