天天看點

android R Variable Refresh Rate 可變幀率 VRR前言API簡述Graphics相關代碼分析使用API以及驗證

android R 可變幀率VRR

  • 前言
  • API簡述
  • Graphics相關代碼分析
    • 1.使用者設定重新整理率流程
    • 2.SurfaceFlinger設定重新整理率
  • 使用API以及驗證

前言

本文代碼基于Android R r1版本。如果不是這個版本的代碼,可能會有部分代碼上的差異。

API簡述

Surface.setFrameRate(float frameRate, int compatibility)
//frameworks/base/core/java/android/view/Surface.java

SurfaceControl.Transaction.setFrameRate(float frameRate, int compatibility)
//frameworks/base/core/java/android/view/SurfaceControl.java

ANativeWindow_setFrameRate(float frameRate, int8_t compatibility)
//frameworks/native/libs/nativewindow/ANativeWindow.cpp

ASurfaceTransaction_setFrameRate(float frameRate, int8_t compatibility)
//frameworks/base/native/android/surface_control.cpp

           

分别有兩個參數:

參數frameRate表示要設定的幀率,設定0表示接受系統重新整理率,設定其餘的整數都可以,系統會預設适配一個正确的重新整理率。

參數compatibility有兩個值可選,分别是

FRAME_RATE_COMPATIBILITY_DEFAULT和

FRAME_RATE_COMPATIBILITY_FIXED_SOURCE。

其中FRAME_RATE_COMPATIBILITY_DEFAULT被設定之後,不能保證設定的幀率一定生效,app要保證在未生效的重新整理率下正常運作,同時這個參數隻能被用在非視訊場景下。FRAME_RATE_COMPATIBILITY_FIXED_SOURCE這個值隻能在視訊場景下被使用。

Graphics相關代碼分析

1.使用者設定重新整理率流程

無論是SDK中的接口還是NDK中的接口最終都會調用到surfaceflinger中。

status_t SurfaceFlinger::setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
                                      int8_t compatibility)

           

進入setFrameRate之後,首先是判斷參數的有效性。這個判斷就是驗證frameRate的值是否大于0,是否為浮點數。然後再判斷compatibility的值是否為兩個枚舉值。

bool ValidateFrameRate(float frameRate, int8_t compatibility, const char* inFunctionName) {
    const char* functionName = inFunctionName != nullptr ? inFunctionName : "call";
    int floatClassification = std::fpclassify(frameRate);
    if (frameRate < 0 || floatClassification == FP_INFINITE || floatClassification == FP_NAN) {
        ALOGE("%s failed - invalid frame rate %f", functionName, frameRate);
        return false;
    }

    if (compatibility != ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT &&
        compatibility != ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE) {
        ALOGE("%s failed - invalid compatibility value %d", functionName, compatibility);
        return false;
    }

    return true;
}
           

接下來就是一個lamda表達式。

static_cast<void>(schedule([=] {
        Mutex::Autolock lock(mStateLock);
        if (authenticateSurfaceTextureLocked(surface)) {
            sp<Layer> layer = (static_cast<MonitoredProducer*>(surface.get()))->getLayer();
            if (layer->setFrameRate(
                        Layer::FrameRate(frameRate,
                                         Layer::FrameRate::convertCompatibility(compatibility)))) {
                setTransactionFlags(eTraversalNeeded);
            }
        } else {
            ALOGE("Attempt to set frame rate on an unrecognized IGraphicBufferProducer");
            return BAD_VALUE;
        }
        return NO_ERROR;
    }));
           

調用的surfaceflinger.cpp中的模闆方法。

template <typename F, typename T>
inline std::future<T> SurfaceFlinger::schedule(F&& f) {
    auto [task, future] = makeTask(std::move(f));
    mEventQueue->postMessage(std::move(task));
    return std::move(future);
}
           

makeTask定義在MessageQueue.h中。

template <typename F>
class Task : public MessageHandler {
    template <typename G>
    friend auto makeTask(G&&);

    explicit Task(F&& f) : mTask(std::move(f)) {}

    void handleMessage(const Message&) override { mTask(); }

    using T = std::invoke_result_t<F>;
    std::packaged_task<T()> mTask;
};

template <typename F>
inline auto makeTask(F&& f) {
    sp<Task<F>> task = new Task<F>(std::move(f));
    return std::make_pair(task, task->mTask.get_future());
}
           

setFrameRate中的lambda表達式被封裝成MessageHander之後postMessage到MessageQueue中去。再Looper中pollOnce的時候被取出來執行。這個時候才會真正調用setFrameRate中的lambda表達式。再看這個表達式是怎麼寫的。authenticateSurfaceTextureLocked就是判斷producer是否存在。然後在調用layer的setFrameRate。

