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