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