diff -r 000000000000 -r 6474c204b198 gfx/layers/ipc/CompositorParent.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gfx/layers/ipc/CompositorParent.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,1336 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=2 et tw=80 : */ +/* 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 "CompositorParent.h" +#include // for fprintf, stdout +#include // for uint64_t +#include // for _Rb_tree_iterator, etc +#include // for pair +#include "LayerTransactionParent.h" // for LayerTransactionParent +#include "RenderTrace.h" // for RenderTraceLayers +#include "base/message_loop.h" // for MessageLoop +#include "base/process.h" // for ProcessHandle +#include "base/process_util.h" // for OpenProcessHandle +#include "base/task.h" // for CancelableTask, etc +#include "base/thread.h" // for Thread +#include "base/tracked.h" // for FROM_HERE +#include "gfxContext.h" // for gfxContext +#include "gfxPlatform.h" // for gfxPlatform +#include "gfxPrefs.h" // for gfxPrefs +#include "ipc/ShadowLayersManager.h" // for ShadowLayersManager +#include "mozilla/AutoRestore.h" // for AutoRestore +#include "mozilla/DebugOnly.h" // for DebugOnly +#include "mozilla/gfx/2D.h" // for DrawTarget +#include "mozilla/gfx/Point.h" // for IntSize +#include "mozilla/ipc/Transport.h" // for Transport +#include "mozilla/layers/APZCTreeManager.h" // for APZCTreeManager +#include "mozilla/layers/AsyncCompositionManager.h" +#include "mozilla/layers/BasicCompositor.h" // for BasicCompositor +#include "mozilla/layers/Compositor.h" // for Compositor +#include "mozilla/layers/CompositorOGL.h" // for CompositorOGL +#include "mozilla/layers/CompositorTypes.h" +#include "mozilla/layers/LayerManagerComposite.h" +#include "mozilla/layers/LayersTypes.h" +#include "mozilla/layers/PLayerTransactionParent.h" +#include "mozilla/mozalloc.h" // for operator new, etc +#include "nsCOMPtr.h" // for already_AddRefed +#include "nsDebug.h" // for NS_ABORT_IF_FALSE, etc +#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc +#include "nsIWidget.h" // for nsIWidget +#include "nsRect.h" // for nsIntRect +#include "nsTArray.h" // for nsTArray +#include "nsThreadUtils.h" // for NS_IsMainThread +#include "nsXULAppAPI.h" // for XRE_GetIOMessageLoop +#ifdef XP_WIN +#include "mozilla/layers/CompositorD3D11.h" +#include "mozilla/layers/CompositorD3D9.h" +#endif +#include "GeckoProfiler.h" +#include "mozilla/ipc/ProtocolTypes.h" +#include "mozilla/unused.h" + +using namespace base; +using namespace mozilla; +using namespace mozilla::ipc; +using namespace mozilla::gfx; +using namespace std; + +namespace mozilla { +namespace layers { + +CompositorParent::LayerTreeState::LayerTreeState() + : mParent(nullptr) + , mLayerManager(nullptr) + , mCrossProcessParent(nullptr) +{ +} + +typedef map LayerTreeMap; +static LayerTreeMap sIndirectLayerTrees; + +// FIXME/bug 774386: we're assuming that there's only one +// CompositorParent, but that's not always true. This assumption only +// affects CrossProcessCompositorParent below. +static Thread* sCompositorThread = nullptr; +// manual reference count of the compositor thread. +static int sCompositorThreadRefCount = 0; +static MessageLoop* sMainLoop = nullptr; +// When ContentParent::StartUp() is called, we use the Thread global. +// When StartUpWithExistingThread() is used, we have to use the two +// duplicated globals, because there's no API to make a Thread from an +// existing thread. +static PlatformThreadId sCompositorThreadID = 0; +static MessageLoop* sCompositorLoop = nullptr; + +// See ImageBridgeChild.cpp +void ReleaseImageBridgeParentSingleton(); + +static void DeferredDeleteCompositorParent(CompositorParent* aNowReadyToDie) +{ + aNowReadyToDie->Release(); +} + +static void DeleteCompositorThread() +{ + if (NS_IsMainThread()){ + ReleaseImageBridgeParentSingleton(); + delete sCompositorThread; + sCompositorThread = nullptr; + sCompositorLoop = nullptr; + sCompositorThreadID = 0; + } else { + sMainLoop->PostTask(FROM_HERE, NewRunnableFunction(&DeleteCompositorThread)); + } +} + +static void ReleaseCompositorThread() +{ + if(--sCompositorThreadRefCount == 0) { + DeleteCompositorThread(); + } +} + +void +CompositorParent::StartUpWithExistingThread(MessageLoop* aMsgLoop, + PlatformThreadId aThreadID) +{ + MOZ_ASSERT(!sCompositorThread); + CreateCompositorMap(); + sCompositorLoop = aMsgLoop; + sCompositorThreadID = aThreadID; + sMainLoop = MessageLoop::current(); + sCompositorThreadRefCount = 1; +} + +void CompositorParent::StartUp() +{ + // Check if compositor started already with StartUpWithExistingThread + if (sCompositorThreadID) { + return; + } + MOZ_ASSERT(!sCompositorLoop); + CreateCompositorMap(); + CreateThread(); + sMainLoop = MessageLoop::current(); +} + +void CompositorParent::ShutDown() +{ + DestroyThread(); + DestroyCompositorMap(); +} + +bool CompositorParent::CreateThread() +{ + NS_ASSERTION(NS_IsMainThread(), "Should be on the main Thread!"); + if (sCompositorThread || sCompositorLoop) { + return true; + } + sCompositorThreadRefCount = 1; + sCompositorThread = new Thread("Compositor"); + + Thread::Options options; + /* Timeout values are powers-of-two to enable us get better data. + 128ms is chosen for transient hangs because 8Hz should be the minimally + acceptable goal for Compositor responsiveness (normal goal is 60Hz). */ + options.transient_hang_timeout = 128; // milliseconds + /* 8192ms is chosen for permanent hangs because it's several seconds longer + than the default hang timeout on major platforms (about 5 seconds). */ + options.permanent_hang_timeout = 8192; // milliseconds + + if (!sCompositorThread->StartWithOptions(options)) { + delete sCompositorThread; + sCompositorThread = nullptr; + return false; + } + return true; +} + +void CompositorParent::DestroyThread() +{ + NS_ASSERTION(NS_IsMainThread(), "Should be on the main Thread!"); + ReleaseCompositorThread(); +} + +MessageLoop* CompositorParent::CompositorLoop() +{ + return sCompositorThread ? sCompositorThread->message_loop() : sCompositorLoop; +} + +CompositorParent::CompositorParent(nsIWidget* aWidget, + bool aUseExternalSurfaceSize, + int aSurfaceWidth, int aSurfaceHeight) + : mWidget(aWidget) + , mCurrentCompositeTask(nullptr) + , mIsTesting(false) + , mPaused(false) + , mUseExternalSurfaceSize(aUseExternalSurfaceSize) + , mEGLSurfaceSize(aSurfaceWidth, aSurfaceHeight) + , mPauseCompositionMonitor("PauseCompositionMonitor") + , mResumeCompositionMonitor("ResumeCompositionMonitor") + , mOverrideComposeReadiness(false) + , mForceCompositionTask(nullptr) + , mWantDidCompositeEvent(false) +{ + NS_ABORT_IF_FALSE(sCompositorThread != nullptr || sCompositorThreadID, + "The compositor thread must be Initialized before instanciating a COmpositorParent."); + MOZ_COUNT_CTOR(CompositorParent); + mCompositorID = 0; + // FIXME: This holds on the the fact that right now the only thing that + // can destroy this instance is initialized on the compositor thread after + // this task has been processed. + CompositorLoop()->PostTask(FROM_HERE, NewRunnableFunction(&AddCompositor, + this, &mCompositorID)); + + mRootLayerTreeID = AllocateLayerTreeId(); + sIndirectLayerTrees[mRootLayerTreeID].mParent = this; + + mApzcTreeManager = new APZCTreeManager(); + ++sCompositorThreadRefCount; +} + +PlatformThreadId +CompositorParent::CompositorThreadID() +{ + return sCompositorThread ? sCompositorThread->thread_id() : sCompositorThreadID; +} + +bool +CompositorParent::IsInCompositorThread() +{ + return CompositorThreadID() == PlatformThread::CurrentId(); +} + +uint64_t +CompositorParent::RootLayerTreeId() +{ + return mRootLayerTreeID; +} + +CompositorParent::~CompositorParent() +{ + MOZ_COUNT_DTOR(CompositorParent); + + ReleaseCompositorThread(); +} + +void +CompositorParent::Destroy() +{ + NS_ABORT_IF_FALSE(ManagedPLayerTransactionParent().Length() == 0, + "CompositorParent destroyed before managed PLayerTransactionParent"); + + // Ensure that the layer manager is destructed on the compositor thread. + mLayerManager = nullptr; + mCompositor = nullptr; + mCompositionManager = nullptr; + mApzcTreeManager->ClearTree(); + mApzcTreeManager = nullptr; + sIndirectLayerTrees.erase(mRootLayerTreeID); +} + +void +CompositorParent::ForceIsFirstPaint() +{ + mCompositionManager->ForceIsFirstPaint(); +} + +bool +CompositorParent::RecvWillStop() +{ + mPaused = true; + RemoveCompositor(mCompositorID); + + // Ensure that the layer manager is destroyed before CompositorChild. + if (mLayerManager) { + for (LayerTreeMap::iterator it = sIndirectLayerTrees.begin(); + it != sIndirectLayerTrees.end(); it++) + { + LayerTreeState* lts = &it->second; + if (lts->mParent == this) { + mLayerManager->ClearCachedResources(lts->mRoot); + lts->mLayerManager = nullptr; + } + } + mLayerManager->Destroy(); + mLayerManager = nullptr; + mCompositor = nullptr; + mCompositionManager = nullptr; + } + + return true; +} + +bool +CompositorParent::RecvStop() +{ + Destroy(); + // There are chances that the ref count reaches zero on the main thread shortly + // after this function returns while some ipdl code still needs to run on + // this thread. + // We must keep the compositor parent alive untill the code handling message + // reception is finished on this thread. + this->AddRef(); // Corresponds to DeferredDeleteCompositorParent's Release + CompositorLoop()->PostTask(FROM_HERE, + NewRunnableFunction(&DeferredDeleteCompositorParent, + this)); + return true; +} + +bool +CompositorParent::RecvPause() +{ + PauseComposition(); + return true; +} + +bool +CompositorParent::RecvResume() +{ + ResumeComposition(); + return true; +} + +bool +CompositorParent::RecvMakeSnapshot(const SurfaceDescriptor& aInSnapshot, + SurfaceDescriptor* aOutSnapshot) +{ + RefPtr target = GetDrawTargetForDescriptor(aInSnapshot, gfx::BackendType::CAIRO); + ForceComposeToTarget(target); + *aOutSnapshot = aInSnapshot; + return true; +} + +bool +CompositorParent::RecvFlushRendering() +{ + // If we're waiting to do a composite, then cancel it + // and do it immediately instead. + if (mCurrentCompositeTask) { + CancelCurrentCompositeTask(); + ForceComposeToTarget(nullptr); + } + return true; +} + +bool +CompositorParent::RecvNotifyRegionInvalidated(const nsIntRegion& aRegion) +{ + if (mLayerManager) { + mLayerManager->AddInvalidRegion(aRegion); + } + return true; +} + +bool +CompositorParent::RecvStartFrameTimeRecording(const int32_t& aBufferSize, uint32_t* aOutStartIndex) +{ + if (mLayerManager) { + *aOutStartIndex = mLayerManager->StartFrameTimeRecording(aBufferSize); + } else { + *aOutStartIndex = 0; + } + return true; +} + +bool +CompositorParent::RecvStopFrameTimeRecording(const uint32_t& aStartIndex, + InfallibleTArray* intervals) +{ + if (mLayerManager) { + mLayerManager->StopFrameTimeRecording(aStartIndex, *intervals); + } + return true; +} + +void +CompositorParent::ActorDestroy(ActorDestroyReason why) +{ + CancelCurrentCompositeTask(); + if (mForceCompositionTask) { + mForceCompositionTask->Cancel(); + mForceCompositionTask = nullptr; + } + mPaused = true; + RemoveCompositor(mCompositorID); + + if (mLayerManager) { + mLayerManager->Destroy(); + mLayerManager = nullptr; + sIndirectLayerTrees[mRootLayerTreeID].mLayerManager = nullptr; + mCompositionManager = nullptr; + mCompositor = nullptr; + } +} + + +void +CompositorParent::ScheduleRenderOnCompositorThread() +{ + CancelableTask *renderTask = NewRunnableMethod(this, &CompositorParent::ScheduleComposition); + CompositorLoop()->PostTask(FROM_HERE, renderTask); +} + +void +CompositorParent::PauseComposition() +{ + NS_ABORT_IF_FALSE(CompositorThreadID() == PlatformThread::CurrentId(), + "PauseComposition() can only be called on the compositor thread"); + + MonitorAutoLock lock(mPauseCompositionMonitor); + + if (!mPaused) { + mPaused = true; + + mCompositor->Pause(); + } + + // if anyone's waiting to make sure that composition really got paused, tell them + lock.NotifyAll(); +} + +void +CompositorParent::ResumeComposition() +{ + NS_ABORT_IF_FALSE(CompositorThreadID() == PlatformThread::CurrentId(), + "ResumeComposition() can only be called on the compositor thread"); + + MonitorAutoLock lock(mResumeCompositionMonitor); + + if (!mCompositor->Resume()) { +#ifdef MOZ_WIDGET_ANDROID + // We can't get a surface. This could be because the activity changed between + // the time resume was scheduled and now. + __android_log_print(ANDROID_LOG_INFO, "CompositorParent", "Unable to renew compositor surface; remaining in paused state"); +#endif + lock.NotifyAll(); + return; + } + + mPaused = false; + + Composite(); + + // if anyone's waiting to make sure that composition really got resumed, tell them + lock.NotifyAll(); +} + +void +CompositorParent::ForceComposition() +{ + // Cancel the orientation changed state to force composition + mForceCompositionTask = nullptr; + ScheduleRenderOnCompositorThread(); +} + +void +CompositorParent::CancelCurrentCompositeTask() +{ + if (mCurrentCompositeTask) { + mCurrentCompositeTask->Cancel(); + mCurrentCompositeTask = nullptr; + } +} + +void +CompositorParent::SetEGLSurfaceSize(int width, int height) +{ + NS_ASSERTION(mUseExternalSurfaceSize, "Compositor created without UseExternalSurfaceSize provided"); + mEGLSurfaceSize.SizeTo(width, height); + if (mCompositor) { + mCompositor->SetDestinationSurfaceSize(gfx::IntSize(mEGLSurfaceSize.width, mEGLSurfaceSize.height)); + } +} + +void +CompositorParent::ResumeCompositionAndResize(int width, int height) +{ + SetEGLSurfaceSize(width, height); + ResumeComposition(); +} + +/* + * This will execute a pause synchronously, waiting to make sure that the compositor + * really is paused. + */ +void +CompositorParent::SchedulePauseOnCompositorThread() +{ + MonitorAutoLock lock(mPauseCompositionMonitor); + + CancelableTask *pauseTask = NewRunnableMethod(this, + &CompositorParent::PauseComposition); + CompositorLoop()->PostTask(FROM_HERE, pauseTask); + + // Wait until the pause has actually been processed by the compositor thread + lock.Wait(); +} + +bool +CompositorParent::ScheduleResumeOnCompositorThread(int width, int height) +{ + MonitorAutoLock lock(mResumeCompositionMonitor); + + CancelableTask *resumeTask = + NewRunnableMethod(this, &CompositorParent::ResumeCompositionAndResize, width, height); + CompositorLoop()->PostTask(FROM_HERE, resumeTask); + + // Wait until the resume has actually been processed by the compositor thread + lock.Wait(); + + return !mPaused; +} + +void +CompositorParent::ScheduleTask(CancelableTask* task, int time) +{ + if (time == 0) { + MessageLoop::current()->PostTask(FROM_HERE, task); + } else { + MessageLoop::current()->PostDelayedTask(FROM_HERE, task, time); + } +} + +void +CompositorParent::NotifyShadowTreeTransaction(uint64_t aId, bool aIsFirstPaint, bool aScheduleComposite) +{ + if (mApzcTreeManager && + mLayerManager && + mLayerManager->GetRoot()) { + AutoResolveRefLayers resolve(mCompositionManager); + mApzcTreeManager->UpdatePanZoomControllerTree(this, mLayerManager->GetRoot(), aIsFirstPaint, aId); + + mLayerManager->NotifyShadowTreeTransaction(); + } + if (aScheduleComposite) { + ScheduleComposition(); + } + + mWantDidCompositeEvent = true; +} + +// Used when layout.frame_rate is -1. Needs to be kept in sync with +// DEFAULT_FRAME_RATE in nsRefreshDriver.cpp. +static const int32_t kDefaultFrameRate = 60; + +static int32_t +CalculateCompositionFrameRate() +{ + int32_t compositionFrameRatePref = gfxPrefs::LayersCompositionFrameRate(); + if (compositionFrameRatePref < 0) { + // Use the same frame rate for composition as for layout. + int32_t layoutFrameRatePref = gfxPrefs::LayoutFrameRate(); + if (layoutFrameRatePref < 0) { + // TODO: The main thread frame scheduling code consults the actual + // monitor refresh rate in this case. We should do the same. + return kDefaultFrameRate; + } + return layoutFrameRatePref; + } + return compositionFrameRatePref; +} + +void +CompositorParent::ScheduleComposition() +{ + if (mCurrentCompositeTask || mPaused) { + return; + } + + bool initialComposition = mLastCompose.IsNull(); + TimeDuration delta; + if (!initialComposition) + delta = TimeStamp::Now() - mLastCompose; + + int32_t rate = CalculateCompositionFrameRate(); + + // If rate == 0 (ASAP mode), minFrameDelta must be 0 so there's no delay. + TimeDuration minFrameDelta = TimeDuration::FromMilliseconds( + rate == 0 ? 0.0 : std::max(0.0, 1000.0 / rate)); + + + mCurrentCompositeTask = NewRunnableMethod(this, &CompositorParent::Composite); + + if (!initialComposition && delta < minFrameDelta) { + TimeDuration delay = minFrameDelta - delta; +#ifdef COMPOSITOR_PERFORMANCE_WARNING + mExpectedComposeStartTime = TimeStamp::Now() + delay; +#endif + ScheduleTask(mCurrentCompositeTask, delay.ToMilliseconds()); + } else { +#ifdef COMPOSITOR_PERFORMANCE_WARNING + mExpectedComposeStartTime = TimeStamp::Now(); +#endif + ScheduleTask(mCurrentCompositeTask, 0); + } +} + +void +CompositorParent::Composite() +{ + CompositeToTarget(nullptr); +} + +void +CompositorParent::CompositeToTarget(DrawTarget* aTarget) +{ + profiler_tracing("Paint", "Composite", TRACING_INTERVAL_START); + PROFILER_LABEL("CompositorParent", "Composite"); + NS_ABORT_IF_FALSE(CompositorThreadID() == PlatformThread::CurrentId(), + "Composite can only be called on the compositor thread"); + +#ifdef COMPOSITOR_PERFORMANCE_WARNING + TimeDuration scheduleDelta = TimeStamp::Now() - mExpectedComposeStartTime; + if (scheduleDelta > TimeDuration::FromMilliseconds(2) || + scheduleDelta < TimeDuration::FromMilliseconds(-2)) { + printf_stderr("Compositor: Compose starting off schedule by %4.1f ms\n", + scheduleDelta.ToMilliseconds()); + } +#endif + + if (mCurrentCompositeTask) { + mCurrentCompositeTask->Cancel(); + mCurrentCompositeTask = nullptr; + } + + mLastCompose = TimeStamp::Now(); + + if (!CanComposite()) { + return; + } + + AutoResolveRefLayers resolve(mCompositionManager); + + if (aTarget) { + mLayerManager->BeginTransactionWithDrawTarget(aTarget); + } else { + mLayerManager->BeginTransaction(); + } + + if (mForceCompositionTask && !mOverrideComposeReadiness) { + if (mCompositionManager->ReadyForCompose()) { + mForceCompositionTask->Cancel(); + mForceCompositionTask = nullptr; + } else { + return; + } + } + + TimeStamp time = mIsTesting ? mTestTime : mLastCompose; + bool requestNextFrame = mCompositionManager->TransformShadowTree(time); + if (requestNextFrame) { + ScheduleComposition(); + } + + RenderTraceLayers(mLayerManager->GetRoot(), "0000"); + + mCompositionManager->ComputeRotation(); + +#ifdef MOZ_DUMP_PAINTING + static bool gDumpCompositorTree = false; + if (gDumpCompositorTree) { + printf_stderr("Painting --- compositing layer tree:\n"); + mLayerManager->Dump(); + } +#endif + mLayerManager->SetDebugOverlayWantsNextFrame(false); + mLayerManager->EndEmptyTransaction(); + + if (!aTarget && mWantDidCompositeEvent) { + DidComposite(); + mWantDidCompositeEvent = false; + } + + if (mLayerManager->DebugOverlayWantsNextFrame()) { + ScheduleComposition(); + } + +#ifdef COMPOSITOR_PERFORMANCE_WARNING + TimeDuration executionTime = TimeStamp::Now() - mLastCompose; + TimeDuration frameBudget = TimeDuration::FromMilliseconds(15); + int32_t frameRate = CalculateCompositionFrameRate(); + if (frameRate > 0) { + frameBudget = TimeDuration::FromSeconds(1.0 / frameRate); + } + if (executionTime > frameBudget) { + printf_stderr("Compositor: Composite execution took %4.1f ms\n", + executionTime.ToMilliseconds()); + } +#endif + + // 0 -> Full-tilt composite + if (gfxPrefs::LayersCompositionFrameRate() == 0 + || mLayerManager->GetCompositor()->GetDiagnosticTypes() & DIAGNOSTIC_FLASH_BORDERS) { + // Special full-tilt composite mode for performance testing + ScheduleComposition(); + } + + profiler_tracing("Paint", "Composite", TRACING_INTERVAL_END); +} + +void +CompositorParent::DidComposite() +{ + unused << SendDidComposite(0); + + for (LayerTreeMap::iterator it = sIndirectLayerTrees.begin(); + it != sIndirectLayerTrees.end(); it++) { + LayerTreeState* lts = &it->second; + if (lts->mParent == this && lts->mCrossProcessParent) { + unused << lts->mCrossProcessParent->SendDidComposite(it->first); + } + } +} + +void +CompositorParent::ForceComposeToTarget(DrawTarget* aTarget) +{ + PROFILER_LABEL("CompositorParent", "ForceComposeToTarget"); + AutoRestore override(mOverrideComposeReadiness); + mOverrideComposeReadiness = true; + + CompositeToTarget(aTarget); +} + +bool +CompositorParent::CanComposite() +{ + return !(mPaused || !mLayerManager || !mLayerManager->GetRoot()); +} + +// Go down the composite layer tree, setting properties to match their +// content-side counterparts. +static void +SetShadowProperties(Layer* aLayer) +{ + // FIXME: Bug 717688 -- Do these updates in LayerTransactionParent::RecvUpdate. + LayerComposite* layerComposite = aLayer->AsLayerComposite(); + // Set the layerComposite's base transform to the layer's base transform. + layerComposite->SetShadowTransform(aLayer->GetBaseTransform()); + layerComposite->SetShadowTransformSetByAnimation(false); + layerComposite->SetShadowVisibleRegion(aLayer->GetVisibleRegion()); + layerComposite->SetShadowClipRect(aLayer->GetClipRect()); + layerComposite->SetShadowOpacity(aLayer->GetOpacity()); + + for (Layer* child = aLayer->GetFirstChild(); + child; child = child->GetNextSibling()) { + SetShadowProperties(child); + } +} + +void +CompositorParent::ShadowLayersUpdated(LayerTransactionParent* aLayerTree, + const TargetConfig& aTargetConfig, + bool aIsFirstPaint, + bool aScheduleComposite) +{ + if (!aIsFirstPaint && + !mCompositionManager->IsFirstPaint() && + mCompositionManager->RequiresReorientation(aTargetConfig.orientation())) { + if (mForceCompositionTask != nullptr) { + mForceCompositionTask->Cancel(); + } + mForceCompositionTask = NewRunnableMethod(this, &CompositorParent::ForceComposition); + ScheduleTask(mForceCompositionTask, gfxPrefs::OrientationSyncMillis()); + } + + // Instruct the LayerManager to update its render bounds now. Since all the orientation + // change, dimension change would be done at the stage, update the size here is free of + // race condition. + mLayerManager->UpdateRenderBounds(aTargetConfig.clientBounds()); + mLayerManager->SetRegionToClear(aTargetConfig.clearRegion()); + + mCompositionManager->Updated(aIsFirstPaint, aTargetConfig); + Layer* root = aLayerTree->GetRoot(); + mLayerManager->SetRoot(root); + + if (mApzcTreeManager) { + AutoResolveRefLayers resolve(mCompositionManager); + mApzcTreeManager->UpdatePanZoomControllerTree(this, root, aIsFirstPaint, mRootLayerTreeID); + } + + if (root) { + SetShadowProperties(root); + } + if (aScheduleComposite) { + ScheduleComposition(); + // When testing we synchronously update the shadow tree with the animated + // values to avoid race conditions when calling GetAnimationTransform etc. + // (since the above SetShadowProperties will remove animation effects). + // However, we only do this update when a composite operation is already + // scheduled in order to better match the behavior under regular sampling + // conditions. + if (mIsTesting && root && mCurrentCompositeTask) { + AutoResolveRefLayers resolve(mCompositionManager); + bool requestNextFrame = + mCompositionManager->TransformShadowTree(mTestTime); + if (!requestNextFrame) { + CancelCurrentCompositeTask(); + } + } + } + mLayerManager->NotifyShadowTreeTransaction(); + mWantDidCompositeEvent = true; +} + +void +CompositorParent::ForceComposite(LayerTransactionParent* aLayerTree) +{ + ScheduleComposition(); +} + +bool +CompositorParent::SetTestSampleTime(LayerTransactionParent* aLayerTree, + const TimeStamp& aTime) +{ + if (aTime.IsNull()) { + return false; + } + + mIsTesting = true; + mTestTime = aTime; + + // Update but only if we were already scheduled to animate + if (mCompositionManager && mCurrentCompositeTask) { + AutoResolveRefLayers resolve(mCompositionManager); + bool requestNextFrame = mCompositionManager->TransformShadowTree(aTime); + if (!requestNextFrame) { + CancelCurrentCompositeTask(); + } + } + + return true; +} + +void +CompositorParent::LeaveTestMode(LayerTransactionParent* aLayerTree) +{ + mIsTesting = false; +} + +void +CompositorParent::InitializeLayerManager(const nsTArray& aBackendHints) +{ + NS_ASSERTION(!mLayerManager, "Already initialised mLayerManager"); + NS_ASSERTION(!mCompositor, "Already initialised mCompositor"); + + for (size_t i = 0; i < aBackendHints.Length(); ++i) { + RefPtr compositor; + if (aBackendHints[i] == LayersBackend::LAYERS_OPENGL) { + compositor = new CompositorOGL(mWidget, + mEGLSurfaceSize.width, + mEGLSurfaceSize.height, + mUseExternalSurfaceSize); + } else if (aBackendHints[i] == LayersBackend::LAYERS_BASIC) { + compositor = new BasicCompositor(mWidget); +#ifdef XP_WIN + } else if (aBackendHints[i] == LayersBackend::LAYERS_D3D11) { + compositor = new CompositorD3D11(mWidget); + } else if (aBackendHints[i] == LayersBackend::LAYERS_D3D9) { + compositor = new CompositorD3D9(this, mWidget); +#endif + } + + if (!compositor) { + // We passed a backend hint for which we can't create a compositor. + // For example, we sometime pass LayersBackend::LAYERS_NONE as filler in aBackendHints. + continue; + } + + compositor->SetCompositorID(mCompositorID); + RefPtr layerManager = new LayerManagerComposite(compositor); + + if (layerManager->Initialize()) { + mLayerManager = layerManager; + MOZ_ASSERT(compositor); + mCompositor = compositor; + sIndirectLayerTrees[mRootLayerTreeID].mLayerManager = layerManager; + return; + } + } +} + +PLayerTransactionParent* +CompositorParent::AllocPLayerTransactionParent(const nsTArray& aBackendHints, + const uint64_t& aId, + TextureFactoryIdentifier* aTextureFactoryIdentifier, + bool *aSuccess) +{ + MOZ_ASSERT(aId == 0); + + // mWidget doesn't belong to the compositor thread, so it should be set to + // nullptr before returning from this method, to avoid accessing it elsewhere. + nsIntRect rect; + mWidget->GetClientBounds(rect); + InitializeLayerManager(aBackendHints); + mWidget = nullptr; + + if (!mLayerManager) { + NS_WARNING("Failed to initialise Compositor"); + *aSuccess = false; + LayerTransactionParent* p = new LayerTransactionParent(nullptr, this, 0); + p->AddIPDLReference(); + return p; + } + + mCompositionManager = new AsyncCompositionManager(mLayerManager); + *aSuccess = true; + + *aTextureFactoryIdentifier = mCompositor->GetTextureFactoryIdentifier(); + LayerTransactionParent* p = new LayerTransactionParent(mLayerManager, this, 0); + p->AddIPDLReference(); + return p; +} + +bool +CompositorParent::DeallocPLayerTransactionParent(PLayerTransactionParent* actor) +{ + static_cast(actor)->ReleaseIPDLReference(); + return true; +} + + +typedef map CompositorMap; +static CompositorMap* sCompositorMap; + +void CompositorParent::CreateCompositorMap() +{ + if (sCompositorMap == nullptr) { + sCompositorMap = new CompositorMap; + } +} + +void CompositorParent::DestroyCompositorMap() +{ + if (sCompositorMap != nullptr) { + NS_ASSERTION(sCompositorMap->empty(), + "The Compositor map should be empty when destroyed>"); + delete sCompositorMap; + sCompositorMap = nullptr; + } +} + +CompositorParent* CompositorParent::GetCompositor(uint64_t id) +{ + CompositorMap::iterator it = sCompositorMap->find(id); + return it != sCompositorMap->end() ? it->second : nullptr; +} + +void CompositorParent::AddCompositor(CompositorParent* compositor, uint64_t* outID) +{ + static uint64_t sNextID = 1; + + ++sNextID; + (*sCompositorMap)[sNextID] = compositor; + *outID = sNextID; +} + +CompositorParent* CompositorParent::RemoveCompositor(uint64_t id) +{ + CompositorMap::iterator it = sCompositorMap->find(id); + if (it == sCompositorMap->end()) { + return nullptr; + } + CompositorParent *retval = it->second; + sCompositorMap->erase(it); + return retval; +} + +bool +CompositorParent::RecvNotifyChildCreated(const uint64_t& child) +{ + NotifyChildCreated(child); + return true; +} + +void +CompositorParent::NotifyChildCreated(uint64_t aChild) +{ + sIndirectLayerTrees[aChild].mParent = this; + sIndirectLayerTrees[aChild].mLayerManager = mLayerManager; +} + +/*static*/ uint64_t +CompositorParent::AllocateLayerTreeId() +{ + MOZ_ASSERT(CompositorLoop()); + MOZ_ASSERT(NS_IsMainThread()); + static uint64_t ids = 0; + return ++ids; +} + +static void +EraseLayerState(uint64_t aId) +{ + sIndirectLayerTrees.erase(aId); +} + +/*static*/ void +CompositorParent::DeallocateLayerTreeId(uint64_t aId) +{ + MOZ_ASSERT(NS_IsMainThread()); + CompositorLoop()->PostTask(FROM_HERE, + NewRunnableFunction(&EraseLayerState, aId)); +} + +static void +UpdateControllerForLayersId(uint64_t aLayersId, + GeckoContentController* aController) +{ + // Adopt ref given to us by SetControllerForLayerTree() + sIndirectLayerTrees[aLayersId].mController = + already_AddRefed(aController); +} + +ScopedLayerTreeRegistration::ScopedLayerTreeRegistration(uint64_t aLayersId, + Layer* aRoot, + GeckoContentController* aController) + : mLayersId(aLayersId) +{ + sIndirectLayerTrees[aLayersId].mRoot = aRoot; + sIndirectLayerTrees[aLayersId].mController = aController; +} + +ScopedLayerTreeRegistration::~ScopedLayerTreeRegistration() +{ + sIndirectLayerTrees.erase(mLayersId); +} + +/*static*/ void +CompositorParent::SetControllerForLayerTree(uint64_t aLayersId, + GeckoContentController* aController) +{ + // This ref is adopted by UpdateControllerForLayersId(). + aController->AddRef(); + CompositorLoop()->PostTask(FROM_HERE, + NewRunnableFunction(&UpdateControllerForLayersId, + aLayersId, + aController)); +} + +/*static*/ APZCTreeManager* +CompositorParent::GetAPZCTreeManager(uint64_t aLayersId) +{ + const CompositorParent::LayerTreeState* state = CompositorParent::GetIndirectShadowTree(aLayersId); + if (state && state->mParent) { + return state->mParent->mApzcTreeManager; + } + return nullptr; +} + +float +CompositorParent::ComputeRenderIntegrity() +{ + if (mLayerManager) { + return mLayerManager->ComputeRenderIntegrity(); + } + + return 1.0f; +} + + +/** + * This class handles layer updates pushed directly from child + * processes to the compositor thread. It's associated with a + * CompositorParent on the compositor thread. While it uses the + * PCompositor protocol to manage these updates, it doesn't actually + * drive compositing itself. For that it hands off work to the + * CompositorParent it's associated with. + */ +class CrossProcessCompositorParent MOZ_FINAL : public PCompositorParent, + public ShadowLayersManager +{ + friend class CompositorParent; + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CrossProcessCompositorParent) +public: + CrossProcessCompositorParent(Transport* aTransport) + : mTransport(aTransport) + {} + + // IToplevelProtocol::CloneToplevel() + virtual IToplevelProtocol* + CloneToplevel(const InfallibleTArray& aFds, + base::ProcessHandle aPeerProcess, + mozilla::ipc::ProtocolCloneContext* aCtx) MOZ_OVERRIDE; + + virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; + + // FIXME/bug 774388: work out what shutdown protocol we need. + virtual bool RecvWillStop() MOZ_OVERRIDE { return true; } + virtual bool RecvStop() MOZ_OVERRIDE { return true; } + virtual bool RecvPause() MOZ_OVERRIDE { return true; } + virtual bool RecvResume() MOZ_OVERRIDE { return true; } + virtual bool RecvNotifyChildCreated(const uint64_t& child) MOZ_OVERRIDE; + virtual bool RecvMakeSnapshot(const SurfaceDescriptor& aInSnapshot, + SurfaceDescriptor* aOutSnapshot) + { return true; } + virtual bool RecvFlushRendering() MOZ_OVERRIDE { return true; } + virtual bool RecvNotifyRegionInvalidated(const nsIntRegion& aRegion) { return true; } + virtual bool RecvStartFrameTimeRecording(const int32_t& aBufferSize, uint32_t* aOutStartIndex) MOZ_OVERRIDE { return true; } + virtual bool RecvStopFrameTimeRecording(const uint32_t& aStartIndex, InfallibleTArray* intervals) MOZ_OVERRIDE { return true; } + + virtual PLayerTransactionParent* + AllocPLayerTransactionParent(const nsTArray& aBackendHints, + const uint64_t& aId, + TextureFactoryIdentifier* aTextureFactoryIdentifier, + bool *aSuccess) MOZ_OVERRIDE; + + virtual bool DeallocPLayerTransactionParent(PLayerTransactionParent* aLayers) MOZ_OVERRIDE; + + virtual void ShadowLayersUpdated(LayerTransactionParent* aLayerTree, + const TargetConfig& aTargetConfig, + bool aIsFirstPaint, + bool aScheduleComposite) MOZ_OVERRIDE; + virtual void ForceComposite(LayerTransactionParent* aLayerTree) MOZ_OVERRIDE; + virtual bool SetTestSampleTime(LayerTransactionParent* aLayerTree, + const TimeStamp& aTime) MOZ_OVERRIDE; + virtual void LeaveTestMode(LayerTransactionParent* aLayerTree) MOZ_OVERRIDE; + + virtual AsyncCompositionManager* GetCompositionManager(LayerTransactionParent* aParent) MOZ_OVERRIDE; + +private: + // Private destructor, to discourage deletion outside of Release(): + virtual ~CrossProcessCompositorParent(); + + void DeferredDestroy(); + + // There can be many CPCPs, and IPDL-generated code doesn't hold a + // reference to top-level actors. So we hold a reference to + // ourself. This is released (deferred) in ActorDestroy(). + nsRefPtr mSelfRef; + Transport* mTransport; +}; + +static void +OpenCompositor(CrossProcessCompositorParent* aCompositor, + Transport* aTransport, ProcessHandle aHandle, + MessageLoop* aIOLoop) +{ + DebugOnly ok = aCompositor->Open(aTransport, aHandle, aIOLoop); + MOZ_ASSERT(ok); +} + +/*static*/ PCompositorParent* +CompositorParent::Create(Transport* aTransport, ProcessId aOtherProcess) +{ + nsRefPtr cpcp = + new CrossProcessCompositorParent(aTransport); + ProcessHandle handle; + if (!base::OpenProcessHandle(aOtherProcess, &handle)) { + // XXX need to kill |aOtherProcess|, it's boned + return nullptr; + } + cpcp->mSelfRef = cpcp; + CompositorLoop()->PostTask( + FROM_HERE, + NewRunnableFunction(OpenCompositor, cpcp.get(), + aTransport, handle, XRE_GetIOMessageLoop())); + // The return value is just compared to null for success checking, + // we're not sharing a ref. + return cpcp.get(); +} + +IToplevelProtocol* +CompositorParent::CloneToplevel(const InfallibleTArray& aFds, + base::ProcessHandle aPeerProcess, + mozilla::ipc::ProtocolCloneContext* aCtx) +{ + for (unsigned int i = 0; i < aFds.Length(); i++) { + if (aFds[i].protocolId() == (unsigned)GetProtocolId()) { + Transport* transport = OpenDescriptor(aFds[i].fd(), + Transport::MODE_SERVER); + PCompositorParent* compositor = Create(transport, base::GetProcId(aPeerProcess)); + compositor->CloneManagees(this, aCtx); + compositor->IToplevelProtocol::SetTransport(transport); + return compositor; + } + } + return nullptr; +} + +static void +UpdateIndirectTree(uint64_t aId, Layer* aRoot, const TargetConfig& aTargetConfig) +{ + sIndirectLayerTrees[aId].mRoot = aRoot; + sIndirectLayerTrees[aId].mTargetConfig = aTargetConfig; +} + +/* static */ const CompositorParent::LayerTreeState* +CompositorParent::GetIndirectShadowTree(uint64_t aId) +{ + LayerTreeMap::const_iterator cit = sIndirectLayerTrees.find(aId); + if (sIndirectLayerTrees.end() == cit) { + return nullptr; + } + return &cit->second; +} + +static void +RemoveIndirectTree(uint64_t aId) +{ + sIndirectLayerTrees.erase(aId); +} + +void +CrossProcessCompositorParent::ActorDestroy(ActorDestroyReason aWhy) +{ + MessageLoop::current()->PostTask( + FROM_HERE, + NewRunnableMethod(this, &CrossProcessCompositorParent::DeferredDestroy)); +} + +PLayerTransactionParent* +CrossProcessCompositorParent::AllocPLayerTransactionParent(const nsTArray&, + const uint64_t& aId, + TextureFactoryIdentifier* aTextureFactoryIdentifier, + bool *aSuccess) +{ + MOZ_ASSERT(aId != 0); + + if (sIndirectLayerTrees[aId].mLayerManager) { + sIndirectLayerTrees[aId].mCrossProcessParent = this; + LayerManagerComposite* lm = sIndirectLayerTrees[aId].mLayerManager; + *aTextureFactoryIdentifier = lm->GetCompositor()->GetTextureFactoryIdentifier(); + *aSuccess = true; + LayerTransactionParent* p = new LayerTransactionParent(lm, this, aId); + p->AddIPDLReference(); + return p; + } + + NS_WARNING("Created child without a matching parent?"); + // XXX: should be false, but that causes us to fail some tests on Mac w/ OMTC. + // Bug 900745. change *aSuccess to false to see test failures. + *aSuccess = true; + LayerTransactionParent* p = new LayerTransactionParent(nullptr, this, aId); + p->AddIPDLReference(); + return p; +} + +bool +CrossProcessCompositorParent::DeallocPLayerTransactionParent(PLayerTransactionParent* aLayers) +{ + LayerTransactionParent* slp = static_cast(aLayers); + RemoveIndirectTree(slp->GetId()); + static_cast(aLayers)->ReleaseIPDLReference(); + return true; +} + +bool +CrossProcessCompositorParent::RecvNotifyChildCreated(const uint64_t& child) +{ + sIndirectLayerTrees[child].mParent->NotifyChildCreated(child); + return true; +} + +void +CrossProcessCompositorParent::ShadowLayersUpdated( + LayerTransactionParent* aLayerTree, + const TargetConfig& aTargetConfig, + bool aIsFirstPaint, + bool aScheduleComposite) +{ + uint64_t id = aLayerTree->GetId(); + MOZ_ASSERT(id != 0); + Layer* shadowRoot = aLayerTree->GetRoot(); + if (shadowRoot) { + SetShadowProperties(shadowRoot); + } + UpdateIndirectTree(id, shadowRoot, aTargetConfig); + + sIndirectLayerTrees[id].mParent->NotifyShadowTreeTransaction(id, aIsFirstPaint, aScheduleComposite); +} + +void +CrossProcessCompositorParent::ForceComposite(LayerTransactionParent* aLayerTree) +{ + uint64_t id = aLayerTree->GetId(); + MOZ_ASSERT(id != 0); + sIndirectLayerTrees[id].mParent->ForceComposite(aLayerTree); +} + +bool +CrossProcessCompositorParent::SetTestSampleTime( + LayerTransactionParent* aLayerTree, const TimeStamp& aTime) +{ + uint64_t id = aLayerTree->GetId(); + MOZ_ASSERT(id != 0); + return sIndirectLayerTrees[id].mParent->SetTestSampleTime(aLayerTree, aTime); +} + +void +CrossProcessCompositorParent::LeaveTestMode(LayerTransactionParent* aLayerTree) +{ + uint64_t id = aLayerTree->GetId(); + MOZ_ASSERT(id != 0); + sIndirectLayerTrees[id].mParent->LeaveTestMode(aLayerTree); +} + +AsyncCompositionManager* +CrossProcessCompositorParent::GetCompositionManager(LayerTransactionParent* aLayerTree) +{ + uint64_t id = aLayerTree->GetId(); + return sIndirectLayerTrees[id].mParent->GetCompositionManager(aLayerTree); +} + +void +CrossProcessCompositorParent::DeferredDestroy() +{ + CrossProcessCompositorParent* self; + mSelfRef.forget(&self); + + nsCOMPtr runnable = + NS_NewNonOwningRunnableMethod(self, &CrossProcessCompositorParent::Release); + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable))); +} + +CrossProcessCompositorParent::~CrossProcessCompositorParent() +{ + XRE_GetIOMessageLoop()->PostTask(FROM_HERE, + new DeleteTask(mTransport)); +} + +IToplevelProtocol* +CrossProcessCompositorParent::CloneToplevel(const InfallibleTArray& aFds, + base::ProcessHandle aPeerProcess, + mozilla::ipc::ProtocolCloneContext* aCtx) +{ + for (unsigned int i = 0; i < aFds.Length(); i++) { + if (aFds[i].protocolId() == (unsigned)GetProtocolId()) { + Transport* transport = OpenDescriptor(aFds[i].fd(), + Transport::MODE_SERVER); + PCompositorParent* compositor = + CompositorParent::Create(transport, base::GetProcId(aPeerProcess)); + compositor->CloneManagees(this, aCtx); + compositor->IToplevelProtocol::SetTransport(transport); + return compositor; + } + } + return nullptr; +} + +} // namespace layers +} // namespace mozilla