tor-browser

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

VsyncSource.cpp (4674B)


      1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #include "VsyncSource.h"
      7 #include "nsThreadUtils.h"
      8 #include "nsXULAppAPI.h"
      9 #include "mozilla/VsyncDispatcher.h"
     10 #include "MainThreadUtils.h"
     11 #include "gfxPlatform.h"
     12 
     13 #ifdef MOZ_WAYLAND
     14 #  include "WaylandVsyncSource.h"
     15 #endif
     16 
     17 namespace mozilla {
     18 namespace gfx {
     19 
     20 VsyncSource::VsyncSource() : mState("VsyncSource::State") {
     21  MOZ_ASSERT(NS_IsMainThread());
     22 }
     23 
     24 VsyncSource::~VsyncSource() { MOZ_ASSERT(NS_IsMainThread()); }
     25 
     26 // Called on the vsync thread
     27 void VsyncSource::NotifyVsync(const TimeStamp& aVsyncTimestamp,
     28                              const TimeStamp& aOutputTimestamp) {
     29  VsyncId vsyncId;
     30  nsTArray<DispatcherRefWithCount> dispatchers;
     31 
     32  {
     33    auto state = mState.Lock();
     34    vsyncId = state->mVsyncId.Next();
     35    dispatchers = state->mDispatchers.Clone();
     36    state->mVsyncId = vsyncId;
     37  }
     38 
     39  // Notify our listeners, outside of the lock.
     40  const VsyncEvent event(vsyncId, aVsyncTimestamp, aOutputTimestamp);
     41  for (const auto& dispatcher : dispatchers) {
     42    dispatcher.mDispatcher->NotifyVsync(event);
     43  }
     44 }
     45 
     46 void VsyncSource::AddVsyncDispatcher(VsyncDispatcher* aVsyncDispatcher) {
     47  MOZ_ASSERT(aVsyncDispatcher);
     48  {
     49    auto state = mState.Lock();
     50 
     51    // Find the dispatcher in mDispatchers. If it is already present, increment
     52    // the count. If not, add it with a count of 1.
     53    bool found = false;
     54    for (auto& dispatcherRefWithCount : state->mDispatchers) {
     55      if (dispatcherRefWithCount.mDispatcher == aVsyncDispatcher) {
     56        dispatcherRefWithCount.mCount++;
     57        found = true;
     58        break;
     59      }
     60    }
     61    if (!found) {
     62      state->mDispatchers.AppendElement(
     63          DispatcherRefWithCount{aVsyncDispatcher, 1});
     64    }
     65  }
     66 
     67  UpdateVsyncStatus();
     68 }
     69 
     70 void VsyncSource::RemoveVsyncDispatcher(VsyncDispatcher* aVsyncDispatcher) {
     71  MOZ_ASSERT(aVsyncDispatcher);
     72  {
     73    auto state = mState.Lock();
     74 
     75    // Find the dispatcher in mDispatchers. If found, decrement the count.
     76    // If the count becomes zero, remove it from mDispatchers.
     77    for (auto it = state->mDispatchers.begin(); it != state->mDispatchers.end();
     78         ++it) {
     79      if (it->mDispatcher == aVsyncDispatcher) {
     80        it->mCount--;
     81        if (it->mCount == 0) {
     82          state->mDispatchers.RemoveElementAt(it);
     83        }
     84        break;
     85      }
     86    }
     87 
     88    // In the future we should probably MOZ_RELEASE_ASSERT here that we don't
     89    // try to remove a dispatcher which isn't in mDispatchers.
     90  }
     91 
     92  UpdateVsyncStatus();
     93 }
     94 
     95 // This is the base class implementation. Subclasses override this method.
     96 TimeDuration VsyncSource::GetVsyncRate() {
     97  // If hardware queries fail / are unsupported, we have to just guess.
     98  return TimeDuration::FromMilliseconds(1000.0 / 60.0);
     99 }
    100 
    101 void VsyncSource::UpdateVsyncStatus() {
    102  if (!NS_IsMainThread()) {
    103    NS_DispatchToMainThread(NS_NewRunnableFunction(
    104        "VsyncSource::UpdateVsyncStatus",
    105        [self = RefPtr{this}] { self->UpdateVsyncStatus(); }));
    106    return;
    107  }
    108 
    109  MOZ_ASSERT(NS_IsMainThread());
    110  // WARNING: This function SHOULD NOT BE CALLED WHILE HOLDING LOCKS
    111  // NotifyVsync grabs a lock to dispatch vsync events
    112  // When disabling vsync, we wait for the underlying thread to stop on some
    113  // platforms We can deadlock if we wait for the underlying vsync thread to
    114  // stop while the vsync thread is in NotifyVsync.
    115  bool enableVsync = false;
    116  {  // scope lock
    117    auto state = mState.Lock();
    118    enableVsync = !state->mDispatchers.IsEmpty();
    119  }
    120 
    121  if (enableVsync) {
    122    EnableVsync();
    123  } else {
    124    DisableVsync();
    125  }
    126 
    127  if (IsVsyncEnabled() != enableVsync) {
    128    NS_WARNING("Vsync status did not change.");
    129  }
    130 }
    131 
    132 // static
    133 Maybe<TimeDuration> VsyncSource::GetFastestVsyncRate() {
    134  Maybe<TimeDuration> retVal;
    135  if (!gfxPlatform::Initialized()) {
    136    return retVal;
    137  }
    138 
    139  RefPtr<VsyncDispatcher> vsyncDispatcher =
    140      gfxPlatform::GetPlatform()->GetGlobalVsyncDispatcher();
    141  RefPtr<VsyncSource> vsyncSource = vsyncDispatcher->GetCurrentVsyncSource();
    142  if (vsyncSource->IsVsyncEnabled()) {
    143    retVal.emplace(vsyncSource->GetVsyncRate());
    144 #ifdef MOZ_WAYLAND
    145    Maybe<TimeDuration> waylandRate = WaylandVsyncSource::GetFastestVsyncRate();
    146    if (waylandRate) {
    147      if (!retVal) {
    148        retVal.emplace(*waylandRate);
    149      } else if (*waylandRate < *retVal) {
    150        retVal = waylandRate;
    151      }
    152    }
    153 #endif
    154  }
    155 
    156  return retVal;
    157 }
    158 
    159 }  // namespace gfx
    160 }  // namespace mozilla