SoftwareVsyncSource.cpp (4347B)
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 7 #include "SoftwareVsyncSource.h" 8 #include "base/task.h" 9 #include "gfxPlatform.h" 10 #include "nsThreadUtils.h" 11 12 namespace mozilla::gfx { 13 14 SoftwareVsyncSource::SoftwareVsyncSource(const TimeDuration& aInitialVsyncRate) 15 : mVsyncEnabled(false), 16 mVsyncRate(TimeDuration{aInitialVsyncRate}, 17 "SoftwareVsyncSource::mVsyncRate") { 18 MOZ_ASSERT(NS_IsMainThread()); 19 mVsyncThread = new base::Thread("SoftwareVsyncThread"); 20 MOZ_RELEASE_ASSERT(mVsyncThread->Start(), 21 "GFX: Could not start software vsync thread"); 22 } 23 24 SoftwareVsyncSource::~SoftwareVsyncSource() { 25 MOZ_ASSERT(NS_IsMainThread()); 26 if (mVsyncThread) { 27 mVsyncThread->Stop(); 28 delete mVsyncThread; 29 } 30 }; 31 32 void SoftwareVsyncSource::EnableVsync() { 33 MOZ_ASSERT(mVsyncThread->IsRunning()); 34 if (NS_IsMainThread()) { 35 if (mVsyncEnabled) { 36 return; 37 } 38 mVsyncEnabled = true; 39 40 mVsyncThread->message_loop()->PostTask( 41 NewRunnableMethod("SoftwareVsyncSource::EnableVsync", this, 42 &SoftwareVsyncSource::EnableVsync)); 43 return; 44 } 45 46 MOZ_ASSERT(IsInSoftwareVsyncThread()); 47 TimeStamp vsyncTime = TimeStamp::Now(); 48 TimeStamp outputTime = vsyncTime + GetVsyncRate(); 49 NotifyVsync(vsyncTime, outputTime); 50 } 51 52 void SoftwareVsyncSource::DisableVsync() { 53 MOZ_ASSERT(mVsyncThread->IsRunning()); 54 if (NS_IsMainThread()) { 55 if (!mVsyncEnabled) { 56 return; 57 } 58 mVsyncEnabled = false; 59 60 mVsyncThread->message_loop()->PostTask( 61 NewRunnableMethod("SoftwareVsyncSource::DisableVsync", this, 62 &SoftwareVsyncSource::DisableVsync)); 63 return; 64 } 65 66 MOZ_ASSERT(IsInSoftwareVsyncThread()); 67 if (mCurrentVsyncTask) { 68 mCurrentVsyncTask->Cancel(); 69 mCurrentVsyncTask = nullptr; 70 } 71 } 72 73 bool SoftwareVsyncSource::IsVsyncEnabled() { 74 MOZ_ASSERT(NS_IsMainThread()); 75 return mVsyncEnabled; 76 } 77 78 bool SoftwareVsyncSource::IsInSoftwareVsyncThread() { 79 return mVsyncThread->thread_id() == PlatformThread::CurrentId(); 80 } 81 82 void SoftwareVsyncSource::NotifyVsync(const TimeStamp& aVsyncTimestamp, 83 const TimeStamp& aOutputTimestamp) { 84 MOZ_ASSERT(IsInSoftwareVsyncThread()); 85 86 TimeStamp displayVsyncTime = aVsyncTimestamp; 87 TimeStamp now = TimeStamp::Now(); 88 // Posted tasks can only have integer millisecond delays 89 // whereas TimeDurations can have floating point delays. 90 // Thus the vsync timestamp can be in the future, which large parts 91 // of the system can't handle, including animations. Force the timestamp to be 92 // now. 93 if (aVsyncTimestamp > now) { 94 displayVsyncTime = now; 95 } 96 97 VsyncSource::NotifyVsync(displayVsyncTime, aOutputTimestamp); 98 99 // Prevent skew by still scheduling based on the original 100 // vsync timestamp 101 ScheduleNextVsync(aVsyncTimestamp); 102 } 103 104 TimeDuration SoftwareVsyncSource::GetVsyncRate() { 105 auto rate = mVsyncRate.Lock(); 106 return *rate; 107 } 108 109 void SoftwareVsyncSource::SetVsyncRate(const TimeDuration& aNewRate) { 110 auto rate = mVsyncRate.Lock(); 111 *rate = aNewRate; 112 } 113 114 void SoftwareVsyncSource::ScheduleNextVsync(TimeStamp aVsyncTimestamp) { 115 MOZ_ASSERT(IsInSoftwareVsyncThread()); 116 TimeDuration vsyncRate = GetVsyncRate(); 117 TimeStamp nextVsync = aVsyncTimestamp + vsyncRate; 118 TimeDuration delay = nextVsync - TimeStamp::Now(); 119 if (delay.ToMilliseconds() < 0) { 120 delay = TimeDuration::FromMilliseconds(0); 121 nextVsync = TimeStamp::Now(); 122 } 123 124 TimeStamp outputTime = nextVsync + vsyncRate; 125 126 mCurrentVsyncTask = NewCancelableRunnableMethod<TimeStamp, TimeStamp>( 127 "SoftwareVsyncSource::NotifyVsync", this, 128 &SoftwareVsyncSource::NotifyVsync, nextVsync, outputTime); 129 130 RefPtr<Runnable> addrefedTask = mCurrentVsyncTask; 131 mVsyncThread->message_loop()->PostDelayedTask(addrefedTask.forget(), 132 delay.ToMilliseconds()); 133 } 134 135 void SoftwareVsyncSource::Shutdown() { 136 MOZ_ASSERT(NS_IsMainThread()); 137 DisableVsync(); 138 mVsyncThread->Stop(); 139 delete mVsyncThread; 140 mVsyncThread = nullptr; 141 } 142 143 } // namespace mozilla::gfx