• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 LOG_TAG "hwc-vsync-worker"
18 
19 #include "VSyncWorker.h"
20 
21 #include <xf86drm.h>
22 #include <xf86drmMode.h>
23 
24 #include <cstdlib>
25 #include <cstring>
26 #include <ctime>
27 
28 #include "utils/log.h"
29 
30 namespace android {
31 
VSyncWorker()32 VSyncWorker::VSyncWorker() : Worker("vsync", HAL_PRIORITY_URGENT_DISPLAY){};
33 
Init(DrmDisplayPipeline * pipe,std::function<void (uint64_t)> callback)34 auto VSyncWorker::Init(DrmDisplayPipeline *pipe,
35                        std::function<void(uint64_t /*timestamp*/)> callback)
36     -> int {
37   pipe_ = pipe;
38   callback_ = std::move(callback);
39 
40   return InitWorker();
41 }
42 
VSyncControl(bool enabled)43 void VSyncWorker::VSyncControl(bool enabled) {
44   Lock();
45   enabled_ = enabled;
46   last_timestamp_ = -1;
47   Unlock();
48 
49   Signal();
50 }
51 
52 /*
53  * Returns the timestamp of the next vsync in phase with last_timestamp_.
54  * For example:
55  *  last_timestamp_ = 137
56  *  frame_ns = 50
57  *  current = 683
58  *
59  *  ret = (50 * ((683 - 137)/50 + 1)) + 137
60  *  ret = 687
61  *
62  *  Thus, we must sleep until timestamp 687 to maintain phase with the last
63  *  timestamp.
64  */
GetPhasedVSync(int64_t frame_ns,int64_t current) const65 int64_t VSyncWorker::GetPhasedVSync(int64_t frame_ns, int64_t current) const {
66   if (last_timestamp_ < 0)
67     return current + frame_ns;
68 
69   return frame_ns * ((current - last_timestamp_) / frame_ns + 1) +
70          last_timestamp_;
71 }
72 
73 static const int64_t kOneSecondNs = 1LL * 1000 * 1000 * 1000;
74 
SyntheticWaitVBlank(int64_t * timestamp)75 int VSyncWorker::SyntheticWaitVBlank(int64_t *timestamp) {
76   struct timespec vsync {};
77   int ret = clock_gettime(CLOCK_MONOTONIC, &vsync);
78   if (ret)
79     return ret;
80 
81   float refresh = 60.0F;  // Default to 60Hz refresh rate
82   if (pipe_ != nullptr &&
83       pipe_->connector->Get()->GetActiveMode().v_refresh() != 0.0F) {
84     refresh = pipe_->connector->Get()->GetActiveMode().v_refresh();
85   }
86 
87   int64_t phased_timestamp = GetPhasedVSync(kOneSecondNs /
88                                                 static_cast<int>(refresh),
89                                             vsync.tv_sec * kOneSecondNs +
90                                                 vsync.tv_nsec);
91   vsync.tv_sec = phased_timestamp / kOneSecondNs;
92   vsync.tv_nsec = int(phased_timestamp - (vsync.tv_sec * kOneSecondNs));
93   do {
94     ret = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &vsync, nullptr);
95   } while (ret == EINTR);
96   if (ret)
97     return ret;
98 
99   *timestamp = (int64_t)vsync.tv_sec * kOneSecondNs + (int64_t)vsync.tv_nsec;
100   return 0;
101 }
102 
Routine()103 void VSyncWorker::Routine() {
104   int ret = 0;
105 
106   Lock();
107   if (!enabled_) {
108     ret = WaitForSignalOrExitLocked();
109     if (ret == -EINTR) {
110       Unlock();
111       return;
112     }
113   }
114 
115   auto *pipe = pipe_;
116   Unlock();
117 
118   ret = -EAGAIN;
119   int64_t timestamp = 0;
120   drmVBlank vblank{};
121 
122   if (pipe != nullptr) {
123     uint32_t high_crtc = (pipe->crtc->Get()->GetIndexInResArray()
124                           << DRM_VBLANK_HIGH_CRTC_SHIFT);
125 
126     vblank.request.type = (drmVBlankSeqType)(DRM_VBLANK_RELATIVE |
127                                              (high_crtc &
128                                               DRM_VBLANK_HIGH_CRTC_MASK));
129     vblank.request.sequence = 1;
130 
131     ret = drmWaitVBlank(pipe->device->GetFd(), &vblank);
132     if (ret == -EINTR)
133       return;
134   }
135 
136   if (ret) {
137     ret = SyntheticWaitVBlank(&timestamp);
138     if (ret)
139       return;
140   } else {
141     timestamp = (int64_t)vblank.reply.tval_sec * kOneSecondNs +
142                 (int64_t)vblank.reply.tval_usec * 1000;
143   }
144 
145   if (!enabled_)
146     return;
147 
148   if (callback_) {
149     callback_(timestamp);
150   }
151 
152   last_timestamp_ = timestamp;
153 }
154 }  // namespace android
155