bool Layer::setFrameRate(FrameRate frameRate) {
    if (!mFlinger->useFrameRateApi) {
        return false;
    }
    if (mCurrentState.frameRate == frameRate) {
        return false;
    }

    // Activate the layer in Scheduler's LayerHistory
    mFlinger->mScheduler->recordLayerHistory(this, systemTime(),
                                             LayerHistory::LayerUpdateType::SetFrameRate);

    mCurrentState.sequence++;
    mCurrentState.frameRate = frameRate;
    mCurrentState.modified = true;

    updateTreeHasFrameRateVote();

    setTransactionFlags(eTransactionNeeded);
    return true;
}
           

首先是調用scheduler的layerhistory,實作在LayerHistoryV2中。

void LayerHistoryV2::record(Layer* layer, nsecs_t presentTime, nsecs_t now,
                            LayerUpdateType updateType) {
    std::lock_guard lock(mLock);

    const auto it = std::find_if(mLayerInfos.begin(), mLayerInfos.end(),
                                 [layer](const auto& pair) { return pair.first == layer; });
    LOG_FATAL_IF(it == mLayerInfos.end(), "%s: unknown layer %p", __FUNCTION__, layer);

    const auto& info = it->second;
    info->setLastPresentTime(presentTime, now, updateType, mConfigChangePending);

    // Activate layer if inactive.
    if (const auto end = activeLayers().end(); it >= end) {
        std::iter_swap(it, end);
        mActiveLayersEnd++;
    }
}
           

根據layer從mLayerInfos中取出對應的LayerPair,LayerPair就是一個Layer和LayerInfo的pair。然後将參數中的各種屬性設定到LayerInfoV2的setLastPresentTime中去。

void LayerInfoV2::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now,
                                     LayerUpdateType updateType, bool pendingConfigChange) {
    lastPresentTime = std::max(lastPresentTime, static_cast<nsecs_t>(0));

    mLastUpdatedTime = std::max(lastPresentTime, now);
    switch (updateType) {
        case LayerUpdateType::AnimationTX:
            mLastAnimationTime = std::max(lastPresentTime, now);
            break;
        case LayerUpdateType::SetFrameRate:
        case LayerUpdateType::Buffer:
            FrameTimeData frameTime = {.presetTime = lastPresentTime,
                                       .queueTime = mLastUpdatedTime,
                                       .pendingConfigChange = pendingConfigChange};
            mFrameTimes.push_back(frameTime);
            if (mFrameTimes.size() > HISTORY_SIZE) {
                mFrameTimes.pop_front();
            }
            break;
    }
}
           

這個函數的作用就是記錄圖層的請求時間,如果now比最後一次請求的時間更大就把now當做最後一次請求的時間。

再往下也沒有什麼特殊的代碼了,就是周遊圖層,設定flag。到這裡,設定refresh rate的代碼就結束了。但是到這裡并不表示設定refresh rate成功。refresh rate也不會是你設定多少就變成多少。

2.SurfaceFlinger設定重新整理率

在sf中的onMessageInvalidate函數中,還會根據layer history決定最終設定怎麼樣的refresh rate。

這是在scheduler中的調用。

void Scheduler::chooseRefreshRateForContent() {
    if (!mLayerHistory) return;

    ATRACE_CALL();

    scheduler::LayerHistory::Summary summary = mLayerHistory->summarize(systemTime());
    HwcConfigIndexType newConfigId;
    {
        std::lock_guard<std::mutex> lock(mFeatureStateLock);
        if (mFeatures.contentRequirements == summary) {
            return;
        }
        mFeatures.contentRequirements = summary;
        mFeatures.contentDetectionV1 =
                !summary.empty() ? ContentDetectionState::On : ContentDetectionState::Off;

        scheduler::RefreshRateConfigs::GlobalSignals consideredSignals;
        newConfigId = calculateRefreshRateConfigIndexType(&consideredSignals);
        if (mFeatures.configId == newConfigId) {
            // We don't need to change the config, but we might need to send an event
            // about a config change, since it was suppressed due to a previous idleConsidered
            if (!consideredSignals.idle) {
                dispatchCachedReportedConfig();
            }
            return;
        }
        mFeatures.configId = newConfigId;
        auto& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
        mSchedulerCallback.changeRefreshRate(newRefreshRate,
                                             consideredSignals.idle ? ConfigEvent::None
                                                                    : ConfigEvent::Changed);
    }
}
           

上面setFrameRate的時候調用的setLastPresentTime在這邊就用上了。這裡先調用了mLayerHistory的summarize。看下summarize的實作。

