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 <HwcTrace.h>
17 #include <SoftVsyncObserver.h>
18 #include <IDisplayDevice.h>
19 
20 extern "C" int clock_nanosleep(clockid_t clock_id, int flags,
21                            const struct timespec *request,
22                            struct timespec *remain);
23 
24 
25 namespace android {
26 namespace intel {
27 
SoftVsyncObserver(IDisplayDevice & disp)28 SoftVsyncObserver::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 
~SoftVsyncObserver()42 SoftVsyncObserver::~SoftVsyncObserver()
43 {
44     WARN_IF_NOT_DEINIT();
45 }
46 
initialize()47 bool SoftVsyncObserver::initialize()
48 {
49     if (mInitialized) {
50         WTRACE("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 
deinitialize()67 void SoftVsyncObserver::deinitialize()
68 {
69     if (mEnabled) {
70         WTRACE("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 
setRefreshRate(int rate)84 void SoftVsyncObserver::setRefreshRate(int rate)
85 {
86     if (mEnabled) {
87         WTRACE("too late to set refresh rate");
88     } else if (rate < 1 || rate > 120) {
89         WTRACE("invalid refresh rate %d", rate);
90     } else {
91         mRefreshRate = rate;
92     }
93 }
94 
control(bool enabled)95 bool SoftVsyncObserver::control(bool enabled)
96 {
97     if (enabled == mEnabled) {
98         WTRACE("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 
threadLoop()111 bool SoftVsyncObserver::threadLoop()
112 {
113     { // scope for lock
114         Mutex::Autolock _l(mLock);
115         while (!mEnabled) {
116             mCondition.wait(mLock);
117             if (mExitThread) {
118                 ITRACE("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