Toybrick

标题: RK3399Pro入门教程(5)图形加速引擎RGA的使用 [打印本页]

作者: jefferyzhang    时间: 2019-4-15 17:48
标题: RK3399Pro入门教程(5)图形加速引擎RGA的使用
本帖最后由 jefferyzhang 于 2019-4-18 09:48 编辑

[attach]237[/attach]

简介

入门教学(1) 中,我们提到了RK自带的RGA核心,可以非常快速的处理二维图像的基本操作,例如
旋转、镜像、缩放、拷贝、剪裁、格式转换等等,那么这篇教学就将详细的说说如何使用。(Android和Linux用法是一致的)


设备节点

RGA的设备节点挂在:
  1. /dev/rga
复制代码


RGA库安装

如果大家不想了解更底层的细节,可以直接安装这个库来使用,我们已经帮大家封装了常用的操作和细节:
  1. sudo dnf install librockchip_rga-devel
复制代码


如果想进一步了解RGA,可以研究下Android的Hardware/rockchip/librga来查看寄存器的配置等更详细的调用方式,或者使用该代码调用RGA。


示例代码下载地址

本教学的下载地址:
  1. https://github.com/Jerzha/samples-toybrick-rga.git
复制代码

注意:
1. sample地址里包含了rockchip_rga目录,该目录的内容就是dnf安装的librockchip_rga内容的源码,如果您不需要修改源码,可以直接dnf安装就好了
2. 如果没有交叉编译环境的话,请放在板子上编译

RGA注意事项

1. 16对齐。所有进入rga的图像的宽高需要进行16对齐,例如1920*1080的图像需要对齐成1920*1088。如果没有显式对齐的话,输出依然会是16对齐的,所以多出几个像素的奇怪的宽高,请不要认为是bug。
2. 可多线程调用来提高利用率
3. 或者设置回调函数的方式提高利用率(库没有提供回调函数注册)
4. RGA自带mmu,可以将虚拟地址转换成物理地址使用(IP核只认识物理地址)。当然如果预先知道物理地址,可以关闭mmu来提升速度。(库没有提供该功能)


基础使用流程

1. 安装RGA库,详见wiki教学

2. 初始化RGA模块,生成context (多线程需要生成多个context)

  1. RockchipRga *mRga;
  2. if (!mRga) {
  3.     printf("create rga failed !\n");
  4.     abort();
  5. }
  6. mRga->ops->initCtx(mRga);
复制代码

3. 设置源(src)和目标(dst)的图片格式、宽高。(需要注意的是,图片格式我们引用Linux V4L2的标准图片格式定义)

  1. mRga->ops->setSrcFormat(mRga, V4L2_PIX_FMT_ABGR32, BUFFER_WIDTH, BUFFER_HEIGHT);
  2. mRga->ops->setDstFormat(mRga, V4L2_PIX_FMT_ABGR32, BUFFER_WIDTH, BUFFER_HEIGHT);
复制代码

4. 设置源(src)和目标(dst)图片的地址。(需要注意的是设置虚拟地址或者fd的接口是不一样的)

  1. mRga->ops->setSrcBufferPtr(mRga, srcBuffer);
  2. mRga->ops->setDstBufferPtr(mRga, dstBuffer);
复制代码

如果是fd(fd一般存在于通过drm申请的buffer), 当然这篇教学里用不到。

  1. mRga->ops->setSrcBufferFd(mRga, srcFd);
  2. mRga->ops->setDstBufferFd(mRga, dstFd);
复制代码

5. 如果只是拷贝和格式转换的话,那做到第四部就可以了。如果还有旋转剪裁等操作,需要再多设置几个:

旋转:
  1. mRga->ops->setRotate(mRga, RGA_ROTATE_90);  // RGA_ROTATE_180, RGA_ROTATE_270
复制代码

剪裁:
  1. mRga->ops->setSrcCrop(mRga, crop_x, crop_y, crop_w, crop_h);