LayerHistoryV2::Summary LayerHistoryV2::summarize(nsecs_t now) {
    LayerHistory::Summary summary;

    std::lock_guard lock(mLock);

    partitionLayers(now);

    for (const auto& [layer, info] : activeLayers()) {
        const auto strong = layer.promote();
        if (!strong) {
            continue;
        }

        const auto frameRateSelectionPriority = strong->getFrameRateSelectionPriority();
        const auto layerFocused = Layer::isLayerFocusedBasedOnPriority(frameRateSelectionPriority);
        ALOGV("%s has priority: %d %s focused", strong->getName().c_str(),
              frameRateSelectionPriority, layerFocused ? "" : "not");

        const auto [type, refreshRate] = info->getRefreshRate(now);
        // Skip NoVote layer as those don't have any requirements
        if (type == LayerHistory::LayerVoteType::NoVote) {
            continue;
        }

        // Compute the layer's position on the screen
        const Rect bounds = Rect(strong->getBounds());
        const ui::Transform transform = strong->getTransform();
        constexpr bool roundOutwards = true;
        Rect transformed = transform.transform(bounds, roundOutwards);

        const float layerArea = transformed.getWidth() * transformed.getHeight();
        float weight = mDisplayArea ? layerArea / mDisplayArea : 0.0f;
        summary.push_back({strong->getName(), type, refreshRate, weight, layerFocused});

        if (CC_UNLIKELY(mTraceEnabled)) {
            trace(layer, *info, type, static_cast<int>(std::round(refreshRate)));
        }
    }

    return summary;
}
           

summarize中又會調用partitionLayers。首先擷取一個閥值,這個閥值範圍是從目前開始算往前推1200ms。然後while循環所有的active的layer,如果這個layer還存在并且在過去的1200ms内是active的再并且這個layer設定的rate不能為0再再并且layer的framerate type不能是NoVote再再再layer是可見的才會用這個layer設定的framerate去投票。不滿足條件的lyaer info被設定忽略或者重置掉。之後再清理掉不需要投票的layer。

void LayerHistoryV2::partitionLayers(nsecs_t now) {
    const nsecs_t threshold = getActiveLayerThreshold(now);

    // Collect expired and inactive layers after active layers.
    size_t i = 0;
    while (i < mActiveLayersEnd) {
        auto& [weak, info] = mLayerInfos[i];
        if (const auto layer = weak.promote(); layer && isLayerActive(*layer, *info, threshold)) {
            i++;
            // Set layer vote if set
            const auto frameRate = layer->getFrameRateForLayerTree();
            const auto voteType = [&]() {
                switch (frameRate.type) {
                    case Layer::FrameRateCompatibility::Default:
                        return LayerVoteType::ExplicitDefault;
                    case Layer::FrameRateCompatibility::ExactOrMultiple:
                        return LayerVoteType::ExplicitExactOrMultiple;
                    case Layer::FrameRateCompatibility::NoVote:
                        return LayerVoteType::NoVote;
                }
            }();

            if (frameRate.rate > 0 || voteType == LayerVoteType::NoVote) {
                const auto type = layer->isVisible() ? voteType : LayerVoteType::NoVote;
                info->setLayerVote(type, frameRate.rate);
            } else {
                info->resetLayerVote();
            }
            continue;
        }

        if (CC_UNLIKELY(mTraceEnabled)) {
            trace(weak, *info, LayerHistory::LayerVoteType::NoVote, 0);
        }

        info->onLayerInactive(now);
        std::swap(mLayerInfos[i], mLayerInfos[--mActiveLayersEnd]);
    }

    // Collect expired layers after inactive layers.
    size_t end = mLayerInfos.size();
    while (i < end) {
        if (mLayerInfos[i].first.promote()) {
            i++;
        } else {
            std::swap(mLayerInfos[i], mLayerInfos[--end]);
        }
    }

    mLayerInfos.erase(mLayerInfos.begin() + static_cast<long>(end), mLayerInfos.end());
}
           

接下來再回到summarize函數。首先是調用getFrameRateSelectionPriority擷取每個layer設定的refresh rate的優先級。代碼比較簡單就是從mDrawingState和parent中擷取layer設定的優先級,從判斷擷取優先級的邏輯可以看出來,被設定的優先級想要起作用一定是在layer 送顯之後。

int32_t Layer::getFrameRateSelectionPriority() const {
    // Check if layer has priority set.
    if (mDrawingState.frameRateSelectionPriority != PRIORITY_UNSET) {
        return mDrawingState.frameRateSelectionPriority;
    }
    // If not, search whether its parents have it set.
    sp<Layer> parent = getParent();
    if (parent != nullptr) {
        return parent->getFrameRateSelectionPriority();
    }

    return Layer::PRIORITY_UNSET;
}
           

