RGA(四)——API 地图与第一个程序

2 阅读20分钟

一、librga 的设计哲学

在记 API 之前,先理解 librga 为什么长成现在这样。官方给出了五条设计取向:

第一,接口定义参考 OpenCV / MATLAB 中常用的 2D 图形接口。 目的是减少二次开发的学习成本,所以它叫 IM2D API。

第二,加入 RGA query 查询功能。 目的是消除 RGA 硬件版本差异带来的兼容问题。查询内容包括版本信息、输入输出最大分辨率、图像格式支持情况。

第三,执行图像操作之前,需要对输入输出缓冲区做预处理。 调用 wrapbuffer_T 把图像信息填充到 rga_buffer_t 结构体,结构体里包含分辨率、格式、stride 等信息。

第四,提供 improcess 复合接口。 其他所有 API 都是基于它开发的,当你需要一次调用实现多种功能时用它。

第五,支持把单次无法完成的复合操作绑定为一个 RGA 图像任务,统一提交到驱动内逐个执行。这就是 Job 模式。


二、API 全景地图

2.1 信息查询类

API作用
querystring(name)返回当前 RGA 硬件能力与版本的字符串
imcheckHeader()校验头文件版本与 librga 版本差异
imcheck() / imcheck_composite()校验参数合法性 + 硬件是否支持;仅调试阶段使用

2.2 缓冲区预处理类

API作用
importbuffer_fd()把 dma-buf fd 导入 RGA 驱动,建立映射
importbuffer_virtualaddr()把虚拟地址导入 RGA 驱动
importbuffer_physicaladdr()把物理地址导入 RGA 驱动
releasebuffer_handle()解除映射与绑定,释放驱动内部资源
wrapbuffer_handle()把参数封装成统一的 rga_buffer_t
wrapbuffer_virtualaddr() / wrapbuffer_fd() / wrapbuffer_physicaladdr()直接封装,内部调 importbuffer

2.3 任务(Job)类

API作用
imbeginJob(flags)创建任务,返回 job_handle
imendJob(job, sync, acq_fd, *rel_fd)提交并执行,完成后自动删除任务资源
imcancelJob(job)取消并删除任务

2.4 单次操作类

每个操作都有同步版本、Task 版本,部分有 Array 版本。

API作用
imcopy快速拷贝
imresize / impyramid缩放 / 金字塔
imcrop裁剪
imtranslate平移
imcvtcolor格式 / 色域转换
imrotate旋转 90 / 180 / 270
imflip镜像翻转
imblend / imcomposite双 / 三通道合成
imcolorkey色键(抠图)
imosdOSD 字幕叠加
imquantizeNN 前处理量化(仅 RV1126/RV1109)
imrop光栅运算
imfill / imfillArray颜色填充
imrectangle / imrectangleArray画框
immakeBorder加边框
immosaic / immosaicArray马赛克
imgaussianBlur高斯模糊
impalette调色板
imcfaCFA 处理

2.5 复合与同步类

API作用
improcess万能复合接口
imsync(fence_fd)异步模式下等待操作完成
imconfig(name, value)配置当前线程默认上下文

2.6 命名规律

  • imxxx() —— 同步单次调用
  • imxxxTask() —— 往 job_handle 里追加操作,最后由 imendJob() 一次性提交
  • imxxxArray() —— 一次对多个矩形区域执行
  • importbuffer_T() / wrapbuffer_T() —— T 是类型占位:_fd / _virtualaddr / _physicaladdr

C 和 C++ 两套写法:im2d.h 是 C 接口(用宏实现可选参数);im2d.hpp 是 C++ 接口(用默认参数实现)。RK3588 上两种都能用,但建议用 C++ ,因为:

  • importbuffer_fd(fd, size) 这个简化版只在 C++ 下可用;C 下必须传 im_handle_param_t *
  • samples 里的 demo 都是 .cpp

三、核心概念一:实宽高 vs 虚宽高

调试日志里会反复看到两组缩写:

缩写含义对应字段
aw / ah(active)实宽实高 = 本次实际操作区域im_rect.width / im_rect.height
vw / vh(virtual)虚宽虚高 = 图像 buffer 本身大小rga_buffer_t.width / rga_buffer_t.height
xoff / yoff操作区域相对 buffer 原点的偏移im_rect.x / im_rect.y

约束是:(x + width) <= wstride 且 (y + height) <= hstride。

违反这条约束,就会看到经典的报错:

err ws[100,1280,1280]
Error srcRect

含义:err ws[x_offset, width, width_stride],X 方向偏移 + 实宽超过了虚宽。

修法:把虚宽改到 1380(= 100 + 1280),或把实宽改到 1180(= 1280 − 100)。


四、核心概念二:内存类型的性能排序

官方明确说明:不同的 buffer 类型调用 RGA 的性能是不同的。性能排序是:

physical address > fd > virtual address

