RGA 2D 图形加速
本章讲 RK182X RGA(Raster Graphic Acceleration)硬件 2D 引擎的使用方法。
整体框图
摄像头 (V4L2)
│ dma_fd
↓
RGA 格式转换(NV12 → RGB)
│ 同一 fd 或新 fd
↓
NPU 推理(RKNN)
│ 结果
↓
RGA 合成(叠加检测框)
↓
编码器(MPP)/ 显示器RGA 是 Host SoC(如 RK3588)内置的硬件 2D 图形引擎。RK182X 协处理器本身不带 RGA,需通过 PCIe 借助 Host SoC 的 RGA 工作,可高效完成图像格式转换、缩放、旋转、裁剪、合成等操作。
1. RGA 功能能力
RGA 硬件 2D 引擎支持六大操作:格式转换、缩放、旋转、裁剪、多图层合成、纯色 / 渐变填充。
| 操作 | 说明 | 典型用途 |
|---|---|---|
| 格式转换 | NV12↔RGB / YUV↔RGB / 任意格式互转 | 摄像头→NPU 输入预处理 |
| 缩放 | 任意比例缩放(1/8x ~ 8x) | 分辨率适配、缩略图 |
| 旋转 | 0° / 90° / 180° / 270° / 镜像 | 竖屏摄像头校正 |
| 裁剪 | 任意矩形区域提取 | ROI 提取 |
| 合成 | 多图层 Alpha 混合叠加 | OSD 水印、画中画 |
| 填充 | 纯色 / 渐变填充 | 背景填充 |
2. RGA 初始化
调用 c_RkRgaInit() 初始化 RGA 设备,返回 0 成功,<0 失败(检查 /dev/rga* 是否存在)。
实测:
/dev/rga存在(Host SoC RK3588 内置 RGA),RGA 设备可用。
#include <rga/RgaApi.h>
#include <stdio.h>
int ret = c_RkRgaInit();
if (ret < 0) {
printf("RGA init failed: %d\n", ret);
return -1;
}
printf("RGA initialized.\n");3. NV12 → RGB888 格式转换
填充 rga_info_t src(fd + NV12 格式 + rect)+ rga_info_t dst(fd + RGB888 格式 + rect),调用 c_RkRgaBlit(&src, &dst, NULL) 执行转换。
#include <rga/RgaApi.h>
#include <rga/drmrga.h> /* rga_info_t / rga_rect_t */
#include <rga/rga.h> /* RK_FORMAT_* */
#include <stdio.h>
#include <string.h>
int main(void) {
// 源图像(NV12)
rga_info_t src = {0};
src.fd = -1; // 由调用方替换为真实 dma fd
src.mmuFlag = 1;
src.rect.xoffset = 0;
src.rect.yoffset = 0;
src.rect.width = 1920;
src.rect.height = 1080;
src.rect.wstride = 1920;
src.rect.hstride = 1080;
src.format = RK_FORMAT_YCbCr_420_SP;
src.rect.format = RK_FORMAT_YCbCr_420_SP; // 老驱动读这一份
// 目标图像(RGB888)
rga_info_t dst = {0};
dst.fd = -1;
dst.mmuFlag = 1;
dst.rect.xoffset = 0;
dst.rect.yoffset = 0;
dst.rect.width = 1920;
dst.rect.height = 1080;
dst.rect.wstride = 1920;
dst.rect.hstride = 1080;
dst.format = RK_FORMAT_RGB_888;
dst.rect.format = RK_FORMAT_RGB_888;
int ret = c_RkRgaBlit(&src, &dst, NULL);
if (ret < 0) printf("RGA blit failed: %d\n", ret);
else printf("NV12 -> RGB888 done.\n");
return 0;
}3.1 关键 API 要点
| 项 | 说明 |
|---|---|
rga_rect_t 字段 | xoffset / yoffset(不是 x / y) |
rga_info_t.format | 同时设置顶层 format 和 rect.format(老驱动只读 rect.format) |
| 转换函数 | c_RkRgaBlit() 或 RgaBlit() 宏(没有 rga_blit()) |
4. 缩放与旋转
IM2D API 路径(推荐):im2d_single.h:147 imrotate()、im2d_single.h:519 imrotate_t()。
#include <rga/im2d.h>
#include <rga/im2d_single.h>
#include <rga/im2d_type.h>
rga_info_t src = {0}; /* 同上 NV12 1920x1080 */
rga_info_t dst = {0}; /* 640x480 RGB */
int ret = c_RkRgaBlit(&src, &dst, NULL);
if (ret < 0) return ret;
/* 旋转常量来自 im2d_single.h:136-139 */
ret = imrotate(src, dst, IM_HAL_TRANSFORM_ROT_90);5. 多图层合成(OSD 叠加水印)
多图层合成走 IM2D API,使用 rga_image_t(im2d_type.h:336,含 global_alpha 字段,rga_info_t 没有该字段)。
#include <rga/im2d.h>
#include <rga/im2d_type.h>
rga_image_t layer0 = {0};
layer0.buffer.fd = video_fd;
layer0.width = 1920;
layer0.height = 1080;
layer0.format = RK_FORMAT_YCbCr_420_SP;
layer0.global_alpha = 0xff; // 全局 alpha
rga_image_t layer1 = {0};
layer1.buffer.fd = logo_fd;
layer1.width = 280;
layer1.height = 80;
layer1.format = RK_FORMAT_RGBA_8888;
layer1.global_alpha = 200; // 半透明
rga_image_t dst_img = {0};
dst_img.buffer.fd = dst_fd;
dst_img.width = 1920;
dst_img.height = 1080;
dst_img.format = RK_FORMAT_YCbCr_420_SP;
int ret = imcomposite(dst_img, layer0, layer1);
if (ret != IM_STATUS_SUCCESS) printf("composite failed\n");6. RGA API 验证
RGA C API 函数声明:
grep -n "c_RkRgaInit\|c_RkRgaBlit" /usr/include/rga/RgaApi.h /usr/include/rga/drmrga.h输出:
/usr/include/rga/RgaApi.h:49: ret = c_RkRgaInit(); \
/usr/include/rga/RgaApi.h:57:#define RgaBlit(...) c_RkRgaBlit(__VA_ARGS__)
/usr/include/rga/RgaApi.h:61:int c_RkRgaInit();
/usr/include/rga/RgaApi.h:64:int c_RkRgaBlit(rga_info_t *src, rga_info_t *dst, rga_info_t *src1);RgaApi.h 完整宏和函数声明:
sed -n '40,65p' /usr/include/rga/RgaApi.h输出:
/*
* Compatible with the old version of C interface.The new
* version of the C interface no longer requires users to
* initialize rga, so RgaInit and RgaDeInit are just for
* compatibility with the old C interface, so please do
* not use ctx, because it is usually a NULL.
*/
#define RgaInit(ctx) ({ \
int ret = 0; \
ret = c_RkRgaInit(); \
c_RkRgaGetContext(ctx); \
ret;\
})
#define RgaDeInit(ctx) { \
(void)ctx; /* unused */ \
c_RkRgaDeInit(); \
}
#define RgaBlit(...) c_RkRgaBlit(__VA_ARGS__)
#define RgaCollorFill(...) c_RkRgaColorFill(__VA_ARGS__)
#define RgaFlush() c_RkRgaFlush()
int c_RkRgaInit();
void c_RkRgaDeInit();
void c_RkRgaGetContext(void **ctx);
int c_RkRgaBlit(rga_info_t *src, rga_info_t *dst, rga_info_t *src1);
int c_RkRgaColorFill(rga_info_t *dst);RGA C 接口验证:
c_RkRgaInit()返回 int(RgaApi.h:61)c_RkRgaBlit()3 个参数:rga_info_t *src, *dst, *src1(RgaApi.h:64)RgaBlit()是宏,封装c_RkRgaBlit()
rga_rect_t 结构定义:
sed -n '114,125p' /usr/include/rga/drmrga.h输出:
typedef struct rga_rect {
int xoffset;
int yoffset;
int width;
int height;
int wstride;
int hstride;
int format;
int size;
} rga_rect_t;
rga_rect_t字段确认:包含xoffset/yoffset/width/height/wstride/hstride/format/size
rga_info_t 结构定义(部分):
sed -n '266,280p' /usr/include/rga/drmrga.h输出:
typedef struct rga_info {
int fd;
void *virAddr;
void *phyAddr;
#ifndef ANDROID /* LINUX */
unsigned hnd;
#else /* Android */
buffer_handle_t hnd;
#endif
int format;
rga_rect_t rect;
unsigned int blend;imrotate / imrotate_t 函数声明:
grep -n "imrotate\|IM_HAL_TRANSFORM_ROT_" /usr/include/rga/im2d_single.h | head -10输出:
137: * IM_HAL_TRANSFORM_ROT_90
138: * IM_HAL_TRANSFORM_ROT_180
139: * IM_HAL_TRANSFORM_ROT_270
147:IM_API IM_STATUS imrotate(const rga_buffer_t src, rga_buffer_t dst, int rotation, int sync = 1, int *release_fence_fd = NULL);
519:IM_C_API IM_STATUS imrotate_t(const rga_buffer_t src, rga_buffer_t dst, int rotation, int sync);旋转常量实际定义:
sed -n '46,55p' /usr/include/rga/im2d_type.h输出:
typedef enum {
/* Rotation */
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,旋转常量验证(
im2d_type.h:50-52):
IM_HAL_TRANSFORM_ROT_90 = 1<<0IM_HAL_TRANSFORM_ROT_180 = 1<<1IM_HAL_TRANSFORM_ROT_270 = 1<<2
IM2D 合成相关结构:
grep -n "rga_image_t\|global_alpha\|imcomposite" /usr/include/rga/im2d_type.h | head -10输出:
336: int global_alpha; /* global_alpha, the default should be 0xff */rga_buffer_t 结构完整定义:
sed -n '323,351p' /usr/include/rga/im2d_type.h输出:
typedef struct {
void* vir_addr; /* virtual address */
void* phy_addr; /* physical address */
int fd; /* shared fd */
int width; /* width */
int height; /* height */
int wstride; /* wstride */
int hstride; /* hstride */
int format; /* format */
int color_space_mode; /* color_space_mode */
union {
int global_alpha; /* global_alpha, the default should be 0xff */
struct {
uint16_t alpha0;
uint16_t alpha1;
} alpha_bit; /* alpha bit(e.g. RGBA5551), 0: alpha0, 1: alpha1 */
};
int rd_mode;
/* legacy */
int color; /* color, used by color fill */
im_colorkey_range colorkey_range; /* range value of color key */
im_nn_t nn;
int rop_code;
rga_buffer_handle_t handle; /* buffer handle */
} rga_buffer_t;imcomposite 函数签名:
grep -n "imcomposite" /usr/include/rga/im2d_single.h | head -3输出:
212:IM_API 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);imcomposite(srcA, srcB, dst, mode=IM_ALPHA_BLEND_SRC_OVER, sync=1, release_fence_fd=NULL)必须用
rga_buffer_t类型,不是rga_image_t。
RK_FORMAT 像素格式宏:
grep -n "RK_FORMAT_RGBA_8888 \|RK_FORMAT_RGB_888 \|RK_FORMAT_BGR_888 \|RK_FORMAT_YCbCr_422_SP \|RK_FORMAT_YCbCr_422_P \|RK_FORMAT_YCbCr_420_SP \|RK_FORMAT_ARGB_8888 " /usr/include/rga/rga.h输出:
30: RK_FORMAT_RGBA_8888 = 0x0 << 8, /* [0:31] R:G:B:A 8:8:8:8 little endian */
32: RK_FORMAT_RGB_888 = 0x2 << 8, /* [0:23] R:G:B 8:8:8 little endian */
37: RK_FORMAT_BGR_888 = 0x7 << 8, /* [0:23] B:G:R 8:8:8 little endian */
39: RK_FORMAT_YCbCr_422_SP = 0x8 << 8, /* 2 plane YCbCr little endian
42: RK_FORMAT_YCbCr_422_P = 0x9 << 8, /* 3 plane YCbCr little endian
46: RK_FORMAT_YCbCr_420_SP = 0xa << 8, /* 2 plane YCbCr little endian
120: RK_FORMAT_ARGB_8888 = 0x28 << 8, /* [0:31] A:R:G:B 8:8:8:8 little endian */7. RGA 支持哪些像素格式?
| 格式 | 宏定义 | 备注 |
|---|---|---|
| NV12 | RK_FORMAT_YCbCr_420_SP | YUV420 半平面 |
| NV16 | RK_FORMAT_YCbCr_422_SP | YUV422 半平面 |
| RGB888 | RK_FORMAT_RGB_888 | — |
| RGBA8888 | RK_FORMAT_RGBA_8888 | — |
| BGR888 | RK_FORMAT_BGR_888 | — |
| YUV422P | RK_FORMAT_YCbCr_422_P | YUV422 平面 |
| ARGB8888 | RK_FORMAT_ARGB_8888 | — |
8. RGA 零拷贝数据流
摄像头 / V4L2、RKNN、MPP 各自通过 IOCTL_VIDIOC_xxx / rknn3_* / mpp_buffer_get_fd() 取得 fd,能正常在 RGA 的 src.fd / dst.fd 中复用,全程零 memcpy。
摄像头 (V4L2)
│ dma_fd
↓
RGA 格式转换(NV12 → RGB)
│ 同一 fd 或新 fd
↓
NPU 推理(RKNN)
│ 结果
↓
RGA 合成(叠加检测框)
↓
编码器(MPP)/ 显示器9. 常见问题
| 现象 | 原因 | 处理 |
|---|---|---|
RGA init failed: -1 | /dev/rga* 不存在 | 装 librga / 加载 rga 内核模块 |
RGA blit failed | fd 无效 / 格式不匹配 | 检查 fd、format 字段 |
xoffset/yoffset 不生效 | 写成 x/y | 改用 xoffset/yoffset |
rect.format 不读 | 老驱动 | 同时设 info.format 和 rect.format |
imrotate 编译报 rga_image_t 错 | 用错类型 | 改 rga_buffer_t |
| 旋转方向反 | 常量搞错 | IM_HAL_TRANSFORM_ROT_90/180/270 |
