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