既然看到了擷取layer設定的優先級,那就看一下上層是如何設定frame rate優先級的。這裡隻看native層設定優先級。調用的地方是SurfaceComposerClient中。上層通過surfacecontrol來設定layer的屬性,基本上全部都是調用SurfaceComposerClient接口來實作的,這裡設定layer的frame rate優先級也不例外。

SurfaceComposerClient::Transaction&
SurfaceComposerClient::Transaction::setFrameRateSelectionPriority(const sp<SurfaceControl>& sc,
                                                                  int32_t priority) {
    layer_state_t* s = getLayerState(sc);
    if (!s) {
        mStatus = BAD_INDEX;
        return *this;
    }

    s->what |= layer_state_t::eFrameRateSelectionPriority;
    s->frameRateSelectionPriority = priority;

    registerSurfaceControlForCallback(sc);
    return *this;
}
           

然後就是通過sf的setClientStateLocked調用到layer的setFrameRateSelectionPriority。邏輯也很簡單,和設定layer的各種屬性一樣。

bool Layer::setFrameRateSelectionPriority(int32_t priority) {
    if (mCurrentState.frameRateSelectionPriority == priority) return false;
    mCurrentState.frameRateSelectionPriority = priority;
    mCurrentState.sequence++;
    mCurrentState.modified = true;
    setTransactionFlags(eTransactionNeeded);
    return true;
}
           

優先級分為4種,0最高,-1最低。

最高的優先級就是有焦點的視窗并且要投票給某一個mode。

次一級的是有焦點但是沒有要設定某一個mode,基本上大部分的優先級都是這種。

再次一等的優先級就是沒有焦點的視窗但是又設定了mode。

static constexpr int32_t PRIORITY_UNSET = -1;
    // Windows that are in focus and voted for the preferred mode ID
    static constexpr int32_t PRIORITY_FOCUSED_WITH_MODE = 0;
    // // Windows that are in focus, but have not requested a specific mode ID.
    static constexpr int32_t PRIORITY_FOCUSED_WITHOUT_MODE = 1;
    // Windows that are not in focus, but voted for a specific mode ID.
    static constexpr int32_t PRIORITY_NOT_FOCUSED_WITH_MODE = 2;
           

getRefreshRate就是擷取每個layer info的投票類型和fps。代碼邏輯很簡答,這裡就略過,有點特殊的就是isFrequent,這是判斷圖層更新的頻率,如果有效記錄少于90幀,有效幀的時間跨度小于1秒,都算是請求不夠頻繁,會被return 掉,不會被用作啟發式計算的資料。最重要的就是calculateRefreshRateIfPossible。

std::pair<LayerHistory::LayerVoteType, float> LayerInfoV2::getRefreshRate(nsecs_t now) {
    if (mLayerVote.type != LayerHistory::LayerVoteType::Heuristic) {
        ALOGV("%s voted %d ", mName.c_str(), static_cast<int>(mLayerVote.type));
        return {mLayerVote.type, mLayerVote.fps};
    }

    if (isAnimating(now)) {
        ALOGV("%s is animating", mName.c_str());
        mLastRefreshRate.animatingOrInfrequent = true;
        return {LayerHistory::LayerVoteType::Max, 0};
    }

    if (!isFrequent(now)) {
        ALOGV("%s is infrequent", mName.c_str());
        mLastRefreshRate.animatingOrInfrequent = true;
        return {LayerHistory::LayerVoteType::Min, 0};
    }

    // If the layer was previously tagged as animating or infrequent, we clear
    // the history as it is likely the layer just changed its behavior
    // and we should not look at stale data
    if (mLastRefreshRate.animatingOrInfrequent) {
        clearHistory(now);
    }

    auto refreshRate = calculateRefreshRateIfPossible(now);
    if (refreshRate.has_value()) {
        ALOGV("%s calculated refresh rate: %.2f", mName.c_str(), refreshRate.value());
        return {LayerHistory::LayerVoteType::Heuristic, refreshRate.value()};
    }

    ALOGV("%s Max (can't resolve refresh rate)", mName.c_str());
    return {LayerHistory::LayerVoteType::Max, 0};
}
           

calculateRefreshRateIfPossible開始是先判斷是否有足夠的資料用來做啟發式計算。滿足條件才能繼續往下走。然後就是根據儲存的資料計算出幀率。

