Bug 1883449 - [wpt-sync] Update web-platform-tests to bb67daef8e6a384ead5da4c991c12f8...
[gecko.git] / image / DecodedSurfaceProvider.cpp
blobcdfc27bcda03b525087548481c9f3aef53e7f92a
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 "mozilla/layers/SharedSurfacesChild.h"
10 #include "nsProxyRelease.h"
12 #include "Decoder.h"
14 using namespace mozilla::gfx;
15 using namespace mozilla::layers;
17 namespace mozilla {
18 namespace image {
20 DecodedSurfaceProvider::DecodedSurfaceProvider(NotNull<RasterImage*> aImage,
21 const SurfaceKey& aSurfaceKey,
22 NotNull<Decoder*> aDecoder)
23 : ISurfaceProvider(ImageKey(aImage.get()), aSurfaceKey,
24 AvailabilityState::StartAsPlaceholder()),
25 mImage(aImage.get()),
26 mMutex("mozilla::image::DecodedSurfaceProvider"),
27 mDecoder(aDecoder.get()) {
28 MOZ_ASSERT(!mDecoder->IsMetadataDecode(),
29 "Use MetadataDecodingTask for metadata decodes");
30 MOZ_ASSERT(mDecoder->IsFirstFrameDecode(),
31 "Use AnimationSurfaceProvider for animation decodes");
34 DecodedSurfaceProvider::~DecodedSurfaceProvider() { DropImageReference(); }
36 void DecodedSurfaceProvider::DropImageReference() {
37 if (!mImage) {
38 return; // Nothing to do.
41 // RasterImage objects need to be destroyed on the main thread. We also need
42 // to destroy them asynchronously, because if our surface cache entry is
43 // destroyed and we were the only thing keeping |mImage| alive, RasterImage's
44 // destructor may call into the surface cache while whatever code caused us to
45 // get evicted is holding the surface cache lock, causing deadlock.
46 RefPtr<RasterImage> image = mImage;
47 mImage = nullptr;
48 SurfaceCache::ReleaseImageOnMainThread(image.forget(),
49 /* aAlwaysProxy = */ true);
52 DrawableFrameRef DecodedSurfaceProvider::DrawableRef(size_t aFrame) {
53 MOZ_ASSERT(aFrame == 0,
54 "Requesting an animation frame from a DecodedSurfaceProvider?");
56 // We depend on SurfaceCache::SurfaceAvailable() to provide synchronization
57 // for methods that touch |mSurface|; after SurfaceAvailable() is called,
58 // |mSurface| should be non-null and shouldn't be mutated further until we get
59 // destroyed. That means that the assertions below are very important; we'll
60 // end up with data races if these assumptions are violated.
61 if (Availability().IsPlaceholder()) {
62 MOZ_ASSERT_UNREACHABLE("Calling DrawableRef() on a placeholder");
63 return DrawableFrameRef();
66 if (!mSurface) {
67 MOZ_ASSERT_UNREACHABLE("Calling DrawableRef() when we have no surface");
68 return DrawableFrameRef();
71 return mSurface->DrawableRef();
74 bool DecodedSurfaceProvider::IsFinished() const {
75 // See DrawableRef() for commentary on these assertions.
76 if (Availability().IsPlaceholder()) {
77 MOZ_ASSERT_UNREACHABLE("Calling IsFinished() on a placeholder");
78 return false;
81 if (!mSurface) {
82 MOZ_ASSERT_UNREACHABLE("Calling IsFinished() when we have no surface");
83 return false;
86 return mSurface->IsFinished();
89 void DecodedSurfaceProvider::SetLocked(bool aLocked) {
90 // See DrawableRef() for commentary on these assertions.
91 if (Availability().IsPlaceholder()) {
92 MOZ_ASSERT_UNREACHABLE("Calling SetLocked() on a placeholder");
93 return;
96 if (!mSurface) {
97 MOZ_ASSERT_UNREACHABLE("Calling SetLocked() when we have no surface");
98 return;
101 if (aLocked == IsLocked()) {
102 return; // Nothing to do.
105 // If we're locked, hold a DrawableFrameRef to |mSurface|, which will keep any
106 // volatile buffer it owns in memory.
107 mLockRef = aLocked ? mSurface->DrawableRef() : DrawableFrameRef();
110 size_t DecodedSurfaceProvider::LogicalSizeInBytes() const {
111 // Single frame images are always 32bpp.
112 IntSize size = GetSurfaceKey().Size();
113 return size_t(size.width) * size_t(size.height) * sizeof(uint32_t);
116 void DecodedSurfaceProvider::Run() {
117 MutexAutoLock lock(mMutex);
119 if (!mDecoder || !mImage) {
120 MOZ_ASSERT_UNREACHABLE("Running after decoding finished?");
121 return;
124 // Run the decoder.
125 LexerResult result = mDecoder->Decode(WrapNotNull(this));
127 // If there's a new surface available, announce it to the surface cache.
128 CheckForNewSurface();
130 if (result.is<TerminalState>()) {
131 FinishDecoding();
132 return; // We're done.
135 // Notify for the progress we've made so far.
136 if (mDecoder->HasProgress()) {
137 NotifyProgress(WrapNotNull(mImage), WrapNotNull(mDecoder));
140 MOZ_ASSERT(result.is<Yield>());
142 if (result == LexerResult(Yield::NEED_MORE_DATA)) {
143 // We can't make any more progress right now. The decoder itself will ensure
144 // that we get reenqueued when more data is available; just return for now.
145 return;
148 // Single-frame images shouldn't yield for any reason except NEED_MORE_DATA.
149 MOZ_ASSERT_UNREACHABLE("Unexpected yield for single-frame image");
150 mDecoder->TerminateFailure();
151 FinishDecoding();
154 void DecodedSurfaceProvider::CheckForNewSurface() {
155 mMutex.AssertCurrentThreadOwns();
156 MOZ_ASSERT(mDecoder);
158 if (mSurface) {
159 // Single-frame images should produce no more than one surface, so if we
160 // have one, it should be the same one the decoder is working on.
161 MOZ_ASSERT(mSurface.get() == mDecoder->GetCurrentFrameRef().get(),
162 "DecodedSurfaceProvider and Decoder have different surfaces?");
163 return;
166 // We don't have a surface yet; try to get one from the decoder.
167 mSurface = mDecoder->GetCurrentFrameRef().get();
168 if (!mSurface) {
169 return; // No surface yet.
172 // We just got a surface for the first time; let the surface cache know.
173 MOZ_ASSERT(mImage);
174 SurfaceCache::SurfaceAvailable(WrapNotNull(this));
177 void DecodedSurfaceProvider::FinishDecoding() {
178 mMutex.AssertCurrentThreadOwns();
179 MOZ_ASSERT(mImage);
180 MOZ_ASSERT(mDecoder);
182 // Send notifications.
183 NotifyDecodeComplete(WrapNotNull(mImage), WrapNotNull(mDecoder));
185 // If we have a new and complete surface, we can try to prune similarly sized
186 // surfaces if the cache supports it.
187 if (mSurface && mSurface->IsFinished()) {
188 SurfaceCache::PruneImage(ImageKey(mImage));
191 // Destroy our decoder; we don't need it anymore. (And if we don't destroy it,
192 // our surface can never be optimized, because the decoder has a
193 // RawAccessFrameRef to it.)
194 mDecoder = nullptr;
196 // We don't need a reference to our image anymore, either, and we don't want
197 // one. We may be stored in the surface cache for a long time after decoding
198 // finishes. If we don't drop our reference to the image, we'll end up
199 // keeping it alive as long as we remain in the surface cache, which could
200 // greatly extend the image's lifetime - in fact, if the image isn't
201 // discardable, it'd result in a leak!
202 DropImageReference();
205 bool DecodedSurfaceProvider::ShouldPreferSyncRun() const {
206 return mDecoder->ShouldSyncDecode(
207 StaticPrefs::image_mem_decode_bytes_at_a_time_AtStartup());
210 nsresult DecodedSurfaceProvider::UpdateKey(
211 layers::RenderRootStateManager* aManager,
212 wr::IpcResourceUpdateQueue& aResources, wr::ImageKey& aKey) {
213 MOZ_ASSERT(mSurface);
214 RefPtr<SourceSurface> surface = mSurface->GetSourceSurface();
215 if (!surface) {
216 return NS_ERROR_FAILURE;
219 return SharedSurfacesChild::Share(surface, aManager, aResources, aKey);
222 nsresult SimpleSurfaceProvider::UpdateKey(
223 layers::RenderRootStateManager* aManager,
224 wr::IpcResourceUpdateQueue& aResources, wr::ImageKey& aKey) {
225 RefPtr<SourceSurface> surface = mSurface->GetSourceSurface();
226 if (!surface) {
227 return NS_ERROR_FAILURE;
230 return SharedSurfacesChild::Share(surface, aManager, aResources, aKey);
233 } // namespace image
234 } // namespace mozilla