一般推荐使用 fd 作为 buffer 类型。

内存类型实际耗时 / 理论耗时评价
物理地址1.1 ~ 1.2 倍最快,但用户态难以拿到
dma_fd(dma-buf)1.3 ~ 1.5 倍推荐:效率与易用性的最佳平衡点
虚拟地址1.8 ~ 2.1 倍,且受 CPU 负载影响大仅适合学习阶段 / 快速验证

为什么虚拟地址这么慢? 官方 FAQ 给了两条原因:页表转换由 CPU 算;每帧强制同步 cache。


五、第一个 RGA 程序:标准六步法

这是使用 RGA 的通用流程,所有操作都按这个骨架来。

┌─ 1. 分配 buffer  ─── dma_heap / DRM → 拿到 dma-buf fd
│
├─ 2. 导入驱动  ───── importbuffer_fd(fd, size) → rga_buffer_handle_t
│
├─ 3. 封装  ───────── wrapbuffer_handle(hnd, w, h, fmt[, wstride, hstride]) → rga_buffer_t
│
├─ 4. 【可选】校验 ── imcheck(src, dst, srect, drect)
│
├─ 5. 执行  ───────── imcopy / imresize / imcvtcolor / ...
│
└─ 6. 释放  ───────── releasebuffer_handle(hnd) + 释放自己的 buffer

5.1 官方最简 demo:rga_copy_demo.cpp

这是 samples 里最简单的 demo,用 malloc + virtualaddr,适合跑通六步法。

#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#include <linux/stddef.h>

#include "RgaUtils.h"
#include "im2d.hpp"
#include "utils.h"

#define LOCAL_FILE_PATH "/data"

int main() {
    int ret = 0;
    int src_width = 1280, src_height = 720, src_format = RK_FORMAT_RGBA_8888;
    int dst_width = 1280, dst_height = 720, dst_format = RK_FORMAT_RGBA_8888;
    char *src_buf, *dst_buf;
    int src_buf_size, dst_buf_size;

    rga_buffer_t src_img, dst_img;
    rga_buffer_handle_t src_handle, dst_handle;

    memset(&src_img, 0, sizeof(src_img));
    memset(&dst_img, 0, sizeof(dst_img));

    src_buf_size = src_width * src_height * get_bpp_from_format(src_format);
    dst_buf_size = dst_width * dst_height * get_bpp_from_format(dst_format);

    src_buf = (char *)malloc(src_buf_size);
    dst_buf = (char *)malloc(dst_buf_size);

    /* 填充测试数据 */
    if (0 != read_image_from_file(src_buf, LOCAL_FILE_PATH, src_width, src_height, src_format, 0)) {
        printf("src image read err\n");
        draw_rgba(src_buf, src_width, src_height);
    }
    memset(dst_buf, 0x80, dst_buf_size);

    /* Step 1+2: 导入 RGA 驱动,取得 handle */
    src_handle = importbuffer_virtualaddr(src_buf, src_buf_size);
    dst_handle = importbuffer_virtualaddr(dst_buf, dst_buf_size);
    if (src_handle == 0 || dst_handle == 0) {
        printf("importbuffer failed!\n");
        goto release_buffer;
    }

    /* Step 3: 封装成 rga_buffer_t */
    src_img = wrapbuffer_handle(src_handle, src_width, src_height, src_format);
    dst_img = wrapbuffer_handle(dst_handle, dst_width, dst_height, dst_format);

    /* Step 4: 校验 */
    ret = imcheck(src_img, dst_img, {}, {});
    if (IM_STATUS_NOERROR != ret) {
        printf("%d, check error! %s", __LINE__, imStrError((IM_STATUS)ret));
        return -1;
    }

    /* Step 5: 执行 */
    ret = imcopy(src_img, dst_img);
    if (ret == IM_STATUS_SUCCESS) {
        printf("rga_copy_demo running success!\n");
    } else {
        printf("rga_copy_demo running failed, %s\n", imStrError((IM_STATUS)ret));
        goto release_buffer;
    }

    printf("output [0x%x, 0x%x, 0x%x, 0x%x]\n", dst_buf[0], dst_buf[1], dst_buf[2], dst_buf[3]);
    write_image_to_file(dst_buf, LOCAL_FILE_PATH, dst_width, dst_height, dst_format, 0);

release_buffer:
    /* Step 6: 释放 */
    if (src_handle) releasebuffer_handle(src_handle);
    if (dst_handle) releasebuffer_handle(dst_handle);

    if (src_buf) free(src_buf);
    if (dst_buf) free(dst_buf);

    return ret;
}

5.2 官方推荐 demo:rga_allocator_dma_demo.cpp

用 dma_heap 分配非 cache 内存,性能比 virtualaddr 好,是推荐路径。

#include <iostream>
#include <fstream>
#include <sstream>
#include <cstddef>
#include <cmath>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <unistd.h>

