Skip to main content

RGA Copy

你可能会问,一个简单的memcpy不就能拷贝图片吗, 为什么还需要使用RGA?对于图像数据而言,直接的内存拷贝存在局限性:

  • 性能瓶颈: 高分辨率图像(如4K、8K)数据量巨大,CPU进行内存拷贝会消耗大量计算资源和时间。
  • 格式处理: 如果源图和目标图的像素格式不同(例如,需要从 NV12 转换为 RGB888),单纯的memcpy无能为力。
  • 功能单一: 无法在拷贝的同时完成缩放、旋转等常用操作。

RGA完美地解决了这些问题。它作为一个专用的图像处理DMA引擎,可以:

  • 极高的吞吐量: 通过硬件直接处理数据流,速度极快。
  • 硬件格式转换: 支持几乎所有常见图像格式之间的相互转换。
  • 一站式处理: 在“拷贝”的同时,可以轻松地叠加缩放、旋转、裁剪等操作。

1. 环境准备

在使用RGA之前,请确保你的RK3588运行环境已具备以下条件:

  • 设备系统里有DMA节点, 如下:
linaro@linaro-alip:~$ ls /dev/dma_heap/system-uncached-dma32 -l
crw-rw---- 1 root video 251, 3 10月14日 05:55 /dev/dma_heap/system-uncached-dma32

此demo也是基于DMA buffer, 详细请查看RGA DMA.

  • RGA库与头文件: SDK中应包含 librga.so 动态库以及对应的头文件(通常是 rga.h 或 RgaApi.h), Neardi设备出厂固件里已经包含。

2. RGA拷贝代码

此demo是单纯地拷贝一帧图像, 拷贝过程中不做其它转换。

输入参数要求使用DMA buffer, 直接传递其handle, 代码如下:

  • src_fd, 输入图片DMA句柄。
  • src_size, 输入DMA大小。
  • dst_fd, 转换后输出DMA句柄。
  • dst_size, 输出DMA大小。
  • width, 输入图片的宽。
  • height, 输入图片的高。
int rga_copy(int src_fd, int src_size, int dst_fd, int dst_size, int width, int height) {

rga_buffer_handle_t src_handle = importbuffer_fd(src_fd, src_size);
rga_buffer_handle_t dst_handle = importbuffer_fd(dst_fd, dst_size);
if (!src_handle || !dst_handle) {
if (src_handle) releasebuffer_handle(src_handle);
if (dst_handle) releasebuffer_handle(dst_handle);
cerr << "importbuffer_fd failed\n";
return -1;
}

rga_buffer_t src = wrapbuffer_handle(src_handle, width, height, RK_FORMAT_BGR_888);
rga_buffer_t dst = wrapbuffer_handle(dst_handle, width, height, RK_FORMAT_BGR_888);

// 检查参数
int ret = imcheck(src, dst, {}, {});
if (IM_STATUS_NOERROR != ret) {
cerr << "imcheck error: " << imStrError((IM_STATUS)ret) << endl;
releasebuffer_handle(src_handle);
releasebuffer_handle(dst_handle);
return -1;
}

/*
* Copy the src image to the dst buffer.
* -------------- --------------
* | | | |
* | src_image | => | dst_image |
* | | | |
* -------------- --------------
*/
ret = imcopy(src, dst);
if (ret == IM_STATUS_SUCCESS) {
printf("imcopy running success!\n");
} else {
printf("imcopy running failed, %s\n", imStrError((IM_STATUS)ret));
}

// 释放 handle
releasebuffer_handle(src_handle);
releasebuffer_handle(dst_handle);
return (ret == IM_STATUS_SUCCESS) ? 0 : -1;
}

3. 使用示例

V4L2-CAMERA基础上, 把上面拷贝代码集成进去。

//////////////////////
// camera thread: capture NV12 into DMA buffer and push to data queue
@@ -465,6 +465,17 @@ int main(int argc, char** argv) {
cerr << "alloc dst dma failed\n";
return -1;
}
+ dstBuf.size = dst_size;
+
+
+ // 新增:用于存储拷贝后的BGR数据
+ DMABuffer bgrCopyBuf;
+ if (!bgrCopyBuf.alloc(DMA_HEAP_DMA32_UNCACHED_PATH, dst_size)) {
+ cerr << "alloc bgr copy dma failed\n";
+ return -1;
+ }
+ bgrCopyBuf.size = dst_size;
+
std::string cam_dev = "/dev/video" + std::to_string(cam_id);
// start camera thread
thread cam_thread(camera_thread_func, std::ref(pool), cam_dev.c_str(), width, height);
@@ -493,11 +504,18 @@ int main(int argc, char** argv) {
continue;
}

- // ensure device->cpu sync before reading dstBuf.va
- dma_sync_device_to_cpu(dst_fd);
- cv::Mat bgr(height, width, CV_8UC3, dstBuf.va);
+ // Step 2: 将BGR数据拷贝到新的buffer
+ ret = rga_copy(dst_fd, dst_size_int, bgrCopyBuf.fd, int(bgrCopyBuf.size), width, height);
+ if (ret != 0) {
+ cerr << "rga copy failed\n";
+ idleQ->push(frame);
+ continue;
+ }
+
+ // 注意这里传入 step,否则 OpenCV 会按 width*bytes_per_pixel 来推断,导致错位
+ cv::Mat copy_img(height, width, CV_8UC3, bgrCopyBuf.va);
+ imshow("Copy Image", copy_img);

- imshow("BGR", bgr);
if (waitKey(1) == 27) {
cout << "exit\n";
break;

此处下载完整的代码.

demo程序运行后, 也可以查看RGA的负载, 如下:

root@linaro-alip:/sys/kernel/debug/rkrga# cat load
num of scheduler = 3
================= load ==================
scheduler[0]: rga3
load = 6%
-----------------------------------
scheduler[1]: rga3
load = 0%
-----------------------------------
scheduler[2]: rga2
load = 0%
-----------------------------------
=========================================
<session> <status> <tgid> <process>
1 active 3806 ./camera 22

从上面可以看见, rk3588使用的是RGA3, 当前RGA拷贝使用的负载是6%.