1/*
2// Copyright (c) 2014 Intel Corporation 
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8//      http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16#include <common/utils/HwcTrace.h>
17#include <common/observers/SoftVsyncObserver.h>
18#include <IDisplayDevice.h>
19
20extern "C" int clock_nanosleep(clockid_t clock_id, int flags,
21                           const struct timespec *request,
22                           struct timespec *remain);
23
24
25namespace android {
26namespace intel {
27
28SoftVsyncObserver::SoftVsyncObserver(IDisplayDevice& disp)
29    : mDisplayDevice(disp),
30      mDevice(IDisplayDevice::DEVICE_COUNT),
31      mEnabled(false),
32      mRefreshRate(60), // default 60 frames per second
33      mRefreshPeriod(0),
34      mLock(),
35      mCondition(),
36      mNextFakeVSync(0),
37      mExitThread(false),
38      mInitialized(false)
39{
40}
41
42SoftVsyncObserver::~SoftVsyncObserver()
43{
44    WARN_IF_NOT_DEINIT();
45}
46
47bool SoftVsyncObserver::initialize()
48{
49    if (mInitialized) {
50        WLOGTRACE("object has been initialized");
51        return true;
52    }
53
54    mExitThread = false;
55    mEnabled = false;
56    mRefreshRate = 60;
57    mDevice = mDisplayDevice.getType();
58    mThread = new VsyncEventPollThread(this);
59    if (!mThread.get()) {
60        DEINIT_AND_RETURN_FALSE("failed to create vsync event poll thread.");
61    }
62    mThread->run("SoftVsyncObserver", PRIORITY_URGENT_DISPLAY);
63    mInitialized = true;
64    return true;
65}
66
67void SoftVsyncObserver::deinitialize()
68{
69    if (mEnabled) {
70        WLOGTRACE("soft vsync is still enabled");
71        control(false);
72    }
73
74    mExitThread = true;
75    mCondition.signal();
76
77    if (mThread.get()) {
78        mThread->requestExitAndWait();
79        mThread = NULL;
80    }
81    mInitialized = false;
82}
83
84void SoftVsyncObserver::setRefreshRate(int rate)
85{
86    if (mEnabled) {
87        WLOGTRACE("too late to set refresh rate");
88    } else if (rate < 1 || rate > 120) {
89        WLOGTRACE("invalid refresh rate %d", rate);
90    } else {
91        mRefreshRate = rate;
92    }
93}
94
95bool SoftVsyncObserver::control(bool enabled)
96{
97    if (enabled == mEnabled) {
98        WLOGTRACE("vsync state %d is not changed", enabled);
99        return true;
100    }
101
102    if (enabled) {
103        mRefreshPeriod = nsecs_t(1e9 / mRefreshRate);
104        mNextFakeVSync = systemTime(CLOCK_MONOTONIC) + mRefreshPeriod;
105    }
106    mEnabled = enabled;
107    mCondition.signal();
108    return true;
109}
110
111bool SoftVsyncObserver::threadLoop()
112{
113    { // scope for lock
114        Mutex::Autolock _l(mLock);
115        while (!mEnabled) {
116            mCondition.wait(mLock);
117            if (mExitThread) {
118                ILOGTRACE("exiting thread loop");
119                return false;
120            }
121        }
122    }
123
124
125    const nsecs_t period = mRefreshPeriod;
126    const nsecs_t now = systemTime(CLOCK_MONOTONIC);
127    nsecs_t next_vsync = mNextFakeVSync;
128    nsecs_t sleep = next_vsync - now;
129    if (sleep < 0) {
130        // we missed, find where the next vsync should be
131        sleep = (period - ((now - next_vsync) % period));
132        next_vsync = now + sleep;
133    }
134    mNextFakeVSync = next_vsync + period;
135
136    struct timespec spec;
137    spec.tv_sec  = next_vsync / 1000000000;
138    spec.tv_nsec = next_vsync % 1000000000;
139
140    int err;
141    do {
142        err = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &spec, NULL);
143    } while (err < 0 && errno == EINTR);
144
145
146    if (err == 0) {
147        mDisplayDevice.onVsync(next_vsync);
148    }
149
150    return true;
151}
152
153} // namespace intel
154} // namesapce android
155
156