std::optional<float> LayerInfoV2::calculateRefreshRateIfPossible(nsecs_t now) {
    static constexpr float MARGIN = 1.0f; // 1Hz
    if (!hasEnoughDataForHeuristic()) {
        ALOGV("Not enough data");
        return std::nullopt;
    }

    const auto averageFrameTime = calculateAverageFrameTime();
    if (averageFrameTime.has_value()) {
        const auto refreshRate = 1e9f / *averageFrameTime;
        const bool refreshRateConsistent = mRefreshRateHistory.add(refreshRate, now);
        if (refreshRateConsistent) {
            const auto knownRefreshRate =
                    sRefreshRateConfigs->findClosestKnownFrameRate(refreshRate);

            // To avoid oscillation, use the last calculated refresh rate if it is
            // close enough
            if (std::abs(mLastRefreshRate.calculated - refreshRate) > MARGIN &&
                mLastRefreshRate.reported != knownRefreshRate) {
                mLastRefreshRate.calculated = refreshRate;
                mLastRefreshRate.reported = knownRefreshRate;
            }

            ALOGV("%s %.2fHz rounded to nearest known frame rate %.2fHz", mName.c_str(),
                  refreshRate, mLastRefreshRate.reported);
        } else {
            ALOGV("%s Not stable (%.2fHz) returning last known frame rate %.2fHz", mName.c_str(),
                  refreshRate, mLastRefreshRate.reported);
        }
    }

    return mLastRefreshRate.reported == 0 ? std::nullopt
                                          : std::make_optional(mLastRefreshRate.reported);
}
           

再繼續回去看這個函數summarize。擷取完幀率之後就算根據圖層的大小計算權重。計算方法也很簡單就是用layer的大小/display的大小,這個mDisplayArea會在主屏被添加之後被設定,大小就是螢幕大小。然後就是把圖層的名稱,權重,重新整理率等資訊儲存到一個vector中。接下來就是calculateRefreshRateConfigIndexType函數,這個函數判斷各種會影響幀率的因素并且傳回configid。在看具體代碼之前先要看這兩部分,一部分和mIdleTimer, mTouchTimer, mDisplayPowerTimer相關。

// Timer that records time between requests for next vsync.
    std::optional<scheduler::OneShotTimer> mIdleTimer;
    // Timer used to monitor touch events.
    std::optional<scheduler::OneShotTimer> mTouchTimer;
    // Timer used to monitor display power mode.
    std::optional<scheduler::OneShotTimer> mDisplayPowerTimer;
           

初始化是在Scheduler的構造函數中。分别初始化了mIdleTimer, mTouchTimer, mDisplayPowerTimer。就拿mTouchTimer看一下。

const int setIdleTimerMs = property_get_int32("debug.sf.set_idle_timer_ms", 0);

if (const auto millis = setIdleTimerMs ? setIdleTimerMs : set_idle_timer_ms(0); millis > 0) {
    const auto callback = mSupportKernelTimer ? &Scheduler::kernelIdleTimerCallback
                                              : &Scheduler::idleTimerCallback;
    mIdleTimer.emplace(
            std::chrono::milliseconds(millis),
            [this, callback] { std::invoke(callback, this, TimerState::Reset); },
            [this, callback] { std::invoke(callback, this, TimerState::Expired); });
    mIdleTimer->start();
}

if (const int64_t millis = set_touch_timer_ms(0); millis > 0) {
    // Touch events are coming to SF every 100ms, so the timer needs to be higher than that
    mTouchTimer.emplace(
            std::chrono::milliseconds(millis),
            [this] { touchTimerCallback(TimerState::Reset); },
            [this] { touchTimerCallback(TimerState::Expired); });
    mTouchTimer->start();
}

if (const int64_t millis = set_display_power_timer_ms(0); millis > 0) {
    mDisplayPowerTimer.emplace(
            std::chrono::milliseconds(millis),
            [this] { displayPowerTimerCallback(TimerState::Reset); },
            [this] { displayPowerTimerCallback(TimerState::Expired); });
    mDisplayPowerTimer->start();
}
           

OneShotTimer的start内容很簡單,就是設定狀态以及建立線程。線程中會調用loop函數。

void OneShotTimer::start() {
    {
        std::lock_guard<std::mutex> lock(mMutex);
        mState = TimerState::RESET;
    }
    mThread = std::thread(&OneShotTimer::loop, this);
}
           

loop函數就是排程的核心了,每一次reset之後通過mResetCallback回調重置狀态,WAITING的這個while等待逾時,如果逾時了就調用mTimeoutCallback回掉。

