michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "ClientLayerManager.h" michael@0: #include "CompositorChild.h" // for CompositorChild michael@0: #include "GeckoProfiler.h" // for PROFILER_LABEL michael@0: #include "gfxPrefs.h" // for gfxPrefs::LayersTileWidth/Height michael@0: #include "mozilla/Assertions.h" // for MOZ_ASSERT, etc michael@0: #include "mozilla/Hal.h" michael@0: #include "mozilla/dom/ScreenOrientation.h" // for ScreenOrientation michael@0: #include "mozilla/dom/TabChild.h" // for TabChild michael@0: #include "mozilla/hal_sandbox/PHal.h" // for ScreenConfiguration michael@0: #include "mozilla/layers/CompositableClient.h" michael@0: #include "mozilla/layers/ContentClient.h" michael@0: #include "mozilla/layers/ISurfaceAllocator.h" michael@0: #include "mozilla/layers/LayersMessages.h" // for EditReply, etc michael@0: #include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor michael@0: #include "mozilla/layers/PLayerChild.h" // for PLayerChild michael@0: #include "mozilla/layers/LayerTransactionChild.h" michael@0: #include "mozilla/layers/TextureClientPool.h" // for TextureClientPool michael@0: #include "mozilla/layers/SimpleTextureClientPool.h" // for SimpleTextureClientPool michael@0: #include "nsAString.h" michael@0: #include "nsIWidget.h" // for nsIWidget michael@0: #include "nsIWidgetListener.h" michael@0: #include "nsTArray.h" // for AutoInfallibleTArray michael@0: #include "nsXULAppAPI.h" // for XRE_GetProcessType, etc michael@0: #include "TiledLayerBuffer.h" michael@0: #ifdef MOZ_WIDGET_ANDROID michael@0: #include "AndroidBridge.h" michael@0: #endif michael@0: michael@0: using namespace mozilla::dom; michael@0: using namespace mozilla::gfx; michael@0: michael@0: namespace mozilla { michael@0: namespace layers { michael@0: michael@0: ClientLayerManager::ClientLayerManager(nsIWidget* aWidget) michael@0: : mPhase(PHASE_NONE) michael@0: , mWidget(aWidget) michael@0: , mTargetRotation(ROTATION_0) michael@0: , mRepeatTransaction(false) michael@0: , mIsRepeatTransaction(false) michael@0: , mTransactionIncomplete(false) michael@0: , mCompositorMightResample(false) michael@0: , mNeedsComposite(false) michael@0: , mForwarder(new ShadowLayerForwarder) michael@0: { michael@0: MOZ_COUNT_CTOR(ClientLayerManager); michael@0: } michael@0: michael@0: ClientLayerManager::~ClientLayerManager() michael@0: { michael@0: mRoot = nullptr; michael@0: michael@0: MOZ_COUNT_DTOR(ClientLayerManager); michael@0: } michael@0: michael@0: int32_t michael@0: ClientLayerManager::GetMaxTextureSize() const michael@0: { michael@0: return mForwarder->GetMaxTextureSize(); michael@0: } michael@0: michael@0: void michael@0: ClientLayerManager::SetDefaultTargetConfiguration(BufferMode aDoubleBuffering, michael@0: ScreenRotation aRotation) michael@0: { michael@0: mTargetRotation = aRotation; michael@0: if (mWidget) { michael@0: mTargetBounds = mWidget->GetNaturalBounds(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: ClientLayerManager::SetRoot(Layer* aLayer) michael@0: { michael@0: if (mRoot != aLayer) { michael@0: // Have to hold the old root and its children in order to michael@0: // maintain the same view of the layer tree in this process as michael@0: // the parent sees. Otherwise layers can be destroyed michael@0: // mid-transaction and bad things can happen (v. bug 612573) michael@0: if (mRoot) { michael@0: Hold(mRoot); michael@0: } michael@0: mForwarder->SetRoot(Hold(aLayer)); michael@0: NS_ASSERTION(aLayer, "Root can't be null"); michael@0: NS_ASSERTION(aLayer->Manager() == this, "Wrong manager"); michael@0: NS_ASSERTION(InConstruction(), "Only allowed in construction phase"); michael@0: mRoot = aLayer; michael@0: } michael@0: } michael@0: michael@0: void michael@0: ClientLayerManager::Mutated(Layer* aLayer) michael@0: { michael@0: LayerManager::Mutated(aLayer); michael@0: michael@0: NS_ASSERTION(InConstruction() || InDrawing(), "wrong phase"); michael@0: mForwarder->Mutated(Hold(aLayer)); michael@0: } michael@0: michael@0: void michael@0: ClientLayerManager::BeginTransactionWithTarget(gfxContext* aTarget) michael@0: { michael@0: mInTransaction = true; michael@0: michael@0: #ifdef MOZ_LAYERS_HAVE_LOG michael@0: MOZ_LAYERS_LOG(("[----- BeginTransaction")); michael@0: Log(); michael@0: #endif michael@0: michael@0: NS_ASSERTION(!InTransaction(), "Nested transactions not allowed"); michael@0: mPhase = PHASE_CONSTRUCTION; michael@0: michael@0: NS_ABORT_IF_FALSE(mKeepAlive.IsEmpty(), "uncommitted txn?"); michael@0: nsRefPtr targetContext = aTarget; michael@0: michael@0: // If the last transaction was incomplete (a failed DoEmptyTransaction), michael@0: // don't signal a new transaction to ShadowLayerForwarder. Carry on adding michael@0: // to the previous transaction. michael@0: ScreenOrientation orientation; michael@0: if (TabChild* window = mWidget->GetOwningTabChild()) { michael@0: orientation = window->GetOrientation(); michael@0: } else { michael@0: hal::ScreenConfiguration currentConfig; michael@0: hal::GetCurrentScreenConfiguration(¤tConfig); michael@0: orientation = currentConfig.orientation(); michael@0: } michael@0: nsIntRect clientBounds; michael@0: mWidget->GetClientBounds(clientBounds); michael@0: clientBounds.x = clientBounds.y = 0; michael@0: mForwarder->BeginTransaction(mTargetBounds, mTargetRotation, clientBounds, orientation); michael@0: michael@0: // If we're drawing on behalf of a context with async pan/zoom michael@0: // enabled, then the entire buffer of thebes layers might be michael@0: // composited (including resampling) asynchronously before we get michael@0: // a chance to repaint, so we have to ensure that it's all valid michael@0: // and not rotated. michael@0: if (mWidget) { michael@0: if (TabChild* window = mWidget->GetOwningTabChild()) { michael@0: mCompositorMightResample = window->IsAsyncPanZoomEnabled(); michael@0: } michael@0: } michael@0: michael@0: // If we have a non-default target, we need to let our shadow manager draw michael@0: // to it. This will happen at the end of the transaction. michael@0: if (aTarget && XRE_GetProcessType() == GeckoProcessType_Default) { michael@0: mShadowTarget = aTarget; michael@0: } michael@0: } michael@0: michael@0: void michael@0: ClientLayerManager::BeginTransaction() michael@0: { michael@0: mInTransaction = true; michael@0: BeginTransactionWithTarget(nullptr); michael@0: } michael@0: michael@0: bool michael@0: ClientLayerManager::EndTransactionInternal(DrawThebesLayerCallback aCallback, michael@0: void* aCallbackData, michael@0: EndTransactionFlags) michael@0: { michael@0: PROFILER_LABEL("ClientLayerManager", "EndTransactionInternal"); michael@0: #ifdef MOZ_LAYERS_HAVE_LOG michael@0: MOZ_LAYERS_LOG((" ----- (beginning paint)")); michael@0: Log(); michael@0: #endif michael@0: profiler_tracing("Paint", "Rasterize", TRACING_INTERVAL_START); michael@0: michael@0: NS_ASSERTION(InConstruction(), "Should be in construction phase"); michael@0: mPhase = PHASE_DRAWING; michael@0: michael@0: ClientLayer* root = ClientLayer::ToClientLayer(GetRoot()); michael@0: michael@0: mTransactionIncomplete = false; michael@0: michael@0: // Apply pending tree updates before recomputing effective michael@0: // properties. michael@0: GetRoot()->ApplyPendingUpdatesToSubtree(); michael@0: michael@0: mThebesLayerCallback = aCallback; michael@0: mThebesLayerCallbackData = aCallbackData; michael@0: michael@0: GetRoot()->ComputeEffectiveTransforms(Matrix4x4()); michael@0: michael@0: root->RenderLayer(); michael@0: if (!mRepeatTransaction && !GetRoot()->GetInvalidRegion().IsEmpty()) { michael@0: GetRoot()->Mutated(); michael@0: } michael@0: michael@0: mThebesLayerCallback = nullptr; michael@0: mThebesLayerCallbackData = nullptr; michael@0: michael@0: // Go back to the construction phase if the transaction isn't complete. michael@0: // Layout will update the layer tree and call EndTransaction(). michael@0: mPhase = mTransactionIncomplete ? PHASE_CONSTRUCTION : PHASE_NONE; michael@0: michael@0: NS_ASSERTION(!aCallback || !mTransactionIncomplete, michael@0: "If callback is not null, transaction must be complete"); michael@0: michael@0: return !mTransactionIncomplete; michael@0: } michael@0: michael@0: void michael@0: ClientLayerManager::EndTransaction(DrawThebesLayerCallback aCallback, michael@0: void* aCallbackData, michael@0: EndTransactionFlags aFlags) michael@0: { michael@0: if (mWidget) { michael@0: mWidget->PrepareWindowEffects(); michael@0: } michael@0: EndTransactionInternal(aCallback, aCallbackData, aFlags); michael@0: ForwardTransaction(!(aFlags & END_NO_REMOTE_COMPOSITE)); michael@0: michael@0: if (mRepeatTransaction) { michael@0: mRepeatTransaction = false; michael@0: mIsRepeatTransaction = true; michael@0: BeginTransaction(); michael@0: ClientLayerManager::EndTransaction(aCallback, aCallbackData, aFlags); michael@0: mIsRepeatTransaction = false; michael@0: } else { michael@0: MakeSnapshotIfRequired(); michael@0: } michael@0: michael@0: for (size_t i = 0; i < mTexturePools.Length(); i++) { michael@0: mTexturePools[i]->ReturnDeferredClients(); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: ClientLayerManager::EndEmptyTransaction(EndTransactionFlags aFlags) michael@0: { michael@0: mInTransaction = false; michael@0: michael@0: if (!mRoot) { michael@0: return false; michael@0: } michael@0: if (!EndTransactionInternal(nullptr, nullptr, aFlags)) { michael@0: // Return without calling ForwardTransaction. This leaves the michael@0: // ShadowLayerForwarder transaction open; the following michael@0: // EndTransaction will complete it. michael@0: return false; michael@0: } michael@0: if (mWidget) { michael@0: mWidget->PrepareWindowEffects(); michael@0: } michael@0: ForwardTransaction(!(aFlags & END_NO_REMOTE_COMPOSITE)); michael@0: MakeSnapshotIfRequired(); michael@0: return true; michael@0: } michael@0: michael@0: CompositorChild * michael@0: ClientLayerManager::GetRemoteRenderer() michael@0: { michael@0: if (!mWidget) { michael@0: return nullptr; michael@0: } michael@0: michael@0: return mWidget->GetRemoteRenderer(); michael@0: } michael@0: michael@0: void michael@0: ClientLayerManager::Composite() michael@0: { michael@0: if (LayerTransactionChild* manager = mForwarder->GetShadowManager()) { michael@0: manager->SendForceComposite(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: ClientLayerManager::DidComposite() michael@0: { michael@0: MOZ_ASSERT(mWidget); michael@0: nsIWidgetListener *listener = mWidget->GetWidgetListener(); michael@0: if (listener) { michael@0: listener->DidCompositeWindow(); michael@0: } michael@0: listener = mWidget->GetAttachedWidgetListener(); michael@0: if (listener) { michael@0: listener->DidCompositeWindow(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: ClientLayerManager::MakeSnapshotIfRequired() michael@0: { michael@0: if (!mShadowTarget) { michael@0: return; michael@0: } michael@0: if (mWidget) { michael@0: if (CompositorChild* remoteRenderer = GetRemoteRenderer()) { michael@0: nsIntRect bounds; michael@0: mWidget->GetBounds(bounds); michael@0: IntSize widgetSize = bounds.Size().ToIntSize(); michael@0: SurfaceDescriptor inSnapshot, snapshot; michael@0: if (mForwarder->AllocSurfaceDescriptor(widgetSize, michael@0: gfxContentType::COLOR_ALPHA, michael@0: &inSnapshot) && michael@0: // The compositor will usually reuse |snapshot| and return michael@0: // it through |outSnapshot|, but if it doesn't, it's michael@0: // responsible for freeing |snapshot|. michael@0: remoteRenderer->SendMakeSnapshot(inSnapshot, &snapshot)) { michael@0: RefPtr surf = GetSurfaceForDescriptor(snapshot); michael@0: DrawTarget* dt = mShadowTarget->GetDrawTarget(); michael@0: Rect widgetRect(Point(0, 0), Size(widgetSize.width, widgetSize.height)); michael@0: dt->DrawSurface(surf, widgetRect, widgetRect, michael@0: DrawSurfaceOptions(), michael@0: DrawOptions(1.0f, CompositionOp::OP_OVER)); michael@0: } michael@0: if (IsSurfaceDescriptorValid(snapshot)) { michael@0: mForwarder->DestroySharedSurface(&snapshot); michael@0: } michael@0: } michael@0: } michael@0: mShadowTarget = nullptr; michael@0: } michael@0: michael@0: void michael@0: ClientLayerManager::FlushRendering() michael@0: { michael@0: if (mWidget) { michael@0: if (CompositorChild* remoteRenderer = mWidget->GetRemoteRenderer()) { michael@0: remoteRenderer->SendFlushRendering(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: ClientLayerManager::SendInvalidRegion(const nsIntRegion& aRegion) michael@0: { michael@0: if (mWidget) { michael@0: if (CompositorChild* remoteRenderer = mWidget->GetRemoteRenderer()) { michael@0: remoteRenderer->SendNotifyRegionInvalidated(aRegion); michael@0: } michael@0: } michael@0: } michael@0: michael@0: uint32_t michael@0: ClientLayerManager::StartFrameTimeRecording(int32_t aBufferSize) michael@0: { michael@0: CompositorChild* renderer = GetRemoteRenderer(); michael@0: if (renderer) { michael@0: uint32_t startIndex; michael@0: renderer->SendStartFrameTimeRecording(aBufferSize, &startIndex); michael@0: return startIndex; michael@0: } michael@0: return -1; michael@0: } michael@0: michael@0: void michael@0: ClientLayerManager::StopFrameTimeRecording(uint32_t aStartIndex, michael@0: nsTArray& aFrameIntervals) michael@0: { michael@0: CompositorChild* renderer = GetRemoteRenderer(); michael@0: if (renderer) { michael@0: renderer->SendStopFrameTimeRecording(aStartIndex, &aFrameIntervals); michael@0: } michael@0: } michael@0: michael@0: void michael@0: ClientLayerManager::ForwardTransaction(bool aScheduleComposite) michael@0: { michael@0: mPhase = PHASE_FORWARD; michael@0: michael@0: // forward this transaction's changeset to our LayerManagerComposite michael@0: bool sent; michael@0: AutoInfallibleTArray replies; michael@0: if (HasShadowManager() && mForwarder->EndTransaction(&replies, mRegionToClear, aScheduleComposite, &sent)) { michael@0: for (nsTArray::size_type i = 0; i < replies.Length(); ++i) { michael@0: const EditReply& reply = replies[i]; michael@0: michael@0: switch (reply.type()) { michael@0: case EditReply::TOpContentBufferSwap: { michael@0: MOZ_LAYERS_LOG(("[LayersForwarder] DoubleBufferSwap")); michael@0: michael@0: const OpContentBufferSwap& obs = reply.get_OpContentBufferSwap(); michael@0: michael@0: CompositableClient* compositable = michael@0: CompositableClient::FromIPDLActor(obs.compositableChild()); michael@0: ContentClientRemote* contentClient = michael@0: static_cast(compositable); michael@0: MOZ_ASSERT(contentClient); michael@0: michael@0: contentClient->SwapBuffers(obs.frontUpdatedRegion()); michael@0: michael@0: break; michael@0: } michael@0: case EditReply::TOpTextureSwap: { michael@0: MOZ_LAYERS_LOG(("[LayersForwarder] TextureSwap")); michael@0: michael@0: const OpTextureSwap& ots = reply.get_OpTextureSwap(); michael@0: michael@0: CompositableClient* compositable = michael@0: CompositableClient::FromIPDLActor(ots.compositableChild()); michael@0: MOZ_ASSERT(compositable); michael@0: compositable->SetDescriptorFromReply(ots.textureId(), ots.image()); michael@0: break; michael@0: } michael@0: case EditReply::TReturnReleaseFence: { michael@0: const ReturnReleaseFence& rep = reply.get_ReturnReleaseFence(); michael@0: FenceHandle fence = rep.fence(); michael@0: PTextureChild* child = rep.textureChild(); michael@0: michael@0: if (!fence.IsValid() || !child) { michael@0: break; michael@0: } michael@0: RefPtr texture = TextureClient::AsTextureClient(child); michael@0: if (texture) { michael@0: texture->SetReleaseFenceHandle(fence); michael@0: } michael@0: break; michael@0: } michael@0: michael@0: default: michael@0: NS_RUNTIMEABORT("not reached"); michael@0: } michael@0: } michael@0: michael@0: if (sent) { michael@0: mNeedsComposite = false; michael@0: } michael@0: } else if (HasShadowManager()) { michael@0: NS_WARNING("failed to forward Layers transaction"); michael@0: } michael@0: michael@0: mForwarder->RemoveTexturesIfNecessary(); michael@0: mPhase = PHASE_NONE; michael@0: michael@0: // this may result in Layers being deleted, which results in michael@0: // PLayer::Send__delete__() and DeallocShmem() michael@0: mKeepAlive.Clear(); michael@0: } michael@0: michael@0: ShadowableLayer* michael@0: ClientLayerManager::Hold(Layer* aLayer) michael@0: { michael@0: NS_ABORT_IF_FALSE(HasShadowManager(), michael@0: "top-level tree, no shadow tree to remote to"); michael@0: michael@0: ShadowableLayer* shadowable = ClientLayer::ToClientLayer(aLayer); michael@0: NS_ABORT_IF_FALSE(shadowable, "trying to remote an unshadowable layer"); michael@0: michael@0: mKeepAlive.AppendElement(aLayer); michael@0: return shadowable; michael@0: } michael@0: michael@0: bool michael@0: ClientLayerManager::IsCompositingCheap() michael@0: { michael@0: // Whether compositing is cheap depends on the parent backend. michael@0: return mForwarder->mShadowManager && michael@0: LayerManager::IsCompositingCheap(mForwarder->GetCompositorBackendType()); michael@0: } michael@0: michael@0: void michael@0: ClientLayerManager::SetIsFirstPaint() michael@0: { michael@0: mForwarder->SetIsFirstPaint(); michael@0: } michael@0: michael@0: TextureClientPool* michael@0: ClientLayerManager::GetTexturePool(SurfaceFormat aFormat) michael@0: { michael@0: for (size_t i = 0; i < mTexturePools.Length(); i++) { michael@0: if (mTexturePools[i]->GetFormat() == aFormat) { michael@0: return mTexturePools[i]; michael@0: } michael@0: } michael@0: michael@0: mTexturePools.AppendElement( michael@0: new TextureClientPool(aFormat, IntSize(gfxPrefs::LayersTileWidth(), michael@0: gfxPrefs::LayersTileHeight()), michael@0: mForwarder)); michael@0: michael@0: return mTexturePools.LastElement(); michael@0: } michael@0: michael@0: SimpleTextureClientPool* michael@0: ClientLayerManager::GetSimpleTileTexturePool(SurfaceFormat aFormat) michael@0: { michael@0: int index = (int) aFormat; michael@0: mSimpleTilePools.EnsureLengthAtLeast(index+1); michael@0: michael@0: if (mSimpleTilePools[index].get() == nullptr) { michael@0: mSimpleTilePools[index] = new SimpleTextureClientPool(aFormat, IntSize(gfxPrefs::LayersTileWidth(), michael@0: gfxPrefs::LayersTileHeight()), michael@0: mForwarder); michael@0: } michael@0: michael@0: return mSimpleTilePools[index]; michael@0: } michael@0: michael@0: void michael@0: ClientLayerManager::ClearCachedResources(Layer* aSubtree) michael@0: { michael@0: MOZ_ASSERT(!HasShadowManager() || !aSubtree); michael@0: if (LayerTransactionChild* manager = mForwarder->GetShadowManager()) { michael@0: manager->SendClearCachedResources(); michael@0: } michael@0: if (aSubtree) { michael@0: ClearLayer(aSubtree); michael@0: } else if (mRoot) { michael@0: ClearLayer(mRoot); michael@0: } michael@0: for (size_t i = 0; i < mTexturePools.Length(); i++) { michael@0: mTexturePools[i]->Clear(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: ClientLayerManager::ClearLayer(Layer* aLayer) michael@0: { michael@0: ClientLayer::ToClientLayer(aLayer)->ClearCachedResources(); michael@0: for (Layer* child = aLayer->GetFirstChild(); child; michael@0: child = child->GetNextSibling()) { michael@0: ClearLayer(child); michael@0: } michael@0: } michael@0: michael@0: void michael@0: ClientLayerManager::GetBackendName(nsAString& aName) michael@0: { michael@0: switch (mForwarder->GetCompositorBackendType()) { michael@0: case LayersBackend::LAYERS_BASIC: aName.AssignLiteral("Basic"); return; michael@0: case LayersBackend::LAYERS_OPENGL: aName.AssignLiteral("OpenGL"); return; michael@0: case LayersBackend::LAYERS_D3D9: aName.AssignLiteral("Direct3D 9"); return; michael@0: case LayersBackend::LAYERS_D3D10: aName.AssignLiteral("Direct3D 10"); return; michael@0: case LayersBackend::LAYERS_D3D11: aName.AssignLiteral("Direct3D 11"); return; michael@0: default: NS_RUNTIMEABORT("Invalid backend"); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: ClientLayerManager::ProgressiveUpdateCallback(bool aHasPendingNewThebesContent, michael@0: ParentLayerRect& aCompositionBounds, michael@0: CSSToParentLayerScale& aZoom, michael@0: bool aDrawingCritical) michael@0: { michael@0: aZoom.scale = 1.0; michael@0: #ifdef MOZ_WIDGET_ANDROID michael@0: Layer* primaryScrollable = GetPrimaryScrollableLayer(); michael@0: if (primaryScrollable) { michael@0: const FrameMetrics& metrics = primaryScrollable->AsContainerLayer()->GetFrameMetrics(); michael@0: michael@0: // This is derived from the code in michael@0: // gfx/layers/ipc/CompositorParent.cpp::TransformShadowTree. michael@0: CSSToLayerScale paintScale = metrics.LayersPixelsPerCSSPixel(); michael@0: const CSSRect& metricsDisplayPort = michael@0: (aDrawingCritical && !metrics.mCriticalDisplayPort.IsEmpty()) ? michael@0: metrics.mCriticalDisplayPort : metrics.mDisplayPort; michael@0: LayerRect displayPort = (metricsDisplayPort + metrics.GetScrollOffset()) * paintScale; michael@0: michael@0: return AndroidBridge::Bridge()->ProgressiveUpdateCallback( michael@0: aHasPendingNewThebesContent, displayPort, paintScale.scale, aDrawingCritical, michael@0: aCompositionBounds, aZoom); michael@0: } michael@0: #endif michael@0: michael@0: return false; michael@0: } michael@0: michael@0: ClientLayer::~ClientLayer() michael@0: { michael@0: if (HasShadow()) { michael@0: PLayerChild::Send__delete__(GetShadow()); michael@0: } michael@0: MOZ_COUNT_DTOR(ClientLayer); michael@0: } michael@0: michael@0: } michael@0: }