• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2022 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 #include "VsyncThread.h"
18 
19 #include <utils/ThreadDefs.h>
20 
21 #include <thread>
22 
23 #include "Time.h"
24 
25 namespace aidl::android::hardware::graphics::composer3::impl {
26 namespace {
27 
28 // Returns the timepoint of the next vsync after the 'now' timepoint that is
29 // a multiple of 'vsyncPeriod' in-phase/offset-from 'previousSync'.
30 //
31 // Some examples:
32 //  * vsyncPeriod=50ns previousVsync=500ns now=510ns => 550ns
33 //  * vsyncPeriod=50ns previousVsync=300ns now=510ns => 550ns
34 //  * vsyncPeriod=50ns previousVsync=500ns now=550ns => 550ns
GetNextVsyncInPhase(Nanoseconds vsyncPeriod,TimePoint previousVsync,TimePoint now)35 TimePoint GetNextVsyncInPhase(Nanoseconds vsyncPeriod, TimePoint previousVsync,
36                               TimePoint now) {
37   const auto elapsed = Nanoseconds(now - previousVsync);
38   const auto nextMultiple = (elapsed / vsyncPeriod) + 1;
39   return previousVsync + (nextMultiple * vsyncPeriod);
40 }
41 
42 }  // namespace
43 
VsyncThread(int64_t displayId)44 VsyncThread::VsyncThread(int64_t displayId) : mDisplayId(displayId) {
45   mPreviousVsync = std::chrono::steady_clock::now() - mVsyncPeriod;
46 }
47 
~VsyncThread()48 VsyncThread::~VsyncThread() { stop(); }
49 
start(int32_t vsyncPeriodNanos)50 HWC3::Error VsyncThread::start(int32_t vsyncPeriodNanos) {
51   DEBUG_LOG("%s for display:%" PRIu64, __FUNCTION__, mDisplayId);
52 
53   mVsyncPeriod = Nanoseconds(vsyncPeriodNanos);
54 
55   mThread = std::thread([this]() { threadLoop(); });
56 
57   const std::string name =
58       "display_" + std::to_string(mDisplayId) + "_vsync_thread";
59 
60   int ret = pthread_setname_np(mThread.native_handle(), name.c_str());
61   if (ret != 0) {
62     ALOGE("%s: failed to set Vsync thread name: %s", __FUNCTION__,
63           strerror(ret));
64   }
65 
66   struct sched_param param = {
67       .sched_priority = ANDROID_PRIORITY_DISPLAY,
68   };
69   ret = pthread_setschedparam(mThread.native_handle(), SCHED_FIFO, &param);
70   if (ret != 0) {
71     ALOGE("%s: failed to set Vsync thread priority: %s", __FUNCTION__,
72           strerror(ret));
73   }
74 
75   return HWC3::Error::None;
76 }
77 
stop()78 HWC3::Error VsyncThread::stop() {
79   mShuttingDown.store(true);
80   mThread.join();
81 
82   return HWC3::Error::None;
83 }
84 
setCallbacks(const std::shared_ptr<IComposerCallback> & callback)85 HWC3::Error VsyncThread::setCallbacks(
86     const std::shared_ptr<IComposerCallback>& callback) {
87   DEBUG_LOG("%s for display:%" PRIu64, __FUNCTION__, mDisplayId);
88 
89   std::unique_lock<std::mutex> lock(mStateMutex);
90 
91   mCallbacks = callback;
92 
93   return HWC3::Error::None;
94 }
95 
setVsyncEnabled(bool enabled)96 HWC3::Error VsyncThread::setVsyncEnabled(bool enabled) {
97   DEBUG_LOG("%s for display:%" PRIu64 " enabled:%d", __FUNCTION__, mDisplayId,
98             enabled);
99 
100   std::unique_lock<std::mutex> lock(mStateMutex);
101 
102   mVsyncEnabled = enabled;
103 
104   return HWC3::Error::None;
105 }
106 
scheduleVsyncUpdate(int32_t newVsyncPeriod,const VsyncPeriodChangeConstraints & constraints,VsyncPeriodChangeTimeline * outTimeline)107 HWC3::Error VsyncThread::scheduleVsyncUpdate(
108     int32_t newVsyncPeriod, const VsyncPeriodChangeConstraints& constraints,
109     VsyncPeriodChangeTimeline* outTimeline) {
110   DEBUG_LOG("%s for display:%" PRIu64, __FUNCTION__, mDisplayId);
111 
112   PendingUpdate update;
113   update.period = Nanoseconds(newVsyncPeriod);
114   update.updateAfter = asTimePoint(constraints.desiredTimeNanos);
115 
116   std::unique_lock<std::mutex> lock(mStateMutex);
117   mPendingUpdate.emplace(std::move(update));
118 
119   TimePoint nextVsync =
120       GetNextVsyncInPhase(mVsyncPeriod, mPreviousVsync, update.updateAfter);
121 
122   outTimeline->newVsyncAppliedTimeNanos = asNanosTimePoint(nextVsync);
123   outTimeline->refreshRequired = false;
124   outTimeline->refreshTimeNanos = 0;
125 
126   return HWC3::Error::None;
127 }
128 
updateVsyncPeriodLocked(TimePoint now)129 Nanoseconds VsyncThread::updateVsyncPeriodLocked(TimePoint now) {
130   if (mPendingUpdate && now > mPendingUpdate->updateAfter) {
131     mVsyncPeriod = mPendingUpdate->period;
132     mPendingUpdate.reset();
133   }
134 
135   return mVsyncPeriod;
136 }
137 
threadLoop()138 void VsyncThread::threadLoop() {
139   ALOGI("Vsync thread for display:%" PRId64 " starting", mDisplayId);
140 
141   Nanoseconds vsyncPeriod = mVsyncPeriod;
142 
143   int vsyncs = 0;
144   TimePoint previousLog = std::chrono::steady_clock::now();
145 
146   while (!mShuttingDown.load()) {
147     TimePoint now = std::chrono::steady_clock::now();
148     TimePoint nextVsync = GetNextVsyncInPhase(vsyncPeriod, mPreviousVsync, now);
149 
150     std::this_thread::sleep_until(nextVsync);
151     {
152       std::unique_lock<std::mutex> lock(mStateMutex);
153 
154       mPreviousVsync = nextVsync;
155 
156       // Display has finished refreshing at previous vsync period. Update the
157       // vsync period if there was a pending update.
158       vsyncPeriod = updateVsyncPeriodLocked(mPreviousVsync);
159     }
160 
161     if (mVsyncEnabled) {
162       if (mCallbacks) {
163         DEBUG_LOG("%s: for display:%" PRIu64 " calling vsync", __FUNCTION__,
164                   mDisplayId);
165         mCallbacks->onVsync(mDisplayId, asNanosTimePoint(nextVsync),
166                             asNanosDuration(vsyncPeriod));
167       }
168     }
169 
170     static constexpr const int kLogIntervalSeconds = 60;
171     if (now > (previousLog + std::chrono::seconds(kLogIntervalSeconds))) {
172       DEBUG_LOG("%s: for display:%" PRIu64 " send %" PRIu32
173                 " in last %d seconds",
174                 __FUNCTION__, mDisplayId, vsyncs, kLogIntervalSeconds);
175       previousLog = now;
176       vsyncs = 0;
177     }
178     ++vsyncs;
179   }
180 
181   ALOGI("Vsync thread for display:%" PRId64 " finished", mDisplayId);
182 }
183 
184 }  // namespace aidl::android::hardware::graphics::composer3::impl
185