#include "im2d.h"
#include "RgaUtils.h"
#include "utils.h"
#include "dma_alloc.h"

#define LOCAL_FILE_PATH "/data"

int main(void) {
    int ret = 0;
    int src_width = 1280, src_height = 720, src_format = RK_FORMAT_RGBA_8888;
    int dst_width = 1280, dst_height = 720, dst_format = RK_FORMAT_RGBA_8888;
    int src_buf_size, dst_buf_size;
    char *src_buf, *dst_buf;
    int src_dma_fd, dst_dma_fd;
    rga_buffer_t src = {}, dst = {};
    im_rect src_rect = {}, dst_rect = {};
    rga_buffer_handle_t src_handle, dst_handle;

    src_buf_size = src_width * src_height * get_bpp_from_format(src_format);
    dst_buf_size = dst_width * dst_height * get_bpp_from_format(dst_format);

    /* Step 1: 用 dma_heap 分配非 cache 的 dma-buf,返回 fd 和虚拟地址 */
    ret = dma_buf_alloc(DMA_HEAP_UNCACHE_PATH, src_buf_size, &src_dma_fd, (void **)&src_buf);
    if (ret < 0) { printf("alloc src failed!\n"); return -1; }
    ret = dma_buf_alloc(DMA_HEAP_UNCACHE_PATH, dst_buf_size, &dst_dma_fd, (void **)&dst_buf);
    if (ret < 0) { printf("alloc dst failed!\n"); dma_buf_free(src_buf_size, &src_dma_fd, src_buf); return -1; }

    if (0 != read_image_from_file(src_buf, LOCAL_FILE_PATH, src_width, src_height, src_format, 0)) {
        draw_rgba(src_buf, src_width, src_height);
    }
    memset(dst_buf, 0x33, dst_buf_size);

    /* Step 2: 导入 fd */
    src_handle = importbuffer_fd(src_dma_fd, src_buf_size);
    dst_handle = importbuffer_fd(dst_dma_fd, dst_buf_size);
    if (src_handle == 0 || dst_handle == 0) {
        printf("import dma_fd error!\n");
        ret = -1;
        goto free_buf;
    }

    /* Step 3: 封装 */
    src = wrapbuffer_handle(src_handle, src_width, src_height, src_format);
    dst = wrapbuffer_handle(dst_handle, dst_width, dst_height, dst_format);

    /* Step 4: 校验 */
    ret = imcheck(src, dst, src_rect, dst_rect);
    if (IM_STATUS_NOERROR != ret) {
        printf("%d, check error! %s", __LINE__, imStrError((IM_STATUS)ret));
        goto release_buffer;
    }

    /* Step 5: 执行 */
    ret = imcopy(src, dst);
    if (ret == IM_STATUS_SUCCESS) {
        printf("rga_allocator_dma_demo running success!\n");
    } else {
        printf("rga_allocator_dma_demo running failed, %s\n", imStrError((IM_STATUS)ret));
        goto release_buffer;
    }

    printf("output [0x%x, 0x%x, 0x%x, 0x%x]\n", dst_buf[0], dst_buf[1], dst_buf[2], dst_buf[3]);
    write_image_to_file(dst_buf, LOCAL_FILE_PATH, dst_width, dst_height, dst_format, 0);

release_buffer:
    /* Step 6: 释放 */
    if (src_handle > 0) releasebuffer_handle(src_handle);
    if (dst_handle > 0) releasebuffer_handle(dst_handle);

free_buf:
    dma_buf_free(src_buf_size, &src_dma_fd, src_buf);
    dma_buf_free(dst_buf_size, &dst_dma_fd, dst_buf);

    return 0;
}

dma_alloc.h 里定义的分配器路径:

#define DMA_HEAP_UNCACHE_PATH           "/dev/dma_heap/system-uncached"
#define DMA_HEAP_PATH                   "/dev/dma_heap/system"
#define DMA_HEAP_DMA32_UNCACHED_PATH    "/dev/dma_heap/system-uncached-dma32"
#define DMA_HEAP_DMA32_PATH             "/dev/dma_heap/system-dma32"
#define CMA_HEAP_UNCACHED_PATH          "/dev/dma_heap/cma-uncached"
#define RV1106_CMA_HEAP_PATH            "/dev/rk_dma_heap/rk-dma-heap-cma"

四个路径的选择:

路径什么时候用
system-uncached推荐,非 cache,性能最好
system默认,cacheable,需要手动同步 cache
system-uncached-dma32Color Fill / ROP / colorkey 等 RGA2 特有功能必须用这个(4G 内)
system-dma324G 内的 cacheable 内存

5.3 关于“缩放 + 换格式”能否一步做完

imresize 要求 dst 的格式与 src 相同。所以“NV12 → RGB888 同时缩放”这类组合需求,官方方案有两个:

