Linux

关注公众号 jb51net

关闭
首页 > 网站技巧 > 服务器 > Linux > Linux libdrm中drm_syncobj的原理

Linux libdrm中drm_syncobj的实现原理

作者:DeeplyMind

本文分析libdrm中drm_syncobj的API实现,涵盖创建销毁、跨进程同步、信号重置及时间线操作,支持多阶段异步任务同步,适用于DMA与GPU渲染等复杂场景,提升同步灵活性与效率

1. 前言

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

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

它具备如下特点:

传统 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查询同步对象的当前状态

典型流程:

3. 创建与销毁

3.1 创建同步对象

3.1.1 DRM_IOCTL_SYNCOBJ_CREATE

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

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

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;
}

3.2 销毁同步对象

3.2.1 DRM_IOCTL_SYNCOBJ_DESTROY

该 ioctl 用于释放同步对象资源。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);
}

后续的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);

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);

等待与事件通知

7.1 普通等待

7.1.1 DRM_IOCTL_SYNCOBJ_WAIT

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

libdrm 封装为 drmSyncobjWait

int drmSyncobjWait(int fd, const uint32_t *handles, 
                uint32_t count, uint64_t timeout_nsec,
                uint32_t flags, uint32_t *first_signaled);

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 任务同步机制。其支持创建与销毁、导入与导出、信号与重置、时间线操作、等待与事件通知等多种操作,适用于多队列渲染、跨进程同步、异步任务调度等多种应用场景。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:
阅读全文