Files
tubestation/gfx/layers/wr/WebRenderCompositableHolder.cpp
sotaro 55343dcb33 Bug 1335335 - Tab move between different windows does not work when e10s is enabled r=nical
When the tab move happens, related non-root WebRenderBridgeParent is updated as to render to different webrender instance. webrender does not support of sharing resources and keys yet. Then when the tab move happens, the patch just removes all keys and resources that belongs to previous webrender. Ideally all resources that belong to WebRenderBridgeParent should be reallocated in an update webrender. But the patch does not do it, instead it just request WebRenderBridgeChild to re-allocate all resources again for simplicity. Performance improvement will happen in a future patch.

This patch support only tab move that uses only raw data external images. Support of native texture external images will happen in a future patch.
2017-06-29 18:06:11 -07:00

359 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 "WebRenderCompositableHolder.h"
#include "CompositableHost.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 {
WebRenderCompositableHolder::AsyncImagePipelineHolder::AsyncImagePipelineHolder()
: mInitialised(false)
, mIsChanged(false)
, mUseExternalImage(false)
, mFilter(WrImageRendering::Auto)
, mMixBlendMode(WrMixBlendMode::Normal)
{}
WebRenderCompositableHolder::WebRenderCompositableHolder(uint32_t aIdNamespace)
: mIdNamespace(aIdNamespace)
, mResourceId(0)
, mAsyncImageEpoch(0)
, mDestroyed(false)
{
MOZ_COUNT_CTOR(WebRenderCompositableHolder);
}
WebRenderCompositableHolder::~WebRenderCompositableHolder()
{
MOZ_COUNT_DTOR(WebRenderCompositableHolder);
}
void
WebRenderCompositableHolder::Destroy(wr::WebRenderAPI* aApi)
{
DeleteOldAsyncImages(aApi);
mDestroyed = true;
}
bool
WebRenderCompositableHolder::HasKeysToDelete()
{
return !mKeysToDelete.IsEmpty();
}
void
WebRenderCompositableHolder::DeleteOldAsyncImages(wr::WebRenderAPI* aApi)
{
for (wr::ImageKey key : mKeysToDelete) {
aApi->DeleteImage(key);
}
mKeysToDelete.Clear();
}
void
WebRenderCompositableHolder::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
WebRenderCompositableHolder::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
WebRenderCompositableHolder::AddAsyncImagePipeline(const wr::PipelineId& aPipelineId, WebRenderImageHost* aImageHost)
{
if (mDestroyed) {
return;
}
MOZ_ASSERT(aImageHost);
uint64_t id = wr::AsUint64(aPipelineId);
MOZ_ASSERT(!mAsyncImagePipelineHolders.Get(id));
AsyncImagePipelineHolder* holder = new AsyncImagePipelineHolder();
holder->mImageHost = aImageHost;
mAsyncImagePipelineHolders.Put(id, holder);
AddPipeline(aPipelineId);
}
void
WebRenderCompositableHolder::RemoveAsyncImagePipeline(wr::WebRenderAPI* aApi, const wr::PipelineId& aPipelineId)
{
if (mDestroyed) {
return;
}
uint64_t id = wr::AsUint64(aPipelineId);
if (auto entry = mAsyncImagePipelineHolders.Lookup(id)) {
AsyncImagePipelineHolder* holder = entry.Data();
++mAsyncImageEpoch; // Update webrender epoch
aApi->ClearRootDisplayList(wr::NewEpoch(mAsyncImageEpoch), aPipelineId);
for (wr::ImageKey key : holder->mKeys) {
aApi->DeleteImage(key);
}
entry.Remove();
RemovePipeline(aPipelineId, wr::NewEpoch(mAsyncImageEpoch));
}
}
void
WebRenderCompositableHolder::UpdateAsyncImagePipeline(const wr::PipelineId& aPipelineId,
const LayerRect& aScBounds,
const gfx::Matrix4x4& aScTransform,
const gfx::MaybeIntSize& aScaleToSize,
const WrImageRendering& aFilter,
const WrMixBlendMode& aMixBlendMode)
{
if (mDestroyed) {
return;
}
AsyncImagePipelineHolder* holder = mAsyncImagePipelineHolders.Get(wr::AsUint64(aPipelineId));
if (!holder) {
return;
}
holder->mInitialised = true;
holder->mIsChanged = true;
holder->mScBounds = aScBounds;
holder->mScTransform = aScTransform;
holder->mScaleToSize = aScaleToSize;
holder->mFilter = aFilter;
holder->mMixBlendMode = aMixBlendMode;
}
bool
WebRenderCompositableHolder::GetImageKeyForTextureHost(wr::WebRenderAPI* aApi, TextureHost* aTexture, nsTArray<wr::ImageKey>& aKeys)
{
MOZ_ASSERT(aKeys.IsEmpty());
MOZ_ASSERT(aTexture);
WebRenderTextureHost* wrTexture = aTexture->AsWebRenderTextureHost();
if (wrTexture) {
wrTexture->GetWRImageKeys(aKeys, std::bind(&WebRenderCompositableHolder::GetImageKey, this));
MOZ_ASSERT(!aKeys.IsEmpty());
Range<const wr::ImageKey> keys(&aKeys[0], aKeys.Length());
wrTexture->AddWRImage(aApi, 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 = GetImageKey();
aKeys.AppendElement(key);
aApi->AddImage(key, descriptor, slice);
dSurf->Unmap();
}
return false;
}
bool
WebRenderCompositableHolder::UpdateImageKeys(wr::WebRenderAPI* aApi,
bool& aUseExternalImage,
AsyncImagePipelineHolder* aHolder,
nsTArray<wr::ImageKey>& aKeys,
nsTArray<wr::ImageKey>& aKeysToDelete)
{
MOZ_ASSERT(aKeys.IsEmpty());
MOZ_ASSERT(aHolder);
TextureHost* texture = aHolder->mImageHost->GetAsTextureHostForComposite();
if (!aHolder->mInitialised) {
return false;
}
// No change
if (!aHolder->mIsChanged && texture == aHolder->mCurrentTexture) {
// No need to update DisplayList.
return false;
}
aHolder->mIsChanged = false;
if (texture == aHolder->mCurrentTexture) {
// Reuse previous ImageKeys.
aKeys.AppendElements(aHolder->mKeys);
aUseExternalImage = aHolder->mUseExternalImage;
return true;
}
// Delete old ImageKeys
aKeysToDelete.AppendElements(aHolder->mKeys);
aHolder->mKeys.Clear();
aHolder->mCurrentTexture = nullptr;
// No txture to render
if (!texture) {
return true;
}
aUseExternalImage = aHolder->mUseExternalImage = GetImageKeyForTextureHost(aApi, texture, aKeys);
MOZ_ASSERT(!aKeys.IsEmpty());
aHolder->mKeys.AppendElements(aKeys);
aHolder->mCurrentTexture = texture;
return true;
}
void
WebRenderCompositableHolder::ApplyAsyncImages(wr::WebRenderAPI* aApi)
{
if (mDestroyed || mAsyncImagePipelineHolders.Count() == 0) {
return;
}
++mAsyncImageEpoch; // Update webrender epoch
wr::Epoch epoch = wr::NewEpoch(mAsyncImageEpoch);
nsTArray<wr::ImageKey> keysToDelete;
for (auto iter = mAsyncImagePipelineHolders.Iter(); !iter.Done(); iter.Next()) {
wr::PipelineId pipelineId = wr::AsPipelineId(iter.Key());
AsyncImagePipelineHolder* holder = iter.Data();
nsTArray<wr::ImageKey> keys;
bool useExternalImage = false;
bool updateDisplayList = UpdateImageKeys(aApi,
useExternalImage,
holder,
keys,
keysToDelete);
if (!updateDisplayList) {
continue;
}
WrSize contentSize { holder->mScBounds.width, holder->mScBounds.height };
wr::DisplayListBuilder builder(pipelineId, contentSize);
if (!keys.IsEmpty()) {
MOZ_ASSERT(holder->mCurrentTexture.get());
float opacity = 1.0f;
builder.PushStackingContext(wr::ToWrRect(holder->mScBounds),
0,
&opacity,
holder->mScTransform.IsIdentity() ? nullptr : &holder->mScTransform,
WrTransformStyle::Flat,
holder->mMixBlendMode,
nsTArray<WrFilterOp>());
LayerRect rect(0, 0, holder->mCurrentTexture->GetSize().width, holder->mCurrentTexture->GetSize().height);
if (holder->mScaleToSize.isSome()) {
rect = LayerRect(0, 0, holder->mScaleToSize.value().width, holder->mScaleToSize.value().height);
}
if (useExternalImage) {
MOZ_ASSERT(holder->mCurrentTexture->AsWebRenderTextureHost());
Range<const wr::ImageKey> range_keys(&keys[0], keys.Length());
holder->mCurrentTexture->PushExternalImage(builder,
wr::ToWrRect(rect),
wr::ToWrRect(rect),
holder->mFilter,
range_keys);
HoldExternalImage(pipelineId, epoch, holder->mCurrentTexture->AsWebRenderTextureHost());
} else {
MOZ_ASSERT(keys.Length() == 1);
builder.PushImage(wr::ToWrRect(rect),
wr::ToWrRect(rect),
holder->mFilter,
keys[0]);
}
builder.PopStackingContext();
}
wr::BuiltDisplayList dl;
WrSize builderContentSize;
builder.Finalize(builderContentSize, dl);
aApi->SetRootDisplayList(gfx::Color(0.f, 0.f, 0.f, 0.f), epoch, LayerSize(holder->mScBounds.width, holder->mScBounds.height),
pipelineId, builderContentSize,
dl.dl_desc, dl.dl.inner.data, dl.dl.inner.length);
}
DeleteOldAsyncImages(aApi);
mKeysToDelete.SwapElements(keysToDelete);
}
void
WebRenderCompositableHolder::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
WebRenderCompositableHolder::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