Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0aa373ac authored by Ady Abraham's avatar Ady Abraham
Browse files

SF: add render rate to the refresh rate overlay

Test: Enable refresh rate overlay
Bug: 257072060
Change-Id: I7c81bdff7ed10beb7487b930448f8c5dc7770073
parent ace3d054
Loading
Loading
Loading
Loading
+16 −7
Original line number Diff line number Diff line
@@ -203,7 +203,7 @@ void DisplayDevice::setActiveMode(DisplayModeId modeId, Fps displayFps, Fps rend
    mRefreshRateSelector->setActiveMode(modeId, renderFps);

    if (mRefreshRateOverlay) {
        mRefreshRateOverlay->changeRefreshRate(displayFps);
        mRefreshRateOverlay->changeRefreshRate(displayFps, renderFps);
    }
}

@@ -410,26 +410,35 @@ HdrCapabilities DisplayDevice::getHdrCapabilities() const {
                           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;
        }
    }
+2 −1
Original line number Diff line number Diff line
@@ -236,7 +236,8 @@ public:
    }

    // 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();
+60 −43
Original line number Diff line number Diff line
@@ -42,8 +42,8 @@ constexpr int kDigitWidth = 64;
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 @@ void RefreshRateOverlay::SevenSegmentDrawer::drawDigit(int digit, int left, SkCo
        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 @@ auto RefreshRateOverlay::SevenSegmentDrawer::draw(int number, SkColor color,
        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 @@ auto RefreshRateOverlay::SevenSegmentDrawer::draw(int number, SkColor color,
            }
        }

        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 @@ auto RefreshRateOverlay::SevenSegmentDrawer::draw(int number, SkColor color,
    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 @@ std::unique_ptr<SurfaceControlHolder> createSurfaceControlHolder() {
    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 @@ RefreshRateOverlay::RefreshRateOverlay(FpsRange fpsRange, bool showSpinner)
            .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 @@ auto RefreshRateOverlay::getOrCreateBuffers(Fps fps) -> const Buffers& {
            .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 @@ auto RefreshRateOverlay::getOrCreateBuffers(Fps fps) -> const Buffers& {

        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 @@ void RefreshRateOverlay::setViewport(ui::Size viewport) {
    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 @@ void RefreshRateOverlay::setLayerStack(ui::LayerStack stack) {
    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();
+21 −8
Original line number Diff line number Diff line
@@ -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 @@ private:

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 @@ private:

    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;
};
+11 −7
Original line number Diff line number Diff line
@@ -836,21 +836,25 @@ auto RefreshRateSelector::getFrameRateOverrides(const std::vector<LayerRequireme
    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 {
Loading