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 &timestamp) {
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