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