void OneShotTimer::loop() {
    while (true) {
        bool triggerReset = false;
        bool triggerTimeout = false;
        {
            std::lock_guard<std::mutex> lock(mMutex);
            if (mState == TimerState::STOPPED) {
                break;
            }

            if (mState == TimerState::IDLE) {
                mCondition.wait(mMutex);
                continue;
            }

            if (mState == TimerState::RESET) {
                triggerReset = true;
            }
        }
        if (triggerReset && mResetCallback) {
            mResetCallback();
        }

        { // lock the mutex again. someone might have called stop meanwhile
            std::lock_guard<std::mutex> lock(mMutex);
            if (mState == TimerState::STOPPED) {
                break;
            }

            auto triggerTime = std::chrono::steady_clock::now() + mInterval;
            mState = TimerState::WAITING;
            while (mState == TimerState::WAITING) {
                constexpr auto zero = std::chrono::steady_clock::duration::zero();
                auto waitTime = triggerTime - std::chrono::steady_clock::now();
                if (waitTime > zero) mCondition.wait_for(mMutex, waitTime);
                if (mState == TimerState::RESET) {
                    triggerTime = std::chrono::steady_clock::now() + mInterval;
                    mState = TimerState::WAITING;
                } else if (mState == TimerState::WAITING &&
                           (triggerTime - std::chrono::steady_clock::now()) <= zero) {
                    triggerTimeout = true;
                    mState = TimerState::IDLE;
                }
            }
        }
        if (triggerTimeout && mTimeoutCallback) {
            mTimeoutCallback();
        }
    }
}
           

最終通過回調調用到getBestRefreshRate函數。這個函數就是最終給出refreshrate的地方。之前各種判斷是否有touch,是否idle,還有權重,投票的圖層數量多少等等都會在這裡拿出來計算,最終計算出最适合目前場景應該的refresh rate。