两步走:先 imresize 到中间 NV12,再 imcvtcolor 转 RGB888。

用 improcess() 一步完成:它是所有其他 API 的基础。


六、逐个操作:参数与坑位速查

6.1 imcopy —— 拷贝

IM_STATUS imcopy(const rga_buffer_t src, rga_buffer_t dst,
                 int sync = 1, int *release_fence_fd = NULL);

最简单的操作,用于验证六步法流程是否通。对应 demo:rga_copy_demo.cpp。

6.2 imresize —— 缩放

IM_STATUS imresize(const rga_buffer_t src, rga_buffer_t dst,
                   double fx = 0, double fy = 0,
                   int interpolation = IM_INTERP_DEFAULT,
                   int sync = 1, int *release_fence_fd = NULL);

要点:

  • 不传 fx/fy 时,自动按 dst.width / src.width 和 dst.height / src.height 缩放
  • 倍率限制:RK3588 上 RGA2 支持 1/16 ~ 16,RGA3 只支持 1/8 ~ 8。超过 8 倍需要走 RGA2
  • 坑:用 fx/fy 倍率缩放时,YUV 等有宽高对齐要求的格式会强制向下对齐,可能改变预期缩放效果

对应 demo:

  • rga_resize_demo.cpp:基础缩放 720p → 1080p
  • rga_resize_rect_demo.cpp:用 improcess 把源图缩放到目标图的四个区域(拼接效果)
  • rga_resize_uv_downsampling_demo.cpp:YUV422 → YUV420 均值降采样

rga_resize_rect_demo.cpp 的用法值得记:

dst_rect[0] = {0,          0,          dst_width/2, dst_height/2};
dst_rect[1] = {dst_width/2, 0,          dst_width/2, dst_height/2};
dst_rect[2] = {0,          dst_height/2, dst_width/2, dst_height/2};
dst_rect[3] = {dst_width/2, dst_height/2, dst_width/2, dst_height/2};

for (int i = 0; i < 4; i++) {
    ret = imcheck(src_img, dst_img, {}, dst_rect[i]);
    if (IM_STATUS_NOERROR != ret) { /* ... */ }

    ret = improcess(src_img, dst_img, {}, {}, dst_rect[i], {}, IM_SYNC);
}

6.3 imcrop —— 裁剪

IM_STATUS imcrop(const rga_buffer_t src, rga_buffer_t dst,
                 im_rect rect, int sync = 1, int *release_fence_fd = NULL);

rect 指定裁剪区域,x、y、width、height 全部 ≥ 2。

对应 demo:rga_crop_demo.cpp、rga_crop_rect_demo.cpp。

6.4 imtranslate —— 平移

IM_STATUS imtranslate(const rga_buffer_t src, rga_buffer_t dst,
                      int x, int y, int sync = 1, int *release_fence_fd = NULL);

注意:src 和 dst 宽高必须一致,超出部分会被裁剪。

6.5 imcvtcolor —— 格式与色域转换

IM_STATUS imcvtcolor(rga_buffer_t src, rga_buffer_t dst,
                     int sfmt, int dfmt,
                     int mode = IM_COLOR_SPACE_DEFAULT,
                     int sync = 1, int *release_fence_fd = NULL);

可选的 mode(来自 im2d_type.h):

IM_YUV_TO_RGB_BT601_LIMIT   = 1 << 0
IM_YUV_TO_RGB_BT601_FULL    = 2 << 0
IM_YUV_TO_RGB_BT709_LIMIT   = 3 << 0

IM_RGB_TO_YUV_BT601_FULL    = 1 << 2
IM_RGB_TO_YUV_BT601_LIMIT   = 2 << 2
IM_RGB_TO_YUV_BT709_LIMIT   = 3 << 2

/* 也可以直接用这些: */
IM_RGB_FULL_RANGE           = IM_RGB_FULL
IM_YUV_BT601_LIMIT_RANGE
IM_YUV_BT601_FULL_RANGE
IM_YUV_BT709_LIMIT_RANGE
IM_YUV_BT2020_LIMIT_RANGE   /* 只有 RGA3 支持 BT.2020 */
IM_COLOR_SPACE_DEFAULT      = 0

音视频方向的“头号色偏坑”:

  • 新版 librga 默认:RGB2YUV 与 YUV2RGB 都是 BT.601-limit_range
  • 旧版 librga 默认:RGB2YUV = BT.601-full_range,YUV2RGB = BT.709-limit_range
  • 新版 librga 配了旧驱动 → 严重色偏(偏粉 / 偏绿)

建议:不要依赖默认值,显式传 mode,并与源数据的 range 保持一致。

三种官方 demo 的写法:

写法一:不传 mode,用默认(rga_cvtcolor_demo.cpp)

ret = imcvtcolor(src_img, dst_img, src_format, dst_format);

