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