首页 / 资讯中心 / 文章详情

RGA2硬件加速实战:嵌入式Linux下NV12图像处理与性能优化

RGA2硬件加速实战:嵌入式Linux下NV12图像处理与性能优化 ★ FEATURED ARTICLE
在嵌入式Linux上做视频采集、显示、编解码只要数据量稍微上来CPU软转就能让你怀疑人生。尤其手里有瑞芯微的片子比如RV1106、RV1126、RK3568这些时却还让CPU去跑颜色空间转换和缩放属实有点暴殄天物。RGA2这个硬件加速单元就是专门干这个脏活累活的。这篇文章我从RGA2硬件加速的原理讲起结合NV12图像处理的实战把我踩过的坑和验证过能跑的代码逻辑分享出来给正好在做这块的朋友做个参考。1. 项目背景与方案选型1.1 为什么需要RGA2硬件加速做嵌入式图像采集和处理的同学应该都有体会视频流从Sensor进来经过ISP之后一般输出的都是NV12或者RAW格式的数据。可感光元件、显示屏幕、算法模型的输入格式这仨往往不是一回事。举个例子你从摄像头拿到的是1920x1080的NV12数据可人脸检测模型内部要的是640x640的RGB图而UI显示层要的可能又是1080P的YUV叠加层。中间这一大段数据搬运和转换如果用CPU跑Data Abort不至于但CPU占用率直接拉满主控芯片的发热和掉帧是少不了的。RGA2就是瑞芯微芯片里一个独立的2D图形加速硬件模块。它能干什么缩放、裁剪、格式转换、旋转、镜像。NV12转RGB、BGR转NV12、YUV转灰度这种日常操作RGA2就是为它们而生的。用一句话概括这套方案的核心思路把重复性高、计算量大、CPU做起来不划算的图像预处理操作全部下沉到RGA2硬件单元去完成CPU只负责配置参数和发起指令剩下的搬运交给DMA计算交给GPU但用RGA更省。我最初接手RV1106项目的时候ISP回传的是1080P30fps的NV12流为了喂给NPU做检测每帧需要缩放并转成RGB888。最开始用CPU模拟实现结果四核A7全跑满CPU占用率超过70%图像分辨率一旦升高或者帧率到60fps系统直接卡顿。后面改用RGA2同样流程CPU基本处于休眠状态整机负载下降了90%以上。做过嵌入式性能优化的人应该明白省下的CPU资源意味着你的产品可以多跑几个算法或者直接把主控降一个档次节约BOM成本这在实际项目里是真金白银的收益。1.2 RGA2能做什么不能做什么RGA2全称是Raster Graphic Acceleration Unit 2在瑞芯微的SoC里它一般作为独立硬件模块存在拥有自己的寄存器控制接口和DMA通道不是GPU。搞清楚这一点很重要很多人会问“我用OpenGL ES不也能做图像处理吗”能但没必要。GPU做的是复杂的三维图形渲染驱动栈重、初始化慢、对嵌入式平台来说开发门槛高。RGA2则是专门的2D硬件接口简单调用一次只需要几十行代码延迟在微秒级别。RGA2的主要能力可以做如下归类格式转换支持NV12、NV21、YUYV、RGB565、RGB888、ARGB8888等常见格式互转。实测下来NV12转RGB888是最常用的场景。几何变换支持最高8倍缩放具体看芯片版本、90/180/270度旋转、水平/垂直镜像翻转。裁剪与合成支持源图像局部区域裁剪后缩放支持多个输入叠加合成到输出画布。色彩调节支持亮度、对比度、饱和度、色相调节部分版本支持全局alpha混合。RGA2不是万能的它处理不了复杂滤镜做不了深度学习推理也不适合处理超过其最大分辨率限制的超大图像。对于1080P、4K级别的视频流预处理它绰绰有余但如果你要跑透视变换、鱼眼矫正这种非线性几何操作还是别指望RGA2了老老实实上CPU或NPU或者用GPU做自定义Shader。因此做方案选型时RGA2的定位就是“图像预处理加速器”它扛最繁重、最频繁、最简单的像素搬运和变换工作复杂逻辑交给上层应用。2. RGA2硬件加速原理深度拆解2.1 RGA2的工作链路RGA2在芯片内部的连接方式一般可以通过芯片手册的存储结构图看到。简单说RGA2挂在内核内存总线上CPU通过Locker寄存器或MMU接口把源地址、目标地址、图像格式、宽高、裁剪框、旋转角度等参数填充到对应的寄存器中然后写一个使能位。RGA2硬件按照寄存器配置通过DMA把源数据从DDR搬运到内部缓冲区经过像素处理引擎做缩放、格式转换、旋转等操作再通过DMA写回目标内存。整个过程不需要CPU逐像素干预。这套机制决定了RGA2的工作模式配置→触发→轮询/中断→完成。在Linux系统下驱动把寄存器封装成了ioctl接口应用层调用rk_rga的RGA2_...接口或者直接操作内存映射寄存器完成控制。更上层瑞芯微的Rockit、MPP、Camera HAL等多媒体框架也封装了RGA调用所以在实际开发中你可以直接调用RGA库也可以在高阶框架里配置参数让框架帮你调用RGA。这个工作链路还引出一个重要思维凡是能从循环中拿出来交给RGA2的操作就不要留在CPU里面。比如视频采集循环里每帧从ISP拿到NV12数据后可能要在送往编码器前做一次裁剪送往预览显示前做一次旋转送往NPU前做一次缩放转换。这些原本是三个不同模块处理的事现在全部压到一个RGA2通道上用一个统一的RGA2_...调用串起来流程看起来就变成流水线式的硬件搬运这也是嵌入式软件性能优化里面典型的“用硬件资源换CPU周期”。2.2 NV12为什么在图像处理中占据特殊地位NV12在视频链路里真的是随处可见它属于YUV420sp格式的一种也就是Y通道单独一张平面UV通道交错打包在另一张平面里。它的排列方式是先连续存放全部像素的Y分量分辨率是W×H然后接着存放UV交错数据UV的总长度是W×H/2其中U、V交替排列形如U0 V0 U1 V1…为什么NV12这么流行因为它兼容了视频编码和显示链路的硬件需求。无论是H.264/H.265编码器、ISP输出还是显示控制器原生都支持NV12的读取。如果你在Sensor输出raw Bayer后交给ISPISP处理完给出来的多半就是NV12。把NV12转成RGB从原理上讲就是按YUV到RGB的线性变换公式算一遍但因为有采样格式的差异取UV的时候需要注意对应关系。NV12的UV采样是2×2共享一个色度点也就是每四个Y对应一个U和一个V而在NV12的排列中UV是交替存储的。不熟悉这块的初学者经常在这里栽跟头取UV的索引取错了画面整体偏色或者色调错乱。NV12对应的色彩空间转换也是RGA2能效最高的场景之一。因为RGA2的像素处理引擎内置了YCbCr到RGB的转换系数矩阵有的芯片版本还支持BT.601和BT.709两种色彩空间的选择。在制作播放器或者显示叠加层的时候用错色彩空间是常见的偏色问题。比如你用BT.601标准转换出的RGB放到按BT.709标准进行色彩管理的显示器上整体画面会变“假”泛白或者发灰。而如果我们直接用RGA2做转换硬件就会按你配置的系数矩阵操作所以配置前一定要确认视频流是标清还是高清从而选择正确的色彩空间标准。2.3 性能指标与底层效率RGA2能跑多快很多人关心。以RV1106为例RGA2支持的最大输入尺寸一般是8192×8192缩放比例最大支持1/8倍到8倍。在1080P的NV12转RGB888同时缩放到640×640这种典型操作下RGA2单帧处理时间大概在1ms到2ms左右。考虑到有驱动的配置开销整链路下来处理100帧以上的能力是有的。如果你的应用场景是30fps甚至60fps的视频流RGA2的性能完全不是瓶颈真正的瓶颈往往在内存带宽上。因此在使用RGA2的实际项目中需要考虑两个核心效率因素内存分配与对齐RGA2对输入输出内存的物理地址连续性和对齐有要求。RGA2驱动内部默认对宽高有16像素对齐、对齐后的stride和原始宽不一定相等。如果上层代码没有给RGA2分配对齐后的缓冲区而是使用普通malloc分配的内存那要么驱动直接报错要么性能下降严重。实际写代码时要用dma_heap或者ion分配物理连续内存并且在配置源地址时准确传stride信息这一点在4K分辨率时尤为重要因为跨行读取时若不对齐DMA的效率会下降很多。同步机制RGA2是异步硬件单元配置完成后你需要等待它的中断或者轮询状态寄存器来确定完成。开发中有两种模式一种是阻塞调用写入配置后一直查询完成标志简单可靠但会占用一定CPU时间另一种是异步模式配置完成后立即返回等中断信号到达后再进行后续处理。在高帧率的工程中异步模式能把平均延迟降下来但需要额外注意线程安全和缓冲生命周期因为你可能在RGA2还在读源缓冲区的时候就提前释放了它造成内存访问错误或画面花屏。3. NV12格式解析与RGA2图像处理实战3.1 NV12内存布局与对齐规则在写任何NV12相关代码前先要在一个127×127的图上搞明白它的内存布局。假设图像宽W、高H那么Y平面的大小是W×HUV平面大小为W×H/2。有些芯片和库要求宽度16对齐高度2对齐。比如W127时stride可能是128对齐到16Y平面大小是128×127UV平面为128×63或者128×64高度也有对齐要求。这样整帧数据实际占用的内存就不是W×H×1.5这么简单而是要按stride计算。RGA2的驱动在librga中体现为一个RgaCrop之类的结构体配置。里面填的宽高是实际有效图像范围但地址偏移和stride必须按对齐后的内存布局来。我见过太多人说“RGA转出来图像是斜的”或者“颜色是花的”最后定位到原因是配置的w/h和实际分配内存的stride不一致。NV12的数据结构定义可用结构体来表示typedef struct rga_img_info_t { unsigned int addr; // 颜色的缓冲区地址 unsigned int uv_addr; // UV缓冲区地址对NV12来说在y_addr w_stride * h之后 unsigned int vir_w; // 虚拟宽度即stride对齐后 unsigned int vir_h; // 虚拟高度 int format; // 设为RGA_FORMAT_YUV420SP } rga_img_info_t;很多初学者直接只填addr不填uv_addr和vir_w/vir_h结果就是图像错位。给RGA2传地址时Y和UV地址必须分开指定。对于RGB888这类packed格式只有一份地址而NV12需要同时指定Y平面和UV平面。这个点看起来不起眼却是整个NV12处理链路中最容易出错的环节。3.2 RGA2核心API详解从初始化到单帧处理在Linux用户空间使用RGA2通常有两种途径一种是直接调用librga动态库另一种是调用Linux内核的/dev/rga节点ioctl。librga封装得更好一点跨平台方便。瑞芯微官方SDK里librga一般位于external/librga编译后生成librga.so。核心流程分为四步初始化、填写配置、调用执行、等待完成。第一步初始化RGA环境。在librga中调用前通常需要确保Ion/DmaHeap设备可用ls /dev/dma_heap/system-uncached如果没有DmaHeap一般可以退而求其次使用ION但新内核上DmaHeap更常用。初始化Rga对象#include RgaApi.h #include rga.h // 一般不需要显式调用初始化函数直接使用RgaBlit等API即可 // 但在某些SDK版本上需要调用 rga_set_mem_mode(RGA_MODE_DMA_HEAP);第二步准备源和目标图像信息。以NV12转RGB888尺寸缩放为例static int rga_nv12_to_rgb888( int src_fd, int src_w, int src_h, uint8_t *dst_rgb, int dst_w, int dst_h) { rga_info_t src; rga_info_t dst; memset(src, 0, sizeof(src)); memset(dst, 0, sizeof(dst)); src.fd src_fd; // 源缓冲区的fd src.mmuFlag 1; // 使用MMU驱动内部处理地址映射 src.rect.xoffset 0; src.rect.yoffset 0; src.rect.width src_w; src.rect.height src_h; src.format RK_FORMAT_YCbCr_420_SP; // 对应NV12 dst.virtualAddr (unsigned long)dst_rgb; dst.mmuFlag 1; dst.rect.xoffset 0; dst.rect.yoffset 0; dst.rect.width dst_w; dst.rect.height dst_h; dst.format RK_FORMAT_RGB_888; int ret rga_set_src_info(src, 0, 0); if (ret 0) return ret; ret rga_set_dst_info(dst, 0, 0); if (ret 0) return ret; ret RgaBlit(src, dst, NULL); return ret; }这里有一个特别容易犯的错误src.fd和dst.virtualAddr混用。有些场景下你拿到的源数据是mmap出来的用户空间虚拟地址没有fd这时应该把地址填到src.virtualAddr并设置mmuFlag1。如果你既有fd又有virtualAddr最好只用一个字段否则驱动内部可能会混淆。这个我踩过坑初始化时给源地址传了虚拟地址但没设置mmuFlag跑起来结果变成花屏回头看了半天才发现是flag没对。第三步执行转换int ret RgaBlit(src, dst, NULL); if (ret ! 0) { // 错误处理可以调用 rga_get_error_string(ret) 打印错误 }RgaBlit是同步调用。在librga的实现里调用完成之后驱动会等待硬件中断所以返回时数据已经就绪。第四步在异步场景中等待或者使用dma fence。一般我们用同步方式更简单因为RGA2单帧很快同步的延迟在微秒级不至于卡死线程。如果是高帧率流水线可以在一个专门的处理线程里做同步调用配合双缓冲甚至三缓冲解耦。3.3 实用案例一NV12画面裁剪缩放摄像头采集的原始画面往往比模型需要的大直接缩放又怕宽高比变形。比如检测模型需要640×640正方形输入而摄像头画面是1920×1080的16:9画面直接拉伸会把人脸拉变形所以通常先做Center Crop裁剪再缩放。操作逻辑是先算好裁切框。假设原图宽1920、高1080目标输出宽640、高640为了保持比例可以按1080这个高度作为基准裁出一个1080×1080的正方形区域然后把这块区域等比缩放到640×640。在RGA2里怎么实现裁剪还是用rga_info_t结构体但需要对源图像rect做文章src.rect.width 1080; // 裁剪宽度 src.rect.height 1080; // 裁剪高度 src.rect.xoffset (1920 - 1080) / 2; // 水平居中裁切 src.rect.yoffset (1080 - 1080) / 2; // 垂直方向从0开始即可注意因为NV12的UV平面存储紧凑xoffset和yoffset必须是偶数。原图颜色采样格式中2×2的Y块共享一组UV如果你从奇数坐标开始裁剪UV相位就错位了结果就是图像颜色在垂直/水平方向出现栅格状偏差。实际测试中xoffset0或2没问题xoffset1时画面边缘会出现周期性色偏严重时会整体偏绿。这就是“偶对齐”限制官方文档有写但很多入门朋友会忽略。裁剪后再缩放对RGA2就是一锤子买卖。它在硬件里先做裁剪再做缩放没有额外开销输出的640×640 RGB数据直接可以喂给NPU。整个函数跑下来单帧不到2ms。3.4 实用案例二NV12旋转与镜像的坐标陷阱经常有人要做竖屏显示摄像头Sensor是横屏输出主控端需要把画面顺时针转90度。在RGA2里设置旋转很简单src.rotation RGA_ROTATE_90; // 顺时针旋转90度但旋转对内存布局有隐藏要求。旋转90度后输出的宽高会互换。如果你设置的dst.width仍然是原来的W而不是HRGA驱动会直接报错或者输出截断的图。而且旋转操作通常要求输入输出缓冲区的宽高都是偶数毕竟是2的倍数对齐规则。镜像操作也常用。在自拍或者前视摄像头预览时屏幕显示的画面往往需要水平镜像和用户面对面才符合直觉。RGA2中开启镜像src.rotation RGA_FLIP_H; // 水平翻转镜像配合旋转的枚举值可以组合使用但需要注意枚举定义的语义。有些版本直接提供RGA_TRANSFORM_ROT_90和RGA_TRANSFORM_FLIP_H的组合枚举有些需要你设置transform的bit位。用librga的rga_set_src_info接口时通常在src.rotation字段中传入RGA_ROTATE_MASK一类的位掩码。这块建议查阅具体芯片的librga头文件因为不同版本API不完全一致。3.5 实用案例三叠加矩形框到NV12画面在视频流上画检测框是一种常见的调试需求。直接在NV12上改像素很麻烦因为UV是交错存储的画一个红色的框需要同时修改Y和UV。用CPU实现的话你可能要先定位到矩形区域的Y坐标再定位到对应的UV坐标分别填充值而且填充色要根据颜色转换公式反推。稍微算错一点框的颜色就偏了。RGA2的合成能力在这时候就派上用场。思路是准备一张ARGB8888的小画布在上面用CPU画好纯色矩形框然后把这张ARGB小图作为输入源把NV12原图作为目标输出利用RGA的合成或者拷贝模式把小图叠加到原图上。但需要注意RGA2的src和dst格式可以不同硬件会在合成时自动做格式转换。所以从NV12原图出发叠加ARGB框输出仍是NV12这个操作RGA2原生支持。实现时把带框的ARGB图作为src把目标NV12作为dstsrc.rect设置为框区域dst.rect设置为框的坐标位置然后调用RgaBlit。这里还有一层隐含关系因为RGBA到YUV的转换也是硬件完成你不用手动算RGB到YUV的映射。画红色框时只需要把ARGB画布的像素填成0xFFFF0000硬件就会自动转换。这个流程做下来比纯CPU写框的效率高一个数量级不止。4. 驱动适配与基于RGA2的图像处理流水线4.1 设备树与驱动开启的关键配置RGA2的驱动在正常SDK中默认开启但如果你想把它用在定制板卡或者裁剪系统的场景下需要确认设备树和内核配置。设备树里一般有rga节点rga { status okay; };如果status是disabled/dev/rga节点就不会出现。另外需要检查内核的DMA Heap和ION配置dma_heap { status okay; };实际开发时可以先跑一下官方测试程序确认RGA工作正常rga_test 1920 1080 NV12 1280 720 RGB888如果不出错说明驱动环境正常如果报错优先查看dmesg输出RGA驱动在出错时会打印rga2 ... check failed之类的信息。4.2 完整流水线ISP采集到NPU输入的RGA2数据通路在瑞芯微平台上做视觉产品典型流水线是Sensor→ISP→NV12→RGA2→RGB→NPU。Rockit或者Camera HAL里一般都会默认做一次RGA转换因为NPU的输入一般要求RGB888或RGB565格式。但有时候SDK默认流水线未必符合你的场景。比如你要同时拿到NV12给编码器还要RGB给检测那ISP可能只输出一路NV12。编码器直接用NV12没问题检测则要RGA2转RGB。此时可以建立两个RGA2任务任务ANV12原图裁剪一半画面转RGB888输出到NPU绑定的内存任务BNV12原图缩放到1080P输出到编码器buffer两个任务在驱动层面是独立通道可以同时提交但需要注意IOMMU和内存分配是否冲突。如果用的是同一个buffer pool就必须做同步一般通过信号量或fence机制。在RV1106上实测一路1080P30fps的NV12流同时跑上述两个RGA2任务RGA2负载约40%CPU在2%以下整体流畅无掉帧。这个方案比在CPU上分别做缩放、转格式性能提升了十倍以上屏幕显示方面还额外多出一路预览图像也可以让RGA2做虎合成。4.3 内存管理策略dma_heap、ion与userptrRGA2拷贝没有真正的输入输出数据拷贝它全部通过DMA访问内存地址。要让数据能到达RGA2就必须把物理内存映射到进程的虚拟地址空间里并且保证这块内存是物理连续的。传统malloc分配的内存不是物理连续的只适用于小尺寸和没有MMU的场景。目前推荐的做法是用共享内存DMA Heap#include linux/dma-heap.h #include sys/ioctl.h int alloc_dma_buffer(int size, unsigned int *fd, void **map) { int heap_fd open(/dev/dma_heap/system-uncached, O_RDWR); if (heap_fd 0) return -1; struct dma_heap_allocation_data alloc {0}; alloc.len size; alloc.fd 0; alloc.fd_flags O_RDWR; if (ioctl(heap_fd, DMA_HEAP_IOCTL_ALLOC, alloc) 0) { close(heap_fd); return -1; } *fd alloc.fd; *map mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, *fd, 0); close(heap_fd); return 0; }这里有个细节DMA Heap分配的内存是页对齐的不用额外对齐。但RGA2对宽高尺寸有对齐要求你需要分配的实际size为aligned_stride * aligned_h * 3 / 2这里的stride和h是向上对齐到16和2之后的数。在某个项目里我犯了“只分配了原始宽高对应的size”的错。源图是1920×1080我分配了1920×1080×3/2个字节理论上够用但RGA2驱动在检查内存范围时因为stride对齐到了19201920本来就是16的倍数高度1080对齐到1088按对齐后的范围计算后断言缓冲区太小直接返回错误。后来分配时多留了一些余量问题就解决了。经验是分配NV12缓冲区时尽量分配为(width_aligned * height_aligned * 3 / 2)其中width_aligned ALIGN(w,16)height_aligned ALIGN(h,16)这样永远不会因为对齐问题导致驱动报错。4.4 与MPP、Rockit框架的配合方式如果你的项目中用了瑞芯微的MPP视频编解码库MPP在解码输出时一般会直接给到NV12帧。MPP的MppBufferGroup默认分配的是自定义内存也能用来做RGA2的源/目标。但MPP buffer可能不是裸的物理连续内存而是通过自身的buffer group管理在RGA2访问前需要获得它的fd。Rockit框架里VENC通道默认从VI接收NV12数据但如果你想在中间插入一个自定义步骤比如先缩放再编码Rockit里通常会让VI输出到RGA模块再让RGA输出到VENC。这种模块间传递的参数本质就是buffer fd的流转所以在上层写好RGA2的插件挂到Rockit的semantic上即可。这种框架配合下的关键优化是零拷贝ISP输出、RGA2转换、编码器输入共用同一个物理内存只是地址被多次映射、不同模块轮流读写。零拷贝在视频处理中能减少大量数据搬运把整体时延降到最低。实测显示用零拷贝链路从Sensor到编码器输出的整体时延约为40ms左右其中RGA2只占几毫秒其余大多是Sensor曝光和编码器排队的时间。5. 常见问题与排查技巧实录5.1 画面偏色、花屏与绿边现象NV12转RGB后整个画面的颜色发绿或者发紫。 排查方向一是色彩空间标准是否填对NV12通常是BT.601但高清源可能需要BT.709二是Y和UV的地址填反或偏移错位导致UV分量错乱三是对齐问题stride按16对齐后Y平面的大小不再是W×H。现象裁剪区域边缘出现绿色条纹。 排查方向这基本就是xoffset/yoffset没有做偶数对齐。NV12的Y采样密度是UV的4倍裁剪起点如果不是偶数Y取到的像素点对应的UV无法匹配硬件会用相邻的UV补偿表现出来就是边缘“彩边”或“绿边”。解决办法很简单任何裁剪偏移都向下取偶数。现象画面只有上半部分有图下半部分灰屏。 排查方向说明图像高度或者stride传错了。如果地址正确但高度是按未对齐的值传的硬件读完了源数据却往错误的目标地址写就会出现只渲染半幅的情况。请检查ic率、高度、stride三者是否一致。5.2 驱动报错与性能排查RGA2调用失败时最常见的报错是EINVAL。EINVAL出现后别急着找RGA代码先查三样东西格式枚举是否属于当前芯片支持的格式列表宽高是否超出RGA2最大限制输入地址是否有权限访问。SDK版本不同库头文件里有的枚举名略有差异比如RK_FORMAT_YCbCr_420_SP也可能写成RK_FORMAT_YUV420SP拼错了编译不报错但驱动会拒绝。性能方面如果调用RgaBlit有很明显卡顿或者高延迟可以先确认是不是连续调用的频率太高。RGA2本身有中断处理开销如果你每帧都执行几十次小区域转换中断会和调度器打架。解决方案是合并操作把多次小图转换合并成一次大图转换目标区域裁剪或者使用RGA2的合成能力一次处理多个目标区域。另外一个性能类似的坑是使用RGA2时表面看起来CPU占用不高但内存带宽打满导致系统整体卡顿。这个问题在高分辨率多路视频流时特别明显RGA2搬运的数据量大时DDR带宽被占满CPU去访问内存也需要排队。如果你发现RGA2处理很快但NPU或者编码效率反而下降优先看DDR带宽利用率必要时降低帧率或者分辨率。5.3 在线升级与多版本RGA的兼容性不同芯片的RGA版本不一样。RV1106这种轻量级芯片和RK3588的RGA版本差异很大在RK3588上可能叫RGA3多了一个更强的NN输入预处理能力但API接口变化不大。如果你在RK3588上调好了RGA代码移植到RV1106上运行大概率能编译通过但某些高级特性可能不支持。所以严格来说移植前要查看对应SDK的librga头文件差异确认枚举值和结构体字段兼容。遇到字段不同最简单的做法是重编SDK对应版本的librga而不是手动改上层代码去适配旧库。我手上的板卡换过一次SDK版本从旧版本升级到新版本之后原来跑得好好的RGA2转换开始偶发出现画面撕裂。后来对比发现新SDK里librga会默认打开RGA的SYNC模式也就是每次RgaBlit变成同步等待。旧版本里是异步模式的两个线程同时在写同一个目标buffer导致竞争。解决办法是调用前把目标buffer和源buffer绑定到不同的buffer group或者加锁保证同一时刻只有一个线程调用RgaBlit。如果你也遇到偶发花屏优先检查多线程的并发调用是否有锁。5.4 调试利器rga_debugfs和官方测试工具瑞芯微提供了维护用的调试节点和测试程序。在Linux内核里RGA2驱动通常会创建debugfs节点比如/sys/kernel/debug/rga/可以读寄存器状态、查看最近一次任务是否报错。使用这些节点前先确认内核挂了debugfsmount -t debugfs none /sys/kernel/debug cat /sys/kernel/debug/rga/versionType为RGA2的版本信息能提示你当前芯片固件的RGA能力。官方SDK里通常还有rga_test测试程序支持参数化调整输入输出格式、尺寸、坐标、旋转角度等。遇到问题后用rga_test复现可以快速排除API使用层面的错误把问题锁定到驱动或者硬件层。我实际调试时经常先用rga_test跑一遍纯缩放和纯格式转换确认驱动正常再检查上层代码哪里参数传错了。这个思路可以帮你在排查问题时省掉大量时间。6. 从RGA2到多路视频处理的扩展思考RGA2在不同芯片上的定位其实有细微区别。在RV1106这种侧重IPC场景的芯片上RGA2主要配合ISP和编码器在RK3568、RK3399等通用平台RGA2也可以用于UI合成、截图、实时滤镜等。关键在于理解只要是2D的图像搬运和变换先把需求梳理清楚能交给RGA就交给RGA。多路视频处理时建议用队列异步方式管理RGA2任务。按视频流的路数创建对应数量的RGA2请求队列请求提交后立即返回完成时通过回调或者Fence通知。这样RGA2硬件不会因为等待上层慢动作而空转吞吐量接近硬件极限。如果板卡上有多个RGA模块还可以把任务拆分到不同RGA模块并行处理实现多路4K同时缩放。这个思路适合在IPC NVR或者多目全景相机项目中使用。回到RGA2与NV12这个组合上做视觉产品的稳定高效输出绕不开这些底层细节。希望这篇文章能让你在实际开发时少走弯路。最后再分享一个小经验调试RGA2的问题时先把格式转换和缩放分开测试确认没问题了再合到一个任务里跑这样排查速度会快很多。
阅读完成 · 觉得有帮助?
咨询建站