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"
12 #include "CompositableHost.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"
31 AsyncImagePipelineManager::ForwardingExternalImage::~ForwardingExternalImage() {
32 DebugOnly
<bool> released
= SharedSurfacesParent::Release(mImageId
);
36 AsyncImagePipelineManager::AsyncImagePipeline::AsyncImagePipeline(
37 wr::PipelineId aPipelineId
, layers::WebRenderBackend aBackend
)
38 : mInitialised(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
)
49 mUseCompositorWnd(aUseCompositorWnd
),
50 mIdNamespace(mApi
->GetNamespace()),
51 mUseTripleBuffering(mApi
->GetUseTripleBuffering()),
54 mWillGenerateFrame(false),
57 mUseWebRenderDCompVideoOverlayWin(
58 gfx::gfxVars::UseWebRenderDCompVideoOverlayWin()),
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
);
72 mPipelineTexturesHolders
.Clear();
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;
101 void AsyncImagePipelineManager::AddPipeline(const wr::PipelineId
& aPipelineId
,
102 WebRenderBridgeParent
* aWrBridge
) {
107 mPipelineTexturesHolders
.WithEntryHandle(
108 wr::AsUint64(aPipelineId
), [&](auto&& holder
) {
110 // This could happen during tab move between different windows.
111 // Previously removed holder could be still alive for waiting
113 MOZ_ASSERT(holder
.Data()->mDestroyedEpoch
.isSome());
114 holder
.Data()->mDestroyedEpoch
= Nothing(); // Revive holder
115 holder
.Data()->mWrBridge
= aWrBridge
;
119 holder
.Insert(MakeUnique
<PipelineTexturesHolder
>())->mWrBridge
=
124 void AsyncImagePipelineManager::RemovePipeline(
125 const wr::PipelineId
& aPipelineId
, const wr::Epoch
& aEpoch
) {
130 PipelineTexturesHolder
* holder
=
131 mPipelineTexturesHolders
.Get(wr::AsUint64(aPipelineId
));
136 holder
->mWrBridge
= nullptr;
137 holder
->mDestroyedEpoch
= Some(aEpoch
);
140 WebRenderBridgeParent
* AsyncImagePipelineManager::GetWrBridge(
141 const wr::PipelineId
& aPipelineId
) {
146 PipelineTexturesHolder
* holder
=
147 mPipelineTexturesHolders
.Get(wr::AsUint64(aPipelineId
));
151 if (holder
->mWrBridge
) {
152 MOZ_ASSERT(holder
->mDestroyedEpoch
.isNothing());
153 return holder
->mWrBridge
;
159 void AsyncImagePipelineManager::AddAsyncImagePipeline(
160 const wr::PipelineId
& aPipelineId
, WebRenderImageHost
* aImageHost
) {
164 MOZ_ASSERT(aImageHost
);
165 uint64_t id
= wr::AsUint64(aPipelineId
);
167 MOZ_ASSERT(!mAsyncImagePipelines
.Contains(id
));
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
) {
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
);
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
) {
201 AsyncImagePipeline
* pipeline
=
202 mAsyncImagePipelines
.Get(wr::AsUint64(aPipelineId
));
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();
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();
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
,
242 aPipeline
->mCurrentTexture
= texture
;
244 WebRenderTextureHost
* wrTexture
= texture
->AsWebRenderTextureHost();
245 MOZ_ASSERT(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
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();
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
;
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
);
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();
310 NS_ERROR("TextureHost does not return DataSourceSurface");
313 gfx::DataSourceSurface::MappedSurface map
;
314 if (!dSurf
->Map(gfx::DataSourceSurface::MapType::READ
, &map
)) {
315 NS_ERROR("DataSourceSurface failed to map");
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
);
329 aTxn
.AddImage(aKey
, descriptor
, bytes
);
337 void AsyncImagePipelineManager::ApplyAsyncImagesOfImageBridge(
338 wr::TransactionBuilder
& aSceneBuilderTxn
,
339 wr::TransactionBuilder
& aFastTxn
) {
340 if (mDestroyed
|| mAsyncImagePipelines
.Count() == 0) {
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();
351 mUseWebRenderDCompVideoOverlayWin
=
352 gfx::gfxVars::UseWebRenderDCompVideoOverlayWin();
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();
366 pipeline
->mIsChanged
= true;
370 // If aync image pipeline does not use ImageBridge, do not need to apply.
371 if (!pipeline
->mImageHost
->GetAsyncRef()) {
374 ApplyAsyncImageForPipeline(epoch
, pipelineId
, pipeline
, aSceneBuilderTxn
,
375 aFastTxn
, /* aList */ nullptr);
379 wr::WrRotation
ToWrRotation(VideoInfo::Rotation 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
);
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
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()) {
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
);
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
));
497 // ready event of RemoteTexture that uses ImageBridge do not need to be
499 if (pipeline
->mImageHost
->GetAsyncRef()) {
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
));
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());
543 wr::BuiltDisplayList dl
;
545 txn
.SetDisplayList(epoch
, aPipelineId
, dl
.dl_desc
, dl
.dl_items
, dl
.dl_cache
,
549 void AsyncImagePipelineManager::HoldExternalImage(
550 const wr::PipelineId
& aPipelineId
, const wr::Epoch
& aEpoch
,
551 TextureHost
* aTexture
) {
555 MOZ_ASSERT(aTexture
);
557 PipelineTexturesHolder
* holder
=
558 mPipelineTexturesHolders
.Get(wr::AsUint64(aPipelineId
));
563 if (aTexture
->NeedsDeferredDeletion()) {
564 // Hold WebRenderTextureHost until rendering completed.
565 holder
->mTextureHostsUntilRenderCompleted
.emplace_back(
566 MakeUnique
<ForwardingTextureHost
>(aEpoch
, aTexture
));
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
) {
577 SharedSurfacesParent::Release(aImageId
);
581 PipelineTexturesHolder
* holder
=
582 mPipelineTexturesHolders
.Get(wr::AsUint64(aPipelineId
));
585 SharedSurfacesParent::Release(aImageId
);
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(
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());
628 std::vector
<std::pair
<wr::RenderedFrameId
, WebRenderPipelineInfoHolder
>>
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
));
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(),
716 void AsyncImagePipelineManager::ProcessPipelineRemoved(
717 const wr::RemovedPipeline
& aRemovedPipeline
,
718 wr::RenderedFrameId aRenderedFrameId
) {
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(
731 std::move(holder
->mTextureHostsUntilRenderCompleted
));
734 // Remove Pipeline releasing all remaining TextureHosts and external
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