Bug 1728955: part 8) Refactor `DisplayErrCode` in Windows' `nsClipboard`. r=masayuki
[gecko.git] / image / DecodedSurfaceProvider.cpp
blobd91a17a58c563ae080b848c2bcb59bc82ffbe6cb
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"
11 #include "Decoder.h"
13 using namespace mozilla::gfx;
15 namespace mozilla {
16 namespace image {
18 DecodedSurfaceProvider::DecodedSurfaceProvider(NotNull<RasterImage*> aImage,
19 const SurfaceKey& aSurfaceKey,
20 NotNull<Decoder*> aDecoder)
21 : ISurfaceProvider(ImageKey(aImage.get()), aSurfaceKey,
22 AvailabilityState::StartAsPlaceholder()),
23 mImage(aImage.get()),
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() {
35 if (!mImage) {
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;
45 mImage = nullptr;
46 SurfaceCache::ReleaseImageOnMainThread(image.forget(),
47 /* aAlwaysProxy = */ true);
50 DrawableFrameRef DecodedSurfaceProvider::DrawableRef(size_t aFrame) {
51 MOZ_ASSERT(aFrame == 0,
52 "Requesting an animation frame from a DecodedSurfaceProvider?");
54 // We depend on SurfaceCache::SurfaceAvailable() to provide synchronization
55 // for methods that touch |mSurface|; after SurfaceAvailable() is called,
56 // |mSurface| should be non-null and shouldn't be mutated further until we get
57 // destroyed. That means that the assertions below are very important; we'll
58 // end up with data races if these assumptions are violated.
59 if (Availability().IsPlaceholder()) {
60 MOZ_ASSERT_UNREACHABLE("Calling DrawableRef() on a placeholder");
61 return DrawableFrameRef();
64 if (!mSurface) {
65 MOZ_ASSERT_UNREACHABLE("Calling DrawableRef() when we have no surface");
66 return DrawableFrameRef();
69 return mSurface->DrawableRef();
72 bool DecodedSurfaceProvider::IsFinished() const {
73 // See DrawableRef() for commentary on these assertions.
74 if (Availability().IsPlaceholder()) {
75 MOZ_ASSERT_UNREACHABLE("Calling IsFinished() on a placeholder");
76 return false;
79 if (!mSurface) {
80 MOZ_ASSERT_UNREACHABLE("Calling IsFinished() when we have no surface");
81 return false;
84 return mSurface->IsFinished();
87 void DecodedSurfaceProvider::SetLocked(bool aLocked) {
88 // See DrawableRef() for commentary on these assertions.
89 if (Availability().IsPlaceholder()) {
90 MOZ_ASSERT_UNREACHABLE("Calling SetLocked() on a placeholder");
91 return;
94 if (!mSurface) {
95 MOZ_ASSERT_UNREACHABLE("Calling SetLocked() when we have no surface");
96 return;
99 if (aLocked == IsLocked()) {
100 return; // Nothing to do.
103 // If we're locked, hold a DrawableFrameRef to |mSurface|, which will keep any
104 // volatile buffer it owns in memory.
105 mLockRef = aLocked ? mSurface->DrawableRef() : DrawableFrameRef();
108 size_t DecodedSurfaceProvider::LogicalSizeInBytes() const {
109 // Single frame images are always 32bpp.
110 IntSize size = GetSurfaceKey().Size();
111 return size_t(size.width) * size_t(size.height) * sizeof(uint32_t);
114 void DecodedSurfaceProvider::Run() {
115 MutexAutoLock lock(mMutex);
117 if (!mDecoder || !mImage) {
118 MOZ_ASSERT_UNREACHABLE("Running after decoding finished?");
119 return;
122 // Run the decoder.
123 LexerResult result = mDecoder->Decode(WrapNotNull(this));
125 // If there's a new surface available, announce it to the surface cache.
126 CheckForNewSurface();
128 if (result.is<TerminalState>()) {
129 FinishDecoding();
130 return; // We're done.
133 // Notify for the progress we've made so far.
134 if (mDecoder->HasProgress()) {
135 NotifyProgress(WrapNotNull(mImage), WrapNotNull(mDecoder));
138 MOZ_ASSERT(result.is<Yield>());
140 if (result == LexerResult(Yield::NEED_MORE_DATA)) {
141 // We can't make any more progress right now. The decoder itself will ensure
142 // that we get reenqueued when more data is available; just return for now.
143 return;
146 // Single-frame images shouldn't yield for any reason except NEED_MORE_DATA.
147 MOZ_ASSERT_UNREACHABLE("Unexpected yield for single-frame image");
148 mDecoder->TerminateFailure();
149 FinishDecoding();
152 void DecodedSurfaceProvider::CheckForNewSurface() {
153 mMutex.AssertCurrentThreadOwns();
154 MOZ_ASSERT(mDecoder);
156 if (mSurface) {
157 // Single-frame images should produce no more than one surface, so if we
158 // have one, it should be the same one the decoder is working on.
159 MOZ_ASSERT(mSurface.get() == mDecoder->GetCurrentFrameRef().get(),
160 "DecodedSurfaceProvider and Decoder have different surfaces?");
161 return;
164 // We don't have a surface yet; try to get one from the decoder.
165 mSurface = mDecoder->GetCurrentFrameRef().get();
166 if (!mSurface) {
167 return; // No surface yet.
170 // We just got a surface for the first time; let the surface cache know.
171 MOZ_ASSERT(mImage);
172 SurfaceCache::SurfaceAvailable(WrapNotNull(this));
175 void DecodedSurfaceProvider::FinishDecoding() {
176 mMutex.AssertCurrentThreadOwns();
177 MOZ_ASSERT(mImage);
178 MOZ_ASSERT(mDecoder);
180 // Send notifications.
181 NotifyDecodeComplete(WrapNotNull(mImage), WrapNotNull(mDecoder));
183 // If we have a new and complete surface, we can try to prune similarly sized
184 // surfaces if the cache supports it.
185 if (mSurface && mSurface->IsFinished()) {
186 SurfaceCache::PruneImage(ImageKey(mImage));
189 // Destroy our decoder; we don't need it anymore. (And if we don't destroy it,
190 // our surface can never be optimized, because the decoder has a
191 // RawAccessFrameRef to it.)
192 mDecoder = nullptr;
194 // We don't need a reference to our image anymore, either, and we don't want
195 // one. We may be stored in the surface cache for a long time after decoding
196 // finishes. If we don't drop our reference to the image, we'll end up
197 // keeping it alive as long as we remain in the surface cache, which could
198 // greatly extend the image's lifetime - in fact, if the image isn't
199 // discardable, it'd result in a leak!
200 DropImageReference();
203 bool DecodedSurfaceProvider::ShouldPreferSyncRun() const {
204 return mDecoder->ShouldSyncDecode(
205 StaticPrefs::image_mem_decode_bytes_at_a_time_AtStartup());
208 } // namespace image
209 } // namespace mozilla