/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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 "AndroidVsync.h" #include "mozilla/java/GeckoAppShellWrappers.h" #include "nsTArray.h" /** * Implementation for the AndroidVsync class. */ namespace mozilla { namespace widget { StaticDataMutex> AndroidVsync::sInstance( "AndroidVsync::sInstance"); /* static */ RefPtr AndroidVsync::GetInstance() { auto weakInstance = sInstance.Lock(); RefPtr instance(*weakInstance); if (!instance) { instance = new AndroidVsync(); *weakInstance = instance; } return instance; } /** * Owned by the Java AndroidVsync instance. */ class AndroidVsyncSupport final : public java::AndroidVsync::Natives { public: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AndroidVsyncSupport) using Base = java::AndroidVsync::Natives; using Base::AttachNative; using Base::DisposeNative; explicit AndroidVsyncSupport(AndroidVsync* aAndroidVsync) : mAndroidVsync(std::move(aAndroidVsync), "AndroidVsyncSupport::mAndroidVsync") {} // Called by Java void NotifyVsync(const java::AndroidVsync::LocalRef& aInstance, int64_t aFrameTimeNanos) { auto androidVsync = mAndroidVsync.Lock(); if (*androidVsync) { (*androidVsync)->NotifyVsync(aFrameTimeNanos); } } // Called by the AndroidVsync destructor void Unlink() { auto androidVsync = mAndroidVsync.Lock(); *androidVsync = nullptr; } protected: ~AndroidVsyncSupport() = default; DataMutex mAndroidVsync; }; AndroidVsync::AndroidVsync() : mImpl("AndroidVsync.mImpl") { AndroidVsyncSupport::Init(); auto impl = mImpl.Lock(); impl->mSupport = new AndroidVsyncSupport(this); impl->mSupportJava = java::AndroidVsync::New(); AndroidVsyncSupport::AttachNative(impl->mSupportJava, impl->mSupport); float fps = java::GeckoAppShell::GetScreenRefreshRate(); impl->mVsyncDuration = TimeDuration::FromMilliseconds(1000.0 / fps); } AndroidVsync::~AndroidVsync() { auto impl = mImpl.Lock(); impl->mInputObservers.Clear(); impl->mRenderObservers.Clear(); impl->UpdateObservingVsync(); impl->mSupport->Unlink(); } TimeDuration AndroidVsync::GetVsyncRate() { auto impl = mImpl.Lock(); return impl->mVsyncDuration; } void AndroidVsync::RegisterObserver(Observer* aObserver, ObserverType aType) { auto impl = mImpl.Lock(); if (aType == AndroidVsync::INPUT) { impl->mInputObservers.AppendElement(aObserver); } else { impl->mRenderObservers.AppendElement(aObserver); } impl->UpdateObservingVsync(); } void AndroidVsync::UnregisterObserver(Observer* aObserver, ObserverType aType) { auto impl = mImpl.Lock(); if (aType == AndroidVsync::INPUT) { impl->mInputObservers.RemoveElement(aObserver); } else { impl->mRenderObservers.RemoveElement(aObserver); } aObserver->Dispose(); impl->UpdateObservingVsync(); } void AndroidVsync::Impl::UpdateObservingVsync() { bool shouldObserve = !mInputObservers.IsEmpty() || !mRenderObservers.IsEmpty(); if (shouldObserve != mObservingVsync) { mObservingVsync = mSupportJava->ObserveVsync(shouldObserve); } } // Always called on the Java UI thread. void AndroidVsync::NotifyVsync(int64_t aFrameTimeNanos) { // Convert aFrameTimeNanos to a TimeStamp. The value converts trivially to // the internal ticks representation of TimeStamp_posix; both use the // monotonic clock and are in nanoseconds. TimeStamp timeStamp = TimeStamp::FromSystemTime(aFrameTimeNanos); // Do not keep the lock held while calling OnVsync. nsTArray observers; { auto impl = mImpl.Lock(); observers.AppendElements(impl->mInputObservers); observers.AppendElements(impl->mRenderObservers); } for (Observer* observer : observers) { observer->OnVsync(timeStamp); } } } // namespace widget } // namespace mozilla