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 "AsyncImagePipelineManager.h"
8 
9 #include <algorithm>
10 #include <iterator>
11 
12 #include "CompositableHost.h"
13 #include "gfxEnv.h"
14 #include "mozilla/gfx/gfxVars.h"
15 #include "mozilla/layers/CompositorThread.h"
16 #include "mozilla/layers/SharedSurfacesParent.h"
17 #include "mozilla/layers/WebRenderImageHost.h"
18 #include "mozilla/layers/WebRenderTextureHost.h"
19 #include "mozilla/webrender/RenderThread.h"
20 #include "mozilla/webrender/WebRenderAPI.h"
21 #include "mozilla/webrender/WebRenderTypes.h"
22 
23 #ifdef MOZ_WIDGET_ANDROID
24 #  include "mozilla/layers/TextureHostOGL.h"
25 #endif
26 
27 namespace mozilla {
28 namespace layers {
29 
~ForwardingExternalImage()30 AsyncImagePipelineManager::ForwardingExternalImage::~ForwardingExternalImage() {
31   DebugOnly<bool> released = SharedSurfacesParent::Release(mImageId);
32   MOZ_ASSERT(released);
33 }
34 
AsyncImagePipeline()35 AsyncImagePipelineManager::AsyncImagePipeline::AsyncImagePipeline()
36     : mInitialised(false),
37       mIsChanged(false),
38       mUseExternalImage(false),
39       mRotation(VideoInfo::Rotation::kDegree_0),
40       mFilter(wr::ImageRendering::Auto),
41       mMixBlendMode(wr::MixBlendMode::Normal) {}
42 
AsyncImagePipelineManager(RefPtr<wr::WebRenderAPI> && aApi,bool aUseCompositorWnd)43 AsyncImagePipelineManager::AsyncImagePipelineManager(
44     RefPtr<wr::WebRenderAPI>&& aApi, bool aUseCompositorWnd)
45     : mApi(aApi),
46       mUseCompositorWnd(aUseCompositorWnd),
47       mIdNamespace(mApi->GetNamespace()),
48       mUseTripleBuffering(mApi->GetUseTripleBuffering()),
49       mResourceId(0),
50       mAsyncImageEpoch{0},
51       mWillGenerateFrame(false),
52       mDestroyed(false),
53 #ifdef XP_WIN
54       mUseWebRenderDCompVideoOverlayWin(
55           gfx::gfxVars::UseWebRenderDCompVideoOverlayWin()),
56 #endif
57       mRenderSubmittedUpdatesLock("SubmittedUpdatesLock"),
58       mLastCompletedFrameId(0) {
59   MOZ_COUNT_CTOR(AsyncImagePipelineManager);
60 }
61 
~AsyncImagePipelineManager()62 AsyncImagePipelineManager::~AsyncImagePipelineManager() {
63   MOZ_COUNT_DTOR(AsyncImagePipelineManager);
64 }
65 
Destroy()66 void AsyncImagePipelineManager::Destroy() {
67   MOZ_ASSERT(!mDestroyed);
68   mApi = nullptr;
69   mPipelineTexturesHolders.Clear();
70   mDestroyed = true;
71 }
72 
73 /* static */
GetNextExternalImageId()74 wr::ExternalImageId AsyncImagePipelineManager::GetNextExternalImageId() {
75   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
76   static uint64_t sResourceId = 0;
77 
78   ++sResourceId;
79   // Upper 32bit(namespace) needs to be 0.
80   // Namespace other than 0 might be used by others.
81   MOZ_RELEASE_ASSERT(sResourceId != UINT32_MAX);
82   return wr::ToExternalImageId(sResourceId);
83 }
84 
SetWillGenerateFrame()85 void AsyncImagePipelineManager::SetWillGenerateFrame() {
86   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
87 
88   mWillGenerateFrame = true;
89 }
90 
GetAndResetWillGenerateFrame()91 bool AsyncImagePipelineManager::GetAndResetWillGenerateFrame() {
92   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
93 
94   bool ret = mWillGenerateFrame;
95   mWillGenerateFrame = false;
96   return ret;
97 }
98 
AddPipeline(const wr::PipelineId & aPipelineId,WebRenderBridgeParent * aWrBridge)99 void AsyncImagePipelineManager::AddPipeline(const wr::PipelineId& aPipelineId,
100                                             WebRenderBridgeParent* aWrBridge) {
101   if (mDestroyed) {
102     return;
103   }
104 
105   mPipelineTexturesHolders.WithEntryHandle(
106       wr::AsUint64(aPipelineId), [&](auto&& holder) {
107         if (holder) {
108           // This could happen during tab move between different windows.
109           // Previously removed holder could be still alive for waiting
110           // destroyed.
111           MOZ_ASSERT(holder.Data()->mDestroyedEpoch.isSome());
112           holder.Data()->mDestroyedEpoch = Nothing();  // Revive holder
113           holder.Data()->mWrBridge = aWrBridge;
114           return;
115         }
116 
117         holder.Insert(MakeUnique<PipelineTexturesHolder>())->mWrBridge =
118             aWrBridge;
119       });
120 }
121 
RemovePipeline(const wr::PipelineId & aPipelineId,const wr::Epoch & aEpoch)122 void AsyncImagePipelineManager::RemovePipeline(
123     const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch) {
124   if (mDestroyed) {
125     return;
126   }
127 
128   PipelineTexturesHolder* holder =
129       mPipelineTexturesHolders.Get(wr::AsUint64(aPipelineId));
130   MOZ_ASSERT(holder);
131   if (!holder) {
132     return;
133   }
134   holder->mWrBridge = nullptr;
135   holder->mDestroyedEpoch = Some(aEpoch);
136 }
137 
GetWrBridge(const wr::PipelineId & aPipelineId)138 WebRenderBridgeParent* AsyncImagePipelineManager::GetWrBridge(
139     const wr::PipelineId& aPipelineId) {
140   if (mDestroyed) {
141     return nullptr;
142   }
143 
144   PipelineTexturesHolder* holder =
145       mPipelineTexturesHolders.Get(wr::AsUint64(aPipelineId));
146   if (!holder) {
147     return nullptr;
148   }
149   if (holder->mWrBridge) {
150     MOZ_ASSERT(holder->mDestroyedEpoch.isNothing());
151     return holder->mWrBridge;
152   }
153 
154   return nullptr;
155 }
156 
AddAsyncImagePipeline(const wr::PipelineId & aPipelineId,WebRenderImageHost * aImageHost)157 void AsyncImagePipelineManager::AddAsyncImagePipeline(
158     const wr::PipelineId& aPipelineId, WebRenderImageHost* aImageHost) {
159   if (mDestroyed) {
160     return;
161   }
162   MOZ_ASSERT(aImageHost);
163   uint64_t id = wr::AsUint64(aPipelineId);
164 
165   MOZ_ASSERT(!mAsyncImagePipelines.Contains(id));
166   auto holder = MakeUnique<AsyncImagePipeline>();
167   holder->mImageHost = aImageHost;
168   mAsyncImagePipelines.InsertOrUpdate(id, std::move(holder));
169   AddPipeline(aPipelineId, /* aWrBridge */ nullptr);
170 }
171 
RemoveAsyncImagePipeline(const wr::PipelineId & aPipelineId,wr::TransactionBuilder & aTxn)172 void AsyncImagePipelineManager::RemoveAsyncImagePipeline(
173     const wr::PipelineId& aPipelineId, wr::TransactionBuilder& aTxn) {
174   if (mDestroyed) {
175     return;
176   }
177 
178   uint64_t id = wr::AsUint64(aPipelineId);
179   if (auto entry = mAsyncImagePipelines.Lookup(id)) {
180     const auto& holder = entry.Data();
181     wr::Epoch epoch = GetNextImageEpoch();
182     aTxn.ClearDisplayList(epoch, aPipelineId);
183     for (wr::ImageKey key : holder->mKeys) {
184       aTxn.DeleteImage(key);
185     }
186     entry.Remove();
187     RemovePipeline(aPipelineId, epoch);
188   }
189 }
190 
UpdateAsyncImagePipeline(const wr::PipelineId & aPipelineId,const LayoutDeviceRect & aScBounds,const VideoInfo::Rotation aRotation,const wr::ImageRendering & aFilter,const wr::MixBlendMode & aMixBlendMode)191 void AsyncImagePipelineManager::UpdateAsyncImagePipeline(
192     const wr::PipelineId& aPipelineId, const LayoutDeviceRect& aScBounds,
193     const VideoInfo::Rotation aRotation, const wr::ImageRendering& aFilter,
194     const wr::MixBlendMode& aMixBlendMode) {
195   if (mDestroyed) {
196     return;
197   }
198   AsyncImagePipeline* pipeline =
199       mAsyncImagePipelines.Get(wr::AsUint64(aPipelineId));
200   if (!pipeline) {
201     return;
202   }
203   pipeline->mInitialised = true;
204   pipeline->Update(aScBounds, aRotation, aFilter, aMixBlendMode);
205 }
206 
UpdateImageKeys(const wr::Epoch & aEpoch,const wr::PipelineId & aPipelineId,AsyncImagePipeline * aPipeline,nsTArray<wr::ImageKey> & aKeys,wr::TransactionBuilder & aSceneBuilderTxn,wr::TransactionBuilder & aMaybeFastTxn)207 Maybe<TextureHost::ResourceUpdateOp> AsyncImagePipelineManager::UpdateImageKeys(
208     const wr::Epoch& aEpoch, const wr::PipelineId& aPipelineId,
209     AsyncImagePipeline* aPipeline, nsTArray<wr::ImageKey>& aKeys,
210     wr::TransactionBuilder& aSceneBuilderTxn,
211     wr::TransactionBuilder& aMaybeFastTxn) {
212   MOZ_ASSERT(aKeys.IsEmpty());
213   MOZ_ASSERT(aPipeline);
214 
215   TextureHost* texture =
216       aPipeline->mImageHost->GetAsTextureHostForComposite(this);
217   TextureHost* previousTexture = aPipeline->mCurrentTexture.get();
218 
219   if (texture == previousTexture) {
220     // The texture has not changed, just reuse previous ImageKeys.
221     aKeys = aPipeline->mKeys.Clone();
222     return Nothing();
223   }
224 
225   if (!texture || texture->NumSubTextures() == 0) {
226     // We don't have a new texture or texture does not have SubTextures, there
227     // isn't much we can do.
228     aKeys = aPipeline->mKeys.Clone();
229     return Nothing();
230   }
231 
232   aPipeline->mCurrentTexture = texture;
233 
234   WebRenderTextureHost* wrTexture = texture->AsWebRenderTextureHost();
235   MOZ_ASSERT(wrTexture);
236   if (!wrTexture) {
237     gfxCriticalNote << "WebRenderTextureHost is not used";
238   }
239 
240   bool useExternalImage = !!wrTexture;
241   aPipeline->mUseExternalImage = useExternalImage;
242 
243   // The non-external image code path falls back to converting the texture into
244   // an rgb image.
245   auto numKeys = useExternalImage ? texture->NumSubTextures() : 1;
246   MOZ_ASSERT(numKeys > 0);
247 
248   // If we already had a texture and the format hasn't changed, better to reuse
249   // the image keys than create new ones.
250   auto backend = aSceneBuilderTxn.GetBackendType();
251   bool canUpdate = !!previousTexture &&
252                    previousTexture->GetSize() == texture->GetSize() &&
253                    previousTexture->GetFormat() == texture->GetFormat() &&
254                    previousTexture->NeedsYFlip() == texture->NeedsYFlip() &&
255                    previousTexture->SupportsExternalCompositing(backend) ==
256                        texture->SupportsExternalCompositing(backend) &&
257                    aPipeline->mKeys.Length() == numKeys;
258 
259   if (!canUpdate) {
260     for (auto key : aPipeline->mKeys) {
261       // Destroy ImageKeys on transaction of scene builder thread, since
262       // DisplayList is updated on SceneBuilder thread. It prevents too early
263       // ImageKey deletion.
264       aSceneBuilderTxn.DeleteImage(key);
265     }
266     aPipeline->mKeys.Clear();
267     for (uint32_t i = 0; i < numKeys; ++i) {
268       aPipeline->mKeys.AppendElement(GenerateImageKey());
269     }
270   }
271 
272   aKeys = aPipeline->mKeys.Clone();
273 
274   auto op = canUpdate ? TextureHost::UPDATE_IMAGE : TextureHost::ADD_IMAGE;
275 
276   if (!useExternalImage) {
277     return UpdateWithoutExternalImage(texture, aKeys[0], op, aMaybeFastTxn);
278   }
279 
280   wrTexture->MaybeNotifyForUse(aMaybeFastTxn);
281 
282   Range<wr::ImageKey> keys(&aKeys[0], aKeys.Length());
283   auto externalImageKey = wrTexture->GetExternalImageKey();
284   wrTexture->PushResourceUpdates(aMaybeFastTxn, op, keys, externalImageKey);
285 
286   return Some(op);
287 }
288 
289 Maybe<TextureHost::ResourceUpdateOp>
UpdateWithoutExternalImage(TextureHost * aTexture,wr::ImageKey aKey,TextureHost::ResourceUpdateOp aOp,wr::TransactionBuilder & aTxn)290 AsyncImagePipelineManager::UpdateWithoutExternalImage(
291     TextureHost* aTexture, wr::ImageKey aKey, TextureHost::ResourceUpdateOp aOp,
292     wr::TransactionBuilder& aTxn) {
293   MOZ_ASSERT(aTexture);
294 
295   RefPtr<gfx::DataSourceSurface> dSurf = aTexture->GetAsSurface();
296   if (!dSurf) {
297     NS_ERROR("TextureHost does not return DataSourceSurface");
298     return Nothing();
299   }
300   gfx::DataSourceSurface::MappedSurface map;
301   if (!dSurf->Map(gfx::DataSourceSurface::MapType::READ, &map)) {
302     NS_ERROR("DataSourceSurface failed to map");
303     return Nothing();
304   }
305 
306   gfx::IntSize size = dSurf->GetSize();
307   wr::ImageDescriptor descriptor(size, map.mStride, dSurf->GetFormat());
308 
309   // Costly copy right here...
310   wr::Vec<uint8_t> bytes;
311   bytes.PushBytes(Range<uint8_t>(map.mData, size.height * map.mStride));
312 
313   if (aOp == TextureHost::UPDATE_IMAGE) {
314     aTxn.UpdateImageBuffer(aKey, descriptor, bytes);
315   } else {
316     aTxn.AddImage(aKey, descriptor, bytes);
317   }
318 
319   dSurf->Unmap();
320 
321   return Some(aOp);
322 }
323 
ApplyAsyncImagesOfImageBridge(wr::TransactionBuilder & aSceneBuilderTxn,wr::TransactionBuilder & aFastTxn)324 void AsyncImagePipelineManager::ApplyAsyncImagesOfImageBridge(
325     wr::TransactionBuilder& aSceneBuilderTxn,
326     wr::TransactionBuilder& aFastTxn) {
327   if (mDestroyed || mAsyncImagePipelines.Count() == 0) {
328     return;
329   }
330 
331 #ifdef XP_WIN
332   // UseWebRenderDCompVideoOverlayWin() could be changed from true to false,
333   // when DCompVideoOverlay task is failed. In this case, DisplayItems need to
334   // be re-pushed to WebRender for disabling video overlay.
335   bool isChanged = mUseWebRenderDCompVideoOverlayWin !=
336                    gfx::gfxVars::UseWebRenderDCompVideoOverlayWin();
337   if (isChanged) {
338     mUseWebRenderDCompVideoOverlayWin =
339         gfx::gfxVars::UseWebRenderDCompVideoOverlayWin();
340   }
341 #endif
342 
343   wr::Epoch epoch = GetNextImageEpoch();
344 
345   // We use a pipeline with a very small display list for each video element.
346   // Update each of them if needed.
347   for (const auto& entry : mAsyncImagePipelines) {
348     wr::PipelineId pipelineId = wr::AsPipelineId(entry.GetKey());
349     AsyncImagePipeline* pipeline = entry.GetWeak();
350 
351 #ifdef XP_WIN
352     if (isChanged) {
353       pipeline->mIsChanged = true;
354     }
355 #endif
356 
357     // If aync image pipeline does not use ImageBridge, do not need to apply.
358     if (!pipeline->mImageHost->GetAsyncRef()) {
359       continue;
360     }
361     ApplyAsyncImageForPipeline(epoch, pipelineId, pipeline, aSceneBuilderTxn,
362                                aFastTxn);
363   }
364 }
365 
ToWrRotation(VideoInfo::Rotation aRotation)366 wr::WrRotation ToWrRotation(VideoInfo::Rotation aRotation) {
367   switch (aRotation) {
368     case VideoInfo::Rotation::kDegree_0:
369       return wr::WrRotation::Degree0;
370     case VideoInfo::Rotation::kDegree_90:
371       return wr::WrRotation::Degree90;
372     case VideoInfo::Rotation::kDegree_180:
373       return wr::WrRotation::Degree180;
374     case VideoInfo::Rotation::kDegree_270:
375       return wr::WrRotation::Degree270;
376   }
377   return wr::WrRotation::Degree0;
378 }
379 
ApplyAsyncImageForPipeline(const wr::Epoch & aEpoch,const wr::PipelineId & aPipelineId,AsyncImagePipeline * aPipeline,wr::TransactionBuilder & aSceneBuilderTxn,wr::TransactionBuilder & aMaybeFastTxn)380 void AsyncImagePipelineManager::ApplyAsyncImageForPipeline(
381     const wr::Epoch& aEpoch, const wr::PipelineId& aPipelineId,
382     AsyncImagePipeline* aPipeline, wr::TransactionBuilder& aSceneBuilderTxn,
383     wr::TransactionBuilder& aMaybeFastTxn) {
384   nsTArray<wr::ImageKey> keys;
385   auto op = UpdateImageKeys(aEpoch, aPipelineId, aPipeline, keys,
386                             aSceneBuilderTxn, aMaybeFastTxn);
387 
388   bool updateDisplayList =
389       aPipeline->mInitialised &&
390       (aPipeline->mIsChanged || op == Some(TextureHost::ADD_IMAGE)) &&
391       !!aPipeline->mCurrentTexture;
392 
393   if (!updateDisplayList) {
394     // We don't need to update the display list, either because we can't or
395     // because the previous one is still up to date. We may, however, have
396     // updated some resources.
397 
398     // Use transaction of scene builder thread to notify epoch.
399     // It is for making epoch update consistent.
400     aSceneBuilderTxn.UpdateEpoch(aPipelineId, aEpoch);
401     if (aPipeline->mCurrentTexture) {
402       HoldExternalImage(aPipelineId, aEpoch, aPipeline->mCurrentTexture);
403     }
404     return;
405   }
406 
407   aPipeline->mIsChanged = false;
408 
409   wr::DisplayListBuilder builder(aPipelineId, mApi->GetBackendType());
410 
411   float opacity = 1.0f;
412   wr::StackingContextParams params;
413   params.opacity = &opacity;
414   params.mix_blend_mode = aPipeline->mMixBlendMode;
415 
416   wr::WrComputedTransformData computedTransform;
417   computedTransform.vertical_flip =
418       aPipeline->mCurrentTexture && aPipeline->mCurrentTexture->NeedsYFlip();
419   computedTransform.scale_from = {
420       float(aPipeline->mCurrentTexture->GetSize().width),
421       float(aPipeline->mCurrentTexture->GetSize().height)};
422   computedTransform.rotation = ToWrRotation(aPipeline->mRotation);
423   params.computed_transform = &computedTransform;
424 
425   Maybe<wr::WrSpatialId> referenceFrameId = builder.PushStackingContext(
426       params, wr::ToLayoutRect(aPipeline->mScBounds),
427       // This is fine to do unconditionally because we only push images here.
428       wr::RasterSpace::Screen());
429 
430   Maybe<wr::SpaceAndClipChainHelper> spaceAndClipChainHelper;
431   if (referenceFrameId) {
432     spaceAndClipChainHelper.emplace(builder, referenceFrameId.ref());
433   }
434 
435   if (aPipeline->mCurrentTexture && !keys.IsEmpty()) {
436     LayoutDeviceRect rect(0, 0, aPipeline->mCurrentTexture->GetSize().width,
437                           aPipeline->mCurrentTexture->GetSize().height);
438 
439     if (aPipeline->mUseExternalImage) {
440       MOZ_ASSERT(aPipeline->mCurrentTexture->AsWebRenderTextureHost());
441       Range<wr::ImageKey> range_keys(&keys[0], keys.Length());
442       TextureHost::PushDisplayItemFlagSet flags;
443       flags += TextureHost::PushDisplayItemFlag::PREFER_COMPOSITOR_SURFACE;
444       if (mApi->SupportsExternalBufferTextures()) {
445         flags +=
446             TextureHost::PushDisplayItemFlag::SUPPORTS_EXTERNAL_BUFFER_TEXTURES;
447       }
448       aPipeline->mCurrentTexture->PushDisplayItems(
449           builder, wr::ToLayoutRect(rect), wr::ToLayoutRect(rect),
450           aPipeline->mFilter, range_keys, flags);
451       HoldExternalImage(aPipelineId, aEpoch, aPipeline->mCurrentTexture);
452     } else {
453       MOZ_ASSERT(keys.Length() == 1);
454       builder.PushImage(wr::ToLayoutRect(rect), wr::ToLayoutRect(rect), true,
455                         aPipeline->mFilter, keys[0]);
456     }
457   }
458 
459   spaceAndClipChainHelper.reset();
460   builder.PopStackingContext(referenceFrameId.isSome());
461 
462   wr::BuiltDisplayList dl;
463   builder.Finalize(dl);
464   aSceneBuilderTxn.SetDisplayList(gfx::DeviceColor(0.f, 0.f, 0.f, 0.f), aEpoch,
465                                   wr::ToLayoutSize(aPipeline->mScBounds.Size()),
466                                   aPipelineId, dl.dl_desc, dl.dl);
467 }
468 
ApplyAsyncImageForPipeline(const wr::PipelineId & aPipelineId,wr::TransactionBuilder & aTxn,wr::TransactionBuilder & aTxnForImageBridge)469 void AsyncImagePipelineManager::ApplyAsyncImageForPipeline(
470     const wr::PipelineId& aPipelineId, wr::TransactionBuilder& aTxn,
471     wr::TransactionBuilder& aTxnForImageBridge) {
472   AsyncImagePipeline* pipeline =
473       mAsyncImagePipelines.Get(wr::AsUint64(aPipelineId));
474   if (!pipeline) {
475     return;
476   }
477   wr::TransactionBuilder fastTxn(mApi, /* aUseSceneBuilderThread */ false);
478   wr::AutoTransactionSender sender(mApi, &fastTxn);
479 
480   // Transaction for async image pipeline that uses ImageBridge always need to
481   // be non low priority.
482   auto& sceneBuilderTxn =
483       pipeline->mImageHost->GetAsyncRef() ? aTxnForImageBridge : aTxn;
484 
485   // Use transaction of using non scene builder thread when ImageHost uses
486   // ImageBridge. ApplyAsyncImagesOfImageBridge() handles transaction of adding
487   // and updating wr::ImageKeys of ImageHosts that uses ImageBridge. Then
488   // AsyncImagePipelineManager always needs to use non scene builder thread
489   // transaction for adding and updating wr::ImageKeys of ImageHosts that uses
490   // ImageBridge. Otherwise, ordering of wr::ImageKeys updating in webrender
491   // becomes inconsistent.
492   auto& maybeFastTxn = pipeline->mImageHost->GetAsyncRef() ? fastTxn : aTxn;
493 
494   wr::Epoch epoch = GetNextImageEpoch();
495 
496   ApplyAsyncImageForPipeline(epoch, aPipelineId, pipeline, sceneBuilderTxn,
497                              maybeFastTxn);
498 }
499 
SetEmptyDisplayList(const wr::PipelineId & aPipelineId,wr::TransactionBuilder & aTxn,wr::TransactionBuilder & aTxnForImageBridge)500 void AsyncImagePipelineManager::SetEmptyDisplayList(
501     const wr::PipelineId& aPipelineId, wr::TransactionBuilder& aTxn,
502     wr::TransactionBuilder& aTxnForImageBridge) {
503   AsyncImagePipeline* pipeline =
504       mAsyncImagePipelines.Get(wr::AsUint64(aPipelineId));
505   if (!pipeline) {
506     return;
507   }
508 
509   // Transaction for async image pipeline that uses ImageBridge always need to
510   // be non low priority.
511   auto& txn = pipeline->mImageHost->GetAsyncRef() ? aTxnForImageBridge : aTxn;
512 
513   wr::Epoch epoch = GetNextImageEpoch();
514   wr::DisplayListBuilder builder(aPipelineId, mApi->GetBackendType());
515 
516   wr::BuiltDisplayList dl;
517   builder.Finalize(dl);
518   txn.SetDisplayList(gfx::DeviceColor(0.f, 0.f, 0.f, 0.f), epoch,
519                      wr::ToLayoutSize(pipeline->mScBounds.Size()), aPipelineId,
520                      dl.dl_desc, dl.dl);
521 }
522 
HoldExternalImage(const wr::PipelineId & aPipelineId,const wr::Epoch & aEpoch,TextureHost * aTexture)523 void AsyncImagePipelineManager::HoldExternalImage(
524     const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch,
525     TextureHost* aTexture) {
526   if (mDestroyed) {
527     return;
528   }
529   MOZ_ASSERT(aTexture);
530 
531   PipelineTexturesHolder* holder =
532       mPipelineTexturesHolders.Get(wr::AsUint64(aPipelineId));
533   MOZ_ASSERT(holder);
534   if (!holder) {
535     return;
536   }
537   if (aTexture->NeedsDeferredDeletion()) {
538     // Hold WebRenderTextureHost until rendering completed.
539     holder->mTextureHostsUntilRenderCompleted.emplace_back(
540         MakeUnique<ForwardingTextureHost>(aEpoch, aTexture));
541   } else {
542     // Hold WebRenderTextureHost until submitted for rendering.
543     holder->mTextureHostsUntilRenderSubmitted.emplace_back(aEpoch, aTexture);
544   }
545 }
546 
HoldExternalImage(const wr::PipelineId & aPipelineId,const wr::Epoch & aEpoch,const wr::ExternalImageId & aImageId)547 void AsyncImagePipelineManager::HoldExternalImage(
548     const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch,
549     const wr::ExternalImageId& aImageId) {
550   if (mDestroyed) {
551     SharedSurfacesParent::Release(aImageId);
552     return;
553   }
554 
555   PipelineTexturesHolder* holder =
556       mPipelineTexturesHolders.Get(wr::AsUint64(aPipelineId));
557   MOZ_ASSERT(holder);
558   if (!holder) {
559     SharedSurfacesParent::Release(aImageId);
560     return;
561   }
562 
563   holder->mExternalImages.emplace_back(
564       MakeUnique<ForwardingExternalImage>(aEpoch, aImageId));
565 }
566 
NotifyPipelinesUpdated(RefPtr<const wr::WebRenderPipelineInfo> aInfo,wr::RenderedFrameId aLatestFrameId,wr::RenderedFrameId aLastCompletedFrameId,ipc::FileDescriptor && aFenceFd)567 void AsyncImagePipelineManager::NotifyPipelinesUpdated(
568     RefPtr<const wr::WebRenderPipelineInfo> aInfo,
569     wr::RenderedFrameId aLatestFrameId,
570     wr::RenderedFrameId aLastCompletedFrameId, ipc::FileDescriptor&& aFenceFd) {
571   MOZ_ASSERT(wr::RenderThread::IsInRenderThread());
572   MOZ_ASSERT(mLastCompletedFrameId <= aLastCompletedFrameId.mId);
573   MOZ_ASSERT(aLatestFrameId.IsValid());
574 
575   mLastCompletedFrameId = aLastCompletedFrameId.mId;
576 
577   {
578     // We need to lock for mRenderSubmittedUpdates because it can be accessed
579     // on the compositor thread.
580     MutexAutoLock lock(mRenderSubmittedUpdatesLock);
581 
582     // Move the pending updates into the submitted ones.
583     mRenderSubmittedUpdates.emplace_back(
584         aLatestFrameId,
585         WebRenderPipelineInfoHolder(std::move(aInfo), std::move(aFenceFd)));
586   }
587 
588   // Queue a runnable on the compositor thread to process the updates.
589   // This will also call CheckForTextureHostsNotUsedByGPU.
590   layers::CompositorThread()->Dispatch(
591       NewRunnableMethod("ProcessPipelineUpdates", this,
592                         &AsyncImagePipelineManager::ProcessPipelineUpdates));
593 }
594 
ProcessPipelineUpdates()595 void AsyncImagePipelineManager::ProcessPipelineUpdates() {
596   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
597 
598   if (mDestroyed) {
599     return;
600   }
601 
602   std::vector<std::pair<wr::RenderedFrameId, WebRenderPipelineInfoHolder>>
603       submittedUpdates;
604   {
605     // We need to lock for mRenderSubmittedUpdates because it can be accessed on
606     // the compositor thread.
607     MutexAutoLock lock(mRenderSubmittedUpdatesLock);
608     mRenderSubmittedUpdates.swap(submittedUpdates);
609   }
610 
611   // submittedUpdates is a vector of RenderedFrameIds paired with vectors of
612   // WebRenderPipelineInfo.
613   for (auto update : submittedUpdates) {
614     auto& holder = update.second;
615     const auto& info = holder.mInfo->Raw();
616 
617     mReleaseFenceFd = std::move(holder.mFenceFd);
618 
619     for (auto& epoch : info.epochs) {
620       ProcessPipelineRendered(epoch.pipeline_id, epoch.epoch, update.first);
621     }
622     for (auto& removedPipeline : info.removed_pipelines) {
623       ProcessPipelineRemoved(removedPipeline, update.first);
624     }
625   }
626   CheckForTextureHostsNotUsedByGPU();
627 }
628 
ProcessPipelineRendered(const wr::PipelineId & aPipelineId,const wr::Epoch & aEpoch,wr::RenderedFrameId aRenderedFrameId)629 void AsyncImagePipelineManager::ProcessPipelineRendered(
630     const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch,
631     wr::RenderedFrameId aRenderedFrameId) {
632   if (auto entry = mPipelineTexturesHolders.Lookup(wr::AsUint64(aPipelineId))) {
633     const auto& holder = entry.Data();
634     // For TextureHosts that can be released on render submission, using aEpoch
635     // find the first that we can't release and then release all prior to that.
636     auto firstSubmittedHostToKeep = std::find_if(
637         holder->mTextureHostsUntilRenderSubmitted.begin(),
638         holder->mTextureHostsUntilRenderSubmitted.end(),
639         [&aEpoch](const auto& entry) { return aEpoch <= entry.mEpoch; });
640 #ifdef MOZ_WIDGET_ANDROID
641     // Set release fence if TextureHost owns AndroidHardwareBuffer.
642     // The TextureHost handled by mTextureHostsUntilRenderSubmitted instead of
643     // mTextureHostsUntilRenderCompleted, since android fence could be used
644     // to wait until its end of usage by GPU.
645     for (auto it = holder->mTextureHostsUntilRenderSubmitted.begin();
646          it != firstSubmittedHostToKeep; ++it) {
647       const auto& entry = it;
648       if (entry->mTexture->GetAndroidHardwareBuffer()) {
649         ipc::FileDescriptor fenceFd = mReleaseFenceFd;
650         entry->mTexture->SetReleaseFence(std::move(fenceFd));
651       }
652     }
653 #endif
654     holder->mTextureHostsUntilRenderSubmitted.erase(
655         holder->mTextureHostsUntilRenderSubmitted.begin(),
656         firstSubmittedHostToKeep);
657 
658     // For TextureHosts that need to wait until render completed, find the first
659     // that is later than aEpoch and then move all prior to that to
660     // mTexturesInUseByGPU paired with aRenderedFrameId.  These will be released
661     // once rendering has completed for aRenderedFrameId.
662     auto firstCompletedHostToKeep = std::find_if(
663         holder->mTextureHostsUntilRenderCompleted.begin(),
664         holder->mTextureHostsUntilRenderCompleted.end(),
665         [&aEpoch](const auto& entry) { return aEpoch <= entry->mEpoch; });
666     if (firstCompletedHostToKeep !=
667         holder->mTextureHostsUntilRenderCompleted.begin()) {
668       std::vector<UniquePtr<ForwardingTextureHost>> hostsUntilCompleted(
669           std::make_move_iterator(
670               holder->mTextureHostsUntilRenderCompleted.begin()),
671           std::make_move_iterator(firstCompletedHostToKeep));
672       mTexturesInUseByGPU.emplace_back(aRenderedFrameId,
673                                        std::move(hostsUntilCompleted));
674       holder->mTextureHostsUntilRenderCompleted.erase(
675           holder->mTextureHostsUntilRenderCompleted.begin(),
676           firstCompletedHostToKeep);
677     }
678 
679     // Using aEpoch, find the first external image that we can't release and
680     // then release all prior to that.
681     auto firstImageToKeep = std::find_if(
682         holder->mExternalImages.begin(), holder->mExternalImages.end(),
683         [&aEpoch](const auto& entry) { return aEpoch <= entry->mEpoch; });
684     holder->mExternalImages.erase(holder->mExternalImages.begin(),
685                                   firstImageToKeep);
686   }
687 }
688 
ProcessPipelineRemoved(const wr::RemovedPipeline & aRemovedPipeline,wr::RenderedFrameId aRenderedFrameId)689 void AsyncImagePipelineManager::ProcessPipelineRemoved(
690     const wr::RemovedPipeline& aRemovedPipeline,
691     wr::RenderedFrameId aRenderedFrameId) {
692   if (mDestroyed) {
693     return;
694   }
695   if (auto entry = mPipelineTexturesHolders.Lookup(
696           wr::AsUint64(aRemovedPipeline.pipeline_id))) {
697     const auto& holder = entry.Data();
698     if (holder->mDestroyedEpoch.isSome()) {
699       if (!holder->mTextureHostsUntilRenderCompleted.empty()) {
700         // Move all TextureHosts that must be held until render completed to
701         // mTexturesInUseByGPU paired with aRenderedFrameId.
702         mTexturesInUseByGPU.emplace_back(
703             aRenderedFrameId,
704             std::move(holder->mTextureHostsUntilRenderCompleted));
705       }
706 
707       // Remove Pipeline releasing all remaining TextureHosts and external
708       // images.
709       entry.Remove();
710     }
711 
712     // If mDestroyedEpoch contains nothing it means we reused the same pipeline
713     // id (probably because we moved the tab to another window). In this case we
714     // need to keep the holder.
715   }
716 }
717 
CheckForTextureHostsNotUsedByGPU()718 void AsyncImagePipelineManager::CheckForTextureHostsNotUsedByGPU() {
719   uint64_t lastCompletedFrameId = mLastCompletedFrameId;
720 
721   // Find first entry after mLastCompletedFrameId and release all prior ones.
722   auto firstTexturesToKeep =
723       std::find_if(mTexturesInUseByGPU.begin(), mTexturesInUseByGPU.end(),
724                    [lastCompletedFrameId](const auto& entry) {
725                      return lastCompletedFrameId < entry.first.mId;
726                    });
727   mTexturesInUseByGPU.erase(mTexturesInUseByGPU.begin(), firstTexturesToKeep);
728 }
729 
GetNextImageEpoch()730 wr::Epoch AsyncImagePipelineManager::GetNextImageEpoch() {
731   mAsyncImageEpoch.mHandle++;
732   return mAsyncImageEpoch;
733 }
734 
735 AsyncImagePipelineManager::WebRenderPipelineInfoHolder::
WebRenderPipelineInfoHolder(RefPtr<const wr::WebRenderPipelineInfo> && aInfo,ipc::FileDescriptor && aFenceFd)736     WebRenderPipelineInfoHolder(RefPtr<const wr::WebRenderPipelineInfo>&& aInfo,
737                                 ipc::FileDescriptor&& aFenceFd)
738     : mInfo(aInfo), mFenceFd(aFenceFd) {}
739 
740 AsyncImagePipelineManager::WebRenderPipelineInfoHolder::
741     ~WebRenderPipelineInfoHolder() = default;
742 
743 }  // namespace layers
744 }  // namespace mozilla
745