Linux?libdrm中drm_syncobj的實現(xiàn)原理
1. 前言
在博文:Linux drm_syncobj 機制原理與應用中介紹了內核drm_syncobj機制的相關原理,本篇是用戶態(tài)libdrm中的syncobj的API實現(xiàn)分析。
syncobj 的設計初衷是為用戶空間提供一種高效、靈活的 GPU 任務同步原語。
它具備如下特點:
- 對象化管理:同步狀態(tài)以對象(syncobj)形式存在,用戶空間可通過句柄引用和操作。
- 多次信號/等待:支持多次信號和等待,適合異步和多階段任務。
- 跨進程同步:支持同步對象的導入/導出,實現(xiàn)進程間同步。
- timeline 擴展:部分驅動支持 timeline syncobj,可表示多個時間點的同步狀態(tài)。
傳統(tǒng) fence 僅能表示單次同步事件,且生命周期受限。而 syncobj 支持多次信號/等待,且可導入/導出,適合復雜的同步場景。timeline syncobj 更是將同步擴展到多時間點,極大提升了靈活性。
2. syncobj 相關 IOCTLs概覽與典型流程
syncobj 的所有操作都通過 ioctl 與內核 DRM 驅動交互。
和內核實現(xiàn)一文中的維度劃分保持一致,把ioctl劃分如下。
| 維度 | IOCTL 名稱 | 功能描述 |
|---|---|---|
| 創(chuàng)建與銷毀 | DRM_IOCTL_SYNCOBJ_CREATE | 創(chuàng)建同步對象,分配資源并返回句柄 |
| DRM_IOCTL_SYNCOBJ_DESTROY | 銷毀同步對象,釋放資源 | |
| 導入與導出 | DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD | 將同步對象句柄導出為文件描述符,實現(xiàn)跨進程同步 |
| DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE | 從文件描述符導入同步對象句柄,實現(xiàn)跨進程同步 | |
| 信號與重置 | DRM_IOCTL_SYNCOBJ_SIGNAL | 將同步對象設為已完成(signaled)狀態(tài) |
| DRM_IOCTL_SYNCOBJ_RESET | 將同步對象設為未完成(unsignaled)狀態(tài) | |
| 時間線操作 | DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL | 對 timeline syncobj 的指定時間點進行信號 |
| DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT | 等待 timeline syncobj 的指定時間點完成 | |
| DRM_IOCTL_SYNCOBJ_TIMELINE_QUERY | 查詢 timeline syncobj 當前時間點狀態(tài) | |
| 等待與事件通知 | DRM_IOCTL_SYNCOBJ_WAIT | 等待同步對象變?yōu)橐淹瓿蔂顟B(tài),支持阻塞/非阻塞及超時 |
| DRM_IOCTL_SYNCOBJ_QUERY | 查詢同步對象的當前狀態(tài) |
典型流程:
- 創(chuàng)建 syncobj:通過
drmSyncobjCreate創(chuàng)建同步對象,獲得句柄。將該句柄隨任務傳遞給內核驅動。 - 導入/導出:如需跨進程同步,通過
drmSyncobjHandleToFd和drmSyncobjFdToHandle實現(xiàn)句柄與 fd 的轉換。 - 信號/重置:根據任務完成情況,調用
drmSyncobjSignal或drmSyncobjReset設置同步對象狀態(tài)。 - timeline 操作:如需多時間點同步,使用 timeline 相關 API 進行信號、等待和查詢。
- 等待/通知:通過
drmSyncobjWait或drmSyncobjTimelineWait等待任務完成(任務完成后,會signal該任務關聯(lián)的步驟1的同步對象),驅動后續(xù)處理。 - 銷毀:任務完成后,調用
drmSyncobjDestroy釋放資源。
3. 創(chuàng)建與銷毀
3.1 創(chuàng)建同步對象
3.1.1 DRM_IOCTL_SYNCOBJ_CREATE
該 ioctl 用于在內核中分配一個同步對象,并返回其句柄。
用戶空間通過 libdrm 的 drmSyncobjCreate API 調用。
- 支持普通 syncobj 和 timeline syncobj 創(chuàng)建
- 句柄在用戶空間唯一標識同步對象
- 可指定初始狀態(tài)(已信號/未信號)
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(初始為已信號狀態(tài))、DRM_SYNCOBJ_CREATE_TIMELINE(創(chuàng)建 timeline syncobj)handle:返回的同步對象句柄
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);
}fd:DRM 設備文件描述符handle:要銷毀的同步對象句柄
后續(xù)的API我不再給出具體實現(xiàn),感興趣的朋友請查看源碼。
4. 導入與導出
4.1 導出同步對象到文件描述符
4.1.1 DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD
該 ioctl 用于將 syncobj 句柄導出為文件描述符,實現(xiàn)跨進程同步。
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)狀態(tài)。
libdrm 封裝為 drmSyncobjSignal。
int drmSyncobjSignal(int fd, const uint32_t *handles, uint32_t count);
5.2 重置同步對象
5.2.1 DRM_IOCTL_SYNCOBJ_RESET
該 ioctl 用于將同步對象設為未完成(unsignaled)狀態(tài)。
libdrm 封裝為 drmSyncobjReset。
int drmSyncobjReset(int fd, const uint32_t *handles, uint32_t count);
6. 時間線操作
這個高級用法,我還沒有在項目中用到過,有用例的朋友歡迎分享。
6.1 timeline syncobj 簡介
timeline syncobj 是對傳統(tǒng)同步對象的擴展,允許一個 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 變?yōu)橐淹瓿蔂顟B(tài)。
libdrm 封裝為 drmSyncobjWait。
- 支持阻塞和非阻塞等待
- 可批量等待多個同步對象
int drmSyncobjWait(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的渲染任務,渲染任務要等待填充任務完成后在進行。
填充任務的執(zhí)行是一個DMA引擎設備;渲染任務的執(zhí)行是一個GPU設備。CPU負責給這兩個設備發(fā)送任務和數據。涉及到跨設備、跨驅動的異步操作的同步問題。
該場景的實現(xiàn)代碼如下。
- 用戶態(tài):
uint32_t dma_syncobj; //創(chuàng)建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引擎?zhèn)鬏敂祿絙o
dma_transfer(bo, size, data);
//signal dma_syncobj同步對象
drm_syncobj_replace_fence(syn_obj, signaled_fence);
}9. 總結
drm_syncobj 作為 libdrm 用戶空間的同步對象抽象,通過一系列 DRM_IOCTL_SYNCOBJ_* ioctl,為開發(fā)者提供了高效、靈活的 GPU 任務同步機制。其支持創(chuàng)建與銷毀、導入與導出、信號與重置、時間線操作、等待與事件通知等多種操作,適用于多隊列渲染、跨進程同步、異步任務調度等多種應用場景。
以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
Centos7遠程桌面 vnc/vnc-server的設置詳解
這篇文章主要介紹了Centos7遠程桌面 vnc/vnc-server的設置詳解的相關資料,需要的朋友可以參考下2016-10-10
Linux crontab定時任務執(zhí)行失敗處理方案
這篇文章主要介紹了Linux crontab定時任務執(zhí)行失敗處理方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-06-06

