/*
// 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