[090]unsignaled-buffer-latch功能

本文基于Android 13的代码。
以下是google官方对unsignaled-buffer-latch的部分介绍
https://source.android.google.cn/docs/core/graphics/unsignaled-buffer-latch?hl=zh-cn

背景

首先是千里马兄弟提出来了一个认知acquireFence只需要在HWC工作的之前signal就可以了,其实我也一直是这个认知,而且在
Android画面显示流程分析(3)-BufferQueue和Fence这篇文章中变相了提到了这点,如图中红色圈圈。但是他看了代码就感觉事实上如果buffer unsignaled,SurfaceFlinger无法放buffer过去给HWC,他自己的认知被颠覆,所以找我来确认,其实我第一眼代码也真的被颠覆了,后来经过我们两个晚上的不断讨论和抓trace分析,现在终于搞明白了。

26874665-0efd0e809cdd33c7.png

一、SurfaceFlinger对acquireFence unsignaled的buffer处理策略

首先有三种处理的策略,注释介绍很清楚

// Latch Unsignaled buffer behaviours
enum class LatchUnsignaledConfig {
    // All buffers are latched signaled.
    Disabled,

    // Latch unsignaled is permitted when a single layer is updated in a frame,
    // and the update includes just a buffer update (i.e. no sync transactions
    // or geometry changes).
    AutoSingleLayer,

    // All buffers are latched unsignaled. This behaviour is discouraged as it
    // can break sync transactions, stall the display and cause undesired side effects.
    Always,
};

对应这个策略有两个属性来控制debug.sf.latch_unsignaleddebug.sf.auto_latch_unsignaled

LatchUnsignaledConfig SurfaceFlinger::getLatchUnsignaledConfig() {
    if (base::GetBoolProperty("debug.sf.latch_unsignaled"s, false)) {
        return LatchUnsignaledConfig::Always;
    }

    if (base::GetBoolProperty("debug.sf.auto_latch_unsignaled"s, true)) {
        return LatchUnsignaledConfig::AutoSingleLayer;
    }

    return LatchUnsignaledConfig::Disabled;
}

总结成表格,大家可以看看手头的控制属性是什么,推断一下你的设备的LatchUnsignaledConfig。

LatchUnsignaledConfig 控制属性 配置说明
Disabled debug.sf.latch_unsignaled = false
debug.sf.auto_latch_unsignaled = false
所有的buffer必须是signaled
AutoSingleLayer debug.sf.latch_unsignaled = false
debug.sf.auto_latch_unsignaled = true
当这一帧中只简单的更新一个layer的buffer是时候,
允许使用unsignaled的buffer(换句话说,不支持sync transactions和layer的几何变化)
Always debug.sf.latch_unsignaled = true 所有unsignaled buffer允许被使用,这个行为是不鼓励的,
因为会影响sync transactions功能,造成不希望的效果

二、代码解读

2.1 flushTransactionQueues

首先用一个图来表示主要逻辑,flushTransactionQueues的主要任务就是找到可以在这一帧被apply的事务



图中的4个步骤,正好对应以下代码中4步

bool SurfaceFlinger::flushTransactionQueues(int64_t vsyncId) {
    // to prevent onHandleDestroyed from being called while the lock is held,
    // we must keep a copy of the transactions (specifically the composer
    // states) around outside the scope of the lock
    std::vector<TransactionState> transactions;
    {
        Mutex::Autolock _l(mStateLock);
        {
            Mutex::Autolock _l(mQueueLock);
             ...
            //图中第1步
            transactionsPendingBarrier =
                    flushPendingTransactionQueues(transactions, bufferLayersReadyToPresent,
                            applyTokensWithUnsignaledTransactions, /*tryApplyUnsignaled*/ false);

            //图中第2步
            while (!mTransactionQueue.empty()) {
                auto& transaction = mTransactionQueue.front();
                const bool pendingTransactions =
                        mPendingTransactionQueues.find(transaction.applyToken) !=
                        mPendingTransactionQueues.end();
                const auto ready = [&]() REQUIRES(mStateLock) {
                    if (pendingTransactions) {
                        ATRACE_NAME("pendingTransactions");
                        return TransactionReadiness::NotReady;
                    }

                    return transactionIsReadyToBeApplied(transaction, transaction.frameTimelineInfo,
                                                         transaction.isAutoTimestamp,
                                                         transaction.desiredPresentTime,
                                                         transaction.originUid, transaction.states,
                                                         bufferLayersReadyToPresent,
                                                         transactions.size(),
                                                         /*tryApplyUnsignaled*/ false);
                }();
                ATRACE_INT("TransactionReadiness", static_cast<int>(ready));
                if (ready != TransactionReadiness::Ready) {
                    ....
                    mPendingTransactionQueues[transaction.applyToken].push(std::move(transaction));
                } else {
                    ....
                    transactions.emplace_back(std::move(transaction));
                }
                mTransactionQueue.pop_front();
                ATRACE_INT("TransactionQueue", mTransactionQueue.size());
            }
            ....
            //图中第3步
            if (enableLatchUnsignaledConfig != LatchUnsignaledConfig::Disabled) {
                flushUnsignaledPendingTransactionQueues(transactions, bufferLayersReadyToPresent,
                                                        applyTokensWithUnsignaledTransactions);
            }
            //图中第4步
            return applyTransactions(transactions, vsyncId);
        }
    }
}

