Android HardwareComposer中的fence机制

    最近在写HardwareComposer,这东西的主要功能简单来说,就是把SurfaceFlinger准备好的各个layer data送到需要的地方去,以aosp中hardware/samsung_slsi/exynos5/libhwc 这个hwc为例,就是把layer data送到/dev/graphics/fb0去显示.

    不过所谓的"sf准备好的layer",其实未必真的准备好了.

    举个例子来说,gralloc分配的一块buffer在apk和sf之间共享,apk作为buffer的producer,sf作为buffer的consumer.apk执行完必要的gl绘图操作后,会把buffer的控制权交给sf,sf再把buffer对应的handle交给hwc做compose,这个时候apk中gl的func虽然都已经return了,但是真正的操作可能还在gpu的queue里面,buffer的内容还没有更新.如果hwc把buffer信息送到fb driver后,fb driver马上就把buffer内容显示到画面上的话,看到的就是错误的画面.反过来也是一样,当画面的内容还在显示状态,apk又在同一块buffer上重新绘制,也会出现类似问题.所以,这就需要一个producer和consumer之间的同步机制,也就是本篇要讨论的fence.

    我们来看下fence在hwc是如何运用的,当sf有内容需要hwc去显示的时候,sf会call hwc的set函数(其实在这之前还会call prepare,不过prepare不会处理fence).set函数return之前不必等待每个layer真正的准备好,只要正确处理了fence,buffer在ready的时候会signal相关的fence,通知真正消费buffer data的人.

hardware/libhardware/include/hardware/hwcomposer.h

  1. int (*set)(struct hwc_composer_device_1 *dev,  
  2.             size_t numDisplays, hwc_display_contents_1_t** displays);  
