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 mSetDisplayPowerTimerMs = set_display_power_timer_ms(0);
80
81 char value[PROPERTY_VALUE_MAX];
82 property_get("debug.sf.set_idle_timer_ms", value, "0");
83 int int_value = atoi(value);
84 if (int_value) {
85 mSetIdleTimerMs = atoi(value);
86 }
87
88 if (mSetIdleTimerMs > 0) {
89 if (mSupportKernelTimer) {
90 mIdleTimer =
91 std::make_unique<scheduler::IdleTimer>(std::chrono::milliseconds(
92 mSetIdleTimerMs),
93 [this] { resetKernelTimerCallback(); },
94 [this] {
95 expiredKernelTimerCallback();
96 });
97 } else {
98 mIdleTimer = std::make_unique<scheduler::IdleTimer>(std::chrono::milliseconds(
99 mSetIdleTimerMs),
100 [this] { resetTimerCallback(); },
101 [this] { expiredTimerCallback(); });
102 }
103 mIdleTimer->start();
104 }
105
106 if (mSetTouchTimerMs > 0) {
107 // Touch events are coming to SF every 100ms, so the timer needs to be higher than that
108 mTouchTimer =
109 std::make_unique<scheduler::IdleTimer>(std::chrono::milliseconds(mSetTouchTimerMs),
110 [this] { resetTouchTimerCallback(); },
111 [this] { expiredTouchTimerCallback(); });
112 mTouchTimer->start();
113 }
114
115 if (mSetDisplayPowerTimerMs > 0) {
116 mDisplayPowerTimer =
117 std::make_unique<scheduler::IdleTimer>(std::chrono::milliseconds(
118 mSetDisplayPowerTimerMs),
119 [this] { resetDisplayPowerTimerCallback(); },
120 [this] {
121 expiredDisplayPowerTimerCallback();
122 });
123 mDisplayPowerTimer->start();
124 }
125 }
126
~Scheduler()127 Scheduler::~Scheduler() {
128 // Ensure the IdleTimer thread is joined before we start destroying state.
129 mDisplayPowerTimer.reset();
130 mTouchTimer.reset();
131 mIdleTimer.reset();
132 }
133
createConnection(const char * connectionName,nsecs_t phaseOffsetNs,nsecs_t offsetThresholdForNextVsync,impl::EventThread::InterceptVSyncsCallback interceptCallback)134 sp<Scheduler::ConnectionHandle> Scheduler::createConnection(
135 const char* connectionName, nsecs_t phaseOffsetNs, nsecs_t offsetThresholdForNextVsync,
136 impl::EventThread::InterceptVSyncsCallback interceptCallback) {
137 const int64_t id = sNextId++;
138 ALOGV("Creating a connection handle with ID: %" PRId64 "\n", id);
139
140 std::unique_ptr<EventThread> eventThread =
141 makeEventThread(connectionName, mPrimaryDispSync.get(), phaseOffsetNs,
142 offsetThresholdForNextVsync, std::move(interceptCallback));
143
144 auto eventThreadConnection =
145 createConnectionInternal(eventThread.get(), ISurfaceComposer::eConfigChangedSuppress);
146 mConnections.emplace(id,
147 std::make_unique<Connection>(new ConnectionHandle(id),
148 eventThreadConnection,
149 std::move(eventThread)));
150 return mConnections[id]->handle;
151 }
152
makeEventThread(const char * connectionName,DispSync * dispSync,nsecs_t phaseOffsetNs,nsecs_t offsetThresholdForNextVsync,impl::EventThread::InterceptVSyncsCallback interceptCallback)153 std::unique_ptr<EventThread> Scheduler::makeEventThread(
154 const char* connectionName, DispSync* dispSync, nsecs_t phaseOffsetNs,
155 nsecs_t offsetThresholdForNextVsync,
156 impl::EventThread::InterceptVSyncsCallback interceptCallback) {
157 std::unique_ptr<VSyncSource> eventThreadSource =
158 std::make_unique<DispSyncSource>(dispSync, phaseOffsetNs, offsetThresholdForNextVsync,
159 true, connectionName);
160 return std::make_unique<impl::EventThread>(std::move(eventThreadSource),
161 std::move(interceptCallback), connectionName);
162 }
163
createConnectionInternal(EventThread * eventThread,ISurfaceComposer::ConfigChanged configChanged)164 sp<EventThreadConnection> Scheduler::createConnectionInternal(
165 EventThread* eventThread, ISurfaceComposer::ConfigChanged configChanged) {
166 return eventThread->createEventConnection([&] { resync(); }, configChanged);
167 }
168
createDisplayEventConnection(const sp<Scheduler::ConnectionHandle> & handle,ISurfaceComposer::ConfigChanged configChanged)169 sp<IDisplayEventConnection> Scheduler::createDisplayEventConnection(
170 const sp<Scheduler::ConnectionHandle>& handle,
171 ISurfaceComposer::ConfigChanged configChanged) {
172 RETURN_VALUE_IF_INVALID(nullptr);
173 return createConnectionInternal(mConnections[handle->id]->thread.get(), configChanged);
174 }
175
getEventThread(const sp<Scheduler::ConnectionHandle> & handle)176 EventThread* Scheduler::getEventThread(const sp<Scheduler::ConnectionHandle>& handle) {
177 RETURN_VALUE_IF_INVALID(nullptr);
178 return mConnections[handle->id]->thread.get();
179 }
180
getEventConnection(const sp<ConnectionHandle> & handle)181 sp<EventThreadConnection> Scheduler::getEventConnection(const sp<ConnectionHandle>& handle) {
182 RETURN_VALUE_IF_INVALID(nullptr);
183 return mConnections[handle->id]->eventConnection;
184 }
185
hotplugReceived(const sp<Scheduler::ConnectionHandle> & handle,PhysicalDisplayId displayId,bool connected)186 void Scheduler::hotplugReceived(const sp<Scheduler::ConnectionHandle>& handle,
187 PhysicalDisplayId displayId, bool connected) {
188 RETURN_IF_INVALID();
189 mConnections[handle->id]->thread->onHotplugReceived(displayId, connected);
190 }
191
onScreenAcquired(const sp<Scheduler::ConnectionHandle> & handle)192 void Scheduler::onScreenAcquired(const sp<Scheduler::ConnectionHandle>& handle) {
193 RETURN_IF_INVALID();
194 mConnections[handle->id]->thread->onScreenAcquired();
195 }
196
onScreenReleased(const sp<Scheduler::ConnectionHandle> & handle)197 void Scheduler::onScreenReleased(const sp<Scheduler::ConnectionHandle>& handle) {
198 RETURN_IF_INVALID();
199 mConnections[handle->id]->thread->onScreenReleased();
200 }
201
onConfigChanged(const sp<ConnectionHandle> & handle,PhysicalDisplayId displayId,int32_t configId)202 void Scheduler::onConfigChanged(const sp<ConnectionHandle>& handle, PhysicalDisplayId displayId,
203 int32_t configId) {
204 RETURN_IF_INVALID();
205 mConnections[handle->id]->thread->onConfigChanged(displayId, configId);
206 }
207
dump(const sp<Scheduler::ConnectionHandle> & handle,std::string & result) const208 void Scheduler::dump(const sp<Scheduler::ConnectionHandle>& handle, std::string& result) const {
209 RETURN_IF_INVALID();
210 mConnections.at(handle->id)->thread->dump(result);
211 }
212
setPhaseOffset(const sp<Scheduler::ConnectionHandle> & handle,nsecs_t phaseOffset)213 void Scheduler::setPhaseOffset(const sp<Scheduler::ConnectionHandle>& handle, nsecs_t phaseOffset) {
214 RETURN_IF_INVALID();
215 mConnections[handle->id]->thread->setPhaseOffset(phaseOffset);
216 }
217
getDisplayStatInfo(DisplayStatInfo * stats)218 void Scheduler::getDisplayStatInfo(DisplayStatInfo* stats) {
219 stats->vsyncTime = mPrimaryDispSync->computeNextRefresh(0);
220 stats->vsyncPeriod = mPrimaryDispSync->getPeriod();
221 }
222
enableHardwareVsync()223 void Scheduler::enableHardwareVsync() {
224 std::lock_guard<std::mutex> lock(mHWVsyncLock);
225 if (!mPrimaryHWVsyncEnabled && mHWVsyncAvailable) {
226 mPrimaryDispSync->beginResync();
227 mEventControlThread->setVsyncEnabled(true);
228 mPrimaryHWVsyncEnabled = true;
229 }
230 }
231
disableHardwareVsync(bool makeUnavailable)232 void Scheduler::disableHardwareVsync(bool makeUnavailable) {
233 std::lock_guard<std::mutex> lock(mHWVsyncLock);
234 if (mPrimaryHWVsyncEnabled) {
235 mEventControlThread->setVsyncEnabled(false);
236 mPrimaryDispSync->endResync();
237 mPrimaryHWVsyncEnabled = false;
238 }
239 if (makeUnavailable) {
240 mHWVsyncAvailable = false;
241 }
242 }
243
resyncToHardwareVsync(bool makeAvailable,nsecs_t period)244 void Scheduler::resyncToHardwareVsync(bool makeAvailable, nsecs_t period) {
245 {
246 std::lock_guard<std::mutex> lock(mHWVsyncLock);
247 if (makeAvailable) {
248 mHWVsyncAvailable = makeAvailable;
249 } else if (!mHWVsyncAvailable) {
250 // Hardware vsync is not currently available, so abort the resync
251 // attempt for now
252 return;
253 }
254 }
255
256 if (period <= 0) {
257 return;
258 }
259
260 setVsyncPeriod(period);
261 }
262
resync()263 void Scheduler::resync() {
264 static constexpr nsecs_t kIgnoreDelay = ms2ns(750);
265
266 const nsecs_t now = systemTime();
267 const nsecs_t last = mLastResyncTime.exchange(now);
268
269 if (now - last > kIgnoreDelay) {
270 resyncToHardwareVsync(false,
271 mRefreshRateConfigs.getCurrentRefreshRate().second.vsyncPeriod);
272 }
273 }
274
setRefreshSkipCount(int count)275 void Scheduler::setRefreshSkipCount(int count) {
276 mPrimaryDispSync->setRefreshSkipCount(count);
277 }
278
setVsyncPeriod(const nsecs_t period)279 void Scheduler::setVsyncPeriod(const nsecs_t period) {
280 std::lock_guard<std::mutex> lock(mHWVsyncLock);
281 mPrimaryDispSync->setPeriod(period);
282
283 if (!mPrimaryHWVsyncEnabled) {
284 mPrimaryDispSync->beginResync();
285 mEventControlThread->setVsyncEnabled(true);
286 mPrimaryHWVsyncEnabled = true;
287 }
288 }
289
addResyncSample(const nsecs_t timestamp,bool * periodFlushed)290 void Scheduler::addResyncSample(const nsecs_t timestamp, bool* periodFlushed) {
291 bool needsHwVsync = false;
292 *periodFlushed = false;
293 { // Scope for the lock
294 std::lock_guard<std::mutex> lock(mHWVsyncLock);
295 if (mPrimaryHWVsyncEnabled) {
296 needsHwVsync = mPrimaryDispSync->addResyncSample(timestamp, periodFlushed);
297 }
298 }
299
300 if (needsHwVsync) {
301 enableHardwareVsync();
302 } else {
303 disableHardwareVsync(false);
304 }
305 }
306
addPresentFence(const std::shared_ptr<FenceTime> & fenceTime)307 void Scheduler::addPresentFence(const std::shared_ptr<FenceTime>& fenceTime) {
308 if (mPrimaryDispSync->addPresentFence(fenceTime)) {
309 enableHardwareVsync();
310 } else {
311 disableHardwareVsync(false);
312 }
313 }
314
setIgnorePresentFences(bool ignore)315 void Scheduler::setIgnorePresentFences(bool ignore) {
316 mPrimaryDispSync->setIgnorePresentFences(ignore);
317 }
318
getDispSyncExpectedPresentTime()319 nsecs_t Scheduler::getDispSyncExpectedPresentTime() {
320 return mPrimaryDispSync->expectedPresentTime();
321 }
322
dumpPrimaryDispSync(std::string & result) const323 void Scheduler::dumpPrimaryDispSync(std::string& result) const {
324 mPrimaryDispSync->dump(result);
325 }
326
registerLayer(std::string const & name,int windowType)327 std::unique_ptr<scheduler::LayerHistory::LayerHandle> Scheduler::registerLayer(
328 std::string const& name, int windowType) {
329 uint32_t defaultFps, performanceFps;
330 if (mRefreshRateConfigs.refreshRateSwitchingSupported()) {
331 defaultFps = mRefreshRateConfigs.getRefreshRateFromType(RefreshRateType::DEFAULT).fps;
332 performanceFps =
333 mRefreshRateConfigs
334 .getRefreshRateFromType((windowType == InputWindowInfo::TYPE_WALLPAPER)
335 ? RefreshRateType::DEFAULT
336 : RefreshRateType::PERFORMANCE)
337 .fps;
338 } else {
339 defaultFps = mRefreshRateConfigs.getCurrentRefreshRate().second.fps;
340 performanceFps = defaultFps;
341 }
342 return mLayerHistory.createLayer(name, defaultFps, performanceFps);
343 }
344
addLayerPresentTimeAndHDR(const std::unique_ptr<scheduler::LayerHistory::LayerHandle> & layerHandle,nsecs_t presentTime,bool isHDR)345 void Scheduler::addLayerPresentTimeAndHDR(
346 const std::unique_ptr<scheduler::LayerHistory::LayerHandle>& layerHandle,
347 nsecs_t presentTime, bool isHDR) {
348 mLayerHistory.insert(layerHandle, presentTime, isHDR);
349 }
350
setLayerVisibility(const std::unique_ptr<scheduler::LayerHistory::LayerHandle> & layerHandle,bool visible)351 void Scheduler::setLayerVisibility(
352 const std::unique_ptr<scheduler::LayerHistory::LayerHandle>& layerHandle, bool visible) {
353 mLayerHistory.setVisibility(layerHandle, visible);
354 }
355
withPrimaryDispSync(std::function<void (DispSync &)> const & fn)356 void Scheduler::withPrimaryDispSync(std::function<void(DispSync&)> const& fn) {
357 fn(*mPrimaryDispSync);
358 }
359
updateFpsBasedOnContent()360 void Scheduler::updateFpsBasedOnContent() {
361 auto [refreshRate, isHDR] = mLayerHistory.getDesiredRefreshRateAndHDR();
362 const uint32_t refreshRateRound = std::round(refreshRate);
363 RefreshRateType newRefreshRateType;
364 {
365 std::lock_guard<std::mutex> lock(mFeatureStateLock);
366 if (mContentRefreshRate == refreshRateRound && mIsHDRContent == isHDR) {
367 return;
368 }
369 mContentRefreshRate = refreshRateRound;
370 ATRACE_INT("ContentFPS", mContentRefreshRate);
371
372 mIsHDRContent = isHDR;
373 ATRACE_INT("ContentHDR", mIsHDRContent);
374
375 mCurrentContentFeatureState = refreshRateRound > 0
376 ? ContentFeatureState::CONTENT_DETECTION_ON
377 : ContentFeatureState::CONTENT_DETECTION_OFF;
378 newRefreshRateType = calculateRefreshRateType();
379 if (mRefreshRateType == newRefreshRateType) {
380 return;
381 }
382 mRefreshRateType = newRefreshRateType;
383 }
384 changeRefreshRate(newRefreshRateType, ConfigEvent::Changed);
385 }
386
setChangeRefreshRateCallback(const ChangeRefreshRateCallback && changeRefreshRateCallback)387 void Scheduler::setChangeRefreshRateCallback(
388 const ChangeRefreshRateCallback&& changeRefreshRateCallback) {
389 std::lock_guard<std::mutex> lock(mCallbackLock);
390 mChangeRefreshRateCallback = changeRefreshRateCallback;
391 }
392
updateFrameSkipping(const int64_t skipCount)393 void Scheduler::updateFrameSkipping(const int64_t skipCount) {
394 ATRACE_INT("FrameSkipCount", skipCount);
395 if (mSkipCount != skipCount) {
396 // Only update DispSync if it hasn't been updated yet.
397 mPrimaryDispSync->setRefreshSkipCount(skipCount);
398 mSkipCount = skipCount;
399 }
400 }
401
resetIdleTimer()402 void Scheduler::resetIdleTimer() {
403 if (mIdleTimer) {
404 mIdleTimer->reset();
405 }
406 }
407
notifyTouchEvent()408 void Scheduler::notifyTouchEvent() {
409 if (mTouchTimer) {
410 mTouchTimer->reset();
411 }
412
413 if (mSupportKernelTimer) {
414 resetIdleTimer();
415 }
416
417 // Touch event will boost the refresh rate to performance.
418 // Clear Layer History to get fresh FPS detection
419 mLayerHistory.clearHistory();
420 }
421
setDisplayPowerState(bool normal)422 void Scheduler::setDisplayPowerState(bool normal) {
423 {
424 std::lock_guard<std::mutex> lock(mFeatureStateLock);
425 mIsDisplayPowerStateNormal = normal;
426 }
427
428 if (mDisplayPowerTimer) {
429 mDisplayPowerTimer->reset();
430 }
431
432 // Display Power event will boost the refresh rate to performance.
433 // Clear Layer History to get fresh FPS detection
434 mLayerHistory.clearHistory();
435 }
436
resetTimerCallback()437 void Scheduler::resetTimerCallback() {
438 handleTimerStateChanged(&mCurrentIdleTimerState, IdleTimerState::RESET, false);
439 ATRACE_INT("ExpiredIdleTimer", 0);
440 }
441
resetKernelTimerCallback()442 void Scheduler::resetKernelTimerCallback() {
443 ATRACE_INT("ExpiredKernelIdleTimer", 0);
444 const auto refreshRate = mRefreshRateConfigs.getCurrentRefreshRate();
445 if (refreshRate.first == RefreshRateType::PERFORMANCE) {
446 // If we're not in performance mode then the kernel timer shouldn't do
447 // anything, as the refresh rate during DPU power collapse will be the
448 // same.
449 resyncToHardwareVsync(true, refreshRate.second.vsyncPeriod);
450 }
451 }
452
expiredTimerCallback()453 void Scheduler::expiredTimerCallback() {
454 handleTimerStateChanged(&mCurrentIdleTimerState, IdleTimerState::EXPIRED, false);
455 ATRACE_INT("ExpiredIdleTimer", 1);
456 }
457
resetTouchTimerCallback()458 void Scheduler::resetTouchTimerCallback() {
459 handleTimerStateChanged(&mCurrentTouchState, TouchState::ACTIVE, true);
460 ATRACE_INT("TouchState", 1);
461 }
462
expiredTouchTimerCallback()463 void Scheduler::expiredTouchTimerCallback() {
464 handleTimerStateChanged(&mCurrentTouchState, TouchState::INACTIVE, true);
465 ATRACE_INT("TouchState", 0);
466 }
467
resetDisplayPowerTimerCallback()468 void Scheduler::resetDisplayPowerTimerCallback() {
469 handleTimerStateChanged(&mDisplayPowerTimerState, DisplayPowerTimerState::RESET, true);
470 ATRACE_INT("ExpiredDisplayPowerTimer", 0);
471 }
472
expiredDisplayPowerTimerCallback()473 void Scheduler::expiredDisplayPowerTimerCallback() {
474 handleTimerStateChanged(&mDisplayPowerTimerState, DisplayPowerTimerState::EXPIRED, true);
475 ATRACE_INT("ExpiredDisplayPowerTimer", 1);
476 }
477
expiredKernelTimerCallback()478 void Scheduler::expiredKernelTimerCallback() {
479 ATRACE_INT("ExpiredKernelIdleTimer", 1);
480 const auto refreshRate = mRefreshRateConfigs.getCurrentRefreshRate();
481 if (refreshRate.first != RefreshRateType::PERFORMANCE) {
482 // Disable HW Vsync if the timer expired, as we don't need it
483 // enabled if we're not pushing frames, and if we're in PERFORMANCE
484 // mode then we'll need to re-update the DispSync model anyways.
485 disableHardwareVsync(false);
486 }
487 }
488
doDump()489 std::string Scheduler::doDump() {
490 std::ostringstream stream;
491 stream << "+ Idle timer interval: " << mSetIdleTimerMs << " ms" << std::endl;
492 stream << "+ Touch timer interval: " << mSetTouchTimerMs << " ms" << std::endl;
493 return stream.str();
494 }
495
496 template <class T>
handleTimerStateChanged(T * currentState,T newState,bool eventOnContentDetection)497 void Scheduler::handleTimerStateChanged(T* currentState, T newState, bool eventOnContentDetection) {
498 ConfigEvent event = ConfigEvent::None;
499 RefreshRateType newRefreshRateType;
500 {
501 std::lock_guard<std::mutex> lock(mFeatureStateLock);
502 if (*currentState == newState) {
503 return;
504 }
505 *currentState = newState;
506 newRefreshRateType = calculateRefreshRateType();
507 if (mRefreshRateType == newRefreshRateType) {
508 return;
509 }
510 mRefreshRateType = newRefreshRateType;
511 if (eventOnContentDetection &&
512 mCurrentContentFeatureState == ContentFeatureState::CONTENT_DETECTION_ON) {
513 event = ConfigEvent::Changed;
514 }
515 }
516 changeRefreshRate(newRefreshRateType, event);
517 }
518
calculateRefreshRateType()519 Scheduler::RefreshRateType Scheduler::calculateRefreshRateType() {
520 if (!mRefreshRateConfigs.refreshRateSwitchingSupported()) {
521 return RefreshRateType::DEFAULT;
522 }
523
524 // HDR content is not supported on PERFORMANCE mode
525 if (mForceHDRContentToDefaultRefreshRate && mIsHDRContent) {
526 return RefreshRateType::DEFAULT;
527 }
528
529 // If Display Power is not in normal operation we want to be in performance mode.
530 // When coming back to normal mode, a grace period is given with DisplayPowerTimer
531 if (!mIsDisplayPowerStateNormal || mDisplayPowerTimerState == DisplayPowerTimerState::RESET) {
532 return RefreshRateType::PERFORMANCE;
533 }
534
535 // As long as touch is active we want to be in performance mode
536 if (mCurrentTouchState == TouchState::ACTIVE) {
537 return RefreshRateType::PERFORMANCE;
538 }
539
540 // If timer has expired as it means there is no new content on the screen
541 if (mCurrentIdleTimerState == IdleTimerState::EXPIRED) {
542 return RefreshRateType::DEFAULT;
543 }
544
545 // If content detection is off we choose performance as we don't know the content fps
546 if (mCurrentContentFeatureState == ContentFeatureState::CONTENT_DETECTION_OFF) {
547 return RefreshRateType::PERFORMANCE;
548 }
549
550 // Content detection is on, find the appropriate refresh rate with minimal error
551 auto begin = mRefreshRateConfigs.getRefreshRateMap().cbegin();
552
553 auto iter = min_element(begin, mRefreshRateConfigs.getRefreshRateMap().cend(),
554 [rate = mContentRefreshRate](const auto& l, const auto& r) -> bool {
555 return std::abs(l.second.fps - static_cast<float>(rate)) <
556 std::abs(r.second.fps - static_cast<float>(rate));
557 });
558 RefreshRateType currRefreshRateType = iter->first;
559
560 // Some content aligns better on higher refresh rate. For example for 45fps we should choose
561 // 90Hz config. However we should still prefer a lower refresh rate if the content doesn't
562 // align well with both
563 constexpr float MARGIN = 0.05f;
564 float ratio = mRefreshRateConfigs.getRefreshRateFromType(currRefreshRateType).fps /
565 float(mContentRefreshRate);
566 if (std::abs(std::round(ratio) - ratio) > MARGIN) {
567 while (iter != mRefreshRateConfigs.getRefreshRateMap().cend()) {
568 ratio = iter->second.fps / float(mContentRefreshRate);
569
570 if (std::abs(std::round(ratio) - ratio) <= MARGIN) {
571 currRefreshRateType = iter->first;
572 break;
573 }
574 ++iter;
575 }
576 }
577
578 return currRefreshRateType;
579 }
580
changeRefreshRate(RefreshRateType refreshRateType,ConfigEvent configEvent)581 void Scheduler::changeRefreshRate(RefreshRateType refreshRateType, ConfigEvent configEvent) {
582 std::lock_guard<std::mutex> lock(mCallbackLock);
583 if (mChangeRefreshRateCallback) {
584 mChangeRefreshRateCallback(refreshRateType, configEvent);
585 }
586 }
587
588 } // namespace android
589