int SurfaceFlinger::flushUnsignaledPendingTransactionQueues(
        std::vector<TransactionState>& transactions,
        std::unordered_map<sp<IBinder>, uint64_t, SpHash<IBinder>>& bufferLayersReadyToPresent,
        std::unordered_set<sp<IBinder>, SpHash<IBinder>>& applyTokensWithUnsignaledTransactions) {
    return flushPendingTransactionQueues(transactions, bufferLayersReadyToPresent,
                                         applyTokensWithUnsignaledTransactions,
                                         /*tryApplyUnsignaled*/ true);
}

这里有三个关键的数据结构

mTransactionQueue:当前这一帧的事务队列
mPendingTransactionQueues:pending未处理的事务队列
transactions:最后需要apply的事务队列
4步的解读
1.处理mPendingTransactionQueues的事务,这里就是前一帧未处理的事务,带入tryApplyUnsignaled=false,如果不是NotReady,就可以加入到了transactions。
2.处理mTransactionQueue的事务,带入tryApplyUnsignaled=false,NOT READY就加入到mPendingTransactionQueues,Ready就加入transactions
3.再次处理mPendingTransactionQueues的事务,这里包含了前一帧未处理的事务以及步骤2中NOT READY的事务,但是这次传入了tryApplyUnsignaled=true,如果不是NotReady,就可以加入到了transactions。
4.最后apply transactions中的事务。

其中1,2,3步都会调用transactionIsReadyToBeApplied,判断事务能否被apply,我们来分析transactionIsReadyToBeApplied

2.2 transactionIsReadyToBeApplied

对于2.1中flushTransactionQueues的前两个环节,可以知道tryApplyUnsignaled = false,所以allowLatchUnsignaled 肯定为false,如果fenceUnsignaled是true,就意味着肯定是NotReady,所以在前面两个步骤,不可能会有unsignaled的buffer对应的事务会被处理,只能寄托于第三个步骤了。

auto SurfaceFlinger::transactionIsReadyToBeApplied(TransactionState& transaction,
        const FrameTimelineInfo& info, bool isAutoTimestamp, int64_t desiredPresentTime,
        uid_t originUid, const Vector<ComposerState>& states,
        const std::unordered_map<
            sp<IBinder>, uint64_t, SpHash<IBinder>>& bufferLayersReadyToPresent,
        size_t totalTXapplied, bool tryApplyUnsignaled) const -> TransactionReadiness {

        ....

        const bool allowLatchUnsignaled = tryApplyUnsignaled &&
                shouldLatchUnsignaled(layer, s, states.size(), totalTXapplied);//如果tryApplyUnsignaled为 false,所以allowLatchUnsignaled 肯定为false

        if (fenceUnsignaled && !allowLatchUnsignaled) {
            if (!transaction.sentFenceTimeoutWarning &&
                queueProcessTime - transaction.queueTime > std::chrono::nanoseconds(4s).count()) {
                transaction.sentFenceTimeoutWarning = true;
                auto listener = s.bufferData->releaseBufferListener;
                if (listener) {
                    listener->onTransactionQueueStalled();
                }
            }

            ATRACE_NAME("fence unsignaled");
            return TransactionReadiness::NotReady;//如果fenceUnsignaled为true,那就返回TransactionReadiness::NotReady
        }
    }
    //如果tryApplyUnsignaled,shouldLatchUnsignaled,fenceUnsignaled 都为true,就可以得到TransactionReadiness::ReadyUnsignaled
    return fenceUnsignaled ? TransactionReadiness::ReadyUnsignaled : TransactionReadiness::Ready;
}

第三个环节中,传入的tryApplyUnsignaled = true,如果shouldLatchUnsignaled可以返回true,以及 fenceUnsignaled = true,就可以达到ReadyUnsignaled的条件,对于flushPendingTransactionQueues的流程,如果“不是NotReady“,然后就可以加入到transactions中。

2.3 flushPendingTransactionQueues

这就是我在第三个环节中讲的,如果“不是NotReady“,就可以加入到了transactions,ReadyUnsignaled就是“不是NotReady”

int SurfaceFlinger::flushPendingTransactionQueues(
        std::vector<TransactionState>& transactions,
        std::unordered_map<sp<IBinder>, uint64_t, SpHash<IBinder>>& bufferLayersReadyToPresent,
        std::unordered_set<sp<IBinder>, SpHash<IBinder>>& applyTokensWithUnsignaledTransactions,
        bool tryApplyUnsignaled) {
            ···
            if (ready == TransactionReadiness::NotReady) {
                setTransactionFlags(eTransactionFlushNeeded);
                break;
            }
            ···
            不是NotReady,所以ReadyUnsignaled就可以加入transactions
            transactions.emplace_back(std::move(transaction));
        }
}

