tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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