写法二:用 imsetColorSpace 单独设(rga_cvtcolor_csc_demo.cpp)

imsetColorSpace(&src_img, IM_RGB_FULL_RANGE);
imsetColorSpace(&dst_img, IM_YUV_BT709_LIMIT_RANGE);

ret = imcvtcolor(src_img, dst_img, src_format, dst_format);

写法三:直接传 mode(rga_cvtcolor_gray256_demo.cpp)

/* RGB → Y400 灰度图,必须用 full range,否则 256 阶灰阶会被裁掉 */
ret = imcvtcolor(src_img, dst_img, src_format, dst_format, IM_RGB_TO_YUV_BT601_FULL);

6.6 imrotate / imflip —— 旋转与镜像

IM_STATUS imrotate(const rga_buffer_t src, rga_buffer_t dst,
                   int rotation, int sync = 1, int *release_fence_fd = NULL);
/* rotation: IM_HAL_TRANSFORM_ROT_90 / _180 / _270 */

IM_STATUS imflip(const rga_buffer_t src, rga_buffer_t dst,
                 int mode, int sync = 1, int *release_fence_fd = NULL);
/* mode: IM_HAL_TRANSFORM_FLIP_H / _V / _H_V */

旋转 90/270 必须交换宽高,否则图像会被拉伸。rgaImDemo.cpp 里的写法是:

if (IM_HAL_TRANSFORM_ROT_90 == rotate || IM_HAL_TRANSFORM_ROT_270 == rotate) {
    dst.width   = src.height;
    dst.height  = src.width;
    dst.wstride = src.hstride;
    dst.hstride = src.wstride;
}

对应 demo:

  • rga_transform_rotate_demo.cpp:旋转
  • rga_transform_flip_demo.cpp:镜像
  • rga_transform_rotate_flip_demo.cpp:旋转 + 镜像

6.7 合成:imblend / imcomposite

/* A + B → B:前景 srcA 与背景 dst 混合,结果输出回 dst */
IM_STATUS imblend(const rga_buffer_t fg, rga_buffer_t bg,
                  int mode = IM_ALPHA_BLEND_SRC_OVER,
                  int sync = 1, int *release_fence_fd = NULL);

/* A + B → C:前景 srcA + 背景 srcB → 输出 dst */
IM_STATUS imcomposite(const rga_buffer_t srcA, const rga_buffer_t srcB,
                      rga_buffer_t dst,
                      int mode = IM_ALPHA_BLEND_SRC_OVER,
                      int sync = 1, int *release_fence_fd = NULL);

核心公式:

Rc = Sc × Sf + Dc × Df
Ra = Sa × Sf + Da × Df

可选的 mode(来自 im2d_type.h):

IM_ALPHA_BLEND_SRC_OVER   = 1 << 6   /* 默认 */
IM_ALPHA_BLEND_SRC        = 1 << 7
IM_ALPHA_BLEND_DST        = 1 << 8
IM_ALPHA_BLEND_SRC_IN     = 1 << 9
IM_ALPHA_BLEND_DST_IN     = 1 << 10
IM_ALPHA_BLEND_SRC_OUT    = 1 << 11
IM_ALPHA_BLEND_DST_OUT    = 1 << 12
IM_ALPHA_BLEND_DST_OVER   = 1 << 13
IM_ALPHA_BLEND_SRC_ATOP   = 1 << 14
IM_ALPHA_BLEND_DST_ATOP   = 1 << 15
IM_ALPHA_BLEND_XOR        = 1 << 16

两个最常见的“没效果”问题:

  • 叠加看不出效果:检查输入两张图的 alpha 是否都是 0xFF。前景 alpha = 0xFF 时结果就是前景直接覆盖背景
  • 前景 alpha = 0x0 却不是全透明:因为颜色值没有预乘 alpha,需要加 IM_ALPHA_BLEND_PRE_MUL 标志

官方 demo 里的写法(rga_alpha_demo.cpp):

ret = imblend(fg_img, bg_img,
              IM_ALPHA_BLEND_SRC_OVER | IM_ALPHA_BLEND_PRE_MUL);

合成不支持 YUV ↔ YUV:imblend 的 dst 不支持 YUV,imcomposite 的 srcB 不支持 YUV。

对应 demo:

  • rga_alpha_demo.cpp:双通道合成,RGB + RGB
  • rga_alpha_3channel_demo.cpp:三通道合成,A + B → C
  • rga_alpha_global_alpha_demo.cpp:用 imsetOpacity() 设置全局 alpha
  • rga_alpha_colorkey_demo.cpp:色键抠图
  • rga_alpha_yuv_demo.cpp:YUV 上叠 RGB(重点)
  • rga_alpha_osd_demo.cpp:OSD 字幕叠加(重点)

6.8 叠字幕(OSD):rga_alpha_yuv_demo.cpp

输出格式用什么模式
RGBimblend()src over
YUVimcomposite()dst over

