ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Android显示链路全解析:从应用到屏幕的分层架构与调试指南

Android显示链路全解析:从应用到屏幕的分层架构与调试指南 1. 一张图背后的显示链路全景1.1 为什么“一张图”值得画Android 显示链路这个话题我在刚接触 Framework 那会儿就尝试过画图结果画了三版都不满意。第一版太粗只画了 App 到 SurfaceFlinger 的箭头第二版太细把每个 BufferQueue 的 slot 状态都标上结果图比代码还难读第三版才找到平衡点——按数据流向分层每层只保留一个核心动作和一组关键角色。之所以强调“一张图看懂”是因为这条链路涉及的模块实在太多应用进程里的 View 树、RenderThread、GPU 驱动、SurfaceFlinger、HWC、DRM/KMS中间还夹着 BufferQueue、Gralloc、Fence 这些同步机制。如果不用一张分层图把它们的职责边界和数据交接点固定下来排查问题时很容易在错误的层里打转。比如画面撕裂有人去查 View 的 onDraw有人去查 HWC 的 compose其实真正的问题可能出在 Fence 的 signal 时机上。这张图的价值不在于好看而在于定位。当你拿到一个掉帧、花屏、黑屏或者延迟问题第一件事应该是问数据现在卡在哪一层是 App 没产出帧还是 SurfaceFlinger 没合成还是 HWC 没送显这张图就是回答这个问题的地图。1.2 链路分层的核心逻辑我把整条链路切成四层从下往上分别是应用绘制层、合成层、硬件合成层、显示驱动层。这个切法不是拍脑袋而是对应了 Android 图形栈的实际进程和硬件边界。应用绘制层跑在 App 进程里核心是 UI 线程和 RenderThread 的配合。UI 线程负责构建 DisplayListRenderThread 负责把 DisplayList 转成 GPU 命令并提交。这一层的产出物是一个填好内容的 GraphicBuffer通过 BufferQueue 交给下一层。合成层就是 SurfaceFlinger 的地盘。它从各个应用的 BufferQueue 里取 buffer根据自己的合成策略决定哪些层用 GPU 合成、哪些层交给 HWC。这一层的关键是层叠管理和合成方式决策。硬件合成层对应 HWCHardware Composer。HWC 是显示硬件的抽象它告诉 SurfaceFlinger 自己能处理几个层、每个层支持什么格式和变换。如果 HWC 说“这个层我搞不定”SurfaceFlinger 就得回退到 GPU 合成。显示驱动层就是 DRM/KMS 这套内核子系统。HWC 最终通过 DRM 接口把合成好的帧提交给显示控制器显示控制器再按照时序把像素推给屏幕。这一层管的是刷新率、分辨率、色彩空间这些和物理面板强相关的参数。注意这四层不是严格串行的。比如 HWC 可以在 SurfaceFlinger 还没完成 GPU 合成时就预提交部分层这种并行是 Android 显示管线低延迟的关键但也是排查时序问题时最容易让人迷惑的地方。1.3 关键角色速查表在展开细节之前先把这条链路上的核心角色列出来后面每一层都会反复提到它们。角色所在层核心职责常见问题信号RenderThread应用绘制层执行 GPU 绘制命令掉帧时先看它的耗时BufferQueue应用/合成交界生产者消费者缓冲管理dequeueBuffer 超时SurfaceFlinger合成层层叠管理与合成决策合成耗时突增HWC硬件合成层硬件合成能力抽象validate 失败回退 GPUDRM/KMS显示驱动层显示控制器提交commit 失败或时序错乱Gralloc跨层图形缓冲分配分配失败或格式不支持Fence跨层GPU/显示同步等待超时导致卡顿这张表建议配合链路图一起看。实际排查时我习惯先看问题现象落在哪个角色的职责范围内再去查那个角色的状态。比如画面卡住不动先看 Fence 是不是没 signal画面颜色不对先看 Gralloc 分配的格式和 HWC 声明的格式是否匹配。2. 应用绘制层从 View 到 GraphicBuffer2.1 UI 线程与 RenderThread 的分工很多刚入行的同学会以为 View 的 onDraw 执行完画面就出来了其实 onDraw 只是把绘制指令记录到 DisplayList 里真正的 GPU 渲染发生在 RenderThread 上。这个分工是 Android 从 5.0 引入 RenderThread 之后定下来的目的是把耗时的 GPU 操作从 UI 线程剥离避免阻塞输入响应。UI 线程的流程大致是ViewRootImpl.performTraversals触发 measure、layout、drawdraw 阶段构建 DisplayList。然后通过ThreadedRenderer把 DisplayList 同步给 RenderThread。RenderThread 收到后执行CanvasContext的绘制把 DisplayList 转成 GPU 命令提交到 GPU。这里有个关键点UI 线程和 RenderThread 是并行的。UI 线程在准备下一帧的 DisplayList 时RenderThread 可能还在渲染上一帧。这种流水线设计提升了吞吐但也意味着如果某一帧的 GPU 渲染特别慢UI 线程准备好的下一帧就得在队列里等着表现为掉帧。2.2 BufferQueue 的生产者消费者模型RenderThread 渲染完成后需要把结果 buffer 交给 SurfaceFlinger。这个交接通过 BufferQueue 完成。BufferQueue 是一个典型的生产者消费者队列App 侧是生产者SurfaceFlinger 侧是消费者。BufferQueue 内部维护一组 slot每个 slot 对应一个 GraphicBuffer。生产者通过dequeueBuffer拿到一个空闲 slot渲染完成后通过queueBuffer把 slot 标记为已填充。消费者通过acquireBuffer拿到已填充的 slot用完后通过releaseBuffer归还。这个模型里最容易出问题的是slot 数量。默认情况下 BufferQueue 可能只有 2 到 3 个 slot。如果消费者消费太慢生产者就会在dequeueBuffer上阻塞UI 线程跟着卡住。反过来如果生产者产出太快消费者来不及消费队列满了之后生产者也会阻塞。所以调优时经常需要根据场景调整 slot 数量比如视频播放场景可能需要更多 buffer 来平滑抖动。实操心得用dumpsys SurfaceFlinger --list可以列出当前所有 layer再针对具体 layer 用dumpsys SurfaceFlinger查看它的 BufferQueue 状态。重点看queued和dequeued的计数如果 queued 一直涨而 dequeued 不动说明消费者侧有问题。2.3 从 DisplayList 到 GPU 命令的转换DisplayList 是一组绘制操作的记录比如“画一个矩形”“画一段文字”“应用一个变换”。RenderThread 拿到 DisplayList 后通过 Skia 或 Vulkan 后端把它转成 GPU 能执行的命令。这个转换过程有几个优化点值得注意。第一是批量合并把相同材质的绘制操作合并成一个 draw call减少 GPU 状态切换。第二是裁剪优化把不可见的绘制操作直接丢弃。第三是纹理上传把 Bitmap 等资源上传到 GPU 显存这一步如果频繁发生会很耗性能。我遇到过一个问题某个列表滑动时掉帧严重查下来是每个 item 的图标都在重新上传纹理。原因是图标没有做缓存每次绑定都触发一次纹理上传。后来改成用Bitmap的prepareToDraw预上传掉帧就消失了。这个案例说明应用绘制层的优化不能只看 onDraw 的耗时还要看 RenderThread 的实际 GPU 操作。2.4 应用层的常见性能陷阱应用绘制层有几个高频陷阱我按遇到频率排个序。第一是过度绘制。多个层叠的 View 都在画背景GPU 做了很多无用功。用开发者选项里的“调试 GPU 过度绘制”可以直观看到红色区域就是过度绘制严重的地方。解决办法是去掉不必要的背景或者用clipRect限制绘制区域。第二是布局层级过深。每多一层 Viewmeasure 和 layout 就多一轮递归。虽然现在有 ConstraintLayout 可以扁平化但很多老项目还是嵌套很深。用 Layout Inspector 可以查看层级超过 10 层的就要考虑优化。第三是在 onDraw 里创建对象。onDraw 会被频繁调用里面 new 一个 Paint 或者 Rect 都会加重 GC 负担。正确做法是在构造函数里创建好onDraw 里只做赋值和绘制。第四是同步等待。比如在 UI 线程里等一个网络请求或者文件读取直接卡死整条链路。这个属于基础问题但实际项目里还是经常见到。3. 合成层SurfaceFlinger 的决策逻辑3.1 SurfaceFlinger 的启动与 layer 管理SurfaceFlinger 是 Android 显示系统的核心服务在系统启动时由 init 进程拉起。它维护着所有 layer 的信息每个 layer 对应一个 Surface也就是应用可以绘制的一块画布。当应用创建一个 Surface 时SurfaceFlinger 会为它创建一个 layer并分配一个 BufferQueue。应用通过这个 BufferQueue 提交 bufferSurfaceFlinger 通过它获取 buffer。layer 有很多属性比如位置、大小、透明度、变换矩阵、裁剪区域、Z 序等。这些属性决定了合成时的最终效果。layer 的 Z 序管理是合成的基础。SurfaceFlinger 按照 Z 序从低到高排列 layer然后决定哪些层可以合并、哪些层需要单独处理。Z 序相同的 layer 会按照创建顺序排列这个细节在排查层叠问题时很重要。3.2 合成方式的选择GPU 还是 HWCSurfaceFlinger 拿到所有 layer 后要决定怎么合成。有两种方式GPU 合成和 HWC 合成。GPU 合成是把所有 layer 的 buffer 作为纹理通过 GPU 绘制到一个新的 buffer 上。这种方式灵活支持任意变换和混合但耗电、占带宽。HWC 合成是把 layer 直接交给显示硬件由硬件完成叠加。这种方式省电、低延迟但受硬件能力限制比如支持的层数、格式、变换都有限。SurfaceFlinger 的决策逻辑是先让 HWC 的prepare接口评估每个 layerHWC 返回每个 layer 的合成方式建议。如果 HWC 说某个 layer 它能处理SurfaceFlinger 就把它标记为 HWC 合成如果 HWC 说处理不了SurfaceFlinger 就把它标记为 GPU 合成。最后 SurfaceFlinger 把 GPU 合成的结果作为一个新的 layer 交给 HWC和那些 HWC 直接处理的 layer 一起送显。这个决策过程每次刷新都会执行因为 layer 的属性可能变化。比如一个视频层从全屏变成小窗HWC 可能就从能处理变成不能处理合成方式随之切换。切换本身有开销所以频繁切换会导致性能抖动。注意HWC 的prepare和set是两个阶段。prepare是评估set是提交。如果prepare之后 layer 属性又变了SurfaceFlinger 需要重新prepare。这个重试机制在排查合成问题时经常被忽略。3.3 合成时机与 VSync 的关系SurfaceFlinger 的合成不是随时进行的而是跟着 VSync 走。VSync 是显示器的垂直同步信号表示一帧显示完成、下一帧可以开始。SurfaceFlinger 收到 VSync 后开始新一轮合成。Android 的 VSync 有多个来源HWC 产生的硬件 VSync、SurfaceFlinger 模拟的软件 VSync、以及应用侧的 VSync。这些 VSync 通过 Choreographer 和 DispSync 机制协调确保应用绘制、SurfaceFlinger 合成、HWC 送显三个环节按节奏进行。如果某个环节错过了 VSync就会掉帧。比如应用在 VSync 到来时还没完成绘制SurfaceFlinger 就只能用上一帧的 buffer表现为画面卡顿。这种掉帧在dumpsys SurfaceFlinger的统计里能看到重点看missed计数。3.4 合成层的性能分析手段分析合成层性能我常用的工具有三个。第一个是dumpsys SurfaceFlinger。它能输出当前所有 layer 的状态、合成方式、耗时统计。重点看Composition部分的耗时如果 GPU 合成耗时超过 8ms在 60Hz 下就很容易掉帧。第二个是dumpsys SurfaceFlinger --latency。它能输出指定 layer 的帧延迟数据包括预期显示时间、实际显示时间、帧间隔。这个数据可以导入表格做进一步分析看延迟分布是否稳定。第三个是 Perfetto。它能抓取整条链路的 trace包括应用绘制、SurfaceFlinger 合成、HWC 提交。Perfetto 的好处是能看到跨进程的时序关系比如应用 queueBuffer 的时间点和 SurfaceFlinger acquireBuffer 的时间点之间的间隔这个间隔就是 buffer 在队列里的等待时间。这三个工具配合使用基本能定位合成层的绝大多数问题。我的习惯是先用 dumpsys 看整体状态再用 Perfetto 抓具体场景的 trace最后用 latency 数据验证优化效果。4. 硬件合成层HWC 的能力与限制4.1 HWC 的抽象接口HWC 是显示硬件的抽象层它定义了一组接口供 SurfaceFlinger 调用。核心接口有三个prepare、set、getDisplayConfigs。prepare接收一组 layer返回每个 layer 的合成方式建议。这个接口是 HWC 表达自己能力的主要途径。如果 HWC 支持某个 layer 的格式和变换它就返回HWC2::Composition::Device如果不支持返回HWC2::Composition::Client意思是让 SurfaceFlinger 用 GPU 合成。set接收最终的 layer 列表和每个 layer 的合成方式HWC 据此配置硬件并提交显示。这个接口是实际送显的步骤。getDisplayConfigs返回显示器支持的配置包括分辨率、刷新率、色彩空间等。SurfaceFlinger 根据这些配置决定使用哪个模式。HWC 的版本从 HWC1 演进到 HWC2接口设计变化很大。HWC2 引入了更细粒度的能力查询和更灵活的 layer 管理。现在主流设备都是 HWC2 或 HWC3。4.2 硬件合成的能力边界HWC 不是万能的它的能力受显示硬件限制。常见的限制包括层数限制很多 HWC 只支持 4 到 8 个 layer 同时硬件合成。超过这个数量多出来的 layer 就得 GPU 合成。格式限制HWC 可能只支持特定的像素格式比如 RGB565、RGBA8888。如果应用提交的 buffer 是其他格式HWC 可能处理不了。变换限制旋转、缩放、裁剪这些变换HWC 可能只支持部分组合。比如支持 90 度旋转但不支持任意角度旋转。混合限制透明度混合、颜色矩阵这些操作HWC 可能只支持简单模式。这些限制不是固定的不同厂商的 HWC 实现差异很大。同一款芯片不同厂商的调优策略不同HWC 的能力声明也可能不同。所以排查问题时不能假设 HWC 一定能处理某个 layer要以实际prepare的返回为准。4.3 回退机制与性能影响当 HWC 处理不了某个 layer 时SurfaceFlinger 会回退到 GPU 合成。这个回退不是免费的它有几个性能影响。第一是额外的 GPU 负载。GPU 合成需要把 layer 的 buffer 作为纹理绘制到目标 buffer这消耗 GPU 算力和带宽。如果回退的 layer 很多GPU 负载会显著上升。第二是额外的内存带宽。GPU 合成需要读写 buffer这消耗内存带宽。在移动设备上内存带宽是稀缺资源过度消耗会导致整体性能下降。第三是额外的同步开销。GPU 合成完成后结果 buffer 要交给 HWC 送显这中间有 Fence 同步。同步本身有延迟如果频繁回退延迟会累积。所以优化显示性能的一个重要方向就是减少 GPU 合成回退。具体做法包括控制 layer 数量、使用 HWC 支持的格式、避免 HWC 不支持的变换、合理设置 layer 属性等。实操心得用dumpsys SurfaceFlinger查看每个 layer 的合成方式如果发现某个 layer 一直是 GPU 合成就要查原因。常见原因是格式不匹配或者变换太复杂。把格式改成 HWC 支持的或者把变换拆解成 HWC 能处理的组合往往能显著降低 GPU 负载。4.4 HWC 与 DRM 的对接HWC 最终要通过 DRM/KMS 把帧提交给显示控制器。DRM 是内核的显示子系统KMS 是它的模式设置部分。HWC 通过 DRM 的 ioctl 接口配置显示控制器包括设置 framebuffer、配置 plane、提交 commit。DRM 的 plane 概念和 HWC 的 layer 概念对应。一个 plane 对应一个硬件叠加层HWC 把 layer 映射到 plane 上。如果 plane 数量不够多出来的 layer 就得 GPU 合成。所以 HWC 的层数限制本质上来自 DRM 的 plane 数量限制。DRM 的 commit 是原子操作要么全部生效要么全部不生效。这个特性保证了显示配置的一致性但也意味着如果某个 plane 的配置有问题整个 commit 会失败。排查 DRM 问题时dmesg里会有 commit 失败的日志重点看是哪个 plane 的哪个属性导致的。5. 显示驱动层DRM/KMS 的提交与显示5.1 DRM 的基本概念DRM 是 Direct Rendering Manager 的缩写是内核管理显示和渲染设备的子系统。KMS 是 Kernel Mode Setting 的缩写是 DRM 里负责显示模式设置的部分。DRM 的核心对象有四个CRTC、Encoder、Connector、Plane。CRTC 代表显示控制器负责从 framebuffer 读取像素并按照时序输出。Encoder 负责把 CRTC 的输出转换成 Connector 能接受的信号。Connector 代表物理接口比如 HDMI、DP、MIPI DSI。Plane 代表硬件叠加层负责把 framebuffer 合成到 CRTC 的输出上。这四个对象的关系是Plane 把多个 framebuffer 合成到一个 CRTC 上CRTC 按照时序输出Encoder 转换信号Connector 送到物理接口。HWC 的工作就是把 Android 的 layer 映射到 DRM 的 plane 上然后配置 CRTC 的时序和 Connector 的输出。5.2 原子提交与显示时序DRM 的原子提交是显示配置的核心机制。一次原子提交包含一组属性变更这些变更要么全部生效要么全部不生效。这个机制保证了显示配置的一致性避免了中间状态导致的画面异常。原子提交的流程是先构建一个 atomic state设置各个对象的属性然后调用drm_atomic_commit。内核会检查这个 state 是否合法如果合法就应用到硬件如果不合法就返回错误。显示时序是 CRTC 的核心配置包括像素时钟、水平同步、垂直同步、前后沿等参数。这些参数决定了显示器的刷新率和分辨率。如果时序配置错误显示器可能不亮或者画面错乱。HWC 从 Connector 的 EDID 里读取显示器支持的时序选择合适的模式配置 CRTC。5.3 刷新率切换与自适应现代显示设备支持多种刷新率比如 60Hz、90Hz、120Hz。Android 支持刷新率切换根据内容需求动态调整。比如静态画面用 60Hz 省电游戏用 120Hz 流畅。刷新率切换的流程是SurfaceFlinger 根据当前场景决定目标刷新率通过 HWC 通知 DRMDRM 配置 CRTC 的时序切换到目标刷新率。切换本身有开销所以不能太频繁。Android 的刷新率切换策略考虑了内容帧率、触摸事件、系统负载等因素。自适应刷新率是更高级的特性比如 LTPO 屏幕支持 1Hz 到 120Hz 的无级调节。这种屏幕可以根据内容精确匹配刷新率进一步省电。但自适应刷新率的控制更复杂需要 HWC 和 DRM 的紧密配合。5.4 显示驱动层的调试手段调试显示驱动层我常用的手段有三个。第一个是dmesg。内核日志里会有 DRM 的提交日志、时序配置日志、错误日志。如果显示异常先看 dmesg 里有没有 commit 失败或者时序错误的记录。第二个是modetest。这是 DRM 的测试工具可以列出所有 DRM 对象、查看当前配置、手动设置模式。用它可以验证硬件是否支持某个时序或者排查配置问题。第三个是drm_info。这个工具能输出 DRM 设备的详细信息包括支持的格式、plane 数量、CRTC 能力等。在排查 HWC 能力问题时drm_info 的输出是重要参考。这三个工具需要 root 权限在用户设备上可能用不了。但在开发板或者工程机上它们是排查显示问题的利器。6. 常见问题与排查技巧实录6.1 掉帧问题的分层排查法掉帧是最常见的显示问题排查时我习惯按层往下查。先看应用层。用dumpsys gfxinfo查看应用的绘制耗时重点看Draw、Prepare、Process三个阶段的耗时。如果某个阶段超过 16ms就是应用层的问题。常见原因是布局太复杂、onDraw 里有耗时操作、频繁创建对象等。再看合成层。用dumpsys SurfaceFlinger查看合成耗时如果 GPU 合成耗时超过 8ms就是合成层的问题。常见原因是 GPU 合成回退太多、layer 数量太多、buffer 格式不匹配等。最后看驱动层。用dmesg查看 DRM 提交日志如果有 commit 失败或者时序错误就是驱动层的问题。常见原因是 plane 配置错误、时序不匹配、带宽不足等。这个分层排查法的好处是每一步都有明确的判断依据不会在错误的层里浪费时间。6.2 画面撕裂与同步问题画面撕裂是显示同步问题的典型表现。原因是显示控制器在读取 framebuffer 时GPU 还在写入导致上半部分是旧帧、下半部分是新帧。解决撕裂的核心是同步。Android 用 Fence 机制同步 GPU 和显示控制器。GPU 渲染完成后通过 Fence 通知显示控制器可以读取。显示控制器读取完成后通过 Fence 通知 GPU 可以复用 buffer。如果 Fence 机制出问题就会撕裂。常见原因是 Fence 没有正确 signal或者 signal 时机不对。排查时用dumpsys SurfaceFlinger查看 Fence 状态重点看acquireFence和releaseFence的时间戳。注意有些撕裂是应用自己造成的比如在 onDraw 里直接操作 Surface绕过了 BufferQueue 的同步机制。这种撕裂在 SurfaceFlinger 层面看不出来需要查应用的绘制代码。6.3 黑屏与花屏的定位思路黑屏和花屏是更严重的显示问题定位思路和掉帧不同。黑屏先查链路是否通。用dumpsys SurfaceFlinger --list看 layer 是否存在用dumpsys SurfaceFlinger看 layer 是否有 buffer用dmesg看 DRM 是否 commit 成功。如果 layer 存在但没 buffer是应用没产出如果有 buffer 但没显示是合成或驱动的问题。花屏先查格式和时序。用drm_info看 plane 支持的格式用modetest看当前时序配置。格式不匹配会导致颜色错乱时序不匹配会导致画面错位。如果格式和时序都正常再查 buffer 的内容是否正确可能是应用绘制的问题。这两种问题的排查都需要对整条链路有清晰的认识这也是为什么我强调要画一张链路图。6.4 常见问题速查表问题现象可能层级排查工具常见原因掉帧应用层dumpsys gfxinfo布局复杂、onDraw 耗时掉帧合成层dumpsys SurfaceFlingerGPU 合成回退多掉帧驱动层dmesgcommit 失败、带宽不足撕裂同步dumpsys SurfaceFlingerFence 未 signal黑屏全链路dumpsys dmesgbuffer 缺失、commit 失败花屏驱动层drm_info modetest格式不匹配、时序错误延迟高合成层Perfettobuffer 等待时间长刷新率异常驱动层dmesg时序切换失败这张表建议打印出来贴在工位上排查问题时先对照现象找到可能层级再用对应工具深入。6.5 几个容易踩的坑第一个坑是只看应用层。很多性能问题表面上是应用掉帧实际根因在合成层或驱动层。比如 HWC 回退导致 GPU 负载高应用绘制本身没问题但整体帧率上不去。所以排查时要养成从下往上看的习惯先确认底层没问题再查上层。第二个坑是忽略 Fence。Fence 是跨层同步的关键但它的状态在常规 dumpsys 里不够直观。我建议在排查同步问题时专门抓 Perfetto trace 看 Fence 的 signal 时间点这比看 dumpsys 的文本输出有效得多。第三个坑是假设 HWC 能力。不同设备的 HWC 能力差异很大不能假设某个 layer 一定能硬件合成。排查时要实际看prepare的返回而不是凭经验判断。第四个坑是忽略刷新率。刷新率切换会影响帧率表现如果排查时没注意当前刷新率可能会误判。比如 120Hz 下 8ms 的合成耗时是正常的但 60Hz 下 8ms 就接近掉帧边缘。所以排查前先确认当前刷新率。7. 从链路图到实际调试的映射7.1 用链路图指导工具选择链路图不只是理解用的它还能指导工具选择。每一层都有对应的调试工具知道问题在哪一层就知道该用哪个工具。应用层用dumpsys gfxinfo、Layout Inspector、Perfetto 的应用 trace。合成层用dumpsys SurfaceFlinger、Perfetto 的 SurfaceFlinger trace。硬件合成层用 HWC 的日志和drm_info。驱动层用dmesg、modetest、drm_info。这个映射关系能大幅提升排查效率。以前我遇到问题会挨个工具试一遍现在先定位层级直接用对应工具省了很多时间。7.2 跨层问题的联合分析有些问题不是单一层的而是跨层的。比如延迟高可能是应用产出慢、buffer 等待久、合成耗时、送显延迟叠加的结果。这种问题需要联合分析。联合分析的工具首选 Perfetto。它能同时抓取应用、SurfaceFlinger、HWC、DRM 的 trace在一个时间轴上展示。通过看各层的耗时占比能定位主要瓶颈在哪一层。我处理过一个案例视频播放延迟高Perfetto 显示应用产出正常、合成耗时正常但 buffer 在队列里等待时间很长。进一步查发现是 BufferQueue 的 slot 数量太少生产者产出后没有空闲 slot只能等消费者释放。把 slot 数量从 2 调到 4 后延迟明显下降。这个案例说明跨层问题需要跨层工具单看某一层的数据是不够的。7.3 建立自己的调试 checklist最后分享一个我自己的习惯把常见问题的排查步骤整理成 checklist每次排查时按 checklist 走避免遗漏。checklist 的内容包括确认当前刷新率、确认 layer 是否存在、确认 buffer 是否产出、确认合成方式、确认 Fence 状态、确认 DRM commit 状态。这六步覆盖了整条链路的关键节点走完基本能定位问题所在层。这个 checklist 不是固定的随着遇到新问题会不断补充。比如后来加了“确认色彩空间”这一项因为遇到过 HDR 内容在 SDR 屏幕上显示异常的问题。建立自己的 checklist 是提升排查效率的有效方法建议每个从业者都做一份。实操心得checklist 最好做成可执行的脚本把常用的 dumpsys 和 dmesg 命令封装进去一键输出关键信息。这样排查时不用记命令直接跑脚本看结果效率更高。
返回列表