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/. */
6 #include "DecodedSurfaceProvider.h"
8 #include "mozilla/StaticPrefs_image.h"
9 #include "nsProxyRelease.h"
13 using namespace mozilla::gfx
;
18 DecodedSurfaceProvider::DecodedSurfaceProvider(NotNull
<RasterImage
*> aImage
,
19 const SurfaceKey
& aSurfaceKey
,
20 NotNull
<Decoder
*> aDecoder
)
21 : ISurfaceProvider(ImageKey(aImage
.get()), aSurfaceKey
,
22 AvailabilityState::StartAsPlaceholder()),
24 mMutex("mozilla::image::DecodedSurfaceProvider"),
25 mDecoder(aDecoder
.get()) {
26 MOZ_ASSERT(!mDecoder
->IsMetadataDecode(),
27 "Use MetadataDecodingTask for metadata decodes");
28 MOZ_ASSERT(mDecoder
->IsFirstFrameDecode(),
29 "Use AnimationSurfaceProvider for animation decodes");
32 DecodedSurfaceProvider::~DecodedSurfaceProvider() { DropImageReference(); }
34 void DecodedSurfaceProvider::DropImageReference() {
36 return; // Nothing to do.
39 // RasterImage objects need to be destroyed on the main thread. We also need
40 // to destroy them asynchronously, because if our surface cache entry is
41 // destroyed and we were the only thing keeping |mImage| alive, RasterImage's
42 // destructor may call into the surface cache while whatever code caused us to
43 // get evicted is holding the surface cache lock, causing deadlock.
44 RefPtr
<RasterImage
> image
= mImage
;
46 NS_ReleaseOnMainThread(image
.forget(), /* aAlwaysProxy = */ true);
49 DrawableFrameRef
DecodedSurfaceProvider::DrawableRef(size_t aFrame
) {
50 MOZ_ASSERT(aFrame
== 0,
51 "Requesting an animation frame from a DecodedSurfaceProvider?");
53 // We depend on SurfaceCache::SurfaceAvailable() to provide synchronization
54 // for methods that touch |mSurface|; after SurfaceAvailable() is called,
55 // |mSurface| should be non-null and shouldn't be mutated further until we get
56 // destroyed. That means that the assertions below are very important; we'll
57 // end up with data races if these assumptions are violated.
58 if (Availability().IsPlaceholder()) {
59 MOZ_ASSERT_UNREACHABLE("Calling DrawableRef() on a placeholder");
60 return DrawableFrameRef();
64 MOZ_ASSERT_UNREACHABLE("Calling DrawableRef() when we have no surface");
65 return DrawableFrameRef();
68 return mSurface
->DrawableRef();
71 bool DecodedSurfaceProvider::IsFinished() const {
72 // See DrawableRef() for commentary on these assertions.
73 if (Availability().IsPlaceholder()) {
74 MOZ_ASSERT_UNREACHABLE("Calling IsFinished() on a placeholder");
79 MOZ_ASSERT_UNREACHABLE("Calling IsFinished() when we have no surface");
83 return mSurface
->IsFinished();
86 void DecodedSurfaceProvider::SetLocked(bool aLocked
) {
87 // See DrawableRef() for commentary on these assertions.
88 if (Availability().IsPlaceholder()) {
89 MOZ_ASSERT_UNREACHABLE("Calling SetLocked() on a placeholder");
94 MOZ_ASSERT_UNREACHABLE("Calling SetLocked() when we have no surface");
98 if (aLocked
== IsLocked()) {
99 return; // Nothing to do.
102 // If we're locked, hold a DrawableFrameRef to |mSurface|, which will keep any
103 // volatile buffer it owns in memory.
104 mLockRef
= aLocked
? mSurface
->DrawableRef() : DrawableFrameRef();
107 size_t DecodedSurfaceProvider::LogicalSizeInBytes() const {
108 // Single frame images are always 32bpp.
109 IntSize size
= GetSurfaceKey().Size();
110 return size_t(size
.width
) * size_t(size
.height
) * sizeof(uint32_t);
113 void DecodedSurfaceProvider::Run() {
114 MutexAutoLock
lock(mMutex
);
116 if (!mDecoder
|| !mImage
) {
117 MOZ_ASSERT_UNREACHABLE("Running after decoding finished?");
122 LexerResult result
= mDecoder
->Decode(WrapNotNull(this));
124 // If there's a new surface available, announce it to the surface cache.
125 CheckForNewSurface();
127 if (result
.is
<TerminalState
>()) {
129 return; // We're done.
132 // Notify for the progress we've made so far.
133 if (mDecoder
->HasProgress()) {
134 NotifyProgress(WrapNotNull(mImage
), WrapNotNull(mDecoder
));
137 MOZ_ASSERT(result
.is
<Yield
>());
139 if (result
== LexerResult(Yield::NEED_MORE_DATA
)) {
140 // We can't make any more progress right now. The decoder itself will ensure
141 // that we get reenqueued when more data is available; just return for now.
145 // Single-frame images shouldn't yield for any reason except NEED_MORE_DATA.
146 MOZ_ASSERT_UNREACHABLE("Unexpected yield for single-frame image");
147 mDecoder
->TerminateFailure();
151 void DecodedSurfaceProvider::CheckForNewSurface() {
152 mMutex
.AssertCurrentThreadOwns();
153 MOZ_ASSERT(mDecoder
);
156 // Single-frame images should produce no more than one surface, so if we
157 // have one, it should be the same one the decoder is working on.
158 MOZ_ASSERT(mSurface
.get() == mDecoder
->GetCurrentFrameRef().get(),
159 "DecodedSurfaceProvider and Decoder have different surfaces?");
163 // We don't have a surface yet; try to get one from the decoder.
164 mSurface
= mDecoder
->GetCurrentFrameRef().get();
166 return; // No surface yet.
169 // We just got a surface for the first time; let the surface cache know.
171 SurfaceCache::SurfaceAvailable(WrapNotNull(this));
174 void DecodedSurfaceProvider::FinishDecoding() {
175 mMutex
.AssertCurrentThreadOwns();
177 MOZ_ASSERT(mDecoder
);
179 // Send notifications.
180 NotifyDecodeComplete(WrapNotNull(mImage
), WrapNotNull(mDecoder
));
182 // If we have a new and complete surface, we can try to prune similarly sized
183 // surfaces if the cache supports it.
184 if (mSurface
&& mSurface
->IsFinished()) {
185 SurfaceCache::PruneImage(ImageKey(mImage
));
188 // Destroy our decoder; we don't need it anymore. (And if we don't destroy it,
189 // our surface can never be optimized, because the decoder has a
190 // RawAccessFrameRef to it.)
193 // We don't need a reference to our image anymore, either, and we don't want
194 // one. We may be stored in the surface cache for a long time after decoding
195 // finishes. If we don't drop our reference to the image, we'll end up
196 // keeping it alive as long as we remain in the surface cache, which could
197 // greatly extend the image's lifetime - in fact, if the image isn't
198 // discardable, it'd result in a leak!
199 DropImageReference();
202 bool DecodedSurfaceProvider::ShouldPreferSyncRun() const {
203 return mDecoder
->ShouldSyncDecode(
204 StaticPrefs::image_mem_decode_bytes_at_a_time_AtStartup());
208 } // namespace mozilla