Skip to content

Commit

Permalink
Bug 1317893 - Split CompositorVsyncScheduler to own file r=kats
Browse files Browse the repository at this point in the history
--HG--
rename : gfx/layers/ipc/CompositorBridgeParent.cpp => gfx/layers/ipc/CompositorVsyncScheduler.cpp
rename : gfx/layers/ipc/CompositorBridgeParent.h => gfx/layers/ipc/CompositorVsyncScheduler.h
  • Loading branch information
Sotaro Ikeda authored and rainemak committed May 27, 2020
1 parent a5fb5f6 commit 0806405
Show file tree
Hide file tree
Showing 5 changed files with 487 additions and 392 deletions.
302 changes: 0 additions & 302 deletions gfx/layers/ipc/CompositorBridgeParent.cpp
Expand Up @@ -280,308 +280,6 @@ CalculateCompositionFrameRate()
}
#endif

CompositorVsyncScheduler::Observer::Observer(CompositorVsyncScheduler* aOwner)
: mMutex("CompositorVsyncScheduler.Observer.Mutex")
, mOwner(aOwner)
{
}

CompositorVsyncScheduler::Observer::~Observer()
{
MOZ_ASSERT(!mOwner);
}

bool
CompositorVsyncScheduler::Observer::NotifyVsync(TimeStamp aVsyncTimestamp)
{
MutexAutoLock lock(mMutex);
if (!mOwner) {
return false;
}
return mOwner->NotifyVsync(aVsyncTimestamp);
}

void
CompositorVsyncScheduler::Observer::Destroy()
{
MutexAutoLock lock(mMutex);
mOwner = nullptr;
}

CompositorVsyncScheduler::CompositorVsyncScheduler(CompositorBridgeParent* aCompositorBridgeParent,
widget::CompositorWidget* aWidget)
: mCompositorBridgeParent(aCompositorBridgeParent)
, mLastCompose(TimeStamp::Now())
, mIsObservingVsync(false)
, mNeedsComposite(0)
, mVsyncNotificationsSkipped(0)
, mWidget(aWidget)
, mCurrentCompositeTaskMonitor("CurrentCompositeTaskMonitor")
, mCurrentCompositeTask(nullptr)
, mSetNeedsCompositeMonitor("SetNeedsCompositeMonitor")
, mSetNeedsCompositeTask(nullptr)
{
MOZ_ASSERT(NS_IsMainThread() || XRE_GetProcessType() == GeckoProcessType_GPU);
mVsyncObserver = new Observer(this);

// mAsapScheduling is set on the main thread during init,
// but is only accessed after on the compositor thread.
mAsapScheduling = gfxPrefs::LayersCompositionFrameRate() == 0 ||
gfxPlatform::IsInLayoutAsapMode();
}

CompositorVsyncScheduler::~CompositorVsyncScheduler()
{
MOZ_ASSERT(!mIsObservingVsync);
MOZ_ASSERT(!mVsyncObserver);
// The CompositorVsyncDispatcher is cleaned up before this in the nsBaseWidget, which stops vsync listeners
mCompositorBridgeParent = nullptr;
}

void
CompositorVsyncScheduler::Destroy()
{
if (!mVsyncObserver) {
// Destroy was already called on this object.
return;
}
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
UnobserveVsync();
mVsyncObserver->Destroy();
mVsyncObserver = nullptr;

CancelCurrentSetNeedsCompositeTask();
CancelCurrentCompositeTask();
}

void
CompositorVsyncScheduler::PostCompositeTask(TimeStamp aCompositeTimestamp)
{
// can be called from the compositor or vsync thread
MonitorAutoLock lock(mCurrentCompositeTaskMonitor);
if (mCurrentCompositeTask == nullptr && CompositorThreadHolder::Loop()) {
RefPtr<CancelableRunnable> task =
NewCancelableRunnableMethod<TimeStamp>(this, &CompositorVsyncScheduler::Composite,
aCompositeTimestamp);
mCurrentCompositeTask = task;
ScheduleTask(task.forget(), 0);
}
}

void
CompositorVsyncScheduler::ScheduleComposition()
{
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
if (mAsapScheduling) {
// Used only for performance testing purposes
PostCompositeTask(TimeStamp::Now());
#ifdef MOZ_WIDGET_ANDROID
} else if (mNeedsComposite >= 2 && mIsObservingVsync) {
// uh-oh, we already requested a composite at least twice so far, and a
// composite hasn't happened yet. It is possible that the vsync observation
// is blocked on the main thread, so let's just composite ASAP and not
// wait for the vsync. Note that this should only ever happen on Fennec
// because there content runs in the same process as the compositor, and so
// content can actually block the main thread in this process.
PostCompositeTask(TimeStamp::Now());
#endif
} else {
SetNeedsComposite();
}
}

void
CompositorVsyncScheduler::CancelCurrentSetNeedsCompositeTask()
{
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
MonitorAutoLock lock(mSetNeedsCompositeMonitor);
if (mSetNeedsCompositeTask) {
mSetNeedsCompositeTask->Cancel();
mSetNeedsCompositeTask = nullptr;
}
mNeedsComposite = 0;
}

/**
* TODO Potential performance heuristics:
* If a composite takes 17 ms, do we composite ASAP or wait until next vsync?
* If a layer transaction comes after vsync, do we composite ASAP or wait until
* next vsync?
* How many skipped vsync events until we stop listening to vsync events?
*/
void
CompositorVsyncScheduler::SetNeedsComposite()
{
if (!CompositorThreadHolder::IsInCompositorThread()) {
MonitorAutoLock lock(mSetNeedsCompositeMonitor);
RefPtr<CancelableRunnable> task =
NewCancelableRunnableMethod(this, &CompositorVsyncScheduler::SetNeedsComposite);
mSetNeedsCompositeTask = task;
ScheduleTask(task.forget(), 0);
return;
} else {
MonitorAutoLock lock(mSetNeedsCompositeMonitor);
mSetNeedsCompositeTask = nullptr;
}

mNeedsComposite++;
if (!mIsObservingVsync && mNeedsComposite) {
ObserveVsync();
}
}

