开发者

Linux libdrm中drm_syncobj的实现原理

开发者 https://www.devze.com 2025-09-24 10:09 出处:网络 作者: DeeplyMind
目录1. 前言2. syncobj 相关 IOCTLs概览与典型流程3. 创建与销毁3.1 创建同步对象3.1.1 DRM_IOCTL_SYNCOBJ_CREATE3.2 销毁同步对象3.2.1 DRM_IOCTL_SYNCOBJ_DESTROY4. 导入与导出4.1 导出同步对象到文件描述符4.1.1
目录
  • 1. 前言
  • 2. syncobj 相关 IOCTLs概览与典型流程
  • 3. 创建与销毁
    • 3.1 创建同步对象
      • 3.1.1 DRM_IOCTL_SYNCOBJ_CREATE
    • 3.2 销毁同步对象
      • 3.2.1 DRM_IOCTL_SYNCOBJ_DESTROY
  • 4. 导入与导出
    • 4.1 导出同步对象到文件描述符
      • 4.1.1 DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD
    • 4.2 从文件描述符导入同步对象
      • 4.2.1 DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE
  • 5. 信号与重置
    • 5.1 信号同步对象
      • 5.1.1 DRM_IOCTL_SYNCOBJ_SIGNAL
    • 5.2 重置同步对象
      • 5.2.1 DRM_IOCTL_SYNCOBJ_RESET
  • 6. 时间线操作
    • 6.1 timeline syncobj 简介
      • 6.2 timeline 信号
        • 6.2.1 DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL
      • 6.3 timeline 等待
        • 6.3.1 DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT
    • 等待与事件通知
      • 7.1 普通等待
        • 7.1.1 DRM_IOCTL_SYNCOBJ_WAIT
    • 8. 应用示例
      • 9. 总结

        1. 前言

        在博文:linux drm_syncobj 机制原理与应用中介绍了内核drm_syncobj机制的相关原理,本篇是用户态libdrm中的syncobj的API实现分析。

        syncobj 的设计初衷是为用户空间提供一种高效、灵活的 GPU 任务同步原语。

        它具备如下特点:

        • 对象化管理:同步状态以对象(syncobj)形式存在,用户空间可通过句柄引用和操作。
        • 多次信号/等待:支持多次信号和等待,适合异步和多阶段任务。
        • 跨进程同步:支持同步对象的导入/导出,实现进程间同步。
        • timeline 扩展:部分驱动支持 timeline syncobj,可表示多个时间点的同步状态。

        传统 fence 仅能表示单次同步事件,且生命周期受限。而 syncobj 支持多次信号/等待,且可导入/导出,适合复杂的同步场景。timeline syncobj 更是将同步扩展到多时间点,极大提升了灵活性。

        2. syncobj 相关 IOCTLs概览与典型流程

        syncobj 的所有操作都通过 ioctl 与内核 DRM 驱动交互。

        和内核实现一文中的维度划分保持一致,把ioctl划分如下。

        维度IOCTL 名称功能描述
        创建与销毁DRM_IOCTL_SYNCOBJ_CREATE创建同步对象,分配资源并返回句柄
        DRM_IOCTL_SYNCOBJ_DESTROY销毁同步对象,释放资源
        导入与导出DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD将同步对象句柄导出为文件描述符,实现跨进程同步
        DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE从文件描述符导入同步对象句柄,实现跨进程同步
        信号与重置DRM_IOCTL_SYNCOBJ_SIGNAL将同步对象设为已完成(signaled)状态
        DRM_IOCTL_SYNCOBJ_RESET将同步对象设为未完成(unsignaled)状态
        时间线操作DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL对 timeline syncobj 的指定时间点进行信号
        DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT等待 timeline syncobj 的指定时间点完成
        DRM_IOCTL_SYNCOBJ_TIMELINE_QUERY查询 timeline syncobj 当前时间点状态
        等待与事件通知DRM_IOCTL_SYNCOBJ_WAIT等待同步对象变为已完成状态,支持阻塞/非阻塞及超时
        DRM_IOCTL_SYNCOBJ_QUERY查询同步对象的当前状态

        典型流程:

        • 创建 syncobj:通过 drmSyncobjCreate 创建同步对象,获得句柄。将该句柄随任务传递给内核驱动。
        • 导入/导出:如需跨进程同步,通过 drmSyncobjHandleToFd 和 drmSyncobjFdToHandle 实现句柄与 fd 的转换。
        • 信号/重置:根据任务完成情况,调用 drmSyncobjsignal 或 drmSyncobjReset 设置同步对象状态。
        • timeline 操作:如需多时间点同步,使用 timeline 相关 API 进行信号、等待和查询。
        • 等待/通知:通过 drmSyncobjWait 或 drmSyncobjTimelineWait 等待任务完成(任务完成后,会signal该任务关联的步android骤1的同步对象),驱动后续处理。
        • 销毁:任务完成后,调用 drmSyncobjDestroy 释放资源。

        3. 创建与销毁

        3.1 创建同步对象

        3.1.1 DRM_IOCTL_SYNCOBJ_CREATE

        该 ioctl 用于在内核中分配一个同步对象,并返回其句柄。

        用户空间通过 libdrm 的 drmSyncobjCreate API 调用。

        • 支持普通 syncobj 和 timeline syncobj 创建
        • 句柄在用户空间唯一标识同jNFmpM步对象
        • 可指定初始状态(已信号/未信号)
        drm_public int drmSyncobjCreate(int fd, uint32_t flags, uint32_t *handle)
        {
            struct drm_syncobj_create args;
            int ret;
        
            memclear(args);
            args.flags = flags;
            args.handle = 0;
            ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_CREATE, &args);
            if (ret)
                return ret;
            *handle = args.handle;
            return 0;
        }
        • fd:打开的DRM 设备文件描述符
        • flags:可选标志,如 DRM_SYNCOBJ_CREATE_SIGNALED(初始为已信号状态)、DRM_SYNCOBJ_CREATE_TIMELINE(创建 timeline syncobj)
        • handle:返回的同步对象句柄

        3.2 销毁同步对象

        3.2.1 DRM_IOCTL_SYNCOBJ_DESTROY

        该 ioc编程客栈tl 用于释放同步对象资源。libdrm 封装为 drmSyncobjDestroy

        drm_public int drmSyncobjDestroy(int fd, uint32_t handle)
        {
            struct drm_syncobj_destroy args;
        
            memclear(args);
            args.handle = handle;
            return drmIoctl(fd, DRM_IOCTL_SYNCOBJ_DESTROY, &args);
        }
        • fd:DRM 设备文件描述符
        • handle:要销毁的同步对象句柄

        后续的API我不再给出具体实现,感兴趣的朋友请查看源码。

        4. 导入与导出

        4.1 导出同步对象到文件描述符

        4.1.1 DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD

        该 ioctl 用于将 syncobj 句柄导出为文件描述符,实现跨进程同步。

        libdrm 封装为 drmSyncobjHandleToFd

        int drmSyncobjHandleToFd(int fd, uint32_t handle, int *out_fd);

        4.2 从文件描述符导入同步对象

        4.2.1 DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE

        该 ioctl 用于将文件描述符导入为 syncobj 句柄。

        libdrm 封装为 drmSyncobjFdToHandle

        drmSyncobjFdToHandle(fd, syncobj_fd, &imported_handle);

        5. 信号与重置

        5.1 信号同步对象

        5.1.1 DRM_IOCTL_SYNCOBJ_SIGNAL

        该 ioctl 用于将同步对象设为已完成(signaled)状态。

        libdrm 封装为 drmSyncobjSignal

        int drmSyncobjSignal(int fd, const uint32_t *handles, uint32_t count);

        5.2 重置同步对象

        5.2.1 DRM_IOCTL_SYNCOBJ_RESET

        该 ioctl 用于将同步对象设为未完成(unsignaled)状态。

        libdrm 封装为 drmSyncobjReset

        int drmSyncobjReset(int fd, const uint32_t *handles, uint32_t count);

        6. 时间线操作

        这个高级用法,我还没有在项目中用到过,有用例的朋友欢迎分享。

        6.1 timeline syncobj 简介

        timeline syncobj 是对传统同步对象的扩展,允许一个 syncobj 维护多个时间点(value),每个 value 都可单独信号和等待。

        适用于多帧渲染、批量任务调度等复杂场景。

        6.2 timeline 信号

        6.2.1 DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL

        该 ioctl 用于对 timeline syncobj 的指定时间点进行信号。

        libdrm 封装为 drmSyncobjTimelineSignal

        int drmSyncobjTimelineSignal(int fd, const uint32_t *handles,
                               const uint64_t *points, uint32_t count);
        • fd:DRM 设备文件描述符
        • handles:syncobj 句柄数组
        • points:对应时间点数组
        • count:数量

        6.3 timeline 等待

        6.3.1 DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT

        该 ioctl 用于等待 timeline syncobj 的指定时间点完成。

        libdrm 封装为 drmSyncobjTimelineWait

        int drmSyncobjTimelineWait(int fd, const uint32_t *handles,
                                   const uint64_t *points, uint32_t count,
                                   uint64_t timeout_nsec, uint32_t flags,
                                   uint32_t *first_signaled);
        • fd:DRM 设备文件描述符
        • handles:syncobj 句柄数组
        • points:对应时间点数组
        • count:数量
        • timeout_nsec:超时时间(纳秒)
        • flags:等待标志
        • first_signaled:返回第一个完成的 syncobj 索引

        等待与事件通知

        7.1 普通等待

        7.1.1 DRM_IOCTL_SYNCOBJ_WAIT

        该 ioctl 用于等待普通 syncobj 变为已完成状态。

        libdrm http://www.devze.com封装为 drmSyncobjWait

        • 支持阻塞和非阻塞等待
        • 可批量等待多个同步对象
        int drmSyncobjWhttp://www.devze.comait(int fd, const uint32_t *handles, 
                        uint32_t count, uint64_t timeout_nsec,
                        uint32_t flags, uint32_t *first_signaled);
        • fd:DRM 设备文件描述符
        • handles:syncobj 句柄数组
        • count:数量
        • timeout_nsec:超时时间
        • flags:等待标志(如阻塞/非阻塞)
        • first_signaled:返回第一个完成的 syncobj 索引

        8. 应用示例

        应用场景:要使用DMA引擎给一个大buffer填充数据,即buffer填充任务;然后把该buffer交给GPU的渲染任务,渲染任务要等待填充任务完成后在进行。

        填充任务的执行是一个DMA引擎设备;渲染任务的执行是一个GPU设备。CPU负责给这两个设备发送任务和数据。涉及到跨设备、跨驱动的异步操作的同步问题。

        该场景的实现代码如下。

        • 用户态:
        uint32_t dma_syncobj;
        
        //创建drm_syncobj对象,获取句柄
        drmSyncobjCreate(fd, flags, &dma_syncobj);
        //将句柄随transfer任务传递给内核
        dma_transfer(bo, size, data, dma_syncobj);
        
        //其他准备工作
        .....
        
        //等待transfer的任务完成,即dma_syncobj同步对象被signal
        drmSyncobjWait(fd, &dma_syncobj, 1, ...);
        
        submit_render_jobs(fd, jobs);
        • 内核驱动:
        do_dma_transfer(bo, size, data, dma_syncobj)
        {
            //使用dma引擎传输数据到bo
            dma_transfer(bo, size, data);
        
            //signal dma_syncobj同步对象
            drm_syncobj_replace_fence(syn_obj, signaled_fence);
        }

        9. 总结

        drm_syncobj 作为 libdrm 用户空间的同步对象抽象,通过一系列 DRM_IOCTL_SYNCOBJ_* ioctl,为开发者提供了高效、灵活的 GPU 任务同步机制。其支持创建与销毁、导入与导出、信号与重置、时间线操作、等待与事件通知等多种操作,适用于多队列渲染、跨进程同步、异步任务调度等多种应用场景。

        以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程客栈(www.devze.com)。

        0

        精彩评论

        暂无评论...
        验证码 换一张
        取 消

        关注公众号