其中hwc_display_contents_1_t是这样定义的(忽略和fence无关的部分):
  1. typedef struct hwc_display_contents_1 {  
  2.     int retireFenceFd;  
  3.     size_t numHwLayers;  
  4.     hwc_layer_1_t hwLayers[0];  
  5.   
  6. } hwc_display_contents_1_t;  

    numHwLayers表示这次需要显示的layer个数,hwLyaers[0]是一个大小为0的数组,指向numHwLayers的下一个地址,sf在构建hwc_display_contents_1_t的时候会根据numHwLayers来分配大小为sizeof(hwc_display_contents_1_t)+numHwLayers*sizeof(hwc_layer_1_t)的memory,这样hwLyaers就指向了真正保存了numHwLayers个hwc_layer_1_t的地址.

    retireFenceFd需要在这次composition retire后被singal,对于physical display来说,retire的概念是:当下个画面替换掉本次画面的时候.对于virtual display来说,retire的概念是:当内容被完整的写进outbuffer,并可以被read的时候.总的来说就是本次composition的结果不再被需要的时候,就可以把hwc_display_contents_1_t标记为retire了.

    retireFenceFd控制的是整个composition,也就是整组的layer.

    再来看hwc_layer_1_t

  1. typedef struct hwc_layer_1 {  
  2.     ...  
  3.     buffer_handle_t handle;  
  4.     int acquireFenceFd;  
  5.     int releaseFenceFd;  
  6.     ...  
  7. } hwc_layer_1_t;  
    handle就是本layer对应的buffer.set函数被call到的时候并不知道buffer是否真正ready,acquireFenceFd会在buffer内容真正ready的时候被signal,hwc要负责close acquireFenceFd.set函数return之前并不能保证buffer内容已经使用完毕,不再需要,所以hwc需要负责为这个buffer构建一个fence,releaseFenceFd在set函数return之前被set起来,sf会等待releaseFenceFd被signal之后才会把buffer拿去重用,sf要负责close releaseFenceFd.

    acquireFenceFd和releaseFenceFd控制的是单个layer.

    仍然以hardware/samsung_slsi/exynos5/libhwc/为例,(简化code只保留和fence相关的部分,完整code请参考aosp):

  1. static int exynos5_set(struct hwc_composer_device_1 *dev,  
  2.         size_t numDisplays, hwc_display_contents_1_t** displays)  
  3. {  
  4.     if (!numDisplays || !displays)  
  5.         return 0;  
  6.   
  7.     exynos5_hwc_composer_device_1_t *pdev =  
  8.             (exynos5_hwc_composer_device_1_t *)dev;  
  9.     hwc_display_contents_1_t *fimd_contents = displays[HWC_DISPLAY_PRIMARY];  
  10.     int fimd_err = 0;  
  11.   
  12.     if (fimd_contents)  
  13.         fimd_err = exynos5_set_fimd(pdev, fimd_contents);  
  14.     return fimd_err;  
  15. }  
    set函数拿到编号为HWC_DISPLAY_PRIMARY的hwc_display_contents_1_t,也就是andorid内建主显示屏需要显示的内容,call到exynos5_set_fimd.
  1. static int exynos5_set_fimd(exynos5_hwc_composer_device_1_t *pdev,  
  2.         hwc_display_contents_1_t* contents)  
  3. {  
  4.     hwc_layer_1_t *fb_layer = NULL;  
  5.     int err = 0;  
  6.   
  7.     int fence;  
  8.     if (!err) {  
  9.         fence = exynos5_post_fimd(pdev, contents);  
  10.         if (fence < 0)  
  11.             err = fence;  
  12.     }  
  13.     for (size_t i = 0; i < NUM_HW_WINDOWS; i++) {  
  14.     int dup_fd = dup(fence);  
  15.         layer.releaseFenceFd = dup_fd;  
  16.     }  
  17.     contents->retireFenceFd = fence;  
  18.   
  19.     return err;  
  20. }  
    exynos5_post_fimd return了int fence,fence其实是个fd,hwc把这个fd dup了n次,赋值给每个layer的releaseFeceFd,最后在把fence本身赋值给整个layer组的retireFenceFd.也就是说,retireFenceFd和所有的releaseFenceFd对应到kernel中都是同一个file.截至目前为止,acqireFenceFd还没有出场,肯定还在exynos5_post_fimd 中.
  1. dev->fd = open("/dev/graphics/fb0", O_RDWR);  


  1. static int exynos5_post_fimd(exynos5_hwc_composer_device_1_t *pdev,  
  2.         hwc_display_contents_1_t* contents)  
  3. {  
  4.     exynos5_hwc_post_data_t *pdata = &pdev->bufs;  
  5.     struct s3c_fb_win_config_data win_data;  
  6.     struct s3c_fb_win_config *config = win_data.config;  
  7.   
  8.     memset(config, 0, sizeof(win_data.config));  
  9.     for (size_t i = 0; i < NUM_HW_WINDOWS; i++)  
  10.         config[i].fence_fd = -1;  
  11.   
  12.     for (size_t i = 0; i < NUM_HW_WINDOWS; i++) {  
  13.         int layer_idx = pdata->overlay_map[i];  
  14.         if (layer_idx != -1) {  
  15.             hwc_layer_1_t &layer = contents->hwLayers[layer_idx];  
  16.             private_handle_t *handle =  
  17.                     private_handle_t::dynamicCast(layer.handle);  
  18.   
  19.             exynos5_config_overlay(&layer, config[i], pdev);  
  20.         }  
  21.     }  
  22.       
  23.     int ret = ioctl(pdev->fd, S3CFB_WIN_CONFIG, &win_data);  
  24.     for (size_t i = 0; i < NUM_HW_WINDOWS; i++)  
  25.         if (config[i].fence_fd != -1)  
  26.             close(config[i].fence_fd);  
  27.     return win_data.fence;  
  28. }  

    每个layer对应一个config,所有的config组成win_data,exynos5_config_overlay会从每个layer中抽取config的需要的信息填写进cofnig,最后整个win_data作为参数,通过S3CFB_WIN_CONFIG ioctl送去fb0了,exynos5_post_fimd return的fence其实是经由fb driver处理后的win_data.fence.

    acqireFenceFd还是没有出现,但是我们注意到,在ioctl之前,每个config的fence_fd被设成-1,ioctl之后,又再次判断fence_fd,如果不是-1,就close它.config.fence_fd一定和acqireFenceFd有关!不过在这里就close fence_fd会不会有点太早了?这个fence不再需要了吗?不是说set函数返回之前不需要等待acquireFenceFd被signal吗?我们再进去exynos5_config_overlay看看.

  1. static void exynos5_config_overlay(hwc_layer_1_t *layer, s3c_fb_win_config &cfg,  
  2.         exynos5_hwc_composer_device_1_t *pdev)  
  3. {  
  4.     private_handle_t *handle = private_handle_t::dynamicCast(layer->handle);  
  5.     exynos5_config_handle(handle, layer->sourceCrop, layer->displayFrame,  
  6.             layer->blending, layer->acquireFenceFd, cfg, pdev);  
  7. }  
  1. static void exynos5_config_handle(private_handle_t *handle,  
  2.         hwc_rect_t &sourceCrop, hwc_rect_t &displayFrame,  
  3.         int32_t blending, int fence_fd, s3c_fb_win_config &cfg,  
  4.         exynos5_hwc_composer_device_1_t *pdev)  
  5. {  
  6.     uint32_t x, y;  
  7.     uint32_t w = WIDTH(displayFrame);  
  8.     uint32_t h = HEIGHT(displayFrame);  
  9.     uint8_t bpp = exynos5_format_to_bpp(handle->format);  
  10.     uint32_t offset = (sourceCrop.top * handle->stride + sourceCrop.left) * bpp / 8;  
  11.   
  12.     cfg.state = cfg.S3C_FB_WIN_STATE_BUFFER;  
  13.     cfg.fd = handle->fd;  
  14.     cfg.x = x;  
  15.     cfg.y = y;  
  16.     cfg.w = w;  
  17.     cfg.h = h;  
  18.     cfg.format = exynos5_format_to_s3c_format(handle->format);  
  19.     cfg.offset = offset;  
  20.     cfg.stride = handle->stride * bpp / 8;  
  21.     cfg.blending = exynos5_blending_to_s3c_blending(blending);  
  22.     cfg.fence_fd = fence_fd;  
  23. }  
    acquireFenceFd终于出现了,它作为每个layer对应的config的cfg.fence_fd,经由S3CFB_WIN_CONFIG  ioctrl送去了fb driver.既然送去了fb driver,那么fence的生死就由driver负责了,hwc不用再理会.所以上面exynos5_post_fimd 才把每个layer的config.fence_fd,也就是layer->acquireFenceFd给close掉了.

    至此,hwc中对于fence的处理流程就走完了,总结一下:

    1. hwc把所有的layer->acquireFenceFd一起送到fb driver后,就把layer->acquireFenceFd close,把fence的管理权交给fb driver.

    2. ioctl rentrun时,win_data.fence被driver填写为一个新的fd,这个fd被dup n次并赋值给每个layer的layer->releaseFenceFd.

    3. win_data.fence赋值给整个hwc_display_contents_1_t,也就是所有layer组合的retireFenceFd.

    4.之后releaseFenceFd和retaireFenceFd的生死就交给sf来负责了.

    接下来的讨论进入fd driver,请参考Android fb driver中的fence机制

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值