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