[052]Q平臺(tái)上setBrightness的巨坑

前言

最近解決了一個(gè)掉幀的問(wèn)題,從應(yīng)用層來(lái)看是buffer申請(qǐng)不到,最后發(fā)現(xiàn)是Q平臺(tái)升級(jí)+高通的代碼+我們自己驅(qū)動(dòng)優(yōu)化算法導(dǎo)致的,三者缺一不可,由于保密協(xié)議,我只能簡(jiǎn)單的原生代碼和簡(jiǎn)單的圖來(lái)描述這個(gè)問(wèn)題,避免大家踩坑。

一、Q平臺(tái)上setBrightness的升級(jí)

1.1 Android Q

@Override
public void setBrightness(int brightness, int brightnessMode) {
    synchronized (this) {
        // LOW_PERSISTENCE cannot be manually set
        if (brightnessMode == BRIGHTNESS_MODE_LOW_PERSISTENCE) {
            Slog.w(TAG, "setBrightness with LOW_PERSISTENCE unexpected #" + mId +
                    ": brightness=0x" + Integer.toHexString(brightness));
            return;
        }
        // Ideally, we'd like to set the brightness mode through the SF/HWC as well, but
        // right now we just fall back to the old path through Lights brightessMode is
        // anything but USER or the device shouldBeInLowPersistenceMode().
        if (brightnessMode == BRIGHTNESS_MODE_USER && !shouldBeInLowPersistenceMode()
                && mSurfaceControlMaximumBrightness == 255) {
            // TODO: the last check should be mSurfaceControlMaximumBrightness != 0; the
            // reason we enforce 255 right now is to stay consistent with the old path. In
            // the future, the framework should be refactored so that brightness is a float
            // between 0.0f and 1.0f, and the actual number of supported brightness levels
            // is determined in the device-specific implementation.
            if (DEBUG) {
                Slog.d(TAG, "Using new setBrightness path!");
            }
            SurfaceControl.setDisplayBrightness(mDisplayToken,
                    (float) brightness / mSurfaceControlMaximumBrightness);
        } else {
            int color = brightness & 0x000000ff;
            color = 0xff000000 | (color << 16) | (color << 8) | color;
            setLightLocked(color, LIGHT_FLASH_NONE, 0, 0, brightnessMode);
        }
    }
}

1.2 Android P

@Override
public void setBrightness(int brightness, int brightnessMode) {
    synchronized (this) {
        // LOW_PERSISTENCE cannot be manually set
        if (brightnessMode == BRIGHTNESS_MODE_LOW_PERSISTENCE) {
            Slog.w(TAG, "setBrightness with LOW_PERSISTENCE unexpected #" + mId +
                    ": brightness=0x" + Integer.toHexString(brightness));
            return;
        }
        int color = brightness & 0x000000ff;
        color = 0xff000000 | (color << 16) | (color << 8) | color;
        setLightLocked(color, LIGHT_FLASH_NONE, 0, 0, brightnessMode);
    }
}

1.3 兩者的區(qū)別

we'd like to set the brightness mode through the SF/HWC as well
我們也希望通過(guò)SF/HWC設(shè)置屏幕亮度

就是一旦以下條件滿足就會(huì)走Q版本上新的設(shè)置亮度流程

 if (brightnessMode == BRIGHTNESS_MODE_USER && !shouldBeInLowPersistenceMode()
                && mSurfaceControlMaximumBrightness == 255) 

Android Q上有兩種方式設(shè)置屏幕亮度,如下圖表示,導(dǎo)致掉幀的就是方式1

方式1:system_server->SF->HWC HAL->設(shè)備節(jié)點(diǎn)->背光驅(qū)動(dòng)(Android Q)
方式2:system_server->Light HAL->設(shè)備節(jié)點(diǎn)->背光驅(qū)動(dòng)(Android Q, P)

二、為什么會(huì)導(dǎo)致掉幀?

我在前言中已經(jīng)說(shuō)了是由Q平臺(tái)升級(jí)+高通的代碼+我們自己驅(qū)動(dòng)優(yōu)化算法導(dǎo)致的,三者缺一不可。

2.1 Q平臺(tái)升級(jí)

出問(wèn)題的項(xiàng)目就是走了方式1的流程,所以滿足條件。

2.2 高通的代碼

高通的HWC HAL層代碼中對(duì)setDisplayBrightness接口實(shí)現(xiàn)中加了一個(gè)display的鎖。也就意味這HWC其他的接口會(huì)和setDisplayBrightness產(chǎn)生鎖的競(jìng)爭(zhēng)關(guān)系。

2.3 我們自己驅(qū)動(dòng)優(yōu)化算法

