AnimationSurfaceProvider.cpp (18429B)
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 "AnimationSurfaceProvider.h" 7 8 #include "mozilla/StaticPrefs_image.h" 9 #include "mozilla/gfx/gfxVars.h" 10 #include "mozilla/layers/SharedSurfacesChild.h" 11 #include "mozilla/layers/SourceSurfaceSharedData.h" 12 #include "nsProxyRelease.h" 13 14 #include "DecodePool.h" 15 #include "Decoder.h" 16 17 using namespace mozilla::gfx; 18 using namespace mozilla::layers; 19 20 namespace mozilla { 21 namespace image { 22 23 AnimationSurfaceProvider::AnimationSurfaceProvider( 24 NotNull<RasterImage*> aImage, const SurfaceKey& aSurfaceKey, 25 NotNull<Decoder*> aDecoder, size_t aCurrentFrame) 26 : ISurfaceProvider(ImageKey(aImage.get()), aSurfaceKey, 27 AvailabilityState::StartAsPlaceholder()), 28 mImage(aImage.get()), 29 mDecodingMutex("AnimationSurfaceProvider::mDecoder"), 30 mDecoder(aDecoder.get()), 31 mFramesMutex("AnimationSurfaceProvider::mFrames"), 32 mCompositedFrameRequested(false), 33 mSharedAnimation(MakeRefPtr<SharedSurfacesAnimation>()) { 34 MOZ_ASSERT(!mDecoder->IsMetadataDecode(), 35 "Use MetadataDecodingTask for metadata decodes"); 36 MOZ_ASSERT(!mDecoder->IsFirstFrameDecode(), 37 "Use DecodedSurfaceProvider for single-frame image decodes"); 38 39 // Calculate how many frames we need to decode in this animation before we 40 // enter decode-on-demand mode. 41 IntSize frameSize = aSurfaceKey.Size(); 42 size_t threshold = 43 (size_t(StaticPrefs::image_animated_decode_on_demand_threshold_kb()) * 44 1024) / 45 (sizeof(uint32_t) * frameSize.width * frameSize.height); 46 size_t batch = StaticPrefs::image_animated_decode_on_demand_batch_size(); 47 48 mFrames.reset( 49 new AnimationFrameRetainedBuffer(threshold, batch, aCurrentFrame)); 50 } 51 52 AnimationSurfaceProvider::~AnimationSurfaceProvider() { 53 DropImageReference(); 54 55 mSharedAnimation->Destroy(); 56 if (mDecoder) { 57 mDecoder->SetFrameRecycler(nullptr); 58 } 59 } 60 61 void AnimationSurfaceProvider::DropImageReference() { 62 if (!mImage) { 63 return; // Nothing to do. 64 } 65 66 // RasterImage objects need to be destroyed on the main thread. 67 SurfaceCache::ReleaseImageOnMainThread(mImage.forget()); 68 } 69 70 void AnimationSurfaceProvider::Reset() { 71 // We want to go back to the beginning. 72 bool mayDiscard; 73 bool restartDecoder = false; 74 75 { 76 MutexAutoLock lock(mFramesMutex); 77 78 // If we have not crossed the threshold, we know we haven't discarded any 79 // frames, and thus we know it is safe move our display index back to the 80 // very beginning. It would be cleaner to let the frame buffer make this 81 // decision inside the AnimationFrameBuffer::Reset method, but if we have 82 // crossed the threshold, we need to hold onto the decoding mutex too. We 83 // should avoid blocking the main thread on the decoder threads. 84 mayDiscard = mFrames->MayDiscard(); 85 if (!mayDiscard) { 86 restartDecoder = mFrames->Reset(); 87 } 88 } 89 90 if (mayDiscard) { 91 // We are over the threshold and have started discarding old frames. In 92 // that case we need to seize the decoding mutex. Thankfully we know that 93 // we are in the process of decoding at most the batch size frames, so 94 // this should not take too long to acquire. 95 MutexAutoLock lock(mDecodingMutex); 96 97 // We may have hit an error while redecoding. Because FrameAnimator is 98 // tightly coupled to our own state, that means we would need to go through 99 // some heroics to resume animating in those cases. The typical reason for 100 // a redecode to fail is out of memory, and recycling should prevent most of 101 // those errors. When image.animated.generate-full-frames has shipped 102 // enabled on a release or two, we can simply remove the old FrameAnimator 103 // blending code and simplify this quite a bit -- just always pop the next 104 // full frame and timeout off the stack. 105 if (mDecoder) { 106 mDecoder = DecoderFactory::CloneAnimationDecoder(mDecoder); 107 MOZ_ASSERT(mDecoder); 108 109 MutexAutoLock lock2(mFramesMutex); 110 restartDecoder = mFrames->Reset(); 111 } else { 112 MOZ_ASSERT(mFrames->HasRedecodeError()); 113 } 114 } 115 116 if (restartDecoder) { 117 DecodePool::Singleton()->AsyncRun(this); 118 } 119 } 120 121 void AnimationSurfaceProvider::Advance(size_t aFrame) { 122 bool restartDecoder; 123 124 RefPtr<SourceSurface> surface; 125 IntRect dirtyRect; 126 { 127 // Typical advancement of a frame. 128 MutexAutoLock lock(mFramesMutex); 129 restartDecoder = mFrames->AdvanceTo(aFrame); 130 131 imgFrame* frame = mFrames->Get(aFrame, /* aForDisplay */ true); 132 MOZ_ASSERT(frame); 133 if (aFrame != 0) { 134 dirtyRect = frame->GetDirtyRect(); 135 } else { 136 MOZ_ASSERT(mFrames->SizeKnown()); 137 dirtyRect = mFrames->FirstFrameRefreshArea(); 138 } 139 surface = frame->GetSourceSurface(); 140 MOZ_ASSERT(surface); 141 } 142 143 if (restartDecoder) { 144 DecodePool::Singleton()->AsyncRun(this); 145 } 146 147 mCompositedFrameRequested = false; 148 auto* sharedSurface = static_cast<SourceSurfaceSharedData*>(surface.get()); 149 mSharedAnimation->SetCurrentFrame(sharedSurface, dirtyRect); 150 } 151 152 DrawableFrameRef AnimationSurfaceProvider::DrawableRef(size_t aFrame) { 153 MutexAutoLock lock(mFramesMutex); 154 155 if (Availability().IsPlaceholder()) { 156 MOZ_ASSERT_UNREACHABLE("Calling DrawableRef() on a placeholder"); 157 return DrawableFrameRef(); 158 } 159 160 imgFrame* frame = mFrames->Get(aFrame, /* aForDisplay */ true); 161 if (!frame) { 162 return DrawableFrameRef(); 163 } 164 165 return frame->DrawableRef(); 166 } 167 168 already_AddRefed<imgFrame> AnimationSurfaceProvider::GetFrame(size_t aFrame) { 169 MutexAutoLock lock(mFramesMutex); 170 171 if (Availability().IsPlaceholder()) { 172 MOZ_ASSERT_UNREACHABLE("Calling GetFrame() on a placeholder"); 173 return nullptr; 174 } 175 176 RefPtr<imgFrame> frame = mFrames->Get(aFrame, /* aForDisplay */ false); 177 MOZ_ASSERT_IF(frame, frame->IsFinished()); 178 return frame.forget(); 179 } 180 181 bool AnimationSurfaceProvider::IsFinished() const { 182 MutexAutoLock lock(mFramesMutex); 183 184 if (Availability().IsPlaceholder()) { 185 MOZ_ASSERT_UNREACHABLE("Calling IsFinished() on a placeholder"); 186 return false; 187 } 188 189 return mFrames->IsFirstFrameFinished(); 190 } 191 192 bool AnimationSurfaceProvider::IsFullyDecoded() const { 193 MutexAutoLock lock(mFramesMutex); 194 return mFrames->SizeKnown() && !mFrames->MayDiscard(); 195 } 196 197 size_t AnimationSurfaceProvider::LogicalSizeInBytes() const { 198 // When decoding animated images, we need at most three live surfaces: the 199 // composited surface, the previous composited surface for 200 // DisposalMethod::RESTORE_PREVIOUS, and the surface we're currently decoding 201 // into. The composited surfaces are always BGRA. Although the surface we're 202 // decoding into may be paletted, and may be smaller than the real size of the 203 // image, we assume the worst case here. 204 // XXX(seth): Note that this is actually not accurate yet; we're storing the 205 // full sequence of frames, not just the three live surfaces mentioned above. 206 // Unfortunately there's no way to know in advance how many frames an 207 // animation has, so we really can't do better here. This will become correct 208 // once bug 1289954 is complete. 209 IntSize size = GetSurfaceKey().Size(); 210 return 3 * size.width * size.height * sizeof(uint32_t); 211 } 212 213 void AnimationSurfaceProvider::AddSizeOfExcludingThis( 214 MallocSizeOf aMallocSizeOf, const AddSizeOfCb& aCallback) { 215 // Note that the surface cache lock is already held here, and then we acquire 216 // mFramesMutex. For this method, this ordering is unavoidable, which means 217 // that we must be careful to always use the same ordering elsewhere. 218 MutexAutoLock lock(mFramesMutex); 219 mFrames->AddSizeOfExcludingThis(aMallocSizeOf, aCallback); 220 } 221 222 void AnimationSurfaceProvider::Run() { 223 MutexAutoLock lock(mDecodingMutex); 224 225 if (!mDecoder) { 226 MOZ_ASSERT_UNREACHABLE("Running after decoding finished?"); 227 return; 228 } 229 230 while (true) { 231 // Run the decoder. 232 LexerResult result = mDecoder->Decode(WrapNotNull(this)); 233 234 if (result.is<TerminalState>()) { 235 // We may have a new frame now, but it's not guaranteed - a decoding 236 // failure or truncated data may mean that no new frame got produced. 237 // Since we're not sure, rather than call CheckForNewFrameAtYield() here 238 // we call CheckForNewFrameAtTerminalState(), which handles both of these 239 // possibilities. 240 bool continueDecoding = CheckForNewFrameAtTerminalState(); 241 FinishDecoding(); 242 243 // Even if it is the last frame, we may not have enough frames buffered 244 // ahead of the current. If we are shutting down, we want to ensure we 245 // release the thread as soon as possible. The animation may advance even 246 // during shutdown, which keeps us decoding, and thus blocking the decode 247 // pool during teardown. 248 if (!mDecoder || !continueDecoding || DecodePool::IsShuttingDown()) { 249 return; 250 } 251 252 // Restart from the very beginning because the decoder was recreated. 253 continue; 254 } 255 256 // If there is output available we want to change the entry in the surface 257 // cache from a placeholder to an actual surface now before NotifyProgress 258 // call below so that when consumers get the frame complete notification 259 // from the NotifyProgress they can actually get a surface from the surface 260 // cache. 261 bool checkForNewFrameAtYieldResult = false; 262 if (result == LexerResult(Yield::OUTPUT_AVAILABLE)) { 263 checkForNewFrameAtYieldResult = CheckForNewFrameAtYield(); 264 } 265 266 // Notify for the progress we've made so far. 267 if (mImage && mDecoder->HasProgress()) { 268 NotifyProgress(WrapNotNull(mImage), WrapNotNull(mDecoder)); 269 } 270 271 if (result == LexerResult(Yield::NEED_MORE_DATA)) { 272 // We can't make any more progress right now. The decoder itself will 273 // ensure that we get reenqueued when more data is available; just return 274 // for now. 275 return; 276 } 277 278 // There's new output available - a new frame! Grab it. If we don't need any 279 // more for the moment we can break out of the loop. If we are shutting 280 // down, we want to ensure we release the thread as soon as possible. The 281 // animation may advance even during shutdown, which keeps us decoding, and 282 // thus blocking the decode pool during teardown. 283 MOZ_ASSERT(result == LexerResult(Yield::OUTPUT_AVAILABLE)); 284 if (!checkForNewFrameAtYieldResult || DecodePool::IsShuttingDown()) { 285 return; 286 } 287 } 288 } 289 290 bool AnimationSurfaceProvider::CheckForNewFrameAtYield() { 291 mDecodingMutex.AssertCurrentThreadOwns(); 292 MOZ_ASSERT(mDecoder); 293 294 bool justGotFirstFrame = false; 295 bool continueDecoding = false; 296 297 { 298 MutexAutoLock lock(mFramesMutex); 299 300 // Try to get the new frame from the decoder. 301 RefPtr<imgFrame> frame = mDecoder->GetCurrentFrame(); 302 MOZ_ASSERT(mDecoder->HasFrameToTake()); 303 mDecoder->ClearHasFrameToTake(); 304 305 if (!frame) { 306 MOZ_ASSERT_UNREACHABLE("Decoder yielded but didn't produce a frame?"); 307 return true; 308 } 309 310 // We should've gotten a different frame than last time. 311 MOZ_ASSERT(!mFrames->IsLastInsertedFrame(frame)); 312 313 // Append the new frame to the list. 314 AnimationFrameBuffer::InsertStatus status = 315 mFrames->Insert(std::move(frame)); 316 317 // If we hit a redecode error, then we actually want to stop. This happens 318 // when we tried to insert more frames than we originally had (e.g. the 319 // original decoder attempt hit an OOM error sooner than we did). Better to 320 // stop the animation than to get out of sync with FrameAnimator. 321 if (mFrames->HasRedecodeError()) { 322 mDecoder = nullptr; 323 return false; 324 } 325 326 switch (status) { 327 case AnimationFrameBuffer::InsertStatus::DISCARD_CONTINUE: 328 continueDecoding = true; 329 [[fallthrough]]; 330 case AnimationFrameBuffer::InsertStatus::DISCARD_YIELD: 331 RequestFrameDiscarding(); 332 break; 333 case AnimationFrameBuffer::InsertStatus::CONTINUE: 334 continueDecoding = true; 335 break; 336 case AnimationFrameBuffer::InsertStatus::YIELD: 337 break; 338 default: 339 MOZ_ASSERT_UNREACHABLE("Unhandled insert status!"); 340 break; 341 } 342 343 // We only want to handle the first frame if it is the first pass for the 344 // animation decoder. The owning image will be cleared after that. 345 size_t frameCount = mFrames->Size(); 346 if (frameCount == 1 && mImage) { 347 justGotFirstFrame = true; 348 } 349 } 350 351 if (justGotFirstFrame) { 352 AnnounceSurfaceAvailable(); 353 } 354 355 return continueDecoding; 356 } 357 358 bool AnimationSurfaceProvider::CheckForNewFrameAtTerminalState() { 359 mDecodingMutex.AssertCurrentThreadOwns(); 360 MOZ_ASSERT(mDecoder); 361 362 bool justGotFirstFrame = false; 363 bool continueDecoding; 364 365 { 366 MutexAutoLock lock(mFramesMutex); 367 368 // The decoder may or may not have a new frame for us at this point. Avoid 369 // reinserting the same frame again. 370 RefPtr<imgFrame> frame = mDecoder->GetCurrentFrame(); 371 372 // If the decoder didn't finish a new frame (ie if, after starting the 373 // frame, it got an error and aborted the frame and the rest of the decode) 374 // that means it won't be reporting it to the image or FrameAnimator so we 375 // should ignore it too, that's what HasFrameToTake tracks basically. 376 if (!mDecoder->HasFrameToTake()) { 377 frame = nullptr; 378 } else { 379 MOZ_ASSERT(frame); 380 mDecoder->ClearHasFrameToTake(); 381 } 382 383 if (!frame || mFrames->IsLastInsertedFrame(frame)) { 384 return mFrames->MarkComplete(mDecoder->GetFirstFrameRefreshArea()); 385 } 386 387 // Append the new frame to the list. 388 AnimationFrameBuffer::InsertStatus status = 389 mFrames->Insert(std::move(frame)); 390 391 // If we hit a redecode error, then we actually want to stop. This will be 392 // fully handled in FinishDecoding. 393 if (mFrames->HasRedecodeError()) { 394 return false; 395 } 396 397 switch (status) { 398 case AnimationFrameBuffer::InsertStatus::DISCARD_CONTINUE: 399 case AnimationFrameBuffer::InsertStatus::DISCARD_YIELD: 400 RequestFrameDiscarding(); 401 break; 402 case AnimationFrameBuffer::InsertStatus::CONTINUE: 403 case AnimationFrameBuffer::InsertStatus::YIELD: 404 break; 405 default: 406 MOZ_ASSERT_UNREACHABLE("Unhandled insert status!"); 407 break; 408 } 409 410 continueDecoding = 411 mFrames->MarkComplete(mDecoder->GetFirstFrameRefreshArea()); 412 413 // We only want to handle the first frame if it is the first pass for the 414 // animation decoder. The owning image will be cleared after that. 415 if (mFrames->Size() == 1 && mImage) { 416 justGotFirstFrame = true; 417 } 418 } 419 420 if (justGotFirstFrame) { 421 AnnounceSurfaceAvailable(); 422 } 423 424 return continueDecoding; 425 } 426 427 void AnimationSurfaceProvider::RequestFrameDiscarding() { 428 mDecodingMutex.AssertCurrentThreadOwns(); 429 mFramesMutex.AssertCurrentThreadOwns(); 430 MOZ_ASSERT(mDecoder); 431 432 if (mFrames->MayDiscard() || mFrames->IsRecycling()) { 433 MOZ_ASSERT_UNREACHABLE("Already replaced frame queue!"); 434 return; 435 } 436 437 auto oldFrameQueue = 438 static_cast<AnimationFrameRetainedBuffer*>(mFrames.get()); 439 440 MOZ_ASSERT(!mDecoder->GetFrameRecycler()); 441 if (StaticPrefs::image_animated_decode_on_demand_recycle_AtStartup()) { 442 mFrames.reset(new AnimationFrameRecyclingQueue(std::move(*oldFrameQueue))); 443 mDecoder->SetFrameRecycler(this); 444 } else { 445 mFrames.reset(new AnimationFrameDiscardingQueue(std::move(*oldFrameQueue))); 446 } 447 } 448 449 void AnimationSurfaceProvider::AnnounceSurfaceAvailable() { 450 mFramesMutex.AssertNotCurrentThreadOwns(); 451 MOZ_ASSERT(mImage); 452 453 // We just got the first frame; let the surface cache know. We deliberately do 454 // this outside of mFramesMutex to avoid a potential deadlock with 455 // AddSizeOfExcludingThis(), since otherwise we'd be acquiring mFramesMutex 456 // and then the surface cache lock, while the memory reporting code would 457 // acquire the surface cache lock and then mFramesMutex. 458 SurfaceCache::SurfaceAvailable(WrapNotNull(this)); 459 } 460 461 void AnimationSurfaceProvider::FinishDecoding() { 462 mDecodingMutex.AssertCurrentThreadOwns(); 463 MOZ_ASSERT(mDecoder); 464 465 if (mImage) { 466 // Send notifications. 467 NotifyDecodeComplete(WrapNotNull(mImage), WrapNotNull(mDecoder)); 468 } 469 470 // Determine if we need to recreate the decoder, in case we are discarding 471 // frames and need to loop back to the beginning. 472 bool recreateDecoder; 473 { 474 MutexAutoLock lock(mFramesMutex); 475 recreateDecoder = !mFrames->HasRedecodeError() && mFrames->MayDiscard(); 476 } 477 478 if (recreateDecoder) { 479 mDecoder = DecoderFactory::CloneAnimationDecoder(mDecoder); 480 MOZ_ASSERT(mDecoder); 481 } else { 482 mDecoder = nullptr; 483 } 484 485 // We don't need a reference to our image anymore, either, and we don't want 486 // one. We may be stored in the surface cache for a long time after decoding 487 // finishes. If we don't drop our reference to the image, we'll end up 488 // keeping it alive as long as we remain in the surface cache, which could 489 // greatly extend the image's lifetime - in fact, if the image isn't 490 // discardable, it'd result in a leak! 491 DropImageReference(); 492 } 493 494 bool AnimationSurfaceProvider::ShouldPreferSyncRun() const { 495 MutexAutoLock lock(mDecodingMutex); 496 MOZ_ASSERT(mDecoder); 497 498 return mDecoder->ShouldSyncDecode( 499 StaticPrefs::image_mem_decode_bytes_at_a_time_AtStartup()); 500 } 501 502 RawAccessFrameRef AnimationSurfaceProvider::RecycleFrame( 503 gfx::IntRect& aRecycleRect) { 504 MutexAutoLock lock(mFramesMutex); 505 MOZ_ASSERT(mFrames->IsRecycling()); 506 return mFrames->RecycleFrame(aRecycleRect); 507 } 508 509 nsresult AnimationSurfaceProvider::UpdateKey( 510 layers::RenderRootStateManager* aManager, 511 wr::IpcResourceUpdateQueue& aResources, wr::ImageKey& aKey) { 512 MOZ_ASSERT(NS_IsMainThread()); 513 514 RefPtr<SourceSurface> surface; 515 { 516 MutexAutoLock lock(mFramesMutex); 517 imgFrame* frame = 518 mFrames->Get(mFrames->Displayed(), /* aForDisplay */ true); 519 if (!frame) { 520 return NS_ERROR_NOT_AVAILABLE; 521 } 522 523 surface = frame->GetSourceSurface(); 524 } 525 526 mCompositedFrameRequested = true; 527 auto* sharedSurface = static_cast<SourceSurfaceSharedData*>(surface.get()); 528 return mSharedAnimation->UpdateKey(sharedSurface, aManager, aResources, aKey); 529 } 530 531 } // namespace image 532 } // namespace mozilla