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