复制代码

(库里提供的就这些。其他功能有需要的话需要自己新增接口了)

6. 插入rga任务队列,开始执行,同步等待返回。

  1. mRga->ops->go(mRga);
复制代码

该函数单个执行其实用不满RGA,因为在设置和返回结果时候RGA是空闲的。这时候可以通过多线程的调用来让RGA不停的排队工作。
或者还有一种更高级的方法就是设置回调函数,给RGA添加任务的时候立即返回,然后等RGA执行完毕后回调告诉程序已经结束,代价当然是程序复杂度就会变得很高。


结果总结

拷贝一张4096*2160的ARGB图像,如果使用malloc的内存(不连续),RGA拷贝速度是比CPU(有cache情况)慢的,测试代码RGA大概需要60ms。实际使用上RGA作为一个流处理器,并且使用CMA连续内容,速度会快非常多,cpu memcpy需要19ms(因为有cache,所以这个代码测试实际上也不能代表问题)如果是DRM分配的物理地址(连续地址)的话,RGA速度是比CPU快很多的,大概在低于15ms(具体数字还需要写代码测试)就可以完成一次搬运。
本篇教学只关注于如果教会大家用RGA,我们就不在这里写drm地址分配的问题了。


RGA优点:
1. 不占用CPU资源
2. DRM/CMA物理内存拷贝和转换速度很快

RGA缺点:
1. 对malloc的不连续的虚拟内存操作速度很慢

作者: hjf515    时间: 2019-4-18 15:38
跑了例程 rga_cpy,发现 checkData 函数中打印出很多dst和src 不一致的数据,这是不是说用 rga copy 有点不可靠。
如果用物理地址,rga cpy是否会更快些,请问在rk3399pro上如何申请物理内存?
物理内存 映射到 虚拟内存,我就可以通过mmap来实现。
作者: jefferyzhang    时间: 2019-4-18 16:59
hjf515 发表于 2019-4-18 15:38
跑了例程 rga_cpy,发现 checkData 函数中打印出很多dst和src 不一致的数据,这是不是说用 rga copy 有点不 ...

rga其实就是一个ioctl,填下结构体就可以了,建议你看库源码自己实现一个,或者用android写好的librga库来二次开发。那个不一致是库代码的bug,不是大问题。

物理地址请参看android的libgralloc库来分配,rk的drm留了一些第三方接口来分配CMA/DMA内存,或者你看rga的库里注释掉的代码也是有分配drm内存的方法。
作者: 盗骊_l    时间: 2019-7-8 16:15
您好,请问我在Android平台上应该怎么使用 RGA,头文件从哪里来?需要编译什么源码吗?
作者: jefferyzhang    时间: 2019-7-9 16:10
盗骊_l 发表于 2019-7-8 16:15
您好,请问我在Android平台上应该怎么使用 RGA,头文件从哪里来?需要编译什么源码吗? ...

android rga查看 device/rockchip/librga
用法不太一样
作者: 盗骊_l    时间: 2019-7-9 19:15
jefferyzhang 发表于 2019-7-9 16:10
android rga查看 device/rockchip/librga
用法不太一样

啊?上面不是说  Android和Linux一致吗?
https://github.com/Jerzha/samples-toybrick-rga上面的demo,我编译之后再Android上能正常跑,性能和你上面给出的基本一致。但是我自己写了个function,做格式转换,NV12转rgba,转换处理的rgba数据是错误的。
  1. //格式转换  NV12->ABGR
  2. void rga_convert()
  3. {
  4.     RockchipRga *rga = RgaCreate();

  5.     if (!rga) {
  6.         printf("create rga failed !\n");
  7.         abort();
  8.     }
  9.     rga->ops->initCtx(rga);
  10.     //V4L2_PIX_FMT_NV12  V4L2_PIX_FMT_ABGR32
  11.     rga->ops->setSrcFormat(rga, V4L2_PIX_FMT_NV12, BUFFER_WIDTH, BUFFER_HEIGHT);
  12.     rga->ops->setDstFormat(rga, V4L2_PIX_FMT_ABGR32, BUFFER_WIDTH, BUFFER_HEIGHT);

  13.     rga->ops->setSrcBufferPtr(rga, srcBuffer);
  14.     rga->ops->setDstBufferPtr(rga, dstBuffer);

  15.     rga->ops->go(rga);
  16. }
