Files
tubestation/gfx/layers/wr/AsyncImagePipelineManager.cpp
smolnar 03bc7728ba Backed out 5 changesets (bug 1691894) for causing hazard failures in nsXULPrototypeCache. CLOSED TREE
Backed out changeset 22dc870ee609 (bug 1691894)
Backed out changeset 58c31e9d6ae3 (bug 1691894)
Backed out changeset 7483e84149d8 (bug 1691894)
Backed out changeset f977d6cfa973 (bug 1691894)
Backed out changeset db4503476f34 (bug 1691894)
2021-02-15 16:43:23 +02:00

747 lines
26 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "AsyncImagePipelineManager.h"
#include <algorithm>
#include <iterator>
#include "CompositableHost.h"
#include "gfxEnv.h"
#include "mozilla/gfx/gfxVars.h"
#include "mozilla/layers/CompositorThread.h"
#include "mozilla/layers/SharedSurfacesParent.h"
#include "mozilla/layers/WebRenderImageHost.h"
#include "mozilla/layers/WebRenderTextureHost.h"
#include "mozilla/webrender/RenderThread.h"
#include "mozilla/webrender/WebRenderAPI.h"
#include "mozilla/webrender/WebRenderTypes.h"
#ifdef MOZ_WIDGET_ANDROID
# include "mozilla/layers/TextureHostOGL.h"
#endif
namespace mozilla {
namespace layers {
AsyncImagePipelineManager::ForwardingExternalImage::~ForwardingExternalImage() {
DebugOnly<bool> released = SharedSurfacesParent::Release(mImageId);
MOZ_ASSERT(released);
}
AsyncImagePipelineManager::AsyncImagePipeline::AsyncImagePipeline()
: mInitialised(false),
mIsChanged(false),
mUseExternalImage(false),
mRotation(VideoInfo::Rotation::kDegree_0),
mFilter(wr::ImageRendering::Auto),
mMixBlendMode(wr::MixBlendMode::Normal) {}
AsyncImagePipelineManager::AsyncImagePipelineManager(
RefPtr<wr::WebRenderAPI>&& aApi, bool aUseCompositorWnd)
: mApi(aApi),
mUseCompositorWnd(aUseCompositorWnd),
mIdNamespace(mApi->GetNamespace()),
mUseTripleBuffering(mApi->GetUseTripleBuffering()),
mResourceId(0),
mAsyncImageEpoch{0},
mWillGenerateFrame(false),
mDestroyed(false),
#ifdef XP_WIN
mUseWebRenderDCompVideoOverlayWin(
gfx::gfxVars::UseWebRenderDCompVideoOverlayWin()),
#endif
mRenderSubmittedUpdatesLock("SubmittedUpdatesLock"),
mLastCompletedFrameId(0) {
MOZ_COUNT_CTOR(AsyncImagePipelineManager);
}
AsyncImagePipelineManager::~AsyncImagePipelineManager() {
MOZ_COUNT_DTOR(AsyncImagePipelineManager);
}
void AsyncImagePipelineManager::Destroy() {
MOZ_ASSERT(!mDestroyed);
mApi = nullptr;
mPipelineTexturesHolders.Clear();
mDestroyed = true;
}
/* static */
wr::ExternalImageId AsyncImagePipelineManager::GetNextExternalImageId() {
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
static uint64_t sResourceId = 0;
++sResourceId;
// Upper 32bit(namespace) needs to be 0.
// Namespace other than 0 might be used by others.
MOZ_RELEASE_ASSERT(sResourceId != UINT32_MAX);
return wr::ToExternalImageId(sResourceId);
}
void AsyncImagePipelineManager::SetWillGenerateFrame() {
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
mWillGenerateFrame = true;
}
bool AsyncImagePipelineManager::GetAndResetWillGenerateFrame() {
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
bool ret = mWillGenerateFrame;
mWillGenerateFrame = false;
return ret;
}
void AsyncImagePipelineManager::AddPipeline(const wr::PipelineId& aPipelineId,
WebRenderBridgeParent* aWrBridge) {
if (mDestroyed) {
return;
}
uint64_t id = wr::AsUint64(aPipelineId);
PipelineTexturesHolder* holder =
mPipelineTexturesHolders.Get(wr::AsUint64(aPipelineId));
if (holder) {
// This could happen during tab move between different windows.
// Previously removed holder could be still alive for waiting destroyed.
MOZ_ASSERT(holder->mDestroyedEpoch.isSome());
holder->mDestroyedEpoch = Nothing(); // Revive holder
holder->mWrBridge = aWrBridge;
return;
}
holder = new PipelineTexturesHolder();
holder->mWrBridge = aWrBridge;
mPipelineTexturesHolders.Put(id, holder);
}
void AsyncImagePipelineManager::RemovePipeline(
const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch) {
if (mDestroyed) {
return;
}
PipelineTexturesHolder* holder =
mPipelineTexturesHolders.Get(wr::AsUint64(aPipelineId));
MOZ_ASSERT(holder);
if (!holder) {
return;
}
holder->mWrBridge = nullptr;
holder->mDestroyedEpoch = Some(aEpoch);
}
WebRenderBridgeParent* AsyncImagePipelineManager::GetWrBridge(
const wr::PipelineId& aPipelineId) {
if (mDestroyed) {
return nullptr;
}
PipelineTexturesHolder* holder =
mPipelineTexturesHolders.Get(wr::AsUint64(aPipelineId));
if (!holder) {
return nullptr;
}
if (holder->mWrBridge) {
MOZ_ASSERT(holder->mDestroyedEpoch.isNothing());
return holder->mWrBridge;
}
return nullptr;
}
void AsyncImagePipelineManager::AddAsyncImagePipeline(
const wr::PipelineId& aPipelineId, WebRenderImageHost* aImageHost) {
if (mDestroyed) {
return;
}
MOZ_ASSERT(aImageHost);
uint64_t id = wr::AsUint64(aPipelineId);
MOZ_ASSERT(!mAsyncImagePipelines.Get(id));
AsyncImagePipeline* holder = new AsyncImagePipeline();
holder->mImageHost = aImageHost;
mAsyncImagePipelines.Put(id, holder);
AddPipeline(aPipelineId, /* aWrBridge */ nullptr);
}
void AsyncImagePipelineManager::RemoveAsyncImagePipeline(
const wr::PipelineId& aPipelineId, wr::TransactionBuilder& aTxn) {
if (mDestroyed) {
return;
}
uint64_t id = wr::AsUint64(aPipelineId);
if (auto entry = mAsyncImagePipelines.Lookup(id)) {
const auto& holder = entry.Data();
wr::Epoch epoch = GetNextImageEpoch();
aTxn.ClearDisplayList(epoch, aPipelineId);
for (wr::ImageKey key : holder->mKeys) {
aTxn.DeleteImage(key);
}
entry.Remove();
RemovePipeline(aPipelineId, epoch);
}
}
void AsyncImagePipelineManager::UpdateAsyncImagePipeline(
const wr::PipelineId& aPipelineId, const LayoutDeviceRect& aScBounds,
const VideoInfo::Rotation aRotation, const wr::ImageRendering& aFilter,
const wr::MixBlendMode& aMixBlendMode) {
if (mDestroyed) {
return;
}
AsyncImagePipeline* pipeline =
mAsyncImagePipelines.Get(wr::AsUint64(aPipelineId));
if (!pipeline) {
return;
}
pipeline->mInitialised = true;
pipeline->Update(aScBounds, aRotation, aFilter, aMixBlendMode);
}
Maybe<TextureHost::ResourceUpdateOp> AsyncImagePipelineManager::UpdateImageKeys(
const wr::Epoch& aEpoch, const wr::PipelineId& aPipelineId,
AsyncImagePipeline* aPipeline, nsTArray<wr::ImageKey>& aKeys,
wr::TransactionBuilder& aSceneBuilderTxn,
wr::TransactionBuilder& aMaybeFastTxn) {
MOZ_ASSERT(aKeys.IsEmpty());
MOZ_ASSERT(aPipeline);
TextureHost* texture =
aPipeline->mImageHost->GetAsTextureHostForComposite(this);
TextureHost* previousTexture = aPipeline->mCurrentTexture.get();
if (texture == previousTexture) {
// The texture has not changed, just reuse previous ImageKeys.
aKeys = aPipeline->mKeys.Clone();
return Nothing();
}
if (!texture || texture->NumSubTextures() == 0) {
// We don't have a new texture or texture does not have SubTextures, there
// isn't much we can do.
aKeys = aPipeline->mKeys.Clone();
return Nothing();
}
aPipeline->mCurrentTexture = texture;
WebRenderTextureHost* wrTexture = texture->AsWebRenderTextureHost();
MOZ_ASSERT(wrTexture);
if (!wrTexture) {
gfxCriticalNote << "WebRenderTextureHost is not used";
}
bool useExternalImage = !!wrTexture;
aPipeline->mUseExternalImage = useExternalImage;
// The non-external image code path falls back to converting the texture into
// an rgb image.
auto numKeys = useExternalImage ? texture->NumSubTextures() : 1;
MOZ_ASSERT(numKeys > 0);
// If we already had a texture and the format hasn't changed, better to reuse
// the image keys than create new ones.
bool canUpdate = !!previousTexture &&
previousTexture->GetSize() == texture->GetSize() &&
previousTexture->GetFormat() == texture->GetFormat() &&
previousTexture->NeedsYFlip() == texture->NeedsYFlip() &&
previousTexture->SupportsExternalCompositing() ==
texture->SupportsExternalCompositing() &&
aPipeline->mKeys.Length() == numKeys;
if (!canUpdate) {
for (auto key : aPipeline->mKeys) {
// Destroy ImageKeys on transaction of scene builder thread, since
// DisplayList is updated on SceneBuilder thread. It prevents too early
// ImageKey deletion.
aSceneBuilderTxn.DeleteImage(key);
}
aPipeline->mKeys.Clear();
for (uint32_t i = 0; i < numKeys; ++i) {
aPipeline->mKeys.AppendElement(GenerateImageKey());
}
}
aKeys = aPipeline->mKeys.Clone();
auto op = canUpdate ? TextureHost::UPDATE_IMAGE : TextureHost::ADD_IMAGE;
if (!useExternalImage) {
return UpdateWithoutExternalImage(texture, aKeys[0], op, aMaybeFastTxn);
}
wrTexture->MaybeNotifyForUse(aMaybeFastTxn);
Range<wr::ImageKey> keys(&aKeys[0], aKeys.Length());
auto externalImageKey = wrTexture->GetExternalImageKey();
wrTexture->PushResourceUpdates(aMaybeFastTxn, op, keys, externalImageKey);
return Some(op);
}
Maybe<TextureHost::ResourceUpdateOp>
AsyncImagePipelineManager::UpdateWithoutExternalImage(
TextureHost* aTexture, wr::ImageKey aKey, TextureHost::ResourceUpdateOp aOp,
wr::TransactionBuilder& aTxn) {
MOZ_ASSERT(aTexture);
RefPtr<gfx::DataSourceSurface> dSurf = aTexture->GetAsSurface();
if (!dSurf) {
NS_ERROR("TextureHost does not return DataSourceSurface");
return Nothing();
}
gfx::DataSourceSurface::MappedSurface map;
if (!dSurf->Map(gfx::DataSourceSurface::MapType::READ, &map)) {
NS_ERROR("DataSourceSurface failed to map");
return Nothing();
}
gfx::IntSize size = dSurf->GetSize();
wr::ImageDescriptor descriptor(size, map.mStride, dSurf->GetFormat());
// Costly copy right here...
wr::Vec<uint8_t> bytes;
bytes.PushBytes(Range<uint8_t>(map.mData, size.height * map.mStride));
if (aOp == TextureHost::UPDATE_IMAGE) {
aTxn.UpdateImageBuffer(aKey, descriptor, bytes);
} else {
aTxn.AddImage(aKey, descriptor, bytes);
}
dSurf->Unmap();
return Some(aOp);
}
void AsyncImagePipelineManager::ApplyAsyncImagesOfImageBridge(
wr::TransactionBuilder& aSceneBuilderTxn,
wr::TransactionBuilder& aFastTxn) {
if (mDestroyed || mAsyncImagePipelines.Count() == 0) {
return;
}
#ifdef XP_WIN
// UseWebRenderDCompVideoOverlayWin() could be changed from true to false,
// when DCompVideoOverlay task is failed. In this case, DisplayItems need to
// be re-pushed to WebRender for disabling video overlay.
bool isChanged = mUseWebRenderDCompVideoOverlayWin !=
gfx::gfxVars::UseWebRenderDCompVideoOverlayWin();
if (isChanged) {
mUseWebRenderDCompVideoOverlayWin =
gfx::gfxVars::UseWebRenderDCompVideoOverlayWin();
}
#endif
wr::Epoch epoch = GetNextImageEpoch();
// We use a pipeline with a very small display list for each video element.
// Update each of them if needed.
for (auto iter = mAsyncImagePipelines.Iter(); !iter.Done(); iter.Next()) {
wr::PipelineId pipelineId = wr::AsPipelineId(iter.Key());
AsyncImagePipeline* pipeline = iter.UserData();
#ifdef XP_WIN
if (isChanged) {
pipeline->mIsChanged = true;
}
#endif
// If aync image pipeline does not use ImageBridge, do not need to apply.
if (!pipeline->mImageHost->GetAsyncRef()) {
continue;
}
ApplyAsyncImageForPipeline(epoch, pipelineId, pipeline, aSceneBuilderTxn,
aFastTxn);
}
}
wr::WrRotation ToWrRotation(VideoInfo::Rotation aRotation) {
switch (aRotation) {
case VideoInfo::Rotation::kDegree_0:
return wr::WrRotation::Degree0;
case VideoInfo::Rotation::kDegree_90:
return wr::WrRotation::Degree90;
case VideoInfo::Rotation::kDegree_180:
return wr::WrRotation::Degree180;
case VideoInfo::Rotation::kDegree_270:
return wr::WrRotation::Degree270;
}
return wr::WrRotation::Degree0;
}
void AsyncImagePipelineManager::ApplyAsyncImageForPipeline(
const wr::Epoch& aEpoch, const wr::PipelineId& aPipelineId,
AsyncImagePipeline* aPipeline, wr::TransactionBuilder& aSceneBuilderTxn,
wr::TransactionBuilder& aMaybeFastTxn) {
nsTArray<wr::ImageKey> keys;
auto op = UpdateImageKeys(aEpoch, aPipelineId, aPipeline, keys,
aSceneBuilderTxn, aMaybeFastTxn);
bool updateDisplayList =
aPipeline->mInitialised &&
(aPipeline->mIsChanged || op == Some(TextureHost::ADD_IMAGE)) &&
!!aPipeline->mCurrentTexture;
if (!updateDisplayList) {
// We don't need to update the display list, either because we can't or
// because the previous one is still up to date. We may, however, have
// updated some resources.
// Use transaction of scene builder thread to notify epoch.
// It is for making epoch update consistent.
aSceneBuilderTxn.UpdateEpoch(aPipelineId, aEpoch);
if (aPipeline->mCurrentTexture) {
HoldExternalImage(aPipelineId, aEpoch, aPipeline->mCurrentTexture);
}
return;
}
aPipeline->mIsChanged = false;
wr::DisplayListBuilder builder(aPipelineId);
float opacity = 1.0f;
wr::StackingContextParams params;
params.opacity = &opacity;
params.mix_blend_mode = aPipeline->mMixBlendMode;
wr::WrComputedTransformData computedTransform;
computedTransform.vertical_flip =
aPipeline->mCurrentTexture && aPipeline->mCurrentTexture->NeedsYFlip();
computedTransform.scale_from = {
float(aPipeline->mCurrentTexture->GetSize().width),
float(aPipeline->mCurrentTexture->GetSize().height)};
computedTransform.rotation = ToWrRotation(aPipeline->mRotation);
params.computed_transform = &computedTransform;
Maybe<wr::WrSpatialId> referenceFrameId = builder.PushStackingContext(
params, wr::ToLayoutRect(aPipeline->mScBounds),
// This is fine to do unconditionally because we only push images here.
wr::RasterSpace::Screen());
Maybe<wr::SpaceAndClipChainHelper> spaceAndClipChainHelper;
if (referenceFrameId) {
spaceAndClipChainHelper.emplace(builder, referenceFrameId.ref());
}
if (aPipeline->mCurrentTexture && !keys.IsEmpty()) {
LayoutDeviceRect rect(0, 0, aPipeline->mCurrentTexture->GetSize().width,
aPipeline->mCurrentTexture->GetSize().height);
if (aPipeline->mUseExternalImage) {
MOZ_ASSERT(aPipeline->mCurrentTexture->AsWebRenderTextureHost());
Range<wr::ImageKey> range_keys(&keys[0], keys.Length());
TextureHost::PushDisplayItemFlagSet flags;
if (IsOpaque(aPipeline->mCurrentTexture->GetFormat()) ||
bool(aPipeline->mCurrentTexture->GetFlags() &
TextureFlags::IS_OPAQUE)) {
flags += TextureHost::PushDisplayItemFlag::PREFER_COMPOSITOR_SURFACE;
}
if (mApi->SupportsExternalBufferTextures()) {
flags +=
TextureHost::PushDisplayItemFlag::SUPPORTS_EXTERNAL_BUFFER_TEXTURES;
}
aPipeline->mCurrentTexture->PushDisplayItems(
builder, wr::ToLayoutRect(rect), wr::ToLayoutRect(rect),
aPipeline->mFilter, range_keys, flags);
HoldExternalImage(aPipelineId, aEpoch, aPipeline->mCurrentTexture);
} else {
MOZ_ASSERT(keys.Length() == 1);
builder.PushImage(wr::ToLayoutRect(rect), wr::ToLayoutRect(rect), true,
aPipeline->mFilter, keys[0]);
}
}
spaceAndClipChainHelper.reset();
builder.PopStackingContext(referenceFrameId.isSome());
wr::BuiltDisplayList dl;
builder.Finalize(dl);
aSceneBuilderTxn.SetDisplayList(gfx::DeviceColor(0.f, 0.f, 0.f, 0.f), aEpoch,
wr::ToLayoutSize(aPipeline->mScBounds.Size()),
aPipelineId, dl.dl_desc, dl.dl);
}
void AsyncImagePipelineManager::ApplyAsyncImageForPipeline(
const wr::PipelineId& aPipelineId, wr::TransactionBuilder& aTxn,
wr::TransactionBuilder& aTxnForImageBridge) {
AsyncImagePipeline* pipeline =
mAsyncImagePipelines.Get(wr::AsUint64(aPipelineId));
if (!pipeline) {
return;
}
wr::TransactionBuilder fastTxn(/* aUseSceneBuilderThread */ false);
wr::AutoTransactionSender sender(mApi, &fastTxn);
// Transaction for async image pipeline that uses ImageBridge always need to
// be non low priority.
auto& sceneBuilderTxn =
pipeline->mImageHost->GetAsyncRef() ? aTxnForImageBridge : aTxn;
// Use transaction of using non scene builder thread when ImageHost uses
// ImageBridge. ApplyAsyncImagesOfImageBridge() handles transaction of adding
// and updating wr::ImageKeys of ImageHosts that uses ImageBridge. Then
// AsyncImagePipelineManager always needs to use non scene builder thread
// transaction for adding and updating wr::ImageKeys of ImageHosts that uses
// ImageBridge. Otherwise, ordering of wr::ImageKeys updating in webrender
// becomes inconsistent.
auto& maybeFastTxn = pipeline->mImageHost->GetAsyncRef() ? fastTxn : aTxn;
wr::Epoch epoch = GetNextImageEpoch();
ApplyAsyncImageForPipeline(epoch, aPipelineId, pipeline, sceneBuilderTxn,
maybeFastTxn);
}
void AsyncImagePipelineManager::SetEmptyDisplayList(
const wr::PipelineId& aPipelineId, wr::TransactionBuilder& aTxn,
wr::TransactionBuilder& aTxnForImageBridge) {
AsyncImagePipeline* pipeline =
mAsyncImagePipelines.Get(wr::AsUint64(aPipelineId));
if (!pipeline) {
return;
}
// Transaction for async image pipeline that uses ImageBridge always need to
// be non low priority.
auto& txn = pipeline->mImageHost->GetAsyncRef() ? aTxnForImageBridge : aTxn;
wr::Epoch epoch = GetNextImageEpoch();
wr::DisplayListBuilder builder(aPipelineId);
wr::BuiltDisplayList dl;
builder.Finalize(dl);
txn.SetDisplayList(gfx::DeviceColor(0.f, 0.f, 0.f, 0.f), epoch,
wr::ToLayoutSize(pipeline->mScBounds.Size()), aPipelineId,
dl.dl_desc, dl.dl);
}
void AsyncImagePipelineManager::HoldExternalImage(
const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch,
TextureHost* aTexture) {
if (mDestroyed) {
return;
}
MOZ_ASSERT(aTexture);
PipelineTexturesHolder* holder =
mPipelineTexturesHolders.Get(wr::AsUint64(aPipelineId));
MOZ_ASSERT(holder);
if (!holder) {
return;
}
if (aTexture->NeedsDeferredDeletion()) {
// Hold WebRenderTextureHost until rendering completed.
holder->mTextureHostsUntilRenderCompleted.emplace_back(
MakeUnique<ForwardingTextureHost>(aEpoch, aTexture));
} else {
// Hold WebRenderTextureHost until submitted for rendering.
holder->mTextureHostsUntilRenderSubmitted.emplace_back(aEpoch, aTexture);
}
}
void AsyncImagePipelineManager::HoldExternalImage(
const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch,
const wr::ExternalImageId& aImageId) {
if (mDestroyed) {
SharedSurfacesParent::Release(aImageId);
return;
}
PipelineTexturesHolder* holder =
mPipelineTexturesHolders.Get(wr::AsUint64(aPipelineId));
MOZ_ASSERT(holder);
if (!holder) {
SharedSurfacesParent::Release(aImageId);
return;
}
holder->mExternalImages.emplace_back(
MakeUnique<ForwardingExternalImage>(aEpoch, aImageId));
}
void AsyncImagePipelineManager::NotifyPipelinesUpdated(
RefPtr<const wr::WebRenderPipelineInfo> aInfo,
wr::RenderedFrameId aLatestFrameId,
wr::RenderedFrameId aLastCompletedFrameId, ipc::FileDescriptor&& aFenceFd) {
MOZ_ASSERT(wr::RenderThread::IsInRenderThread());
MOZ_ASSERT(mLastCompletedFrameId <= aLastCompletedFrameId.mId);
MOZ_ASSERT(aLatestFrameId.IsValid());
mLastCompletedFrameId = aLastCompletedFrameId.mId;
{
// We need to lock for mRenderSubmittedUpdates because it can be accessed
// on the compositor thread.
MutexAutoLock lock(mRenderSubmittedUpdatesLock);
// Move the pending updates into the submitted ones.
mRenderSubmittedUpdates.emplace_back(
aLatestFrameId,
WebRenderPipelineInfoHolder(std::move(aInfo), std::move(aFenceFd)));
}
// Queue a runnable on the compositor thread to process the updates.
// This will also call CheckForTextureHostsNotUsedByGPU.
layers::CompositorThread()->Dispatch(
NewRunnableMethod("ProcessPipelineUpdates", this,
&AsyncImagePipelineManager::ProcessPipelineUpdates));
}
void AsyncImagePipelineManager::ProcessPipelineUpdates() {
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
if (mDestroyed) {
return;
}
std::vector<std::pair<wr::RenderedFrameId, WebRenderPipelineInfoHolder>>
submittedUpdates;
{
// We need to lock for mRenderSubmittedUpdates because it can be accessed on
// the compositor thread.
MutexAutoLock lock(mRenderSubmittedUpdatesLock);
mRenderSubmittedUpdates.swap(submittedUpdates);
}
// submittedUpdates is a vector of RenderedFrameIds paired with vectors of
// WebRenderPipelineInfo.
for (auto update : submittedUpdates) {
auto& holder = update.second;
const auto& info = holder.mInfo->Raw();
mReleaseFenceFd = std::move(holder.mFenceFd);
for (auto& epoch : info.epochs) {
ProcessPipelineRendered(epoch.pipeline_id, epoch.epoch, update.first);
}
for (auto& removedPipeline : info.removed_pipelines) {
ProcessPipelineRemoved(removedPipeline, update.first);
}
}
CheckForTextureHostsNotUsedByGPU();
}
void AsyncImagePipelineManager::ProcessPipelineRendered(
const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch,
wr::RenderedFrameId aRenderedFrameId) {
if (auto entry = mPipelineTexturesHolders.Lookup(wr::AsUint64(aPipelineId))) {
const auto& holder = entry.Data();
// For TextureHosts that can be released on render submission, using aEpoch
// find the first that we can't release and then release all prior to that.
auto firstSubmittedHostToKeep = std::find_if(
holder->mTextureHostsUntilRenderSubmitted.begin(),
holder->mTextureHostsUntilRenderSubmitted.end(),
[&aEpoch](const auto& entry) { return aEpoch <= entry.mEpoch; });
#ifdef MOZ_WIDGET_ANDROID
// Set release fence if TextureHost owns AndroidHardwareBuffer.
// The TextureHost handled by mTextureHostsUntilRenderSubmitted instead of
// mTextureHostsUntilRenderCompleted, since android fence could be used
// to wait until its end of usage by GPU.
for (auto it = holder->mTextureHostsUntilRenderSubmitted.begin();
it != firstSubmittedHostToKeep; ++it) {
const auto& entry = it;
if (entry->mTexture->GetAndroidHardwareBuffer()) {
ipc::FileDescriptor fenceFd = mReleaseFenceFd;
entry->mTexture->SetReleaseFence(std::move(fenceFd));
}
}
#endif
holder->mTextureHostsUntilRenderSubmitted.erase(
holder->mTextureHostsUntilRenderSubmitted.begin(),
firstSubmittedHostToKeep);
// For TextureHosts that need to wait until render completed, find the first
// that is later than aEpoch and then move all prior to that to
// mTexturesInUseByGPU paired with aRenderedFrameId. These will be released
// once rendering has completed for aRenderedFrameId.
auto firstCompletedHostToKeep = std::find_if(
holder->mTextureHostsUntilRenderCompleted.begin(),
holder->mTextureHostsUntilRenderCompleted.end(),
[&aEpoch](const auto& entry) { return aEpoch <= entry->mEpoch; });
if (firstCompletedHostToKeep !=
holder->mTextureHostsUntilRenderCompleted.begin()) {
std::vector<UniquePtr<ForwardingTextureHost>> hostsUntilCompleted(
std::make_move_iterator(
holder->mTextureHostsUntilRenderCompleted.begin()),
std::make_move_iterator(firstCompletedHostToKeep));
mTexturesInUseByGPU.emplace_back(aRenderedFrameId,
std::move(hostsUntilCompleted));
holder->mTextureHostsUntilRenderCompleted.erase(
holder->mTextureHostsUntilRenderCompleted.begin(),
firstCompletedHostToKeep);
}
// Using aEpoch, find the first external image that we can't release and
// then release all prior to that.
auto firstImageToKeep = std::find_if(
holder->mExternalImages.begin(), holder->mExternalImages.end(),
[&aEpoch](const auto& entry) { return aEpoch <= entry->mEpoch; });
holder->mExternalImages.erase(holder->mExternalImages.begin(),
firstImageToKeep);
}
}
void AsyncImagePipelineManager::ProcessPipelineRemoved(
const wr::RemovedPipeline& aRemovedPipeline,
wr::RenderedFrameId aRenderedFrameId) {
if (mDestroyed) {
return;
}
if (auto entry = mPipelineTexturesHolders.Lookup(
wr::AsUint64(aRemovedPipeline.pipeline_id))) {
const auto& holder = entry.Data();
if (holder->mDestroyedEpoch.isSome()) {
if (!holder->mTextureHostsUntilRenderCompleted.empty()) {
// Move all TextureHosts that must be held until render completed to
// mTexturesInUseByGPU paired with aRenderedFrameId.
mTexturesInUseByGPU.emplace_back(
aRenderedFrameId,
std::move(holder->mTextureHostsUntilRenderCompleted));
}
// Remove Pipeline releasing all remaining TextureHosts and external
// images.
entry.Remove();
}
// If mDestroyedEpoch contains nothing it means we reused the same pipeline
// id (probably because we moved the tab to another window). In this case we
// need to keep the holder.
}
}
void AsyncImagePipelineManager::CheckForTextureHostsNotUsedByGPU() {
uint64_t lastCompletedFrameId = mLastCompletedFrameId;
// Find first entry after mLastCompletedFrameId and release all prior ones.
auto firstTexturesToKeep =
std::find_if(mTexturesInUseByGPU.begin(), mTexturesInUseByGPU.end(),
[lastCompletedFrameId](const auto& entry) {
return lastCompletedFrameId < entry.first.mId;
});
mTexturesInUseByGPU.erase(mTexturesInUseByGPU.begin(), firstTexturesToKeep);
}
wr::Epoch AsyncImagePipelineManager::GetNextImageEpoch() {
mAsyncImageEpoch.mHandle++;
return mAsyncImageEpoch;
}
AsyncImagePipelineManager::WebRenderPipelineInfoHolder::
WebRenderPipelineInfoHolder(RefPtr<const wr::WebRenderPipelineInfo>&& aInfo,
ipc::FileDescriptor&& aFenceFd)
: mInfo(aInfo), mFenceFd(aFenceFd) {}
AsyncImagePipelineManager::WebRenderPipelineInfoHolder::
~WebRenderPipelineInfoHolder() = default;
} // namespace layers
} // namespace mozilla