为什么不一样? RGA2 有三个图像通道:src、src1/pat、dst。src 支持 YUV2RGB,src1/pat 和 dst 只支持 RGB2YUV。而 RGA 内部的叠加必须在 RGB 域进行。所以 RGB 叠加到 YUV 上,唯一路径是:src(YUV,作背景)→ YUV2RGB → RGB 域叠加 → dst 通道 RGB2YUV → 输出 YUV。

rga_alpha_yuv_demo.cpp 用 improcess 一步完成:

usage = IM_SYNC | IM_ALPHA_BLEND_DST_OVER | IM_ALPHA_BLEND_PRE_MUL;

ret = improcess(fg_img, output_img, bg_img,
                fg_rect, output_rect, bg_rect,
                -1, NULL, NULL, usage);

6.9 其它常用操作

API要点对应 demo
imcolorkey将符合色键过滤条件的像素 alpha 置零后与目标做 alpha 混合。颜色范围排列为 ABGRrga_alpha_colorkey_demo.cpp
imropIM_ROP_AND / OR / NOT_DST / NOT_SRC / XOR / NOT_XORrop_demo
imfillcolor 按 RGBA 排列,由高到低位依次是 A、B、G、R。填充区域 rect 宽高须 ≥ 2rga_fill_demo.cpp
imrectanglerect 描述边框外径,thickness 为线宽;负数(如 −1)表示画实心矩形rga_fill_rectangle_demo.cpp
imrectangleArray一次画多个矩形框rga_fill_rectangle_array_demo.cpp
immakeBorderIM_BORDER_CONSTANT / REFLECT / WRAPrga_padding_demo.cpp
immosaicIM_MOSAIC_8/16/32/64/128(RK3588 硬件不支持)mosaic_demo
imquantize仅 RV1126 / RV1109—
imgaussianBlur高斯模糊,kernel 宽高必须为正奇数—
impalette调色板转换—
imcfaCFA(Color Filter Array)处理—

七、任务模式(Job):把多个操作打包成一次提交

硬件一次只能顺序工作一个任务。任务模式让你把多个操作攒起来,一次提交到驱动,由硬件连续执行。

官方 demo 写法(rga_fill_rectangle_task_demo.cpp):

im_job_handle_t job_handle;

/* 1. 创建任务 */
job_handle = imbeginJob();
if (job_handle <= 0) {
    printf("job begin failed![%d], %s\n", job_handle, imStrError());
    goto release_buffer;
}

/* 2. 添加第一个任务:填充绿色实心矩形 */
dst_rect[0] = {0, 0, 300, 200};

ret = imcheck({}, dst, {}, dst_rect[0], IM_COLOR_FILL);
if (IM_STATUS_NOERROR != ret) {
    printf("%d, check error! %s", __LINE__, imStrError((IM_STATUS)ret));
    imcancelJob(job_handle);       /* 失败必须取消 */
    goto release_buffer;
}

ret = imrectangleTask(job_handle, dst, dst_rect[0], 0xff00ff00, -1);
if (ret != IM_STATUS_SUCCESS) {
    printf("add fill task failed, %s\n", imStrError((IM_STATUS)ret));
    imcancelJob(job_handle);
    goto release_buffer;
}

/* 3. 添加第二个任务:画红色空心矩形 */
dst_rect[0] = {100, 100, 300, 200};

ret = imcheck({}, dst, {}, dst_rect[0], IM_COLOR_FILL);
if (IM_STATUS_NOERROR != ret) {
    imcancelJob(job_handle);
    goto release_buffer;
}

ret = imrectangleTask(job_handle, dst, dst_rect[0], 0xffff0000, 4);
if (ret != IM_STATUS_SUCCESS) {
    imcancelJob(job_handle);
    goto release_buffer;
}

/* 4. 一次性提交并执行(默认 IM_SYNC) */
ret = imendJob(job_handle);
if (ret == IM_STATUS_SUCCESS) {
    printf("job[%d] running success!\n", job_handle);
} else {
    printf("job[%d] running failed, %s\n", job_handle, imStrError((IM_STATUS)ret));
    goto release_buffer;
}

Job 句柄一定要收尾。 官方明确:配置失败后须使用 imcancelJob 释放当前任务句柄,避免内存泄漏。imendJob 成功后会自动删除任务资源;但失败或中途放弃时必须手动 imcancelJob。上面每个 imcheck 失败都调 imcancelJob,就是这个原则。

对应 demo:

  • rga_copy_splice_task_demo.cpp:用 task 做图像拼接
  • rga_fill_rectangle_task_demo.cpp:用 task 画两个矩形
  • rga_fill_rectangle_task_array_demo.cpp:用 task + array 画两组矩形

八、improcess:万能底层接口

其他所有 API 都是基于 improcess 开发的。当你需要一次调用实现多种功能时,用它。

