美文网首页
[069]PLL_CLK引发的降帧问题

[069]PLL_CLK引发的降帧问题

作者: 王小二的技术栈 | 来源:发表于2021-11-05 18:06 被阅读0次

    前言

    一个新的项目不管在什么情况下,画面都只能维持30帧左右,不能达到60帧。
    一般这种问题首先是转给性能组分析,那就让我开始分析吧。

    一、最简单的demo

    首先我写了一个最简单的demo,看看能不能达到60帧,结果无法只能达到30帧。


    1.1 dequeueBuffer时间长

    一般就是没有可用的buffer,SurfaceFlinger的消费能力有问题,需要去看SurfaceFlinger的Trace。

    1.2 waiting for GPU completion时间长

    一般是GPU的性能不行导致了绘制时间过长,但是我的demo就画了一根线,不可能是GPU性能的问题,有可能是GPU没有及时signal,导致了timeout。虽然我没有找到GPU绘制完成signal代码,但是我很快就放弃了这个思路。因为waitForever中虽然有3000ms的timeout温馨提示,但是最后还是会继续等,而且是timeout never。

    status_t Fence::waitForever(const char* logname) {
        ATRACE_CALL();
        if (mFenceFd == -1) {
            return NO_ERROR;
        }
        int warningTimeout = 3000;//温馨提示3000ms,
        int err = sync_wait(mFenceFd, warningTimeout);
        if (err < 0 && errno == ETIME) {
            ALOGE("waitForever: %s: fence %d didn't signal in %u ms", logname, mFenceFd.get(),
                  warningTimeout);
            ...
            err = sync_wait(mFenceFd, TIMEOUT_NEVER);//这里是time out never
        }
        return err < 0 ? -errno : status_t(NO_ERROR);
    }
    

    加入waiting for HWC release以后,原来是release的fence信号signal慢了,导致的GPU completion的时间也变长了(S平台和之前的平台对于release buffer的流程有所差异)。
    为什么waiting for HWC release会慢就需要去看SurfaceFlinger了。


    1.3 小结

    两个问题点最后都需要指向到SurfaceFlinger,我们继续查看SF的Trace。
    PS:以后遇到waiting for GPU completion时间长的问题,不能直接下定论是GPU性能不行。

    二、SurfaceFlinger分析

    一看SurfaceFlinger发现非常奇怪的事情,sf竟然绘制一帧,丢一帧。


    丢一帧的原因是framePending为true,hwcFrameMissed为true,gpuFrameMissed为false。
    然后满足了提前return的条件。

    void SurfaceFlinger::onMessageInvalidate(int64_t vsyncId, nsecs_t expectedVSyncTime) {
        ....
        // Pending frames may trigger backpressure propagation.
        const TracedOrdinal<bool> framePending = {"PrevFramePending",
                                                  previousFramePending(graceTimeForPresentFenceMs)};
        const TracedOrdinal<bool> frameMissed = {"PrevFrameMissed",
                                                 framePending ||
                                                         (previousPresentTime >= 0 &&
                                                          (lastScheduledPresentTime <
                                                           previousPresentTime - frameMissedSlop))};
        const TracedOrdinal<bool> hwcFrameMissed = {"PrevHwcFrameMissed",
                                                    mHadDeviceComposition && frameMissed};
        const TracedOrdinal<bool> gpuFrameMissed = {"PrevGpuFrameMissed",
                                                    mHadClientComposition && frameMissed};
         ....
        // framePending true
        // frameMissed true
        // hwcFrameMissed true
        // gpuFrameMissed false
        if (framePending) {
            if ((hwcFrameMissed && !gpuFrameMissed) || mPropagateBackpressureClientComposition) {
                signalLayerUpdate();
                return;//满足条件提前返回。
            }
        }
        ...
    }
    

    而且从图中看到,waiting for presentFence,而且整个wait过程竟然需要27.4ms。也就说sf合成后到开始刷新这一帧到屏幕需要27ms。一时,我也无法继续跟踪下去了,因为对HWC我不是很熟悉。

    三、PLL_CLK值有问题

    好在驱动工程师突然告诉我说PLL_CLK有问题,从475改成了560问题就解决了。
    当时我就一面懵逼,PLL_CLK是什么东西,这个数值代表什么意思。

    3.1 PLL_CLK是什么

    PLL_CLK就是图中CLK的那段波的频率,也就每秒一次高低电频发生的次数。


    转自诺比亚团队

    3.2 CMD屏PLL_CLK计算公式

    (Data rate) = width * height * 1.2 * total_bit_per_pixel * frame_per_second / total_lane_num
    DSI采用的是双边采样,则clk等于数据速率的一半,也就是说一个clk周期内传送2位,所以你计算出来的值还要除以2
    即PLL_CLOCK = Data rate / 2 (单位是MHZ)
    PS:其中1.2应该是一个经验值。
    

    经过计算我们屏幕PLL_CLK合适的值应该是559左右

    width = 1080 (屏幕分辨率是1080 * 2400)
    height = 2400
    total_bit_per_pixel = 24 (RGB值,每个字节是8位,三个字节)
    frame_per_second = 60 (60帧的屏幕)
    total_lane_num = 4(4根线)
    Data rate = 1080 * 2400 * 1.2 * 24 * 60 / 4 = 1119744000
    即PLL_CLOCK = Data rate / 2 = 559872000 = 559.872MHZ
    

    公式可能看不明白,这样子解释你就明白了。

    1秒内60hz的手机需要传递的数据是多少。
    屏幕的宽x屏幕的高x每个像素点的数据量x每秒的帧率。
    1080x2400x24x60
    由于有4根传输线,并且一次高低电频可以传输2次,所以PLL_CLOCK至少要达到以下数值
    1080x2400x24x60/4/2
    但是不能那么小气,加上一个经验值1.2
    1080x2400x24x60x1.2/4/2 = 559872000 = 559.872MHZ
    

    3.3 小结

    之前设置的PLL_CLK值过小,传输速率过低,导致前一帧无法在一个vsync周期内将屏幕的数据传输给屏幕,导致这一帧的presentFence等待signal时间过久,然后sf主动丢了一帧,从而导致屏幕从60fps降为了30fps。但是目前presentFence和传输数据给屏幕之前的关系,我还没有找到对应的代码,因为我对驱动不是很熟悉。

    四、整个过程还原

    可以用已经掌握的知识来还原整个上层的流程,整个过程更加清晰了。


    总结

    整个问题还是非常有意思的,强烈推荐大家阅读参考资料中的文章,让我对屏幕显示画面有了更加深入的理解,而且也终于理解了为什么画面会有出现撕裂。

    参考资料

    https://www.jianshu.com/p/df46e4b39428
    这几个图画的是真好,仍不住转载一下

    可以看到DSI有4根线,就是total_lane_num
    如果写的速度慢于扫描的速度,就有可能花屏

    尾巴

    当然有时间还是想去看看显示驱动那块的代码,给自己留几个问题。
    有知道朋友欢迎留言解惑。

    presentFence          唤醒的代码位置
    GPU completion        唤醒的代码位置
    HWC release           唤醒的代码位置
    

    相关文章

      网友评论

          本文标题:[069]PLL_CLK引发的降帧问题

          本文链接:https://www.haomeiwen.com/subject/btahzltx.html