ARTICLE DETAIL

资讯详情

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

12、HWC多屏实现:HWC2接口、DisplayAttributes、Layer分配、Present与ReleaseFence

12、HWC多屏实现:HWC2接口、DisplayAttributes、Layer分配、Present与ReleaseFence 12.1 HWC2接口从HWC1到HWC2的进化先说说HWC2接口。Android从HWC1升级到HWC2最大的变化是什么我个人觉得是职责的重新划分。HWC1时代HWC几乎包揽了所有合成工作。到了HWC2它退居二线变成了一个「合成建议者」。什么意思呢就是SurfaceFlinger自己先尝试合成搞不定了再找HWC帮忙。嗯这里要注意HWC2的核心接口变了不再是单一的prepare和set而是拆成了更细粒度的操作。看一个典型的HWC2设备初始化流程// HWC2设备创建 hwc2_device_t* hwc2_device; hwc2_open(module, hwc2_device); // 注册回调 hwc2_device-registerCallback(device, HWC2_CALLBACK_VSYNC, vsync_callback); hwc2_device-registerCallback(device, HWC2_CALLBACK_HOTPLUG, hotplug_callback);我在项目中遇到过一个问题回调注册的顺序搞反了导致热插拔事件丢失。你想想看屏幕都插上去了系统却不知道那画面能对吗核心要点HWC2的回调注册必须在setPowerMode之前完成否则部分事件会被吞掉。12.2 DisplayAttributes屏幕的身份证每个物理屏幕都有自己的一套属性。在MTK8678上我们通过DisplayAttributes来描述这些信息。说白了这就是屏幕的「身份证」。常见的属性包括宽高物理像素尺寸比如1920x1080DPI每英寸像素数影响UI缩放刷新率60Hz、90Hz还是120Hz颜色格式RGB888还是YUV420连接类型HDMI、DP、MIPI DSI等代码里怎么拿这些属性看这里// 获取DisplayAttributes hwc2_display_t display primaryDisplay; hwc2_display_attribute_t attrs[32]; uint32_t num_attrs 32; hwc2_device-getDisplayAttributes(device, display, HWC2_DISPLAY_ATTRIBUTE_WIDTH_IN_PIXELS, attrs, num_attrs); // 遍历属性 for (int i 0; i num_attrs; i) { switch (attrs[i]) { case HWC2_DISPLAY_ATTRIBUTE_WIDTH_IN_PIXELS: width attrs[i].value; break; case HWC2_DISPLAY_ATTRIBUTE_HEIGHT_IN_PIXELS: height attrs[i].value; break; // ... 其他属性 } }我曾经在调试副屏时发现DPI值不对导致UI显示特别小。查了半天原来是EDID解析时把物理尺寸读错了。所以啊拿到属性后最好先打印出来跟硬件规格对一下。调试技巧在getDisplayAttributes返回后把所有属性值用log打出来。我习惯在开机阶段做一次全量dump方便后续对比。12.3 Layer分配谁该在哪个屏幕上多屏显示的核心问题之一就是Layer分配。每个应用窗口都是一个Layer系统需要决定这些Layer分别送到哪个屏幕去合成。在HWC2中Layer分配的逻辑是这样的SurfaceFlinger根据窗口的displayId把Layer分组每组Layer对应一个物理显示设备HWC2的createLayer接口为每个显示设备创建Layer栈看代码// 为每个显示设备创建Layer hwc2_layer_t layer; for (int i 0; i num_layers; i) { hwc2_device-createLayer(device, display, layer); // 设置Layer属性 hwc2_device-setLayerCompositionType(device, display, layer, HWC2_COMPOSITION_DEVICE); hwc2_device-setLayerBuffer(device, display, layer, buffer_handle, acquire_fence); }这里有个坑Layer的创建顺序很重要。我记得有一次副屏的Layer创建在主屏之前结果副屏的UI渲染优先级被拉低了出现明显的卡顿。后来我调整了创建顺序先主屏后副屏问题就解决了。注意事项Layer的compositionType要谨慎设置。如果设成HWC2_COMPOSITION_CLIENT意味着这个Layer由SurfaceFlinger合成性能开销会大很多。能走HWC2_COMPOSITION_DEVICE的尽量走硬件合成。12.4 Present与ReleaseFence同步的艺术最后聊聊Present和ReleaseFence。这两个东西说白了就是同步机制。PresentFenceHWC告诉SurfaceFlinger「这个帧我已经送出去了你可以准备下一帧了」。ReleaseFenceHWC告诉SurfaceFlinger「这个Buffer我用完了你可以回收了」。流程是这样的// HWC的Present调用 int32_t hwc2_present(hwc2_device_t* device, hwc2_display_t display, int32_t* out_present_fence) { // 等待硬件完成合成 wait_for_hardware_composition(display); // 创建PresentFence *out_present_fence create_fence_fd(); // 释放所有Layer的Buffer for (int i 0; i num_layers; i) { release_layer_buffer(display, i); } return HWC2_ERROR_NONE; }我曾经在调试双屏时发现副屏的画面总是比主屏慢一帧。查来查去问题出在ReleaseFence的时序上。副屏的ReleaseFence发得太早导致SurfaceFlinger以为Buffer已经释放了实际上硬件还在读。嗯这个坑我印象特别深。避坑指南我曾经因为ReleaseFence的fd没有正确dup导致Buffer泄漏。记住每个Fence fd只能被消费一次如果需要多次使用一定要dup。最后总结一下多屏同步的关键点PresentFence保证帧的显示顺序正确ReleaseFence保证Buffer的回收安全多屏场景每个屏幕的Fence要独立管理不能混用好了这一章的内容就到这儿。下一章我们会聊HWC的性能优化包括如何减少合成延迟、如何利用硬件Overlay等。到时候我会分享一些我在MTK8678上实测的数据应该对你有帮助。
返回列表