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
566 lines
15 KiB
C++
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
|