Merge mozilla-central to autoland. CLOSED TREE
[gecko.git] / gfx / layers / wr / AsyncImagePipelineManager.cpp
blob8a49ffef69adae009a38bf9494304b2cdb3ad6c8
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/AsyncImagePipelineOp.h"
16 #include "mozilla/layers/CompositorThread.h"
17 #include "mozilla/layers/RemoteTextureHostWrapper.h"
18 #include "mozilla/layers/SharedSurfacesParent.h"
19 #include "mozilla/layers/WebRenderImageHost.h"
20 #include "mozilla/layers/WebRenderTextureHost.h"
21 #include "mozilla/webrender/RenderThread.h"
22 #include "mozilla/webrender/WebRenderAPI.h"
23 #include "mozilla/webrender/WebRenderTypes.h"
25 #ifdef MOZ_WIDGET_ANDROID
26 # include "mozilla/layers/TextureHostOGL.h"
27 #endif
29 namespace mozilla {
30 namespace layers {
32 AsyncImagePipelineManager::ForwardingExternalImage::~ForwardingExternalImage() {
33 DebugOnly<bool> released = SharedSurfacesParent::Release(mImageId);
34 MOZ_ASSERT(released);
37 AsyncImagePipelineManager::AsyncImagePipeline::AsyncImagePipeline(
38 wr::PipelineId aPipelineId, layers::WebRenderBackend aBackend)
39 : mInitialised(false),
40 mIsChanged(false),
41 mUseExternalImage(false),
42 mRotation(wr::WrRotation::Degree0),
43 mFilter(wr::ImageRendering::Auto),
44 mMixBlendMode(wr::MixBlendMode::Normal),
45 mDLBuilder(aPipelineId, aBackend) {}
47 AsyncImagePipelineManager::AsyncImagePipelineManager(
48 RefPtr<wr::WebRenderAPI>&& aApi, bool aUseCompositorWnd)
49 : mApi(aApi),
50 mUseCompositorWnd(aUseCompositorWnd),
51 mIdNamespace(mApi->GetNamespace()),
52 mUseTripleBuffering(mApi->GetUseTripleBuffering()),
53 mResourceId(0),
54 mAsyncImageEpoch{0},
55 mWillGenerateFrame(false),
56 mDestroyed(false),
57 #ifdef XP_WIN
58 mUseWebRenderDCompVideoHwOverlayWin(
59 gfx::gfxVars::UseWebRenderDCompVideoHwOverlayWin()),
60 mUseWebRenderDCompVideoSwOverlayWin(
61 gfx::gfxVars::UseWebRenderDCompVideoSwOverlayWin()),
62 #endif
63 mRenderSubmittedUpdatesLock("SubmittedUpdatesLock"),
64 mLastCompletedFrameId(0) {
65 MOZ_COUNT_CTOR(AsyncImagePipelineManager);
68 AsyncImagePipelineManager::~AsyncImagePipelineManager() {
69 MOZ_COUNT_DTOR(AsyncImagePipelineManager);
72 void AsyncImagePipelineManager::Destroy() {
73 MOZ_ASSERT(!mDestroyed);
74 mApi = nullptr;
75 mPipelineTexturesHolders.Clear();
76 mDestroyed = true;
79 /* static */
80 wr::ExternalImageId AsyncImagePipelineManager::GetNextExternalImageId() {
81 static std::atomic<uint64_t> sCounter = 0;
83 uint64_t id = ++sCounter;
84 // Upper 32bit(namespace) needs to be 0.
85 // Namespace other than 0 might be used by others.
86 MOZ_RELEASE_ASSERT(id != UINT32_MAX);
87 return wr::ToExternalImageId(id);
90 void AsyncImagePipelineManager::SetWillGenerateFrame() {
91 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
93 mWillGenerateFrame = true;
96 bool AsyncImagePipelineManager::GetAndResetWillGenerateFrame() {
97 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
99 bool ret = mWillGenerateFrame;
100 mWillGenerateFrame = false;
101 return ret;
104 void AsyncImagePipelineManager::AddPipeline(const wr::PipelineId& aPipelineId,
105 WebRenderBridgeParent* aWrBridge) {
106 if (mDestroyed) {
107 return;
110 mPipelineTexturesHolders.WithEntryHandle(
111 wr::AsUint64(aPipelineId), [&](auto&& holder) {
112 if (holder) {
113 // This could happen during tab move between different windows.
114 // Previously removed holder could be still alive for waiting
115 // destroyed.
116 MOZ_ASSERT(holder.Data()->mDestroyedEpoch.isSome());
117 holder.Data()->mDestroyedEpoch = Nothing(); // Revive holder
118 holder.Data()->mWrBridge = aWrBridge;
119 return;
122 holder.Insert(MakeUnique<PipelineTexturesHolder>())->mWrBridge =
123 aWrBridge;
127 void AsyncImagePipelineManager::RemovePipeline(
128 const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch) {
129 if (mDestroyed) {
130 return;
133 PipelineTexturesHolder* holder =
134 mPipelineTexturesHolders.Get(wr::AsUint64(aPipelineId));
135 MOZ_ASSERT(holder);
136 if (!holder) {
137 return;
139 holder->mWrBridge = nullptr;
140 holder->mDestroyedEpoch = Some(aEpoch);
143 WebRenderBridgeParent* AsyncImagePipelineManager::GetWrBridge(
144 const wr::PipelineId& aPipelineId) {
145 if (mDestroyed) {
146 return nullptr;
149 PipelineTexturesHolder* holder =
150 mPipelineTexturesHolders.Get(wr::AsUint64(aPipelineId));
151 if (!holder) {
152 return nullptr;
154 if (holder->mWrBridge) {
155 MOZ_ASSERT(holder->mDestroyedEpoch.isNothing());
156 return holder->mWrBridge;
159 return nullptr;
162 void AsyncImagePipelineManager::AddAsyncImagePipeline(
163 const wr::PipelineId& aPipelineId, WebRenderImageHost* aImageHost) {
164 if (mDestroyed) {
165 return;
167 MOZ_ASSERT(aImageHost);
168 uint64_t id = wr::AsUint64(aPipelineId);
170 MOZ_ASSERT(!mAsyncImagePipelines.Contains(id));
171 auto holder =
172 MakeUnique<AsyncImagePipeline>(aPipelineId, mApi->GetBackendType());
173 holder->mImageHost = aImageHost;
174 mAsyncImagePipelines.InsertOrUpdate(id, std::move(holder));
175 AddPipeline(aPipelineId, /* aWrBridge */ nullptr);
178 void AsyncImagePipelineManager::RemoveAsyncImagePipeline(
179 const wr::PipelineId& aPipelineId, wr::TransactionBuilder& aTxn) {
180 if (mDestroyed) {
181 return;
184 uint64_t id = wr::AsUint64(aPipelineId);
185 if (auto entry = mAsyncImagePipelines.Lookup(id)) {
186 const auto& holder = entry.Data();
187 wr::Epoch epoch = GetNextImageEpoch();
188 aTxn.ClearDisplayList(epoch, aPipelineId);
189 for (wr::ImageKey key : holder->mKeys) {
190 aTxn.DeleteImage(key);
192 entry.Remove();
193 RemovePipeline(aPipelineId, epoch);
197 void AsyncImagePipelineManager::UpdateAsyncImagePipeline(
198 const wr::PipelineId& aPipelineId, const LayoutDeviceRect& aScBounds,
199 const wr::WrRotation aRotation, const wr::ImageRendering& aFilter,
200 const wr::MixBlendMode& aMixBlendMode) {
201 if (mDestroyed) {
202 return;
204 AsyncImagePipeline* pipeline =
205 mAsyncImagePipelines.Get(wr::AsUint64(aPipelineId));
206 if (!pipeline) {
207 return;
209 pipeline->mInitialised = true;
210 pipeline->Update(aScBounds, aRotation, aFilter, aMixBlendMode);
213 Maybe<TextureHost::ResourceUpdateOp> AsyncImagePipelineManager::UpdateImageKeys(
214 const wr::Epoch& aEpoch, const wr::PipelineId& aPipelineId,
215 AsyncImagePipeline* aPipeline, TextureHost* aTexture,
216 nsTArray<wr::ImageKey>& aKeys, wr::TransactionBuilder& aSceneBuilderTxn,
217 wr::TransactionBuilder& aMaybeFastTxn) {
218 MOZ_ASSERT(aKeys.IsEmpty());
219 MOZ_ASSERT(aPipeline);
221 TextureHost* previousTexture = aPipeline->mCurrentTexture.get();
223 if (aTexture == previousTexture) {
224 // The texture has not changed, just reuse previous ImageKeys.
225 aKeys = aPipeline->mKeys.Clone();
226 return Nothing();
229 auto* wrapper = aTexture ? aTexture->AsRemoteTextureHostWrapper() : nullptr;
230 if (wrapper && !aPipeline->mImageHost->GetAsyncRef()) {
231 std::function<void(const RemoteTextureInfo&)> function;
232 RemoteTextureMap::Get()->GetRemoteTexture(wrapper, std::move(function));
235 if (!aTexture || aTexture->NumSubTextures() == 0) {
236 // We don't have a new texture or texture does not have SubTextures, there
237 // isn't much we can do.
238 aKeys = aPipeline->mKeys.Clone();
239 return Nothing();
242 aPipeline->mCurrentTexture = aTexture;
244 WebRenderTextureHost* wrTexture = aTexture->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 ? aTexture->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() == aTexture->GetTextureHostType() &&
264 previousTexture->GetSize() == aTexture->GetSize() &&
265 previousTexture->GetFormat() == aTexture->GetFormat() &&
266 previousTexture->GetColorDepth() == aTexture->GetColorDepth() &&
267 previousTexture->NeedsYFlip() == aTexture->NeedsYFlip() &&
268 previousTexture->SupportsExternalCompositing(backend) ==
269 aTexture->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(aTexture, 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 // UseWebRenderDCompVideoHwOverlayWin() and
346 // UseWebRenderDCompVideoSwOverlayWin() could be changed from true to false,
347 // when DCompVideoOverlay task is failed. In this case, DisplayItems need to
348 // be re-pushed to WebRender for disabling video overlay.
349 bool isChanged = (mUseWebRenderDCompVideoHwOverlayWin !=
350 gfx::gfxVars::UseWebRenderDCompVideoHwOverlayWin()) ||
351 (mUseWebRenderDCompVideoSwOverlayWin !=
352 gfx::gfxVars::UseWebRenderDCompVideoSwOverlayWin());
354 if (isChanged) {
355 mUseWebRenderDCompVideoHwOverlayWin =
356 gfx::gfxVars::UseWebRenderDCompVideoHwOverlayWin();
357 mUseWebRenderDCompVideoSwOverlayWin =
358 gfx::gfxVars::UseWebRenderDCompVideoSwOverlayWin();
360 #endif
362 wr::Epoch epoch = GetNextImageEpoch();
364 // We use a pipeline with a very small display list for each video element.
365 // Update each of them if needed.
366 for (const auto& entry : mAsyncImagePipelines) {
367 wr::PipelineId pipelineId = wr::AsPipelineId(entry.GetKey());
368 AsyncImagePipeline* pipeline = entry.GetWeak();
370 #ifdef XP_WIN
371 if (isChanged) {
372 pipeline->mIsChanged = true;
374 #endif
376 // If aync image pipeline does not use ImageBridge, do not need to apply.
377 if (!pipeline->mImageHost->GetAsyncRef()) {
378 continue;
380 TextureHost* texture =
381 pipeline->mImageHost->GetAsTextureHostForComposite(this);
383 ApplyAsyncImageForPipeline(epoch, pipelineId, pipeline, texture,
384 aSceneBuilderTxn, aFastTxn);
388 void AsyncImagePipelineManager::ApplyAsyncImageForPipeline(
389 const wr::Epoch& aEpoch, const wr::PipelineId& aPipelineId,
390 AsyncImagePipeline* aPipeline, TextureHost* aTexture,
391 wr::TransactionBuilder& aSceneBuilderTxn,
392 wr::TransactionBuilder& aMaybeFastTxn) {
393 nsTArray<wr::ImageKey> keys;
394 auto op = UpdateImageKeys(aEpoch, aPipelineId, aPipeline, aTexture, keys,
395 aSceneBuilderTxn, aMaybeFastTxn);
397 bool updateDisplayList =
398 aPipeline->mInitialised &&
399 (aPipeline->mIsChanged || op == Some(TextureHost::ADD_IMAGE)) &&
400 !!aPipeline->mCurrentTexture;
402 if (!updateDisplayList) {
403 // We don't need to update the display list, either because we can't or
404 // because the previous one is still up to date. We may, however, have
405 // updated some resources.
407 // Use transaction of scene builder thread to notify epoch.
408 // It is for making epoch update consistent.
409 aSceneBuilderTxn.UpdateEpoch(aPipelineId, aEpoch);
410 if (aPipeline->mCurrentTexture) {
411 HoldExternalImage(aPipelineId, aEpoch, aPipeline->mCurrentTexture);
413 return;
416 aPipeline->mIsChanged = false;
417 aPipeline->mDLBuilder.Begin();
419 float opacity = 1.0f;
420 wr::StackingContextParams params;
421 params.opacity = &opacity;
422 params.mix_blend_mode = aPipeline->mMixBlendMode;
424 wr::WrComputedTransformData computedTransform;
425 computedTransform.vertical_flip =
426 aPipeline->mCurrentTexture && aPipeline->mCurrentTexture->NeedsYFlip();
427 computedTransform.scale_from = {
428 float(aPipeline->mCurrentTexture->GetSize().width),
429 float(aPipeline->mCurrentTexture->GetSize().height)};
430 computedTransform.rotation = aPipeline->mRotation;
431 // We don't have a frame / per-frame key here, but we can use the pipeline id
432 // and the key kind to create a unique stable key.
433 computedTransform.key = wr::SpatialKey(
434 aPipelineId.mNamespace, aPipelineId.mHandle, wr::SpatialKeyKind::APZ);
435 params.computed_transform = &computedTransform;
437 Maybe<wr::WrSpatialId> referenceFrameId =
438 aPipeline->mDLBuilder.PushStackingContext(
439 params, wr::ToLayoutRect(aPipeline->mScBounds),
440 // This is fine to do unconditionally because we only push images
441 // here.
442 wr::RasterSpace::Screen());
444 Maybe<wr::SpaceAndClipChainHelper> spaceAndClipChainHelper;
445 if (referenceFrameId) {
446 spaceAndClipChainHelper.emplace(aPipeline->mDLBuilder,
447 referenceFrameId.ref());
450 if (aPipeline->mCurrentTexture && !keys.IsEmpty()) {
451 LayoutDeviceRect rect(0, 0, aPipeline->mCurrentTexture->GetSize().width,
452 aPipeline->mCurrentTexture->GetSize().height);
454 if (aPipeline->mUseExternalImage) {
455 MOZ_ASSERT(aPipeline->mCurrentTexture->AsWebRenderTextureHost());
456 Range<wr::ImageKey> range_keys(&keys[0], keys.Length());
457 TextureHost::PushDisplayItemFlagSet flags;
458 flags += TextureHost::PushDisplayItemFlag::PREFER_COMPOSITOR_SURFACE;
459 if (mApi->SupportsExternalBufferTextures()) {
460 flags +=
461 TextureHost::PushDisplayItemFlag::SUPPORTS_EXTERNAL_BUFFER_TEXTURES;
463 aPipeline->mCurrentTexture->PushDisplayItems(
464 aPipeline->mDLBuilder, wr::ToLayoutRect(rect), wr::ToLayoutRect(rect),
465 aPipeline->mFilter, range_keys, flags);
466 HoldExternalImage(aPipelineId, aEpoch, aPipeline->mCurrentTexture);
467 } else {
468 MOZ_ASSERT(keys.Length() == 1);
469 aPipeline->mDLBuilder.PushImage(wr::ToLayoutRect(rect),
470 wr::ToLayoutRect(rect), true, false,
471 aPipeline->mFilter, keys[0]);
475 spaceAndClipChainHelper.reset();
476 aPipeline->mDLBuilder.PopStackingContext(referenceFrameId.isSome());
478 wr::BuiltDisplayList dl;
479 aPipeline->mDLBuilder.End(dl);
480 aSceneBuilderTxn.SetDisplayList(aEpoch, aPipelineId, dl.dl_desc, dl.dl_items,
481 dl.dl_cache, dl.dl_spatial_tree);
484 void AsyncImagePipelineManager::ApplyAsyncImageForPipeline(
485 const wr::PipelineId& aPipelineId, wr::TransactionBuilder& aTxn,
486 wr::TransactionBuilder& aTxnForImageBridge,
487 AsyncImagePipelineOps* aPendingOps,
488 RemoteTextureInfoList* aPendingRemoteTextures) {
489 AsyncImagePipeline* pipeline =
490 mAsyncImagePipelines.Get(wr::AsUint64(aPipelineId));
491 if (!pipeline) {
492 return;
495 // ready event of RemoteTexture that uses ImageBridge do not need to be
496 // checked here.
497 if (pipeline->mImageHost->GetAsyncRef()) {
498 aPendingRemoteTextures = nullptr;
501 wr::TransactionBuilder fastTxn(mApi, /* aUseSceneBuilderThread */ false);
502 wr::AutoTransactionSender sender(mApi, &fastTxn);
504 // Transaction for async image pipeline that uses ImageBridge always need to
505 // be non low priority.
506 auto& sceneBuilderTxn =
507 pipeline->mImageHost->GetAsyncRef() ? aTxnForImageBridge : aTxn;
509 // Use transaction of using non scene builder thread when ImageHost uses
510 // ImageBridge. ApplyAsyncImagesOfImageBridge() handles transaction of adding
511 // and updating wr::ImageKeys of ImageHosts that uses ImageBridge. Then
512 // AsyncImagePipelineManager always needs to use non scene builder thread
513 // transaction for adding and updating wr::ImageKeys of ImageHosts that uses
514 // ImageBridge. Otherwise, ordering of wr::ImageKeys updating in webrender
515 // becomes inconsistent.
516 auto& maybeFastTxn = pipeline->mImageHost->GetAsyncRef() ? fastTxn : aTxn;
518 wr::Epoch epoch = GetNextImageEpoch();
519 TextureHost* texture =
520 pipeline->mImageHost->GetAsTextureHostForComposite(this);
521 auto* wrapper = texture ? texture->AsRemoteTextureHostWrapper() : nullptr;
523 // Store pending remote texture that is used for waiting at WebRenderAPI.
524 if (aPendingRemoteTextures && texture &&
525 texture != pipeline->mCurrentTexture && wrapper) {
526 aPendingRemoteTextures->mList.emplace(wrapper->mTextureId,
527 wrapper->mOwnerId, wrapper->mForPid);
530 if (aPendingOps && !pipeline->mImageHost->GetAsyncRef()) {
531 aPendingOps->mList.emplace(AsyncImagePipelineOp::ApplyAsyncImageForPipeline(
532 this, aPipelineId, texture));
533 return;
536 ApplyAsyncImageForPipeline(epoch, aPipelineId, pipeline, texture,
537 sceneBuilderTxn, maybeFastTxn);
540 void AsyncImagePipelineManager::ApplyAsyncImageForPipeline(
541 const wr::PipelineId& aPipelineId, TextureHost* aTexture,
542 wr::TransactionBuilder& aTxn) {
543 AsyncImagePipeline* pipeline =
544 mAsyncImagePipelines.Get(wr::AsUint64(aPipelineId));
545 if (!pipeline) {
546 return;
548 MOZ_ASSERT(!pipeline->mImageHost->GetAsyncRef());
550 wr::Epoch epoch = GetNextImageEpoch();
551 ApplyAsyncImageForPipeline(epoch, aPipelineId, pipeline, aTexture, aTxn,
552 aTxn);
555 void AsyncImagePipelineManager::SetEmptyDisplayList(
556 const wr::PipelineId& aPipelineId, wr::TransactionBuilder& aTxn,
557 wr::TransactionBuilder& aTxnForImageBridge) {
558 AsyncImagePipeline* pipeline =
559 mAsyncImagePipelines.Get(wr::AsUint64(aPipelineId));
560 if (!pipeline) {
561 return;
564 // Transaction for async image pipeline that uses ImageBridge always need to
565 // be non low priority.
566 auto& txn = pipeline->mImageHost->GetAsyncRef() ? aTxnForImageBridge : aTxn;
568 wr::Epoch epoch = GetNextImageEpoch();
569 wr::DisplayListBuilder builder(aPipelineId, mApi->GetBackendType());
570 builder.Begin();
572 wr::BuiltDisplayList dl;
573 builder.End(dl);
574 txn.SetDisplayList(epoch, aPipelineId, dl.dl_desc, dl.dl_items, dl.dl_cache,
575 dl.dl_spatial_tree);
578 void AsyncImagePipelineManager::HoldExternalImage(
579 const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch,
580 TextureHost* aTexture) {
581 if (mDestroyed) {
582 return;
584 MOZ_ASSERT(aTexture);
586 PipelineTexturesHolder* holder =
587 mPipelineTexturesHolders.Get(wr::AsUint64(aPipelineId));
588 MOZ_ASSERT(holder);
589 if (!holder) {
590 return;
592 if (aTexture->NeedsDeferredDeletion()) {
593 // Hold WebRenderTextureHost until rendering completed.
594 holder->mTextureHostsUntilRenderCompleted.emplace_back(
595 MakeUnique<ForwardingTextureHost>(aEpoch, aTexture));
596 } else {
597 // Hold WebRenderTextureHost until submitted for rendering.
598 holder->mTextureHostsUntilRenderSubmitted.emplace_back(aEpoch, aTexture);
602 void AsyncImagePipelineManager::HoldExternalImage(
603 const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch,
604 const wr::ExternalImageId& aImageId) {
605 if (mDestroyed) {
606 SharedSurfacesParent::Release(aImageId);
607 return;
610 PipelineTexturesHolder* holder =
611 mPipelineTexturesHolders.Get(wr::AsUint64(aPipelineId));
612 MOZ_ASSERT(holder);
613 if (!holder) {
614 SharedSurfacesParent::Release(aImageId);
615 return;
618 holder->mExternalImages.emplace_back(
619 MakeUnique<ForwardingExternalImage>(aEpoch, aImageId));
622 void AsyncImagePipelineManager::NotifyPipelinesUpdated(
623 RefPtr<const wr::WebRenderPipelineInfo> aInfo,
624 wr::RenderedFrameId aLatestFrameId,
625 wr::RenderedFrameId aLastCompletedFrameId, ipc::FileDescriptor&& aFenceFd) {
626 MOZ_ASSERT(wr::RenderThread::IsInRenderThread());
627 MOZ_ASSERT(mLastCompletedFrameId <= aLastCompletedFrameId.mId);
628 MOZ_ASSERT(aLatestFrameId.IsValid());
630 mLastCompletedFrameId = aLastCompletedFrameId.mId;
633 // We need to lock for mRenderSubmittedUpdates because it can be accessed
634 // on the compositor thread.
635 MutexAutoLock lock(mRenderSubmittedUpdatesLock);
637 // Move the pending updates into the submitted ones.
638 mRenderSubmittedUpdates.emplace_back(
639 aLatestFrameId,
640 WebRenderPipelineInfoHolder(std::move(aInfo), std::move(aFenceFd)));
643 // Queue a runnable on the compositor thread to process the updates.
644 // This will also call CheckForTextureHostsNotUsedByGPU.
645 layers::CompositorThread()->Dispatch(
646 NewRunnableMethod("ProcessPipelineUpdates", this,
647 &AsyncImagePipelineManager::ProcessPipelineUpdates));
650 void AsyncImagePipelineManager::ProcessPipelineUpdates() {
651 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
653 if (mDestroyed) {
654 return;
657 std::vector<std::pair<wr::RenderedFrameId, WebRenderPipelineInfoHolder>>
658 submittedUpdates;
660 // We need to lock for mRenderSubmittedUpdates because it can be accessed on
661 // the compositor thread.
662 MutexAutoLock lock(mRenderSubmittedUpdatesLock);
663 mRenderSubmittedUpdates.swap(submittedUpdates);
666 // submittedUpdates is a vector of RenderedFrameIds paired with vectors of
667 // WebRenderPipelineInfo.
668 for (auto update : submittedUpdates) {
669 auto& holder = update.second;
670 const auto& info = holder.mInfo->Raw();
672 mReleaseFenceFd = std::move(holder.mFenceFd);
674 for (auto& epoch : info.epochs) {
675 ProcessPipelineRendered(epoch.pipeline_id, epoch.epoch, update.first);
677 for (auto& removedPipeline : info.removed_pipelines) {
678 ProcessPipelineRemoved(removedPipeline, update.first);
681 CheckForTextureHostsNotUsedByGPU();
684 void AsyncImagePipelineManager::ProcessPipelineRendered(
685 const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch,
686 wr::RenderedFrameId aRenderedFrameId) {
687 if (auto entry = mPipelineTexturesHolders.Lookup(wr::AsUint64(aPipelineId))) {
688 const auto& holder = entry.Data();
689 // For TextureHosts that can be released on render submission, using aEpoch
690 // find the first that we can't release and then release all prior to that.
691 auto firstSubmittedHostToKeep = std::find_if(
692 holder->mTextureHostsUntilRenderSubmitted.begin(),
693 holder->mTextureHostsUntilRenderSubmitted.end(),
694 [&aEpoch](const auto& entry) { return aEpoch <= entry.mEpoch; });
695 #ifdef MOZ_WIDGET_ANDROID
696 // Set release fence if TextureHost owns AndroidHardwareBuffer.
697 // The TextureHost handled by mTextureHostsUntilRenderSubmitted instead of
698 // mTextureHostsUntilRenderCompleted, since android fence could be used
699 // to wait until its end of usage by GPU.
700 for (auto it = holder->mTextureHostsUntilRenderSubmitted.begin();
701 it != firstSubmittedHostToKeep; ++it) {
702 const auto& entry = it;
703 if (entry->mTexture->GetAndroidHardwareBuffer() &&
704 mReleaseFenceFd.IsValid()) {
705 ipc::FileDescriptor fenceFd = mReleaseFenceFd;
706 entry->mTexture->SetReleaseFence(std::move(fenceFd));
709 #endif
710 holder->mTextureHostsUntilRenderSubmitted.erase(
711 holder->mTextureHostsUntilRenderSubmitted.begin(),
712 firstSubmittedHostToKeep);
714 // For TextureHosts that need to wait until render completed, find the first
715 // that is later than aEpoch and then move all prior to that to
716 // mTexturesInUseByGPU paired with aRenderedFrameId. These will be released
717 // once rendering has completed for aRenderedFrameId.
718 auto firstCompletedHostToKeep = std::find_if(
719 holder->mTextureHostsUntilRenderCompleted.begin(),
720 holder->mTextureHostsUntilRenderCompleted.end(),
721 [&aEpoch](const auto& entry) { return aEpoch <= entry->mEpoch; });
722 if (firstCompletedHostToKeep !=
723 holder->mTextureHostsUntilRenderCompleted.begin()) {
724 std::vector<UniquePtr<ForwardingTextureHost>> hostsUntilCompleted(
725 std::make_move_iterator(
726 holder->mTextureHostsUntilRenderCompleted.begin()),
727 std::make_move_iterator(firstCompletedHostToKeep));
728 mTexturesInUseByGPU.emplace_back(aRenderedFrameId,
729 std::move(hostsUntilCompleted));
730 holder->mTextureHostsUntilRenderCompleted.erase(
731 holder->mTextureHostsUntilRenderCompleted.begin(),
732 firstCompletedHostToKeep);
735 // Using aEpoch, find the first external image that we can't release and
736 // then release all prior to that.
737 auto firstImageToKeep = std::find_if(
738 holder->mExternalImages.begin(), holder->mExternalImages.end(),
739 [&aEpoch](const auto& entry) { return aEpoch <= entry->mEpoch; });
740 holder->mExternalImages.erase(holder->mExternalImages.begin(),
741 firstImageToKeep);
745 void AsyncImagePipelineManager::ProcessPipelineRemoved(
746 const wr::RemovedPipeline& aRemovedPipeline,
747 wr::RenderedFrameId aRenderedFrameId) {
748 if (mDestroyed) {
749 return;
751 if (auto entry = mPipelineTexturesHolders.Lookup(
752 wr::AsUint64(aRemovedPipeline.pipeline_id))) {
753 const auto& holder = entry.Data();
754 if (holder->mDestroyedEpoch.isSome()) {
755 if (!holder->mTextureHostsUntilRenderCompleted.empty()) {
756 // Move all TextureHosts that must be held until render completed to
757 // mTexturesInUseByGPU paired with aRenderedFrameId.
758 mTexturesInUseByGPU.emplace_back(
759 aRenderedFrameId,
760 std::move(holder->mTextureHostsUntilRenderCompleted));
763 // Remove Pipeline releasing all remaining TextureHosts and external
764 // images.
765 entry.Remove();
768 // If mDestroyedEpoch contains nothing it means we reused the same pipeline
769 // id (probably because we moved the tab to another window). In this case we
770 // need to keep the holder.
774 void AsyncImagePipelineManager::CheckForTextureHostsNotUsedByGPU() {
775 uint64_t lastCompletedFrameId = mLastCompletedFrameId;
777 // Find first entry after mLastCompletedFrameId and release all prior ones.
778 auto firstTexturesToKeep =
779 std::find_if(mTexturesInUseByGPU.begin(), mTexturesInUseByGPU.end(),
780 [lastCompletedFrameId](const auto& entry) {
781 return lastCompletedFrameId < entry.first.mId;
783 mTexturesInUseByGPU.erase(mTexturesInUseByGPU.begin(), firstTexturesToKeep);
786 wr::Epoch AsyncImagePipelineManager::GetNextImageEpoch() {
787 mAsyncImageEpoch.mHandle++;
788 return mAsyncImageEpoch;
791 AsyncImagePipelineManager::WebRenderPipelineInfoHolder::
792 WebRenderPipelineInfoHolder(RefPtr<const wr::WebRenderPipelineInfo>&& aInfo,
793 ipc::FileDescriptor&& aFenceFd)
794 : mInfo(aInfo), mFenceFd(aFenceFd) {}
796 AsyncImagePipelineManager::WebRenderPipelineInfoHolder::
797 ~WebRenderPipelineInfoHolder() = default;
799 } // namespace layers
800 } // namespace mozilla