DecodedSurfaceProvider.cpp (8088B)
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 "mozilla/StaticPrefs_image.h" 9 #include "mozilla/layers/SharedSurfacesChild.h" 10 #include "nsProxyRelease.h" 11 12 #include "Decoder.h" 13 14 using namespace mozilla::gfx; 15 using namespace mozilla::layers; 16 17 namespace mozilla { 18 namespace image { 19 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"); 32 } 33 34 DecodedSurfaceProvider::~DecodedSurfaceProvider() { DropImageReference(); } 35 36 void DecodedSurfaceProvider::DropImageReference() { 37 if (!mImage) { 38 return; // Nothing to do. 39 } 40 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); 50 } 51 52 DrawableFrameRef DecodedSurfaceProvider::DrawableRef(size_t aFrame) { 53 MOZ_ASSERT(aFrame == 0, 54 "Requesting an animation frame from a DecodedSurfaceProvider?"); 55 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(); 64 } 65 66 if (!mSurface) { 67 MOZ_ASSERT_UNREACHABLE("Calling DrawableRef() when we have no surface"); 68 return DrawableFrameRef(); 69 } 70 71 return mSurface->DrawableRef(); 72 } 73 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; 79 } 80 81 if (!mSurface) { 82 MOZ_ASSERT_UNREACHABLE("Calling IsFinished() when we have no surface"); 83 return false; 84 } 85 86 return mSurface->IsFinished(); 87 } 88 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; 94 } 95 96 if (!mSurface) { 97 MOZ_ASSERT_UNREACHABLE("Calling SetLocked() when we have no surface"); 98 return; 99 } 100 101 if (aLocked == IsLocked()) { 102 return; // Nothing to do. 103 } 104 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(); 108 } 109 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); 114 } 115 116 void DecodedSurfaceProvider::Run() { 117 MutexAutoLock lock(mMutex); 118 119 if (!mDecoder || !mImage) { 120 MOZ_ASSERT_UNREACHABLE("Running after decoding finished?"); 121 return; 122 } 123 124 // Run the decoder. 125 LexerResult result = mDecoder->Decode(WrapNotNull(this)); 126 127 // If there's a new surface available, announce it to the surface cache. 128 CheckForNewSurface(); 129 130 if (result.is<TerminalState>()) { 131 FinishDecoding(); 132 return; // We're done. 133 } 134 135 // Notify for the progress we've made so far. 136 if (mDecoder->HasProgress()) { 137 NotifyProgress(WrapNotNull(mImage), WrapNotNull(mDecoder)); 138 } 139 140 MOZ_ASSERT(result.is<Yield>()); 141 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; 146 } 147 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(); 152 } 153 154 void DecodedSurfaceProvider::CheckForNewSurface() { 155 mMutex.AssertCurrentThreadOwns(); 156 MOZ_ASSERT(mDecoder); 157 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; 164 } 165 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. 170 } 171 172 // We just got a surface for the first time; let the surface cache know. 173 MOZ_ASSERT(mImage); 174 SurfaceCache::SurfaceAvailable(WrapNotNull(this)); 175 } 176 177 void DecodedSurfaceProvider::FinishDecoding() { 178 mMutex.AssertCurrentThreadOwns(); 179 MOZ_ASSERT(mImage); 180 MOZ_ASSERT(mDecoder); 181 182 // Send notifications. 183 NotifyDecodeComplete(WrapNotNull(mImage), WrapNotNull(mDecoder)); 184 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)); 189 } 190 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; 195 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(); 203 } 204 205 bool DecodedSurfaceProvider::ShouldPreferSyncRun() const { 206 return mDecoder->ShouldSyncDecode( 207 StaticPrefs::image_mem_decode_bytes_at_a_time_AtStartup()); 208 } 209 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; 217 } 218 219 return SharedSurfacesChild::Share(surface, aManager, aResources, aKey); 220 } 221 222 nsresult SimpleSurfaceProvider::UpdateKey( 223 layers::RenderRootStateManager* aManager, 224 wr::IpcResourceUpdateQueue& aResources, wr::ImageKey& aKey) { 225 if (mDirty) { 226 return NS_ERROR_FAILURE; 227 } 228 229 RefPtr<SourceSurface> surface = mSurface->GetSourceSurface(); 230 if (!surface) { 231 return NS_ERROR_FAILURE; 232 } 233 234 return SharedSurfacesChild::Share(surface, aManager, aResources, aKey); 235 } 236 237 void SimpleSurfaceProvider::InvalidateSurface() { mDirty = true; } 238 239 } // namespace image 240 } // namespace mozilla