bool
CompositorVsyncScheduler::NotifyVsync(TimeStamp aVsyncTimestamp)
{
// Called from the vsync dispatch thread. When in the GPU Process, that's
// the same as the compositor thread.
MOZ_ASSERT_IF(XRE_IsParentProcess(), !CompositorThreadHolder::IsInCompositorThread());
MOZ_ASSERT_IF(XRE_GetProcessType() == GeckoProcessType_GPU, CompositorThreadHolder::IsInCompositorThread());
MOZ_ASSERT(!NS_IsMainThread());
PostCompositeTask(aVsyncTimestamp);
return true;
}

void
CompositorVsyncScheduler::CancelCurrentCompositeTask()
{
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread() || NS_IsMainThread());
MonitorAutoLock lock(mCurrentCompositeTaskMonitor);
if (mCurrentCompositeTask) {
mCurrentCompositeTask->Cancel();
mCurrentCompositeTask = nullptr;
}
}

void
CompositorVsyncScheduler::Composite(TimeStamp aVsyncTimestamp)
{
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
{
MonitorAutoLock lock(mCurrentCompositeTaskMonitor);
mCurrentCompositeTask = nullptr;
}

if ((aVsyncTimestamp < mLastCompose) && !mAsapScheduling) {
// We can sometimes get vsync timestamps that are in the past
// compared to the last compose with force composites.
// In those cases, wait until the next vsync;
return;
}

MOZ_ASSERT(mCompositorBridgeParent);
if (!mAsapScheduling && mCompositorBridgeParent->IsPendingComposite()) {
// If previous composite is still on going, finish it and does a next
// composite in a next vsync.
mCompositorBridgeParent->FinishPendingComposite();
return;
}

DispatchTouchEvents(aVsyncTimestamp);
DispatchVREvents(aVsyncTimestamp);

if (mNeedsComposite || mAsapScheduling) {
mNeedsComposite = 0;
mLastCompose = aVsyncTimestamp;
ComposeToTarget(nullptr);
mVsyncNotificationsSkipped = 0;

TimeDuration compositeFrameTotal = TimeStamp::Now() - aVsyncTimestamp;
mozilla::Telemetry::Accumulate(mozilla::Telemetry::COMPOSITE_FRAME_ROUNDTRIP_TIME,
compositeFrameTotal.ToMilliseconds());
} else if (mVsyncNotificationsSkipped++ > gfxPrefs::CompositorUnobserveCount()) {
UnobserveVsync();
}
}

void
CompositorVsyncScheduler::OnForceComposeToTarget()
{
/**
* bug 1138502 - There are cases such as during long-running window resizing events
* where we receive many sync RecvFlushComposites. We also get vsync notifications which
* will increment mVsyncNotificationsSkipped because a composite just occurred. After
* enough vsyncs and RecvFlushComposites occurred, we will disable vsync. Then at the next
* ScheduleComposite, we will enable vsync, then get a RecvFlushComposite, which will
* force us to unobserve vsync again. On some platforms, enabling/disabling vsync is not
* free and this oscillating behavior causes a performance hit. In order to avoid this problem,
* we reset the mVsyncNotificationsSkipped counter to keep vsync enabled.
*/
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
mVsyncNotificationsSkipped = 0;
}

void
CompositorVsyncScheduler::ForceComposeToTarget(gfx::DrawTarget* aTarget, const IntRect* aRect)
{
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
OnForceComposeToTarget();
mLastCompose = TimeStamp::Now();
ComposeToTarget(aTarget, aRect);
}

bool
CompositorVsyncScheduler::NeedsComposite()
{
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
return mNeedsComposite;
}

void
CompositorVsyncScheduler::ObserveVsync()
{
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
mWidget->ObserveVsync(mVsyncObserver);
mIsObservingVsync = true;
}

void
CompositorVsyncScheduler::UnobserveVsync()
{
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
mWidget->ObserveVsync(nullptr);
mIsObservingVsync = false;
}

void
CompositorVsyncScheduler::DispatchTouchEvents(TimeStamp aVsyncTimestamp)
{
}

void
CompositorVsyncScheduler::DispatchVREvents(TimeStamp aVsyncTimestamp)
{
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());

VRManager* vm = VRManager::Get();
vm->NotifyVsync(aVsyncTimestamp);
}

void
CompositorVsyncScheduler::ScheduleTask(already_AddRefed<CancelableRunnable> aTask,
int aTime)
{
MOZ_ASSERT(CompositorThreadHolder::Loop());
MOZ_ASSERT(aTime >= 0);
CompositorThreadHolder::Loop()->PostDelayedTask(Move(aTask), aTime);
}

void
CompositorVsyncScheduler::ResumeComposition()
{
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
mLastCompose = TimeStamp::Now();
ComposeToTarget(nullptr);
}

void
CompositorVsyncScheduler::ComposeToTarget(gfx::DrawTarget* aTarget, const IntRect* aRect)
{
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
MOZ_ASSERT(mCompositorBridgeParent);
mCompositorBridgeParent->CompositeToTarget(aTarget, aRect);
}

static inline MessageLoop*
CompositorLoop()
{
Expand Down

0 comments on commit 0806405

Please sign in to comment.