2.4 shouldLatchUnsignaled

接下来继续回来看shouldLatchUnsignaled的代码了,这里前面LatchUnsignaledConfig::Disabled和LatchUnsignaledConfig::Always的条件返回的结果很简单。关键就看LatchUnsignaledConfig::AutoSingleLayer的情况下,也就是手机配置成了AutoSingleLayer,在什么条件下会返回true。

bool SurfaceFlinger::shouldLatchUnsignaled(const sp<Layer>& layer, const layer_state_t& state,
                                           size_t numStates, size_t totalTXapplied) const {
    if (enableLatchUnsignaledConfig == LatchUnsignaledConfig::Disabled) {
        ALOGV("%s: false (LatchUnsignaledConfig::Disabled)", __func__);
        return false;//永远不允许处理Unsignaled的buffer
    }

    if (enableLatchUnsignaledConfig == LatchUnsignaledConfig::Always) {
        ALOGV("%s: true (LatchUnsignaledConfig::Always)", __func__);
        return true;//永远允许处理Unsignaled的buffer
    }

    // 只允许处理single layer
    // We only want to latch unsignaled when a single layer is updated in this
    // transaction (i.e. not a blast sync transaction).
    if (numStates != 1) {
        ALOGV("%s: false (numStates=%zu)", __func__, numStates);
        return false;//第一关
    }

    if (enableLatchUnsignaledConfig == LatchUnsignaledConfig::AutoSingleLayer) {
        if (totalTXapplied > 0) {
            ALOGV("%s: false (LatchUnsignaledConfig::AutoSingleLayer; totalTXapplied=%zu)",
                  __func__, totalTXapplied);
            return false;//第二关
        }

        // We don't want to latch unsignaled if are in early / client composition
        // as it leads to jank due to RenderEngine waiting for unsignaled buffer
        // or window animations being slow.
        const auto isDefaultVsyncConfig = mVsyncModulator->isVsyncConfigDefault();
        if (!isDefaultVsyncConfig) {
            ALOGV("%s: false (LatchUnsignaledConfig::AutoSingleLayer; !isDefaultVsyncConfig)",
                  __func__);
            return false;//第三关
        }
    }

    if (!layer->simpleBufferUpdate(state)) {
        ALOGV("%s: false (!simpleBufferUpdate)", __func__);
        return false;//第四关
    }

    ALOGV("%s: true", __func__);
    return true;
}

如果手机配置成了AutoSingleLayer,可以看到要达到shouldLatchUnsignaled返回true,至少要闯过4关,其中第一和第四关分别对应就是注释中描述的i.e. no sync transactions or geometry changes

numStates == 1 : 本次事务中是有一次layer,对应no sync transactions
totalTXapplied == 0 :transactions.size()为0,本次事务之前,还没有成功加入transactions的事务
isDefaultVsyncConfig == true : vsync的配置是默认
layer->simpleBufferUpdate(state) == true :本次layer只有简单的buffer更新,不涉及layer的几何变化, 对应no geometry changes。

至此我们已经分析完成了,整个处理的逻辑,最后就是将transactions中所有保存的事务进行apply,开始这一帧的合成。

三、感悟

这两个晚上,我和千里马兄弟也是不断通过分析代码逻辑,猜测代码意图,再通过不同的属性值设置,以及抓了很多次trace来验证我们的猜想,两个人也是一起激烈的争论,真理总是越辩越明,最后终于把整个逻辑理顺,也许看了这个文章,你还是云里雾里的,整个逻辑的确不好理解,这次分析主要关注的就是在哪些情况下acquireFence unsignaled的buffer对应的事务可以被当前这一帧sf的合成处理,希望你按着这个思路去跟一下代码。

尾巴

这个问题讨论的意义在哪里呢,那就是画面的延迟问题,看下面这个trace,就差那么一丢丢,其实把图中GPU处理的这一个buffer放过去,sf的合成的时候acquireFence肯定完成signal了,但是因为这台机器上的策略,在当前情况下连续两次acquireFence的判断(紫色,绿色线),都无法将这次buffer放过去,最后只能空跑一帧,下一帧合成了,这样子就出现画面的延迟问题了。


官方的一段话,介绍为什么要使用AutoSingleLayer ,非常值得认真读几遍。

Android 13 添加了一项名为 AutoSingleLayer 的新配置,用于锁存无信号的缓冲区。此配置可让 SurfaceFlinger 在仅有单个层更新时锁存无信号的缓冲区,但不适用于跨层发生的情况,例如几何图形变化或同步事务。
在 Android 13 之前,AOSP 中的 debug.sf.latch_unsignaled 标志允许 SurfaceFlinger 锁存所有无信号的缓冲区(无论在何种使用情形下)。启用此配置会出现一些非预期的附带效应,例如在等待未完成的缓冲区期间破坏同步事务和冻结整个显示屏。
使用 AutoSingleLayer 模式时,只会更新相应帧中单个 Surface 的缓冲区。此模式让游戏和其他全屏应用可锁存无信号的缓冲区并减少应用卡顿,同时不受显示屏冻结的影响。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容