diff options
Diffstat (limited to 'gfx/thebes/SoftwareVsyncSource.cpp')
-rw-r--r-- | gfx/thebes/SoftwareVsyncSource.cpp | 143 |
1 files changed, 143 insertions, 0 deletions
diff --git a/gfx/thebes/SoftwareVsyncSource.cpp b/gfx/thebes/SoftwareVsyncSource.cpp new file mode 100644 index 0000000000..b8e12390e7 --- /dev/null +++ b/gfx/thebes/SoftwareVsyncSource.cpp @@ -0,0 +1,143 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "SoftwareVsyncSource.h" +#include "base/task.h" +#include "gfxPlatform.h" +#include "nsThreadUtils.h" + +namespace mozilla::gfx { + +SoftwareVsyncSource::SoftwareVsyncSource(const TimeDuration& aInitialVsyncRate) + : mVsyncEnabled(false), + mVsyncRate(TimeDuration{aInitialVsyncRate}, + "SoftwareVsyncSource::mVsyncRate") { + MOZ_ASSERT(NS_IsMainThread()); + mVsyncThread = new base::Thread("SoftwareVsyncThread"); + MOZ_RELEASE_ASSERT(mVsyncThread->Start(), + "GFX: Could not start software vsync thread"); +} + +SoftwareVsyncSource::~SoftwareVsyncSource() { + MOZ_ASSERT(NS_IsMainThread()); + if (mVsyncThread) { + mVsyncThread->Stop(); + delete mVsyncThread; + } +}; + +void SoftwareVsyncSource::EnableVsync() { + MOZ_ASSERT(mVsyncThread->IsRunning()); + if (NS_IsMainThread()) { + if (mVsyncEnabled) { + return; + } + mVsyncEnabled = true; + + mVsyncThread->message_loop()->PostTask( + NewRunnableMethod("SoftwareVsyncSource::EnableVsync", this, + &SoftwareVsyncSource::EnableVsync)); + return; + } + + MOZ_ASSERT(IsInSoftwareVsyncThread()); + TimeStamp vsyncTime = TimeStamp::Now(); + TimeStamp outputTime = vsyncTime + GetVsyncRate(); + NotifyVsync(vsyncTime, outputTime); +} + +void SoftwareVsyncSource::DisableVsync() { + MOZ_ASSERT(mVsyncThread->IsRunning()); + if (NS_IsMainThread()) { + if (!mVsyncEnabled) { + return; + } + mVsyncEnabled = false; + + mVsyncThread->message_loop()->PostTask( + NewRunnableMethod("SoftwareVsyncSource::DisableVsync", this, + &SoftwareVsyncSource::DisableVsync)); + return; + } + + MOZ_ASSERT(IsInSoftwareVsyncThread()); + if (mCurrentVsyncTask) { + mCurrentVsyncTask->Cancel(); + mCurrentVsyncTask = nullptr; + } +} + +bool SoftwareVsyncSource::IsVsyncEnabled() { + MOZ_ASSERT(NS_IsMainThread()); + return mVsyncEnabled; +} + +bool SoftwareVsyncSource::IsInSoftwareVsyncThread() { + return mVsyncThread->thread_id() == PlatformThread::CurrentId(); +} + +void SoftwareVsyncSource::NotifyVsync(const TimeStamp& aVsyncTimestamp, + const TimeStamp& aOutputTimestamp) { + MOZ_ASSERT(IsInSoftwareVsyncThread()); + + TimeStamp displayVsyncTime = aVsyncTimestamp; + TimeStamp now = TimeStamp::Now(); + // Posted tasks can only have integer millisecond delays + // whereas TimeDurations can have floating point delays. + // Thus the vsync timestamp can be in the future, which large parts + // of the system can't handle, including animations. Force the timestamp to be + // now. + if (aVsyncTimestamp > now) { + displayVsyncTime = now; + } + + VsyncSource::NotifyVsync(displayVsyncTime, aOutputTimestamp); + + // Prevent skew by still scheduling based on the original + // vsync timestamp + ScheduleNextVsync(aVsyncTimestamp); +} + +TimeDuration SoftwareVsyncSource::GetVsyncRate() { + auto rate = mVsyncRate.Lock(); + return *rate; +} + +void SoftwareVsyncSource::SetVsyncRate(const TimeDuration& aNewRate) { + auto rate = mVsyncRate.Lock(); + *rate = aNewRate; +} + +void SoftwareVsyncSource::ScheduleNextVsync(TimeStamp aVsyncTimestamp) { + MOZ_ASSERT(IsInSoftwareVsyncThread()); + TimeDuration vsyncRate = GetVsyncRate(); + TimeStamp nextVsync = aVsyncTimestamp + vsyncRate; + TimeDuration delay = nextVsync - TimeStamp::Now(); + if (delay.ToMilliseconds() < 0) { + delay = TimeDuration::FromMilliseconds(0); + nextVsync = TimeStamp::Now(); + } + + TimeStamp outputTime = nextVsync + vsyncRate; + + mCurrentVsyncTask = NewCancelableRunnableMethod<TimeStamp, TimeStamp>( + "SoftwareVsyncSource::NotifyVsync", this, + &SoftwareVsyncSource::NotifyVsync, nextVsync, outputTime); + + RefPtr<Runnable> addrefedTask = mCurrentVsyncTask; + mVsyncThread->message_loop()->PostDelayedTask(addrefedTask.forget(), + delay.ToMilliseconds()); +} + +void SoftwareVsyncSource::Shutdown() { + MOZ_ASSERT(NS_IsMainThread()); + DisableVsync(); + mVsyncThread->Stop(); + delete mVsyncThread; + mVsyncThread = nullptr; +} + +} // namespace mozilla::gfx |