/* -*- 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 "ClientLayerManager.h" #include "nsIWidget.h" #include "mozilla/dom/TabChild.h" #include "mozilla/Hal.h" #include "mozilla/layers/PLayerChild.h" #include "mozilla/layers/PLayerTransactionChild.h" #include "mozilla/layers/PLayerTransactionParent.h" #include "CompositorChild.h" #include "ipc/AutoOpenSurface.h" #include "ipc/ShadowLayerChild.h" #include "mozilla/layers/CompositableClient.h" #include "mozilla/layers/ContentClient.h" #ifdef MOZ_WIDGET_ANDROID #include "AndroidBridge.h" #endif using namespace mozilla::dom; using namespace mozilla::gfx; namespace mozilla { namespace layers { ClientLayerManager::ClientLayerManager(nsIWidget* aWidget) : mPhase(PHASE_NONE) , mWidget(aWidget) , mTargetRotation(ROTATION_0) , mRepeatTransaction(false) , mIsRepeatTransaction(false) , mTransactionIncomplete(false) , mCompositorMightResample(false) { MOZ_COUNT_CTOR(ClientLayerManager); } ClientLayerManager::~ClientLayerManager() { mRoot = nullptr; MOZ_COUNT_DTOR(ClientLayerManager); } int32_t ClientLayerManager::GetMaxTextureSize() const { return ShadowLayerForwarder::GetMaxTextureSize(); } void ClientLayerManager::SetDefaultTargetConfiguration(BufferMode aDoubleBuffering, ScreenRotation aRotation) { mTargetRotation = aRotation; if (mWidget) { mTargetBounds = mWidget->GetNaturalBounds(); } } void ClientLayerManager::SetRoot(Layer* aLayer) { if (mRoot != aLayer) { // Have to hold the old root and its children in order to // maintain the same view of the layer tree in this process as // the parent sees. Otherwise layers can be destroyed // mid-transaction and bad things can happen (v. bug 612573) if (mRoot) { Hold(mRoot); } ShadowLayerForwarder::SetRoot(Hold(aLayer)); NS_ASSERTION(aLayer, "Root can't be null"); NS_ASSERTION(aLayer->Manager() == this, "Wrong manager"); NS_ASSERTION(InConstruction(), "Only allowed in construction phase"); mRoot = aLayer; } } void ClientLayerManager::Mutated(Layer* aLayer) { LayerManager::Mutated(aLayer); NS_ASSERTION(InConstruction() || InDrawing(), "wrong phase"); ShadowLayerForwarder::Mutated(Hold(aLayer)); } void ClientLayerManager::BeginTransactionWithTarget(gfxContext* aTarget) { mInTransaction = true; #ifdef MOZ_LAYERS_HAVE_LOG MOZ_LAYERS_LOG(("[----- BeginTransaction")); Log(); #endif NS_ASSERTION(!InTransaction(), "Nested transactions not allowed"); mPhase = PHASE_CONSTRUCTION; NS_ABORT_IF_FALSE(mKeepAlive.IsEmpty(), "uncommitted txn?"); nsRefPtr targetContext = aTarget; // If the last transaction was incomplete (a failed DoEmptyTransaction), // don't signal a new transaction to ShadowLayerForwarder. Carry on adding // to the previous transaction. ScreenOrientation orientation; nsIntRect clientBounds; if (TabChild* window = mWidget->GetOwningTabChild()) { orientation = window->GetOrientation(); } else { hal::ScreenConfiguration currentConfig; hal::GetCurrentScreenConfiguration(¤tConfig); orientation = currentConfig.orientation(); } mWidget->GetClientBounds(clientBounds); ShadowLayerForwarder::BeginTransaction(mTargetBounds, mTargetRotation, clientBounds, orientation); // If we're drawing on behalf of a context with async pan/zoom // enabled, then the entire buffer of thebes layers might be // composited (including resampling) asynchronously before we get // a chance to repaint, so we have to ensure that it's all valid // and not rotated. if (mWidget) { if (TabChild* window = mWidget->GetOwningTabChild()) { mCompositorMightResample = window->IsAsyncPanZoomEnabled(); } } // If we have a non-default target, we need to let our shadow manager draw // to it. This will happen at the end of the transaction. if (aTarget && XRE_GetProcessType() == GeckoProcessType_Default) { mShadowTarget = aTarget; } } void ClientLayerManager::BeginTransaction() { mInTransaction = true; BeginTransactionWithTarget(nullptr); } bool ClientLayerManager::EndTransactionInternal(DrawThebesLayerCallback aCallback, void* aCallbackData, EndTransactionFlags) { PROFILER_LABEL("ClientLayerManager", "EndTransactionInternal"); #ifdef MOZ_LAYERS_HAVE_LOG MOZ_LAYERS_LOG((" ----- (beginning paint)")); Log(); #endif NS_ASSERTION(InConstruction(), "Should be in construction phase"); mPhase = PHASE_DRAWING; ClientLayer* root = ClientLayer::ToClientLayer(GetRoot()); mTransactionIncomplete = false; // Apply pending tree updates before recomputing effective // properties. GetRoot()->ApplyPendingUpdatesToSubtree(); mThebesLayerCallback = aCallback; mThebesLayerCallbackData = aCallbackData; GetRoot()->ComputeEffectiveTransforms(gfx3DMatrix()); root->RenderLayer(); mThebesLayerCallback = nullptr; mThebesLayerCallbackData = nullptr; // Go back to the construction phase if the transaction isn't complete. // Layout will update the layer tree and call EndTransaction(). mPhase = mTransactionIncomplete ? PHASE_CONSTRUCTION : PHASE_NONE; NS_ASSERTION(!aCallback || !mTransactionIncomplete, "If callback is not null, transaction must be complete"); return !mTransactionIncomplete; } void ClientLayerManager::EndTransaction(DrawThebesLayerCallback aCallback, void* aCallbackData, EndTransactionFlags aFlags) { if (mWidget) { mWidget->PrepareWindowEffects(); } EndTransactionInternal(aCallback, aCallbackData, aFlags); ForwardTransaction(); if (mRepeatTransaction) { mRepeatTransaction = false; mIsRepeatTransaction = true; BeginTransaction(); ClientLayerManager::EndTransaction(aCallback, aCallbackData, aFlags); mIsRepeatTransaction = false; } else { MakeSnapshotIfRequired(); } } bool ClientLayerManager::EndEmptyTransaction(EndTransactionFlags aFlags) { mInTransaction = false; if (!mRoot) { return false; } if (!EndTransactionInternal(nullptr, nullptr, aFlags)) { // Return without calling ForwardTransaction. This leaves the // ShadowLayerForwarder transaction open; the following // EndTransaction will complete it. return false; } ForwardTransaction(); MakeSnapshotIfRequired(); return true; } void ClientLayerManager::MakeSnapshotIfRequired() { if (!mShadowTarget) { return; } if (mWidget) { if (CompositorChild* remoteRenderer = mWidget->GetRemoteRenderer()) { nsIntRect bounds; mWidget->GetBounds(bounds); SurfaceDescriptor inSnapshot, snapshot; if (AllocSurfaceDescriptor(bounds.Size(), gfxASurface::CONTENT_COLOR_ALPHA, &inSnapshot) && // The compositor will usually reuse |snapshot| and return // it through |outSnapshot|, but if it doesn't, it's // responsible for freeing |snapshot|. remoteRenderer->SendMakeSnapshot(inSnapshot, &snapshot)) { AutoOpenSurface opener(OPEN_READ_ONLY, snapshot); gfxASurface* source = opener.Get(); mShadowTarget->DrawSurface(source, source->GetSize()); } if (IsSurfaceDescriptorValid(snapshot)) { ShadowLayerForwarder::DestroySharedSurface(&snapshot); } } } mShadowTarget = nullptr; } void ClientLayerManager::ForwardTransaction() { mPhase = PHASE_FORWARD; // forward this transaction's changeset to our LayerManagerComposite AutoInfallibleTArray replies; if (HasShadowManager() && ShadowLayerForwarder::EndTransaction(&replies)) { for (nsTArray::size_type i = 0; i < replies.Length(); ++i) { const EditReply& reply = replies[i]; switch (reply.type()) { case EditReply::TOpContentBufferSwap: { MOZ_LAYERS_LOG(("[LayersForwarder] DoubleBufferSwap")); const OpContentBufferSwap& obs = reply.get_OpContentBufferSwap(); CompositableChild* compositableChild = static_cast(obs.compositableChild()); ContentClientRemote* contentClient = static_cast(compositableChild->GetCompositableClient()); MOZ_ASSERT(contentClient); contentClient->SwapBuffers(obs.frontUpdatedRegion()); break; } case EditReply::TOpTextureSwap: { MOZ_LAYERS_LOG(("[LayersForwarder] TextureSwap")); const OpTextureSwap& ots = reply.get_OpTextureSwap(); CompositableChild* compositableChild = static_cast(ots.compositableChild()); MOZ_ASSERT(compositableChild); compositableChild->GetCompositableClient() ->SetDescriptorFromReply(ots.textureId(), ots.image()); break; } default: NS_RUNTIMEABORT("not reached"); } } } else if (HasShadowManager()) { NS_WARNING("failed to forward Layers transaction"); } mPhase = PHASE_NONE; // this may result in Layers being deleted, which results in // PLayer::Send__delete__() and DeallocShmem() mKeepAlive.Clear(); } ShadowableLayer* ClientLayerManager::Hold(Layer* aLayer) { NS_ABORT_IF_FALSE(HasShadowManager(), "top-level tree, no shadow tree to remote to"); ShadowableLayer* shadowable = ClientLayer::ToClientLayer(aLayer); NS_ABORT_IF_FALSE(shadowable, "trying to remote an unshadowable layer"); mKeepAlive.AppendElement(aLayer); return shadowable; } bool ClientLayerManager::IsCompositingCheap() { // Whether compositing is cheap depends on the parent backend. return mShadowManager && LayerManager::IsCompositingCheap(GetCompositorBackendType()); } void ClientLayerManager::SetIsFirstPaint() { ShadowLayerForwarder::SetIsFirstPaint(); } void ClientLayerManager::ClearCachedResources(Layer* aSubtree) { MOZ_ASSERT(!HasShadowManager() || !aSubtree); if (PLayerTransactionChild* manager = GetShadowManager()) { manager->SendClearCachedResources(); } if (aSubtree) { ClearLayer(aSubtree); } else if (mRoot) { ClearLayer(mRoot); } } void ClientLayerManager::ClearLayer(Layer* aLayer) { ClientLayer::ToClientLayer(aLayer)->ClearCachedResources(); for (Layer* child = aLayer->GetFirstChild(); child; child = child->GetNextSibling()) { ClearLayer(child); } } void ClientLayerManager::GetBackendName(nsAString& aName) { switch (Compositor::GetBackend()) { case LAYERS_BASIC: aName.AssignLiteral("Basic"); return; case LAYERS_OPENGL: aName.AssignLiteral("OpenGL"); return; case LAYERS_D3D9: aName.AssignLiteral("Direct3D 9"); return; case LAYERS_D3D10: aName.AssignLiteral("Direct3D 10"); return; default: NS_RUNTIMEABORT("Invalid backend"); } } bool ClientLayerManager::ProgressiveUpdateCallback(bool aHasPendingNewThebesContent, gfx::Rect& aViewport, float& aScaleX, float& aScaleY, bool aDrawingCritical) { #ifdef MOZ_WIDGET_ANDROID Layer* primaryScrollable = GetPrimaryScrollableLayer(); if (primaryScrollable) { const FrameMetrics& metrics = primaryScrollable->AsContainerLayer()->GetFrameMetrics(); // This is derived from the code in // gfx/layers/ipc/CompositorParent.cpp::TransformShadowTree. const gfx3DMatrix& rootTransform = GetRoot()->GetTransform(); float devPixelRatioX = 1 / rootTransform.GetXScale(); float devPixelRatioY = 1 / rootTransform.GetYScale(); const gfx::Rect& metricsDisplayPort = (aDrawingCritical && !metrics.mCriticalDisplayPort.IsEmpty()) ? metrics.mCriticalDisplayPort : metrics.mDisplayPort; gfx::Rect displayPort((metricsDisplayPort.x + metrics.mScrollOffset.x) * devPixelRatioX, (metricsDisplayPort.y + metrics.mScrollOffset.y) * devPixelRatioY, metricsDisplayPort.width * devPixelRatioX, metricsDisplayPort.height * devPixelRatioY); return AndroidBridge::Bridge()->ProgressiveUpdateCallback( aHasPendingNewThebesContent, displayPort, devPixelRatioX, aDrawingCritical, aViewport, aScaleX, aScaleY); } #endif return false; } ClientLayer::~ClientLayer() { if (HasShadow()) { PLayerChild::Send__delete__(GetShadow()); } MOZ_COUNT_DTOR(ClientLayer); } } }