mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
aff8275376
With image offscreen surfaces enabled for content layers on GTK3, our Basic compositor needs a way to deal with image layers buffering and compositing in a performant way. This patch subclasses BasicCompositor into a new X11BasicCompositor and makes use of a new TextureSource (X11DataTextureSourceBasic) in order to buffer TextureHost's data into gfxXlibSurface on compositor side so that we can use XRender when available to composite layer contents directly to the Window. When this buffering will occur, switch to ContentClientSingleBuffered. --HG-- extra : rebase_source : adad6b1c05dcf516a1ea84c6a529df5f141c198f
1492 lines
46 KiB
C++
1492 lines
46 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 "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 ProcessHandle
|
|
#include "base/process_util.h" // for OpenProcessHandle
|
|
#include "base/task.h" // for CancelableTask, etc
|
|
#include "base/thread.h" // for Thread
|
|
#include "base/tracked.h" // for FROM_HERE
|
|
#include "gfxContext.h" // for gfxContext
|
|
#include "gfxPlatform.h" // for gfxPlatform
|
|
#ifdef MOZ_WIDGET_GTK
|
|
#include "gfxPlatformGtk.h" // for gfxPlatform
|
|
#endif
|
|
#include "gfxPrefs.h" // for gfxPrefs
|
|
#include "ipc/ShadowLayersManager.h" // for ShadowLayersManager
|
|
#include "mozilla/AutoRestore.h" // for AutoRestore
|
|
#include "mozilla/DebugOnly.h" // for DebugOnly
|
|
#include "mozilla/gfx/2D.h" // for DrawTarget
|
|
#include "mozilla/gfx/Point.h" // for IntSize
|
|
#include "mozilla/ipc/Transport.h" // for Transport
|
|
#include "mozilla/layers/APZCTreeManager.h" // for APZCTreeManager
|
|
#include "mozilla/layers/AsyncCompositionManager.h"
|
|
#include "mozilla/layers/BasicCompositor.h" // for BasicCompositor
|
|
#include "mozilla/layers/Compositor.h" // for Compositor
|
|
#include "mozilla/layers/CompositorOGL.h" // for CompositorOGL
|
|
#include "mozilla/layers/CompositorTypes.h"
|
|
#include "mozilla/layers/LayerManagerComposite.h"
|
|
#include "mozilla/layers/LayersTypes.h"
|
|
#include "mozilla/layers/PLayerTransactionParent.h"
|
|
#include "mozilla/mozalloc.h" // for operator new, etc
|
|
#ifdef MOZ_WIDGET_GTK
|
|
#include "basic/X11BasicCompositor.h" // for X11BasicCompositor
|
|
#endif
|
|
#include "nsCOMPtr.h" // for already_AddRefed
|
|
#include "nsDebug.h" // for NS_ABORT_IF_FALSE, etc
|
|
#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
|
|
#include "nsIWidget.h" // for nsIWidget
|
|
#include "nsRect.h" // for nsIntRect
|
|
#include "nsTArray.h" // for nsTArray
|
|
#include "nsThreadUtils.h" // for NS_IsMainThread
|
|
#include "nsXULAppAPI.h" // for XRE_GetIOMessageLoop
|
|
#ifdef XP_WIN
|
|
#include "mozilla/layers/CompositorD3D11.h"
|
|
#include "mozilla/layers/CompositorD3D9.h"
|
|
#endif
|
|
#include "GeckoProfiler.h"
|
|
#include "mozilla/ipc/ProtocolTypes.h"
|
|
#include "mozilla/unused.h"
|
|
#include "mozilla/Hal.h"
|
|
#include "mozilla/HalTypes.h"
|
|
#include "mozilla/StaticPtr.h"
|
|
|
|
namespace mozilla {
|
|
namespace layers {
|
|
|
|
using namespace base;
|
|
using namespace mozilla::ipc;
|
|
using namespace mozilla::gfx;
|
|
using namespace std;
|
|
|
|
CompositorParent::LayerTreeState::LayerTreeState()
|
|
: mParent(nullptr)
|
|
, mLayerManager(nullptr)
|
|
, mCrossProcessParent(nullptr)
|
|
, mLayerTree(nullptr)
|
|
{
|
|
}
|
|
|
|
typedef map<uint64_t, CompositorParent::LayerTreeState> LayerTreeMap;
|
|
static LayerTreeMap sIndirectLayerTrees;
|
|
|
|
/**
|
|
* 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
|
|
/* 8192ms is chosen for permanent hangs because it's several seconds longer
|
|
than the default hang timeout on major platforms (about 5 seconds). */
|
|
options.permanent_hang_timeout = 8192; // milliseconds
|
|
|
|
if (!compositorThread->StartWithOptions(options)) {
|
|
delete compositorThread;
|
|
return nullptr;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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)
|
|
{
|
|
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.
|
|
CompositorLoop()->PostTask(FROM_HERE, NewRunnableFunction(&AddCompositor,
|
|
this, &mCompositorID));
|
|
|
|
CompositorLoop()->PostTask(FROM_HERE, NewRunnableFunction(SetThreadPriority));
|
|
|
|
mRootLayerTreeID = AllocateLayerTreeId();
|
|
sIndirectLayerTrees[mRootLayerTreeID].mParent = this;
|
|
|
|
mApzcTreeManager = new APZCTreeManager();
|
|
}
|
|
|
|
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()
|
|
{
|
|
NS_ABORT_IF_FALSE(ManagedPLayerTransactionParent().Length() == 0,
|
|
"CompositorParent destroyed before managed PLayerTransactionParent");
|
|
|
|
// Ensure that the layer manager is destructed on the compositor thread.
|
|
mLayerManager = nullptr;
|
|
if (mCompositor) {
|
|
mCompositor->Destroy();
|
|
}
|
|
mCompositor = nullptr;
|
|
|
|
mCompositionManager = nullptr;
|
|
mApzcTreeManager->ClearTree();
|
|
mApzcTreeManager = nullptr;
|
|
sIndirectLayerTrees.erase(mRootLayerTreeID);
|
|
}
|
|
|
|
void
|
|
CompositorParent::ForceIsFirstPaint()
|
|
{
|
|
mCompositionManager->ForceIsFirstPaint();
|
|
}
|
|
|
|
bool
|
|
CompositorParent::RecvWillStop()
|
|
{
|
|
mPaused = true;
|
|
RemoveCompositor(mCompositorID);
|
|
|
|
// Ensure that the layer manager is destroyed before CompositorChild.
|
|
if (mLayerManager) {
|
|
for (LayerTreeMap::iterator it = sIndirectLayerTrees.begin();
|
|
it != sIndirectLayerTrees.end(); it++)
|
|
{
|
|
LayerTreeState* lts = &it->second;
|
|
if (lts->mParent == this) {
|
|
mLayerManager->ClearCachedResources(lts->mRoot);
|
|
lts->mLayerManager = nullptr;
|
|
}
|
|
}
|
|
mLayerManager->Destroy();
|
|
mLayerManager = nullptr;
|
|
mCompositionManager = nullptr;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
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 nsIntRect& aRect)
|
|
{
|
|
RefPtr<DrawTarget> target = GetDrawTargetForDescriptor(aInSnapshot, gfx::BackendType::CAIRO);
|
|
ForceComposeToTarget(target, &aRect);
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
CompositorParent::RecvFlushRendering()
|
|
{
|
|
// If we're waiting to do a composite, then cancel it
|
|
// and do it immediately instead.
|
|
if (mCurrentCompositeTask) {
|
|
CancelCurrentCompositeTask();
|
|
ForceComposeToTarget(nullptr);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
CompositorParent::RecvNotifyRegionInvalidated(const nsIntRegion& aRegion)
|
|
{
|
|
if (mLayerManager) {
|
|
mLayerManager->AddInvalidRegion(aRegion);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
CompositorParent::RecvStartFrameTimeRecording(const int32_t& aBufferSize, uint32_t* aOutStartIndex)
|
|
{
|
|
if (mLayerManager) {
|
|
*aOutStartIndex = mLayerManager->StartFrameTimeRecording(aBufferSize);
|
|
} else {
|
|
*aOutStartIndex = 0;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
CompositorParent::RecvStopFrameTimeRecording(const uint32_t& aStartIndex,
|
|
InfallibleTArray<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;
|
|
sIndirectLayerTrees[mRootLayerTreeID].mLayerManager = nullptr;
|
|
mCompositionManager = nullptr;
|
|
mCompositor = nullptr;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
CompositorParent::ScheduleRenderOnCompositorThread()
|
|
{
|
|
CancelableTask *renderTask = NewRunnableMethod(this, &CompositorParent::ScheduleComposition);
|
|
CompositorLoop()->PostTask(FROM_HERE, renderTask);
|
|
}
|
|
|
|
void
|
|
CompositorParent::PauseComposition()
|
|
{
|
|
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;
|
|
|
|
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 (mCurrentCompositeTask) {
|
|
mCurrentCompositeTask->Cancel();
|
|
mCurrentCompositeTask = nullptr;
|
|
}
|
|
}
|
|
|
|
void
|
|
CompositorParent::SetEGLSurfaceSize(int width, int height)
|
|
{
|
|
NS_ASSERTION(mUseExternalSurfaceSize, "Compositor created without UseExternalSurfaceSize provided");
|
|
mEGLSurfaceSize.SizeTo(width, height);
|
|
if (mCompositor) {
|
|
mCompositor->SetDestinationSurfaceSize(gfx::IntSize(mEGLSurfaceSize.width, mEGLSurfaceSize.height));
|
|
}
|
|
}
|
|
|
|
void
|
|
CompositorParent::ResumeCompositionAndResize(int width, int height)
|
|
{
|
|
SetEGLSurfaceSize(width, height);
|
|
ResumeComposition();
|
|
}
|
|
|
|
/*
|
|
* This will execute a pause synchronously, waiting to make sure that the compositor
|
|
* really is paused.
|
|
*/
|
|
void
|
|
CompositorParent::SchedulePauseOnCompositorThread()
|
|
{
|
|
MonitorAutoLock lock(mPauseCompositionMonitor);
|
|
|
|
CancelableTask *pauseTask = NewRunnableMethod(this,
|
|
&CompositorParent::PauseComposition);
|
|
CompositorLoop()->PostTask(FROM_HERE, pauseTask);
|
|
|
|
// Wait until the pause has actually been processed by the compositor thread
|
|
lock.Wait();
|
|
}
|
|
|
|
bool
|
|
CompositorParent::ScheduleResumeOnCompositorThread(int width, int height)
|
|
{
|
|
MonitorAutoLock lock(mResumeCompositionMonitor);
|
|
|
|
CancelableTask *resumeTask =
|
|
NewRunnableMethod(this, &CompositorParent::ResumeCompositionAndResize, width, height);
|
|
CompositorLoop()->PostTask(FROM_HERE, resumeTask);
|
|
|
|
// Wait until the resume has actually been processed by the compositor thread
|
|
lock.Wait();
|
|
|
|
return !mPaused;
|
|
}
|
|
|
|
void
|
|
CompositorParent::ScheduleTask(CancelableTask* task, int time)
|
|
{
|
|
if (time == 0) {
|
|
MessageLoop::current()->PostTask(FROM_HERE, task);
|
|
} else {
|
|
MessageLoop::current()->PostDelayedTask(FROM_HERE, task, time);
|
|
}
|
|
}
|
|
|
|
void
|
|
CompositorParent::NotifyShadowTreeTransaction(uint64_t aId, bool aIsFirstPaint,
|
|
bool aScheduleComposite, uint32_t aPaintSequenceNumber,
|
|
bool aIsRepeatTransaction)
|
|
{
|
|
if (mApzcTreeManager &&
|
|
!aIsRepeatTransaction &&
|
|
mLayerManager &&
|
|
mLayerManager->GetRoot()) {
|
|
AutoResolveRefLayers resolve(mCompositionManager);
|
|
mApzcTreeManager->UpdatePanZoomControllerTree(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::ScheduleComposition()
|
|
{
|
|
if (mCurrentCompositeTask || mPaused) {
|
|
return;
|
|
}
|
|
|
|
bool initialComposition = mLastCompose.IsNull();
|
|
TimeDuration delta;
|
|
if (!initialComposition)
|
|
delta = TimeStamp::Now() - mLastCompose;
|
|
|
|
int32_t rate = CalculateCompositionFrameRate();
|
|
|
|
// If rate == 0 (ASAP mode), minFrameDelta must be 0 so there's no delay.
|
|
TimeDuration minFrameDelta = TimeDuration::FromMilliseconds(
|
|
rate == 0 ? 0.0 : std::max(0.0, 1000.0 / rate));
|
|
|
|
|
|
mCurrentCompositeTask = NewRunnableMethod(this, &CompositorParent::CompositeCallback);
|
|
|
|
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::CompositeCallback()
|
|
{
|
|
mCurrentCompositeTask = nullptr;
|
|
CompositeToTarget(nullptr);
|
|
}
|
|
|
|
void
|
|
CompositorParent::CompositeToTarget(DrawTarget* aTarget, const nsIntRect* 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");
|
|
|
|
#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
|
|
|
|
mLastCompose = TimeStamp::Now();
|
|
|
|
if (!CanComposite()) {
|
|
DidComposite();
|
|
return;
|
|
}
|
|
|
|
AutoResolveRefLayers resolve(mCompositionManager);
|
|
|
|
if (aTarget) {
|
|
mLayerManager->BeginTransactionWithDrawTarget(aTarget, *aRect);
|
|
} else {
|
|
mLayerManager->BeginTransaction();
|
|
}
|
|
|
|
if (mForceCompositionTask && !mOverrideComposeReadiness) {
|
|
if (mCompositionManager->ReadyForCompose()) {
|
|
mForceCompositionTask->Cancel();
|
|
mForceCompositionTask = nullptr;
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
|
|
TimeStamp time = mIsTesting ? mTestTime : mLastCompose;
|
|
bool requestNextFrame = mCompositionManager->TransformShadowTree(time);
|
|
if (requestNextFrame) {
|
|
ScheduleComposition();
|
|
}
|
|
|
|
RenderTraceLayers(mLayerManager->GetRoot(), "0000");
|
|
|
|
mCompositionManager->ComputeRotation();
|
|
|
|
#ifdef MOZ_DUMP_PAINTING
|
|
static bool gDumpCompositorTree = false;
|
|
if (gDumpCompositorTree) {
|
|
printf_stderr("Painting --- compositing layer tree:\n");
|
|
mLayerManager->Dump();
|
|
}
|
|
#endif
|
|
mLayerManager->SetDebugOverlayWantsNextFrame(false);
|
|
mLayerManager->EndEmptyTransaction();
|
|
|
|
if (!aTarget) {
|
|
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();
|
|
}
|
|
|
|
profiler_tracing("Paint", "Composite", TRACING_INTERVAL_END);
|
|
}
|
|
|
|
void
|
|
CompositorParent::ForceComposeToTarget(DrawTarget* aTarget, const nsIntRect* aRect)
|
|
{
|
|
PROFILER_LABEL("CompositorParent", "ForceComposeToTarget",
|
|
js::ProfileEntry::Category::GRAPHICS);
|
|
|
|
AutoRestore<bool> override(mOverrideComposeReadiness);
|
|
mOverrideComposeReadiness = true;
|
|
|
|
CompositeToTarget(aTarget, aRect);
|
|
}
|
|
|
|
bool
|
|
CompositorParent::CanComposite()
|
|
{
|
|
return mLayerManager &&
|
|
mLayerManager->GetRoot() &&
|
|
!mPaused;
|
|
}
|
|
|
|
// Go down the composite layer tree, setting properties to match their
|
|
// content-side counterparts.
|
|
static void
|
|
SetShadowProperties(Layer* aLayer)
|
|
{
|
|
// FIXME: Bug 717688 -- Do these updates in LayerTransactionParent::RecvUpdate.
|
|
LayerComposite* layerComposite = aLayer->AsLayerComposite();
|
|
// Set the layerComposite's base transform to the layer's base transform.
|
|
layerComposite->SetShadowTransform(aLayer->GetBaseTransform());
|
|
layerComposite->SetShadowTransformSetByAnimation(false);
|
|
layerComposite->SetShadowVisibleRegion(aLayer->GetVisibleRegion());
|
|
layerComposite->SetShadowClipRect(aLayer->GetClipRect());
|
|
layerComposite->SetShadowOpacity(aLayer->GetOpacity());
|
|
|
|
for (Layer* child = aLayer->GetFirstChild();
|
|
child; child = child->GetNextSibling()) {
|
|
SetShadowProperties(child);
|
|
}
|
|
}
|
|
|
|
void
|
|
CompositorParent::ScheduleRotationOnCompositorThread(const TargetConfig& aTargetConfig,
|
|
bool aIsFirstPaint)
|
|
{
|
|
MOZ_ASSERT(IsInCompositorThread());
|
|
|
|
if (!aIsFirstPaint &&
|
|
!mCompositionManager->IsFirstPaint() &&
|
|
mCompositionManager->RequiresReorientation(aTargetConfig.orientation())) {
|
|
if (mForceCompositionTask != nullptr) {
|
|
mForceCompositionTask->Cancel();
|
|
}
|
|
mForceCompositionTask = NewRunnableMethod(this, &CompositorParent::ForceComposition);
|
|
ScheduleTask(mForceCompositionTask, gfxPrefs::OrientationSyncMillis());
|
|
}
|
|
}
|
|
|
|
void
|
|
CompositorParent::ShadowLayersUpdated(LayerTransactionParent* aLayerTree,
|
|
const uint64_t& aTransactionId,
|
|
const TargetConfig& aTargetConfig,
|
|
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.clientBounds());
|
|
mLayerManager->SetRegionToClear(aTargetConfig.clearRegion());
|
|
|
|
mCompositionManager->Updated(aIsFirstPaint, aTargetConfig);
|
|
Layer* root = aLayerTree->GetRoot();
|
|
mLayerManager->SetRoot(root);
|
|
|
|
if (mApzcTreeManager && !aIsRepeatTransaction) {
|
|
AutoResolveRefLayers resolve(mCompositionManager);
|
|
mApzcTreeManager->UpdatePanZoomControllerTree(this, root, aIsFirstPaint,
|
|
mRootLayerTreeID, aPaintSequenceNumber);
|
|
}
|
|
|
|
MOZ_ASSERT(aTransactionId > mPendingTransaction);
|
|
mPendingTransaction = aTransactionId;
|
|
|
|
if (root) {
|
|
SetShadowProperties(root);
|
|
}
|
|
if (aScheduleComposite) {
|
|
ScheduleComposition();
|
|
if (mPaused) {
|
|
DidComposite();
|
|
}
|
|
// When testing we synchronously update the shadow tree with the animated
|
|
// values to avoid race conditions when calling GetAnimationTransform etc.
|
|
// (since the above SetShadowProperties will remove animation effects).
|
|
// However, we only do this update when a composite operation is already
|
|
// scheduled in order to better match the behavior under regular sampling
|
|
// conditions.
|
|
if (mIsTesting && root && mCurrentCompositeTask) {
|
|
AutoResolveRefLayers resolve(mCompositionManager);
|
|
bool requestNextFrame =
|
|
mCompositionManager->TransformShadowTree(mTestTime);
|
|
if (!requestNextFrame) {
|
|
CancelCurrentCompositeTask();
|
|
// Pretend we composited in case someone is wating for this event.
|
|
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;
|
|
|
|
// Update but only if we were already scheduled to animate
|
|
if (mCompositionManager && mCurrentCompositeTask) {
|
|
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;
|
|
}
|
|
|
|
bool
|
|
CompositorParent::RecvRequestOverfill()
|
|
{
|
|
uint32_t overfillRatio = mCompositor->GetFillRatio();
|
|
unused << SendOverfill(overfillRatio);
|
|
return true;
|
|
}
|
|
|
|
void
|
|
CompositorParent::GetAPZTestData(const LayerTransactionParent* aLayerTree,
|
|
APZTestData* aOutData)
|
|
{
|
|
*aOutData = sIndirectLayerTrees[mRootLayerTreeID].mApzTestData;
|
|
}
|
|
|
|
|
|
void
|
|
CompositorParent::InitializeLayerManager(const nsTArray<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;
|
|
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.
|
|
nsIntRect rect;
|
|
mWidget->GetClientBounds(rect);
|
|
InitializeLayerManager(aBackendHints);
|
|
mWidget = nullptr;
|
|
|
|
if (!mLayerManager) {
|
|
NS_WARNING("Failed to initialise Compositor");
|
|
*aSuccess = false;
|
|
LayerTransactionParent* p = new LayerTransactionParent(nullptr, this, 0,
|
|
// child side's process id is current process Id
|
|
base::GetProcId(base::GetCurrentProcessHandle()));
|
|
p->AddIPDLReference();
|
|
return p;
|
|
}
|
|
|
|
mCompositionManager = new AsyncCompositionManager(mLayerManager);
|
|
*aSuccess = true;
|
|
|
|
*aTextureFactoryIdentifier = mCompositor->GetTextureFactoryIdentifier();
|
|
LayerTransactionParent* p = new LayerTransactionParent(mLayerManager, this, 0,
|
|
// child side's process id is current process Id
|
|
base::GetProcId(base::GetCurrentProcessHandle()));
|
|
p->AddIPDLReference();
|
|
return p;
|
|
}
|
|
|
|
bool
|
|
CompositorParent::DeallocPLayerTransactionParent(PLayerTransactionParent* actor)
|
|
{
|
|
static_cast<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)
|
|
{
|
|
NotifyChildCreated(child);
|
|
return true;
|
|
}
|
|
|
|
void
|
|
CompositorParent::NotifyChildCreated(const uint64_t& aChild)
|
|
{
|
|
sIndirectLayerTrees[aChild].mParent = this;
|
|
sIndirectLayerTrees[aChild].mLayerManager = mLayerManager;
|
|
}
|
|
|
|
/*static*/ uint64_t
|
|
CompositorParent::AllocateLayerTreeId()
|
|
{
|
|
MOZ_ASSERT(CompositorLoop());
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
static uint64_t ids = 0;
|
|
return ++ids;
|
|
}
|
|
|
|
static void
|
|
EraseLayerState(uint64_t aId)
|
|
{
|
|
sIndirectLayerTrees.erase(aId);
|
|
}
|
|
|
|
/*static*/ void
|
|
CompositorParent::DeallocateLayerTreeId(uint64_t aId)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
// Here main thread notifies compositor to remove an element from
|
|
// sIndirectLayerTrees. This removed element might be queried soon.
|
|
// Checking the elements of sIndirectLayerTrees exist or not before using.
|
|
CompositorLoop()->PostTask(FROM_HERE,
|
|
NewRunnableFunction(&EraseLayerState, aId));
|
|
}
|
|
|
|
static void
|
|
UpdateControllerForLayersId(uint64_t aLayersId,
|
|
GeckoContentController* aController)
|
|
{
|
|
// Adopt ref given to us by SetControllerForLayerTree()
|
|
sIndirectLayerTrees[aLayersId].mController =
|
|
already_AddRefed<GeckoContentController>(aController);
|
|
}
|
|
|
|
ScopedLayerTreeRegistration::ScopedLayerTreeRegistration(uint64_t aLayersId,
|
|
Layer* aRoot,
|
|
GeckoContentController* aController)
|
|
: mLayersId(aLayersId)
|
|
{
|
|
sIndirectLayerTrees[aLayersId].mRoot = aRoot;
|
|
sIndirectLayerTrees[aLayersId].mController = aController;
|
|
}
|
|
|
|
ScopedLayerTreeRegistration::~ScopedLayerTreeRegistration()
|
|
{
|
|
sIndirectLayerTrees.erase(mLayersId);
|
|
}
|
|
|
|
/*static*/ void
|
|
CompositorParent::SetControllerForLayerTree(uint64_t aLayersId,
|
|
GeckoContentController* aController)
|
|
{
|
|
// This ref is adopted by UpdateControllerForLayersId().
|
|
aController->AddRef();
|
|
CompositorLoop()->PostTask(FROM_HERE,
|
|
NewRunnableFunction(&UpdateControllerForLayersId,
|
|
aLayersId,
|
|
aController));
|
|
}
|
|
|
|
/*static*/ APZCTreeManager*
|
|
CompositorParent::GetAPZCTreeManager(uint64_t aLayersId)
|
|
{
|
|
const CompositorParent::LayerTreeState* state = CompositorParent::GetIndirectShadowTree(aLayersId);
|
|
if (state && state->mParent) {
|
|
return state->mParent->mApzcTreeManager;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
float
|
|
CompositorParent::ComputeRenderIntegrity()
|
|
{
|
|
if (mLayerManager) {
|
|
return mLayerManager->ComputeRenderIntegrity();
|
|
}
|
|
|
|
return 1.0f;
|
|
}
|
|
|
|
|
|
/**
|
|
* This class handles layer updates pushed directly from child
|
|
* processes to the compositor thread. It's associated with a
|
|
* CompositorParent on the compositor thread. While it uses the
|
|
* PCompositor protocol to manage these updates, it doesn't actually
|
|
* drive compositing itself. For that it hands off work to the
|
|
* CompositorParent it's associated with.
|
|
*/
|
|
class CrossProcessCompositorParent MOZ_FINAL : public PCompositorParent,
|
|
public ShadowLayersManager
|
|
{
|
|
friend class CompositorParent;
|
|
|
|
NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_MAIN_THREAD_DESTRUCTION(CrossProcessCompositorParent)
|
|
public:
|
|
CrossProcessCompositorParent(Transport* aTransport, ProcessId aOtherProcess)
|
|
: mTransport(aTransport)
|
|
, mChildProcessId(aOtherProcess)
|
|
, mCompositorThreadHolder(sCompositorThreadHolder)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
}
|
|
|
|
// IToplevelProtocol::CloneToplevel()
|
|
virtual IToplevelProtocol*
|
|
CloneToplevel(const InfallibleTArray<mozilla::ipc::ProtocolFdMapping>& aFds,
|
|
base::ProcessHandle aPeerProcess,
|
|
mozilla::ipc::ProtocolCloneContext* aCtx) MOZ_OVERRIDE;
|
|
|
|
virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
|
|
|
|
// FIXME/bug 774388: work out what shutdown protocol we need.
|
|
virtual bool RecvRequestOverfill() MOZ_OVERRIDE { return true; }
|
|
virtual bool RecvWillStop() MOZ_OVERRIDE { return true; }
|
|
virtual bool RecvStop() MOZ_OVERRIDE { return true; }
|
|
virtual bool RecvPause() MOZ_OVERRIDE { return true; }
|
|
virtual bool RecvResume() MOZ_OVERRIDE { return true; }
|
|
virtual bool RecvNotifyChildCreated(const uint64_t& child) MOZ_OVERRIDE;
|
|
virtual bool RecvMakeSnapshot(const SurfaceDescriptor& aInSnapshot,
|
|
const nsIntRect& aRect)
|
|
{ return true; }
|
|
virtual bool RecvFlushRendering() MOZ_OVERRIDE { return true; }
|
|
virtual bool RecvNotifyRegionInvalidated(const nsIntRegion& aRegion) { return true; }
|
|
virtual bool RecvStartFrameTimeRecording(const int32_t& aBufferSize, uint32_t* aOutStartIndex) MOZ_OVERRIDE { return true; }
|
|
virtual bool RecvStopFrameTimeRecording(const uint32_t& aStartIndex, InfallibleTArray<float>* intervals) MOZ_OVERRIDE { return true; }
|
|
|
|
virtual PLayerTransactionParent*
|
|
AllocPLayerTransactionParent(const nsTArray<LayersBackend>& aBackendHints,
|
|
const uint64_t& aId,
|
|
TextureFactoryIdentifier* aTextureFactoryIdentifier,
|
|
bool *aSuccess) MOZ_OVERRIDE;
|
|
|
|
virtual bool DeallocPLayerTransactionParent(PLayerTransactionParent* aLayers) MOZ_OVERRIDE;
|
|
|
|
virtual void ShadowLayersUpdated(LayerTransactionParent* aLayerTree,
|
|
const uint64_t& aTransactionId,
|
|
const TargetConfig& aTargetConfig,
|
|
bool aIsFirstPaint,
|
|
bool aScheduleComposite,
|
|
uint32_t aPaintSequenceNumber,
|
|
bool aIsRepeatTransaction) MOZ_OVERRIDE;
|
|
virtual void ForceComposite(LayerTransactionParent* aLayerTree) MOZ_OVERRIDE;
|
|
virtual bool SetTestSampleTime(LayerTransactionParent* aLayerTree,
|
|
const TimeStamp& aTime) MOZ_OVERRIDE;
|
|
virtual void LeaveTestMode(LayerTransactionParent* aLayerTree) MOZ_OVERRIDE;
|
|
virtual void GetAPZTestData(const LayerTransactionParent* aLayerTree,
|
|
APZTestData* aOutData) MOZ_OVERRIDE;
|
|
|
|
virtual AsyncCompositionManager* GetCompositionManager(LayerTransactionParent* aParent) MOZ_OVERRIDE;
|
|
|
|
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;
|
|
// Child side's process Id.
|
|
base::ProcessId mChildProcessId;
|
|
|
|
nsRefPtr<CompositorThreadHolder> mCompositorThreadHolder;
|
|
};
|
|
|
|
void
|
|
CompositorParent::DidComposite()
|
|
{
|
|
if (mPendingTransaction) {
|
|
unused << SendDidComposite(0, mPendingTransaction);
|
|
mPendingTransaction = 0;
|
|
}
|
|
|
|
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, ProcessHandle aHandle,
|
|
MessageLoop* aIOLoop)
|
|
{
|
|
DebugOnly<bool> ok = aCompositor->Open(aTransport, aHandle, aIOLoop);
|
|
MOZ_ASSERT(ok);
|
|
}
|
|
|
|
/*static*/ PCompositorParent*
|
|
CompositorParent::Create(Transport* aTransport, ProcessId aOtherProcess)
|
|
{
|
|
gfxPlatform::InitLayersIPC();
|
|
|
|
nsRefPtr<CrossProcessCompositorParent> cpcp =
|
|
new CrossProcessCompositorParent(aTransport, aOtherProcess);
|
|
ProcessHandle handle;
|
|
if (!base::OpenProcessHandle(aOtherProcess, &handle)) {
|
|
// XXX need to kill |aOtherProcess|, it's boned
|
|
return nullptr;
|
|
}
|
|
|
|
cpcp->mSelfRef = cpcp;
|
|
CompositorLoop()->PostTask(
|
|
FROM_HERE,
|
|
NewRunnableFunction(OpenCompositor, cpcp.get(),
|
|
aTransport, handle, XRE_GetIOMessageLoop()));
|
|
// The return value is just compared to null for success checking,
|
|
// we're not sharing a ref.
|
|
return cpcp.get();
|
|
}
|
|
|
|
IToplevelProtocol*
|
|
CompositorParent::CloneToplevel(const InfallibleTArray<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)
|
|
{
|
|
sIndirectLayerTrees[aId].mRoot = aRoot;
|
|
sIndirectLayerTrees[aId].mTargetConfig = aTargetConfig;
|
|
}
|
|
|
|
/* static */ CompositorParent::LayerTreeState*
|
|
CompositorParent::GetIndirectShadowTree(uint64_t aId)
|
|
{
|
|
LayerTreeMap::iterator cit = sIndirectLayerTrees.find(aId);
|
|
if (sIndirectLayerTrees.end() == cit) {
|
|
return nullptr;
|
|
}
|
|
return &cit->second;
|
|
}
|
|
|
|
static void
|
|
RemoveIndirectTree(uint64_t aId)
|
|
{
|
|
sIndirectLayerTrees.erase(aId);
|
|
}
|
|
|
|
void
|
|
CrossProcessCompositorParent::ActorDestroy(ActorDestroyReason aWhy)
|
|
{
|
|
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);
|
|
|
|
CompositorParent::LayerTreeState* state = nullptr;
|
|
LayerTreeMap::iterator itr = sIndirectLayerTrees.find(aId);
|
|
if (sIndirectLayerTrees.end() != itr) {
|
|
state = &itr->second;
|
|
}
|
|
|
|
if (state && state->mLayerManager) {
|
|
state->mCrossProcessParent = this;
|
|
LayerManagerComposite* lm = state->mLayerManager;
|
|
*aTextureFactoryIdentifier = lm->GetCompositor()->GetTextureFactoryIdentifier();
|
|
*aSuccess = true;
|
|
LayerTransactionParent* p = new LayerTransactionParent(lm, this, aId, mChildProcessId);
|
|
p->AddIPDLReference();
|
|
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, mChildProcessId);
|
|
p->AddIPDLReference();
|
|
return p;
|
|
}
|
|
|
|
bool
|
|
CrossProcessCompositorParent::DeallocPLayerTransactionParent(PLayerTransactionParent* aLayers)
|
|
{
|
|
LayerTransactionParent* slp = static_cast<LayerTransactionParent*>(aLayers);
|
|
RemoveIndirectTree(slp->GetId());
|
|
static_cast<LayerTransactionParent*>(aLayers)->ReleaseIPDLReference();
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
CrossProcessCompositorParent::RecvNotifyChildCreated(const uint64_t& child)
|
|
{
|
|
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,
|
|
bool aIsFirstPaint,
|
|
bool aScheduleComposite,
|
|
uint32_t aPaintSequenceNumber,
|
|
bool aIsRepeatTransaction)
|
|
{
|
|
uint64_t id = aLayerTree->GetId();
|
|
|
|
MOZ_ASSERT(id != 0);
|
|
|
|
const CompositorParent::LayerTreeState* state = CompositorParent::GetIndirectShadowTree(id);
|
|
if (!state) {
|
|
return;
|
|
}
|
|
MOZ_ASSERT(state->mParent);
|
|
state->mParent->ScheduleRotationOnCompositorThread(aTargetConfig, aIsFirstPaint);
|
|
|
|
Layer* shadowRoot = aLayerTree->GetRoot();
|
|
if (shadowRoot) {
|
|
SetShadowProperties(shadowRoot);
|
|
}
|
|
UpdateIndirectTree(id, shadowRoot, aTargetConfig);
|
|
|
|
state->mParent->NotifyShadowTreeTransaction(id, aIsFirstPaint, aScheduleComposite,
|
|
aPaintSequenceNumber, aIsRepeatTransaction);
|
|
aLayerTree->SetPendingTransactionId(aTransactionId);
|
|
}
|
|
|
|
void
|
|
CrossProcessCompositorParent::DidComposite(uint64_t aId)
|
|
{
|
|
LayerTransactionParent *layerTree = sIndirectLayerTrees[aId].mLayerTree;
|
|
if (layerTree && layerTree->GetPendingTransactionId()) {
|
|
unused << SendDidComposite(aId, layerTree->GetPendingTransactionId());
|
|
layerTree->SetPendingTransactionId(0);
|
|
}
|
|
}
|
|
|
|
void
|
|
CrossProcessCompositorParent::ForceComposite(LayerTransactionParent* aLayerTree)
|
|
{
|
|
uint64_t id = aLayerTree->GetId();
|
|
MOZ_ASSERT(id != 0);
|
|
sIndirectLayerTrees[id].mParent->ForceComposite(aLayerTree);
|
|
}
|
|
|
|
bool
|
|
CrossProcessCompositorParent::SetTestSampleTime(
|
|
LayerTransactionParent* aLayerTree, const TimeStamp& aTime)
|
|
{
|
|
uint64_t id = aLayerTree->GetId();
|
|
MOZ_ASSERT(id != 0);
|
|
const CompositorParent::LayerTreeState* state = CompositorParent::GetIndirectShadowTree(id);
|
|
if (!state) {
|
|
return false;
|
|
}
|
|
|
|
MOZ_ASSERT(state->mParent);
|
|
return state->mParent->SetTestSampleTime(aLayerTree, aTime);
|
|
}
|
|
|
|
void
|
|
CrossProcessCompositorParent::LeaveTestMode(LayerTransactionParent* aLayerTree)
|
|
{
|
|
uint64_t id = aLayerTree->GetId();
|
|
MOZ_ASSERT(id != 0);
|
|
const CompositorParent::LayerTreeState* state = CompositorParent::GetIndirectShadowTree(id);
|
|
if (!state) {
|
|
return;
|
|
}
|
|
|
|
MOZ_ASSERT(state->mParent);
|
|
state->mParent->LeaveTestMode(aLayerTree);
|
|
}
|
|
|
|
void
|
|
CrossProcessCompositorParent::GetAPZTestData(const LayerTransactionParent* aLayerTree,
|
|
APZTestData* aOutData)
|
|
{
|
|
uint64_t id = aLayerTree->GetId();
|
|
MOZ_ASSERT(id != 0);
|
|
*aOutData = sIndirectLayerTrees[id].mApzTestData;
|
|
}
|
|
|
|
|
|
AsyncCompositionManager*
|
|
CrossProcessCompositorParent::GetCompositionManager(LayerTransactionParent* aLayerTree)
|
|
{
|
|
uint64_t id = aLayerTree->GetId();
|
|
const CompositorParent::LayerTreeState* state = CompositorParent::GetIndirectShadowTree(id);
|
|
if (!state) {
|
|
return nullptr;
|
|
}
|
|
|
|
MOZ_ASSERT(state->mParent);
|
|
return state->mParent->GetCompositionManager(aLayerTree);
|
|
}
|
|
|
|
void
|
|
CrossProcessCompositorParent::DeferredDestroy()
|
|
{
|
|
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
|