tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

CompositorVsyncScheduler.cpp (14108B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 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 "mozilla/AutoRestore.h"  // for AutoRestore
     18 #include "mozilla/DebugOnly.h"    // for DebugOnly
     19 #include "mozilla/StaticPrefs_gfx.h"
     20 #include "mozilla/StaticPrefs_layers.h"
     21 #include "mozilla/gfx/2D.h"     // for DrawTarget
     22 #include "mozilla/gfx/Point.h"  // for IntSize
     23 #include "mozilla/gfx/Rect.h"   // for IntSize
     24 #include "mozilla/layers/CompositorThread.h"
     25 #include "mozilla/layers/CompositorVsyncSchedulerOwner.h"
     26 #include "mozilla/mozalloc.h"  // for operator new, etc
     27 #include "nsCOMPtr.h"          // for already_AddRefed
     28 #include "nsDebug.h"           // for NS_ASSERTION, etc
     29 #include "nsISupportsImpl.h"   // for MOZ_COUNT_CTOR, etc
     30 #include "nsIWidget.h"         // for nsIWidget
     31 #include "nsThreadUtils.h"     // for NS_IsMainThread
     32 #include "mozilla/glean/GfxMetrics.h"
     33 #include "mozilla/VsyncDispatcher.h"
     34 #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
     35 #  include "VsyncSource.h"
     36 #endif
     37 #include "mozilla/widget/CompositorWidget.h"
     38 #include "VRManager.h"
     39 
     40 namespace mozilla {
     41 
     42 namespace layers {
     43 
     44 using namespace mozilla::gfx;
     45 
     46 CompositorVsyncScheduler::Observer::Observer(CompositorVsyncScheduler* aOwner)
     47    : mMutex("CompositorVsyncScheduler.Observer.Mutex"), mOwner(aOwner) {}
     48 
     49 CompositorVsyncScheduler::Observer::~Observer() { MOZ_ASSERT(!mOwner); }
     50 
     51 void CompositorVsyncScheduler::Observer::NotifyVsync(const VsyncEvent& aVsync) {
     52  MutexAutoLock lock(mMutex);
     53  if (!mOwner) {
     54    return;
     55  }
     56  mOwner->NotifyVsync(aVsync);
     57 }
     58 
     59 void CompositorVsyncScheduler::Observer::Destroy() {
     60  MutexAutoLock lock(mMutex);
     61  mOwner = nullptr;
     62 }
     63 
     64 CompositorVsyncScheduler::CompositorVsyncScheduler(
     65    CompositorVsyncSchedulerOwner* aVsyncSchedulerOwner,
     66    widget::CompositorWidget* aWidget)
     67    : mVsyncSchedulerOwner(aVsyncSchedulerOwner),
     68      mLastComposeTime(SampleTime::FromNow()),
     69      mLastVsyncTime(TimeStamp::Now()),
     70      mLastVsyncOutputTime(TimeStamp::Now()),
     71      mIsObservingVsync(false),
     72      mRendersDelayedByVsyncReasons(wr::RenderReasons::NONE),
     73      mVsyncNotificationsSkipped(0),
     74      mWidget(aWidget),
     75      mCurrentCompositeTaskMonitor("CurrentCompositeTaskMonitor"),
     76      mCurrentCompositeTask(nullptr),
     77      mCurrentCompositeTaskReasons(wr::RenderReasons::NONE),
     78      mCurrentVRTaskMonitor("CurrentVRTaskMonitor"),
     79      mCurrentVRTask(nullptr) {
     80  mVsyncObserver = new Observer(this);
     81 
     82  // mAsapScheduling is set on the main thread during init,
     83  // but is only accessed after on the compositor thread.
     84  mAsapScheduling =
     85      StaticPrefs::layers_offmainthreadcomposition_frame_rate() == 0 ||
     86      gfxPlatform::IsInLayoutAsapMode();
     87 }
     88 
     89 CompositorVsyncScheduler::~CompositorVsyncScheduler() {
     90  MOZ_ASSERT(!mIsObservingVsync);
     91  MOZ_ASSERT(!mVsyncObserver);
     92  // The CompositorVsyncDispatcher is cleaned up before this in the
     93  // nsIWidget, which stops vsync listeners
     94  mVsyncSchedulerOwner = nullptr;
     95 }
     96 
     97 void CompositorVsyncScheduler::Destroy() {
     98  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
     99 
    100  if (!mVsyncObserver) {
    101    // Destroy was already called on this object.
    102    return;
    103  }
    104  UnobserveVsync();
    105  mVsyncObserver->Destroy();
    106  mVsyncObserver = nullptr;
    107 
    108  mCompositeRequestedAt = TimeStamp();
    109  CancelCurrentCompositeTask();
    110  CancelCurrentVRTask();
    111 }
    112 
    113 void CompositorVsyncScheduler::PostCompositeTask(const VsyncEvent& aVsyncEvent,
    114                                                 wr::RenderReasons aReasons) {
    115  MonitorAutoLock lock(mCurrentCompositeTaskMonitor);
    116  mCurrentCompositeTaskReasons = mCurrentCompositeTaskReasons | aReasons;
    117  if (mCurrentCompositeTask == nullptr && CompositorThread()) {
    118    RefPtr<CancelableRunnable> task =
    119        NewCancelableRunnableMethod<VsyncEvent, wr::RenderReasons>(
    120            "layers::CompositorVsyncScheduler::Composite", this,
    121            &CompositorVsyncScheduler::Composite, aVsyncEvent, aReasons);
    122    mCurrentCompositeTask = task;
    123    CompositorThread()->Dispatch(task.forget());
    124  }
    125 }
    126 
    127 void CompositorVsyncScheduler::PostVRTask(TimeStamp aTimestamp) {
    128  MonitorAutoLock lockVR(mCurrentVRTaskMonitor);
    129  if (mCurrentVRTask == nullptr && CompositorThread()) {
    130    RefPtr<CancelableRunnable> task = NewCancelableRunnableMethod<TimeStamp>(
    131        "layers::CompositorVsyncScheduler::DispatchVREvents", this,
    132        &CompositorVsyncScheduler::DispatchVREvents, aTimestamp);
    133    mCurrentVRTask = task;
    134    CompositorThread()->Dispatch(task.forget());
    135  }
    136 }
    137 
    138 void CompositorVsyncScheduler::ScheduleComposition(wr::RenderReasons aReasons) {
    139  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
    140  if (!mVsyncObserver) {
    141    // Destroy was already called on this object.
    142    return;
    143  }
    144 
    145  // Make a synthetic vsync event for the calls to PostCompositeTask below.
    146  TimeStamp vsyncTime = TimeStamp::Now();
    147  TimeStamp outputTime = vsyncTime + mVsyncSchedulerOwner->GetVsyncInterval();
    148  VsyncEvent vsyncEvent(VsyncId(), vsyncTime, outputTime);
    149 
    150  if (mAsapScheduling) {
    151    // Used only for performance testing purposes, and when recording/replaying
    152    // to ensure that graphics are up to date.
    153    PostCompositeTask(vsyncEvent, aReasons);
    154  } else {
    155    if (!mCompositeRequestedAt) {
    156      mCompositeRequestedAt = TimeStamp::Now();
    157    }
    158    if (!mIsObservingVsync && mCompositeRequestedAt) {
    159      ObserveVsync();
    160      // Starting to observe vsync is an async operation that goes
    161      // through the main thread of the UI process. It's possible that
    162      // we're blocking there waiting on a composite, so schedule an initial
    163      // one now to get things started.
    164      PostCompositeTask(vsyncEvent,
    165                        aReasons | wr::RenderReasons::START_OBSERVING_VSYNC);
    166    } else {
    167      mRendersDelayedByVsyncReasons = aReasons;
    168    }
    169  }
    170 }
    171 
    172 void CompositorVsyncScheduler::NotifyVsync(const VsyncEvent& aVsync) {
    173  // Called from the vsync dispatch thread. When in the GPU Process, that's
    174  // the same as the compositor thread.
    175 #ifdef DEBUG
    176 #  ifdef MOZ_WAYLAND
    177  // On Wayland, we dispatch vsync from the main thread, without a GPU process.
    178  // To allow this, we skip the following asserts if we're currently utilizing
    179  // the Wayland backend. The IsParentProcess guard is needed to ensure that
    180  // we don't accidentally attempt to initialize the gfxPlatform in the GPU
    181  // process on X11.
    182  if (!XRE_IsParentProcess() ||
    183      !gfxPlatformGtk::GetPlatform()->IsWaylandDisplay())
    184 #  endif  // MOZ_WAYLAND
    185  {
    186    MOZ_ASSERT_IF(XRE_IsParentProcess(),
    187                  !CompositorThreadHolder::IsInCompositorThread());
    188    MOZ_ASSERT(!NS_IsMainThread());
    189  }
    190 
    191  MOZ_ASSERT_IF(XRE_GetProcessType() == GeckoProcessType_GPU,
    192                CompositorThreadHolder::IsInCompositorThread());
    193 #endif  // DEBUG
    194 
    195 #if defined(MOZ_WIDGET_ANDROID)
    196  gfx::VRManager* vm = gfx::VRManager::Get();
    197  if (!vm->IsPresenting()) {
    198    PostCompositeTask(aVsync, wr::RenderReasons::VSYNC);
    199  }
    200 #else
    201  PostCompositeTask(aVsync, wr::RenderReasons::VSYNC);
    202 #endif  // defined(MOZ_WIDGET_ANDROID)
    203 
    204  PostVRTask(aVsync.mTime);
    205 }
    206 
    207 void CompositorVsyncScheduler::CancelCurrentVRTask() {
    208  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread() ||
    209             NS_IsMainThread());
    210  MonitorAutoLock lock(mCurrentVRTaskMonitor);
    211  if (mCurrentVRTask) {
    212    mCurrentVRTask->Cancel();
    213    mCurrentVRTask = nullptr;
    214  }
    215 }
    216 
    217 wr::RenderReasons CompositorVsyncScheduler::CancelCurrentCompositeTask() {
    218  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread() ||
    219             NS_IsMainThread());
    220  MonitorAutoLock lock(mCurrentCompositeTaskMonitor);
    221  wr::RenderReasons canceledTaskRenderReasons = mCurrentCompositeTaskReasons;
    222  mCurrentCompositeTaskReasons = wr::RenderReasons::NONE;
    223  if (mCurrentCompositeTask) {
    224    mCurrentCompositeTask->Cancel();
    225    mCurrentCompositeTask = nullptr;
    226  }
    227 
    228  return canceledTaskRenderReasons;
    229 }
    230 
    231 void CompositorVsyncScheduler::Composite(const VsyncEvent& aVsyncEvent,
    232                                         wr::RenderReasons aReasons) {
    233  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
    234  MOZ_ASSERT(mVsyncSchedulerOwner);
    235 
    236  {  // scope lock
    237    MonitorAutoLock lock(mCurrentCompositeTaskMonitor);
    238    aReasons =
    239        aReasons | mCurrentCompositeTaskReasons | mRendersDelayedByVsyncReasons;
    240    mCurrentCompositeTaskReasons = wr::RenderReasons::NONE;
    241    mRendersDelayedByVsyncReasons = wr::RenderReasons::NONE;
    242    mCurrentCompositeTask = nullptr;
    243  }
    244 
    245  mLastVsyncTime = aVsyncEvent.mTime;
    246  mLastVsyncOutputTime = aVsyncEvent.mOutputTime;
    247  mLastVsyncId = aVsyncEvent.mId;
    248 
    249  if (!mAsapScheduling) {
    250    // Some early exit conditions if we're not in ASAP mode
    251    if (aVsyncEvent.mTime < mLastComposeTime.Time()) {
    252      // We can sometimes get vsync timestamps that are in the past
    253      // compared to the last compose with force composites.
    254      // In those cases, wait until the next vsync;
    255      return;
    256    }
    257 
    258    if (mVsyncSchedulerOwner->IsPendingComposite()) {
    259      // If previous composite is still on going, finish it and wait for the
    260      // next vsync.
    261      mVsyncSchedulerOwner->FinishPendingComposite();
    262      return;
    263    }
    264  }
    265 
    266  if (mCompositeRequestedAt || mAsapScheduling) {
    267    mCompositeRequestedAt = TimeStamp();
    268    mLastComposeTime = SampleTime::FromVsync(aVsyncEvent.mTime);
    269 
    270    // Tell the owner to do a composite
    271    mVsyncSchedulerOwner->CompositeToTarget(aVsyncEvent.mId, aReasons, nullptr,
    272                                            nullptr);
    273 
    274    mVsyncNotificationsSkipped = 0;
    275 
    276    TimeDuration compositeFrameTotal = TimeStamp::Now() - aVsyncEvent.mTime;
    277    mozilla::glean::gfx::composite_frame_roundtrip_time.AccumulateRawDuration(
    278        compositeFrameTotal);
    279  } else if (mVsyncNotificationsSkipped++ >
    280             StaticPrefs::gfx_vsync_compositor_unobserve_count_AtStartup()) {
    281    UnobserveVsync();
    282  }
    283 }
    284 
    285 void CompositorVsyncScheduler::ForceComposeToTarget(wr::RenderReasons aReasons,
    286                                                    gfx::DrawTarget* aTarget,
    287                                                    const IntRect* aRect) {
    288  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
    289 
    290  /**
    291   * bug 1138502 - There are cases such as during long-running window resizing
    292   * events where we receive many force-composites. We also continue to get
    293   * vsync notifications. Because the force-composites trigger compositing and
    294   * clear the mCompositeRequestedAt timestamp, the vsync notifications will not
    295   * need to do anything and so will increment the mVsyncNotificationsSkipped
    296   * counter to indicate the vsync was ignored. If this happens enough times, we
    297   * will disable listening for vsync entirely. On the next force-composite we
    298   * will enable listening for vsync again, and continued force-composites and
    299   * vsyncs will cause oscillation between observing vsync and not. On some
    300   * platforms, enabling/disabling vsync is not free and this oscillating
    301   * behavior causes a performance hit. In order to avoid this problem, we reset
    302   * the mVsyncNotificationsSkipped counter to keep vsync enabled.
    303   */
    304  mVsyncNotificationsSkipped = 0;
    305 
    306  mLastComposeTime = SampleTime::FromNow();
    307  MOZ_ASSERT(mVsyncSchedulerOwner);
    308  mVsyncSchedulerOwner->CompositeToTarget(VsyncId(), aReasons, aTarget, aRect);
    309 }
    310 
    311 bool CompositorVsyncScheduler::NeedsComposite() {
    312  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
    313  return (bool)mCompositeRequestedAt;
    314 }
    315 
    316 bool CompositorVsyncScheduler::FlushPendingComposite() {
    317  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
    318  if (mCompositeRequestedAt) {
    319    wr::RenderReasons reasons = CancelCurrentCompositeTask();
    320    ForceComposeToTarget(reasons, nullptr, nullptr);
    321    return true;
    322  }
    323  return false;
    324 }
    325 
    326 void CompositorVsyncScheduler::ObserveVsync() {
    327  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
    328  mWidget->ObserveVsync(mVsyncObserver);
    329  mIsObservingVsync = true;
    330 }
    331 
    332 void CompositorVsyncScheduler::UnobserveVsync() {
    333  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
    334  mWidget->ObserveVsync(nullptr);
    335  mIsObservingVsync = false;
    336 }
    337 
    338 void CompositorVsyncScheduler::DispatchVREvents(TimeStamp aVsyncTimestamp) {
    339  {
    340    MonitorAutoLock lock(mCurrentVRTaskMonitor);
    341    mCurrentVRTask = nullptr;
    342  }
    343  // This only allows to be called by CompositorVsyncScheduler::PostVRTask()
    344  // When the process is going to shutdown, the runnable has chance to be
    345  // executed by other threads, we only want it to be run in the compositor
    346  // thread.
    347  if (!CompositorThreadHolder::IsInCompositorThread()) {
    348    return;
    349  }
    350 
    351  VRManager* vm = VRManager::Get();
    352  vm->NotifyVsync(aVsyncTimestamp);
    353 }
    354 
    355 const SampleTime& CompositorVsyncScheduler::GetLastComposeTime() const {
    356  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
    357  return mLastComposeTime;
    358 }
    359 
    360 const TimeStamp& CompositorVsyncScheduler::GetLastVsyncTime() const {
    361  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
    362  return mLastVsyncTime;
    363 }
    364 
    365 const TimeStamp& CompositorVsyncScheduler::GetLastVsyncOutputTime() const {
    366  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
    367  return mLastVsyncOutputTime;
    368 }
    369 
    370 const VsyncId& CompositorVsyncScheduler::GetLastVsyncId() const {
    371  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
    372  return mLastVsyncId;
    373 }
    374 
    375 void CompositorVsyncScheduler::UpdateLastComposeTime() {
    376  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
    377  mLastComposeTime = SampleTime::FromNow();
    378 }
    379 
    380 }  // namespace layers
    381 }  // namespace mozilla