SharedSurfacesChild.cpp (20940B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "SharedSurfacesChild.h" 8 #include "CompositorManagerChild.h" 9 #include "mozilla/layers/IpcResourceUpdateQueue.h" 10 #include "mozilla/layers/SourceSurfaceSharedData.h" 11 #include "mozilla/layers/WebRenderBridgeChild.h" 12 #include "mozilla/layers/RenderRootStateManager.h" 13 #include "mozilla/layers/WebRenderLayerManager.h" 14 #include "mozilla/layers/CompositorBridgeChild.h" 15 #include "mozilla/layers/CompositorManagerParent.h" 16 #include "mozilla/SchedulerGroup.h" 17 #include "mozilla/StaticPrefs_image.h" 18 #include "mozilla/PresShell.h" 19 #include "nsRefreshDriver.h" 20 21 namespace mozilla { 22 namespace layers { 23 24 using namespace mozilla::gfx; 25 26 /* static */ 27 UserDataKey SharedSurfacesChild::sSharedKey; 28 29 SharedSurfacesChild::ImageKeyData::ImageKeyData( 30 RenderRootStateManager* aManager, const wr::ImageKey& aImageKey) 31 : mManager(aManager), mImageKey(aImageKey) {} 32 33 SharedSurfacesChild::ImageKeyData::ImageKeyData( 34 SharedSurfacesChild::ImageKeyData&& aOther) 35 : mManager(std::move(aOther.mManager)), 36 mDirtyRect(std::move(aOther.mDirtyRect)), 37 mImageKey(aOther.mImageKey) {} 38 39 SharedSurfacesChild::ImageKeyData& SharedSurfacesChild::ImageKeyData::operator=( 40 SharedSurfacesChild::ImageKeyData&& aOther) { 41 mManager = std::move(aOther.mManager); 42 mDirtyRect = std::move(aOther.mDirtyRect); 43 mImageKey = aOther.mImageKey; 44 return *this; 45 } 46 47 SharedSurfacesChild::ImageKeyData::~ImageKeyData() = default; 48 49 void SharedSurfacesChild::ImageKeyData::MergeDirtyRect( 50 const Maybe<IntRect>& aDirtyRect) { 51 if (mDirtyRect) { 52 if (aDirtyRect) { 53 mDirtyRect->UnionRect(mDirtyRect.ref(), aDirtyRect.ref()); 54 } 55 } else { 56 mDirtyRect = aDirtyRect; 57 } 58 } 59 60 SharedSurfacesChild::SharedUserData::SharedUserData() 61 : Runnable("SharedSurfacesChild::SharedUserData"), 62 mId({}), 63 mShared(false) {} 64 65 SharedSurfacesChild::SharedUserData::~SharedUserData() { 66 // We may fail to dispatch during shutdown, and since it would be issued on 67 // the main thread, it releases the runnable instead of leaking it. 68 if (mShared || !mKeys.IsEmpty()) { 69 if (NS_IsMainThread()) { 70 SharedSurfacesChild::Unshare(mId, mShared, mKeys); 71 } else { 72 MOZ_ASSERT_UNREACHABLE("Shared resources not released!"); 73 } 74 } 75 } 76 77 /* static */ 78 void SharedSurfacesChild::SharedUserData::Destroy(void* aClosure) { 79 MOZ_ASSERT(aClosure); 80 RefPtr<SharedUserData> data = 81 dont_AddRef(static_cast<SharedUserData*>(aClosure)); 82 if (data->mShared || !data->mKeys.IsEmpty()) { 83 SchedulerGroup::Dispatch(data.forget()); 84 } 85 } 86 87 NS_IMETHODIMP SharedSurfacesChild::SharedUserData::Run() { 88 SharedSurfacesChild::Unshare(mId, mShared, mKeys); 89 mShared = false; 90 mKeys.Clear(); 91 return NS_OK; 92 } 93 94 wr::ImageKey SharedSurfacesChild::SharedUserData::UpdateKey( 95 RenderRootStateManager* aManager, wr::IpcResourceUpdateQueue& aResources, 96 const Maybe<IntRect>& aDirtyRect) { 97 MOZ_ASSERT(aManager); 98 MOZ_ASSERT(!aManager->IsDestroyed()); 99 100 // We iterate through all of the items to ensure we clean up the old 101 // RenderRootStateManager references. Most of the time there will be few 102 // entries and this should not be particularly expensive compared to the 103 // cost of duplicating image keys. In an ideal world, we would generate a 104 // single key for the surface, and it would be usable on all of the 105 // renderer instances. For now, we must allocate a key for each WR bridge. 106 wr::ImageKey key; 107 bool found = false; 108 auto i = mKeys.Length(); 109 while (i > 0) { 110 --i; 111 ImageKeyData& entry = mKeys[i]; 112 if (entry.mManager->IsDestroyed()) { 113 mKeys.RemoveElementAt(i); 114 } else if (entry.mManager == aManager) { 115 WebRenderBridgeChild* wrBridge = aManager->WrBridge(); 116 MOZ_ASSERT(wrBridge); 117 118 // Even if the manager is the same, its underlying WebRenderBridgeChild 119 // can change state. If our namespace differs, then our old key has 120 // already been discarded. 121 bool ownsKey = wrBridge->GetNamespace() == entry.mImageKey.mNamespace; 122 if (!ownsKey) { 123 entry.mImageKey = wrBridge->GetNextImageKey(); 124 entry.TakeDirtyRect(); 125 aResources.AddSharedExternalImage(mId, entry.mImageKey); 126 } else { 127 entry.MergeDirtyRect(aDirtyRect); 128 Maybe<IntRect> dirtyRect = entry.TakeDirtyRect(); 129 if (dirtyRect) { 130 MOZ_ASSERT(mShared); 131 aResources.UpdateSharedExternalImage( 132 mId, entry.mImageKey, ViewAs<ImagePixel>(dirtyRect.ref())); 133 } 134 } 135 136 key = entry.mImageKey; 137 found = true; 138 } else { 139 // We don't have the resource update queue for this manager, so just 140 // accumulate the dirty rects until it is requested. 141 entry.MergeDirtyRect(aDirtyRect); 142 } 143 } 144 145 if (!found) { 146 key = aManager->WrBridge()->GetNextImageKey(); 147 ImageKeyData data(aManager, key); 148 mKeys.AppendElement(std::move(data)); 149 aResources.AddSharedExternalImage(mId, key); 150 } 151 152 return key; 153 } 154 155 /* static */ 156 SourceSurfaceSharedData* SharedSurfacesChild::AsSourceSurfaceSharedData( 157 SourceSurface* aSurface) { 158 MOZ_ASSERT(aSurface); 159 switch (aSurface->GetType()) { 160 case SurfaceType::DATA_SHARED: 161 case SurfaceType::DATA_RECYCLING_SHARED: 162 return static_cast<SourceSurfaceSharedData*>(aSurface); 163 default: 164 return nullptr; 165 } 166 } 167 168 /* static */ 169 nsresult SharedSurfacesChild::ShareInternal(SourceSurfaceSharedData* aSurface, 170 SharedUserData** aUserData) { 171 MOZ_ASSERT(NS_IsMainThread()); 172 MOZ_ASSERT(aSurface); 173 MOZ_ASSERT(aUserData); 174 175 CompositorManagerChild* manager = CompositorManagerChild::GetInstance(); 176 if (NS_WARN_IF(!manager || !manager->CanSend())) { 177 // We cannot try to share the surface, most likely because the GPU process 178 // crashed. Ideally, we would retry when it is ready, but the handles may be 179 // a scarce resource, which can cause much more serious problems if we run 180 // out. Better to copy into a fresh buffer later. 181 aSurface->FinishedSharing(); 182 return NS_ERROR_NOT_INITIALIZED; 183 } 184 185 SharedUserData* data = 186 static_cast<SharedUserData*>(aSurface->GetUserData(&sSharedKey)); 187 if (!data) { 188 data = MakeAndAddRef<SharedUserData>().take(); 189 aSurface->AddUserData(&sSharedKey, data, SharedUserData::Destroy); 190 } else if (data->IsShared()) { 191 if (manager->OwnsExternalImageId(data->Id())) { 192 // It has already been shared with the GPU process. 193 *aUserData = data; 194 return NS_OK; 195 } 196 197 // If the id isn't owned by us, that means the bridge was reinitialized, due 198 // to the GPU process crashing. All previous mappings have been released. 199 data->ClearShared(); 200 } 201 202 // Ensure that the handle doesn't get released until after we have finished 203 // sending the buffer to the GPU process and/or reallocating it. 204 // FinishedSharing is not a sufficient condition because another thread may 205 // decide we are done while we are in the processing of sharing our newly 206 // reallocated handle. Once it goes out of scope, it may release the handle. 207 SourceSurfaceSharedData::HandleLock lock(aSurface); 208 209 // If we live in the same process, then it is a simple matter of directly 210 // asking the parent instance to store a pointer to the same data, no need 211 // to map the data into our memory space twice. 212 if (manager->SameProcess()) { 213 data->MarkShared(manager->GetNextExternalImageId()); 214 CompositorManagerParent::AddSharedSurface(data->Id(), aSurface); 215 *aUserData = data; 216 return NS_OK; 217 } 218 219 // Attempt to share a handle with the GPU process. The handle may or may not 220 // be available -- it will only be available if it is either not yet finalized 221 // and/or if it has been finalized but never used for drawing in process. 222 ipc::ReadOnlySharedMemoryHandle handle; 223 nsresult rv = aSurface->CloneHandle(handle); 224 if (rv == NS_ERROR_NOT_AVAILABLE) { 225 // It is at least as expensive to copy the image to the GPU process if we 226 // have already closed the handle necessary to share, but if we reallocate 227 // the shared buffer to get a new handle, we can save some memory. 228 if (NS_WARN_IF(!aSurface->ReallocHandle())) { 229 return NS_ERROR_OUT_OF_MEMORY; 230 } 231 232 // Reattempt the sharing of the handle to the GPU process. 233 rv = aSurface->CloneHandle(handle); 234 } 235 236 if (NS_WARN_IF(NS_FAILED(rv))) { 237 MOZ_ASSERT(rv != NS_ERROR_NOT_AVAILABLE); 238 return rv; 239 } 240 241 SurfaceFormat format = aSurface->GetFormat(); 242 MOZ_RELEASE_ASSERT( 243 format == SurfaceFormat::B8G8R8X8 || format == SurfaceFormat::B8G8R8A8, 244 "bad format"); 245 246 data->MarkShared(manager->GetNextExternalImageId()); 247 manager->SendAddSharedSurface( 248 data->Id(), 249 SurfaceDescriptorShared(aSurface->GetSize(), aSurface->Stride(), format, 250 std::move(handle))); 251 *aUserData = data; 252 return NS_OK; 253 } 254 255 /* static */ 256 void SharedSurfacesChild::Share(SourceSurfaceSharedData* aSurface) { 257 MOZ_ASSERT(aSurface); 258 259 // The IPDL actor to do sharing can only be accessed on the main thread so we 260 // need to dispatch if off the main thread. However there is no real danger if 261 // we end up racing because if it is already shared, this method will do 262 // nothing. 263 if (!NS_IsMainThread()) { 264 class ShareRunnable final : public Runnable { 265 public: 266 explicit ShareRunnable(SourceSurfaceSharedData* aSurface) 267 : Runnable("SharedSurfacesChild::Share"), mSurface(aSurface) {} 268 269 NS_IMETHOD Run() override { 270 SharedUserData* unused = nullptr; 271 SharedSurfacesChild::ShareInternal(mSurface, &unused); 272 return NS_OK; 273 } 274 275 private: 276 RefPtr<SourceSurfaceSharedData> mSurface; 277 }; 278 279 SchedulerGroup::Dispatch(MakeAndAddRef<ShareRunnable>(aSurface)); 280 return; 281 } 282 283 SharedUserData* unused = nullptr; 284 SharedSurfacesChild::ShareInternal(aSurface, &unused); 285 } 286 287 /* static */ 288 nsresult SharedSurfacesChild::Share(SourceSurfaceSharedData* aSurface, 289 RenderRootStateManager* aManager, 290 wr::IpcResourceUpdateQueue& aResources, 291 wr::ImageKey& aKey) { 292 MOZ_ASSERT(NS_IsMainThread()); 293 MOZ_ASSERT(aSurface); 294 MOZ_ASSERT(aManager); 295 296 // Each time the surface changes, the producers of SourceSurfaceSharedData 297 // surfaces promise to increment the invalidation counter each time the 298 // surface has changed. We can use this counter to determine whether or not 299 // we should update our paired ImageKey. 300 Maybe<IntRect> dirtyRect = aSurface->TakeDirtyRect(); 301 SharedUserData* data = nullptr; 302 nsresult rv = SharedSurfacesChild::ShareInternal(aSurface, &data); 303 if (NS_SUCCEEDED(rv)) { 304 MOZ_ASSERT(data); 305 aKey = data->UpdateKey(aManager, aResources, dirtyRect); 306 } 307 308 return rv; 309 } 310 311 /* static */ 312 nsresult SharedSurfacesChild::Share(SourceSurface* aSurface, 313 RenderRootStateManager* aManager, 314 wr::IpcResourceUpdateQueue& aResources, 315 wr::ImageKey& aKey) { 316 MOZ_ASSERT(NS_IsMainThread()); 317 MOZ_ASSERT(aSurface); 318 MOZ_ASSERT(aManager); 319 320 auto sharedSurface = AsSourceSurfaceSharedData(aSurface); 321 if (!sharedSurface) { 322 return NS_ERROR_NOT_IMPLEMENTED; 323 } 324 325 return Share(sharedSurface, aManager, aResources, aKey); 326 } 327 328 /* static */ 329 nsresult SharedSurfacesChild::Share(SourceSurface* aSurface, 330 wr::ExternalImageId& aId) { 331 MOZ_ASSERT(NS_IsMainThread()); 332 MOZ_ASSERT(aSurface); 333 334 auto sharedSurface = AsSourceSurfaceSharedData(aSurface); 335 if (!sharedSurface) { 336 return NS_ERROR_NOT_IMPLEMENTED; 337 } 338 339 // The external image ID does not change with the invalidation counter. The 340 // caller of this should be aware of the invalidations of the surface through 341 // another mechanism (e.g. imgRequestProxy listener notifications). 342 SharedUserData* data = nullptr; 343 nsresult rv = ShareInternal(sharedSurface, &data); 344 if (NS_SUCCEEDED(rv)) { 345 MOZ_ASSERT(data); 346 aId = data->Id(); 347 } 348 349 return rv; 350 } 351 352 /* static */ nsresult SharedSurfacesChild::Share( 353 gfx::SourceSurface* aSurface, Maybe<SurfaceDescriptor>& aDesc) { 354 if (!aSurface) { 355 return NS_ERROR_INVALID_ARG; 356 } 357 358 // TODO(aosmond): With a refactor of how we store the external image ID, we 359 // could probably make it safe to access off the main thread. This would be 360 // useful for OffscreenCanvas on DOM workers. 361 if (!NS_IsMainThread()) { 362 return NS_ERROR_UNEXPECTED; 363 } 364 365 wr::ExternalImageId extId{}; 366 nsresult rv = Share(aSurface, extId); 367 if (NS_FAILED(rv)) { 368 return rv; 369 } 370 371 aDesc = Some(SurfaceDescriptorExternalImage( 372 wr::ExternalImageSource::SharedSurfaces, extId)); 373 return NS_OK; 374 } 375 376 /* static */ 377 void SharedSurfacesChild::Unshare(const wr::ExternalImageId& aId, 378 bool aReleaseId, 379 nsTArray<ImageKeyData>& aKeys) { 380 MOZ_ASSERT(NS_IsMainThread()); 381 382 for (const auto& entry : aKeys) { 383 if (!entry.mManager->IsDestroyed()) { 384 entry.mManager->AddImageKeyForDiscard(entry.mImageKey); 385 } 386 } 387 388 if (!aReleaseId) { 389 // We don't own the external image ID itself. 390 return; 391 } 392 393 CompositorManagerChild* manager = CompositorManagerChild::GetInstance(); 394 if (MOZ_UNLIKELY(!manager || !manager->CanSend())) { 395 return; 396 } 397 398 if (manager->OwnsExternalImageId(aId)) { 399 // Only attempt to release current mappings in the compositor process. It is 400 // possible we had a surface that was previously shared, the compositor 401 // process crashed / was restarted, and then we freed the surface. In that 402 // case we know the mapping has already been freed. 403 manager->SendRemoveSharedSurface(aId); 404 } 405 } 406 407 /* static */ Maybe<wr::ExternalImageId> SharedSurfacesChild::GetExternalId( 408 const SourceSurfaceSharedData* aSurface) { 409 MOZ_ASSERT(NS_IsMainThread()); 410 MOZ_ASSERT(aSurface); 411 412 SharedUserData* data = 413 static_cast<SharedUserData*>(aSurface->GetUserData(&sSharedKey)); 414 if (!data || !data->IsShared()) { 415 return Nothing(); 416 } 417 418 return Some(data->Id()); 419 } 420 421 AnimationImageKeyData::AnimationImageKeyData(RenderRootStateManager* aManager, 422 const wr::ImageKey& aImageKey) 423 : SharedSurfacesChild::ImageKeyData(aManager, aImageKey) {} 424 425 AnimationImageKeyData::AnimationImageKeyData(AnimationImageKeyData&& aOther) 426 : SharedSurfacesChild::ImageKeyData(std::move(aOther)), 427 mPendingRelease(std::move(aOther.mPendingRelease)) {} 428 429 AnimationImageKeyData& AnimationImageKeyData::operator=( 430 AnimationImageKeyData&& aOther) { 431 mPendingRelease = std::move(aOther.mPendingRelease); 432 SharedSurfacesChild::ImageKeyData::operator=(std::move(aOther)); 433 return *this; 434 } 435 436 AnimationImageKeyData::~AnimationImageKeyData() = default; 437 438 SharedSurfacesAnimation::~SharedSurfacesAnimation() { 439 MOZ_ASSERT(mKeys.IsEmpty()); 440 } 441 442 void SharedSurfacesAnimation::Destroy() { 443 if (!NS_IsMainThread()) { 444 nsCOMPtr<nsIRunnable> task = 445 NewRunnableMethod("SharedSurfacesAnimation::Destroy", this, 446 &SharedSurfacesAnimation::Destroy); 447 NS_DispatchToMainThread(task.forget()); 448 return; 449 } 450 451 if (mKeys.IsEmpty()) { 452 return; 453 } 454 455 for (const auto& entry : mKeys) { 456 MOZ_ASSERT(!entry.mManager->IsDestroyed()); 457 if (StaticPrefs::image_animated_decode_on_demand_recycle_AtStartup()) { 458 entry.mManager->DeregisterAsyncAnimation(entry.mImageKey); 459 } 460 entry.mManager->AddImageKeyForDiscard(entry.mImageKey); 461 } 462 463 mKeys.Clear(); 464 } 465 466 void SharedSurfacesAnimation::HoldSurfaceForRecycling( 467 AnimationImageKeyData& aEntry, SourceSurfaceSharedData* aSurface) { 468 if (aSurface->GetType() != SurfaceType::DATA_RECYCLING_SHARED) { 469 return; 470 } 471 472 MOZ_ASSERT(StaticPrefs::image_animated_decode_on_demand_recycle_AtStartup()); 473 aEntry.mPendingRelease.AppendElement(aSurface); 474 } 475 476 nsresult SharedSurfacesAnimation::SetCurrentFrame( 477 SourceSurfaceSharedData* aSurface, const gfx::IntRect& aDirtyRect) { 478 MOZ_ASSERT(aSurface); 479 480 SharedSurfacesChild::SharedUserData* data = nullptr; 481 nsresult rv = SharedSurfacesChild::ShareInternal(aSurface, &data); 482 if (NS_FAILED(rv)) { 483 return rv; 484 } 485 486 MOZ_ASSERT(data); 487 mId = data->Id(); 488 489 auto i = mKeys.Length(); 490 while (i > 0) { 491 --i; 492 AnimationImageKeyData& entry = mKeys[i]; 493 MOZ_ASSERT(!entry.mManager->IsDestroyed()); 494 495 if (auto* cbc = 496 entry.mManager->LayerManager()->GetCompositorBridgeChild()) { 497 if (cbc->IsPaused()) { 498 continue; 499 } 500 } 501 502 // Only root compositor bridge childs record if they are paused, so check 503 // the refresh driver. 504 if (auto* widget = entry.mManager->LayerManager()->GetWidget()) { 505 if (auto* ps = widget->GetPresShell()) { 506 if (auto* rd = ps->GetRefreshDriver(); rd && rd->IsThrottled()) { 507 continue; 508 } 509 } 510 } 511 512 entry.MergeDirtyRect(Some(aDirtyRect)); 513 Maybe<IntRect> dirtyRect = entry.TakeDirtyRect(); 514 if (dirtyRect) { 515 HoldSurfaceForRecycling(entry, aSurface); 516 auto& resourceUpdates = entry.mManager->AsyncResourceUpdates(); 517 resourceUpdates.UpdateSharedExternalImage( 518 mId, entry.mImageKey, ViewAs<ImagePixel>(dirtyRect.ref())); 519 } 520 } 521 522 return NS_OK; 523 } 524 525 nsresult SharedSurfacesAnimation::UpdateKey( 526 SourceSurfaceSharedData* aSurface, RenderRootStateManager* aManager, 527 wr::IpcResourceUpdateQueue& aResources, wr::ImageKey& aKey) { 528 SharedSurfacesChild::SharedUserData* data = nullptr; 529 nsresult rv = SharedSurfacesChild::ShareInternal(aSurface, &data); 530 if (NS_FAILED(rv)) { 531 return rv; 532 } 533 534 MOZ_ASSERT(data); 535 if (wr::AsUint64(mId) != wr::AsUint64(data->Id())) { 536 mKeys.Clear(); 537 mId = data->Id(); 538 } 539 540 // We iterate through all of the items to ensure we clean up the old 541 // RenderRootStateManager references. Most of the time there will be few 542 // entries and this should not be particularly expensive compared to the 543 // cost of duplicating image keys. In an ideal world, we would generate a 544 // single key for the surface, and it would be usable on all of the 545 // renderer instances. For now, we must allocate a key for each WR bridge. 546 bool found = false; 547 auto i = mKeys.Length(); 548 while (i > 0) { 549 --i; 550 AnimationImageKeyData& entry = mKeys[i]; 551 MOZ_ASSERT(!entry.mManager->IsDestroyed()); 552 if (entry.mManager == aManager) { 553 WebRenderBridgeChild* wrBridge = aManager->WrBridge(); 554 MOZ_ASSERT(wrBridge); 555 556 // Even if the manager is the same, its underlying WebRenderBridgeChild 557 // can change state. If our namespace differs, then our old key has 558 // already been discarded. 559 bool ownsKey = wrBridge->GetNamespace() == entry.mImageKey.mNamespace; 560 if (!ownsKey) { 561 entry.mImageKey = wrBridge->GetNextImageKey(); 562 HoldSurfaceForRecycling(entry, aSurface); 563 aResources.AddSharedExternalImage(mId, entry.mImageKey); 564 } else { 565 MOZ_ASSERT(entry.mDirtyRect.isNothing()); 566 } 567 568 aKey = entry.mImageKey; 569 found = true; 570 break; 571 } 572 } 573 574 if (!found) { 575 aKey = aManager->WrBridge()->GetNextImageKey(); 576 if (StaticPrefs::image_animated_decode_on_demand_recycle_AtStartup()) { 577 aManager->RegisterAsyncAnimation(aKey, this); 578 } 579 580 AnimationImageKeyData data(aManager, aKey); 581 HoldSurfaceForRecycling(data, aSurface); 582 mKeys.AppendElement(std::move(data)); 583 aResources.AddSharedExternalImage(mId, aKey); 584 } 585 586 return NS_OK; 587 } 588 589 void SharedSurfacesAnimation::ReleasePreviousFrame( 590 RenderRootStateManager* aManager, const wr::ExternalImageId& aId) { 591 MOZ_ASSERT(aManager); 592 593 auto i = mKeys.Length(); 594 while (i > 0) { 595 --i; 596 AnimationImageKeyData& entry = mKeys[i]; 597 MOZ_ASSERT(!entry.mManager->IsDestroyed()); 598 if (entry.mManager == aManager) { 599 size_t k; 600 for (k = 0; k < entry.mPendingRelease.Length(); ++k) { 601 Maybe<wr::ExternalImageId> extId = 602 SharedSurfacesChild::GetExternalId(entry.mPendingRelease[k]); 603 if (extId && extId.ref() == aId) { 604 break; 605 } 606 } 607 608 if (k == entry.mPendingRelease.Length()) { 609 continue; 610 } 611 612 entry.mPendingRelease.RemoveElementsAt(0, k + 1); 613 break; 614 } 615 } 616 } 617 618 void SharedSurfacesAnimation::Invalidate(RenderRootStateManager* aManager) { 619 auto i = mKeys.Length(); 620 while (i > 0) { 621 --i; 622 AnimationImageKeyData& entry = mKeys[i]; 623 if (entry.mManager == aManager) { 624 mKeys.RemoveElementAt(i); 625 break; 626 } 627 } 628 } 629 630 } // namespace layers 631 } // namespace mozilla