// Copyright 2018 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "Vsync.h" #include "base/ConditionVariable.h" #include "base/Lock.h" #include "base/System.h" #include "base/FunctorThread.h" #include #include using android::base::AutoLock; using android::base::ConditionVariable; using android::base::FunctorThread; using android::base::Lock; namespace aemu { class Vsync::Impl { public: Impl(Callback callback, int refreshRate = 60) : mCallback(std::move(callback)), mRefreshRate(refreshRate), mRefreshIntervalUs(1000000ULL / mRefreshRate), mThread([this] { mNowUs = android::base::getHighResTimeUs(); mNextVsyncDeadlineUs = mNowUs + mRefreshIntervalUs; while (true) { if (mShouldStop.load(std::memory_order_relaxed)) return 0; mNowUs = android::base::getHighResTimeUs(); if (mNextVsyncDeadlineUs > mNowUs) { android::base::sleepUs(mNextVsyncDeadlineUs - mNowUs); } mNowUs = android::base::getHighResTimeUs(); mNextVsyncDeadlineUs = mNowUs + mRefreshIntervalUs; AutoLock lock(mLock); mSync = 1; mCv.signal(); mCallback(); } return 0; }) { } void start() { mShouldStop.store(false, std::memory_order_relaxed); mThread.start(); } void join() { mShouldStop.store(true, std::memory_order_relaxed); mThread.wait(); } ~Impl() { join(); } void waitUntilNextVsync() { AutoLock lock(mLock); mSync = 0; while (!mSync) { mCv.wait(&mLock); } } private: std::atomic mShouldStop { false }; int mSync = 0; Lock mLock; ConditionVariable mCv; Callback mCallback; int mRefreshRate = 60; uint64_t mRefreshIntervalUs; uint64_t mNowUs = 0; uint64_t mNextVsyncDeadlineUs = 0; FunctorThread mThread; }; Vsync::Vsync(int refreshRate, Vsync::Callback callback) : mImpl(new Vsync::Impl(std::move(callback), refreshRate)) {} Vsync::~Vsync() = default; void Vsync::start() { mImpl->start(); } void Vsync::join() { mImpl->join(); } void Vsync::waitUntilNextVsync() { mImpl->waitUntilNextVsync(); } } // namespace aemu