SF: add render rate to the refresh rate overlay

Test: Enable refresh rate overlay
Bug: 257072060
Change-Id: I7c81bdff7ed10beb7487b930448f8c5dc7770073
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 269a5ea..46b857b 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -203,7 +203,7 @@
     mRefreshRateSelector->setActiveMode(modeId, renderFps);
 
     if (mRefreshRateOverlay) {
-        mRefreshRateOverlay->changeRefreshRate(displayFps);
+        mRefreshRateOverlay->changeRefreshRate(displayFps, renderFps);
     }
 }
 
@@ -410,26 +410,35 @@
                            capabilities.getDesiredMinLuminance());
 }
 
-void DisplayDevice::enableRefreshRateOverlay(bool enable, bool showSpinnner) {
+void DisplayDevice::enableRefreshRateOverlay(bool enable, bool showSpinner, bool showRenderRate) {
     if (!enable) {
         mRefreshRateOverlay.reset();
         return;
     }
 
+    ftl::Flags<RefreshRateOverlay::Features> features;
+    if (showSpinner) {
+        features |= RefreshRateOverlay::Features::Spinner;
+    }
+
+    if (showRenderRate) {
+        features |= RefreshRateOverlay::Features::RenderRate;
+    }
+
     const auto fpsRange = mRefreshRateSelector->getSupportedRefreshRateRange();
-    mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(fpsRange, showSpinnner);
+    mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(fpsRange, features);
     mRefreshRateOverlay->setLayerStack(getLayerStack());
     mRefreshRateOverlay->setViewport(getSize());
-    mRefreshRateOverlay->changeRefreshRate(getActiveMode().modePtr->getFps());
+    mRefreshRateOverlay->changeRefreshRate(getActiveMode().modePtr->getFps(), getActiveMode().fps);
 }
 
 bool DisplayDevice::onKernelTimerChanged(std::optional<DisplayModeId> desiredModeId,
                                          bool timerExpired) {
     if (mRefreshRateSelector && mRefreshRateOverlay) {
-        const auto newRefreshRate =
+        const auto newMode =
                 mRefreshRateSelector->onKernelTimerChanged(desiredModeId, timerExpired);
-        if (newRefreshRate) {
-            mRefreshRateOverlay->changeRefreshRate(*newRefreshRate);
+        if (newMode) {
+            mRefreshRateOverlay->changeRefreshRate(newMode->modePtr->getFps(), newMode->fps);
             return true;
         }
     }
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 852a8a2..8f9b2a1 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -236,7 +236,8 @@
     }
 
     // Enables an overlay to be displayed with the current refresh rate
-    void enableRefreshRateOverlay(bool enable, bool showSpinner) REQUIRES(kMainThreadContext);
+    void enableRefreshRateOverlay(bool enable, bool showSpinner, bool showRenderRate)
+            REQUIRES(kMainThreadContext);
     bool isRefreshRateOverlayEnabled() const { return mRefreshRateOverlay != nullptr; }
     bool onKernelTimerChanged(std::optional<DisplayModeId>, bool timerExpired);
     void animateRefreshRateOverlay();
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index 9b19afb..7aa7e17 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -42,8 +42,8 @@
 constexpr int kDigitHeight = 100;
 constexpr int kDigitSpace = 16;
 
-// Layout is digit, space, digit, space, digit, space, spinner.
-constexpr int kBufferWidth = 4 * kDigitWidth + 3 * kDigitSpace;
+constexpr int kMaxDigits = /*displayFps*/ 3 + /*renderFps*/ 3 + /*spinner*/ 1;
+constexpr int kBufferWidth = kMaxDigits * kDigitWidth + (kMaxDigits - 1) * kDigitSpace;
 constexpr int kBufferHeight = kDigitHeight;
 
 SurfaceComposerClient::Transaction createTransaction(const sp<SurfaceControl>& surface) {
@@ -121,16 +121,10 @@
         drawSegment(Segment::Bottom, left, color, canvas);
 }
 
-auto RefreshRateOverlay::SevenSegmentDrawer::draw(int number, SkColor color,
+auto RefreshRateOverlay::SevenSegmentDrawer::draw(int displayFps, int renderFps, SkColor color,
                                                   ui::Transform::RotationFlags rotation,
-                                                  bool showSpinner) -> Buffers {
-    if (number < 0 || number > 1000) return {};
-
-    const auto hundreds = number / 100;
-    const auto tens = (number / 10) % 10;
-    const auto ones = number % 10;
-
-    const size_t loopCount = showSpinner ? 6 : 1;
+                                                  ftl::Flags<Features> features) -> Buffers {
+    const size_t loopCount = features.test(Features::Spinner) ? 6 : 1;
 
     Buffers buffers;
     buffers.reserve(loopCount);
@@ -169,20 +163,9 @@
         canvas->setMatrix(canvasTransform);
 
         int left = 0;
-        if (hundreds != 0) {
-            drawDigit(hundreds, left, color, *canvas);
-        }
-        left += kDigitWidth + kDigitSpace;
-
-        if (tens != 0) {
-            drawDigit(tens, left, color, *canvas);
-        }
-        left += kDigitWidth + kDigitSpace;
-
-        drawDigit(ones, left, color, *canvas);
-        left += kDigitWidth + kDigitSpace;
-
-        if (showSpinner) {
+        drawNumber(displayFps, left, color, *canvas);
+        left += 3 * (kDigitWidth + kDigitSpace);
+        if (features.test(Features::Spinner)) {
             switch (i) {
                 case 0:
                     drawSegment(Segment::Upper, left, color, *canvas);
@@ -205,6 +188,13 @@
             }
         }
 
+        left += kDigitWidth + kDigitSpace;
+
+        if (features.test(Features::RenderRate)) {
+            drawNumber(renderFps, left, color, *canvas);
+        }
+        left += 3 * (kDigitWidth + kDigitSpace);
+
         void* pixels = nullptr;
         buffer->lock(GRALLOC_USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&pixels));
 
@@ -219,6 +209,23 @@
     return buffers;
 }
 
+void RefreshRateOverlay::SevenSegmentDrawer::drawNumber(int number, int left, SkColor color,
+                                                        SkCanvas& canvas) {
+    if (number < 0 || number >= 1000) return;
+
+    if (number >= 100) {
+        drawDigit(number / 100, left, color, canvas);
+    }
+    left += kDigitWidth + kDigitSpace;
+
+    if (number >= 10) {
+        drawDigit((number / 10) % 10, left, color, canvas);
+    }
+    left += kDigitWidth + kDigitSpace;
+
+    drawDigit(number % 10, left, color, canvas);
+}
+
 std::unique_ptr<SurfaceControlHolder> createSurfaceControlHolder() {
     sp<SurfaceControl> surfaceControl =
             SurfaceComposerClient::getDefault()
@@ -228,10 +235,8 @@
     return std::make_unique<SurfaceControlHolder>(std::move(surfaceControl));
 }
 
-RefreshRateOverlay::RefreshRateOverlay(FpsRange fpsRange, bool showSpinner)
-      : mFpsRange(fpsRange),
-        mShowSpinner(showSpinner),
-        mSurfaceControl(createSurfaceControlHolder()) {
+RefreshRateOverlay::RefreshRateOverlay(FpsRange fpsRange, ftl::Flags<Features> features)
+      : mFpsRange(fpsRange), mFeatures(features), mSurfaceControl(createSurfaceControlHolder()) {
     if (!mSurfaceControl) {
         ALOGE("%s: Failed to create buffer state layer", __func__);
         return;
@@ -243,10 +248,15 @@
             .apply();
 }
 
-auto RefreshRateOverlay::getOrCreateBuffers(Fps fps) -> const Buffers& {
+auto RefreshRateOverlay::getOrCreateBuffers(Fps displayFps, Fps renderFps) -> const Buffers& {
     static const Buffers kNoBuffers;
     if (!mSurfaceControl) return kNoBuffers;
 
+    // avoid caching different render rates if RenderRate is anyway not visible
+    if (!mFeatures.test(Features::RenderRate)) {
+        renderFps = 0_Hz;
+    }
+
     const auto transformHint =
             static_cast<ui::Transform::RotationFlags>(mSurfaceControl->get()->getTransformHint());
 
@@ -266,17 +276,20 @@
             .setTransform(mSurfaceControl->get(), transform)
             .apply();
 
-    BufferCache::const_iterator it = mBufferCache.find({fps.getIntValue(), transformHint});
+    BufferCache::const_iterator it =
+            mBufferCache.find({displayFps.getIntValue(), renderFps.getIntValue(), transformHint});
     if (it == mBufferCache.end()) {
         const int minFps = mFpsRange.min.getIntValue();
         const int maxFps = mFpsRange.max.getIntValue();
 
-        // Clamp to the range. The current fps may be outside of this range if the display has
-        // changed its set of supported refresh rates.
-        const int intFps = std::clamp(fps.getIntValue(), minFps, maxFps);
+        // Clamp to the range. The current displayFps may be outside of this range if the display
+        // has changed its set of supported refresh rates.
+        const int displayIntFps = std::clamp(displayFps.getIntValue(), minFps, maxFps);
+        const int renderIntFps = renderFps.getIntValue();
 
         // Ensure non-zero range to avoid division by zero.
-        const float fpsScale = static_cast<float>(intFps - minFps) / std::max(1, maxFps - minFps);
+        const float fpsScale =
+                static_cast<float>(displayIntFps - minFps) / std::max(1, maxFps - minFps);
 
         constexpr SkColor kMinFpsColor = SK_ColorRED;
         constexpr SkColor kMaxFpsColor = SK_ColorGREEN;
@@ -292,8 +305,11 @@
 
         const SkColor color = colorBase.toSkColor();
 
-        auto buffers = SevenSegmentDrawer::draw(intFps, color, transformHint, mShowSpinner);
-        it = mBufferCache.try_emplace({intFps, transformHint}, std::move(buffers)).first;
+        auto buffers = SevenSegmentDrawer::draw(displayIntFps, renderIntFps, color, transformHint,
+                                                mFeatures);
+        it = mBufferCache
+                     .try_emplace({displayIntFps, renderIntFps, transformHint}, std::move(buffers))
+                     .first;
     }
 
     return it->second;
@@ -303,7 +319,7 @@
     constexpr int32_t kMaxWidth = 1000;
     const auto width = std::min({kMaxWidth, viewport.width, viewport.height});
     const auto height = 2 * width;
-    Rect frame((3 * width) >> 4, height >> 5);
+    Rect frame((5 * width) >> 4, height >> 5);
     frame.offsetBy(width >> 5, height >> 4);
 
     createTransaction(mSurfaceControl->get())
@@ -317,16 +333,17 @@
     createTransaction(mSurfaceControl->get()).setLayerStack(mSurfaceControl->get(), stack).apply();
 }
 
-void RefreshRateOverlay::changeRefreshRate(Fps fps) {
-    mCurrentFps = fps;
-    const auto buffer = getOrCreateBuffers(fps)[mFrame];
+void RefreshRateOverlay::changeRefreshRate(Fps displayFps, Fps renderFps) {
+    mDisplayFps = displayFps;
+    mRenderFps = renderFps;
+    const auto buffer = getOrCreateBuffers(displayFps, renderFps)[mFrame];
     createTransaction(mSurfaceControl->get()).setBuffer(mSurfaceControl->get(), buffer).apply();
 }
 
 void RefreshRateOverlay::animate() {
-    if (!mShowSpinner || !mCurrentFps) return;
+    if (!mFeatures.test(Features::Spinner) || !mDisplayFps) return;
 
-    const auto& buffers = getOrCreateBuffers(*mCurrentFps);
+    const auto& buffers = getOrCreateBuffers(*mDisplayFps, *mRenderFps);
     mFrame = (mFrame + 1) % buffers.size();
     const auto buffer = buffers[mFrame];
     createTransaction(mSurfaceControl->get()).setBuffer(mSurfaceControl->get(), buffer).apply();
diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h
index a2966e6..d6f828f 100644
--- a/services/surfaceflinger/RefreshRateOverlay.h
+++ b/services/surfaceflinger/RefreshRateOverlay.h
@@ -19,6 +19,7 @@
 #include <SkColor.h>
 #include <vector>
 
+#include <ftl/flags.h>
 #include <ftl/small_map.h>
 #include <ui/LayerStack.h>
 #include <ui/Size.h>
@@ -50,11 +51,16 @@
 
 class RefreshRateOverlay {
 public:
-    RefreshRateOverlay(FpsRange, bool showSpinner);
+    enum class Features {
+        Spinner = 1 << 0,
+        RenderRate = 1 << 1,
+    };
+
+    RefreshRateOverlay(FpsRange, ftl::Flags<Features>);
 
     void setLayerStack(ui::LayerStack);
     void setViewport(ui::Size);
-    void changeRefreshRate(Fps);
+    void changeRefreshRate(Fps, Fps);
     void animate();
 
 private:
@@ -62,32 +68,39 @@
 
     class SevenSegmentDrawer {
     public:
-        static Buffers draw(int number, SkColor, ui::Transform::RotationFlags, bool showSpinner);
+        static Buffers draw(int displayFps, int renderFps, SkColor, ui::Transform::RotationFlags,
+                            ftl::Flags<Features>);
 
     private:
         enum class Segment { Upper, UpperLeft, UpperRight, Middle, LowerLeft, LowerRight, Bottom };
 
         static void drawSegment(Segment, int left, SkColor, SkCanvas&);
         static void drawDigit(int digit, int left, SkColor, SkCanvas&);
+        static void drawNumber(int number, int left, SkColor, SkCanvas&);
     };
 
-    const Buffers& getOrCreateBuffers(Fps);
+    const Buffers& getOrCreateBuffers(Fps, Fps);
 
     struct Key {
-        int fps;
+        int displayFps;
+        int renderFps;
         ui::Transform::RotationFlags flags;
 
-        bool operator==(Key other) const { return fps == other.fps && flags == other.flags; }
+        bool operator==(Key other) const {
+            return displayFps == other.displayFps && renderFps == other.renderFps &&
+                    flags == other.flags;
+        }
     };
 
     using BufferCache = ftl::SmallMap<Key, Buffers, 9>;
     BufferCache mBufferCache;
 
-    std::optional<Fps> mCurrentFps;
+    std::optional<Fps> mDisplayFps;
+    std::optional<Fps> mRenderFps;
     size_t mFrame = 0;
 
     const FpsRange mFpsRange; // For color interpolation.
-    const bool mShowSpinner;
+    const ftl::Flags<Features> mFeatures;
 
     const std::unique_ptr<SurfaceControlHolder> mSurfaceControl;
 };
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
index 4be1ac7..59f8899 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -836,21 +836,25 @@
     return frameRateOverrides;
 }
 
-std::optional<Fps> RefreshRateSelector::onKernelTimerChanged(
+ftl::Optional<FrameRateMode> RefreshRateSelector::onKernelTimerChanged(
         std::optional<DisplayModeId> desiredActiveModeId, bool timerExpired) const {
     std::lock_guard lock(mLock);
 
-    const DisplayModePtr& current = desiredActiveModeId
-            ? mDisplayModes.get(*desiredActiveModeId)->get()
-            : getActiveModeLocked().modePtr.get();
+    const auto current = [&]() REQUIRES(mLock) -> FrameRateMode {
+        if (desiredActiveModeId) {
+            const auto& modePtr = mDisplayModes.get(*desiredActiveModeId)->get();
+            return FrameRateMode{modePtr->getFps(), ftl::as_non_null(modePtr)};
+        }
+
+        return getActiveModeLocked();
+    }();
 
     const DisplayModePtr& min = mMinRefreshRateModeIt->second;
-    if (current == min) {
+    if (current.modePtr->getId() == min->getId()) {
         return {};
     }
 
-    const auto& mode = timerExpired ? min : current;
-    return mode->getFps();
+    return timerExpired ? FrameRateMode{min->getFps(), ftl::as_non_null(min)} : current;
 }
 
 const DisplayModePtr& RefreshRateSelector::getMinRefreshRateByPolicyLocked() const {
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
index 1ed16c6..4f5842a 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
@@ -240,8 +240,9 @@
         return {mMinRefreshRateModeIt->second->getFps(), mMaxRefreshRateModeIt->second->getFps()};
     }
 
-    std::optional<Fps> onKernelTimerChanged(std::optional<DisplayModeId> desiredActiveModeId,
-                                            bool timerExpired) const EXCLUDES(mLock);
+    ftl::Optional<FrameRateMode> onKernelTimerChanged(
+            std::optional<DisplayModeId> desiredActiveModeId, bool timerExpired) const
+            EXCLUDES(mLock);
 
     void setActiveMode(DisplayModeId, Fps renderFrameRate) EXCLUDES(mLock);
 
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 111b927..77b2778 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -422,7 +422,9 @@
         android::hardware::details::setTrebleTestingOverride(true);
     }
 
-    mRefreshRateOverlaySpinner = property_get_bool("sf.debug.show_refresh_rate_overlay_spinner", 0);
+    mRefreshRateOverlaySpinner = property_get_bool("debug.sf.show_refresh_rate_overlay_spinner", 0);
+    mRefreshRateOverlayRenderRate =
+            property_get_bool("debug.sf.show_refresh_rate_overlay_render_rate", 0);
 
     if (!mIsUserBuild && base::GetBoolProperty("debug.sf.enable_transaction_tracing"s, true)) {
         mTransactionTracing.emplace();
@@ -6882,7 +6884,8 @@
     for (const auto& [id, display] : mPhysicalDisplays) {
         if (display.snapshot().connectionType() == ui::DisplayConnectionType::Internal) {
             if (const auto device = getDisplayDeviceLocked(id)) {
-                device->enableRefreshRateOverlay(enable, mRefreshRateOverlaySpinner);
+                device->enableRefreshRateOverlay(enable, mRefreshRateOverlaySpinner,
+                                                 mRefreshRateOverlayRenderRate);
             }
         }
     }
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index e3217a3..b9903a7 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -639,6 +639,8 @@
     bool mKernelIdleTimerEnabled = false;
     // Show spinner with refresh rate overlay
     bool mRefreshRateOverlaySpinner = false;
+    // Show render rate with refresh rate overlay
+    bool mRefreshRateOverlayRenderRate = false;
 
     void setDesiredActiveMode(display::DisplayModeRequest&&) REQUIRES(mStateLock);