/* -*- 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 "GeckoProfiler.h" #include "LayersLogging.h" #include "mozilla/layers/CompositorBridgeChild.h" #include "mozilla/layers/StackingContextHelper.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" #include "nsDisplayList.h" namespace mozilla { using namespace gfx; namespace layers { WebRenderLayerManager::WebRenderLayerManager(nsIWidget* aWidget) : mWidget(aWidget) , mLatestTransactionId(0) , mNeedsComposite(false) , mIsFirstPaint(false) , mEndTransactionWithoutLayers(false) , mTarget(nullptr) , mPaintSequenceNumber(0) { 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() { DoDestroy(/* aIsSync */ false); } void WebRenderLayerManager::DoDestroy(bool aIsSync) { if (IsDestroyed()) { return; } mWidget->CleanupWebRenderWindowOverlay(WrBridge()); LayerManager::Destroy(); DiscardImages(); DiscardCompositorAnimations(); WrBridge()->Destroy(aIsSync); 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 WrBridge()->GetCompositorBridgeChild(); } int32_t WebRenderLayerManager::GetMaxTextureSize() const { return WrBridge()->GetMaxTextureSize(); } bool WebRenderLayerManager::BeginTransactionWithTarget(gfxContext* aTarget) { mTarget = aTarget; return BeginTransaction(); } bool WebRenderLayerManager::BeginTransaction() { // Increment the paint sequence number even if test logging isn't // enabled in this process; it may be enabled in the parent process, // and the parent process expects unique sequence numbers. ++mPaintSequenceNumber; if (gfxPrefs::APZTestLoggingEnabled()) { mApzTestData.StartNewPaint(mPaintSequenceNumber); } 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::CreateWebRenderCommandsFromDisplayList(nsDisplayList* aDisplayList, nsDisplayListBuilder* aDisplayListBuilder, StackingContextHelper& aSc, wr::DisplayListBuilder& aBuilder) { nsDisplayList savedItems; nsDisplayItem* item; while ((item = aDisplayList->RemoveBottom()) != nullptr) { nsDisplayItem::Type itemType = item->GetType(); // If the item is a event regions item, but is empty (has no regions in it) // then we should just throw it out if (itemType == nsDisplayItem::TYPE_LAYER_EVENT_REGIONS) { nsDisplayLayerEventRegions* eventRegions = static_cast(item); if (eventRegions->IsEmpty()) { item->~nsDisplayItem(); continue; } } // Peek ahead to the next item and try merging with it or swapping with it // if necessary. nsDisplayItem* aboveItem; while ((aboveItem = aDisplayList->GetBottom()) != nullptr) { if (aboveItem->TryMerge(item)) { aDisplayList->RemoveBottom(); item->~nsDisplayItem(); item = aboveItem; itemType = item->GetType(); } else { break; } } nsDisplayList* itemSameCoordinateSystemChildren = item->GetSameCoordinateSystemChildren(); if (item->ShouldFlattenAway(aDisplayListBuilder)) { aDisplayList->AppendToBottom(itemSameCoordinateSystemChildren); item->~nsDisplayItem(); continue; } savedItems.AppendToTop(item); if (!item->CreateWebRenderCommands(aBuilder, aSc, mParentCommands, this, aDisplayListBuilder)) { // TODO: fallback } } aDisplayList->AppendToTop(&savedItems); } void WebRenderLayerManager::EndTransactionWithoutLayer(nsDisplayList* aDisplayList, nsDisplayListBuilder* aDisplayListBuilder) { MOZ_ASSERT(aDisplayList && aDisplayListBuilder); mEndTransactionWithoutLayers = true; DiscardImages(); WrBridge()->RemoveExpiredFontKeys(); EndTransactionInternal(nullptr, nullptr, EndTransactionFlags::END_DEFAULT, aDisplayList, aDisplayListBuilder); } void WebRenderLayerManager::EndTransaction(DrawPaintedLayerCallback aCallback, void* aCallbackData, EndTransactionFlags aFlags) { mEndTransactionWithoutLayers = false; DiscardImages(); WrBridge()->RemoveExpiredFontKeys(); EndTransactionInternal(aCallback, aCallbackData, aFlags); } bool WebRenderLayerManager::EndTransactionInternal(DrawPaintedLayerCallback aCallback, void* aCallbackData, EndTransactionFlags aFlags, nsDisplayList* aDisplayList, nsDisplayListBuilder* aDisplayListBuilder) { AutoProfilerTracing tracing("Paint", "RenderLayers"); 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(); WrSize contentSize { (float)size.width, (float)size.height }; wr::DisplayListBuilder builder(WrBridge()->GetPipeline(), contentSize); if (mEndTransactionWithoutLayers) { // aDisplayList being null here means this is an empty transaction following a layers-free // transaction, so we reuse the previously built displaylist. if (aDisplayList && aDisplayListBuilder) { StackingContextHelper sc; mParentCommands.Clear(); CreateWebRenderCommandsFromDisplayList(aDisplayList, aDisplayListBuilder, sc, builder); builder.Finalize(contentSize, mBuiltDisplayList); } builder.PushBuiltDisplayList(mBuiltDisplayList); WrBridge()->AddWebRenderParentCommands(mParentCommands); } else { mRoot->StartPendingAnimations(mAnimationReadyTime); StackingContextHelper sc; WebRenderLayer::ToWebRenderLayer(mRoot)->RenderLayer(builder, sc); } mWidget->AddWindowOverlayWebRenderCommands(WrBridge(), 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(); WrBridge()->ProcessWebRenderParentCommands(); return false; } WebRenderScrollData scrollData; if (AsyncPanZoomEnabled()) { scrollData.SetFocusTarget(mFocusTarget); mFocusTarget = FocusTarget(); if (mIsFirstPaint) { scrollData.SetIsFirstPaint(); mIsFirstPaint = false; } scrollData.SetPaintSequenceNumber(mPaintSequenceNumber); if (mRoot) { PopulateScrollData(scrollData, mRoot.get()); } } bool sync = mTarget != nullptr; mLatestTransactionId = mTransactionIdAllocator->GetTransactionId(); { AutoProfilerTracing tracing("Paint", sync ? "ForwardDPTransactionSync":"ForwardDPTransaction"); 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::SetFocusTarget(const FocusTarget& aFocusTarget) { mFocusTarget = aFocusTarget; } bool WebRenderLayerManager::AsyncPanZoomEnabled() const { return mWidget->AsyncPanZoomEnabled(); } 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() { if (WrBridge()->IPCOpen()) { for (auto key : mImageKeys) { WrBridge()->SendDeleteImage(key); } } mImageKeys.clear(); } void WebRenderLayerManager::AddCompositorAnimationsIdForDiscard(uint64_t aId) { mDiscardedCompositorAnimationsIds.AppendElement(aId); } void WebRenderLayerManager::DiscardCompositorAnimations() { if (WrBridge()->IPCOpen() && !mDiscardedCompositorAnimationsIds.IsEmpty()) { WrBridge()-> SendDeleteCompositorAnimations(mDiscardedCompositorAnimationsIds); } 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* cBridge = GetCompositorBridgeChild(); if (!cBridge) { return; } MOZ_ASSERT(mWidget); if (mWidget->SynchronouslyRepaintOnResize() || gfxPrefs::LayersForceSynchronousResize()) { cBridge->SendFlushRendering(); } else { cBridge->SendFlushRenderingAsync(); } } void WebRenderLayerManager::WaitOnTransactionProcessed() { CompositorBridgeChild* bridge = GetCompositorBridgeChild(); if (bridge) { bridge->SendWaitOnTransactionProcessed(); } } void WebRenderLayerManager::SendInvalidRegion(const nsIntRegion& aRegion) { // XXX Webrender does not support invalid region yet. } void WebRenderLayerManager::ScheduleComposite() { WrBridge()->SendForceComposite(); } void WebRenderLayerManager::SetRoot(Layer* aLayer) { mRoot = aLayer; } already_AddRefed WebRenderLayerManager::CreatePaintedLayer() { if (gfxPrefs::WebRenderBlobImages()) { return MakeAndAddRef(this); } else { 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