复制代码

作者: jefferyzhang    时间: 2019-7-10 16:39
盗骊_l 发表于 2019-7-9 19:15
啊?上面不是说  Android和Linux一致吗?
https://github.com/Jerzha/samples-toybrick-rga上面的demo, ...

驱动一样,用都可以用。只是android的封装会更高级一点。
你这里的转换代码没有问题,有问题要看看你开的buffer大小是不是对的。
作者: 15992605143    时间: 2019-9-6 22:31
rga可以按比例缩放吗?
作者: jefferyzhang    时间: 2019-9-8 12:10
15992605143 发表于 2019-9-6 22:31
rga可以按比例缩放吗?

当然可以。但是长宽只能是定点数
作者: 盗骊_l    时间: 2019-9-9 13:59
本帖最后由 盗骊_l 于 2019-9-9 14:03 编辑

请问,多线程使用的时候需要注意什么?
我现在遇到的问题:
1>.每个线程里,mpp解码后使用RGA进行格式转换,
  1. RockchipRga *rga = RgaCreate();
复制代码
,初始化一个rga的上下文,一路视频解码后使用RGA转换输出都正常,2路视频解码RGA转换一段时间后,设备重启,3路视频解码,10分钟左右就会造成,设备重启,请问这是什么原因?解码、格式转换结果都正常,就是多路多线程中,一段时间后会造成设备重启。
作者: jefferyzhang    时间: 2019-9-9 14:16
盗骊_l 发表于 2019-9-9 13:59
请问,多线程使用的时候需要注意什么?
我现在遇到的问题:
1>.每个线程里,mpp解码后使用RGA进行格式转换,, ...

设备重启请检查你自己的代码,RGA是物理的硬件,对内存溢出、地址越界是无法在CPU层面报错的,给什么搬运什么。越界就会覆盖其他地址,直接崩溃。
长宽都需要size需要16bit对齐。
作者: shopping    时间: 2019-9-17 17:15
老哥,最近抽时间看了下你发的一些资料,问一句:
1.目前我除了用 opencv 调取摄像头,将视频数据裁剪缩放后输入算法模型,然后将结果返回并显示。
2.我还可以用 GStreamer/MPP 来调用摄像头,用 RGA 裁剪缩放图片,然后输入算法模型,最后利用 GPU OpenGL来做显示。
看您资料里描述,方案2内存占用少,模型运算更快。
作者: jefferyzhang    时间: 2019-9-17 18:03
shopping 发表于 2019-9-17 17:15
老哥,最近抽时间看了下你发的一些资料,问一句:
1.目前我除了用 opencv 调取摄像头,将视频数据裁剪缩放 ...

所以要问啥呢?我看了半天不知道你问题是啥
作者: shopping    时间: 2019-9-18 09:45
jefferyzhang 发表于 2019-9-17 18:03
所以要问啥呢?我看了半天不知道你问题是啥

老哥,我想问的是,现在我想不用opencv库,因为它占用内存较大。所以替代方案就是:
GStreamer/MPP 来调用摄像头,用 RGA 裁剪缩放图片,然后输入算法模型,最后利用 GPU OpenGL来做显示。
这个方案对吗?
作者: jefferyzhang    时间: 2019-9-18 11:13
shopping 发表于 2019-9-18 09:45
老哥,我想问的是,现在我想不用opencv库,因为它占用内存较大。所以替代方案就是:
GStreamer/MPP 来调 ...