IM_STATUS improcess(rga_buffer_t src, rga_buffer_t dst, rga_buffer_t pat,
                   im_rect srect, im_rect drect, im_rect prect,
                   int acquire_fence_fd, int *release_fence_fd,
                   im_opt_t *opt_ptr, int usage);

C 接口还有一个简化版(im2d_single.h):

IM_C_API IM_STATUS improcess(rga_buffer_t src, rga_buffer_t dst, rga_buffer_t pat,
                             im_rect srect, im_rect drect, im_rect prect, int usage);

usage 位域(来自 im2d_type.h):

/* 旋转 */
IM_HAL_TRANSFORM_ROT_90     = 1 << 0
IM_HAL_TRANSFORM_ROT_180    = 1 << 1
IM_HAL_TRANSFORM_ROT_270    = 1 << 2
IM_HAL_TRANSFORM_FLIP_H     = 1 << 3
IM_HAL_TRANSFORM_FLIP_V     = 1 << 4
IM_HAL_TRANSFORM_FLIP_H_V   = 1 << 5
IM_HAL_TRANSFORM_MASK       = 0x3f

/* 混合 */
IM_ALPHA_BLEND_SRC_OVER     = 1 << 6      /* 默认 */
IM_ALPHA_BLEND_SRC          = 1 << 7
IM_ALPHA_BLEND_DST          = 1 << 8
IM_ALPHA_BLEND_SRC_IN       = 1 << 9
IM_ALPHA_BLEND_DST_IN       = 1 << 10
IM_ALPHA_BLEND_SRC_OUT      = 1 << 11
IM_ALPHA_BLEND_DST_OUT      = 1 << 12
IM_ALPHA_BLEND_DST_OVER     = 1 << 13
IM_ALPHA_BLEND_SRC_ATOP     = 1 << 14
IM_ALPHA_BLEND_DST_ATOP     = 1 << 15
IM_ALPHA_BLEND_XOR          = 1 << 16
IM_ALPHA_BLEND_MASK         = 0x1ffc0

IM_ALPHA_COLORKEY_NORMAL    = 1 << 17
IM_ALPHA_COLORKEY_INVERTED  = 1 << 18
IM_ALPHA_COLORKEY_MASK      = 0x60000

IM_SYNC                     = 1 << 19
IM_CROP                     = 1 << 20      /* Unused */
IM_COLOR_FILL               = 1 << 21
IM_COLOR_PALETTE            = 1 << 22
IM_NN_QUANTIZE              = 1 << 23
IM_ROP                      = 1 << 24
IM_ALPHA_BLEND_PRE_MUL      = 1 << 25
IM_ASYNC                    = 1 << 26
IM_MOSAIC                   = 1 << 27
IM_OSD                      = 1 << 28
IM_PRE_INTR                 = 1 << 29
IM_ALPHA_BIT_MAP            = 1 << 30
IM_GAUSS                    = 1 << 31

用法建议:先用封装好的 imresize / imblend 等跑通语义,再用 improcess 合并优化。

rga_resize_rect_demo.cpp 用 improcess 做缩放 + 拼接:

ret = improcess(src_img, dst_img, {}, {}, dst_rect[i], {}, IM_SYNC);

rga_alpha_yuv_demo.cpp 用 improcess 做 YUV 上的 dst-over 合成:

usage = IM_SYNC | IM_ALPHA_BLEND_DST_OVER | IM_ALPHA_BLEND_PRE_MUL;

ret = improcess(fg_img, output_img, bg_img,
                fg_rect, output_rect, bg_rect,
                -1, NULL, NULL, usage);

rga_config_single_core_demo.cpp 用 improcess + im_opt_t 指定核心:

im_opt_t opt = {};
opt.core = IM_SCHEDULER_RGA3_CORE0;

ret = improcess(src_img, dst_img, {}, {}, {}, {}, -1, NULL, &opt, IM_SYNC);

九、异步模式与 fence

把 API 的 sync 形参设为 0 即进入异步调用模式:效果相当于 OpenGL 中的 glFlush;再调用 imsync(release_fence_fd) 则达到 glFinish 的效果。

官方 demo 写法(rga_async_demo.cpp):

int acquire_fence_fd, release_fence_fd;

/* (1) 异步拷贝 src → tmp,拿到 release_fence */
release_fence_fd = -1;
ret = imcopy(src_img, tmp_img, 0, &release_fence_fd);

/* (2) 用 (1) 的 release_fence 作为 (2) 的 acquire_fence */
acquire_fence_fd = release_fence_fd;
release_fence_fd = -1;

ret = improcess(tmp_img, dst_img, {}, {}, {}, {},
                acquire_fence_fd, &release_fence_fd, NULL, IM_ASYNC);

/* (3) 等待最后一次操作的 release_fence */
ret = imsync(release_fence_fd);

