SF: add a new behaviour for idle timer on VRR
When idle timer times out on VRR, change the refresh rate
indicator to show "- -". The render rate doesn't cange as a result
of idleness.
Bug: 333443503
Test: manual
Flag: EXEMPT bugfix
(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:eba039c2d28646c5064a9a952faaba0cff6350cb)
Merged-In: Ie4f51a2a9da1a5e229b3504881117b12f1fd1b6a
Change-Id: Ie4f51a2a9da1a5e229b3504881117b12f1fd1b6a
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 27ea4a9..05f4da2 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -467,6 +467,12 @@
return false;
}
+void DisplayDevice::onVrrIdle(bool idle) {
+ if (mRefreshRateOverlay) {
+ mRefreshRateOverlay->onVrrIdle(idle);
+ }
+}
+
void DisplayDevice::animateOverlay() {
if (mRefreshRateOverlay) {
mRefreshRateOverlay->animate();
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 3cc8cf5..1b8a3a8 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -196,6 +196,7 @@
bool isRefreshRateOverlayEnabled() const { return mRefreshRateOverlay != nullptr; }
void animateOverlay();
bool onKernelTimerChanged(std::optional<DisplayModeId>, bool timerExpired);
+ void onVrrIdle(bool idle);
// Enables an overlay to be display with the hdr/sdr ratio
void enableHdrSdrRatioOverlay(bool enable) REQUIRES(kMainThreadContext);
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index 9527a99..35f12a0 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -28,10 +28,11 @@
namespace android {
-auto RefreshRateOverlay::draw(int refreshRate, int renderFps, SkColor color,
+auto RefreshRateOverlay::draw(int refreshRate, int renderFps, bool idle, SkColor color,
ui::Transform::RotationFlags rotation, ftl::Flags<Features> features)
-> Buffers {
const size_t loopCount = features.test(Features::Spinner) ? 6 : 1;
+ const bool isSetByHwc = features.test(Features::SetByHwc);
Buffers buffers;
buffers.reserve(loopCount);
@@ -71,7 +72,11 @@
canvas->setMatrix(canvasTransform);
int left = 0;
- drawNumber(refreshRate, left, color, *canvas);
+ if (idle && !isSetByHwc) {
+ drawDash(left, *canvas);
+ } else {
+ drawNumber(refreshRate, left, color, *canvas);
+ }
left += 3 * (kDigitWidth + kDigitSpace);
if (features.test(Features::Spinner)) {
switch (i) {
@@ -104,7 +109,11 @@
left += kDigitWidth + kDigitSpace;
if (features.test(Features::RenderRate)) {
- drawNumber(renderFps, left, color, *canvas);
+ if (idle) {
+ drawDash(left, *canvas);
+ } else {
+ drawNumber(renderFps, left, color, *canvas);
+ }
}
left += 3 * (kDigitWidth + kDigitSpace);
@@ -138,6 +147,14 @@
SegmentDrawer::drawDigit(number % 10, left, color, canvas);
}
+void RefreshRateOverlay::drawDash(int left, SkCanvas& canvas) {
+ left += kDigitWidth + kDigitSpace;
+ SegmentDrawer::drawSegment(SegmentDrawer::Segment::Middle, left, SK_ColorRED, canvas);
+
+ left += kDigitWidth + kDigitSpace;
+ SegmentDrawer::drawSegment(SegmentDrawer::Segment::Middle, left, SK_ColorRED, canvas);
+}
+
std::unique_ptr<RefreshRateOverlay> RefreshRateOverlay::create(FpsRange range,
ftl::Flags<Features> features) {
std::unique_ptr<RefreshRateOverlay> overlay =
@@ -171,7 +188,8 @@
return mSurfaceControl != nullptr;
}
-auto RefreshRateOverlay::getOrCreateBuffers(Fps refreshRate, Fps renderFps) -> const Buffers& {
+auto RefreshRateOverlay::getOrCreateBuffers(Fps refreshRate, Fps renderFps, bool idle)
+ -> const Buffers& {
static const Buffers kNoBuffers;
if (!mSurfaceControl) return kNoBuffers;
@@ -197,8 +215,8 @@
createTransaction().setTransform(mSurfaceControl->get(), transform).apply();
- BufferCache::const_iterator it =
- mBufferCache.find({refreshRate.getIntValue(), renderFps.getIntValue(), transformHint});
+ BufferCache::const_iterator it = mBufferCache.find(
+ {refreshRate.getIntValue(), renderFps.getIntValue(), transformHint, idle});
if (it == mBufferCache.end()) {
const int maxFps = mFpsRange.max.getIntValue();
@@ -222,10 +240,10 @@
const SkColor color = colorBase.toSkColor();
- auto buffers = draw(refreshIntFps, renderIntFps, color, transformHint, mFeatures);
+ auto buffers = draw(refreshIntFps, renderIntFps, idle, color, transformHint, mFeatures);
it = mBufferCache
- .try_emplace({refreshIntFps, renderIntFps, transformHint}, std::move(buffers))
- .first;
+ .try_emplace({refreshIntFps, renderIntFps, transformHint, idle},
+ std::move(buffers)).first;
}
return it->second;
@@ -257,7 +275,15 @@
void RefreshRateOverlay::changeRefreshRate(Fps refreshRate, Fps renderFps) {
mRefreshRate = refreshRate;
mRenderFps = renderFps;
- const auto buffer = getOrCreateBuffers(refreshRate, renderFps)[mFrame];
+ const auto buffer = getOrCreateBuffers(refreshRate, renderFps, mIsVrrIdle)[mFrame];
+ createTransaction().setBuffer(mSurfaceControl->get(), buffer).apply();
+}
+
+void RefreshRateOverlay::onVrrIdle(bool idle) {
+ mIsVrrIdle = idle;
+ if (!mRefreshRate || !mRenderFps) return;
+
+ const auto buffer = getOrCreateBuffers(*mRefreshRate, *mRenderFps, mIsVrrIdle)[mFrame];
createTransaction().setBuffer(mSurfaceControl->get(), buffer).apply();
}
@@ -265,7 +291,7 @@
if (mFeatures.test(Features::RenderRate) && mRefreshRate &&
FlagManager::getInstance().misc1()) {
mRenderFps = renderFps;
- const auto buffer = getOrCreateBuffers(*mRefreshRate, renderFps)[mFrame];
+ const auto buffer = getOrCreateBuffers(*mRefreshRate, renderFps, mIsVrrIdle)[mFrame];
createTransaction().setBuffer(mSurfaceControl->get(), buffer).apply();
}
}
@@ -273,7 +299,7 @@
void RefreshRateOverlay::animate() {
if (!mFeatures.test(Features::Spinner) || !mRefreshRate) return;
- const auto& buffers = getOrCreateBuffers(*mRefreshRate, *mRenderFps);
+ const auto& buffers = getOrCreateBuffers(*mRefreshRate, *mRenderFps, mIsVrrIdle);
mFrame = (mFrame + 1) % buffers.size();
const auto buffer = buffers[mFrame];
createTransaction().setBuffer(mSurfaceControl->get(), buffer).apply();
diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h
index b2896f0..d8aa048 100644
--- a/services/surfaceflinger/RefreshRateOverlay.h
+++ b/services/surfaceflinger/RefreshRateOverlay.h
@@ -57,6 +57,7 @@
void changeRenderRate(Fps);
void animate();
bool isSetByHwc() const { return mFeatures.test(RefreshRateOverlay::Features::SetByHwc); }
+ void onVrrIdle(bool idle);
RefreshRateOverlay(ConstructorTag, FpsRange, ftl::Flags<Features>);
@@ -65,11 +66,12 @@
using Buffers = std::vector<sp<GraphicBuffer>>;
- static Buffers draw(int refreshRate, int renderFps, SkColor, ui::Transform::RotationFlags,
- ftl::Flags<Features>);
+ static Buffers draw(int refreshRate, int renderFps, bool idle, SkColor,
+ ui::Transform::RotationFlags, ftl::Flags<Features>);
static void drawNumber(int number, int left, SkColor, SkCanvas&);
+ static void drawDash(int left, SkCanvas&);
- const Buffers& getOrCreateBuffers(Fps, Fps);
+ const Buffers& getOrCreateBuffers(Fps, Fps, bool);
SurfaceComposerClient::Transaction createTransaction() const;
@@ -77,10 +79,11 @@
int refreshRate;
int renderFps;
ui::Transform::RotationFlags flags;
+ bool idle;
bool operator==(Key other) const {
return refreshRate == other.refreshRate && renderFps == other.renderFps &&
- flags == other.flags;
+ flags == other.flags && idle == other.idle;
}
};
@@ -89,6 +92,7 @@
std::optional<Fps> mRefreshRate;
std::optional<Fps> mRenderFps;
+ bool mIsVrrIdle = false;
size_t mFrame = 0;
const FpsRange mFpsRange; // For color interpolation.
diff --git a/services/surfaceflinger/Scheduler/ISchedulerCallback.h b/services/surfaceflinger/Scheduler/ISchedulerCallback.h
index 43cdb5e..f430526 100644
--- a/services/surfaceflinger/Scheduler/ISchedulerCallback.h
+++ b/services/surfaceflinger/Scheduler/ISchedulerCallback.h
@@ -33,6 +33,7 @@
virtual void onExpectedPresentTimePosted(TimePoint, ftl::NonNull<DisplayModePtr>,
Fps renderRate) = 0;
virtual void onCommitNotComposited(PhysicalDisplayId pacesetterDisplayId) = 0;
+ virtual void vrrDisplayIdle(bool idle) = 0;
protected:
~ISchedulerCallback() = default;
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
index 846727b..dd86e4f 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -1501,7 +1501,7 @@
return str;
};
ALOGV("%s render rates: %s, isVrrDevice? %d", rangeName, stringifyModes().c_str(),
- mIsVrrDevice);
+ mIsVrrDevice.load());
return frameRateModes;
};
@@ -1511,7 +1511,6 @@
}
bool RefreshRateSelector::isVrrDevice() const {
- std::lock_guard lock(mLock);
return mIsVrrDevice;
}
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
index 4f491d9..6f9c146 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
@@ -383,6 +383,7 @@
Callbacks platform;
Callbacks kernel;
+ Callbacks vrr;
};
void setIdleTimerCallbacks(IdleTimerCallbacks callbacks) EXCLUDES(mIdleTimerCallbacksMutex) {
@@ -501,6 +502,9 @@
std::optional<IdleTimerCallbacks::Callbacks> getIdleTimerCallbacks() const
REQUIRES(mIdleTimerCallbacksMutex) {
if (!mIdleTimerCallbacks) return {};
+
+ if (mIsVrrDevice) return mIdleTimerCallbacks->vrr;
+
return mConfig.kernelIdleTimerController.has_value() ? mIdleTimerCallbacks->kernel
: mIdleTimerCallbacks->platform;
}
@@ -536,7 +540,7 @@
std::vector<FrameRateMode> mAppRequestFrameRates GUARDED_BY(mLock);
// Caches whether the device is VRR-compatible based on the active display mode.
- bool mIsVrrDevice GUARDED_BY(mLock) = false;
+ std::atomic_bool mIsVrrDevice = false;
Policy mDisplayManagerPolicy GUARDED_BY(mLock);
std::optional<Policy> mOverridePolicy GUARDED_BY(mLock);
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 26e11e5..0ef61b9 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -942,8 +942,9 @@
{.platform = {.onReset = [this] { idleTimerCallback(TimerState::Reset); },
.onExpired = [this] { idleTimerCallback(TimerState::Expired); }},
.kernel = {.onReset = [this] { kernelIdleTimerCallback(TimerState::Reset); },
- .onExpired =
- [this] { kernelIdleTimerCallback(TimerState::Expired); }}});
+ .onExpired = [this] { kernelIdleTimerCallback(TimerState::Expired); }},
+ .vrr = {.onReset = [this] { mSchedulerCallback.vrrDisplayIdle(false); },
+ .onExpired = [this] { mSchedulerCallback.vrrDisplayIdle(true); }}});
pacesetter.selectorPtr->startIdleTimer();
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index b8dd34d..4bdff29 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -7703,6 +7703,22 @@
}));
}
+void SurfaceFlinger::vrrDisplayIdle(bool idle) {
+ // Update the overlay on the main thread to avoid race conditions with
+ // RefreshRateSelector::getActiveMode
+ static_cast<void>(mScheduler->schedule([=, this] {
+ const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked());
+ if (!display) {
+ ALOGW("%s: default display is null", __func__);
+ return;
+ }
+ if (!display->isRefreshRateOverlayEnabled()) return;
+
+ display->onVrrIdle(idle);
+ mScheduler->scheduleFrame();
+ }));
+}
+
std::pair<std::optional<KernelIdleTimerController>, std::chrono::milliseconds>
SurfaceFlinger::getKernelIdleTimerProperties(PhysicalDisplayId displayId) {
const bool isKernelIdleTimerHwcSupported = getHwComposer().getComposer()->isSupported(
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index ee541c4..3a34e46 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -703,6 +703,7 @@
Fps renderRate) override;
void onCommitNotComposited(PhysicalDisplayId pacesetterDisplayId) override
REQUIRES(kMainThreadContext);
+ void vrrDisplayIdle(bool idle) override;
// ICEPowerCallback overrides:
void notifyCpuLoadUp() override;
diff --git a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
index dec5fa5..8f21cdb 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
@@ -31,6 +31,7 @@
MOCK_METHOD(void, onExpectedPresentTimePosted, (TimePoint, ftl::NonNull<DisplayModePtr>, Fps),
(override));
MOCK_METHOD(void, onCommitNotComposited, (PhysicalDisplayId), (override));
+ MOCK_METHOD(void, vrrDisplayIdle, (bool), (override));
};
struct NoOpSchedulerCallback final : ISchedulerCallback {
@@ -41,6 +42,7 @@
void onChoreographerAttached() override {}
void onExpectedPresentTimePosted(TimePoint, ftl::NonNull<DisplayModePtr>, Fps) override {}
void onCommitNotComposited(PhysicalDisplayId) override {}
+ void vrrDisplayIdle(bool) override {}
};
} // namespace android::scheduler::mock