/* -*- 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" #include "mozilla/Hal.h" #include "mozilla/HalTypes.h" namespace mozilla { namespace layers { using namespace base; using namespace mozilla::ipc; using namespace mozilla::gfx; using namespace std; 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(); } } static void SetThreadPriority() { hal::SetCurrentThreadPriority(hal::THREAD_PRIORITY_COMPOSITOR); } 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)); CompositorLoop()->PostTask(FROM_HERE, NewRunnableFunction(SetThreadPriority)); 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; if (mCompositor) { mCompositor->Destroy(); } 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; 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, uint32_t aPaintSequenceNumber) { if (mApzcTreeManager && mLayerManager && mLayerManager->GetRoot()) { AutoResolveRefLayers resolve(mCompositionManager); mApzcTreeManager->UpdatePanZoomControllerTree(this, mLayerManager->GetRoot(), aIsFirstPaint, aId, aPaintSequenceNumber); 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() & DiagnosticTypes::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::ScheduleRotationOnCompositorThread(const TargetConfig& aTargetConfig, bool aIsFirstPaint) { MOZ_ASSERT(IsInCompositorThread()); if (!aIsFirstPaint && !mCompositionManager->IsFirstPaint() && mCompositionManager->RequiresReorientation(aTargetConfig.orientation())) { if (mForceCompositionTask != nullptr) { mForceCompositionTask->Cancel(); } mForceCompositionTask = NewRunnableMethod(this, &CompositorParent::ForceComposition); ScheduleTask(mForceCompositionTask, gfxPrefs::OrientationSyncMillis()); } } void CompositorParent::ShadowLayersUpdated(LayerTransactionParent* aLayerTree, const TargetConfig& aTargetConfig, bool aIsFirstPaint, bool aScheduleComposite, uint32_t aPaintSequenceNumber) { ScheduleRotationOnCompositorThread(aTargetConfig, aIsFirstPaint); // 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, aPaintSequenceNumber); } 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; } bool CompositorParent::RecvRequestOverfill() { uint32_t overfillRatio = mCompositor->GetFillRatio(); unused << SendOverfill(overfillRatio); return true; } void CompositorParent::GetAPZTestData(const LayerTransactionParent* aLayerTree, APZTestData* aOutData) { *aOutData = sIndirectLayerTrees[mRootLayerTreeID].mApzTestData; } 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, // child side's process id is current process Id base::GetProcId(base::GetCurrentProcessHandle())); p->AddIPDLReference(); return p; } mCompositionManager = new AsyncCompositionManager(mLayerManager); *aSuccess = true; *aTextureFactoryIdentifier = mCompositor->GetTextureFactoryIdentifier(); LayerTransactionParent* p = new LayerTransactionParent(mLayerManager, this, 0, // child side's process id is current process Id base::GetProcId(base::GetCurrentProcessHandle())); 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()); // Here main thread notifies compositor to remove an element from // sIndirectLayerTrees. This removed element might be queried soon. // Checking the elements of sIndirectLayerTrees exist or not before using. 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, ProcessId aOtherProcess) : mTransport(aTransport) , mChildProcessId(aOtherProcess) {} // 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 RecvRequestOverfill() MOZ_OVERRIDE { return true; } 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, uint32_t aPaintSequenceNumber) 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 void GetAPZTestData(const LayerTransactionParent* aLayerTree, APZTestData* aOutData) 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; // Child side's process Id. base::ProcessId mChildProcessId; }; 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, aOtherProcess); 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 */ CompositorParent::LayerTreeState* CompositorParent::GetIndirectShadowTree(uint64_t aId) { LayerTreeMap::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) { fprintf(stderr, " --- CrossProcessCompositorParent ActorDestroy\n"); 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); CompositorParent::LayerTreeState* state = nullptr; LayerTreeMap::iterator itr = sIndirectLayerTrees.find(aId); if (sIndirectLayerTrees.end() != itr) { state = &itr->second; } if (state && state->mLayerManager) { state->mCrossProcessParent = this; LayerManagerComposite* lm = state->mLayerManager; *aTextureFactoryIdentifier = lm->GetCompositor()->GetTextureFactoryIdentifier(); *aSuccess = true; LayerTransactionParent* p = new LayerTransactionParent(lm, this, aId, mChildProcessId); 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, mChildProcessId); 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) { const CompositorParent::LayerTreeState* state = CompositorParent::GetIndirectShadowTree(child); if (!state) { return false; } MOZ_ASSERT(state->mParent); state->mParent->NotifyChildCreated(child); return true; } void CrossProcessCompositorParent::ShadowLayersUpdated( LayerTransactionParent* aLayerTree, const TargetConfig& aTargetConfig, bool aIsFirstPaint, bool aScheduleComposite, uint32_t aPaintSequenceNumber) { uint64_t id = aLayerTree->GetId(); MOZ_ASSERT(id != 0); const CompositorParent::LayerTreeState* state = CompositorParent::GetIndirectShadowTree(id); if (!state) { return; } MOZ_ASSERT(state->mParent); state->mParent->ScheduleRotationOnCompositorThread(aTargetConfig, aIsFirstPaint); Layer* shadowRoot = aLayerTree->GetRoot(); if (shadowRoot) { SetShadowProperties(shadowRoot); } UpdateIndirectTree(id, shadowRoot, aTargetConfig); state->mParent->NotifyShadowTreeTransaction(id, aIsFirstPaint, aScheduleComposite, aPaintSequenceNumber); } 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); const CompositorParent::LayerTreeState* state = CompositorParent::GetIndirectShadowTree(id); if (!state) { return false; } MOZ_ASSERT(state->mParent); return state->mParent->SetTestSampleTime(aLayerTree, aTime); } void CrossProcessCompositorParent::LeaveTestMode(LayerTransactionParent* aLayerTree) { uint64_t id = aLayerTree->GetId(); MOZ_ASSERT(id != 0); const CompositorParent::LayerTreeState* state = CompositorParent::GetIndirectShadowTree(id); if (!state) { return; } MOZ_ASSERT(state->mParent); state->mParent->LeaveTestMode(aLayerTree); } void CrossProcessCompositorParent::GetAPZTestData(const LayerTransactionParent* aLayerTree, APZTestData* aOutData) { uint64_t id = aLayerTree->GetId(); MOZ_ASSERT(id != 0); *aOutData = sIndirectLayerTrees[id].mApzTestData; } AsyncCompositionManager* CrossProcessCompositorParent::GetCompositionManager(LayerTransactionParent* aLayerTree) { uint64_t id = aLayerTree->GetId(); const CompositorParent::LayerTreeState* state = CompositorParent::GetIndirectShadowTree(id); if (!state) { return nullptr; } MOZ_ASSERT(state->mParent); return state->mParent->GetCompositionManager(aLayerTree); } void CrossProcessCompositorParent::DeferredDestroy() { fprintf(stderr, " --- CrossProcessCompositorParent DeferredDestroy\n"); CrossProcessCompositorParent* self; mSelfRef.forget(&self); nsCOMPtr runnable = NS_NewNonOwningRunnableMethod(self, &CrossProcessCompositorParent::Release); MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable))); } CrossProcessCompositorParent::~CrossProcessCompositorParent() { fprintf(stderr, " --- CrossProcessCompositorParent destructor\n"); 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