1 /*
2 * Copyright (C) 2015 The Android Open Source Project
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
17 #define ATRACE_TAG (ATRACE_TAG_GRAPHICS | ATRACE_TAG_HAL)
18
19 #define LOG_TAG "hwc-vsync-worker"
20
21 #include "vsyncworker.h"
22
23 #include <hardware/hardware.h>
24 #include <log/log.h>
25 #include <stdlib.h>
26 #include <time.h>
27 #include <utils/Trace.h>
28 #include <xf86drm.h>
29 #include <xf86drmMode.h>
30
31 #include <map>
32
33 #include "drmdevice.h"
34 #include "worker.h"
35
36 using namespace std::chrono_literals;
37
38 constexpr auto nsecsPerSec = std::chrono::nanoseconds(1s).count();
39
40 namespace android {
41
VSyncWorker()42 VSyncWorker::VSyncWorker()
43 : Worker("vsync", 2, true),
44 mDrmDevice(NULL),
45 mDisplay(-1),
46 mEnabled(false),
47 mLastTimestampNs(-1) {}
48
~VSyncWorker()49 VSyncWorker::~VSyncWorker() {
50 Exit();
51 }
52
Init(DrmDevice * drm,int display,const String8 & displayTraceName)53 int VSyncWorker::Init(DrmDevice *drm, int display, const String8 &displayTraceName) {
54 mDrmDevice = drm;
55 mDisplay = display;
56 mDisplayTraceName = displayTraceName;
57 mHwVsyncPeriodTag.appendFormat("HWVsyncPeriod for %s", displayTraceName.c_str());
58 mHwVsyncEnabledTag.appendFormat("HWCVsync for %s", displayTraceName.c_str());
59
60 return InitWorker();
61 }
62
RegisterCallback(std::shared_ptr<VsyncCallback> callback)63 void VSyncWorker::RegisterCallback(std::shared_ptr<VsyncCallback> callback) {
64 Lock();
65 mCallback = callback;
66 Unlock();
67 }
68
VSyncControl(bool enabled)69 void VSyncWorker::VSyncControl(bool enabled) {
70 Lock();
71 mEnabled = enabled;
72 mLastTimestampNs = -1;
73 Unlock();
74
75 ATRACE_INT(mHwVsyncEnabledTag.c_str(), static_cast<int32_t>(enabled));
76 ATRACE_INT64(mHwVsyncPeriodTag.c_str(), 0);
77 Signal();
78 }
79
80 /*
81 * Returns the timestamp of the next vsync in phase with mLastTimestampNs.
82 * For example:
83 * mLastTimestampNs = 137
84 * vsyncPeriodNs = 50
85 * currentTimeNs = 683
86 *
87 * expectTimeNs = (50 * ((683 - 137) / 50 + 1)) + 137
88 * expectTimeNs = 687
89 *
90 * Thus, we must sleep until timestamp 687 to maintain phase with the last
91 * timestamp. But if we don't know last vblank timestamp, sleep one vblank
92 * then try to get vblank from driver again.
93 */
GetPhasedVSync(uint32_t vsyncPeriodNs,int64_t & expectTimeNs)94 int VSyncWorker::GetPhasedVSync(uint32_t vsyncPeriodNs, int64_t &expectTimeNs) {
95 struct timespec now;
96 if (clock_gettime(CLOCK_MONOTONIC, &now)) {
97 ALOGE("clock_gettime failed %d", errno);
98 return -EPERM;
99 }
100
101 int64_t currentTimeNs = now.tv_sec * nsecsPerSec + now.tv_nsec;
102 if (mLastTimestampNs < 0) {
103 expectTimeNs = currentTimeNs + vsyncPeriodNs;
104 return -EAGAIN;
105 }
106
107 expectTimeNs = vsyncPeriodNs * ((currentTimeNs - mLastTimestampNs) / vsyncPeriodNs + 1)
108 + mLastTimestampNs;
109
110 return 0;
111 }
112
SyntheticWaitVBlank(int64_t & timestampNs)113 int VSyncWorker::SyntheticWaitVBlank(int64_t ×tampNs) {
114 uint32_t vsyncPeriodNs = kDefaultVsyncPeriodNanoSecond;
115 int32_t refreshRate = kDefaultRefreshRateFrequency;
116
117 DrmConnector *conn = mDrmDevice->GetConnectorForDisplay(mDisplay);
118 if (conn && conn->active_mode().te_period() != 0.0f &&
119 conn->active_mode().v_refresh() != 0.0f) {
120 vsyncPeriodNs = static_cast<uint32_t>(conn->active_mode().te_period());
121 refreshRate = static_cast<int32_t>(conn->active_mode().v_refresh());
122 } else {
123 ALOGW("Vsync worker active with conn=%p vsync=%u refresh=%d\n", conn,
124 conn ? static_cast<uint32_t>(conn->active_mode().te_period()) :
125 kDefaultVsyncPeriodNanoSecond,
126 conn ? static_cast<int32_t>(conn->active_mode().v_refresh()) :
127 kDefaultRefreshRateFrequency);
128 }
129
130 int64_t phasedTimestampNs;
131 int ret = GetPhasedVSync(vsyncPeriodNs, phasedTimestampNs);
132 if (ret && ret != -EAGAIN) return -1;
133
134 struct timespec vsync;
135 vsync.tv_sec = phasedTimestampNs / nsecsPerSec;
136 vsync.tv_nsec = phasedTimestampNs % nsecsPerSec;
137
138 int err;
139 do {
140 err = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &vsync, nullptr);
141 } while (err == EINTR);
142 if (err || ret) return -1;
143
144 timestampNs = (int64_t)vsync.tv_sec * nsecsPerSec + (int64_t)vsync.tv_nsec;
145
146 return 0;
147 }
148
Routine()149 void VSyncWorker::Routine() {
150 int ret;
151
152 Lock();
153 if (!mEnabled) {
154 ret = WaitForSignalOrExitLocked();
155 if (ret == -EINTR) {
156 Unlock();
157 return;
158 }
159 }
160
161 int display = mDisplay;
162 std::shared_ptr<VsyncCallback> callback(mCallback);
163 Unlock();
164
165 DrmCrtc *crtc = mDrmDevice->GetCrtcForDisplay(display);
166 if (!crtc) {
167 ALOGE("Failed to get crtc for display");
168 return;
169 }
170 uint32_t highCrtc = (crtc->pipe() << DRM_VBLANK_HIGH_CRTC_SHIFT);
171
172 drmVBlank vblank;
173 memset(&vblank, 0, sizeof(vblank));
174 vblank.request.type =
175 (drmVBlankSeqType)(DRM_VBLANK_RELATIVE | (highCrtc & DRM_VBLANK_HIGH_CRTC_MASK));
176 vblank.request.sequence = 1;
177
178 int64_t timestampNs;
179 ret = drmWaitVBlank(mDrmDevice->fd(), &vblank);
180 if (ret) {
181 if (SyntheticWaitVBlank(timestampNs)) {
182 // postpone the callback until we get a real value from the hardware
183 return;
184 }
185 } else {
186 timestampNs = (int64_t)vblank.reply.tval_sec * nsecsPerSec +
187 (int64_t)vblank.reply.tval_usec * 1000;
188 }
189
190 /*
191 * VSync could be disabled during routine execution so it could potentially
192 * lead to crash since callback's inner hook could be invalid anymore. We have
193 * no control over lifetime of this hook, therefore we can't rely that it'll
194 * be valid after vsync disabling.
195 *
196 * Blocking VSyncControl to wait until routine
197 * will finish execution is logically correct way to fix this issue, but it
198 * creates visible lags and stutters, so we have to resort to other ways of
199 * mitigating this issue.
200 *
201 * Doing check before attempt to invoke callback drastically shortens the
202 * window when such situation could happen and that allows us to practically
203 * avoid this issue.
204 *
205 * Please note that issue described below is different one and it is related
206 * to RegisterCallback, not to disabling vsync via VSyncControl.
207 */
208 if (!mEnabled)
209 return;
210 /*
211 * There's a race here where a change in mCallback will not take effect until
212 * the next subsequent requested vsync. This is unavoidable since we can't
213 * call the vsync hook while holding the thread lock.
214 *
215 * We could shorten the race window by caching mCallback right before calling
216 * the hook. However, in practice, mCallback is only updated once, so it's not
217 * worth the overhead.
218 */
219 if (callback) callback->Callback(display, timestampNs);
220
221 if (mLastTimestampNs >= 0) {
222 int64_t period = timestampNs - mLastTimestampNs;
223 ATRACE_INT64(mHwVsyncPeriodTag.c_str(), period);
224 ALOGV("HW vsync period %" PRId64 "ns for %s", period, mDisplayTraceName.c_str());
225 }
226
227 mLastTimestampNs = timestampNs;
228 }
229 } // namespace android
230