对的。没问题
作者: shopping    时间: 2019-9-18 11:22
jefferyzhang 发表于 2019-9-18 11:13
对的。没问题

千头万绪,理不清楚,现在感觉脑子一片混沌。再追问一下:Debian系统下,MPP OpenGL  GStreamer  RGA我都要自己编译安装?
作者: jefferyzhang    时间: 2019-9-18 14:15
shopping 发表于 2019-9-18 11:22
千头万绪,理不清楚,现在感觉脑子一片混沌。再追问一下:Debian系统下,MPP OpenGL  GStreamer  RGA我都 ...

请自行看wiki,全部都是dnf安装就可以。
当然除了opengl以外都有源码,愿意自己编译安装也行
作者: shopping    时间: 2019-9-20 14:45
本帖最后由 shopping 于 2019-9-20 14:47 编辑
jefferyzhang 发表于 2019-9-18 14:15
请自行看wiki,全部都是dnf安装就可以。
当然除了opengl以外都有源码,愿意自己编译安装也行 ...

呃,请问 OpenGL 要怎么安装?C:\Users\lenovo\Desktop\BUG.jpg还有我测试RGA时报了这样的问题,请问我这个RGA是安装好了?
作者: jefferyzhang    时间: 2019-9-20 16:30
shopping 发表于 2019-9-20 14:45
呃,请问 OpenGL 要怎么安装?还有我测试RGA时报了这样的问题,请问我这个RGA是安装好了? ...

rga不需要装,他是硬件的一个设备,挂载在/dev/rga下。
封装的rga库只是方便大家访问这个外设而已
作者: shopping    时间: 2019-9-20 17:07
jefferyzhang 发表于 2019-9-20 16:30
rga不需要装,他是硬件的一个设备,挂载在/dev/rga下。
封装的rga库只是方便大家访问这个外设而已 ...

难怪编译的时候是把它目录下的 cpp 文件给编译了,那 OpenGl 呢,老哥。
作者: jefferyzhang    时间: 2019-9-20 18:09
shopping 发表于 2019-9-20 17:07
难怪编译的时候是把它目录下的 cpp 文件给编译了,那 OpenGl 呢,老哥。

ogl请用dnf安装 ocl 包。 mali的 cl和gl是同一个so。
目前我们暂时只能放cl里,系统自带的gl依赖太多,我们还在整理。
作者: jefferyzhang    时间: 2019-9-20 18:10
rga那些库请看wiki文档,都有说如何安装
作者: shopping    时间: 2019-10-8 15:44
jefferyzhang 发表于 2019-9-20 18:09
ogl请用dnf安装 ocl 包。 mali的 cl和gl是同一个so。
目前我们暂时只能放cl里,系统自带的gl依赖太多,我 ...

老哥,关于这个OpenGL库,如果我直接用 FrameBuffer 显示图片/视频,这样是否可行?即 V4L2+ FrameBuffer方案。
作者: jefferyzhang    时间: 2019-10-9 10:35
shopping 发表于 2019-10-8 15:44
老哥,关于这个OpenGL库,如果我直接用 FrameBuffer 显示图片/视频,这样是否可行?即 V4L2+ FrameBuffer ...

Linux 4.x以后就改用DRM框架了,没有FB设备了
作者: shopping    时间: 2019-10-9 10:42
本帖最后由 shopping 于 2019-10-9 11:49 编辑
jefferyzhang 发表于 2019-10-9 10:35
Linux 4.x以后就改用DRM框架了,没有FB设备了

好吧,这几天的FB白看了,看样子最后还是得用 OpenGL 来做显示?还有老哥,V4L2它有解码功能这与MPP功能是重合的,如果我用V4L2解码,处理效率区别大吗?
麻烦指教一下,谢谢。

