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