/* -*- 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 "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(bridge); WrBridge()->SendCreate(size.ToUnknownSize()); WrBridge()->IdentifyTextureHost(textureFactoryIdentifier); WrBridge()->SetNamespace(id_namespace); *aTextureFactoryIdentifier = textureFactoryIdentifier; } void WebRenderLayerManager::Destroy() { if (IsDestroyed()) { return; } LayerManager::Destroy(); DiscardImages(); 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 allocator = mTransactionIdAllocator; uint64_t id = mLatestTransactionId; RefPtr 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 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 = texture->BorrowDrawTarget(); if (!drawTarget || !drawTarget->IsValid()) { return; } RefPtr 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()->AddWebRenderParentCommand(OpRemoveCompositorAnimations(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 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 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 WebRenderLayerManager::CreatePaintedLayer() { return MakeAndAddRef(this); } already_AddRefed WebRenderLayerManager::CreateContainerLayer() { return MakeAndAddRef(this); } already_AddRefed WebRenderLayerManager::CreateImageLayer() { return MakeAndAddRef(this); } already_AddRefed WebRenderLayerManager::CreateCanvasLayer() { return MakeAndAddRef(this); } already_AddRefed WebRenderLayerManager::CreateReadbackLayer() { return nullptr; } already_AddRefed WebRenderLayerManager::CreateColorLayer() { return MakeAndAddRef(this); } already_AddRefed WebRenderLayerManager::CreateRefLayer() { return MakeAndAddRef(this); } already_AddRefed WebRenderLayerManager::CreateTextLayer() { return MakeAndAddRef(this); } already_AddRefed WebRenderLayerManager::CreateBorderLayer() { return nullptr; } already_AddRefed WebRenderLayerManager::CreateDisplayItemLayer() { return MakeAndAddRef(this); } } // namespace layers } // namespace mozilla