diff options
Diffstat (limited to 'gfx/layers/ipc/CompositorBridgeParent.cpp')
-rw-r--r-- | gfx/layers/ipc/CompositorBridgeParent.cpp | 2441 |
1 files changed, 2441 insertions, 0 deletions
diff --git a/gfx/layers/ipc/CompositorBridgeParent.cpp b/gfx/layers/ipc/CompositorBridgeParent.cpp new file mode 100644 index 000000000..6728e8841 --- /dev/null +++ b/gfx/layers/ipc/CompositorBridgeParent.cpp @@ -0,0 +1,2441 @@ +/* -*- 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 "mozilla/layers/CompositorBridgeParent.h" +#include <stdio.h> // for fprintf, stdout +#include <stdint.h> // for uint64_t +#include <map> // for _Rb_tree_iterator, etc +#include <utility> // for pair +#include "LayerTransactionParent.h" // for LayerTransactionParent +#include "RenderTrace.h" // for RenderTraceLayers +#include "base/message_loop.h" // for MessageLoop +#include "base/process.h" // for ProcessId +#include "base/task.h" // for CancelableTask, etc +#include "base/thread.h" // for Thread +#include "gfxContext.h" // for gfxContext +#include "gfxPlatform.h" // for gfxPlatform +#include "TreeTraversal.h" // for ForEachNode +#ifdef MOZ_WIDGET_GTK +#include "gfxPlatformGtk.h" // for gfxPlatform +#endif +#include "gfxPrefs.h" // for gfxPrefs +#include "mozilla/AutoRestore.h" // for AutoRestore +#include "mozilla/ClearOnShutdown.h" // for ClearOnShutdown +#include "mozilla/DebugOnly.h" // for DebugOnly +#include "mozilla/dom/ContentParent.h" +#include "mozilla/dom/TabParent.h" +#include "mozilla/gfx/2D.h" // for DrawTarget +#include "mozilla/gfx/Point.h" // for IntSize +#include "mozilla/gfx/Rect.h" // for IntSize +#include "VRManager.h" // for VRManager +#include "mozilla/ipc/Transport.h" // for Transport +#include "mozilla/layers/APZCTreeManager.h" // for APZCTreeManager +#include "mozilla/layers/APZCTreeManagerParent.h" // for APZCTreeManagerParent +#include "mozilla/layers/APZThreadUtils.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/CompositorThread.h" +#include "mozilla/layers/CompositorTypes.h" +#include "mozilla/layers/CrossProcessCompositorBridgeParent.h" +#include "mozilla/layers/FrameUniformityData.h" +#include "mozilla/layers/ImageBridgeParent.h" +#include "mozilla/layers/LayerManagerComposite.h" +#include "mozilla/layers/LayerTreeOwnerTracker.h" +#include "mozilla/layers/LayersTypes.h" +#include "mozilla/layers/PLayerTransactionParent.h" +#include "mozilla/layers/RemoteContentController.h" +#include "mozilla/layout/RenderFrameParent.h" +#include "mozilla/media/MediaSystemResourceService.h" // for MediaSystemResourceService +#include "mozilla/mozalloc.h" // for operator new, etc +#include "mozilla/Telemetry.h" +#ifdef MOZ_WIDGET_GTK +#include "basic/X11BasicCompositor.h" // for X11BasicCompositor +#endif +#include "nsCOMPtr.h" // for already_AddRefed +#include "nsDebug.h" // for NS_ASSERTION, etc +#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc +#include "nsIWidget.h" // for nsIWidget +#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" +#include "mozilla/StaticPtr.h" +#include "mozilla/Telemetry.h" +#ifdef MOZ_ENABLE_PROFILER_SPS +#include "ProfilerMarkers.h" +#endif +#include "mozilla/VsyncDispatcher.h" +#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) +#include "VsyncSource.h" +#endif +#include "mozilla/widget/CompositorWidget.h" +#ifdef MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING +# include "mozilla/widget/CompositorWidgetParent.h" +#endif + +#include "LayerScope.h" + +namespace mozilla { + +namespace layers { + +using namespace mozilla::ipc; +using namespace mozilla::gfx; +using namespace std; + +using base::ProcessId; +using base::Thread; + +ProcessId +CompositorBridgeParentBase::GetChildProcessId() +{ + return OtherPid(); +} + +void +CompositorBridgeParentBase::NotifyNotUsed(PTextureParent* aTexture, uint64_t aTransactionId) +{ + RefPtr<TextureHost> texture = TextureHost::AsTextureHost(aTexture); + if (!texture) { + return; + } + + if (!(texture->GetFlags() & TextureFlags::RECYCLE)) { + return; + } + + uint64_t textureId = TextureHost::GetTextureSerial(aTexture); + mPendingAsyncMessage.push_back( + OpNotifyNotUsed(textureId, aTransactionId)); +} + +void +CompositorBridgeParentBase::SendAsyncMessage(const InfallibleTArray<AsyncParentMessageData>& aMessage) +{ + Unused << SendParentAsyncMessages(aMessage); +} + +bool +CompositorBridgeParentBase::AllocShmem(size_t aSize, + ipc::SharedMemory::SharedMemoryType aType, + ipc::Shmem* aShmem) +{ + return PCompositorBridgeParent::AllocShmem(aSize, aType, aShmem); +} + +bool +CompositorBridgeParentBase::AllocUnsafeShmem(size_t aSize, + ipc::SharedMemory::SharedMemoryType aType, + ipc::Shmem* aShmem) +{ + return PCompositorBridgeParent::AllocUnsafeShmem(aSize, aType, aShmem); +} + +void +CompositorBridgeParentBase::DeallocShmem(ipc::Shmem& aShmem) +{ + PCompositorBridgeParent::DeallocShmem(aShmem); +} + +base::ProcessId +CompositorBridgeParentBase::RemotePid() +{ + return OtherPid(); +} + +bool +CompositorBridgeParentBase::StartSharingMetrics(ipc::SharedMemoryBasic::Handle aHandle, + CrossProcessMutexHandle aMutexHandle, + uint64_t aLayersId, + uint32_t aApzcId) +{ + return PCompositorBridgeParent::SendSharedCompositorFrameMetrics( + aHandle, aMutexHandle, aLayersId, aApzcId); +} + +bool +CompositorBridgeParentBase::StopSharingMetrics(FrameMetrics::ViewID aScrollId, + uint32_t aApzcId) +{ + return PCompositorBridgeParent::SendReleaseSharedCompositorFrameMetrics( + aScrollId, aApzcId); +} + +CompositorBridgeParent::LayerTreeState::LayerTreeState() + : mApzcTreeManagerParent(nullptr) + , mParent(nullptr) + , mLayerManager(nullptr) + , mCrossProcessParent(nullptr) + , mLayerTree(nullptr) + , mUpdatedPluginDataAvailable(false) + , mPendingCompositorUpdates(0) +{ +} + +CompositorBridgeParent::LayerTreeState::~LayerTreeState() +{ + if (mController) { + mController->Destroy(); + } +} + +typedef map<uint64_t, CompositorBridgeParent::LayerTreeState> LayerTreeMap; +static LayerTreeMap sIndirectLayerTrees; +static StaticAutoPtr<mozilla::Monitor> sIndirectLayerTreesLock; + +static void EnsureLayerTreeMapReady() +{ + MOZ_ASSERT(NS_IsMainThread()); + if (!sIndirectLayerTreesLock) { + sIndirectLayerTreesLock = new Monitor("IndirectLayerTree"); + mozilla::ClearOnShutdown(&sIndirectLayerTreesLock); + } +} + +template <typename Lambda> +inline void +CompositorBridgeParent::ForEachIndirectLayerTree(const Lambda& aCallback) +{ + sIndirectLayerTreesLock->AssertCurrentThreadOwns(); + for (auto it = sIndirectLayerTrees.begin(); it != sIndirectLayerTrees.end(); it++) { + LayerTreeState* state = &it->second; + if (state->mParent == this) { + aCallback(state, it->first); + } + } +} + +/** + * A global map referencing each compositor by ID. + * + * This map is used by the ImageBridge protocol to trigger + * compositions without having to keep references to the + * compositor + */ +typedef map<uint64_t,CompositorBridgeParent*> CompositorMap; +static StaticAutoPtr<CompositorMap> sCompositorMap; + +void +CompositorBridgeParent::Setup() +{ + EnsureLayerTreeMapReady(); + + MOZ_ASSERT(!sCompositorMap); + sCompositorMap = new CompositorMap; +} + +void +CompositorBridgeParent::Shutdown() +{ + MOZ_ASSERT(sCompositorMap); + MOZ_ASSERT(sCompositorMap->empty()); + sCompositorMap = nullptr; +} + +void +CompositorBridgeParent::FinishShutdown() +{ + // TODO: this should be empty by now... + sIndirectLayerTrees.clear(); +} + +static void SetThreadPriority() +{ + hal::SetCurrentThreadPriority(hal::THREAD_PRIORITY_COMPOSITOR); +} + +#ifdef COMPOSITOR_PERFORMANCE_WARNING +static int32_t +CalculateCompositionFrameRate() +{ + // Used when layout.frame_rate is -1. Needs to be kept in sync with + // DEFAULT_FRAME_RATE in nsRefreshDriver.cpp. + // TODO: This should actually return the vsync rate. + const int32_t defaultFrameRate = 60; + 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 defaultFrameRate; + } + return layoutFrameRatePref; + } + return compositionFrameRatePref; +} +#endif + +CompositorVsyncScheduler::Observer::Observer(CompositorVsyncScheduler* aOwner) + : mMutex("CompositorVsyncScheduler.Observer.Mutex") + , mOwner(aOwner) +{ +} + +CompositorVsyncScheduler::Observer::~Observer() +{ + MOZ_ASSERT(!mOwner); +} + +bool +CompositorVsyncScheduler::Observer::NotifyVsync(TimeStamp aVsyncTimestamp) +{ + MutexAutoLock lock(mMutex); + if (!mOwner) { + return false; + } + return mOwner->NotifyVsync(aVsyncTimestamp); +} + +void +CompositorVsyncScheduler::Observer::Destroy() +{ + MutexAutoLock lock(mMutex); + mOwner = nullptr; +} + +CompositorVsyncScheduler::CompositorVsyncScheduler(CompositorBridgeParent* aCompositorBridgeParent, + widget::CompositorWidget* aWidget) + : mCompositorBridgeParent(aCompositorBridgeParent) + , mLastCompose(TimeStamp::Now()) + , mIsObservingVsync(false) + , mNeedsComposite(0) + , mVsyncNotificationsSkipped(0) + , mWidget(aWidget) + , mCurrentCompositeTaskMonitor("CurrentCompositeTaskMonitor") + , mCurrentCompositeTask(nullptr) + , mSetNeedsCompositeMonitor("SetNeedsCompositeMonitor") + , mSetNeedsCompositeTask(nullptr) +{ + MOZ_ASSERT(NS_IsMainThread() || XRE_GetProcessType() == GeckoProcessType_GPU); + mVsyncObserver = new Observer(this); + + // mAsapScheduling is set on the main thread during init, + // but is only accessed after on the compositor thread. + mAsapScheduling = gfxPrefs::LayersCompositionFrameRate() == 0 || + gfxPlatform::IsInLayoutAsapMode(); +} + +CompositorVsyncScheduler::~CompositorVsyncScheduler() +{ + MOZ_ASSERT(!mIsObservingVsync); + MOZ_ASSERT(!mVsyncObserver); + // The CompositorVsyncDispatcher is cleaned up before this in the nsBaseWidget, which stops vsync listeners + mCompositorBridgeParent = nullptr; +} + +void +CompositorVsyncScheduler::Destroy() +{ + if (!mVsyncObserver) { + // Destroy was already called on this object. + return; + } + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + UnobserveVsync(); + mVsyncObserver->Destroy(); + mVsyncObserver = nullptr; + + CancelCurrentSetNeedsCompositeTask(); + CancelCurrentCompositeTask(); +} + +void +CompositorVsyncScheduler::PostCompositeTask(TimeStamp aCompositeTimestamp) +{ + // can be called from the compositor or vsync thread + MonitorAutoLock lock(mCurrentCompositeTaskMonitor); + if (mCurrentCompositeTask == nullptr && CompositorThreadHolder::Loop()) { + RefPtr<CancelableRunnable> task = + NewCancelableRunnableMethod<TimeStamp>(this, &CompositorVsyncScheduler::Composite, + aCompositeTimestamp); + mCurrentCompositeTask = task; + ScheduleTask(task.forget(), 0); + } +} + +void +CompositorVsyncScheduler::ScheduleComposition() +{ + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + if (mAsapScheduling) { + // Used only for performance testing purposes + PostCompositeTask(TimeStamp::Now()); +#ifdef MOZ_WIDGET_ANDROID + } else if (mNeedsComposite >= 2 && mIsObservingVsync) { + // uh-oh, we already requested a composite at least twice so far, and a + // composite hasn't happened yet. It is possible that the vsync observation + // is blocked on the main thread, so let's just composite ASAP and not + // wait for the vsync. Note that this should only ever happen on Fennec + // because there content runs in the same process as the compositor, and so + // content can actually block the main thread in this process. + PostCompositeTask(TimeStamp::Now()); +#endif + } else { + SetNeedsComposite(); + } +} + +void +CompositorVsyncScheduler::CancelCurrentSetNeedsCompositeTask() +{ + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + MonitorAutoLock lock(mSetNeedsCompositeMonitor); + if (mSetNeedsCompositeTask) { + mSetNeedsCompositeTask->Cancel(); + mSetNeedsCompositeTask = nullptr; + } + mNeedsComposite = 0; +} + +/** + * TODO Potential performance heuristics: + * If a composite takes 17 ms, do we composite ASAP or wait until next vsync? + * If a layer transaction comes after vsync, do we composite ASAP or wait until + * next vsync? + * How many skipped vsync events until we stop listening to vsync events? + */ +void +CompositorVsyncScheduler::SetNeedsComposite() +{ + if (!CompositorThreadHolder::IsInCompositorThread()) { + MonitorAutoLock lock(mSetNeedsCompositeMonitor); + RefPtr<CancelableRunnable> task = + NewCancelableRunnableMethod(this, &CompositorVsyncScheduler::SetNeedsComposite); + mSetNeedsCompositeTask = task; + ScheduleTask(task.forget(), 0); + return; + } else { + MonitorAutoLock lock(mSetNeedsCompositeMonitor); + mSetNeedsCompositeTask = nullptr; + } + + mNeedsComposite++; + if (!mIsObservingVsync && mNeedsComposite) { + ObserveVsync(); + } +} + +bool +CompositorVsyncScheduler::NotifyVsync(TimeStamp aVsyncTimestamp) +{ + // Called from the vsync dispatch thread. When in the GPU Process, that's + // the same as the compositor thread. + MOZ_ASSERT_IF(XRE_IsParentProcess(), !CompositorThreadHolder::IsInCompositorThread()); + MOZ_ASSERT_IF(XRE_GetProcessType() == GeckoProcessType_GPU, CompositorThreadHolder::IsInCompositorThread()); + MOZ_ASSERT(!NS_IsMainThread()); + PostCompositeTask(aVsyncTimestamp); + return true; +} + +void +CompositorVsyncScheduler::CancelCurrentCompositeTask() +{ + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread() || NS_IsMainThread()); + MonitorAutoLock lock(mCurrentCompositeTaskMonitor); + if (mCurrentCompositeTask) { + mCurrentCompositeTask->Cancel(); + mCurrentCompositeTask = nullptr; + } +} + +void +CompositorVsyncScheduler::Composite(TimeStamp aVsyncTimestamp) +{ + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + { + MonitorAutoLock lock(mCurrentCompositeTaskMonitor); + mCurrentCompositeTask = nullptr; + } + + if ((aVsyncTimestamp < mLastCompose) && !mAsapScheduling) { + // We can sometimes get vsync timestamps that are in the past + // compared to the last compose with force composites. + // In those cases, wait until the next vsync; + return; + } + + MOZ_ASSERT(mCompositorBridgeParent); + if (!mAsapScheduling && mCompositorBridgeParent->IsPendingComposite()) { + // If previous composite is still on going, finish it and does a next + // composite in a next vsync. + mCompositorBridgeParent->FinishPendingComposite(); + return; + } + + DispatchTouchEvents(aVsyncTimestamp); + DispatchVREvents(aVsyncTimestamp); + + if (mNeedsComposite || mAsapScheduling) { + mNeedsComposite = 0; + mLastCompose = aVsyncTimestamp; + ComposeToTarget(nullptr); + mVsyncNotificationsSkipped = 0; + + TimeDuration compositeFrameTotal = TimeStamp::Now() - aVsyncTimestamp; + mozilla::Telemetry::Accumulate(mozilla::Telemetry::COMPOSITE_FRAME_ROUNDTRIP_TIME, + compositeFrameTotal.ToMilliseconds()); + } else if (mVsyncNotificationsSkipped++ > gfxPrefs::CompositorUnobserveCount()) { + UnobserveVsync(); + } +} + +void +CompositorVsyncScheduler::OnForceComposeToTarget() +{ + /** + * bug 1138502 - There are cases such as during long-running window resizing events + * where we receive many sync RecvFlushComposites. We also get vsync notifications which + * will increment mVsyncNotificationsSkipped because a composite just occurred. After + * enough vsyncs and RecvFlushComposites occurred, we will disable vsync. Then at the next + * ScheduleComposite, we will enable vsync, then get a RecvFlushComposite, which will + * force us to unobserve vsync again. On some platforms, enabling/disabling vsync is not + * free and this oscillating behavior causes a performance hit. In order to avoid this problem, + * we reset the mVsyncNotificationsSkipped counter to keep vsync enabled. + */ + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + mVsyncNotificationsSkipped = 0; +} + +void +CompositorVsyncScheduler::ForceComposeToTarget(gfx::DrawTarget* aTarget, const IntRect* aRect) +{ + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + OnForceComposeToTarget(); + mLastCompose = TimeStamp::Now(); + ComposeToTarget(aTarget, aRect); +} + +bool +CompositorVsyncScheduler::NeedsComposite() +{ + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + return mNeedsComposite; +} + +void +CompositorVsyncScheduler::ObserveVsync() +{ + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + mWidget->ObserveVsync(mVsyncObserver); + mIsObservingVsync = true; +} + +void +CompositorVsyncScheduler::UnobserveVsync() +{ + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + mWidget->ObserveVsync(nullptr); + mIsObservingVsync = false; +} + +void +CompositorVsyncScheduler::DispatchTouchEvents(TimeStamp aVsyncTimestamp) +{ +} + +void +CompositorVsyncScheduler::DispatchVREvents(TimeStamp aVsyncTimestamp) +{ + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + + VRManager* vm = VRManager::Get(); + vm->NotifyVsync(aVsyncTimestamp); +} + +void +CompositorVsyncScheduler::ScheduleTask(already_AddRefed<CancelableRunnable> aTask, + int aTime) +{ + MOZ_ASSERT(CompositorThreadHolder::Loop()); + MOZ_ASSERT(aTime >= 0); + CompositorThreadHolder::Loop()->PostDelayedTask(Move(aTask), aTime); +} + +void +CompositorVsyncScheduler::ResumeComposition() +{ + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + mLastCompose = TimeStamp::Now(); + ComposeToTarget(nullptr); +} + +void +CompositorVsyncScheduler::ComposeToTarget(gfx::DrawTarget* aTarget, const IntRect* aRect) +{ + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + MOZ_ASSERT(mCompositorBridgeParent); + mCompositorBridgeParent->CompositeToTarget(aTarget, aRect); +} + +static inline MessageLoop* +CompositorLoop() +{ + return CompositorThreadHolder::Loop(); +} + +CompositorBridgeParent::CompositorBridgeParent(CSSToLayoutDeviceScale aScale, + const TimeDuration& aVsyncRate, + bool aUseExternalSurfaceSize, + const gfx::IntSize& aSurfaceSize) + : mWidget(nullptr) + , mScale(aScale) + , mVsyncRate(aVsyncRate) + , mIsTesting(false) + , mPendingTransaction(0) + , mPaused(false) + , mUseExternalSurfaceSize(aUseExternalSurfaceSize) + , mEGLSurfaceSize(aSurfaceSize) + , mPauseCompositionMonitor("PauseCompositionMonitor") + , mResumeCompositionMonitor("ResumeCompositionMonitor") + , mResetCompositorMonitor("ResetCompositorMonitor") + , mRootLayerTreeID(0) + , mOverrideComposeReadiness(false) + , mForceCompositionTask(nullptr) + , mCompositorThreadHolder(CompositorThreadHolder::GetSingleton()) + , mCompositorScheduler(nullptr) + , mPaintTime(TimeDuration::Forever()) +#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) + , mLastPluginUpdateLayerTreeId(0) + , mDeferPluginWindows(false) + , mPluginWindowsHidden(false) +#endif +{ + // Always run destructor on the main thread + MOZ_ASSERT(NS_IsMainThread()); +} + +void +CompositorBridgeParent::InitSameProcess(widget::CompositorWidget* aWidget, + const uint64_t& aLayerTreeId, + bool aUseAPZ) +{ + mWidget = aWidget; + mRootLayerTreeID = aLayerTreeId; + if (aUseAPZ) { + mApzcTreeManager = new APZCTreeManager(); + } + + // IPDL initialization. mSelfRef is cleared in DeferredDestroy. + SetOtherProcessId(base::GetCurrentProcId()); + mSelfRef = this; + + Initialize(); +} + +bool +CompositorBridgeParent::Bind(Endpoint<PCompositorBridgeParent>&& aEndpoint) +{ + if (!aEndpoint.Bind(this)) { + return false; + } + mSelfRef = this; + return true; +} + +bool +CompositorBridgeParent::RecvInitialize(const uint64_t& aRootLayerTreeId) +{ + mRootLayerTreeID = aRootLayerTreeId; + + Initialize(); + return true; +} + +void +CompositorBridgeParent::Initialize() +{ + MOZ_ASSERT(CompositorThread(), + "The compositor thread must be Initialized before instanciating a CompositorBridgeParent."); + + 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. + MOZ_ASSERT(CompositorLoop()); + CompositorLoop()->PostTask(NewRunnableFunction(&AddCompositor, + this, &mCompositorID)); + + CompositorLoop()->PostTask(NewRunnableFunction(SetThreadPriority)); + + + { // scope lock + MonitorAutoLock lock(*sIndirectLayerTreesLock); + sIndirectLayerTrees[mRootLayerTreeID].mParent = this; + } + + LayerScope::SetPixelScale(mScale.scale); + + mCompositorScheduler = new CompositorVsyncScheduler(this, mWidget); +} + +bool +CompositorBridgeParent::RecvReset(nsTArray<LayersBackend>&& aBackendHints, bool* aResult, TextureFactoryIdentifier* aOutIdentifier) +{ + Maybe<TextureFactoryIdentifier> newIdentifier; + ResetCompositorTask(aBackendHints, &newIdentifier); + + if (newIdentifier) { + *aResult = true; + *aOutIdentifier = newIdentifier.value(); + } else { + *aResult = false; + } + + return true; +} + +uint64_t +CompositorBridgeParent::RootLayerTreeId() +{ + MOZ_ASSERT(mRootLayerTreeID); + return mRootLayerTreeID; +} + +CompositorBridgeParent::~CompositorBridgeParent() +{ + InfallibleTArray<PTextureParent*> textures; + ManagedPTextureParent(textures); + // We expect all textures to be destroyed by now. + MOZ_DIAGNOSTIC_ASSERT(textures.Length() == 0); + for (unsigned int i = 0; i < textures.Length(); ++i) { + RefPtr<TextureHost> tex = TextureHost::AsTextureHost(textures[i]); + tex->DeallocateDeviceData(); + } +} + +void +CompositorBridgeParent::ForceIsFirstPaint() +{ + mCompositionManager->ForceIsFirstPaint(); +} + +void +CompositorBridgeParent::StopAndClearResources() +{ + if (mForceCompositionTask) { + mForceCompositionTask->Cancel(); + mForceCompositionTask = nullptr; + } + + mPaused = true; + + // Ensure that the layer manager is destroyed before CompositorBridgeChild. + if (mLayerManager) { + MonitorAutoLock lock(*sIndirectLayerTreesLock); + ForEachIndirectLayerTree([this] (LayerTreeState* lts, uint64_t) -> void { + mLayerManager->ClearCachedResources(lts->mRoot); + lts->mLayerManager = nullptr; + lts->mParent = nullptr; + }); + mLayerManager->Destroy(); + mLayerManager = nullptr; + mCompositionManager = nullptr; + } + + if (mCompositor) { + mCompositor->DetachWidget(); + mCompositor->Destroy(); + mCompositor = nullptr; + } + + // This must be destroyed now since it accesses the widget. + if (mCompositorScheduler) { + mCompositorScheduler->Destroy(); + mCompositorScheduler = nullptr; + } + + // After this point, it is no longer legal to access the widget. + mWidget = nullptr; +} + +bool +CompositorBridgeParent::RecvWillClose() +{ + StopAndClearResources(); + return true; +} + +void CompositorBridgeParent::DeferredDestroy() +{ + MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(mCompositorThreadHolder); + mCompositorThreadHolder = nullptr; + mSelfRef = nullptr; +} + +bool +CompositorBridgeParent::RecvPause() +{ + PauseComposition(); + return true; +} + +bool +CompositorBridgeParent::RecvResume() +{ + ResumeComposition(); + return true; +} + +bool +CompositorBridgeParent::RecvMakeSnapshot(const SurfaceDescriptor& aInSnapshot, + const gfx::IntRect& aRect) +{ + RefPtr<DrawTarget> target = GetDrawTargetForDescriptor(aInSnapshot, gfx::BackendType::CAIRO); + MOZ_ASSERT(target); + if (!target) { + // We kill the content process rather than have it continue with an invalid + // snapshot, that may be too harsh and we could decide to return some sort + // of error to the child process and let it deal with it... + return false; + } + ForceComposeToTarget(target, &aRect); + return true; +} + +bool +CompositorBridgeParent::RecvFlushRendering() +{ + if (mCompositorScheduler->NeedsComposite()) + { + CancelCurrentCompositeTask(); + ForceComposeToTarget(nullptr); + } + return true; +} + +bool +CompositorBridgeParent::RecvForcePresent() +{ + // During the shutdown sequence mLayerManager may be null + if (mLayerManager) { + mLayerManager->ForcePresent(); + } + return true; +} + +bool +CompositorBridgeParent::RecvNotifyRegionInvalidated(const nsIntRegion& aRegion) +{ + if (mLayerManager) { + mLayerManager->AddInvalidRegion(aRegion); + } + return true; +} + +void +CompositorBridgeParent::Invalidate() +{ + if (mLayerManager && mLayerManager->GetRoot()) { + mLayerManager->AddInvalidRegion( + mLayerManager->GetRoot()->GetLocalVisibleRegion().ToUnknownRegion().GetBounds()); + } +} + +bool +CompositorBridgeParent::RecvStartFrameTimeRecording(const int32_t& aBufferSize, uint32_t* aOutStartIndex) +{ + if (mLayerManager) { + *aOutStartIndex = mLayerManager->StartFrameTimeRecording(aBufferSize); + } else { + *aOutStartIndex = 0; + } + return true; +} + +bool +CompositorBridgeParent::RecvStopFrameTimeRecording(const uint32_t& aStartIndex, + InfallibleTArray<float>* intervals) +{ + if (mLayerManager) { + mLayerManager->StopFrameTimeRecording(aStartIndex, *intervals); + } + return true; +} + +bool +CompositorBridgeParent::RecvClearApproximatelyVisibleRegions(const uint64_t& aLayersId, + const uint32_t& aPresShellId) +{ + ClearApproximatelyVisibleRegions(aLayersId, Some(aPresShellId)); + return true; +} + +void +CompositorBridgeParent::ClearApproximatelyVisibleRegions(const uint64_t& aLayersId, + const Maybe<uint32_t>& aPresShellId) +{ + if (mLayerManager) { + mLayerManager->ClearApproximatelyVisibleRegions(aLayersId, aPresShellId); + + // We need to recomposite to update the minimap. + ScheduleComposition(); + } +} + +bool +CompositorBridgeParent::RecvNotifyApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid, + const CSSIntRegion& aRegion) +{ + if (mLayerManager) { + mLayerManager->UpdateApproximatelyVisibleRegion(aGuid, aRegion); + + // We need to recomposite to update the minimap. + ScheduleComposition(); + } + return true; +} + +void +CompositorBridgeParent::ActorDestroy(ActorDestroyReason why) +{ + StopAndClearResources(); + + RemoveCompositor(mCompositorID); + + mCompositionManager = nullptr; + + if (mApzcTreeManager) { + mApzcTreeManager->ClearTree(); + mApzcTreeManager = nullptr; + } + + { // scope lock + MonitorAutoLock lock(*sIndirectLayerTreesLock); + sIndirectLayerTrees.erase(mRootLayerTreeID); + } + + // 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. + mSelfRef = this; + MessageLoop::current()->PostTask(NewRunnableMethod(this, &CompositorBridgeParent::DeferredDestroy)); +} + +void +CompositorBridgeParent::ScheduleRenderOnCompositorThread() +{ + MOZ_ASSERT(CompositorLoop()); + CompositorLoop()->PostTask(NewRunnableMethod(this, &CompositorBridgeParent::ScheduleComposition)); +} + +void +CompositorBridgeParent::InvalidateOnCompositorThread() +{ + MOZ_ASSERT(CompositorLoop()); + CompositorLoop()->PostTask(NewRunnableMethod(this, &CompositorBridgeParent::Invalidate)); +} + +void +CompositorBridgeParent::PauseComposition() +{ + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread(), + "PauseComposition() can only be called on the compositor thread"); + + MonitorAutoLock lock(mPauseCompositionMonitor); + + if (!mPaused) { + mPaused = true; + + mCompositor->Pause(); + + TimeStamp now = TimeStamp::Now(); + DidComposite(now, now); + } + + // if anyone's waiting to make sure that composition really got paused, tell them + lock.NotifyAll(); +} + +void +CompositorBridgeParent::ResumeComposition() +{ + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread(), + "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, "CompositorBridgeParent", "Unable to renew compositor surface; remaining in paused state"); +#endif + lock.NotifyAll(); + return; + } + + mPaused = false; + + Invalidate(); + mCompositorScheduler->ResumeComposition(); + + // if anyone's waiting to make sure that composition really got resumed, tell them + lock.NotifyAll(); +} + +void +CompositorBridgeParent::ForceComposition() +{ + // Cancel the orientation changed state to force composition + mForceCompositionTask = nullptr; + ScheduleRenderOnCompositorThread(); +} + +void +CompositorBridgeParent::CancelCurrentCompositeTask() +{ + mCompositorScheduler->CancelCurrentCompositeTask(); +} + +void +CompositorBridgeParent::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 +CompositorBridgeParent::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 +CompositorBridgeParent::SchedulePauseOnCompositorThread() +{ + MonitorAutoLock lock(mPauseCompositionMonitor); + + MOZ_ASSERT(CompositorLoop()); + CompositorLoop()->PostTask(NewRunnableMethod(this, &CompositorBridgeParent::PauseComposition)); + + // Wait until the pause has actually been processed by the compositor thread + lock.Wait(); +} + +bool +CompositorBridgeParent::ScheduleResumeOnCompositorThread() +{ + MonitorAutoLock lock(mResumeCompositionMonitor); + + MOZ_ASSERT(CompositorLoop()); + CompositorLoop()->PostTask(NewRunnableMethod(this, &CompositorBridgeParent::ResumeComposition)); + + // Wait until the resume has actually been processed by the compositor thread + lock.Wait(); + + return !mPaused; +} + +bool +CompositorBridgeParent::ScheduleResumeOnCompositorThread(int width, int height) +{ + MonitorAutoLock lock(mResumeCompositionMonitor); + + MOZ_ASSERT(CompositorLoop()); + CompositorLoop()->PostTask(NewRunnableMethod + <int, int>(this, + &CompositorBridgeParent::ResumeCompositionAndResize, + width, height)); + + // Wait until the resume has actually been processed by the compositor thread + lock.Wait(); + + return !mPaused; +} + +void +CompositorBridgeParent::ScheduleTask(already_AddRefed<CancelableRunnable> task, int time) +{ + if (time == 0) { + MessageLoop::current()->PostTask(Move(task)); + } else { + MessageLoop::current()->PostDelayedTask(Move(task), time); + } +} + +void +CompositorBridgeParent::UpdatePaintTime(LayerTransactionParent* aLayerTree, + const TimeDuration& aPaintTime) +{ + // We get a lot of paint timings for things with empty transactions. + if (!mLayerManager || aPaintTime.ToMilliseconds() < 1.0) { + return; + } + + mLayerManager->SetPaintTime(aPaintTime); +} + +void +CompositorBridgeParent::NotifyShadowTreeTransaction(uint64_t aId, bool aIsFirstPaint, + bool aScheduleComposite, uint32_t aPaintSequenceNumber, + bool aIsRepeatTransaction, bool aHitTestUpdate) +{ + if (!aIsRepeatTransaction && + mLayerManager && + mLayerManager->GetRoot()) { + // Process plugin data here to give time for them to update before the next + // composition. + bool pluginsUpdatedFlag = true; + AutoResolveRefLayers resolve(mCompositionManager, this, nullptr, + &pluginsUpdatedFlag); + +#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) + // If plugins haven't been updated, stop waiting. + if (!pluginsUpdatedFlag) { + mWaitForPluginsUntil = TimeStamp(); + mHaveBlockedForPlugins = false; + } +#endif + + if (mApzcTreeManager && aHitTestUpdate) { + mApzcTreeManager->UpdateHitTestingTree(mRootLayerTreeID, + mLayerManager->GetRoot(), aIsFirstPaint, aId, aPaintSequenceNumber); + } + + mLayerManager->NotifyShadowTreeTransaction(); + } + if (aScheduleComposite) { + ScheduleComposition(); + } +} + +void +CompositorBridgeParent::ScheduleComposition() +{ + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + if (mPaused) { + return; + } + + mCompositorScheduler->ScheduleComposition(); +} + +// Go down the composite layer tree, setting properties to match their +// content-side counterparts. +/* static */ void +CompositorBridgeParent::SetShadowProperties(Layer* aLayer) +{ + ForEachNode<ForwardIterator>( + aLayer, + [] (Layer *layer) + { + if (Layer* maskLayer = layer->GetMaskLayer()) { + SetShadowProperties(maskLayer); + } + for (size_t i = 0; i < layer->GetAncestorMaskLayerCount(); i++) { + SetShadowProperties(layer->GetAncestorMaskLayerAt(i)); + } + + // FIXME: Bug 717688 -- Do these updates in LayerTransactionParent::RecvUpdate. + LayerComposite* layerComposite = layer->AsLayerComposite(); + // Set the layerComposite's base transform to the layer's base transform. + layerComposite->SetShadowBaseTransform(layer->GetBaseTransform()); + layerComposite->SetShadowTransformSetByAnimation(false); + layerComposite->SetShadowVisibleRegion(layer->GetVisibleRegion()); + layerComposite->SetShadowClipRect(layer->GetClipRect()); + layerComposite->SetShadowOpacity(layer->GetOpacity()); + layerComposite->SetShadowOpacitySetByAnimation(false); + } + ); +} + +void +CompositorBridgeParent::CompositeToTarget(DrawTarget* aTarget, const gfx::IntRect* aRect) +{ + profiler_tracing("Paint", "Composite", TRACING_INTERVAL_START); + PROFILER_LABEL("CompositorBridgeParent", "Composite", + js::ProfileEntry::Category::GRAPHICS); + + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread(), + "Composite can only be called on the compositor thread"); + TimeStamp start = TimeStamp::Now(); + +#ifdef COMPOSITOR_PERFORMANCE_WARNING + TimeDuration scheduleDelta = TimeStamp::Now() - mCompositorScheduler->GetExpectedComposeStartTime(); + 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 (!CanComposite()) { + TimeStamp end = TimeStamp::Now(); + DidComposite(start, end); + return; + } + +#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) + if (!mWaitForPluginsUntil.IsNull() && + mWaitForPluginsUntil > start) { + mHaveBlockedForPlugins = true; + ScheduleComposition(); + return; + } +#endif + + /* + * AutoResolveRefLayers handles two tasks related to Windows and Linux + * plugin window management: + * 1) calculating if we have remote content in the view. If we do not have + * remote content, all plugin windows for this CompositorBridgeParent (window) + * can be hidden since we do not support plugins in chrome when running + * under e10s. + * 2) Updating plugin position, size, and clip. We do this here while the + * remote layer tree is hooked up to to chrome layer tree. This is needed + * since plugin clipping can depend on chrome (for example, due to tab modal + * prompts). Updates in step 2 are applied via an async ipc message sent + * to the main thread. + */ + bool hasRemoteContent = false; + bool updatePluginsFlag = true; + AutoResolveRefLayers resolve(mCompositionManager, this, + &hasRemoteContent, + &updatePluginsFlag); + +#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) + // We do not support plugins in local content. When switching tabs + // to local pages, hide every plugin associated with the window. + if (!hasRemoteContent && gfxVars::BrowserTabsRemoteAutostart() && + mCachedPluginData.Length()) { + Unused << SendHideAllPlugins(GetWidget()->GetWidgetKey()); + mCachedPluginData.Clear(); + } +#endif + + if (aTarget) { + mLayerManager->BeginTransactionWithDrawTarget(aTarget, *aRect); + } else { + mLayerManager->BeginTransaction(); + } + + SetShadowProperties(mLayerManager->GetRoot()); + + if (mForceCompositionTask && !mOverrideComposeReadiness) { + if (mCompositionManager->ReadyForCompose()) { + mForceCompositionTask->Cancel(); + mForceCompositionTask = nullptr; + } else { + return; + } + } + + mCompositionManager->ComputeRotation(); + + TimeStamp time = mIsTesting ? mTestTime : mCompositorScheduler->GetLastComposeTime(); + bool requestNextFrame = mCompositionManager->TransformShadowTree(time, mVsyncRate); + if (requestNextFrame) { + ScheduleComposition(); +#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) + // If we have visible windowed plugins then we need to wait for content (and + // then the plugins) to have been updated by the active animation. + if (!mPluginWindowsHidden && mCachedPluginData.Length()) { + mWaitForPluginsUntil = mCompositorScheduler->GetLastComposeTime() + (mVsyncRate * 2); + } +#endif + } + + RenderTraceLayers(mLayerManager->GetRoot(), "0000"); + +#ifdef MOZ_DUMP_PAINTING + if (gfxPrefs::DumpHostLayers()) { + printf_stderr("Painting --- compositing layer tree:\n"); + mLayerManager->Dump(/* aSorted = */ true); + } +#endif + mLayerManager->SetDebugOverlayWantsNextFrame(false); + mLayerManager->EndTransaction(time); + + if (!aTarget) { + TimeStamp end = TimeStamp::Now(); + DidComposite(start, end); + } + + // We're not really taking advantage of the stored composite-again-time here. + // We might be able to skip the next few composites altogether. However, + // that's a bit complex to implement and we'll get most of the advantage + // by skipping compositing when we detect there's nothing invalid. This is why + // we do "composite until" rather than "composite again at". + if (!mCompositor->GetCompositeUntilTime().IsNull() || + mLayerManager->DebugOverlayWantsNextFrame()) { + ScheduleComposition(); + } + +#ifdef COMPOSITOR_PERFORMANCE_WARNING + TimeDuration executionTime = TimeStamp::Now() - mCompositorScheduler->GetLastComposeTime(); + 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(); + } + mCompositor->SetCompositionTime(TimeStamp()); + + mozilla::Telemetry::AccumulateTimeDelta(mozilla::Telemetry::COMPOSITE_TIME, start); + profiler_tracing("Paint", "Composite", TRACING_INTERVAL_END); +} + +bool +CompositorBridgeParent::RecvRemotePluginsReady() +{ +#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) + mWaitForPluginsUntil = TimeStamp(); + if (mHaveBlockedForPlugins) { + mHaveBlockedForPlugins = false; + ForceComposeToTarget(nullptr); + } else { + ScheduleComposition(); + } + return true; +#else + NS_NOTREACHED("CompositorBridgeParent::RecvRemotePluginsReady calls " + "unexpected on this platform."); + return false; +#endif +} + +void +CompositorBridgeParent::ForceComposeToTarget(DrawTarget* aTarget, const gfx::IntRect* aRect) +{ + PROFILER_LABEL("CompositorBridgeParent", "ForceComposeToTarget", + js::ProfileEntry::Category::GRAPHICS); + + AutoRestore<bool> override(mOverrideComposeReadiness); + mOverrideComposeReadiness = true; + mCompositorScheduler->ForceComposeToTarget(aTarget, aRect); +} + +PAPZCTreeManagerParent* +CompositorBridgeParent::AllocPAPZCTreeManagerParent(const uint64_t& aLayersId) +{ + // The main process should pass in 0 because we assume mRootLayerTreeID + MOZ_ASSERT(aLayersId == 0); + + // This message doubles as initialization + MOZ_ASSERT(!mApzcTreeManager); + mApzcTreeManager = new APZCTreeManager(); + + MonitorAutoLock lock(*sIndirectLayerTreesLock); + CompositorBridgeParent::LayerTreeState& state = sIndirectLayerTrees[mRootLayerTreeID]; + MOZ_ASSERT(state.mParent); + MOZ_ASSERT(!state.mApzcTreeManagerParent); + state.mApzcTreeManagerParent = new APZCTreeManagerParent(mRootLayerTreeID, state.mParent->GetAPZCTreeManager()); + + return state.mApzcTreeManagerParent; +} + +bool +CompositorBridgeParent::DeallocPAPZCTreeManagerParent(PAPZCTreeManagerParent* aActor) +{ + delete aActor; + return true; +} + +PAPZParent* +CompositorBridgeParent::AllocPAPZParent(const uint64_t& aLayersId) +{ + // The main process should pass in 0 because we assume mRootLayerTreeID + MOZ_ASSERT(aLayersId == 0); + + RemoteContentController* controller = new RemoteContentController(); + + // Increment the controller's refcount before we return it. This will keep the + // controller alive until it is released by IPDL in DeallocPAPZParent. + controller->AddRef(); + + MonitorAutoLock lock(*sIndirectLayerTreesLock); + CompositorBridgeParent::LayerTreeState& state = sIndirectLayerTrees[mRootLayerTreeID]; + MOZ_ASSERT(!state.mController); + state.mController = controller; + + return controller; +} + +bool +CompositorBridgeParent::DeallocPAPZParent(PAPZParent* aActor) +{ + RemoteContentController* controller = static_cast<RemoteContentController*>(aActor); + controller->Release(); + return true; +} + +bool +CompositorBridgeParent::RecvAsyncPanZoomEnabled(const uint64_t& aLayersId, bool* aHasAPZ) +{ + // The main process should pass in 0 because we assume mRootLayerTreeID + MOZ_ASSERT(aLayersId == 0); + *aHasAPZ = AsyncPanZoomEnabled(); + return true; +} + +RefPtr<APZCTreeManager> +CompositorBridgeParent::GetAPZCTreeManager() +{ + return mApzcTreeManager; +} + +bool +CompositorBridgeParent::CanComposite() +{ + return mLayerManager && + mLayerManager->GetRoot() && + !mPaused; +} + +void +CompositorBridgeParent::ScheduleRotationOnCompositorThread(const TargetConfig& aTargetConfig, + bool aIsFirstPaint) +{ + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + + if (!aIsFirstPaint && + !mCompositionManager->IsFirstPaint() && + mCompositionManager->RequiresReorientation(aTargetConfig.orientation())) { + if (mForceCompositionTask != nullptr) { + mForceCompositionTask->Cancel(); + } + RefPtr<CancelableRunnable> task = + NewCancelableRunnableMethod(this, &CompositorBridgeParent::ForceComposition); + mForceCompositionTask = task; + ScheduleTask(task.forget(), gfxPrefs::OrientationSyncMillis()); + } +} + +void +CompositorBridgeParent::ShadowLayersUpdated(LayerTransactionParent* aLayerTree, + const uint64_t& aTransactionId, + const TargetConfig& aTargetConfig, + const InfallibleTArray<PluginWindowData>& aUnused, + bool aIsFirstPaint, + bool aScheduleComposite, + uint32_t aPaintSequenceNumber, + bool aIsRepeatTransaction, + int32_t aPaintSyncId, + bool aHitTestUpdate) +{ + 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.naturalBounds()); + mLayerManager->SetRegionToClear(aTargetConfig.clearRegion()); + mLayerManager->GetCompositor()->SetScreenRotation(aTargetConfig.rotation()); + + mCompositionManager->Updated(aIsFirstPaint, aTargetConfig, aPaintSyncId); + Layer* root = aLayerTree->GetRoot(); + mLayerManager->SetRoot(root); + + if (mApzcTreeManager && !aIsRepeatTransaction && aHitTestUpdate) { + AutoResolveRefLayers resolve(mCompositionManager); + + mApzcTreeManager->UpdateHitTestingTree(mRootLayerTreeID, root, aIsFirstPaint, + mRootLayerTreeID, aPaintSequenceNumber); + } + + // The transaction ID might get reset to 1 if the page gets reloaded, see + // https://bugzilla.mozilla.org/show_bug.cgi?id=1145295#c41 + // Otherwise, it should be continually increasing. + MOZ_ASSERT(aTransactionId == 1 || aTransactionId > mPendingTransaction); + mPendingTransaction = aTransactionId; + + if (root) { + SetShadowProperties(root); + } + if (aScheduleComposite) { + ScheduleComposition(); + if (mPaused) { + TimeStamp now = TimeStamp::Now(); + DidComposite(now, now); + } + } + mLayerManager->NotifyShadowTreeTransaction(); +} + +void +CompositorBridgeParent::ForceComposite(LayerTransactionParent* aLayerTree) +{ + ScheduleComposition(); +} + +bool +CompositorBridgeParent::SetTestSampleTime(LayerTransactionParent* aLayerTree, + const TimeStamp& aTime) +{ + if (aTime.IsNull()) { + return false; + } + + mIsTesting = true; + mTestTime = aTime; + + bool testComposite = mCompositionManager && + mCompositorScheduler->NeedsComposite(); + + // Update but only if we were already scheduled to animate + if (testComposite) { + AutoResolveRefLayers resolve(mCompositionManager); + bool requestNextFrame = mCompositionManager->TransformShadowTree(aTime, mVsyncRate); + if (!requestNextFrame) { + CancelCurrentCompositeTask(); + // Pretend we composited in case someone is wating for this event. + TimeStamp now = TimeStamp::Now(); + DidComposite(now, now); + } + } + + return true; +} + +void +CompositorBridgeParent::LeaveTestMode(LayerTransactionParent* aLayerTree) +{ + mIsTesting = false; +} + +void +CompositorBridgeParent::ApplyAsyncProperties(LayerTransactionParent* aLayerTree) +{ + // NOTE: This should only be used for testing. For example, when mIsTesting is + // true or when called from test-only methods like + // LayerTransactionParent::RecvGetAnimationTransform. + + // Synchronously update the layer tree + if (aLayerTree->GetRoot()) { + AutoResolveRefLayers resolve(mCompositionManager); + SetShadowProperties(mLayerManager->GetRoot()); + + TimeStamp time = mIsTesting ? mTestTime : mCompositorScheduler->GetLastComposeTime(); + bool requestNextFrame = + mCompositionManager->TransformShadowTree(time, mVsyncRate, + AsyncCompositionManager::TransformsToSkip::APZ); + if (!requestNextFrame) { + CancelCurrentCompositeTask(); + // Pretend we composited in case someone is waiting for this event. + TimeStamp now = TimeStamp::Now(); + DidComposite(now, now); + } + } +} + +bool +CompositorBridgeParent::RecvGetFrameUniformity(FrameUniformityData* aOutData) +{ + mCompositionManager->GetFrameUniformity(aOutData); + return true; +} + +bool +CompositorBridgeParent::RecvRequestOverfill() +{ + uint32_t overfillRatio = mCompositor->GetFillRatio(); + Unused << SendOverfill(overfillRatio); + return true; +} + +void +CompositorBridgeParent::FlushApzRepaints(const LayerTransactionParent* aLayerTree) +{ + MOZ_ASSERT(mApzcTreeManager); + uint64_t layersId = aLayerTree->GetId(); + if (layersId == 0) { + // The request is coming from the parent-process layer tree, so we should + // use the compositor's root layer tree id. + layersId = mRootLayerTreeID; + } + RefPtr<CompositorBridgeParent> self = this; + APZThreadUtils::RunOnControllerThread(NS_NewRunnableFunction([=] () { + self->mApzcTreeManager->FlushApzRepaints(layersId); + })); +} + +void +CompositorBridgeParent::GetAPZTestData(const LayerTransactionParent* aLayerTree, + APZTestData* aOutData) +{ + MonitorAutoLock lock(*sIndirectLayerTreesLock); + *aOutData = sIndirectLayerTrees[mRootLayerTreeID].mApzTestData; +} + +void +CompositorBridgeParent::SetConfirmedTargetAPZC(const LayerTransactionParent* aLayerTree, + const uint64_t& aInputBlockId, + const nsTArray<ScrollableLayerGuid>& aTargets) +{ + if (!mApzcTreeManager) { + return; + } + // Need to specifically bind this since it's overloaded. + void (APZCTreeManager::*setTargetApzcFunc) + (uint64_t, const nsTArray<ScrollableLayerGuid>&) = + &APZCTreeManager::SetTargetAPZC; + RefPtr<Runnable> task = NewRunnableMethod + <uint64_t, StoreCopyPassByConstLRef<nsTArray<ScrollableLayerGuid>>> + (mApzcTreeManager.get(), setTargetApzcFunc, aInputBlockId, aTargets); + APZThreadUtils::RunOnControllerThread(task.forget()); + +} + +void +CompositorBridgeParent::InitializeLayerManager(const nsTArray<LayersBackend>& aBackendHints) +{ + NS_ASSERTION(!mLayerManager, "Already initialised mLayerManager"); + NS_ASSERTION(!mCompositor, "Already initialised mCompositor"); + + mCompositor = NewCompositor(aBackendHints); + if (!mCompositor) { + return; + } + + mLayerManager = new LayerManagerComposite(mCompositor); + + MonitorAutoLock lock(*sIndirectLayerTreesLock); + sIndirectLayerTrees[mRootLayerTreeID].mLayerManager = mLayerManager; +} + +RefPtr<Compositor> +CompositorBridgeParent::NewCompositor(const nsTArray<LayersBackend>& aBackendHints) +{ + for (size_t i = 0; i < aBackendHints.Length(); ++i) { + RefPtr<Compositor> compositor; + if (aBackendHints[i] == LayersBackend::LAYERS_OPENGL) { + compositor = new CompositorOGL(this, + mWidget, + mEGLSurfaceSize.width, + mEGLSurfaceSize.height, + mUseExternalSurfaceSize); + } else if (aBackendHints[i] == LayersBackend::LAYERS_BASIC) { +#ifdef MOZ_WIDGET_GTK + if (gfxVars::UseXRender()) { + compositor = new X11BasicCompositor(this, mWidget); + } else +#endif + { + compositor = new BasicCompositor(this, mWidget); + } +#ifdef XP_WIN + } else if (aBackendHints[i] == LayersBackend::LAYERS_D3D11) { + compositor = new CompositorD3D11(this, mWidget); + } else if (aBackendHints[i] == LayersBackend::LAYERS_D3D9) { + compositor = new CompositorD3D9(this, mWidget); +#endif + } + nsCString failureReason; + if (compositor && compositor->Initialize(&failureReason)) { + if (failureReason.IsEmpty()){ + failureReason = "SUCCESS"; + } + + // should only report success here + if (aBackendHints[i] == LayersBackend::LAYERS_OPENGL){ + Telemetry::Accumulate(Telemetry::OPENGL_COMPOSITING_FAILURE_ID, failureReason); + } +#ifdef XP_WIN + else if (aBackendHints[i] == LayersBackend::LAYERS_D3D9){ + Telemetry::Accumulate(Telemetry::D3D9_COMPOSITING_FAILURE_ID, failureReason); + } + else if (aBackendHints[i] == LayersBackend::LAYERS_D3D11){ + Telemetry::Accumulate(Telemetry::D3D11_COMPOSITING_FAILURE_ID, failureReason); + } +#endif + + compositor->SetCompositorID(mCompositorID); + return compositor; + } + + // report any failure reasons here + if (aBackendHints[i] == LayersBackend::LAYERS_OPENGL){ + gfxCriticalNote << "[OPENGL] Failed to init compositor with reason: " + << failureReason.get(); + Telemetry::Accumulate(Telemetry::OPENGL_COMPOSITING_FAILURE_ID, failureReason); + } +#ifdef XP_WIN + else if (aBackendHints[i] == LayersBackend::LAYERS_D3D9){ + gfxCriticalNote << "[D3D9] Failed to init compositor with reason: " + << failureReason.get(); + Telemetry::Accumulate(Telemetry::D3D9_COMPOSITING_FAILURE_ID, failureReason); + } + else if (aBackendHints[i] == LayersBackend::LAYERS_D3D11){ + gfxCriticalNote << "[D3D11] Failed to init compositor with reason: " + << failureReason.get(); + Telemetry::Accumulate(Telemetry::D3D11_COMPOSITING_FAILURE_ID, failureReason); + } +#endif + } + + return nullptr; +} + +PLayerTransactionParent* +CompositorBridgeParent::AllocPLayerTransactionParent(const nsTArray<LayersBackend>& aBackendHints, + const uint64_t& aId, + TextureFactoryIdentifier* aTextureFactoryIdentifier, + bool *aSuccess) +{ + MOZ_ASSERT(aId == 0); + + InitializeLayerManager(aBackendHints); + + 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 +CompositorBridgeParent::DeallocPLayerTransactionParent(PLayerTransactionParent* actor) +{ + static_cast<LayerTransactionParent*>(actor)->ReleaseIPDLReference(); + return true; +} + +CompositorBridgeParent* CompositorBridgeParent::GetCompositorBridgeParent(uint64_t id) +{ + CompositorMap::iterator it = sCompositorMap->find(id); + return it != sCompositorMap->end() ? it->second : nullptr; +} + +void CompositorBridgeParent::AddCompositor(CompositorBridgeParent* compositor, uint64_t* outID) +{ + static uint64_t sNextID = 1; + + ++sNextID; + (*sCompositorMap)[sNextID] = compositor; + *outID = sNextID; +} + +CompositorBridgeParent* CompositorBridgeParent::RemoveCompositor(uint64_t id) +{ + CompositorMap::iterator it = sCompositorMap->find(id); + if (it == sCompositorMap->end()) { + return nullptr; + } + CompositorBridgeParent *retval = it->second; + sCompositorMap->erase(it); + return retval; +} + +void +CompositorBridgeParent::NotifyVsync(const TimeStamp& aTimeStamp, const uint64_t& aLayersId) +{ + MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_GPU); + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + + MonitorAutoLock lock(*sIndirectLayerTreesLock); + auto it = sIndirectLayerTrees.find(aLayersId); + if (it == sIndirectLayerTrees.end()) + return; + + CompositorBridgeParent* cbp = it->second.mParent; + if (!cbp || !cbp->mWidget) + return; + + RefPtr<VsyncObserver> obs = cbp->mWidget->GetVsyncObserver(); + if (!obs) + return; + + obs->NotifyVsync(aTimeStamp); +} + +bool +CompositorBridgeParent::RecvNotifyChildCreated(const uint64_t& child) +{ + MonitorAutoLock lock(*sIndirectLayerTreesLock); + NotifyChildCreated(child); + return true; +} + +bool +CompositorBridgeParent::RecvNotifyChildRecreated(const uint64_t& aChild) +{ + MonitorAutoLock lock(*sIndirectLayerTreesLock); + + if (sIndirectLayerTrees.find(aChild) != sIndirectLayerTrees.end()) { + // Invalid to register the same layer tree twice. + return false; + } + + NotifyChildCreated(aChild); + return true; +} + +void +CompositorBridgeParent::NotifyChildCreated(uint64_t aChild) +{ + sIndirectLayerTreesLock->AssertCurrentThreadOwns(); + sIndirectLayerTrees[aChild].mParent = this; + sIndirectLayerTrees[aChild].mLayerManager = mLayerManager; +} + +bool +CompositorBridgeParent::RecvAdoptChild(const uint64_t& child) +{ + APZCTreeManagerParent* parent; + { + MonitorAutoLock lock(*sIndirectLayerTreesLock); + NotifyChildCreated(child); + if (sIndirectLayerTrees[child].mLayerTree) { + sIndirectLayerTrees[child].mLayerTree->SetLayerManager(mLayerManager); + } + parent = sIndirectLayerTrees[child].mApzcTreeManagerParent; + } + + if (mApzcTreeManager && parent) { + parent->ChildAdopted(mApzcTreeManager); + } + return true; +} + +static void +EraseLayerState(uint64_t aId) +{ + MonitorAutoLock lock(*sIndirectLayerTreesLock); + + auto iter = sIndirectLayerTrees.find(aId); + if (iter != sIndirectLayerTrees.end()) { + CompositorBridgeParent* parent = iter->second.mParent; + if (parent) { + parent->ClearApproximatelyVisibleRegions(aId, Nothing()); + } + + sIndirectLayerTrees.erase(iter); + } +} + +/*static*/ void +CompositorBridgeParent::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. + if (!CompositorLoop()) { + gfxCriticalError() << "Attempting to post to a invalid Compositor Loop"; + return; + } + CompositorLoop()->PostTask(NewRunnableFunction(&EraseLayerState, aId)); +} + +static void +UpdateControllerForLayersId(uint64_t aLayersId, + GeckoContentController* aController) +{ + // Adopt ref given to us by SetControllerForLayerTree() + MonitorAutoLock lock(*sIndirectLayerTreesLock); + sIndirectLayerTrees[aLayersId].mController = + already_AddRefed<GeckoContentController>(aController); +} + +ScopedLayerTreeRegistration::ScopedLayerTreeRegistration(APZCTreeManager* aApzctm, + uint64_t aLayersId, + Layer* aRoot, + GeckoContentController* aController) + : mLayersId(aLayersId) +{ + EnsureLayerTreeMapReady(); + MonitorAutoLock lock(*sIndirectLayerTreesLock); + sIndirectLayerTrees[aLayersId].mRoot = aRoot; + sIndirectLayerTrees[aLayersId].mController = aController; +} + +ScopedLayerTreeRegistration::~ScopedLayerTreeRegistration() +{ + MonitorAutoLock lock(*sIndirectLayerTreesLock); + sIndirectLayerTrees.erase(mLayersId); +} + +/*static*/ void +CompositorBridgeParent::SetControllerForLayerTree(uint64_t aLayersId, + GeckoContentController* aController) +{ + // This ref is adopted by UpdateControllerForLayersId(). + aController->AddRef(); + CompositorLoop()->PostTask(NewRunnableFunction(&UpdateControllerForLayersId, + aLayersId, + aController)); +} + +/*static*/ already_AddRefed<APZCTreeManager> +CompositorBridgeParent::GetAPZCTreeManager(uint64_t aLayersId) +{ + EnsureLayerTreeMapReady(); + MonitorAutoLock lock(*sIndirectLayerTreesLock); + LayerTreeMap::iterator cit = sIndirectLayerTrees.find(aLayersId); + if (sIndirectLayerTrees.end() == cit) { + return nullptr; + } + LayerTreeState* lts = &cit->second; + + RefPtr<APZCTreeManager> apzctm = lts->mParent + ? lts->mParent->mApzcTreeManager.get() + : nullptr; + return apzctm.forget(); +} + +static void +InsertVsyncProfilerMarker(TimeStamp aVsyncTimestamp) +{ +#ifdef MOZ_ENABLE_PROFILER_SPS + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + VsyncPayload* payload = new VsyncPayload(aVsyncTimestamp); + PROFILER_MARKER_PAYLOAD("VsyncTimestamp", payload); +#endif +} + +/*static */ void +CompositorBridgeParent::PostInsertVsyncProfilerMarker(TimeStamp aVsyncTimestamp) +{ + // Called in the vsync thread + if (profiler_is_active() && CompositorThreadHolder::IsActive()) { + CompositorLoop()->PostTask( + NewRunnableFunction(InsertVsyncProfilerMarker, aVsyncTimestamp)); + } +} + +widget::PCompositorWidgetParent* +CompositorBridgeParent::AllocPCompositorWidgetParent(const CompositorWidgetInitData& aInitData) +{ +#if defined(MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING) + if (mWidget) { + // Should not create two widgets on the same compositor. + return nullptr; + } + + widget::CompositorWidgetParent* widget = + new widget::CompositorWidgetParent(aInitData); + widget->AddRef(); + + // Sending the constructor acts as initialization as well. + mWidget = widget; + return widget; +#else + return nullptr; +#endif +} + +bool +CompositorBridgeParent::DeallocPCompositorWidgetParent(PCompositorWidgetParent* aActor) +{ +#if defined(MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING) + static_cast<widget::CompositorWidgetParent*>(aActor)->Release(); + return true; +#else + return false; +#endif +} + +bool +CompositorBridgeParent::IsPendingComposite() +{ + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + if (!mCompositor) { + return false; + } + return mCompositor->IsPendingComposite(); +} + +void +CompositorBridgeParent::FinishPendingComposite() +{ + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + if (!mCompositor) { + return; + } + return mCompositor->FinishPendingComposite(); +} + +CompositorController* +CompositorBridgeParent::LayerTreeState::GetCompositorController() const +{ + return mParent; +} + +MetricsSharingController* +CompositorBridgeParent::LayerTreeState::CrossProcessSharingController() const +{ + return mCrossProcessParent; +} + +MetricsSharingController* +CompositorBridgeParent::LayerTreeState::InProcessSharingController() const +{ + return mParent; +} + +void +CompositorBridgeParent::DidComposite(TimeStamp& aCompositeStart, + TimeStamp& aCompositeEnd) +{ + Unused << SendDidComposite(0, mPendingTransaction, aCompositeStart, aCompositeEnd); + mPendingTransaction = 0; + + if (mLayerManager) { + nsTArray<ImageCompositeNotification> notifications; + mLayerManager->ExtractImageCompositeNotifications(¬ifications); + if (!notifications.IsEmpty()) { + Unused << ImageBridgeParent::NotifyImageComposites(notifications); + } + } + + MonitorAutoLock lock(*sIndirectLayerTreesLock); + ForEachIndirectLayerTree([&] (LayerTreeState* lts, const uint64_t& aLayersId) -> void { + if (lts->mCrossProcessParent && lts->mParent == this) { + CrossProcessCompositorBridgeParent* cpcp = lts->mCrossProcessParent; + cpcp->DidComposite(aLayersId, aCompositeStart, aCompositeEnd); + } + }); +} + +void +CompositorBridgeParent::InvalidateRemoteLayers() +{ + MOZ_ASSERT(CompositorLoop() == MessageLoop::current()); + + Unused << PCompositorBridgeParent::SendInvalidateLayers(0); + + MonitorAutoLock lock(*sIndirectLayerTreesLock); + ForEachIndirectLayerTree([] (LayerTreeState* lts, const uint64_t& aLayersId) -> void { + if (lts->mCrossProcessParent) { + CrossProcessCompositorBridgeParent* cpcp = lts->mCrossProcessParent; + Unused << cpcp->SendInvalidateLayers(aLayersId); + } + }); +} + +bool +CompositorBridgeParent::ResetCompositor(const nsTArray<LayersBackend>& aBackendHints, + TextureFactoryIdentifier* aOutIdentifier) +{ + Maybe<TextureFactoryIdentifier> newIdentifier; + { + MonitorAutoLock lock(mResetCompositorMonitor); + + CompositorLoop()->PostTask(NewRunnableMethod + <StoreCopyPassByConstLRef<nsTArray<LayersBackend>>, + Maybe<TextureFactoryIdentifier>*>(this, + &CompositorBridgeParent::ResetCompositorTask, + aBackendHints, + &newIdentifier)); + + mResetCompositorMonitor.Wait(); + } + + if (!newIdentifier) { + return false; + } + + *aOutIdentifier = newIdentifier.value(); + return true; +} + +// Invoked on the compositor thread. The main thread is waiting on the given +// monitor. +void +CompositorBridgeParent::ResetCompositorTask(const nsTArray<LayersBackend>& aBackendHints, + Maybe<TextureFactoryIdentifier>* aOutNewIdentifier) +{ + // Perform the reset inside a lock, so the main thread can wake up as soon as + // possible. We notify child processes (if necessary) outside the lock. + Maybe<TextureFactoryIdentifier> newIdentifier; + { + MonitorAutoLock lock(mResetCompositorMonitor); + + newIdentifier = ResetCompositorImpl(aBackendHints); + *aOutNewIdentifier = newIdentifier; + + mResetCompositorMonitor.NotifyAll(); + } + + // NOTE: |aBackendHints|, and |aOutNewIdentifier| are now all invalid since + // they are allocated on ResetCompositor's stack on the main thread, which + // is no longer waiting on the lock. + + if (!newIdentifier) { + // No compositor change; nothing to do. + return; + } + + MonitorAutoLock lock(*sIndirectLayerTreesLock); + ForEachIndirectLayerTree([&] (LayerTreeState* lts, uint64_t layersId) -> void { + if (CrossProcessCompositorBridgeParent* cpcp = lts->mCrossProcessParent) { + Unused << cpcp->SendCompositorUpdated(layersId, newIdentifier.value()); + + if (LayerTransactionParent* ltp = lts->mLayerTree) { + ltp->AddPendingCompositorUpdate(); + } + lts->mPendingCompositorUpdates++; + } + }); +} + +Maybe<TextureFactoryIdentifier> +CompositorBridgeParent::ResetCompositorImpl(const nsTArray<LayersBackend>& aBackendHints) +{ + if (!mLayerManager) { + return Nothing(); + } + + RefPtr<Compositor> compositor = NewCompositor(aBackendHints); + if (!compositor) { + MOZ_RELEASE_ASSERT(compositor, "Failed to reset compositor."); + } + + // Don't bother changing from basic->basic. + if (mCompositor && + mCompositor->GetBackendType() == LayersBackend::LAYERS_BASIC && + compositor->GetBackendType() == LayersBackend::LAYERS_BASIC) + { + return Nothing(); + } + + if (mCompositor) { + mCompositor->SetInvalid(); + } + mCompositor = compositor; + mLayerManager->ChangeCompositor(compositor); + + return Some(compositor->GetTextureFactoryIdentifier()); +} + +static void +OpenCompositor(RefPtr<CrossProcessCompositorBridgeParent> aCompositor, + Endpoint<PCompositorBridgeParent>&& aEndpoint) +{ + aCompositor->Bind(Move(aEndpoint)); +} + +/* static */ bool +CompositorBridgeParent::CreateForContent(Endpoint<PCompositorBridgeParent>&& aEndpoint) +{ + gfxPlatform::InitLayersIPC(); + + RefPtr<CrossProcessCompositorBridgeParent> cpcp = + new CrossProcessCompositorBridgeParent(); + + CompositorLoop()->PostTask(NewRunnableFunction(OpenCompositor, cpcp, Move(aEndpoint))); + return true; +} + +static void +UpdateIndirectTree(uint64_t aId, Layer* aRoot, const TargetConfig& aTargetConfig) +{ + MonitorAutoLock lock(*sIndirectLayerTreesLock); + sIndirectLayerTrees[aId].mRoot = aRoot; + sIndirectLayerTrees[aId].mTargetConfig = aTargetConfig; +} + +/* static */ CompositorBridgeParent::LayerTreeState* +CompositorBridgeParent::GetIndirectShadowTree(uint64_t aId) +{ + MonitorAutoLock lock(*sIndirectLayerTreesLock); + LayerTreeMap::iterator cit = sIndirectLayerTrees.find(aId); + if (sIndirectLayerTrees.end() == cit) { + return nullptr; + } + return &cit->second; +} + +static CompositorBridgeParent::LayerTreeState* +GetStateForRoot(uint64_t aContentLayersId, const MonitorAutoLock& aProofOfLock) +{ + CompositorBridgeParent::LayerTreeState* state = nullptr; + LayerTreeMap::iterator itr = sIndirectLayerTrees.find(aContentLayersId); + if (sIndirectLayerTrees.end() != itr) { + state = &itr->second; + } + + // |state| is the state for the content process, but we want the APZCTMParent + // for the parent process owning that content process. So we have to jump to + // the LayerTreeState for the root layer tree id for that layer tree, and use + // the mApzcTreeManagerParent from that. This should also work with nested + // content processes, because RootLayerTreeId() will bypass any intermediate + // processes' ids and go straight to the root. + if (state) { + uint64_t rootLayersId = state->mParent->RootLayerTreeId(); + itr = sIndirectLayerTrees.find(rootLayersId); + state = (sIndirectLayerTrees.end() != itr) ? &itr->second : nullptr; + } + + return state; +} + +/* static */ APZCTreeManagerParent* +CompositorBridgeParent::GetApzcTreeManagerParentForRoot(uint64_t aContentLayersId) +{ + MonitorAutoLock lock(*sIndirectLayerTreesLock); + CompositorBridgeParent::LayerTreeState* state = + GetStateForRoot(aContentLayersId, lock); + return state ? state->mApzcTreeManagerParent : nullptr; +} + +/* static */ GeckoContentController* +CompositorBridgeParent::GetGeckoContentControllerForRoot(uint64_t aContentLayersId) +{ + MonitorAutoLock lock(*sIndirectLayerTreesLock); + CompositorBridgeParent::LayerTreeState* state = + GetStateForRoot(aContentLayersId, lock); + return state ? state->mController.get() : nullptr; +} + +PTextureParent* +CompositorBridgeParent::AllocPTextureParent(const SurfaceDescriptor& aSharedData, + const LayersBackend& aLayersBackend, + const TextureFlags& aFlags, + const uint64_t& aId, + const uint64_t& aSerial) +{ + return TextureHost::CreateIPDLActor(this, aSharedData, aLayersBackend, aFlags, aSerial); +} + +bool +CompositorBridgeParent::DeallocPTextureParent(PTextureParent* actor) +{ + return TextureHost::DestroyIPDLActor(actor); +} + +bool +CompositorBridgeParent::IsSameProcess() const +{ + return OtherPid() == base::GetCurrentProcId(); +} + +#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) +//#define PLUGINS_LOG(...) printf_stderr("CP [%s]: ", __FUNCTION__); +// printf_stderr(__VA_ARGS__); +// printf_stderr("\n"); +#define PLUGINS_LOG(...) + +bool +CompositorBridgeParent::UpdatePluginWindowState(uint64_t aId) +{ + MonitorAutoLock lock(*sIndirectLayerTreesLock); + CompositorBridgeParent::LayerTreeState& lts = sIndirectLayerTrees[aId]; + if (!lts.mParent) { + PLUGINS_LOG("[%" PRIu64 "] layer tree compositor parent pointer is null", aId); + return false; + } + + // Check if this layer tree has received any shadow layer updates + if (!lts.mUpdatedPluginDataAvailable) { + PLUGINS_LOG("[%" PRIu64 "] no plugin data", aId); + return false; + } + + // pluginMetricsChanged tracks whether we need to send plugin update + // data to the main thread. If we do we'll have to block composition, + // which we want to avoid if at all possible. + bool pluginMetricsChanged = false; + + // Same layer tree checks + if (mLastPluginUpdateLayerTreeId == aId) { + // no plugin data and nothing has changed, bail. + if (!mCachedPluginData.Length() && !lts.mPluginData.Length()) { + PLUGINS_LOG("[%" PRIu64 "] no data, no changes", aId); + return false; + } + + if (mCachedPluginData.Length() == lts.mPluginData.Length()) { + // check for plugin data changes + for (uint32_t idx = 0; idx < lts.mPluginData.Length(); idx++) { + if (!(mCachedPluginData[idx] == lts.mPluginData[idx])) { + pluginMetricsChanged = true; + break; + } + } + } else { + // array lengths don't match, need to update + pluginMetricsChanged = true; + } + } else { + // exchanging layer trees, we need to update + pluginMetricsChanged = true; + } + + // Check if plugin windows are currently hidden due to scrolling + if (mDeferPluginWindows) { + PLUGINS_LOG("[%" PRIu64 "] suppressing", aId); + return false; + } + + // If the plugin windows were hidden but now are not, we need to force + // update the metrics to make sure they are visible again. + if (mPluginWindowsHidden) { + PLUGINS_LOG("[%" PRIu64 "] re-showing", aId); + mPluginWindowsHidden = false; + pluginMetricsChanged = true; + } + + if (!lts.mPluginData.Length()) { + // Don't hide plugins if the previous remote layer tree didn't contain any. + if (!mCachedPluginData.Length()) { + PLUGINS_LOG("[%" PRIu64 "] nothing to hide", aId); + return false; + } + + uintptr_t parentWidget = GetWidget()->GetWidgetKey(); + + // We will pass through here in cases where the previous shadow layer + // tree contained visible plugins and the new tree does not. All we need + // to do here is hide the plugins for the old tree, so don't waste time + // calculating clipping. + mPluginsLayerOffset = nsIntPoint(0,0); + mPluginsLayerVisibleRegion.SetEmpty(); + Unused << lts.mParent->SendHideAllPlugins(parentWidget); + lts.mUpdatedPluginDataAvailable = false; + PLUGINS_LOG("[%" PRIu64 "] hide all", aId); + } else { + // Retrieve the offset and visible region of the layer that hosts + // the plugins, CompositorBridgeChild needs these in calculating proper + // plugin clipping. + LayerTransactionParent* layerTree = lts.mLayerTree; + Layer* contentRoot = layerTree->GetRoot(); + if (contentRoot) { + nsIntPoint offset; + nsIntRegion visibleRegion; + if (contentRoot->GetVisibleRegionRelativeToRootLayer(visibleRegion, + &offset)) { + // Check to see if these values have changed, if so we need to + // update plugin window position within the window. + if (!pluginMetricsChanged && + mPluginsLayerVisibleRegion == visibleRegion && + mPluginsLayerOffset == offset) { + PLUGINS_LOG("[%" PRIu64 "] no change", aId); + return false; + } + mPluginsLayerOffset = offset; + mPluginsLayerVisibleRegion = visibleRegion; + Unused << lts.mParent->SendUpdatePluginConfigurations( + LayoutDeviceIntPoint::FromUnknownPoint(offset), + LayoutDeviceIntRegion::FromUnknownRegion(visibleRegion), + lts.mPluginData); + lts.mUpdatedPluginDataAvailable = false; + PLUGINS_LOG("[%" PRIu64 "] updated", aId); + } else { + PLUGINS_LOG("[%" PRIu64 "] no visibility data", aId); + return false; + } + } else { + PLUGINS_LOG("[%" PRIu64 "] no content root", aId); + return false; + } + } + + mLastPluginUpdateLayerTreeId = aId; + mCachedPluginData = lts.mPluginData; + return true; +} + +void +CompositorBridgeParent::ScheduleShowAllPluginWindows() +{ + MOZ_ASSERT(CompositorLoop()); + CompositorLoop()->PostTask(NewRunnableMethod(this, &CompositorBridgeParent::ShowAllPluginWindows)); +} + +void +CompositorBridgeParent::ShowAllPluginWindows() +{ + MOZ_ASSERT(!NS_IsMainThread()); + mDeferPluginWindows = false; + ScheduleComposition(); +} + +void +CompositorBridgeParent::ScheduleHideAllPluginWindows() +{ + MOZ_ASSERT(CompositorLoop()); + CompositorLoop()->PostTask(NewRunnableMethod(this, &CompositorBridgeParent::HideAllPluginWindows)); +} + +void +CompositorBridgeParent::HideAllPluginWindows() +{ + MOZ_ASSERT(!NS_IsMainThread()); + // No plugins in the cache implies no plugins to manage + // in this content. + if (!mCachedPluginData.Length() || mDeferPluginWindows) { + return; + } + + uintptr_t parentWidget = GetWidget()->GetWidgetKey(); + + mDeferPluginWindows = true; + mPluginWindowsHidden = true; + +#if defined(XP_WIN) + // We will get an async reply that this has happened and then send hide. + mWaitForPluginsUntil = TimeStamp::Now() + mVsyncRate; + Unused << SendCaptureAllPlugins(parentWidget); +#else + Unused << SendHideAllPlugins(parentWidget); + ScheduleComposition(); +#endif +} +#endif // #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) + +bool +CompositorBridgeParent::RecvAllPluginsCaptured() +{ +#if defined(XP_WIN) + mWaitForPluginsUntil = TimeStamp(); + mHaveBlockedForPlugins = false; + ForceComposeToTarget(nullptr); + Unused << SendHideAllPlugins(GetWidget()->GetWidgetKey()); + return true; +#else + MOZ_ASSERT_UNREACHABLE( + "CompositorBridgeParent::RecvAllPluginsCaptured calls unexpected."); + return false; +#endif +} + +} // namespace layers +} // namespace mozilla |