mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
cfe17b6e52
Containerless scrolling means that the pan zoom controller applies it's transforms (to compensate for differences between the state of layout the last time we painted and the current state as composited to the screen) to the layers that are scrolled instead of the container layer that contains the layers that scroll. When running test_animations_omta.html there is a zoom of 1.306122 applied, and the page is scrolled down to 67 screen pixels (before the test starts, not sure why exactly). Gecko scrolls as close to 67 screen pixels as it can: 67/1.306122 = 51.29689 css pixels, which is 3077.813 appunits. Gecko scrolls to 3078 app units. When AsyncCompositionManager::TransformScrollableLayer runs we calculate the scroll position of gecko and the current scroll position that the pan zoom controller is using. Since there are no async pan or zoom operations taking place these should match. However when the gecko scroll position is calculated we get 3078/60*1.306122 = 67.0040586. So it applies a transform of 0.0040586 to the container layer for the transform that test_animations_omta.html is animating off main thread. When test_animations_omta.html reads the transform of this layer it fails because it's expecting 0 and 0.0040586 is outside of it's epsilon for considering it to be close enough.
2106 lines
65 KiB
C++
2106 lines
65 KiB
C++
/* -*- 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/CompositorParent.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 "base/tracked.h" // for FROM_HERE
|
|
#include "gfxContext.h" // for gfxContext
|
|
#include "gfxPlatform.h" // for gfxPlatform
|
|
#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/gfx/2D.h" // for DrawTarget
|
|
#include "mozilla/gfx/Point.h" // for IntSize
|
|
#include "mozilla/gfx/Rect.h" // for IntSize
|
|
#include "mozilla/ipc/Transport.h" // for Transport
|
|
#include "mozilla/layers/APZCTreeManager.h" // for APZCTreeManager
|
|
#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/CompositorTypes.h"
|
|
#include "mozilla/layers/LayerManagerComposite.h"
|
|
#include "mozilla/layers/LayersTypes.h"
|
|
#include "mozilla/layers/PLayerTransactionParent.h"
|
|
#include "mozilla/layers/ShadowLayersManager.h" // for ShadowLayersManager
|
|
#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"
|
|
#ifdef MOZ_ENABLE_PROFILER_SPS
|
|
#include "ProfilerMarkers.h"
|
|
#endif
|
|
#include "mozilla/VsyncDispatcher.h"
|
|
|
|
#ifdef MOZ_WIDGET_GONK
|
|
#include "GeckoTouchDispatcher.h"
|
|
#endif
|
|
|
|
namespace mozilla {
|
|
namespace layers {
|
|
|
|
using namespace mozilla::ipc;
|
|
using namespace mozilla::gfx;
|
|
using namespace std;
|
|
|
|
using base::ProcessId;
|
|
using base::Thread;
|
|
|
|
CompositorParent::LayerTreeState::LayerTreeState()
|
|
: mParent(nullptr)
|
|
, mLayerManager(nullptr)
|
|
, mCrossProcessParent(nullptr)
|
|
, mLayerTree(nullptr)
|
|
, mUpdatedPluginDataAvailable(false)
|
|
{
|
|
}
|
|
|
|
CompositorParent::LayerTreeState::~LayerTreeState()
|
|
{
|
|
if (mController) {
|
|
mController->Destroy();
|
|
}
|
|
}
|
|
|
|
typedef map<uint64_t, CompositorParent::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);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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,CompositorParent*> CompositorMap;
|
|
static CompositorMap* sCompositorMap;
|
|
|
|
static void CreateCompositorMap()
|
|
{
|
|
MOZ_ASSERT(!sCompositorMap);
|
|
sCompositorMap = new CompositorMap;
|
|
}
|
|
|
|
static void DestroyCompositorMap()
|
|
{
|
|
MOZ_ASSERT(sCompositorMap);
|
|
MOZ_ASSERT(sCompositorMap->empty());
|
|
delete sCompositorMap;
|
|
sCompositorMap = nullptr;
|
|
}
|
|
|
|
// See ImageBridgeChild.cpp
|
|
void ReleaseImageBridgeParentSingleton();
|
|
|
|
CompositorThreadHolder::CompositorThreadHolder()
|
|
: mCompositorThread(CreateCompositorThread())
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
MOZ_COUNT_CTOR(CompositorThreadHolder);
|
|
}
|
|
|
|
CompositorThreadHolder::~CompositorThreadHolder()
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
MOZ_COUNT_DTOR(CompositorThreadHolder);
|
|
|
|
DestroyCompositorThread(mCompositorThread);
|
|
}
|
|
|
|
static StaticRefPtr<CompositorThreadHolder> sCompositorThreadHolder;
|
|
static bool sFinishedCompositorShutDown = false;
|
|
|
|
CompositorThreadHolder* GetCompositorThreadHolder()
|
|
{
|
|
return sCompositorThreadHolder;
|
|
}
|
|
|
|
/* static */ Thread*
|
|
CompositorThreadHolder::CreateCompositorThread()
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
MOZ_ASSERT(!sCompositorThreadHolder, "The compositor thread has already been started!");
|
|
|
|
Thread* compositorThread = 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
|
|
/* 2048ms is chosen for permanent hangs because it's longer than most
|
|
* Compositor hangs seen in the wild, but is short enough to not miss getting
|
|
* native hang stacks. */
|
|
options.permanent_hang_timeout = 2048; // milliseconds
|
|
#if defined(_WIN32)
|
|
/* With d3d9 the compositor thread creates native ui, see DeviceManagerD3D9. As
|
|
* such the thread is a gui thread, and must process a windows message queue or
|
|
* risk deadlocks. Chromium message loop TYPE_UI does exactly what we need. */
|
|
options.message_loop_type = MessageLoop::TYPE_UI;
|
|
#endif
|
|
|
|
if (!compositorThread->StartWithOptions(options)) {
|
|
delete compositorThread;
|
|
return nullptr;
|
|
}
|
|
|
|
EnsureLayerTreeMapReady();
|
|
CreateCompositorMap();
|
|
|
|
return compositorThread;
|
|
}
|
|
|
|
/* static */ void
|
|
CompositorThreadHolder::DestroyCompositorThread(Thread* aCompositorThread)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
MOZ_ASSERT(!sCompositorThreadHolder, "We shouldn't be destroying the compositor thread yet.");
|
|
|
|
DestroyCompositorMap();
|
|
delete aCompositorThread;
|
|
sFinishedCompositorShutDown = true;
|
|
}
|
|
|
|
static Thread* CompositorThread() {
|
|
return sCompositorThreadHolder ? sCompositorThreadHolder->GetCompositorThread() : nullptr;
|
|
}
|
|
|
|
static void SetThreadPriority()
|
|
{
|
|
hal::SetCurrentThreadPriority(hal::THREAD_PRIORITY_COMPOSITOR);
|
|
}
|
|
|
|
CompositorVsyncObserver::CompositorVsyncObserver(CompositorParent* aCompositorParent, nsIWidget* aWidget)
|
|
: mNeedsComposite(false)
|
|
, mIsObservingVsync(false)
|
|
, mVsyncNotificationsSkipped(0)
|
|
, mCompositorParent(aCompositorParent)
|
|
, mCurrentCompositeTaskMonitor("CurrentCompositeTaskMonitor")
|
|
, mCurrentCompositeTask(nullptr)
|
|
, mSetNeedsCompositeMonitor("SetNeedsCompositeMonitor")
|
|
, mSetNeedsCompositeTask(nullptr)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
MOZ_ASSERT(aWidget != nullptr);
|
|
mCompositorVsyncDispatcher = aWidget->GetCompositorVsyncDispatcher();
|
|
#ifdef MOZ_WIDGET_GONK
|
|
GeckoTouchDispatcher::GetInstance()->SetCompositorVsyncObserver(this);
|
|
#endif
|
|
}
|
|
|
|
CompositorVsyncObserver::~CompositorVsyncObserver()
|
|
{
|
|
MOZ_ASSERT(!mIsObservingVsync);
|
|
// The CompositorVsyncDispatcher is cleaned up before this in the nsBaseWidget, which stops vsync listeners
|
|
mCompositorParent = nullptr;
|
|
mCompositorVsyncDispatcher = nullptr;
|
|
}
|
|
|
|
void
|
|
CompositorVsyncObserver::Destroy()
|
|
{
|
|
MOZ_ASSERT(CompositorParent::IsInCompositorThread());
|
|
UnobserveVsync();
|
|
CancelCurrentCompositeTask();
|
|
CancelCurrentSetNeedsCompositeTask();
|
|
}
|
|
|
|
void
|
|
CompositorVsyncObserver::CancelCurrentSetNeedsCompositeTask()
|
|
{
|
|
MOZ_ASSERT(CompositorParent::IsInCompositorThread());
|
|
MonitorAutoLock lock(mSetNeedsCompositeMonitor);
|
|
if (mSetNeedsCompositeTask) {
|
|
mSetNeedsCompositeTask->Cancel();
|
|
mSetNeedsCompositeTask = nullptr;
|
|
}
|
|
mNeedsComposite = false;
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
CompositorVsyncObserver::SetNeedsComposite(bool aNeedsComposite)
|
|
{
|
|
if (!CompositorParent::IsInCompositorThread()) {
|
|
MonitorAutoLock lock(mSetNeedsCompositeMonitor);
|
|
mSetNeedsCompositeTask = NewRunnableMethod(this,
|
|
&CompositorVsyncObserver::SetNeedsComposite,
|
|
aNeedsComposite);
|
|
MOZ_ASSERT(CompositorParent::CompositorLoop());
|
|
CompositorParent::CompositorLoop()->PostTask(FROM_HERE, mSetNeedsCompositeTask);
|
|
return;
|
|
} else {
|
|
MonitorAutoLock lock(mSetNeedsCompositeMonitor);
|
|
mSetNeedsCompositeTask = nullptr;
|
|
}
|
|
|
|
mNeedsComposite = aNeedsComposite;
|
|
if (!mIsObservingVsync && mNeedsComposite) {
|
|
ObserveVsync();
|
|
}
|
|
}
|
|
|
|
bool
|
|
CompositorVsyncObserver::NotifyVsync(TimeStamp aVsyncTimestamp)
|
|
{
|
|
// Called from the vsync dispatch thread
|
|
MOZ_ASSERT(!CompositorParent::IsInCompositorThread());
|
|
MOZ_ASSERT(!NS_IsMainThread());
|
|
|
|
MonitorAutoLock lock(mCurrentCompositeTaskMonitor);
|
|
if (mCurrentCompositeTask == nullptr) {
|
|
mCurrentCompositeTask = NewRunnableMethod(this,
|
|
&CompositorVsyncObserver::Composite,
|
|
aVsyncTimestamp);
|
|
MOZ_ASSERT(CompositorParent::CompositorLoop());
|
|
CompositorParent::CompositorLoop()->PostTask(FROM_HERE, mCurrentCompositeTask);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void
|
|
CompositorVsyncObserver::CancelCurrentCompositeTask()
|
|
{
|
|
MOZ_ASSERT(CompositorParent::IsInCompositorThread() || NS_IsMainThread());
|
|
MonitorAutoLock lock(mCurrentCompositeTaskMonitor);
|
|
if (mCurrentCompositeTask) {
|
|
mCurrentCompositeTask->Cancel();
|
|
mCurrentCompositeTask = nullptr;
|
|
}
|
|
}
|
|
|
|
void
|
|
CompositorVsyncObserver::Composite(TimeStamp aVsyncTimestamp)
|
|
{
|
|
MOZ_ASSERT(CompositorParent::IsInCompositorThread());
|
|
{
|
|
MonitorAutoLock lock(mCurrentCompositeTaskMonitor);
|
|
mCurrentCompositeTask = nullptr;
|
|
}
|
|
|
|
DispatchTouchEvents(aVsyncTimestamp);
|
|
|
|
if (mNeedsComposite && mCompositorParent) {
|
|
mNeedsComposite = false;
|
|
mCompositorParent->CompositeCallback(aVsyncTimestamp);
|
|
mVsyncNotificationsSkipped = 0;
|
|
} else if (mVsyncNotificationsSkipped++ > gfxPrefs::CompositorUnobserveCount()) {
|
|
UnobserveVsync();
|
|
}
|
|
}
|
|
|
|
void
|
|
CompositorVsyncObserver::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(CompositorParent::IsInCompositorThread());
|
|
mVsyncNotificationsSkipped = 0;
|
|
}
|
|
|
|
bool
|
|
CompositorVsyncObserver::NeedsComposite()
|
|
{
|
|
MOZ_ASSERT(CompositorParent::IsInCompositorThread());
|
|
return mNeedsComposite;
|
|
}
|
|
|
|
void
|
|
CompositorVsyncObserver::ObserveVsync()
|
|
{
|
|
MOZ_ASSERT(CompositorParent::IsInCompositorThread());
|
|
mCompositorVsyncDispatcher->SetCompositorVsyncObserver(this);
|
|
mIsObservingVsync = true;
|
|
}
|
|
|
|
void
|
|
CompositorVsyncObserver::UnobserveVsync()
|
|
{
|
|
MOZ_ASSERT(CompositorParent::IsInCompositorThread());
|
|
mCompositorVsyncDispatcher->SetCompositorVsyncObserver(nullptr);
|
|
mIsObservingVsync = false;
|
|
}
|
|
|
|
void
|
|
CompositorVsyncObserver::DispatchTouchEvents(TimeStamp aVsyncTimestamp)
|
|
{
|
|
#ifdef MOZ_WIDGET_GONK
|
|
GeckoTouchDispatcher::GetInstance()->NotifyVsync(aVsyncTimestamp);
|
|
#endif
|
|
}
|
|
|
|
void CompositorParent::StartUp()
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread(), "Should be on the main Thread!");
|
|
MOZ_ASSERT(!sCompositorThreadHolder, "The compositor thread has already been started!");
|
|
|
|
sCompositorThreadHolder = new CompositorThreadHolder();
|
|
}
|
|
|
|
void CompositorParent::ShutDown()
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread(), "Should be on the main Thread!");
|
|
MOZ_ASSERT(sCompositorThreadHolder, "The compositor thread has already been shut down!");
|
|
|
|
ReleaseImageBridgeParentSingleton();
|
|
|
|
sCompositorThreadHolder = nullptr;
|
|
|
|
// No locking is needed around sFinishedCompositorShutDown because it is only
|
|
// ever accessed on the main thread.
|
|
while (!sFinishedCompositorShutDown) {
|
|
NS_ProcessNextEvent(nullptr, true);
|
|
}
|
|
}
|
|
|
|
MessageLoop* CompositorParent::CompositorLoop()
|
|
{
|
|
return CompositorThread() ? CompositorThread()->message_loop() : nullptr;
|
|
}
|
|
|
|
static bool
|
|
IsInCompositorAsapMode()
|
|
{
|
|
// Returns true if the compositor is allowed to be in ASAP mode
|
|
// and layout is not in ASAP mode
|
|
return gfxPrefs::LayersCompositionFrameRate() == 0 &&
|
|
!gfxPlatform::IsInLayoutAsapMode();
|
|
}
|
|
|
|
static bool
|
|
UseVsyncComposition()
|
|
{
|
|
return gfxPrefs::VsyncAlignedCompositor()
|
|
&& gfxPrefs::HardwareVsyncEnabled()
|
|
&& !IsInCompositorAsapMode()
|
|
&& !gfxPlatform::IsInLayoutAsapMode();
|
|
}
|
|
|
|
CompositorParent::CompositorParent(nsIWidget* aWidget,
|
|
bool aUseExternalSurfaceSize,
|
|
int aSurfaceWidth, int aSurfaceHeight)
|
|
: mWidget(aWidget)
|
|
, mCurrentCompositeTask(nullptr)
|
|
, mIsTesting(false)
|
|
, mPendingTransaction(0)
|
|
, mPaused(false)
|
|
, mUseExternalSurfaceSize(aUseExternalSurfaceSize)
|
|
, mEGLSurfaceSize(aSurfaceWidth, aSurfaceHeight)
|
|
, mPauseCompositionMonitor("PauseCompositionMonitor")
|
|
, mResumeCompositionMonitor("ResumeCompositionMonitor")
|
|
, mOverrideComposeReadiness(false)
|
|
, mForceCompositionTask(nullptr)
|
|
, mCompositorThreadHolder(sCompositorThreadHolder)
|
|
, mCompositorVsyncObserver(nullptr)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
MOZ_ASSERT(CompositorThread(),
|
|
"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.
|
|
MOZ_ASSERT(CompositorLoop());
|
|
CompositorLoop()->PostTask(FROM_HERE, NewRunnableFunction(&AddCompositor,
|
|
this, &mCompositorID));
|
|
|
|
CompositorLoop()->PostTask(FROM_HERE, NewRunnableFunction(SetThreadPriority));
|
|
|
|
mRootLayerTreeID = AllocateLayerTreeId();
|
|
|
|
{ // scope lock
|
|
MonitorAutoLock lock(*sIndirectLayerTreesLock);
|
|
sIndirectLayerTrees[mRootLayerTreeID].mParent = this;
|
|
}
|
|
|
|
if (gfxPrefs::AsyncPanZoomEnabled()) {
|
|
mApzcTreeManager = new APZCTreeManager();
|
|
}
|
|
|
|
if (UseVsyncComposition()) {
|
|
NS_WARNING("Enabling vsync compositor\n");
|
|
mCompositorVsyncObserver = new CompositorVsyncObserver(this, aWidget);
|
|
}
|
|
|
|
gfxPlatform::GetPlatform()->ComputeTileSize();
|
|
}
|
|
|
|
bool
|
|
CompositorParent::IsInCompositorThread()
|
|
{
|
|
return CompositorThread() && CompositorThread()->thread_id() == PlatformThread::CurrentId();
|
|
}
|
|
|
|
uint64_t
|
|
CompositorParent::RootLayerTreeId()
|
|
{
|
|
return mRootLayerTreeID;
|
|
}
|
|
|
|
CompositorParent::~CompositorParent()
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
MOZ_COUNT_DTOR(CompositorParent);
|
|
}
|
|
|
|
void
|
|
CompositorParent::Destroy()
|
|
{
|
|
MOZ_ASSERT(ManagedPLayerTransactionParent().Length() == 0,
|
|
"CompositorParent destroyed before managed PLayerTransactionParent");
|
|
|
|
MOZ_ASSERT(mPaused); // Ensure RecvWillStop was called
|
|
// Ensure that the layer manager is destructed on the compositor thread.
|
|
mLayerManager = nullptr;
|
|
if (mCompositor) {
|
|
mCompositor->Destroy();
|
|
}
|
|
mCompositor = nullptr;
|
|
|
|
mCompositionManager = nullptr;
|
|
if (mApzcTreeManager) {
|
|
mApzcTreeManager->ClearTree();
|
|
mApzcTreeManager = nullptr;
|
|
}
|
|
{ // scope lock
|
|
MonitorAutoLock lock(*sIndirectLayerTreesLock);
|
|
sIndirectLayerTrees.erase(mRootLayerTreeID);
|
|
}
|
|
if (mCompositorVsyncObserver) {
|
|
mCompositorVsyncObserver->Destroy();
|
|
mCompositorVsyncObserver = nullptr;
|
|
}
|
|
}
|
|
|
|
void
|
|
CompositorParent::ForceIsFirstPaint()
|
|
{
|
|
mCompositionManager->ForceIsFirstPaint();
|
|
}
|
|
|
|
bool
|
|
CompositorParent::RecvWillStop()
|
|
{
|
|
mPaused = true;
|
|
RemoveCompositor(mCompositorID);
|
|
|
|
// Ensure that the layer manager is destroyed before CompositorChild.
|
|
if (mLayerManager) {
|
|
MonitorAutoLock lock(*sIndirectLayerTreesLock);
|
|
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;
|
|
lts->mParent = nullptr;
|
|
}
|
|
}
|
|
mLayerManager->Destroy();
|
|
mLayerManager = nullptr;
|
|
mCompositionManager = nullptr;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void CompositorParent::DeferredDestroy()
|
|
{
|
|
MOZ_ASSERT(!NS_IsMainThread());
|
|
mCompositorThreadHolder = nullptr;
|
|
Release();
|
|
}
|
|
|
|
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 DeferredDestroy's Release
|
|
MessageLoop::current()->PostTask(FROM_HERE,
|
|
NewRunnableMethod(this,&CompositorParent::DeferredDestroy));
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
CompositorParent::RecvPause()
|
|
{
|
|
PauseComposition();
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
CompositorParent::RecvResume()
|
|
{
|
|
ResumeComposition();
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
CompositorParent::RecvMakeSnapshot(const SurfaceDescriptor& aInSnapshot,
|
|
const gfx::IntRect& aRect)
|
|
{
|
|
RefPtr<DrawTarget> target = GetDrawTargetForDescriptor(aInSnapshot, gfx::BackendType::CAIRO);
|
|
ForceComposeToTarget(target, &aRect);
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
CompositorParent::RecvFlushRendering()
|
|
{
|
|
if (mCompositorVsyncObserver && mCompositorVsyncObserver->NeedsComposite()) {
|
|
mCompositorVsyncObserver->SetNeedsComposite(false);
|
|
CancelCurrentCompositeTask();
|
|
ForceComposeToTarget(nullptr);
|
|
} else if (mCurrentCompositeTask) {
|
|
// If we're waiting to do a composite, then cancel it
|
|
// and do it immediately instead.
|
|
CancelCurrentCompositeTask();
|
|
ForceComposeToTarget(nullptr);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
CompositorParent::RecvGetTileSize(int32_t* aWidth, int32_t* aHeight)
|
|
{
|
|
*aWidth = gfxPlatform::GetPlatform()->GetTileWidth();
|
|
*aHeight = gfxPlatform::GetPlatform()->GetTileHeight();
|
|
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<float>* 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;
|
|
{ // scope lock
|
|
MonitorAutoLock lock(*sIndirectLayerTreesLock);
|
|
sIndirectLayerTrees[mRootLayerTreeID].mLayerManager = nullptr;
|
|
}
|
|
mCompositionManager = nullptr;
|
|
mCompositor = nullptr;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
CompositorParent::ScheduleRenderOnCompositorThread()
|
|
{
|
|
CancelableTask *renderTask = NewRunnableMethod(this, &CompositorParent::ScheduleComposition);
|
|
MOZ_ASSERT(CompositorLoop());
|
|
CompositorLoop()->PostTask(FROM_HERE, renderTask);
|
|
}
|
|
|
|
void
|
|
CompositorParent::PauseComposition()
|
|
{
|
|
MOZ_ASSERT(IsInCompositorThread(),
|
|
"PauseComposition() can only be called on the compositor thread");
|
|
|
|
MonitorAutoLock lock(mPauseCompositionMonitor);
|
|
|
|
if (!mPaused) {
|
|
mPaused = true;
|
|
|
|
mCompositor->Pause();
|
|
DidComposite();
|
|
}
|
|
|
|
// if anyone's waiting to make sure that composition really got paused, tell them
|
|
lock.NotifyAll();
|
|
}
|
|
|
|
void
|
|
CompositorParent::ResumeComposition()
|
|
{
|
|
MOZ_ASSERT(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, "CompositorParent", "Unable to renew compositor surface; remaining in paused state");
|
|
#endif
|
|
lock.NotifyAll();
|
|
return;
|
|
}
|
|
|
|
mPaused = false;
|
|
|
|
mLastCompose = TimeStamp::Now();
|
|
CompositeToTarget(nullptr);
|
|
|
|
// 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 (mCompositorVsyncObserver) {
|
|
mCompositorVsyncObserver->CancelCurrentCompositeTask();
|
|
} else 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);
|
|
MOZ_ASSERT(CompositorLoop());
|
|
CompositorLoop()->PostTask(FROM_HERE, pauseTask);
|
|
|
|
// Wait until the pause has actually been processed by the compositor thread
|
|
lock.Wait();
|
|
}
|
|
|
|
bool
|
|
CompositorParent::ScheduleResumeOnCompositorThread()
|
|
{
|
|
MonitorAutoLock lock(mResumeCompositionMonitor);
|
|
|
|
CancelableTask *resumeTask =
|
|
NewRunnableMethod(this, &CompositorParent::ResumeComposition);
|
|
MOZ_ASSERT(CompositorLoop());
|
|
CompositorLoop()->PostTask(FROM_HERE, resumeTask);
|
|
|
|
// Wait until the resume has actually been processed by the compositor thread
|
|
lock.Wait();
|
|
|
|
return !mPaused;
|
|
}
|
|
|
|
bool
|
|
CompositorParent::ScheduleResumeOnCompositorThread(int width, int height)
|
|
{
|
|
MonitorAutoLock lock(mResumeCompositionMonitor);
|
|
|
|
CancelableTask *resumeTask =
|
|
NewRunnableMethod(this, &CompositorParent::ResumeCompositionAndResize, width, height);
|
|
MOZ_ASSERT(CompositorLoop());
|
|
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,
|
|
bool aIsRepeatTransaction)
|
|
{
|
|
if (mApzcTreeManager &&
|
|
!aIsRepeatTransaction &&
|
|
mLayerManager &&
|
|
mLayerManager->GetRoot()) {
|
|
AutoResolveRefLayers resolve(mCompositionManager);
|
|
mApzcTreeManager->UpdateHitTestingTree(this, mLayerManager->GetRoot(),
|
|
aIsFirstPaint, aId, aPaintSequenceNumber);
|
|
|
|
mLayerManager->NotifyShadowTreeTransaction();
|
|
}
|
|
if (aScheduleComposite) {
|
|
ScheduleComposition();
|
|
}
|
|
}
|
|
|
|
// 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::ScheduleSoftwareTimerComposition()
|
|
{
|
|
MOZ_ASSERT(!mCompositorVsyncObserver);
|
|
|
|
if (mCurrentCompositeTask) {
|
|
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::CompositeCallback,
|
|
TimeStamp::Now());
|
|
|
|
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::ScheduleComposition()
|
|
{
|
|
MOZ_ASSERT(IsInCompositorThread());
|
|
if (mPaused) {
|
|
return;
|
|
}
|
|
|
|
if (mCompositorVsyncObserver) {
|
|
mCompositorVsyncObserver->SetNeedsComposite(true);
|
|
} else {
|
|
ScheduleSoftwareTimerComposition();
|
|
}
|
|
}
|
|
|
|
void
|
|
CompositorParent::CompositeCallback(TimeStamp aScheduleTime)
|
|
{
|
|
if (mCompositorVsyncObserver) {
|
|
// Align OMTA to vsync time.
|
|
// TODO: ensure it aligns with the refresh / start time of
|
|
// animations
|
|
mLastCompose = aScheduleTime;
|
|
} else {
|
|
mLastCompose = TimeStamp::Now();
|
|
}
|
|
|
|
mCurrentCompositeTask = nullptr;
|
|
CompositeToTarget(nullptr);
|
|
}
|
|
|
|
// Go down the composite layer tree, setting properties to match their
|
|
// content-side counterparts.
|
|
/* static */ void
|
|
CompositorParent::SetShadowProperties(Layer* aLayer)
|
|
{
|
|
if (Layer* maskLayer = aLayer->GetMaskLayer()) {
|
|
SetShadowProperties(maskLayer);
|
|
}
|
|
|
|
// 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::CompositeToTarget(DrawTarget* aTarget, const gfx::IntRect* aRect)
|
|
{
|
|
profiler_tracing("Paint", "Composite", TRACING_INTERVAL_START);
|
|
PROFILER_LABEL("CompositorParent", "Composite",
|
|
js::ProfileEntry::Category::GRAPHICS);
|
|
|
|
MOZ_ASSERT(IsInCompositorThread(),
|
|
"Composite can only be called on the compositor thread");
|
|
TimeStamp start = TimeStamp::Now();
|
|
|
|
#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 (!CanComposite()) {
|
|
DidComposite();
|
|
return;
|
|
}
|
|
|
|
AutoResolveRefLayers resolve(mCompositionManager);
|
|
|
|
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 : mLastCompose;
|
|
bool requestNextFrame = mCompositionManager->TransformShadowTree(time);
|
|
if (requestNextFrame) {
|
|
ScheduleComposition();
|
|
}
|
|
|
|
RenderTraceLayers(mLayerManager->GetRoot(), "0000");
|
|
|
|
#ifdef MOZ_DUMP_PAINTING
|
|
if (gfxPrefs::DumpHostLayers()) {
|
|
printf_stderr("Painting --- compositing layer tree:\n");
|
|
mLayerManager->Dump();
|
|
}
|
|
#endif
|
|
mLayerManager->SetDebugOverlayWantsNextFrame(false);
|
|
mLayerManager->EndEmptyTransaction();
|
|
|
|
if (!aTarget) {
|
|
DidComposite();
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
mozilla::Telemetry::AccumulateTimeDelta(mozilla::Telemetry::COMPOSITE_TIME, start);
|
|
profiler_tracing("Paint", "Composite", TRACING_INTERVAL_END);
|
|
}
|
|
|
|
void
|
|
CompositorParent::ForceComposeToTarget(DrawTarget* aTarget, const gfx::IntRect* aRect)
|
|
{
|
|
PROFILER_LABEL("CompositorParent", "ForceComposeToTarget",
|
|
js::ProfileEntry::Category::GRAPHICS);
|
|
|
|
if (mCompositorVsyncObserver) {
|
|
mCompositorVsyncObserver->OnForceComposeToTarget();
|
|
}
|
|
|
|
AutoRestore<bool> override(mOverrideComposeReadiness);
|
|
mOverrideComposeReadiness = true;
|
|
|
|
mLastCompose = TimeStamp::Now();
|
|
CompositeToTarget(aTarget, aRect);
|
|
}
|
|
|
|
bool
|
|
CompositorParent::CanComposite()
|
|
{
|
|
return mLayerManager &&
|
|
mLayerManager->GetRoot() &&
|
|
!mPaused;
|
|
}
|
|
|
|
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 uint64_t& aTransactionId,
|
|
const TargetConfig& aTargetConfig,
|
|
const InfallibleTArray<PluginWindowData>& aUnused,
|
|
bool aIsFirstPaint,
|
|
bool aScheduleComposite,
|
|
uint32_t aPaintSequenceNumber,
|
|
bool aIsRepeatTransaction)
|
|
{
|
|
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());
|
|
|
|
mCompositionManager->Updated(aIsFirstPaint, aTargetConfig);
|
|
Layer* root = aLayerTree->GetRoot();
|
|
mLayerManager->SetRoot(root);
|
|
|
|
if (mApzcTreeManager && !aIsRepeatTransaction) {
|
|
AutoResolveRefLayers resolve(mCompositionManager);
|
|
mApzcTreeManager->UpdateHitTestingTree(this, root, aIsFirstPaint,
|
|
mRootLayerTreeID, aPaintSequenceNumber);
|
|
}
|
|
|
|
MOZ_ASSERT(aTransactionId > mPendingTransaction);
|
|
mPendingTransaction = aTransactionId;
|
|
|
|
if (root) {
|
|
SetShadowProperties(root);
|
|
}
|
|
if (aScheduleComposite) {
|
|
ScheduleComposition();
|
|
if (mPaused) {
|
|
DidComposite();
|
|
}
|
|
}
|
|
mLayerManager->NotifyShadowTreeTransaction();
|
|
}
|
|
|
|
void
|
|
CompositorParent::ForceComposite(LayerTransactionParent* aLayerTree)
|
|
{
|
|
ScheduleComposition();
|
|
}
|
|
|
|
bool
|
|
CompositorParent::SetTestSampleTime(LayerTransactionParent* aLayerTree,
|
|
const TimeStamp& aTime)
|
|
{
|
|
if (aTime.IsNull()) {
|
|
return false;
|
|
}
|
|
|
|
mIsTesting = true;
|
|
mTestTime = aTime;
|
|
|
|
bool testComposite = mCompositionManager && (mCurrentCompositeTask ||
|
|
(mCompositorVsyncObserver
|
|
&& mCompositorVsyncObserver->NeedsComposite()));
|
|
|
|
// Update but only if we were already scheduled to animate
|
|
if (testComposite) {
|
|
AutoResolveRefLayers resolve(mCompositionManager);
|
|
bool requestNextFrame = mCompositionManager->TransformShadowTree(aTime);
|
|
if (!requestNextFrame) {
|
|
CancelCurrentCompositeTask();
|
|
// Pretend we composited in case someone is wating for this event.
|
|
DidComposite();
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
CompositorParent::LeaveTestMode(LayerTransactionParent* aLayerTree)
|
|
{
|
|
mIsTesting = false;
|
|
}
|
|
|
|
void
|
|
CompositorParent::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 : mLastCompose;
|
|
bool requestNextFrame =
|
|
mCompositionManager->TransformShadowTree(time,
|
|
AsyncCompositionManager::TransformsToSkip::APZ);
|
|
if (!requestNextFrame) {
|
|
CancelCurrentCompositeTask();
|
|
// Pretend we composited in case someone is waiting for this event.
|
|
DidComposite();
|
|
}
|
|
}
|
|
}
|
|
|
|
bool
|
|
CompositorParent::RecvRequestOverfill()
|
|
{
|
|
uint32_t overfillRatio = mCompositor->GetFillRatio();
|
|
unused << SendOverfill(overfillRatio);
|
|
return true;
|
|
}
|
|
|
|
void
|
|
CompositorParent::GetAPZTestData(const LayerTransactionParent* aLayerTree,
|
|
APZTestData* aOutData)
|
|
{
|
|
MonitorAutoLock lock(*sIndirectLayerTreesLock);
|
|
*aOutData = sIndirectLayerTrees[mRootLayerTreeID].mApzTestData;
|
|
}
|
|
|
|
class NotifyAPZConfirmedTargetTask : public Task
|
|
{
|
|
public:
|
|
explicit NotifyAPZConfirmedTargetTask(const nsRefPtr<APZCTreeManager>& aAPZCTM,
|
|
const uint64_t& aInputBlockId,
|
|
const nsTArray<ScrollableLayerGuid>& aTargets)
|
|
: mAPZCTM(aAPZCTM),
|
|
mInputBlockId(aInputBlockId),
|
|
mTargets(aTargets)
|
|
{
|
|
}
|
|
|
|
virtual void Run() override {
|
|
mAPZCTM->SetTargetAPZC(mInputBlockId, mTargets);
|
|
}
|
|
|
|
private:
|
|
nsRefPtr<APZCTreeManager> mAPZCTM;
|
|
uint64_t mInputBlockId;
|
|
nsTArray<ScrollableLayerGuid> mTargets;
|
|
};
|
|
|
|
void
|
|
CompositorParent::SetConfirmedTargetAPZC(const LayerTransactionParent* aLayerTree,
|
|
const uint64_t& aInputBlockId,
|
|
const nsTArray<ScrollableLayerGuid>& aTargets)
|
|
{
|
|
if (!mApzcTreeManager) {
|
|
return;
|
|
}
|
|
APZThreadUtils::RunOnControllerThread(new NotifyAPZConfirmedTargetTask(
|
|
mApzcTreeManager, aInputBlockId, aTargets));
|
|
}
|
|
|
|
void
|
|
CompositorParent::InitializeLayerManager(const nsTArray<LayersBackend>& aBackendHints)
|
|
{
|
|
NS_ASSERTION(!mLayerManager, "Already initialised mLayerManager");
|
|
NS_ASSERTION(!mCompositor, "Already initialised mCompositor");
|
|
|
|
for (size_t i = 0; i < aBackendHints.Length(); ++i) {
|
|
RefPtr<Compositor> compositor;
|
|
if (aBackendHints[i] == LayersBackend::LAYERS_OPENGL) {
|
|
compositor = new CompositorOGL(mWidget,
|
|
mEGLSurfaceSize.width,
|
|
mEGLSurfaceSize.height,
|
|
mUseExternalSurfaceSize);
|
|
} else if (aBackendHints[i] == LayersBackend::LAYERS_BASIC) {
|
|
#ifdef MOZ_WIDGET_GTK
|
|
if (gfxPlatformGtk::GetPlatform()->UseXRender()) {
|
|
compositor = new X11BasicCompositor(mWidget);
|
|
} else
|
|
#endif
|
|
{
|
|
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<LayerManagerComposite> layerManager = new LayerManagerComposite(compositor);
|
|
|
|
if (layerManager->Initialize()) {
|
|
mLayerManager = layerManager;
|
|
MOZ_ASSERT(compositor);
|
|
mCompositor = compositor;
|
|
MonitorAutoLock lock(*sIndirectLayerTreesLock);
|
|
sIndirectLayerTrees[mRootLayerTreeID].mLayerManager = layerManager;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
PLayerTransactionParent*
|
|
CompositorParent::AllocPLayerTransactionParent(const nsTArray<LayersBackend>& 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.
|
|
gfx::IntRect rect;
|
|
mWidget->GetClientBounds(rect);
|
|
InitializeLayerManager(aBackendHints);
|
|
mWidget = nullptr;
|
|
|
|
if (!mLayerManager) {
|
|
NS_WARNING("Failed to initialise Compositor");
|
|
*aSuccess = false;
|
|
LayerTransactionParent* p = new LayerTransactionParent(nullptr, this, 0);
|
|
p->AddIPDLReference();
|
|
return p;
|
|
}
|
|
|
|
mCompositionManager = new AsyncCompositionManager(mLayerManager);
|
|
*aSuccess = true;
|
|
|
|
*aTextureFactoryIdentifier = mCompositor->GetTextureFactoryIdentifier();
|
|
LayerTransactionParent* p = new LayerTransactionParent(mLayerManager, this, 0);
|
|
p->AddIPDLReference();
|
|
return p;
|
|
}
|
|
|
|
bool
|
|
CompositorParent::DeallocPLayerTransactionParent(PLayerTransactionParent* actor)
|
|
{
|
|
static_cast<LayerTransactionParent*>(actor)->ReleaseIPDLReference();
|
|
return true;
|
|
}
|
|
|
|
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)
|
|
{
|
|
MonitorAutoLock lock(*sIndirectLayerTreesLock);
|
|
NotifyChildCreated(child);
|
|
return true;
|
|
}
|
|
|
|
void
|
|
CompositorParent::NotifyChildCreated(const uint64_t& aChild)
|
|
{
|
|
sIndirectLayerTreesLock->AssertCurrentThreadOwns();
|
|
sIndirectLayerTrees[aChild].mParent = this;
|
|
sIndirectLayerTrees[aChild].mLayerManager = mLayerManager;
|
|
}
|
|
|
|
bool
|
|
CompositorParent::RecvAdoptChild(const uint64_t& child)
|
|
{
|
|
MonitorAutoLock lock(*sIndirectLayerTreesLock);
|
|
NotifyChildCreated(child);
|
|
if (sIndirectLayerTrees[child].mLayerTree) {
|
|
sIndirectLayerTrees[child].mLayerTree->mLayerManager = mLayerManager;
|
|
}
|
|
if (sIndirectLayerTrees[child].mRoot) {
|
|
sIndirectLayerTrees[child].mRoot->AsLayerComposite()->SetLayerManager(mLayerManager);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*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)
|
|
{
|
|
MonitorAutoLock lock(*sIndirectLayerTreesLock);
|
|
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.
|
|
MOZ_ASSERT(CompositorLoop());
|
|
CompositorLoop()->PostTask(FROM_HERE,
|
|
NewRunnableFunction(&EraseLayerState, aId));
|
|
}
|
|
|
|
/* static */ void
|
|
CompositorParent::SwapLayerTreeObservers(uint64_t aLayerId, uint64_t aOtherLayerId)
|
|
{
|
|
EnsureLayerTreeMapReady();
|
|
MonitorAutoLock lock(*sIndirectLayerTreesLock);
|
|
NS_ASSERTION(sIndirectLayerTrees.find(aLayerId) != sIndirectLayerTrees.end(),
|
|
"SwapLayerTrees missing layer 1");
|
|
NS_ASSERTION(sIndirectLayerTrees.find(aOtherLayerId) != sIndirectLayerTrees.end(),
|
|
"SwapLayerTrees missing layer 2");
|
|
std::swap(sIndirectLayerTrees[aLayerId].mLayerTreeReadyObserver,
|
|
sIndirectLayerTrees[aOtherLayerId].mLayerTreeReadyObserver);
|
|
}
|
|
|
|
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(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
|
|
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;
|
|
}
|
|
|
|
static void
|
|
InsertVsyncProfilerMarker(TimeStamp aVsyncTimestamp)
|
|
{
|
|
#ifdef MOZ_ENABLE_PROFILER_SPS
|
|
MOZ_ASSERT(CompositorParent::IsInCompositorThread());
|
|
VsyncPayload* payload = new VsyncPayload(aVsyncTimestamp);
|
|
PROFILER_MARKER_PAYLOAD("VsyncTimestamp", payload);
|
|
#endif
|
|
}
|
|
|
|
/*static */ void
|
|
CompositorParent::PostInsertVsyncProfilerMarker(TimeStamp aVsyncTimestamp)
|
|
{
|
|
// Called in the vsync thread
|
|
if (profiler_is_active() && sCompositorThreadHolder) {
|
|
CompositorLoop()->PostTask(FROM_HERE,
|
|
NewRunnableFunction(InsertVsyncProfilerMarker, aVsyncTimestamp));
|
|
}
|
|
}
|
|
|
|
/* static */ void
|
|
CompositorParent::RequestNotifyLayerTreeReady(uint64_t aLayersId, CompositorUpdateObserver* aObserver)
|
|
{
|
|
EnsureLayerTreeMapReady();
|
|
MonitorAutoLock lock(*sIndirectLayerTreesLock);
|
|
sIndirectLayerTrees[aLayersId].mLayerTreeReadyObserver = aObserver;
|
|
}
|
|
|
|
/* static */ void
|
|
CompositorParent::RequestNotifyLayerTreeCleared(uint64_t aLayersId, CompositorUpdateObserver* aObserver)
|
|
{
|
|
EnsureLayerTreeMapReady();
|
|
MonitorAutoLock lock(*sIndirectLayerTreesLock);
|
|
sIndirectLayerTrees[aLayersId].mLayerTreeClearedObserver = aObserver;
|
|
}
|
|
|
|
/**
|
|
* 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 final : public PCompositorParent,
|
|
public ShadowLayersManager
|
|
{
|
|
friend class CompositorParent;
|
|
|
|
NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_MAIN_THREAD_DESTRUCTION(CrossProcessCompositorParent)
|
|
public:
|
|
explicit CrossProcessCompositorParent(Transport* aTransport)
|
|
: mTransport(aTransport)
|
|
, mCompositorThreadHolder(sCompositorThreadHolder)
|
|
, mNotifyAfterRemotePaint(false)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
gfxPlatform::GetPlatform()->ComputeTileSize();
|
|
}
|
|
|
|
// IToplevelProtocol::CloneToplevel()
|
|
virtual IToplevelProtocol*
|
|
CloneToplevel(const InfallibleTArray<mozilla::ipc::ProtocolFdMapping>& aFds,
|
|
base::ProcessHandle aPeerProcess,
|
|
mozilla::ipc::ProtocolCloneContext* aCtx) override;
|
|
|
|
virtual void ActorDestroy(ActorDestroyReason aWhy) override;
|
|
|
|
// FIXME/bug 774388: work out what shutdown protocol we need.
|
|
virtual bool RecvRequestOverfill() override { return true; }
|
|
virtual bool RecvWillStop() override { return true; }
|
|
virtual bool RecvStop() override { return true; }
|
|
virtual bool RecvPause() override { return true; }
|
|
virtual bool RecvResume() override { return true; }
|
|
virtual bool RecvNotifyChildCreated(const uint64_t& child) override;
|
|
virtual bool RecvAdoptChild(const uint64_t& child) override { return false; }
|
|
virtual bool RecvMakeSnapshot(const SurfaceDescriptor& aInSnapshot,
|
|
const gfx::IntRect& aRect) override
|
|
{ return true; }
|
|
virtual bool RecvFlushRendering() override { return true; }
|
|
virtual bool RecvNotifyRegionInvalidated(const nsIntRegion& aRegion) override { return true; }
|
|
virtual bool RecvStartFrameTimeRecording(const int32_t& aBufferSize, uint32_t* aOutStartIndex) override { return true; }
|
|
virtual bool RecvStopFrameTimeRecording(const uint32_t& aStartIndex, InfallibleTArray<float>* intervals) override { return true; }
|
|
virtual bool RecvGetTileSize(int32_t* aWidth, int32_t* aHeight) override
|
|
{
|
|
*aWidth = gfxPlatform::GetPlatform()->GetTileWidth();
|
|
*aHeight = gfxPlatform::GetPlatform()->GetTileHeight();
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
* Tells this CompositorParent to send a message when the compositor has received the transaction.
|
|
*/
|
|
virtual bool RecvRequestNotifyAfterRemotePaint() override;
|
|
|
|
virtual PLayerTransactionParent*
|
|
AllocPLayerTransactionParent(const nsTArray<LayersBackend>& aBackendHints,
|
|
const uint64_t& aId,
|
|
TextureFactoryIdentifier* aTextureFactoryIdentifier,
|
|
bool *aSuccess) override;
|
|
|
|
virtual bool DeallocPLayerTransactionParent(PLayerTransactionParent* aLayers) override;
|
|
|
|
virtual void ShadowLayersUpdated(LayerTransactionParent* aLayerTree,
|
|
const uint64_t& aTransactionId,
|
|
const TargetConfig& aTargetConfig,
|
|
const InfallibleTArray<PluginWindowData>& aPlugins,
|
|
bool aIsFirstPaint,
|
|
bool aScheduleComposite,
|
|
uint32_t aPaintSequenceNumber,
|
|
bool aIsRepeatTransaction) override;
|
|
virtual void ForceComposite(LayerTransactionParent* aLayerTree) override;
|
|
virtual void NotifyClearCachedResources(LayerTransactionParent* aLayerTree) override;
|
|
virtual bool SetTestSampleTime(LayerTransactionParent* aLayerTree,
|
|
const TimeStamp& aTime) override;
|
|
virtual void LeaveTestMode(LayerTransactionParent* aLayerTree) override;
|
|
virtual void ApplyAsyncProperties(LayerTransactionParent* aLayerTree)
|
|
override;
|
|
virtual void GetAPZTestData(const LayerTransactionParent* aLayerTree,
|
|
APZTestData* aOutData) override;
|
|
virtual void SetConfirmedTargetAPZC(const LayerTransactionParent* aLayerTree,
|
|
const uint64_t& aInputBlockId,
|
|
const nsTArray<ScrollableLayerGuid>& aTargets) override;
|
|
|
|
virtual AsyncCompositionManager* GetCompositionManager(LayerTransactionParent* aParent) override;
|
|
|
|
void DidComposite(uint64_t aId);
|
|
|
|
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<CrossProcessCompositorParent> mSelfRef;
|
|
Transport* mTransport;
|
|
|
|
nsRefPtr<CompositorThreadHolder> mCompositorThreadHolder;
|
|
// If true, we should send a RemotePaintIsReady message when the layer transaction
|
|
// is received
|
|
bool mNotifyAfterRemotePaint;
|
|
};
|
|
|
|
void
|
|
CompositorParent::DidComposite()
|
|
{
|
|
if (mPendingTransaction) {
|
|
unused << SendDidComposite(0, mPendingTransaction);
|
|
mPendingTransaction = 0;
|
|
}
|
|
|
|
MonitorAutoLock lock(*sIndirectLayerTreesLock);
|
|
for (LayerTreeMap::iterator it = sIndirectLayerTrees.begin();
|
|
it != sIndirectLayerTrees.end(); it++) {
|
|
LayerTreeState* lts = &it->second;
|
|
if (lts->mParent == this && lts->mCrossProcessParent) {
|
|
static_cast<CrossProcessCompositorParent*>(lts->mCrossProcessParent)->DidComposite(it->first);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
OpenCompositor(CrossProcessCompositorParent* aCompositor,
|
|
Transport* aTransport, ProcessId aOtherPid,
|
|
MessageLoop* aIOLoop)
|
|
{
|
|
DebugOnly<bool> ok = aCompositor->Open(aTransport, aOtherPid, aIOLoop);
|
|
MOZ_ASSERT(ok);
|
|
}
|
|
|
|
/*static*/ PCompositorParent*
|
|
CompositorParent::Create(Transport* aTransport, ProcessId aOtherPid)
|
|
{
|
|
gfxPlatform::InitLayersIPC();
|
|
|
|
nsRefPtr<CrossProcessCompositorParent> cpcp =
|
|
new CrossProcessCompositorParent(aTransport);
|
|
|
|
cpcp->mSelfRef = cpcp;
|
|
CompositorLoop()->PostTask(
|
|
FROM_HERE,
|
|
NewRunnableFunction(OpenCompositor, cpcp.get(),
|
|
aTransport, aOtherPid, 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<mozilla::ipc::ProtocolFdMapping>& 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)
|
|
{
|
|
MonitorAutoLock lock(*sIndirectLayerTreesLock);
|
|
sIndirectLayerTrees[aId].mRoot = aRoot;
|
|
sIndirectLayerTrees[aId].mTargetConfig = aTargetConfig;
|
|
}
|
|
|
|
/* static */ CompositorParent::LayerTreeState*
|
|
CompositorParent::GetIndirectShadowTree(uint64_t aId)
|
|
{
|
|
MonitorAutoLock lock(*sIndirectLayerTreesLock);
|
|
LayerTreeMap::iterator cit = sIndirectLayerTrees.find(aId);
|
|
if (sIndirectLayerTrees.end() == cit) {
|
|
return nullptr;
|
|
}
|
|
return &cit->second;
|
|
}
|
|
|
|
bool
|
|
CrossProcessCompositorParent::RecvRequestNotifyAfterRemotePaint()
|
|
{
|
|
mNotifyAfterRemotePaint = true;
|
|
return true;
|
|
}
|
|
|
|
void
|
|
CrossProcessCompositorParent::ActorDestroy(ActorDestroyReason aWhy)
|
|
{
|
|
MessageLoop::current()->PostTask(
|
|
FROM_HERE,
|
|
NewRunnableMethod(this, &CrossProcessCompositorParent::DeferredDestroy));
|
|
}
|
|
|
|
PLayerTransactionParent*
|
|
CrossProcessCompositorParent::AllocPLayerTransactionParent(const nsTArray<LayersBackend>&,
|
|
const uint64_t& aId,
|
|
TextureFactoryIdentifier* aTextureFactoryIdentifier,
|
|
bool *aSuccess)
|
|
{
|
|
MOZ_ASSERT(aId != 0);
|
|
|
|
MonitorAutoLock lock(*sIndirectLayerTreesLock);
|
|
|
|
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);
|
|
p->AddIPDLReference();
|
|
sIndirectLayerTrees[aId].mLayerTree = p;
|
|
return p;
|
|
}
|
|
|
|
NS_WARNING("Created child without a matching parent?");
|
|
// XXX: should be false, but that causes us to fail some tests on Mac w/ OMTC.
|
|
// Bug 900745. change *aSuccess to false to see test failures.
|
|
*aSuccess = true;
|
|
LayerTransactionParent* p = new LayerTransactionParent(nullptr, this, aId);
|
|
p->AddIPDLReference();
|
|
return p;
|
|
}
|
|
|
|
bool
|
|
CrossProcessCompositorParent::DeallocPLayerTransactionParent(PLayerTransactionParent* aLayers)
|
|
{
|
|
LayerTransactionParent* slp = static_cast<LayerTransactionParent*>(aLayers);
|
|
EraseLayerState(slp->GetId());
|
|
static_cast<LayerTransactionParent*>(aLayers)->ReleaseIPDLReference();
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
CrossProcessCompositorParent::RecvNotifyChildCreated(const uint64_t& child)
|
|
{
|
|
MonitorAutoLock lock(*sIndirectLayerTreesLock);
|
|
for (LayerTreeMap::iterator it = sIndirectLayerTrees.begin();
|
|
it != sIndirectLayerTrees.end(); it++) {
|
|
CompositorParent::LayerTreeState* lts = &it->second;
|
|
if (lts->mParent && lts->mCrossProcessParent == this) {
|
|
lts->mParent->NotifyChildCreated(child);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void
|
|
CrossProcessCompositorParent::ShadowLayersUpdated(
|
|
LayerTransactionParent* aLayerTree,
|
|
const uint64_t& aTransactionId,
|
|
const TargetConfig& aTargetConfig,
|
|
const InfallibleTArray<PluginWindowData>& aPlugins,
|
|
bool aIsFirstPaint,
|
|
bool aScheduleComposite,
|
|
uint32_t aPaintSequenceNumber,
|
|
bool aIsRepeatTransaction)
|
|
{
|
|
uint64_t id = aLayerTree->GetId();
|
|
|
|
MOZ_ASSERT(id != 0);
|
|
|
|
CompositorParent::LayerTreeState* state = CompositorParent::GetIndirectShadowTree(id);
|
|
if (!state) {
|
|
return;
|
|
}
|
|
MOZ_ASSERT(state->mParent);
|
|
state->mParent->ScheduleRotationOnCompositorThread(aTargetConfig, aIsFirstPaint);
|
|
|
|
Layer* shadowRoot = aLayerTree->GetRoot();
|
|
if (shadowRoot) {
|
|
CompositorParent::SetShadowProperties(shadowRoot);
|
|
}
|
|
UpdateIndirectTree(id, shadowRoot, aTargetConfig);
|
|
|
|
// Cache the plugin data for this remote layer tree
|
|
state->mPluginData = aPlugins;
|
|
state->mUpdatedPluginDataAvailable = true;
|
|
|
|
state->mParent->NotifyShadowTreeTransaction(id, aIsFirstPaint, aScheduleComposite,
|
|
aPaintSequenceNumber, aIsRepeatTransaction);
|
|
|
|
// Send the 'remote paint ready' message to the content thread if it has already asked.
|
|
if(mNotifyAfterRemotePaint) {
|
|
unused << SendRemotePaintIsReady();
|
|
mNotifyAfterRemotePaint = false;
|
|
}
|
|
|
|
if (state->mLayerTreeReadyObserver) {
|
|
nsRefPtr<CompositorUpdateObserver> observer = state->mLayerTreeReadyObserver;
|
|
state->mLayerTreeReadyObserver = nullptr;
|
|
observer->ObserveUpdate(id, true);
|
|
}
|
|
|
|
aLayerTree->SetPendingTransactionId(aTransactionId);
|
|
}
|
|
|
|
#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
|
|
// Sends plugin window state changes to the main thread
|
|
static void
|
|
UpdatePluginWindowState(uint64_t aId)
|
|
{
|
|
CompositorParent::LayerTreeState& lts = sIndirectLayerTrees[aId];
|
|
if (!lts.mPluginData.Length() && !lts.mUpdatedPluginDataAvailable) {
|
|
return;
|
|
}
|
|
|
|
bool shouldComposePlugin = !!lts.mRoot &&
|
|
!!lts.mRoot->GetParent();
|
|
|
|
bool shouldHidePlugin = (!lts.mRoot ||
|
|
!lts.mRoot->GetParent()) &&
|
|
!lts.mUpdatedPluginDataAvailable;
|
|
if (shouldComposePlugin) {
|
|
if (!lts.mPluginData.Length()) {
|
|
// 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.
|
|
nsTArray<uintptr_t> aVisibleIdList;
|
|
unused << lts.mParent->SendUpdatePluginVisibility(aVisibleIdList);
|
|
lts.mUpdatedPluginDataAvailable = false;
|
|
return;
|
|
}
|
|
|
|
// Retrieve the offset and visible region of the layer that hosts
|
|
// the plugins, CompositorChild 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)) {
|
|
unused <<
|
|
lts.mParent->SendUpdatePluginConfigurations(offset, visibleRegion,
|
|
lts.mPluginData);
|
|
lts.mUpdatedPluginDataAvailable = false;
|
|
} else {
|
|
shouldHidePlugin = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Hide all plugins, this remote layer tree is no longer active
|
|
if (shouldHidePlugin) {
|
|
// hide all the plugins
|
|
for (uint32_t pluginsIdx = 0; pluginsIdx < lts.mPluginData.Length();
|
|
pluginsIdx++) {
|
|
lts.mPluginData[pluginsIdx].visible() = false;
|
|
}
|
|
nsIntPoint offset;
|
|
nsIntRegion region;
|
|
unused << lts.mParent->SendUpdatePluginConfigurations(offset,
|
|
region,
|
|
lts.mPluginData);
|
|
// Clear because there's no recovering from this until we receive
|
|
// new shadow layer plugin data in ShadowLayersUpdated.
|
|
lts.mPluginData.Clear();
|
|
}
|
|
}
|
|
#endif // #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
|
|
|
|
void
|
|
CrossProcessCompositorParent::DidComposite(uint64_t aId)
|
|
{
|
|
sIndirectLayerTreesLock->AssertCurrentThreadOwns();
|
|
LayerTransactionParent *layerTree = sIndirectLayerTrees[aId].mLayerTree;
|
|
if (layerTree && layerTree->GetPendingTransactionId()) {
|
|
unused << SendDidComposite(aId, layerTree->GetPendingTransactionId());
|
|
layerTree->SetPendingTransactionId(0);
|
|
}
|
|
#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
|
|
UpdatePluginWindowState(aId);
|
|
#endif
|
|
}
|
|
|
|
void
|
|
CrossProcessCompositorParent::ForceComposite(LayerTransactionParent* aLayerTree)
|
|
{
|
|
uint64_t id = aLayerTree->GetId();
|
|
MOZ_ASSERT(id != 0);
|
|
CompositorParent* parent;
|
|
{ // scope lock
|
|
MonitorAutoLock lock(*sIndirectLayerTreesLock);
|
|
parent = sIndirectLayerTrees[id].mParent;
|
|
}
|
|
if (parent) {
|
|
parent->ForceComposite(aLayerTree);
|
|
}
|
|
}
|
|
|
|
void
|
|
CrossProcessCompositorParent::NotifyClearCachedResources(LayerTransactionParent* aLayerTree)
|
|
{
|
|
uint64_t id = aLayerTree->GetId();
|
|
MOZ_ASSERT(id != 0);
|
|
|
|
nsRefPtr<CompositorUpdateObserver> observer;
|
|
{ // scope lock
|
|
MonitorAutoLock lock(*sIndirectLayerTreesLock);
|
|
observer = sIndirectLayerTrees[id].mLayerTreeClearedObserver;
|
|
sIndirectLayerTrees[id].mLayerTreeClearedObserver = nullptr;
|
|
}
|
|
if (observer) {
|
|
observer->ObserveUpdate(id, false);
|
|
}
|
|
}
|
|
|
|
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::ApplyAsyncProperties(
|
|
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->ApplyAsyncProperties(aLayerTree);
|
|
}
|
|
|
|
void
|
|
CrossProcessCompositorParent::GetAPZTestData(const LayerTransactionParent* aLayerTree,
|
|
APZTestData* aOutData)
|
|
{
|
|
uint64_t id = aLayerTree->GetId();
|
|
MOZ_ASSERT(id != 0);
|
|
MonitorAutoLock lock(*sIndirectLayerTreesLock);
|
|
*aOutData = sIndirectLayerTrees[id].mApzTestData;
|
|
}
|
|
|
|
void
|
|
CrossProcessCompositorParent::SetConfirmedTargetAPZC(const LayerTransactionParent* aLayerTree,
|
|
const uint64_t& aInputBlockId,
|
|
const nsTArray<ScrollableLayerGuid>& aTargets)
|
|
{
|
|
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->SetConfirmedTargetAPZC(aLayerTree, aInputBlockId, aTargets);
|
|
}
|
|
|
|
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()
|
|
{
|
|
mCompositorThreadHolder = nullptr;
|
|
mSelfRef = nullptr;
|
|
}
|
|
|
|
CrossProcessCompositorParent::~CrossProcessCompositorParent()
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
MOZ_ASSERT(XRE_GetIOMessageLoop());
|
|
XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
|
|
new DeleteTask<Transport>(mTransport));
|
|
}
|
|
|
|
IToplevelProtocol*
|
|
CrossProcessCompositorParent::CloneToplevel(const InfallibleTArray<mozilla::ipc::ProtocolFdMapping>& 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
|