Previously, the WebRenderBridgeParent for each content layer tree would use the same WebRenderAPI instance as the top-level WebRenderBridgeParent for that window. However, in order to make the namespacing changes work we now need to use a separate WebRenderAPI instance for each WebRenderBridgeParent. The content WebRenderAPIs are cloned from the parent one, so that they all share the same backend, but can allocate resource IDs in distinct namespaces. MozReview-Commit-ID: 7VTFL8F09n7
362 lines
11 KiB
C++
362 lines
11 KiB
C++
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
* 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 "CompositableHost.h"
|
|
#include "gfxEnv.h"
|
|
#include "mozilla/gfx/gfxVars.h"
|
|
#include "mozilla/layers/WebRenderImageHost.h"
|
|
#include "mozilla/layers/WebRenderTextureHost.h"
|
|
#include "mozilla/webrender/WebRenderAPI.h"
|
|
|
|
namespace mozilla {
|
|
namespace layers {
|
|
|
|
AsyncImagePipelineManager::AsyncImagePipeline::AsyncImagePipeline()
|
|
: mInitialised(false)
|
|
, mIsChanged(false)
|
|
, mUseExternalImage(false)
|
|
, mFilter(wr::ImageRendering::Auto)
|
|
, mMixBlendMode(wr::MixBlendMode::Normal)
|
|
{}
|
|
|
|
AsyncImagePipelineManager::AsyncImagePipelineManager(already_AddRefed<wr::WebRenderAPI>&& aApi)
|
|
: mApi(aApi)
|
|
, mIdNamespace(mApi->GetNamespace())
|
|
, mResourceId(0)
|
|
, mAsyncImageEpoch(0)
|
|
, mDestroyed(false)
|
|
{
|
|
MOZ_COUNT_CTOR(AsyncImagePipelineManager);
|
|
}
|
|
|
|
AsyncImagePipelineManager::~AsyncImagePipelineManager()
|
|
{
|
|
MOZ_COUNT_DTOR(AsyncImagePipelineManager);
|
|
}
|
|
|
|
void
|
|
AsyncImagePipelineManager::Destroy()
|
|
{
|
|
MOZ_ASSERT(!mDestroyed);
|
|
DeleteOldAsyncImages();
|
|
mApi = nullptr;
|
|
mDestroyed = true;
|
|
}
|
|
|
|
bool
|
|
AsyncImagePipelineManager::HasKeysToDelete()
|
|
{
|
|
return !mKeysToDelete.IsEmpty();
|
|
}
|
|
|
|
void
|
|
AsyncImagePipelineManager::DeleteOldAsyncImages()
|
|
{
|
|
MOZ_ASSERT(!mDestroyed);
|
|
for (wr::ImageKey key : mKeysToDelete) {
|
|
mApi->DeleteImage(key);
|
|
}
|
|
mKeysToDelete.Clear();
|
|
}
|
|
|
|
void
|
|
AsyncImagePipelineManager::AddPipeline(const wr::PipelineId& aPipelineId)
|
|
{
|
|
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
|
|
return;
|
|
}
|
|
holder = new PipelineTexturesHolder();
|
|
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->mDestroyedEpoch = Some(aEpoch);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
void
|
|
AsyncImagePipelineManager::RemoveAsyncImagePipeline(const wr::PipelineId& aPipelineId)
|
|
{
|
|
if (mDestroyed) {
|
|
return;
|
|
}
|
|
|
|
uint64_t id = wr::AsUint64(aPipelineId);
|
|
if (auto entry = mAsyncImagePipelines.Lookup(id)) {
|
|
AsyncImagePipeline* holder = entry.Data();
|
|
++mAsyncImageEpoch; // Update webrender epoch
|
|
mApi->ClearRootDisplayList(wr::NewEpoch(mAsyncImageEpoch), aPipelineId);
|
|
for (wr::ImageKey key : holder->mKeys) {
|
|
mApi->DeleteImage(key);
|
|
}
|
|
entry.Remove();
|
|
RemovePipeline(aPipelineId, wr::NewEpoch(mAsyncImageEpoch));
|
|
}
|
|
}
|
|
|
|
void
|
|
AsyncImagePipelineManager::UpdateAsyncImagePipeline(const wr::PipelineId& aPipelineId,
|
|
const LayerRect& aScBounds,
|
|
const gfx::Matrix4x4& aScTransform,
|
|
const gfx::MaybeIntSize& aScaleToSize,
|
|
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->mIsChanged = true;
|
|
pipeline->mScBounds = aScBounds;
|
|
pipeline->mScTransform = aScTransform;
|
|
pipeline->mScaleToSize = aScaleToSize;
|
|
pipeline->mFilter = aFilter;
|
|
pipeline->mMixBlendMode = aMixBlendMode;
|
|
}
|
|
|
|
bool
|
|
AsyncImagePipelineManager::GenerateImageKeyForTextureHost(TextureHost* aTexture, nsTArray<wr::ImageKey>& aKeys)
|
|
{
|
|
MOZ_ASSERT(aKeys.IsEmpty());
|
|
MOZ_ASSERT(aTexture);
|
|
|
|
WebRenderTextureHost* wrTexture = aTexture->AsWebRenderTextureHost();
|
|
|
|
if (!gfxEnv::EnableWebRenderRecording() && wrTexture) {
|
|
wrTexture->GetWRImageKeys(aKeys, std::bind(&AsyncImagePipelineManager::GenerateImageKey, this));
|
|
MOZ_ASSERT(!aKeys.IsEmpty());
|
|
Range<const wr::ImageKey> keys(&aKeys[0], aKeys.Length());
|
|
wrTexture->AddWRImage(mApi, keys, wrTexture->GetExternalImageKey());
|
|
return true;
|
|
} else {
|
|
RefPtr<gfx::DataSourceSurface> dSurf = aTexture->GetAsSurface();
|
|
if (!dSurf) {
|
|
NS_ERROR("TextureHost does not return DataSourceSurface");
|
|
return false;
|
|
}
|
|
gfx::DataSourceSurface::MappedSurface map;
|
|
if (!dSurf->Map(gfx::DataSourceSurface::MapType::READ, &map)) {
|
|
NS_ERROR("DataSourceSurface failed to map");
|
|
return false;
|
|
}
|
|
gfx::IntSize size = dSurf->GetSize();
|
|
wr::ImageDescriptor descriptor(size, map.mStride, dSurf->GetFormat());
|
|
auto slice = Range<uint8_t>(map.mData, size.height * map.mStride);
|
|
|
|
wr::ImageKey key = GenerateImageKey();
|
|
aKeys.AppendElement(key);
|
|
mApi->AddImage(key, descriptor, slice);
|
|
dSurf->Unmap();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
AsyncImagePipelineManager::UpdateImageKeys(bool& aUseExternalImage,
|
|
AsyncImagePipeline* aImageMgr,
|
|
nsTArray<wr::ImageKey>& aKeys,
|
|
nsTArray<wr::ImageKey>& aKeysToDelete)
|
|
{
|
|
MOZ_ASSERT(aKeys.IsEmpty());
|
|
MOZ_ASSERT(aImageMgr);
|
|
TextureHost* texture = aImageMgr->mImageHost->GetAsTextureHostForComposite();
|
|
|
|
if (!aImageMgr->mInitialised) {
|
|
return false;
|
|
}
|
|
|
|
// No change
|
|
if (!aImageMgr->mIsChanged && texture == aImageMgr->mCurrentTexture) {
|
|
// No need to update DisplayList.
|
|
return false;
|
|
}
|
|
|
|
aImageMgr->mIsChanged = false;
|
|
|
|
if (texture == aImageMgr->mCurrentTexture) {
|
|
// Reuse previous ImageKeys.
|
|
aKeys.AppendElements(aImageMgr->mKeys);
|
|
aUseExternalImage = aImageMgr->mUseExternalImage;
|
|
return true;
|
|
}
|
|
|
|
// Delete old ImageKeys
|
|
aKeysToDelete.AppendElements(aImageMgr->mKeys);
|
|
aImageMgr->mKeys.Clear();
|
|
aImageMgr->mCurrentTexture = nullptr;
|
|
|
|
// No txture to render
|
|
if (!texture) {
|
|
return true;
|
|
}
|
|
|
|
aUseExternalImage = aImageMgr->mUseExternalImage = GenerateImageKeyForTextureHost(texture, aKeys);
|
|
MOZ_ASSERT(!aKeys.IsEmpty());
|
|
aImageMgr->mKeys.AppendElements(aKeys);
|
|
aImageMgr->mCurrentTexture = texture;
|
|
return true;
|
|
}
|
|
|
|
void
|
|
AsyncImagePipelineManager::ApplyAsyncImages()
|
|
{
|
|
if (mDestroyed || mAsyncImagePipelines.Count() == 0) {
|
|
return;
|
|
}
|
|
|
|
++mAsyncImageEpoch; // Update webrender epoch
|
|
wr::Epoch epoch = wr::NewEpoch(mAsyncImageEpoch);
|
|
nsTArray<wr::ImageKey> keysToDelete;
|
|
|
|
for (auto iter = mAsyncImagePipelines.Iter(); !iter.Done(); iter.Next()) {
|
|
wr::PipelineId pipelineId = wr::AsPipelineId(iter.Key());
|
|
AsyncImagePipeline* pipeline = iter.Data();
|
|
|
|
nsTArray<wr::ImageKey> keys;
|
|
bool useExternalImage = false;
|
|
bool updateDisplayList = UpdateImageKeys(useExternalImage,
|
|
pipeline,
|
|
keys,
|
|
keysToDelete);
|
|
if (!updateDisplayList) {
|
|
continue;
|
|
}
|
|
|
|
wr::LayoutSize contentSize { pipeline->mScBounds.width, pipeline->mScBounds.height };
|
|
wr::DisplayListBuilder builder(pipelineId, contentSize);
|
|
|
|
if (!keys.IsEmpty()) {
|
|
MOZ_ASSERT(pipeline->mCurrentTexture.get());
|
|
|
|
float opacity = 1.0f;
|
|
builder.PushStackingContext(wr::ToLayoutRect(pipeline->mScBounds),
|
|
0,
|
|
&opacity,
|
|
pipeline->mScTransform.IsIdentity() ? nullptr : &pipeline->mScTransform,
|
|
wr::TransformStyle::Flat,
|
|
pipeline->mMixBlendMode,
|
|
nsTArray<wr::WrFilterOp>());
|
|
|
|
LayerRect rect(0, 0, pipeline->mCurrentTexture->GetSize().width, pipeline->mCurrentTexture->GetSize().height);
|
|
if (pipeline->mScaleToSize.isSome()) {
|
|
rect = LayerRect(0, 0, pipeline->mScaleToSize.value().width, pipeline->mScaleToSize.value().height);
|
|
}
|
|
|
|
if (useExternalImage) {
|
|
MOZ_ASSERT(pipeline->mCurrentTexture->AsWebRenderTextureHost());
|
|
Range<const wr::ImageKey> range_keys(&keys[0], keys.Length());
|
|
pipeline->mCurrentTexture->PushExternalImage(builder,
|
|
wr::ToLayoutRect(rect),
|
|
wr::ToLayoutRect(rect),
|
|
pipeline->mFilter,
|
|
range_keys);
|
|
HoldExternalImage(pipelineId, epoch, pipeline->mCurrentTexture->AsWebRenderTextureHost());
|
|
} else {
|
|
MOZ_ASSERT(keys.Length() == 1);
|
|
builder.PushImage(wr::ToLayoutRect(rect),
|
|
wr::ToLayoutRect(rect),
|
|
pipeline->mFilter,
|
|
keys[0]);
|
|
}
|
|
builder.PopStackingContext();
|
|
}
|
|
|
|
wr::BuiltDisplayList dl;
|
|
wr::LayoutSize builderContentSize;
|
|
builder.Finalize(builderContentSize, dl);
|
|
mApi->SetRootDisplayList(gfx::Color(0.f, 0.f, 0.f, 0.f), epoch, LayerSize(pipeline->mScBounds.width, pipeline->mScBounds.height),
|
|
pipelineId, builderContentSize,
|
|
dl.dl_desc, dl.dl.inner.data, dl.dl.inner.length);
|
|
}
|
|
DeleteOldAsyncImages();
|
|
mKeysToDelete.SwapElements(keysToDelete);
|
|
}
|
|
|
|
void
|
|
AsyncImagePipelineManager::HoldExternalImage(const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch, WebRenderTextureHost* aTexture)
|
|
{
|
|
if (mDestroyed) {
|
|
return;
|
|
}
|
|
MOZ_ASSERT(aTexture);
|
|
|
|
PipelineTexturesHolder* holder = mPipelineTexturesHolders.Get(wr::AsUint64(aPipelineId));
|
|
MOZ_ASSERT(holder);
|
|
if (!holder) {
|
|
return;
|
|
}
|
|
// Hold WebRenderTextureHost until end of its usage on RenderThread
|
|
holder->mTextureHosts.push(ForwardingTextureHost(aEpoch, aTexture));
|
|
}
|
|
|
|
void
|
|
AsyncImagePipelineManager::Update(const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch)
|
|
{
|
|
if (mDestroyed) {
|
|
return;
|
|
}
|
|
if (auto entry = mPipelineTexturesHolders.Lookup(wr::AsUint64(aPipelineId))) {
|
|
PipelineTexturesHolder* holder = entry.Data();
|
|
// Remove Pipeline
|
|
if (holder->mDestroyedEpoch.isSome() && holder->mDestroyedEpoch.ref() <= aEpoch) {
|
|
entry.Remove();
|
|
return;
|
|
}
|
|
|
|
// Release TextureHosts based on Epoch
|
|
while (!holder->mTextureHosts.empty()) {
|
|
if (aEpoch <= holder->mTextureHosts.front().mEpoch) {
|
|
break;
|
|
}
|
|
holder->mTextureHosts.pop();
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace layers
|
|
} // namespace mozilla
|