作者: jefferyzhang    时间: 2019-10-9 17:36
shopping 发表于 2019-10-9 10:42
好吧,这几天的FB白看了,看样子最后还是得用 OpenGL 来做显示?还有老哥,V4L2它有解码功能这与MPP功能是 ...

v4l2没有解码,openGL只是GPU接口,最终绘制还是要走到DRM。
作者: shopping    时间: 2019-10-9 19:31
jefferyzhang 发表于 2019-10-9 17:36
v4l2没有解码,openGL只是GPU接口,最终绘制还是要走到DRM。

老哥,我刚刚又看了一遍代码,V4L2框架调用摄像头输出格式已经是 yuv422 格式了,可以直接保存成 yuv文件的。https://www.cnblogs.com/surpassa ... lab1.html#!comments
作者: jefferyzhang    时间: 2019-10-10 08:33
shopping 发表于 2019-10-9 19:31
老哥,我刚刚又看了一遍代码,V4L2框架调用摄像头输出格式已经是 yuv422 格式了,可以直接保存成 yuv文件 ...

那是因为你的usb相机出来的格式就是yuv422
作者: shopping    时间: 2019-10-11 11:17
本帖最后由 shopping 于 2019-10-11 11:30 编辑
jefferyzhang 发表于 2019-10-10 08:33
那是因为你的usb相机出来的格式就是yuv422

老哥,用RGA转换格式出现花屏重影的问题。我自己写了个 yuv422 转 BGR 的函数,结果显示正常,RGA sample demo :https://github.com/Jerzha/samples-toybrick-rga .
  1. // rga start

  2.     srcBuffer = (unsigned char*)malloc(sizeof(unsigned char) * BUFFER_SIZE_src);  // yuyv  BUFFER_SIZE_src = MAGE_WIDTH*IMAGE_HEIGHT*2
  3.     dstBuffer = (unsigned char*)malloc(sizeof(unsigned char) * BUFFER_SIZE_det);  // RGB  BUFFER_SIZE_det = MAGE_WIDTH*IMAGE_HEIGHT*3

  4.     RockchipRga *mRga = RgaCreate();
  5.     if (!mRga) {
  6.         printf("create rga failed !\n");
  7.         abort();
  8.     }
  9.     mRga->ops->initCtx(mRga);
  10.     mRga->ops->setSrcFormat(mRga, V4L2_PIX_FMT_YUYV, IMAGE_WIDTH, IMAGE_HEIGHT);
  11.     mRga->ops->setDstFormat(mRga, V4L2_PIX_FMT_BGR24, IMAGE_WIDTH, IMAGE_HEIGHT);

  12.     struct v4l2_buffer enqueue,dequeue ;
  13.     memset(&enqueue, 0, sizeof(enqueue));
  14.     memset(&dequeue, 0, sizeof(dequeue));

  15.     enqueue.type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
  16.     enqueue.memory = V4L2_MEMORY_MMAP ;

  17.     dequeue.type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
  18.     dequeue.memory = V4L2_MEMORY_MMAP ;
  19.     while (1)
  20.     {
  21.     // loop start
  22.         // dequeue
  23.         ioctl(fd, VIDIOC_DQBUF, &dequeue) ; // get frame v4l2取缓存帧
  24.         //memcpy((void*)srcBuffer , buffers[dequeue.index].start , dequeue.length);
  25.         srcBuffer = (unsigned char *)buffers[dequeue.index].start;  // 将取出来的缓存帧起始地址赋给 srcBuffer

  26.         mRga->ops->setSrcBufferPtr(mRga, srcBuffer);
  27.         mRga->ops->setDstBufferPtr(mRga, dstBuffer);
  28.         mRga->ops->go(mRga);

  29.         unchar_to_Mat(dstBuffer);  // 将 unsigned char BGR格式转换为 Mat BGR格式
  30.         cv::imshow("image",img);
  31.         cv::waitKey(20);

  32.         //enqueue
  33.         enqueue.index = dequeue.index ;
  34.         ioctl(fd, VIDIOC_QBUF, &enqueue) ;  // enqueue

  35.     }
  36.     ioctl(fd, VIDIOC_STREAMOFF, &type);
  37.     free(srcBuffer);
  38.     free(dstBuffer);

  39.     for(int i=0;i<req.count;i++)
  40.     {
  41.     munmap(buffers[i].start,buffers[i].length);
  42.     }
  43.     return(TRUE);
  44. }
