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