| /* |
| // Copyright (c) 2014 Intel Corporation |
| // |
| // 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 <HwcTrace.h> |
| #include <SoftVsyncObserver.h> |
| #include <IDisplayDevice.h> |
| |
| extern "C" int clock_nanosleep(clockid_t clock_id, int flags, |
| const struct timespec *request, |
| struct timespec *remain); |
| |
| |
| namespace android { |
| namespace intel { |
| |
| SoftVsyncObserver::SoftVsyncObserver(IDisplayDevice& disp) |
| : mDisplayDevice(disp), |
| mDevice(IDisplayDevice::DEVICE_COUNT), |
| mEnabled(false), |
| mRefreshRate(60), // default 60 frames per second |
| mRefreshPeriod(0), |
| mLock(), |
| mCondition(), |
| mNextFakeVSync(0), |
| mExitThread(false), |
| mInitialized(false) |
| { |
| } |
| |
| SoftVsyncObserver::~SoftVsyncObserver() |
| { |
| WARN_IF_NOT_DEINIT(); |
| } |
| |
| bool SoftVsyncObserver::initialize() |
| { |
| if (mInitialized) { |
| WTRACE("object has been initialized"); |
| return true; |
| } |
| |
| mExitThread = false; |
| mEnabled = false; |
| mRefreshRate = 60/mDisplayDevice.getFpsDivider(); |
| mDevice = mDisplayDevice.getType(); |
| mThread = new VsyncEventPollThread(this); |
| if (!mThread.get()) { |
| DEINIT_AND_RETURN_FALSE("failed to create vsync event poll thread."); |
| } |
| mThread->run("SoftVsyncObserver", PRIORITY_URGENT_DISPLAY); |
| mInitialized = true; |
| return true; |
| } |
| |
| void SoftVsyncObserver::deinitialize() |
| { |
| if (mEnabled) { |
| WTRACE("soft vsync is still enabled"); |
| control(false); |
| } |
| |
| mExitThread = true; |
| mCondition.signal(); |
| |
| if (mThread.get()) { |
| mThread->requestExitAndWait(); |
| mThread = NULL; |
| } |
| mInitialized = false; |
| } |
| |
| void SoftVsyncObserver::setRefreshRate(int rate) |
| { |
| if (mEnabled) { |
| WTRACE("too late to set refresh rate"); |
| } else if (rate < 1 || rate > 120) { |
| WTRACE("invalid refresh rate %d", rate); |
| } else { |
| mRefreshRate = rate; |
| } |
| } |
| |
| bool SoftVsyncObserver::control(bool enabled) |
| { |
| if (enabled == mEnabled) { |
| WTRACE("vsync state %d is not changed", enabled); |
| return true; |
| } |
| |
| if (enabled) { |
| mRefreshPeriod = nsecs_t(1e9 / mRefreshRate); |
| mNextFakeVSync = systemTime(CLOCK_MONOTONIC) + mRefreshPeriod; |
| } |
| mEnabled = enabled; |
| mCondition.signal(); |
| return true; |
| } |
| |
| bool SoftVsyncObserver::threadLoop() |
| { |
| { // scope for lock |
| Mutex::Autolock _l(mLock); |
| while (!mEnabled) { |
| mCondition.wait(mLock); |
| if (mExitThread) { |
| ITRACE("exiting thread loop"); |
| return false; |
| } |
| } |
| } |
| |
| |
| const nsecs_t period = mRefreshPeriod * mDisplayDevice.getFpsDivider(); |
| const nsecs_t now = systemTime(CLOCK_MONOTONIC); |
| nsecs_t next_vsync = mNextFakeVSync; |
| nsecs_t sleep = next_vsync - now; |
| if (sleep < 0) { |
| // we missed, find where the next vsync should be |
| sleep = (period - ((now - next_vsync) % period)); |
| next_vsync = now + sleep; |
| } |
| mNextFakeVSync = next_vsync + period; |
| |
| struct timespec spec; |
| spec.tv_sec = next_vsync / 1000000000; |
| spec.tv_nsec = next_vsync % 1000000000; |
| |
| int err; |
| do { |
| err = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &spec, NULL); |
| } while (err < 0 && errno == EINTR); |
| |
| |
| if (err == 0) { |
| mDisplayDevice.onVsync(next_vsync); |
| } |
| |
| return true; |
| } |
| |
| } // namespace intel |
| } // namesapce android |
| |