const RefreshRate& RefreshRateConfigs::getBestRefreshRate(
        const std::vector<LayerRequirement>& layers, const GlobalSignals& globalSignals,
        GlobalSignals* outSignalsConsidered) const {
    ATRACE_CALL();
    ALOGV("getRefreshRateForContent %zu layers", layers.size());

    if (outSignalsConsidered) *outSignalsConsidered = {};
    const auto setTouchConsidered = [&] {
        if (outSignalsConsidered) {
            outSignalsConsidered->touch = true;
        }
    };

    const auto setIdleConsidered = [&] {
        if (outSignalsConsidered) {
            outSignalsConsidered->idle = true;
        }
    };

    std::lock_guard lock(mLock);

    int noVoteLayers = 0;
    int minVoteLayers = 0;
    int maxVoteLayers = 0;
    int explicitDefaultVoteLayers = 0;
    int explicitExactOrMultipleVoteLayers = 0;
    float maxExplicitWeight = 0;
    for (const auto& layer : layers) {
        if (layer.vote == LayerVoteType::NoVote) {
            noVoteLayers++;
        } else if (layer.vote == LayerVoteType::Min) {
            minVoteLayers++;
        } else if (layer.vote == LayerVoteType::Max) {
            maxVoteLayers++;
        } else if (layer.vote == LayerVoteType::ExplicitDefault) {
            explicitDefaultVoteLayers++;
            maxExplicitWeight = std::max(maxExplicitWeight, layer.weight);
        } else if (layer.vote == LayerVoteType::ExplicitExactOrMultiple) {
            explicitExactOrMultipleVoteLayers++;
            maxExplicitWeight = std::max(maxExplicitWeight, layer.weight);
        }
    }

    const bool hasExplicitVoteLayers =
            explicitDefaultVoteLayers > 0 || explicitExactOrMultipleVoteLayers > 0;

    // Consider the touch event if there are no Explicit* layers. Otherwise wait until after we've
    // selected a refresh rate to see if we should apply touch boost.
    if (globalSignals.touch && !hasExplicitVoteLayers) {
        ALOGV("TouchBoost - choose %s", getMaxRefreshRateByPolicyLocked().getName().c_str());
        setTouchConsidered();
        return getMaxRefreshRateByPolicyLocked();
    }

    // If the primary range consists of a single refresh rate then we can only
    // move out the of range if layers explicitly request a different refresh
    // rate.
    const Policy* policy = getCurrentPolicyLocked();
    const bool primaryRangeIsSingleRate = policy->primaryRange.min == policy->primaryRange.max;

    if (!globalSignals.touch && globalSignals.idle &&
        !(primaryRangeIsSingleRate && hasExplicitVoteLayers)) {
        ALOGV("Idle - choose %s", getMinRefreshRateByPolicyLocked().getName().c_str());
        setIdleConsidered();
        return getMinRefreshRateByPolicyLocked();
    }

    if (layers.empty() || noVoteLayers == layers.size()) {
        return getMaxRefreshRateByPolicyLocked();
    }

    // Only if all layers want Min we should return Min
    if (noVoteLayers + minVoteLayers == layers.size()) {
        ALOGV("all layers Min - choose %s", getMinRefreshRateByPolicyLocked().getName().c_str());
        return getMinRefreshRateByPolicyLocked();
    }

    // Find the best refresh rate based on score
    std::vector<std::pair<const RefreshRate*, float>> scores;
    scores.reserve(mAppRequestRefreshRates.size());

    for (const auto refreshRate : mAppRequestRefreshRates) {
        scores.emplace_back(refreshRate, 0.0f);
    }

    for (const auto& layer : layers) {
        ALOGV("Calculating score for %s (%s, weight %.2f)", layer.name.c_str(),
              layerVoteTypeString(layer.vote).c_str(), layer.weight);
        if (layer.vote == LayerVoteType::NoVote || layer.vote == LayerVoteType::Min) {
            continue;
        }

        auto weight = layer.weight;

        for (auto i = 0u; i < scores.size(); i++) {
            bool inPrimaryRange =
                    scores[i].first->inPolicy(policy->primaryRange.min, policy->primaryRange.max);
            if ((primaryRangeIsSingleRate || !inPrimaryRange) &&
                !(layer.focused && layer.vote == LayerVoteType::ExplicitDefault)) {
                // Only focused layers with ExplicitDefault frame rate settings are allowed to score
                // refresh rates outside the primary range.
                continue;
            }

            // If the layer wants Max, give higher score to the higher refresh rate
            if (layer.vote == LayerVoteType::Max) {
                const auto ratio = scores[i].first->fps / scores.back().first->fps;
                // use ratio^2 to get a lower score the more we get further from peak
                const auto layerScore = ratio * ratio;
                ALOGV("%s (Max, weight %.2f) gives %s score of %.2f", layer.name.c_str(), weight,
                      scores[i].first->name.c_str(), layerScore);
                scores[i].second += weight * layerScore;
                continue;
            }

            const auto displayPeriod = scores[i].first->hwcConfig->getVsyncPeriod();
            const auto layerPeriod = round<nsecs_t>(1e9f / layer.desiredRefreshRate);
            if (layer.vote == LayerVoteType::ExplicitDefault) {
                const auto layerScore = [&]() {
                    // Find the actual rate the layer will render, assuming
                    // that layerPeriod is the minimal time to render a frame
                    auto actualLayerPeriod = displayPeriod;
                    int multiplier = 1;
                    while (layerPeriod > actualLayerPeriod + MARGIN_FOR_PERIOD_CALCULATION) {
                        multiplier++;
                        actualLayerPeriod = displayPeriod * multiplier;
                    }
                    return std::min(1.0f,
                                    static_cast<float>(layerPeriod) /
                                            static_cast<float>(actualLayerPeriod));
                }();

                ALOGV("%s (ExplicitDefault, weight %.2f) %.2fHz gives %s score of %.2f",
                      layer.name.c_str(), weight, 1e9f / layerPeriod, scores[i].first->name.c_str(),
                      layerScore);
                scores[i].second += weight * layerScore;
                continue;
            }

            if (layer.vote == LayerVoteType::ExplicitExactOrMultiple ||
                layer.vote == LayerVoteType::Heuristic) {
                const auto layerScore = [&] {
                    // Calculate how many display vsyncs we need to present a single frame for this
                    // layer
                    const auto [displayFramesQuot, displayFramesRem] =
                            getDisplayFrames(layerPeriod, displayPeriod);
                    static constexpr size_t MAX_FRAMES_TO_FIT =
                            10; // Stop calculating when score < 0.1
                    if (displayFramesRem == 0) {
                        // Layer desired refresh rate matches the display rate.
                        return 1.0f;
                    }

                    if (displayFramesQuot == 0) {
                        // Layer desired refresh rate is higher the display rate.
                        return (static_cast<float>(layerPeriod) /
                                static_cast<float>(displayPeriod)) *
                                (1.0f / (MAX_FRAMES_TO_FIT + 1));
                    }

                    // Layer desired refresh rate is lower the display rate. Check how well it fits
                    // the cadence
                    auto diff = std::abs(displayFramesRem - (displayPeriod - displayFramesRem));
                    int iter = 2;
                    while (diff > MARGIN_FOR_PERIOD_CALCULATION && iter < MAX_FRAMES_TO_FIT) {
                        diff = diff - (displayPeriod - diff);
                        iter++;
                    }

                    return 1.0f / iter;
                }();
                ALOGV("%s (%s, weight %.2f) %.2fHz gives %s score of %.2f", layer.name.c_str(),
                      layerVoteTypeString(layer.vote).c_str(), weight, 1e9f / layerPeriod,
                      scores[i].first->name.c_str(), layerScore);
                scores[i].second += weight * layerScore;
                continue;
            }
        }
    }

    // Now that we scored all the refresh rates we need to pick the one that got the highest score.
    // In case of a tie we will pick the higher refresh rate if any of the layers wanted Max,
    // or the lower otherwise.
    const RefreshRate* bestRefreshRate = maxVoteLayers > 0
            ? getBestRefreshRate(scores.rbegin(), scores.rend())
            : getBestRefreshRate(scores.begin(), scores.end());

    if (primaryRangeIsSingleRate) {
        // If we never scored any layers, then choose the rate from the primary
        // range instead of picking a random score from the app range.
        if (std::all_of(scores.begin(), scores.end(),
                        [](std::pair<const RefreshRate*, float> p) { return p.second == 0; })) {
            ALOGV("layers not scored - choose %s",
                  getMaxRefreshRateByPolicyLocked().getName().c_str());
            return getMaxRefreshRateByPolicyLocked();
        } else {
            return *bestRefreshRate;
        }
    }

    // Consider the touch event if there are no ExplicitDefault layers. ExplicitDefault are mostly
    // interactive (as opposed to ExplicitExactOrMultiple) and therefore if those posted an explicit
    // vote we should not change it if we get a touch event. Only apply touch boost if it will
    // actually increase the refresh rate over the normal selection.
    const RefreshRate& touchRefreshRate = getMaxRefreshRateByPolicyLocked();

    if (globalSignals.touch && explicitDefaultVoteLayers == 0 &&
        bestRefreshRate->fps < touchRefreshRate.fps) {
        setTouchConsidered();
        ALOGV("TouchBoost - choose %s", touchRefreshRate.getName().c_str());
        return touchRefreshRate;
    }

    return *bestRefreshRate;
}
           