复制代码

作者: jefferyzhang    时间: 2019-10-11 14:28
shopping 发表于 2019-10-11 11:17
老哥,用RGA转换格式出现花屏重影的问题。我自己写了个 yuv422 转 BGR 的函数,结果显示正常,RGA sample  ...

RGA不支持BGR和422,422也是软转的
作者: shopping    时间: 2019-10-11 14:46
jefferyzhang 发表于 2019-10-11 14:28
RGA不支持BGR和422,422也是软转的

刚刚回去看了下官方文档,果然不支持 BGR 和 YUV422,又走了弯路。
作者: will92    时间: 2019-10-21 16:31
shopping 发表于 2019-10-11 14:46
刚刚回去看了下官方文档,果然不支持 BGR 和 YUV422,又走了弯路。

请问rga的官方文档在哪里可以找到呢?
作者: shopping    时间: 2019-10-21 17:00
will92 发表于 2019-10-21 16:31
请问rga的官方文档在哪里可以找到呢?

官方给的固件包里附带的,docs目录下。
作者: heyunteng251314    时间: 2019-10-29 16:01
版主你好!对于RGA模块,我有个疑问的是mRga->ops->go(mRga)这个函数的程序代码是跑在RGA模块上嘛?
我下这里面的DEMON(https://github.com/Jerzha/samples-toybrick-rga),是安卓的,哪有LINUX端的呢?
作者: jefferyzhang    时间: 2019-10-29 16:20
heyunteng251314 发表于 2019-10-29 16:01
版主你好!对于RGA模块,我有个疑问的是mRga->ops->go(mRga)这个函数的程序代码是跑在RGA模块上嘛?
我下这 ...

安卓linux是通用的,并且安卓有自己单独一套接口。
Linux你用这个就可以了。
RGA是一个IP核,不会执行cpu代码的。你说的go就是主控把数据送给rga正在处理了。
作者: heyunteng251314    时间: 2019-11-4 16:49
jefferyzhang 发表于 2019-10-29 16:20
安卓linux是通用的,并且安卓有自己单独一套接口。
Linux你用这个就可以了。
RGA是一个IP核,不会执行cpu ...

哦!谢谢
作者: 时光丶    时间: 2019-12-2 10:27
shopping 发表于 2019-10-21 17:00
官方给的固件包里附带的,docs目录下。

我找到了一个RKDocs文件夹,请问rga的文档叫啥?
作者: shopping    时间: 2019-12-2 10:39
时光丶 发表于 2019-12-2 10:27
我找到了一个RKDocs文件夹,请问rga的文档叫啥?

具体什么名字忘了,反正名字里面有RGA,记得docs目录下叫RGA的就一个,你直接搜RGA就行了

作者: 时光丶    时间: 2019-12-2 10:43
shopping 发表于 2019-12-2 10:39
具体什么名字忘了,反正名字里面有RGA,记得docs目录下叫RGA的就一个,你直接搜RGA就行了
...

那我的固件包里面没有叫rga的文件,能给我传一个吗?
作者: 时光丶    时间: 2019-12-2 17:17
rga->ops->setSrcFormat(rga,V4L2_PIX_FMT_NV21,1280,720);我从摄像头采集到的数据是nv21的,但是设置格式nv21他会阻塞在这里,是不是不支持nv21啊?我手动把数据转换成nv12然后换成nv12格式就没问题




欢迎光临 Toybrick (http://t.rock-chips.com/) Powered by Discuz! X3.3