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