Files
tubestation/gfx/layers/wr/WebRenderLayerManager.cpp
peter chang da7e930e78 Bug 1357320 - Deal with the lifetime of compositor animations, r?kats
First, hook the Layer's ClearAnimation API to delete unnecessary
animations in next layer transaction. Second, add another async
DeleteCompositorAnimations API to delete animations on the compositor,
especially calling this API before WebRenderLayerManager got destroyed.

MozReview-Commit-ID: 4mbj5IgsXYa
2017-05-02 10:26:13 +08:00

566 lines
15 KiB
C++

/* -*- Mode: C++; tab-width: 2; 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 "WebRenderLayerManager.h"
#include "gfxPrefs.h"
#include "LayersLogging.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/gfx/GPUProcessManager.h"
#include "mozilla/layers/CompositorBridgeChild.h"
#include "mozilla/layers/TextureClient.h"
#include "mozilla/layers/WebRenderBridgeChild.h"
#include "WebRenderCanvasLayer.h"
#include "WebRenderColorLayer.h"
#include "WebRenderContainerLayer.h"
#include "WebRenderImageLayer.h"
#include "WebRenderPaintedLayer.h"
#include "WebRenderPaintedLayerBlob.h"
#include "WebRenderTextLayer.h"
#include "WebRenderDisplayItemLayer.h"
namespace mozilla {
using namespace gfx;
namespace layers {
WebRenderLayerManager::WebRenderLayerManager(nsIWidget* aWidget)
: mWidget(aWidget)
, mLatestTransactionId(0)
, mNeedsComposite(false)
, mIsFirstPaint(false)
, mTarget(nullptr)
{
MOZ_COUNT_CTOR(WebRenderLayerManager);
}
KnowsCompositor*
WebRenderLayerManager::AsKnowsCompositor()
{
return mWrChild;
}
void
WebRenderLayerManager::Initialize(PCompositorBridgeChild* aCBChild,
wr::PipelineId aLayersId,
TextureFactoryIdentifier* aTextureFactoryIdentifier)
{
MOZ_ASSERT(mWrChild == nullptr);
MOZ_ASSERT(aTextureFactoryIdentifier);
LayoutDeviceIntSize size = mWidget->GetClientSize();
TextureFactoryIdentifier textureFactoryIdentifier;
uint32_t id_namespace;
PWebRenderBridgeChild* bridge = aCBChild->SendPWebRenderBridgeConstructor(aLayersId,
size,
&textureFactoryIdentifier,
&id_namespace);
MOZ_ASSERT(bridge);
mWrChild = static_cast<WebRenderBridgeChild*>(bridge);
WrBridge()->SendCreate(size.ToUnknownSize());
WrBridge()->IdentifyTextureHost(textureFactoryIdentifier);
WrBridge()->SetNamespace(id_namespace);
*aTextureFactoryIdentifier = textureFactoryIdentifier;
}
void
WebRenderLayerManager::Destroy()
{
if (IsDestroyed()) {
return;
}
LayerManager::Destroy();
DiscardImages();
DiscardCompositorAnimations();
WrBridge()->Destroy();
if (mTransactionIdAllocator) {
// Make sure to notify the refresh driver just in case it's waiting on a
// pending transaction. Do this at the top of the event loop so we don't
// cause a paint to occur during compositor shutdown.
RefPtr<TransactionIdAllocator> allocator = mTransactionIdAllocator;
uint64_t id = mLatestTransactionId;
RefPtr<Runnable> task = NS_NewRunnableFunction(
"TransactionIdAllocator::NotifyTransactionCompleted",
[allocator, id] () -> void {
allocator->NotifyTransactionCompleted(id);
});
NS_DispatchToMainThread(task.forget());
}
// Forget the widget pointer in case we outlive our owning widget.
mWidget = nullptr;
}
WebRenderLayerManager::~WebRenderLayerManager()
{
Destroy();
MOZ_COUNT_DTOR(WebRenderLayerManager);
}
CompositorBridgeChild*
WebRenderLayerManager::GetCompositorBridgeChild()
{
return mWidget ? mWidget->GetRemoteRenderer() : nullptr;
}
int32_t
WebRenderLayerManager::GetMaxTextureSize() const
{
return WrBridge()->GetMaxTextureSize();
}
bool
WebRenderLayerManager::BeginTransactionWithTarget(gfxContext* aTarget)
{
mTarget = aTarget;
return BeginTransaction();
}
bool
WebRenderLayerManager::BeginTransaction()
{
return true;
}
bool
WebRenderLayerManager::EndEmptyTransaction(EndTransactionFlags aFlags)
{
if (!mRoot) {
return false;
}
// We might used painted layer images so don't delete them yet.
return EndTransactionInternal(nullptr, nullptr, aFlags);
}
/*static*/ int32_t
PopulateScrollData(WebRenderScrollData& aTarget, Layer* aLayer)
{
MOZ_ASSERT(aLayer);
// We want to allocate a WebRenderLayerScrollData object for this layer,
// but don't keep a pointer to it since it might get memmove'd during the
// recursion below. Instead keep the index and get the pointer later.
size_t index = aTarget.AddNewLayerData();
int32_t descendants = 0;
for (Layer* child = aLayer->GetLastChild(); child; child = child->GetPrevSibling()) {
descendants += PopulateScrollData(aTarget, child);
}
aTarget.GetLayerDataMutable(index)->Initialize(aTarget, aLayer, descendants);
return descendants + 1;
}
void
WebRenderLayerManager::EndTransaction(DrawPaintedLayerCallback aCallback,
void* aCallbackData,
EndTransactionFlags aFlags)
{
DiscardImages();
WrBridge()->RemoveExpiredFontKeys();
EndTransactionInternal(aCallback, aCallbackData, aFlags);
}
bool
WebRenderLayerManager::EndTransactionInternal(DrawPaintedLayerCallback aCallback,
void* aCallbackData,
EndTransactionFlags aFlags)
{
mPaintedLayerCallback = aCallback;
mPaintedLayerCallbackData = aCallbackData;
mTransactionIncomplete = false;
if (gfxPrefs::LayersDump()) {
this->Dump();
}
// Since we don't do repeat transactions right now, just set the time
mAnimationReadyTime = TimeStamp::Now();
LayoutDeviceIntSize size = mWidget->GetClientSize();
if (!WrBridge()->DPBegin(size.ToUnknownSize())) {
return false;
}
DiscardCompositorAnimations();
mRoot->StartPendingAnimations(mAnimationReadyTime);
wr::DisplayListBuilder builder(WrBridge()->GetPipeline());
WebRenderLayer::ToWebRenderLayer(mRoot)->RenderLayer(builder);
WrBridge()->ClearReadLocks();
// We can't finish this transaction so return. This usually
// happens in an empty transaction where we can't repaint a painted layer.
// In this case, leave the transaction open and let a full transaction happen.
if (mTransactionIncomplete) {
DiscardLocalImages();
return false;
}
WebRenderScrollData scrollData;
if (mWidget->AsyncPanZoomEnabled()) {
if (mIsFirstPaint) {
scrollData.SetIsFirstPaint();
mIsFirstPaint = false;
}
if (mRoot) {
PopulateScrollData(scrollData, mRoot.get());
}
}
bool sync = mTarget != nullptr;
mLatestTransactionId = mTransactionIdAllocator->GetTransactionId();
WrBridge()->DPEnd(builder, size.ToUnknownSize(), sync, mLatestTransactionId, scrollData);
MakeSnapshotIfRequired(size);
mNeedsComposite = false;
ClearDisplayItemLayers();
// this may result in Layers being deleted, which results in
// PLayer::Send__delete__() and DeallocShmem()
mKeepAlive.Clear();
ClearMutatedLayers();
return true;
}
void
WebRenderLayerManager::MakeSnapshotIfRequired(LayoutDeviceIntSize aSize)
{
if (!mTarget || aSize.IsEmpty()) {
return;
}
// XXX Add other TextureData supports.
// Only BufferTexture is supported now.
// TODO: fixup for proper surface format.
RefPtr<TextureClient> texture =
TextureClient::CreateForRawBufferAccess(WrBridge(),
SurfaceFormat::B8G8R8A8,
aSize.ToUnknownSize(),
BackendType::SKIA,
TextureFlags::SNAPSHOT);
if (!texture) {
return;
}
texture->InitIPDLActor(WrBridge());
if (!texture->GetIPDLActor()) {
return;
}
IntRect bounds = ToOutsideIntRect(mTarget->GetClipExtents());
if (!WrBridge()->SendDPGetSnapshot(texture->GetIPDLActor())) {
return;
}
TextureClientAutoLock autoLock(texture, OpenMode::OPEN_READ_ONLY);
if (!autoLock.Succeeded()) {
return;
}
RefPtr<DrawTarget> drawTarget = texture->BorrowDrawTarget();
if (!drawTarget || !drawTarget->IsValid()) {
return;
}
RefPtr<SourceSurface> snapshot = drawTarget->Snapshot();
/*
static int count = 0;
char filename[100];
snprintf(filename, 100, "output%d.png", count++);
printf_stderr("Writing to :%s\n", filename);
gfxUtils::WriteAsPNG(snapshot, filename);
*/
Rect dst(bounds.x, bounds.y, bounds.width, bounds.height);
Rect src(0, 0, bounds.width, bounds.height);
// The data we get from webrender is upside down. So flip and translate up so the image is rightside up.
// Webrender always does a full screen readback.
SurfacePattern pattern(snapshot, ExtendMode::CLAMP,
Matrix::Scaling(1.0, -1.0).PostTranslate(0.0, aSize.height));
DrawTarget* dt = mTarget->GetDrawTarget();
MOZ_RELEASE_ASSERT(dt);
dt->FillRect(dst, pattern);
mTarget = nullptr;
}
void
WebRenderLayerManager::AddImageKeyForDiscard(wr::ImageKey key)
{
mImageKeys.push_back(key);
}
void
WebRenderLayerManager::DiscardImages()
{
for (auto key : mImageKeys) {
WrBridge()->SendDeleteImage(key);
}
mImageKeys.clear();
}
void
WebRenderLayerManager::AddCompositorAnimationsIdForDiscard(uint64_t aId)
{
mDiscardedCompositorAnimationsIds.push_back(aId);
}
void
WebRenderLayerManager::DiscardCompositorAnimations()
{
for (auto id : mDiscardedCompositorAnimationsIds) {
WrBridge()->SendDeleteCompositorAnimations(id);
}
mDiscardedCompositorAnimationsIds.clear();
}
void
WebRenderLayerManager::DiscardLocalImages()
{
// Removes images but doesn't tell the parent side about them
// This is useful in empty / failed transactions where we created
// image keys but didn't tell the parent about them yet.
mImageKeys.clear();
}
void
WebRenderLayerManager::Mutated(Layer* aLayer)
{
LayerManager::Mutated(aLayer);
AddMutatedLayer(aLayer);
}
void
WebRenderLayerManager::MutatedSimple(Layer* aLayer)
{
LayerManager::Mutated(aLayer);
AddMutatedLayer(aLayer);
}
void
WebRenderLayerManager::AddMutatedLayer(Layer* aLayer)
{
mMutatedLayers.AppendElement(aLayer);
}
void
WebRenderLayerManager::ClearMutatedLayers()
{
mMutatedLayers.Clear();
}
bool
WebRenderLayerManager::IsMutatedLayer(Layer* aLayer)
{
return mMutatedLayers.Contains(aLayer);
}
void
WebRenderLayerManager::Hold(Layer* aLayer)
{
mKeepAlive.AppendElement(aLayer);
}
void
WebRenderLayerManager::SetLayerObserverEpoch(uint64_t aLayerObserverEpoch)
{
WrBridge()->SendSetLayerObserverEpoch(aLayerObserverEpoch);
}
void
WebRenderLayerManager::DidComposite(uint64_t aTransactionId,
const mozilla::TimeStamp& aCompositeStart,
const mozilla::TimeStamp& aCompositeEnd)
{
MOZ_ASSERT(mWidget);
// |aTransactionId| will be > 0 if the compositor is acknowledging a shadow
// layers transaction.
if (aTransactionId) {
nsIWidgetListener *listener = mWidget->GetWidgetListener();
if (listener) {
listener->DidCompositeWindow(aTransactionId, aCompositeStart, aCompositeEnd);
}
listener = mWidget->GetAttachedWidgetListener();
if (listener) {
listener->DidCompositeWindow(aTransactionId, aCompositeStart, aCompositeEnd);
}
mTransactionIdAllocator->NotifyTransactionCompleted(aTransactionId);
}
// These observers fire whether or not we were in a transaction.
for (size_t i = 0; i < mDidCompositeObservers.Length(); i++) {
mDidCompositeObservers[i]->DidComposite();
}
}
void
WebRenderLayerManager::ClearLayer(Layer* aLayer)
{
aLayer->ClearCachedResources();
for (Layer* child = aLayer->GetFirstChild(); child;
child = child->GetNextSibling()) {
ClearLayer(child);
}
}
void
WebRenderLayerManager::ClearCachedResources(Layer* aSubtree)
{
WrBridge()->SendClearCachedResources();
if (aSubtree) {
ClearLayer(aSubtree);
} else if (mRoot) {
ClearLayer(mRoot);
}
}
void
WebRenderLayerManager::UpdateTextureFactoryIdentifier(const TextureFactoryIdentifier& aNewIdentifier,
uint64_t aDeviceResetSeqNo)
{
WrBridge()->IdentifyTextureHost(aNewIdentifier);
}
TextureFactoryIdentifier
WebRenderLayerManager::GetTextureFactoryIdentifier()
{
return WrBridge()->GetTextureFactoryIdentifier();
}
void
WebRenderLayerManager::AddDidCompositeObserver(DidCompositeObserver* aObserver)
{
if (!mDidCompositeObservers.Contains(aObserver)) {
mDidCompositeObservers.AppendElement(aObserver);
}
}
void
WebRenderLayerManager::RemoveDidCompositeObserver(DidCompositeObserver* aObserver)
{
mDidCompositeObservers.RemoveElement(aObserver);
}
void
WebRenderLayerManager::FlushRendering()
{
CompositorBridgeChild* bridge = GetCompositorBridgeChild();
if (bridge) {
bridge->SendFlushRendering();
}
}
void
WebRenderLayerManager::SendInvalidRegion(const nsIntRegion& aRegion)
{
// XXX Webrender does not support invalid region yet.
}
void
WebRenderLayerManager::Composite()
{
WrBridge()->SendForceComposite();
}
RefPtr<PipelineIdPromise>
WebRenderLayerManager::AllocPipelineId()
{
if (XRE_IsParentProcess()) {
GPUProcessManager* pm = GPUProcessManager::Get();
if (!pm) {
return PipelineIdPromise::CreateAndReject(ipc::PromiseRejectReason::HandlerRejected, __func__);
}
return PipelineIdPromise::CreateAndResolve(wr::AsPipelineId(pm->AllocateLayerTreeId()), __func__);;
}
MOZ_ASSERT(XRE_IsContentProcess());
RefPtr<dom::ContentChild> contentChild = dom::ContentChild::GetSingleton();
if (!contentChild) {
return PipelineIdPromise::CreateAndReject(ipc::PromiseRejectReason::HandlerRejected, __func__);
}
return contentChild->SendAllocPipelineId();
}
void
WebRenderLayerManager::SetRoot(Layer* aLayer)
{
mRoot = aLayer;
}
already_AddRefed<PaintedLayer>
WebRenderLayerManager::CreatePaintedLayer()
{
if (gfxPrefs::WebRenderBlobImages()) {
return MakeAndAddRef<WebRenderPaintedLayerBlob>(this);
} else {
return MakeAndAddRef<WebRenderPaintedLayer>(this);
}
}
already_AddRefed<ContainerLayer>
WebRenderLayerManager::CreateContainerLayer()
{
return MakeAndAddRef<WebRenderContainerLayer>(this);
}
already_AddRefed<ImageLayer>
WebRenderLayerManager::CreateImageLayer()
{
return MakeAndAddRef<WebRenderImageLayer>(this);
}
already_AddRefed<CanvasLayer>
WebRenderLayerManager::CreateCanvasLayer()
{
return MakeAndAddRef<WebRenderCanvasLayer>(this);
}
already_AddRefed<ReadbackLayer>
WebRenderLayerManager::CreateReadbackLayer()
{
return nullptr;
}
already_AddRefed<ColorLayer>
WebRenderLayerManager::CreateColorLayer()
{
return MakeAndAddRef<WebRenderColorLayer>(this);
}
already_AddRefed<RefLayer>
WebRenderLayerManager::CreateRefLayer()
{
return MakeAndAddRef<WebRenderRefLayer>(this);
}
already_AddRefed<TextLayer>
WebRenderLayerManager::CreateTextLayer()
{
return MakeAndAddRef<WebRenderTextLayer>(this);
}
already_AddRefed<BorderLayer>
WebRenderLayerManager::CreateBorderLayer()
{
return nullptr;
}
already_AddRefed<DisplayItemLayer>
WebRenderLayerManager::CreateDisplayItemLayer()
{
return MakeAndAddRef<WebRenderDisplayItemLayer>(this);
}
} // namespace layers
} // namespace mozilla