RGA how to use DMA buffer
DMA buffer
1. 为什么需要DMA buffer?
1.1 硬件直接内存访问(DMA)需求
- RGA是一个硬件加速器,不是纯软件库.
- 硬件模块需要直接访问物理内存,而不经过CPU
- DMA内存提供了硬件可识别的物理连续内存块 避免内存拷贝
// 传统方式:需要CPU参与拷贝
CPU: 应用内存 → 临时缓冲区 → 输出内存
// DMA方式:硬件直接处理
RGA硬件: DMA内存 → 直接处理 → 输出结果
1.2 性能优化
零拷贝架构
- 消除CPU在内存间的数据搬运
- 减少内存带宽占用
- 降低CPU负载,提高并行性
缓存一致性
// 使用非缓存DMA内存(UNCACHED)
DMA_HEAP_DMA32_UNCACHED_PATH
- 避免CPU缓存与硬件访问之间的同步问题
- 确保RGA硬件看到的是最新数据
1.3 内存特性要求
物理连续性
- RGA硬件需要物理上连续的内存块
- 普通malloc分配的内存可能是虚拟连续但物理分散的
- DMA分配器保证物理内存的连续性
4G地址空间限制
// 分配在4G地址空间内
DMA_HEAP_DMA32_UNCACHED_PATH
- 某些硬件只能访问32位地址空间
- 确保RGA硬件能够直接寻址
1.4 系统架构优势
内存管理单元(MMU)绕过
- DMA允许硬件直接访问物理地址
- 避免经过MMU的地址转换开销
进程间共享
- DMA缓冲区通过文件描述符(dma_fd)共享
- 多个进程或驱动可以访问同一块物理内存
2. 如何分配DMA内存?
2.1 DMA_HEAP
先确保系统里有DMA_HEAP节点, 如下:
linaro@linaro-alip:~$ ls /dev/dma_heap/system-uncached-dma32
/dev/dma_heap/system-uncached-dma32
2.2 dma_buf_alloc
调用Rockchip提供的API dma_buf_alloc, 如下代码申请DMA内存:
/*
* Allocate dma_buf within 4G from dma32_heap,
* return dma_fd and virtual address.
*/
ret = dma_buf_alloc(DMA_HEAP_DMA32_UNCACHED_PATH, dst_buf_size, &dst_dma_fd, (void **)&dst_buf);
if (ret < 0) {
printf("alloc dma32_heap buffer failed!\n");
return -1;
}
实际上 dma_buf_alloc实现如下:
int dma_buf_alloc(const char *path, size_t size, int *fd, void **va) {
int ret;
int prot;
void *mmap_va;
int dma_heap_fd = -1;
struct dma_heap_allocation_data buf_data;
/* open dma_heap fd */
dma_heap_fd = open(path, O_RDWR);
if (dma_heap_fd < 0) {
printf("open %s fail!\n", path);
return dma_heap_fd;
}
/* alloc buffer */
memset(&buf_data, 0x0, sizeof(struct dma_heap_allocation_data));
buf_data.len = size;
buf_data.fd_flags = O_CLOEXEC | O_RDWR;
ret = ioctl(dma_heap_fd, DMA_HEAP_IOCTL_ALLOC, &buf_data);
if (ret < 0) {
printf("RK_DMA_HEAP_ALLOC_BUFFER failed\n");
return ret;
}
/* mmap va */
if (fcntl(buf_data.fd, F_GETFL) & O_RDWR)
prot = PROT_READ | PROT_WRITE;
else
prot = PROT_READ;
/* mmap contiguors buffer to user */
mmap_va = (void *)mmap(NULL, buf_data.len, prot, MAP_SHARED, buf_data.fd, 0);
if (mmap_va == MAP_FAILED) {
printf("mmap failed: %s\n", strerror(errno));
return -errno;
}
*va = mmap_va;
*fd = buf_data.fd;
close(dma_heap_fd);
return 0;
}
3 RGA 如何使用DMA buffer
RGA通过DMA buffer申请时返回的handle即可, 如下:
/*
* Import the allocated dma_fd into RGA by calling
* importbuffer_fd, and use the returned buffer_handle
* to call RGA to process the image.
*/
dst_handle = importbuffer_fd(dst_dma_fd, dst_buf_size);
if (dst_handle == 0) {
printf("import dma_fd error!\n");
ret = -1;
goto free_buf;
}
dst = wrapbuffer_handle(dst_handle, dst_width, dst_height, dst_format);
dst_rect.x = 0;
dst_rect.y = 0;
dst_rect.width = 300;
dst_rect.height = 200;
ret = imcheck({}, dst, {}, dst_rect, IM_COLOR_FILL);
if (IM_STATUS_NOERROR != ret) {
printf("%d, check error! %s", __LINE__, imStrError((IM_STATUS)ret));
goto release_buffer;
}
ts = get_cur_us();
ret = imfill(dst, dst_rect, 0xff00ff00);
if (ret == IM_STATUS_SUCCESS) {
printf("%s running success! cost %ld us\n", LOG_TAG, get_cur_us() - ts);
} else {
printf("%s running failed, %s\n", LOG_TAG, imStrError((IM_STATUS)ret));
goto release_buffer;
}
5. 完整demo代码
此demo是Rockchip github里的sampleRGA sample, 下面是完整的demo代码:
#define LOG_NDEBUG 0
#undef LOG_TAG
#define LOG_TAG "rga_allocator_dma32_demo"
#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;
int64_t ts;
int dst_width, dst_height, dst_format;
int dst_buf_size;
char *dst_buf;
int dst_dma_fd;
rga_buffer_t dst = {};
im_rect dst_rect = {};
rga_buffer_handle_t dst_handle;
dst_width = 1280;
dst_height = 720;
dst_format = RK_FORMAT_RGBA_8888;
dst_buf_size = dst_width * dst_height * get_bpp_from_format(dst_format);
/*
* Allocate dma_buf within 4G from dma32_heap,
* return dma_fd and virtual address.
*/
ret = dma_buf_alloc(DMA_HEAP_DMA32_UNCACHED_PATH, dst_buf_size, &dst_dma_fd, (void **)&dst_buf);
if (ret < 0) {
printf("alloc dma32_heap buffer failed!\n");
return -1;
}
memset(dst_buf, 0x33, dst_buf_size);
/*
* Import the allocated dma_fd into RGA by calling
* importbuffer_fd, and use the returned buffer_handle
* to call RGA to process the image.
*/
dst_handle = importbuffer_fd(dst_dma_fd, dst_buf_size);
if (dst_handle == 0) {
printf("import dma_fd error!\n");
ret = -1;
goto free_buf;
}
dst = wrapbuffer_handle(dst_handle, dst_width, dst_height, dst_format);
dst_rect.x = 0;
dst_rect.y = 0;
dst_rect.width = 300;
dst_rect.height = 200;
ret = imcheck({}, dst, {}, dst_rect, IM_COLOR_FILL);
if (IM_STATUS_NOERROR != ret) {
printf("%d, check error! %s", __LINE__, imStrError((IM_STATUS)ret));
goto release_buffer;
}
ts = get_cur_us();
ret = imfill(dst, dst_rect, 0xff00ff00);
if (ret == IM_STATUS_SUCCESS) {
printf("%s running success! cost %ld us\n", LOG_TAG, get_cur_us() - ts);
} else {
printf("%s running failed, %s\n", LOG_TAG, 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:
if (dst_handle > 0)
releasebuffer_handle(dst_handle);
free_buf:
dma_buf_free(dst_buf_size, &dst_dma_fd, dst_buf);
return 0;
}
6. 编译及运行
在Neardi LKD3588设备上面clone RGA sample代码, 更改如下编译脚本:
diff --git a/toolchains/toolchain_linux.cmake b/toolchains/toolchain_linux.cmake
index f446557..b65e8b2 100644
--- a/toolchains/toolchain_linux.cmake
+++ b/toolchains/toolchain_linux.cmake
@@ -1,15 +1,15 @@
-SET(TOOLCHAIN_HOME "/home/yqw/workspace/linux/common/prebuilts/gcc/linux-x86/aarch64/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu")
-SET(TOOLCHAIN_NAME "aarch64-rockchip1031-linux-gnu")
+#SET(TOOLCHAIN_HOME "/home/yqw/workspace/linux/common/prebuilts/gcc/linux-x86/aarch64/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu")
+SET(TOOLCHAIN_NAME "aarch64-linux-gnu")
# this is required
#SET(CMAKE_SYSTEM_NAME Linux)
# specify the cross compiler
-SET(CMAKE_C_COMPILER ${TOOLCHAIN_HOME}/bin/${TOOLCHAIN_NAME}-gcc)
-SET(CMAKE_CXX_COMPILER ${TOOLCHAIN_HOME}/bin/${TOOLCHAIN_NAME}-g++)
+SET(CMAKE_C_COMPILER /usr/bin/${TOOLCHAIN_NAME}-gcc)
+SET(CMAKE_CXX_COMPILER /usr/bin/${TOOLCHAIN_NAME}-g++)
# where is the target environment
-SET(CMAKE_FIND_ROOT_PATH ${TOOLCHAIN_HOME})
+#SET(CMAKE_FIND_ROOT_PATH ${TOOLCHAIN_HOME})
# search for programs in the build host directories (not necessary)
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
之后执行如下命令编译:
linaro@linaro-alip:~/librga/samples/allocator_demo$ ./cmake-linux.sh
~/librga/samples/allocator_demo/build/build_linux ~/librga/samples/allocator_demo
load /home/linaro/librga/samples/allocator_demo/../../toolchains/toolchain_linux.cmake
-- The C compiler identification is GNU 12.2.0
-- The CXX compiler identification is GNU 12.2.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/aarch64-linux-gnu-gcc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/aarch64-linux-gnu-g++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/linaro/librga/samples/allocator_demo/build/build_linux
[ 13%] Building CXX object CMakeFiles/utils_obj.dir/home/linaro/librga/samples/utils/allocator/dma_alloc.cpp.o
...
最后, 运行demo, 如下:
linaro@linaro-alip:~/librga/samples/allocator_demo/build/build_linux/install/bin$ ./rga_allocator_dma32_demo
rga_api version 1.10.1_[10]
rga_allocator_dma32_demo running success! cost 323 us
output [0x0, 0xff, 0x0, 0xff]
open /data/out0w1280-h720-rgba8888.bin and write ok
也可以查看每一块DMA内存分配的大小, 如下�路径:
linaro@linaro-alip:/sys/kernel/dmabuf/buffers$ ls
1 16 17 18 19 2 20 21 22 23 24 25 26 27 28 3 35 4 5 6 7 8 9
linaro@linaro-alip:/sys/kernel/dmabuf/buffers$ cd 35
linaro@linaro-alip:/sys/kernel/dmabuf/buffers/35$ ls
exporter_name size
linaro@linaro-alip:/sys/kernel/dmabuf/buffers/35$ cat size
3686400
linaro@linaro-alip:/sys/kernel/dmabuf/buffers/35$ cat exporter_name
system-uncached-dma32