我們?cè)隍?qū)動(dòng)中對(duì)背光設(shè)置有一些優(yōu)化,在特定的情況下,會(huì)導(dǎo)致寫(xiě)設(shè)備節(jié)點(diǎn)的時(shí)間耗時(shí)200ms左右。

2.4 還原現(xiàn)場(chǎng)

首先lightsensor觸發(fā)了自動(dòng)背光調(diào)節(jié),然后走SF-HWC去設(shè)置了亮度,持有了display的鎖。

由于驅(qū)動(dòng)的優(yōu)化算法,導(dǎo)致這把鎖持有了200ms。

這個(gè)200ms時(shí)間段里,SF繪制每一個(gè)幀的代碼中也有和HWC的調(diào)用,因?yàn)槟貌坏芥i導(dǎo)致也
block了,從而鎖住了一個(gè)buffer。

應(yīng)用申請(qǐng)一個(gè)buffer,完成繪制,交給sf,sf來(lái)不及使用。
應(yīng)用又申請(qǐng)一個(gè)buffer,完成繪制,交給sf,sf來(lái)不及使用。
最后應(yīng)用的三個(gè)buffer,一個(gè)處于lock,兩個(gè)處于未用的狀態(tài)(手機(jī)中bufferqueue設(shè)置的是3個(gè))
應(yīng)用再次申請(qǐng)buffer的時(shí)候,沒(méi)有可用的buffer了,導(dǎo)致了主線程的block,最后導(dǎo)致了掉幀的問(wèn)題的出現(xiàn)。

三、另外一個(gè)詭異的事情。

雖然問(wèn)題基本已經(jīng)解決,但是我無(wú)法解釋?zhuān)€有一個(gè)詭異的事情。

同一個(gè)手機(jī),在驅(qū)動(dòng)代碼完全一樣,只不過(guò)刷了不同高通基線的代碼
竟然一個(gè)走方式1,一個(gè)走方式2。

日志發(fā)現(xiàn)的原因:

方式1的時(shí)候mSurfaceControlMaximumBrightness為255
方式2的時(shí)候mSurfaceControlMaximumBrightness為0

3.1 maximumBrightness為什么是0

基本可以猜到下面的代碼在初始化的時(shí)候有異常,導(dǎo)致了maximumBrightness為0.
為什么會(huì)導(dǎo)致maximumBrightness為0,簡(jiǎn)單的說(shuō)一下就是高通的基線升級(jí)導(dǎo)致了getDisplayBrightnessSupport返回了true,我就不展開(kāi)講了。

private LightImpl(Context context, int id) {
    mId = id;
    mDisplayToken = SurfaceControl.getInternalDisplayToken();
    final boolean brightnessSupport = SurfaceControl.getDisplayBrightnessSupport(
            mDisplayToken);
    if (DEBUG) {
        Slog.d(TAG, "Display brightness support: " + brightnessSupport);
    }
    int maximumBrightness = 0;
    if (brightnessSupport) {
        PowerManager pm = context.getSystemService(PowerManager.class);
        if (pm != null) {
            maximumBrightness = pm.getMaximumScreenBrightnessSetting();
        }
    }
    mSurfaceControlMaximumBrightness = maximumBrightness;
}

3.2 負(fù)負(fù)得正

這個(gè)就是典型的負(fù)負(fù)得正,基線沒(méi)有升級(jí)導(dǎo)致了getDisplayBrightnessSupport為false,導(dǎo)致了mSurfaceControlMaximumBrightness為0,最后走方式2,掉幀問(wèn)題也就消失。

總結(jié)

基本上整個(gè)問(wèn)題的分析過(guò)程,我是通過(guò)trace分析出來(lái)的,然后結(jié)合特定關(guān)鍵點(diǎn)的log,把這個(gè)問(wèn)題給解決了。這是一個(gè)很有意思的問(wèn)題,不方便放trace的截圖,無(wú)法和大家分享如何看trace。

但是除了學(xué)會(huì)看trace的技巧,你一定要清楚的知道每一個(gè)進(jìn)程,每一個(gè)線程之前的通信的關(guān)系,然后在自己的腦海中去還原現(xiàn)場(chǎng),抽絲剝繭,找到問(wèn)題點(diǎn)。

整個(gè)問(wèn)題牽涉到APP-Framework-Kernel,如果你想要在性能優(yōu)化上更近一步,我個(gè)人認(rèn)為打通APP-Framework-Kernel是非常重要的一步。

尾巴

為什么Android Q上要大費(fèi)周章通過(guò)SF/HWC去設(shè)置屏幕亮度,我推測(cè)是谷歌希望將屏幕亮度調(diào)節(jié)和屏幕UI顯示之間建立起一個(gè)關(guān)系,一起配合調(diào)整,讓用戶對(duì)屏幕的觀感效果更好。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容