注意:RK3588 用的是 multicore 驱动,有 fence 机制,可以放心用异步。旧驱动(RGA Device Driver、RGA2 Device Driver)上 imsync() 会等该进程所有异步任务,反而可能更慢。


十、多核调度:指定核心

RK3588 上有三颗 RGA 核(1 颗 RGA2 + 2 颗 RGA3),librga 默认会自动分配。某些场景(比如规避 RGA2/RGA3 缩放算法差异导致的画面抖动)需要指定核心。

10.1 线程级配置

官方 demo(rga_config_thread_core_demo.cpp):

/* 配置当前线程只用 RGA3 core0 或 core1 */
imconfig(IM_CONFIG_SCHEDULER_CORE,
         IM_SCHEDULER_RGA3_CORE0 | IM_SCHEDULER_RGA3_CORE1);

ret = imcopy(src_img, dst_img);

可用的核心值(来自 im2d_type.h):

IM_SCHEDULER_RGA3_CORE0     = 1 << 0
IM_SCHEDULER_RGA3_CORE1     = 1 << 1
IM_SCHEDULER_RGA2_CORE0     = 1 << 2
IM_SCHEDULER_RGA2_CORE1     = 1 << 3
IM_SCHEDULER_RGA3_DEFAULT   = IM_SCHEDULER_RGA3_CORE0
IM_SCHEDULER_RGA2_DEFAULT   = IM_SCHEDULER_RGA2_CORE0
IM_SCHEDULER_MASK           = 0xf
IM_SCHEDULER_DEFAULT        = 0

10.2 单任务指定

官方 demo(rga_config_single_core_demo.cpp):

im_opt_t opt = {};
opt.core = IM_SCHEDULER_RGA3_CORE0;

ret = improcess(src_img, dst_img, {}, {}, {}, {}, -1, NULL, &opt, IM_SYNC);

使用警告:官方反复强调,priority、core 权限极高,操作不当可能导致系统崩溃或死锁,建议仅用于开发调试阶段,极度不建议在实际产品场景进行配置。唯一的例外是明确必要且经过验证的场景。


十一、RK3588 上哪些 API 受硬件限制

虽然 librga 1.10.0 把所有 API 都实现了,但 RK3588 的硬件对部分 API 有限制:

API / 功能限制demo 里的对策
imresize 倍率 > 8RGA3 只支持 1/8~8,需回退到 RGA2—
imquantizeRK3588 硬件不支持(仅 RV1126/RV1109)—
immosaicRK3588 硬件不支持—
imosd官方标注仅部分平台支持—
imcolorkey / imrop / Color Fill仅 RGA2 支持。任务被分到 RGA3 会失败demo 里用 DMA_HEAP_DMA32_UNCACHED_PATH 分配 4G 内内存
imblend 的 dst不支持 YUV 格式rga_alpha_yuv_demo.cpp 用 imcomposite
imcomposite 的 srcB不支持 YUV 格式同上

Color Fill 的场景要特别注意:rga_fill_demo.cpp、rga_fill_rectangle_demo.cpp、rga_fill_rectangle_task_demo.cpp、rga_fill_rectangle_task_array_demo.cpp 这几个 demo 都用了 DMA_HEAP_DMA32_UNCACHED_PATH,也就是专门分配 4G 以内的内存。因为 Color Fill 是 RGA2 特有的功能,而 RGA2 的 IOMMU 只支持 32bit。这个坑第五篇会详细展开。

代码里的注释也写明了这一点:

/*
 * Allocate dma_buf within 4G from dma32_heap,
 * return dma_fd and virtual address.
 * ColorFill can only be used on buffers within 4G.
 */
ret = dma_buf_alloc(DMA_HEAP_DMA32_UNCACHED_PATH, dst_buf_size, &dst_dma_fd, (void **)&dst_buf);

十二、本篇小结

第一,六步法:分配 dma-buf fd → importbuffer_fd → wrapbuffer_handle → imcheck(调试期)→ 执行 → releasebuffer_handle。

第二,实宽高(aw/ah)vs 虚宽高(vw/vh):x+w ≤ wstride、y+h ≤ hstride,YUV 还要 2 对齐。

第三,内存类型性能:phy > fd > va,用 fd;分配时禁用 cache。dma_heap 的四个路径各有用途,Color Fill 必须用 system-uncached-dma32。

第四,旋转 90/270 必须交换宽高;imresize 要求 src/dst 格式一致。

第五,色域显式指定 mode;默认 BT.601 limit range。也可以用 imsetColorSpace() 单独设。

第六,YUV 上叠图用 imcomposite(dst over),RGB 上用 imblend(src over)。

第七,Job 失败必须 imcancelJob。

第八,RK3588 上 Color Fill / ROP / colorkey 只有 RGA2 能做,需要 4G 以内的内存。demo 里用 DMA_HEAP_DMA32_UNCACHED_PATH 就是这个原因。