LCOV - code coverage report
Current view: top level - gfx/layers/ipc - CompositorVsyncScheduler.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 105 153 68.6 %
Date: 2017-07-14 16:53:18 Functions: 18 26 69.2 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set sw=2 ts=2 et tw=80 : */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "mozilla/layers/CompositorVsyncScheduler.h"
       8             : 
       9             : #include <stdio.h>                      // for fprintf, stdout
      10             : #include <stdint.h>                     // for uint64_t
      11             : #include "base/task.h"                  // for CancelableTask, etc
      12             : #include "base/thread.h"                // for Thread
      13             : #include "gfxPlatform.h"                // for gfxPlatform
      14             : #ifdef MOZ_WIDGET_GTK
      15             : #include "gfxPlatformGtk.h"             // for gfxPlatform
      16             : #endif
      17             : #include "gfxPrefs.h"                   // for gfxPrefs
      18             : #include "mozilla/AutoRestore.h"        // for AutoRestore
      19             : #include "mozilla/DebugOnly.h"          // for DebugOnly
      20             : #include "mozilla/gfx/2D.h"             // for DrawTarget
      21             : #include "mozilla/gfx/Point.h"          // for IntSize
      22             : #include "mozilla/gfx/Rect.h"           // for IntSize
      23             : #include "mozilla/layers/CompositorThread.h"
      24             : #include "mozilla/layers/CompositorVsyncSchedulerOwner.h"
      25             : #include "mozilla/mozalloc.h"           // for operator new, etc
      26             : #include "nsCOMPtr.h"                   // for already_AddRefed
      27             : #include "nsDebug.h"                    // for NS_ASSERTION, etc
      28             : #include "nsISupportsImpl.h"            // for MOZ_COUNT_CTOR, etc
      29             : #include "nsIWidget.h"                  // for nsIWidget
      30             : #include "nsThreadUtils.h"              // for NS_IsMainThread
      31             : #include "mozilla/Telemetry.h"
      32             : #include "mozilla/VsyncDispatcher.h"
      33             : #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
      34             : #include "VsyncSource.h"
      35             : #endif
      36             : #include "mozilla/widget/CompositorWidget.h"
      37             : #include "VRManager.h"
      38             : 
      39             : namespace mozilla {
      40             : 
      41             : namespace layers {
      42             : 
      43             : using namespace mozilla::gfx;
      44             : using namespace std;
      45             : 
      46           1 : CompositorVsyncScheduler::Observer::Observer(CompositorVsyncScheduler* aOwner)
      47             :   : mMutex("CompositorVsyncScheduler.Observer.Mutex")
      48           1 :   , mOwner(aOwner)
      49             : {
      50           1 : }
      51             : 
      52           0 : CompositorVsyncScheduler::Observer::~Observer()
      53             : {
      54           0 :   MOZ_ASSERT(!mOwner);
      55           0 : }
      56             : 
      57             : bool
      58         142 : CompositorVsyncScheduler::Observer::NotifyVsync(TimeStamp aVsyncTimestamp)
      59             : {
      60         284 :   MutexAutoLock lock(mMutex);
      61         142 :   if (!mOwner) {
      62           0 :     return false;
      63             :   }
      64         142 :   return mOwner->NotifyVsync(aVsyncTimestamp);
      65             : }
      66             : 
      67             : void
      68           0 : CompositorVsyncScheduler::Observer::Destroy()
      69             : {
      70           0 :   MutexAutoLock lock(mMutex);
      71           0 :   mOwner = nullptr;
      72           0 : }
      73             : 
      74           1 : CompositorVsyncScheduler::CompositorVsyncScheduler(CompositorVsyncSchedulerOwner* aVsyncSchedulerOwner,
      75           1 :                                                    widget::CompositorWidget* aWidget)
      76             :   : mVsyncSchedulerOwner(aVsyncSchedulerOwner)
      77             :   , mLastCompose(TimeStamp::Now())
      78             :   , mIsObservingVsync(false)
      79             :   , mNeedsComposite(0)
      80             :   , mVsyncNotificationsSkipped(0)
      81             :   , mWidget(aWidget)
      82             :   , mCurrentCompositeTaskMonitor("CurrentCompositeTaskMonitor")
      83             :   , mCurrentCompositeTask(nullptr)
      84             :   , mSetNeedsCompositeMonitor("SetNeedsCompositeMonitor")
      85           1 :   , mSetNeedsCompositeTask(nullptr)
      86             : {
      87           1 :   mVsyncObserver = new Observer(this);
      88             : 
      89             :   // mAsapScheduling is set on the main thread during init,
      90             :   // but is only accessed after on the compositor thread.
      91           2 :   mAsapScheduling = gfxPrefs::LayersCompositionFrameRate() == 0 ||
      92           1 :                     gfxPlatform::IsInLayoutAsapMode();
      93           1 : }
      94             : 
      95           0 : CompositorVsyncScheduler::~CompositorVsyncScheduler()
      96             : {
      97           0 :   MOZ_ASSERT(!mIsObservingVsync);
      98           0 :   MOZ_ASSERT(!mVsyncObserver);
      99             :   // The CompositorVsyncDispatcher is cleaned up before this in the nsBaseWidget, which stops vsync listeners
     100           0 :   mVsyncSchedulerOwner = nullptr;
     101           0 : }
     102             : 
     103             : void
     104           0 : CompositorVsyncScheduler::Destroy()
     105             : {
     106           0 :   if (!mVsyncObserver) {
     107             :     // Destroy was already called on this object.
     108           0 :     return;
     109             :   }
     110           0 :   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
     111           0 :   UnobserveVsync();
     112           0 :   mVsyncObserver->Destroy();
     113           0 :   mVsyncObserver = nullptr;
     114             : 
     115           0 :   CancelCurrentSetNeedsCompositeTask();
     116           0 :   CancelCurrentCompositeTask();
     117             : }
     118             : 
     119             : void
     120         151 : CompositorVsyncScheduler::PostCompositeTask(TimeStamp aCompositeTimestamp)
     121             : {
     122             :   // can be called from the compositor or vsync thread
     123         302 :   MonitorAutoLock lock(mCurrentCompositeTaskMonitor);
     124         151 :   if (mCurrentCompositeTask == nullptr && CompositorThreadHolder::Loop()) {
     125         302 :     RefPtr<CancelableRunnable> task = NewCancelableRunnableMethod<TimeStamp>(
     126             :       "layers::CompositorVsyncScheduler::Composite",
     127             :       this,
     128             :       &CompositorVsyncScheduler::Composite,
     129         302 :       aCompositeTimestamp);
     130         151 :     mCurrentCompositeTask = task;
     131         151 :     ScheduleTask(task.forget(), 0);
     132             :   }
     133         151 : }
     134             : 
     135             : void
     136          28 : CompositorVsyncScheduler::ScheduleComposition()
     137             : {
     138          28 :   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
     139          28 :   if (!mVsyncObserver) {
     140             :     // Destroy was already called on this object.
     141           0 :     return;
     142             :   }
     143             : 
     144          28 :   if (mAsapScheduling) {
     145             :     // Used only for performance testing purposes
     146           0 :     PostCompositeTask(TimeStamp::Now());
     147             : #ifdef MOZ_WIDGET_ANDROID
     148             :   } else if (mNeedsComposite >= 2 && mIsObservingVsync) {
     149             :     // uh-oh, we already requested a composite at least twice so far, and a
     150             :     // composite hasn't happened yet. It is possible that the vsync observation
     151             :     // is blocked on the main thread, so let's just composite ASAP and not
     152             :     // wait for the vsync. Note that this should only ever happen on Fennec
     153             :     // because there content runs in the same process as the compositor, and so
     154             :     // content can actually block the main thread in this process.
     155             :     PostCompositeTask(TimeStamp::Now());
     156             : #endif
     157             :   } else {
     158          28 :     SetNeedsComposite();
     159             :   }
     160             : }
     161             : 
     162             : void
     163           0 : CompositorVsyncScheduler::CancelCurrentSetNeedsCompositeTask()
     164             : {
     165           0 :   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
     166           0 :   MonitorAutoLock lock(mSetNeedsCompositeMonitor);
     167           0 :   if (mSetNeedsCompositeTask) {
     168           0 :     mSetNeedsCompositeTask->Cancel();
     169           0 :     mSetNeedsCompositeTask = nullptr;
     170             :   }
     171           0 :   mNeedsComposite = 0;
     172           0 : }
     173             : 
     174             : /**
     175             :  * TODO Potential performance heuristics:
     176             :  * If a composite takes 17 ms, do we composite ASAP or wait until next vsync?
     177             :  * If a layer transaction comes after vsync, do we composite ASAP or wait until
     178             :  * next vsync?
     179             :  * How many skipped vsync events until we stop listening to vsync events?
     180             :  */
     181             : void
     182          28 : CompositorVsyncScheduler::SetNeedsComposite()
     183             : {
     184          28 :   if (!CompositorThreadHolder::IsInCompositorThread()) {
     185           0 :     MonitorAutoLock lock(mSetNeedsCompositeMonitor);
     186           0 :     RefPtr<CancelableRunnable> task = NewCancelableRunnableMethod(
     187             :       "layers::CompositorVsyncScheduler::SetNeedsComposite",
     188             :       this,
     189           0 :       &CompositorVsyncScheduler::SetNeedsComposite);
     190           0 :     mSetNeedsCompositeTask = task;
     191           0 :     ScheduleTask(task.forget(), 0);
     192           0 :     return;
     193             :   } else {
     194          56 :     MonitorAutoLock lock(mSetNeedsCompositeMonitor);
     195          28 :     mSetNeedsCompositeTask = nullptr;
     196             :   }
     197             : 
     198          28 :   mNeedsComposite++;
     199          28 :   if (!mIsObservingVsync && mNeedsComposite) {
     200           9 :     ObserveVsync();
     201             :     // Starting to observe vsync is an async operation that goes
     202             :     // through the main thread of the UI process. It's possible that
     203             :     // we're blocking there waiting on a composite, so schedule an initial
     204             :     // one now to get things started.
     205           9 :     PostCompositeTask(TimeStamp::Now());
     206             :   }
     207             : }
     208             : 
     209             : bool
     210         142 : CompositorVsyncScheduler::NotifyVsync(TimeStamp aVsyncTimestamp)
     211             : {
     212             :   // Called from the vsync dispatch thread. When in the GPU Process, that's
     213             :   // the same as the compositor thread.
     214         142 :   MOZ_ASSERT_IF(XRE_IsParentProcess(), !CompositorThreadHolder::IsInCompositorThread());
     215         142 :   MOZ_ASSERT_IF(XRE_GetProcessType() == GeckoProcessType_GPU, CompositorThreadHolder::IsInCompositorThread());
     216         142 :   MOZ_ASSERT(!NS_IsMainThread());
     217         142 :   PostCompositeTask(aVsyncTimestamp);
     218         142 :   return true;
     219             : }
     220             : 
     221             : void
     222           1 : CompositorVsyncScheduler::CancelCurrentCompositeTask()
     223             : {
     224           1 :   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread() || NS_IsMainThread());
     225           2 :   MonitorAutoLock lock(mCurrentCompositeTaskMonitor);
     226           1 :   if (mCurrentCompositeTask) {
     227           0 :     mCurrentCompositeTask->Cancel();
     228           0 :     mCurrentCompositeTask = nullptr;
     229             :   }
     230           1 : }
     231             : 
     232             : void
     233         151 : CompositorVsyncScheduler::Composite(TimeStamp aVsyncTimestamp)
     234             : {
     235         151 :   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
     236             :   {
     237         302 :     MonitorAutoLock lock(mCurrentCompositeTaskMonitor);
     238         151 :     mCurrentCompositeTask = nullptr;
     239             :   }
     240             : 
     241         151 :   if ((aVsyncTimestamp < mLastCompose) && !mAsapScheduling) {
     242             :     // We can sometimes get vsync timestamps that are in the past
     243             :     // compared to the last compose with force composites.
     244             :     // In those cases, wait until the next vsync;
     245           0 :     return;
     246             :   }
     247             : 
     248         151 :   MOZ_ASSERT(mVsyncSchedulerOwner);
     249         151 :   if (!mAsapScheduling && mVsyncSchedulerOwner->IsPendingComposite()) {
     250             :     // If previous composite is still on going, finish it and does a next
     251             :     // composite in a next vsync.
     252           0 :     mVsyncSchedulerOwner->FinishPendingComposite();
     253           0 :     return;
     254             :   }
     255             : 
     256         151 :   DispatchTouchEvents(aVsyncTimestamp);
     257         151 :   DispatchVREvents(aVsyncTimestamp);
     258             : 
     259         151 :   if (mNeedsComposite || mAsapScheduling) {
     260          28 :     mNeedsComposite = 0;
     261          28 :     mLastCompose = aVsyncTimestamp;
     262          28 :     ComposeToTarget(nullptr);
     263          28 :     mVsyncNotificationsSkipped = 0;
     264             : 
     265          28 :     TimeDuration compositeFrameTotal = TimeStamp::Now() - aVsyncTimestamp;
     266          28 :     mozilla::Telemetry::Accumulate(mozilla::Telemetry::COMPOSITE_FRAME_ROUNDTRIP_TIME,
     267          56 :                                    compositeFrameTotal.ToMilliseconds());
     268         123 :   } else if (mVsyncNotificationsSkipped++ > gfxPrefs::CompositorUnobserveCount()) {
     269           8 :     UnobserveVsync();
     270             :   }
     271             : }
     272             : 
     273             : void
     274           1 : CompositorVsyncScheduler::OnForceComposeToTarget()
     275             : {
     276             :   /**
     277             :    * bug 1138502 - There are cases such as during long-running window resizing events
     278             :    * where we receive many sync RecvFlushComposites. We also get vsync notifications which
     279             :    * will increment mVsyncNotificationsSkipped because a composite just occurred. After
     280             :    * enough vsyncs and RecvFlushComposites occurred, we will disable vsync. Then at the next
     281             :    * ScheduleComposite, we will enable vsync, then get a RecvFlushComposite, which will
     282             :    * force us to unobserve vsync again. On some platforms, enabling/disabling vsync is not
     283             :    * free and this oscillating behavior causes a performance hit. In order to avoid this problem,
     284             :    * we reset the mVsyncNotificationsSkipped counter to keep vsync enabled.
     285             :    */
     286           1 :   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
     287           1 :   mVsyncNotificationsSkipped = 0;
     288           1 : }
     289             : 
     290             : void
     291           1 : CompositorVsyncScheduler::ForceComposeToTarget(gfx::DrawTarget* aTarget, const IntRect* aRect)
     292             : {
     293           1 :   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
     294           1 :   OnForceComposeToTarget();
     295           1 :   mLastCompose = TimeStamp::Now();
     296           1 :   ComposeToTarget(aTarget, aRect);
     297           1 : }
     298             : 
     299             : bool
     300           1 : CompositorVsyncScheduler::NeedsComposite()
     301             : {
     302           1 :   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
     303           1 :   return mNeedsComposite;
     304             : }
     305             : 
     306             : void
     307           9 : CompositorVsyncScheduler::ObserveVsync()
     308             : {
     309           9 :   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
     310           9 :   mWidget->ObserveVsync(mVsyncObserver);
     311           9 :   mIsObservingVsync = true;
     312           9 : }
     313             : 
     314             : void
     315           8 : CompositorVsyncScheduler::UnobserveVsync()
     316             : {
     317           8 :   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
     318           8 :   mWidget->ObserveVsync(nullptr);
     319           8 :   mIsObservingVsync = false;
     320           8 : }
     321             : 
     322             : void
     323         151 : CompositorVsyncScheduler::DispatchTouchEvents(TimeStamp aVsyncTimestamp)
     324             : {
     325         151 : }
     326             : 
     327             : void
     328         151 : CompositorVsyncScheduler::DispatchVREvents(TimeStamp aVsyncTimestamp)
     329             : {
     330         151 :   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
     331             : 
     332         151 :   VRManager* vm = VRManager::Get();
     333         151 :   vm->NotifyVsync(aVsyncTimestamp);
     334         151 : }
     335             : 
     336             : void
     337         151 : CompositorVsyncScheduler::ScheduleTask(already_AddRefed<CancelableRunnable> aTask,
     338             :                                        int aTime)
     339             : {
     340         151 :   MOZ_ASSERT(CompositorThreadHolder::Loop());
     341         151 :   MOZ_ASSERT(aTime >= 0);
     342         151 :   CompositorThreadHolder::Loop()->PostDelayedTask(Move(aTask), aTime);
     343         151 : }
     344             : 
     345             : void
     346           0 : CompositorVsyncScheduler::ResumeComposition()
     347             : {
     348           0 :   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
     349           0 :   mLastCompose = TimeStamp::Now();
     350           0 :   ComposeToTarget(nullptr);
     351           0 : }
     352             : 
     353             : void
     354          29 : CompositorVsyncScheduler::ComposeToTarget(gfx::DrawTarget* aTarget, const IntRect* aRect)
     355             : {
     356          29 :   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
     357          29 :   MOZ_ASSERT(mVsyncSchedulerOwner);
     358          29 :   mVsyncSchedulerOwner->CompositeToTarget(aTarget, aRect);
     359          29 : }
     360             : 
     361             : } // namespace layers
     362             : } // namespace mozilla

Generated by: LCOV version 1.13