• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2018 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
18 
19 #include "Scheduler.h"
20 
21 #include <algorithm>
22 #include <cinttypes>
23 #include <cstdint>
24 #include <memory>
25 #include <numeric>
26 
27 #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
28 #include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
29 #include <configstore/Utils.h>
30 #include <cutils/properties.h>
31 #include <input/InputWindow.h>
32 #include <system/window.h>
33 #include <ui/DisplayStatInfo.h>
34 #include <utils/Timers.h>
35 #include <utils/Trace.h>
36 
37 #include "DispSync.h"
38 #include "DispSyncSource.h"
39 #include "EventControlThread.h"
40 #include "EventThread.h"
41 #include "IdleTimer.h"
42 #include "InjectVSyncSource.h"
43 #include "LayerInfo.h"
44 #include "SchedulerUtils.h"
45 #include "SurfaceFlingerProperties.h"
46 
47 namespace android {
48 
49 using namespace android::hardware::configstore;
50 using namespace android::hardware::configstore::V1_0;
51 using namespace android::sysprop;
52 
53 #define RETURN_VALUE_IF_INVALID(value) \
54     if (handle == nullptr || mConnections.count(handle->id) == 0) return value
55 #define RETURN_IF_INVALID() \
56     if (handle == nullptr || mConnections.count(handle->id) == 0) return
57 
58 std::atomic<int64_t> Scheduler::sNextId = 0;
59 
Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function,const scheduler::RefreshRateConfigs & refreshRateConfig)60 Scheduler::Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function,
61                      const scheduler::RefreshRateConfigs& refreshRateConfig)
62       : mHasSyncFramework(running_without_sync_framework(true)),
63         mDispSyncPresentTimeOffset(present_time_offset_from_vsync_ns(0)),
64         mPrimaryHWVsyncEnabled(false),
65         mHWVsyncAvailable(false),
66         mRefreshRateConfigs(refreshRateConfig) {
67     // Note: We create a local temporary with the real DispSync implementation
68     // type temporarily so we can initialize it with the configured values,
69     // before storing it for more generic use using the interface type.
70     auto primaryDispSync = std::make_unique<impl::DispSync>("SchedulerDispSync");
71     primaryDispSync->init(mHasSyncFramework, mDispSyncPresentTimeOffset);
72     mPrimaryDispSync = std::move(primaryDispSync);
73     mEventControlThread = std::make_unique<impl::EventControlThread>(function);
74 
75     mSetIdleTimerMs = set_idle_timer_ms(0);
76     mSupportKernelTimer = support_kernel_idle_timer(false);
77 
78     mSetTouchTimerMs = set_touch_timer_ms(0);
79 
80     char value[PROPERTY_VALUE_MAX];
81     property_get("debug.sf.set_idle_timer_ms", value, "0");
82     int int_value = atoi(value);
83     if (int_value) {
84         mSetIdleTimerMs = atoi(value);
85     }
86 
87     if (mSetIdleTimerMs > 0) {
88         if (mSupportKernelTimer) {
89             mIdleTimer =
90                     std::make_unique<scheduler::IdleTimer>(std::chrono::milliseconds(
91                                                                    mSetIdleTimerMs),
92                                                            [this] { resetKernelTimerCallback(); },
93                                                            [this] {
94                                                                expiredKernelTimerCallback();
95                                                            });
96         } else {
97             mIdleTimer = std::make_unique<scheduler::IdleTimer>(std::chrono::milliseconds(
98                                                                         mSetIdleTimerMs),
99                                                                 [this] { resetTimerCallback(); },
100                                                                 [this] { expiredTimerCallback(); });
101         }
102         mIdleTimer->start();
103     }
104 
105     if (mSetTouchTimerMs > 0) {
106         // Touch events are coming to SF every 100ms, so the timer needs to be higher than that
107         mTouchTimer =
108                 std::make_unique<scheduler::IdleTimer>(std::chrono::milliseconds(mSetTouchTimerMs),
109                                                        [this] { resetTouchTimerCallback(); },
110                                                        [this] { expiredTouchTimerCallback(); });
111         mTouchTimer->start();
112     }
113 }
114 
~Scheduler()115 Scheduler::~Scheduler() {
116     // Ensure the IdleTimer thread is joined before we start destroying state.
117     mTouchTimer.reset();
118     mIdleTimer.reset();
119 }
120 
createConnection(const char * connectionName,int64_t phaseOffsetNs,ResyncCallback resyncCallback,impl::EventThread::InterceptVSyncsCallback interceptCallback)121 sp<Scheduler::ConnectionHandle> Scheduler::createConnection(
122         const char* connectionName, int64_t phaseOffsetNs, ResyncCallback resyncCallback,
123         impl::EventThread::InterceptVSyncsCallback interceptCallback) {
124     const int64_t id = sNextId++;
125     ALOGV("Creating a connection handle with ID: %" PRId64 "\n", id);
126 
127     std::unique_ptr<EventThread> eventThread =
128             makeEventThread(connectionName, mPrimaryDispSync.get(), phaseOffsetNs,
129                             std::move(interceptCallback));
130 
131     auto eventThreadConnection =
132             createConnectionInternal(eventThread.get(), std::move(resyncCallback));
133     mConnections.emplace(id,
134                          std::make_unique<Connection>(new ConnectionHandle(id),
135                                                       eventThreadConnection,
136                                                       std::move(eventThread)));
137     return mConnections[id]->handle;
138 }
139 
makeEventThread(const char * connectionName,DispSync * dispSync,int64_t phaseOffsetNs,impl::EventThread::InterceptVSyncsCallback interceptCallback)140 std::unique_ptr<EventThread> Scheduler::makeEventThread(
141         const char* connectionName, DispSync* dispSync, int64_t phaseOffsetNs,
142         impl::EventThread::InterceptVSyncsCallback interceptCallback) {
143     std::unique_ptr<VSyncSource> eventThreadSource =
144             std::make_unique<DispSyncSource>(dispSync, phaseOffsetNs, true, connectionName);
145     return std::make_unique<impl::EventThread>(std::move(eventThreadSource),
146                                                std::move(interceptCallback), connectionName);
147 }
148 
createConnectionInternal(EventThread * eventThread,ResyncCallback && resyncCallback)149 sp<EventThreadConnection> Scheduler::createConnectionInternal(EventThread* eventThread,
150                                                               ResyncCallback&& resyncCallback) {
151     return eventThread->createEventConnection(std::move(resyncCallback));
152 }
153 
createDisplayEventConnection(const sp<Scheduler::ConnectionHandle> & handle,ResyncCallback resyncCallback)154 sp<IDisplayEventConnection> Scheduler::createDisplayEventConnection(
155         const sp<Scheduler::ConnectionHandle>& handle, ResyncCallback resyncCallback) {
156     RETURN_VALUE_IF_INVALID(nullptr);
157     return createConnectionInternal(mConnections[handle->id]->thread.get(),
158                                     std::move(resyncCallback));
159 }
160 
getEventThread(const sp<Scheduler::ConnectionHandle> & handle)161 EventThread* Scheduler::getEventThread(const sp<Scheduler::ConnectionHandle>& handle) {
162     RETURN_VALUE_IF_INVALID(nullptr);
163     return mConnections[handle->id]->thread.get();
164 }
165 
getEventConnection(const sp<ConnectionHandle> & handle)166 sp<EventThreadConnection> Scheduler::getEventConnection(const sp<ConnectionHandle>& handle) {
167     RETURN_VALUE_IF_INVALID(nullptr);
168     return mConnections[handle->id]->eventConnection;
169 }
170 
hotplugReceived(const sp<Scheduler::ConnectionHandle> & handle,PhysicalDisplayId displayId,bool connected)171 void Scheduler::hotplugReceived(const sp<Scheduler::ConnectionHandle>& handle,
172                                 PhysicalDisplayId displayId, bool connected) {
173     RETURN_IF_INVALID();
174     mConnections[handle->id]->thread->onHotplugReceived(displayId, connected);
175 }
176 
onScreenAcquired(const sp<Scheduler::ConnectionHandle> & handle)177 void Scheduler::onScreenAcquired(const sp<Scheduler::ConnectionHandle>& handle) {
178     RETURN_IF_INVALID();
179     mConnections[handle->id]->thread->onScreenAcquired();
180 }
181 
onScreenReleased(const sp<Scheduler::ConnectionHandle> & handle)182 void Scheduler::onScreenReleased(const sp<Scheduler::ConnectionHandle>& handle) {
183     RETURN_IF_INVALID();
184     mConnections[handle->id]->thread->onScreenReleased();
185 }
186 
onConfigChanged(const sp<ConnectionHandle> & handle,PhysicalDisplayId displayId,int32_t configId)187 void Scheduler::onConfigChanged(const sp<ConnectionHandle>& handle, PhysicalDisplayId displayId,
188                                 int32_t configId) {
189     RETURN_IF_INVALID();
190     mConnections[handle->id]->thread->onConfigChanged(displayId, configId);
191 }
192 
dump(const sp<Scheduler::ConnectionHandle> & handle,std::string & result) const193 void Scheduler::dump(const sp<Scheduler::ConnectionHandle>& handle, std::string& result) const {
194     RETURN_IF_INVALID();
195     mConnections.at(handle->id)->thread->dump(result);
196 }
197 
setPhaseOffset(const sp<Scheduler::ConnectionHandle> & handle,nsecs_t phaseOffset)198 void Scheduler::setPhaseOffset(const sp<Scheduler::ConnectionHandle>& handle, nsecs_t phaseOffset) {
199     RETURN_IF_INVALID();
200     mConnections[handle->id]->thread->setPhaseOffset(phaseOffset);
201 }
202 
getDisplayStatInfo(DisplayStatInfo * stats)203 void Scheduler::getDisplayStatInfo(DisplayStatInfo* stats) {
204     stats->vsyncTime = mPrimaryDispSync->computeNextRefresh(0);
205     stats->vsyncPeriod = mPrimaryDispSync->getPeriod();
206 }
207 
enableHardwareVsync()208 void Scheduler::enableHardwareVsync() {
209     std::lock_guard<std::mutex> lock(mHWVsyncLock);
210     if (!mPrimaryHWVsyncEnabled && mHWVsyncAvailable) {
211         mPrimaryDispSync->beginResync();
212         mEventControlThread->setVsyncEnabled(true);
213         mPrimaryHWVsyncEnabled = true;
214     }
215 }
216 
disableHardwareVsync(bool makeUnavailable)217 void Scheduler::disableHardwareVsync(bool makeUnavailable) {
218     std::lock_guard<std::mutex> lock(mHWVsyncLock);
219     if (mPrimaryHWVsyncEnabled) {
220         mEventControlThread->setVsyncEnabled(false);
221         mPrimaryDispSync->endResync();
222         mPrimaryHWVsyncEnabled = false;
223     }
224     if (makeUnavailable) {
225         mHWVsyncAvailable = false;
226     }
227 }
228 
resyncToHardwareVsync(bool makeAvailable,nsecs_t period)229 void Scheduler::resyncToHardwareVsync(bool makeAvailable, nsecs_t period) {
230     {
231         std::lock_guard<std::mutex> lock(mHWVsyncLock);
232         if (makeAvailable) {
233             mHWVsyncAvailable = makeAvailable;
234         } else if (!mHWVsyncAvailable) {
235             // Hardware vsync is not currently available, so abort the resync
236             // attempt for now
237             return;
238         }
239     }
240 
241     if (period <= 0) {
242         return;
243     }
244 
245     setVsyncPeriod(period);
246 }
247 
makeResyncCallback(GetVsyncPeriod && getVsyncPeriod)248 ResyncCallback Scheduler::makeResyncCallback(GetVsyncPeriod&& getVsyncPeriod) {
249     std::weak_ptr<VsyncState> ptr = mPrimaryVsyncState;
250     return [ptr, getVsyncPeriod = std::move(getVsyncPeriod)]() {
251         if (const auto vsync = ptr.lock()) {
252             vsync->resync(getVsyncPeriod);
253         }
254     };
255 }
256 
resync(const GetVsyncPeriod & getVsyncPeriod)257 void Scheduler::VsyncState::resync(const GetVsyncPeriod& getVsyncPeriod) {
258     static constexpr nsecs_t kIgnoreDelay = ms2ns(500);
259 
260     const nsecs_t now = systemTime();
261     const nsecs_t last = lastResyncTime.exchange(now);
262 
263     if (now - last > kIgnoreDelay) {
264         scheduler.resyncToHardwareVsync(false, getVsyncPeriod());
265     }
266 }
267 
setRefreshSkipCount(int count)268 void Scheduler::setRefreshSkipCount(int count) {
269     mPrimaryDispSync->setRefreshSkipCount(count);
270 }
271 
setVsyncPeriod(const nsecs_t period)272 void Scheduler::setVsyncPeriod(const nsecs_t period) {
273     std::lock_guard<std::mutex> lock(mHWVsyncLock);
274     mPrimaryDispSync->setPeriod(period);
275 
276     if (!mPrimaryHWVsyncEnabled) {
277         mPrimaryDispSync->beginResync();
278         mEventControlThread->setVsyncEnabled(true);
279         mPrimaryHWVsyncEnabled = true;
280     }
281 }
282 
addResyncSample(const nsecs_t timestamp,bool * periodChanged)283 void Scheduler::addResyncSample(const nsecs_t timestamp, bool* periodChanged) {
284     bool needsHwVsync = false;
285     *periodChanged = false;
286     { // Scope for the lock
287         std::lock_guard<std::mutex> lock(mHWVsyncLock);
288         if (mPrimaryHWVsyncEnabled) {
289             needsHwVsync = mPrimaryDispSync->addResyncSample(timestamp, periodChanged);
290         }
291     }
292 
293     if (needsHwVsync) {
294         enableHardwareVsync();
295     } else {
296         disableHardwareVsync(false);
297     }
298 }
299 
addPresentFence(const std::shared_ptr<FenceTime> & fenceTime)300 void Scheduler::addPresentFence(const std::shared_ptr<FenceTime>& fenceTime) {
301     if (mPrimaryDispSync->addPresentFence(fenceTime)) {
302         enableHardwareVsync();
303     } else {
304         disableHardwareVsync(false);
305     }
306 }
307 
setIgnorePresentFences(bool ignore)308 void Scheduler::setIgnorePresentFences(bool ignore) {
309     mPrimaryDispSync->setIgnorePresentFences(ignore);
310 }
311 
expectedPresentTime()312 nsecs_t Scheduler::expectedPresentTime() {
313     return mPrimaryDispSync->expectedPresentTime();
314 }
315 
dumpPrimaryDispSync(std::string & result) const316 void Scheduler::dumpPrimaryDispSync(std::string& result) const {
317     mPrimaryDispSync->dump(result);
318 }
319 
registerLayer(std::string const & name,int windowType)320 std::unique_ptr<scheduler::LayerHistory::LayerHandle> Scheduler::registerLayer(
321         std::string const& name, int windowType) {
322     RefreshRateType refreshRateType = (windowType == InputWindowInfo::TYPE_WALLPAPER)
323             ? RefreshRateType::DEFAULT
324             : RefreshRateType::PERFORMANCE;
325 
326     const auto refreshRate = mRefreshRateConfigs.getRefreshRate(refreshRateType);
327     const uint32_t fps = (refreshRate) ? refreshRate->fps : 0;
328     return mLayerHistory.createLayer(name, fps);
329 }
330 
addLayerPresentTimeAndHDR(const std::unique_ptr<scheduler::LayerHistory::LayerHandle> & layerHandle,nsecs_t presentTime,bool isHDR)331 void Scheduler::addLayerPresentTimeAndHDR(
332         const std::unique_ptr<scheduler::LayerHistory::LayerHandle>& layerHandle,
333         nsecs_t presentTime, bool isHDR) {
334     mLayerHistory.insert(layerHandle, presentTime, isHDR);
335 }
336 
setLayerVisibility(const std::unique_ptr<scheduler::LayerHistory::LayerHandle> & layerHandle,bool visible)337 void Scheduler::setLayerVisibility(
338         const std::unique_ptr<scheduler::LayerHistory::LayerHandle>& layerHandle, bool visible) {
339     mLayerHistory.setVisibility(layerHandle, visible);
340 }
341 
withPrimaryDispSync(std::function<void (DispSync &)> const & fn)342 void Scheduler::withPrimaryDispSync(std::function<void(DispSync&)> const& fn) {
343     fn(*mPrimaryDispSync);
344 }
345 
updateFpsBasedOnContent()346 void Scheduler::updateFpsBasedOnContent() {
347     auto [refreshRate, isHDR] = mLayerHistory.getDesiredRefreshRateAndHDR();
348     const uint32_t refreshRateRound = std::round(refreshRate);
349     RefreshRateType newRefreshRateType;
350     {
351         std::lock_guard<std::mutex> lock(mFeatureStateLock);
352         if (mContentRefreshRate == refreshRateRound && mIsHDRContent == isHDR) {
353             return;
354         }
355         mContentRefreshRate = refreshRateRound;
356         ATRACE_INT("ContentFPS", mContentRefreshRate);
357 
358         mIsHDRContent = isHDR;
359         ATRACE_INT("ContentHDR", mIsHDRContent);
360 
361         mCurrentContentFeatureState = refreshRateRound > 0
362                 ? ContentFeatureState::CONTENT_DETECTION_ON
363                 : ContentFeatureState::CONTENT_DETECTION_OFF;
364         newRefreshRateType = calculateRefreshRateType();
365         if (mRefreshRateType == newRefreshRateType) {
366             return;
367         }
368         mRefreshRateType = newRefreshRateType;
369     }
370     changeRefreshRate(newRefreshRateType, ConfigEvent::Changed);
371 }
372 
setChangeRefreshRateCallback(const ChangeRefreshRateCallback & changeRefreshRateCallback)373 void Scheduler::setChangeRefreshRateCallback(
374         const ChangeRefreshRateCallback& changeRefreshRateCallback) {
375     std::lock_guard<std::mutex> lock(mCallbackLock);
376     mChangeRefreshRateCallback = changeRefreshRateCallback;
377 }
378 
setGetVsyncPeriodCallback(const GetVsyncPeriod && getVsyncPeriod)379 void Scheduler::setGetVsyncPeriodCallback(const GetVsyncPeriod&& getVsyncPeriod) {
380     std::lock_guard<std::mutex> lock(mCallbackLock);
381     mGetVsyncPeriod = getVsyncPeriod;
382 }
383 
updateFrameSkipping(const int64_t skipCount)384 void Scheduler::updateFrameSkipping(const int64_t skipCount) {
385     ATRACE_INT("FrameSkipCount", skipCount);
386     if (mSkipCount != skipCount) {
387         // Only update DispSync if it hasn't been updated yet.
388         mPrimaryDispSync->setRefreshSkipCount(skipCount);
389         mSkipCount = skipCount;
390     }
391 }
392 
resetIdleTimer()393 void Scheduler::resetIdleTimer() {
394     if (mIdleTimer) {
395         mIdleTimer->reset();
396     }
397 }
398 
notifyTouchEvent()399 void Scheduler::notifyTouchEvent() {
400     if (mTouchTimer) {
401         mTouchTimer->reset();
402     }
403 
404     if (mSupportKernelTimer) {
405         resetIdleTimer();
406     }
407 }
408 
resetTimerCallback()409 void Scheduler::resetTimerCallback() {
410     timerChangeRefreshRate(IdleTimerState::RESET);
411     ATRACE_INT("ExpiredIdleTimer", 0);
412 }
413 
resetKernelTimerCallback()414 void Scheduler::resetKernelTimerCallback() {
415     ATRACE_INT("ExpiredKernelIdleTimer", 0);
416     std::lock_guard<std::mutex> lock(mCallbackLock);
417     if (mGetVsyncPeriod) {
418         resyncToHardwareVsync(false, mGetVsyncPeriod());
419     }
420 }
421 
expiredTimerCallback()422 void Scheduler::expiredTimerCallback() {
423     timerChangeRefreshRate(IdleTimerState::EXPIRED);
424     ATRACE_INT("ExpiredIdleTimer", 1);
425 }
426 
resetTouchTimerCallback()427 void Scheduler::resetTouchTimerCallback() {
428     // We do not notify the applications about config changes when idle timer is reset.
429     touchChangeRefreshRate(TouchState::ACTIVE);
430     ATRACE_INT("TouchState", 1);
431 }
432 
expiredTouchTimerCallback()433 void Scheduler::expiredTouchTimerCallback() {
434     // We do not notify the applications about config changes when idle timer expires.
435     touchChangeRefreshRate(TouchState::INACTIVE);
436     ATRACE_INT("TouchState", 0);
437 }
438 
expiredKernelTimerCallback()439 void Scheduler::expiredKernelTimerCallback() {
440     ATRACE_INT("ExpiredKernelIdleTimer", 1);
441     // Disable HW Vsync if the timer expired, as we don't need it
442     // enabled if we're not pushing frames.
443     disableHardwareVsync(false);
444 }
445 
doDump()446 std::string Scheduler::doDump() {
447     std::ostringstream stream;
448     stream << "+  Idle timer interval: " << mSetIdleTimerMs << " ms" << std::endl;
449     stream << "+  Touch timer interval: " << mSetTouchTimerMs << " ms" << std::endl;
450     return stream.str();
451 }
452 
timerChangeRefreshRate(IdleTimerState idleTimerState)453 void Scheduler::timerChangeRefreshRate(IdleTimerState idleTimerState) {
454     RefreshRateType newRefreshRateType;
455     {
456         std::lock_guard<std::mutex> lock(mFeatureStateLock);
457         if (mCurrentIdleTimerState == idleTimerState) {
458             return;
459         }
460         mCurrentIdleTimerState = idleTimerState;
461         newRefreshRateType = calculateRefreshRateType();
462         if (mRefreshRateType == newRefreshRateType) {
463             return;
464         }
465         mRefreshRateType = newRefreshRateType;
466     }
467     changeRefreshRate(newRefreshRateType, ConfigEvent::None);
468 }
469 
touchChangeRefreshRate(TouchState touchState)470 void Scheduler::touchChangeRefreshRate(TouchState touchState) {
471     ConfigEvent event = ConfigEvent::None;
472     RefreshRateType newRefreshRateType;
473     {
474         std::lock_guard<std::mutex> lock(mFeatureStateLock);
475         if (mCurrentTouchState == touchState) {
476             return;
477         }
478         mCurrentTouchState = touchState;
479         newRefreshRateType = calculateRefreshRateType();
480         if (mRefreshRateType == newRefreshRateType) {
481             return;
482         }
483         mRefreshRateType = newRefreshRateType;
484         // Send an event in case that content detection is on as touch has a higher priority
485         if (mCurrentContentFeatureState == ContentFeatureState::CONTENT_DETECTION_ON) {
486             event = ConfigEvent::Changed;
487         }
488     }
489     changeRefreshRate(newRefreshRateType, event);
490 }
491 
calculateRefreshRateType()492 Scheduler::RefreshRateType Scheduler::calculateRefreshRateType() {
493     // HDR content is not supported on PERFORMANCE mode
494     if (mForceHDRContentToDefaultRefreshRate && mIsHDRContent) {
495         return RefreshRateType::DEFAULT;
496     }
497 
498     // As long as touch is active we want to be in performance mode
499     if (mCurrentTouchState == TouchState::ACTIVE) {
500         return RefreshRateType::PERFORMANCE;
501     }
502 
503     // If timer has expired as it means there is no new content on the screen
504     if (mCurrentIdleTimerState == IdleTimerState::EXPIRED) {
505         return RefreshRateType::DEFAULT;
506     }
507 
508     // If content detection is off we choose performance as we don't know the content fps
509     if (mCurrentContentFeatureState == ContentFeatureState::CONTENT_DETECTION_OFF) {
510         return RefreshRateType::PERFORMANCE;
511     }
512 
513     // Content detection is on, find the appropriate refresh rate
514     // Start with the smallest refresh rate which is within a margin of the content
515     RefreshRateType currRefreshRateType = RefreshRateType::PERFORMANCE;
516     constexpr float MARGIN = 0.05f;
517     auto iter = mRefreshRateConfigs.getRefreshRates().cbegin();
518     while (iter != mRefreshRateConfigs.getRefreshRates().cend()) {
519         if (iter->second->fps >= mContentRefreshRate * (1 - MARGIN)) {
520             currRefreshRateType = iter->first;
521             break;
522         }
523         ++iter;
524     }
525 
526     // Some content aligns better on higher refresh rate. For example for 45fps we should choose
527     // 90Hz config. However we should still prefer a lower refresh rate if the content doesn't
528     // align well with both
529     float ratio = mRefreshRateConfigs.getRefreshRate(currRefreshRateType)->fps /
530             float(mContentRefreshRate);
531     if (std::abs(std::round(ratio) - ratio) > MARGIN) {
532         while (iter != mRefreshRateConfigs.getRefreshRates().cend()) {
533             ratio = iter->second->fps / float(mContentRefreshRate);
534 
535             if (std::abs(std::round(ratio) - ratio) <= MARGIN) {
536                 currRefreshRateType = iter->first;
537                 break;
538             }
539             ++iter;
540         }
541     }
542 
543     return currRefreshRateType;
544 }
545 
changeRefreshRate(RefreshRateType refreshRateType,ConfigEvent configEvent)546 void Scheduler::changeRefreshRate(RefreshRateType refreshRateType, ConfigEvent configEvent) {
547     std::lock_guard<std::mutex> lock(mCallbackLock);
548     if (mChangeRefreshRateCallback) {
549         mChangeRefreshRateCallback(refreshRateType, configEvent);
550     }
551 }
552 
553 } // namespace android
554