使用API以及驗證

上面那麼多全部都是代碼層面的分析,寫兩個app測試一下。

先分别寫app調用setFrameRate和preferredDisplayModeId。看看是否真的像API中說的那樣,preferredDisplayModeId設定之後setFrameRate設定的值就無效了。

我這邊用來測試的機器,高刷是120hz的,是以判斷裡面擷取到mode值判斷是否大于61。因為這個getRefreshRate擷取到的值可能是60.xxx并不一定是60。

public void btn_setpreferredDisplayModeId(View v) {
    Display display = this.getDisplay();
    Display.Mode[] modes = display.getSupportedModes();
    for (Display.Mode m : modes) {
        Log.e(TAG, "btn_setpreferredDisplayModeId: " + m.getRefreshRate());
            if (m.getRefreshRate() >= 61) {
            WindowManager.LayoutParams param = getWindow().getAttributes();
            param.preferredDisplayModeId = m.getModeId();
            getWindow().setAttributes(param);
        }
    }
}
           

這是調用setFrameRate的代碼。

public void surfaceCreated(SurfaceHolder holder) {
    holder.getSurface().setFrameRate(60, FRAME_RATE_COMPATIBILITY_DEFAULT);
}
           

假設調用setFrameRate的APP叫做A,調用preferredDisplayModeId的APP叫做B。

我作的對比很簡單,先運作B,但是不點選B中設定重新整理率的按鈕,然後将目前的window設定成multi-window或者pop-window,再運作A。

這個時候因為我點選了螢幕,是以重新整理率會維持在120hz,檢視目前的重新整理率很簡單,1是在開發者模式中“show refresh rate”,2是dumpsys SurfaceFlinger,從dump的資訊中可以看到目前的重新整理率是多少。

當一段時間不觸碰螢幕之後,重新整理率會降低到60hz。這就是A設定的重新整理率。這時候我再點選B中的按鈕,調用preferredDisplayModeId設定到120hz。這時候重新整理率就會變成120hz,之後無論怎麼操作,隻要B的window還在(relaunch之後要重新點選button)重新整理率會一直在120hz,就算你重新關閉打開A。

這是一個對比,兩種設定重新整理率方式的對比。另外還有一個對比,就是一個app設120hz,一個app設60fps,到底會怎麼樣呢?

經過測試發現,這和app的focus相關。假設設定60hz的app叫做app_60,另外一個叫做app_120,當焦點在app_60上的時候,啟發式計算就會将重新整理率設定為60,反之則會設定為120.原因在上面的代碼分析中也寫過了,和window的幾種級别相關。

還有一些别的場景,我也不想去一一做對比了,有興趣的可以自己測試。以上兩種測試隻是為了分析完代碼之後寫個APP印證一下。