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

Rockchip RGA实战:2D硬件加速引擎的关键技术与性能优化

Rockchip RGA实战:2D硬件加速引擎的关键技术与性能优化 ★ FEATURED ARTICLE
1. 认识Rockchip RGA一块被低估的2D硬件加速引擎先说结论如果你在RK3588、RK3568、RV1126这类瑞芯微平台上做视频处理、AI图像预处理或者UI合成却还在用CPU做缩放、格式转换、旋转那真的亏大了。RGARaster Graphic Acceleration是Rockchip芯片内部专门负责2D图像处理操作的硬件模块它能以极低的CPU占用完成图像缩放、旋转、裁剪、格式转换、叠加和透明度混合等任务。我第一次接触RGA是在一个RK3588的项目上当时要做四路摄像头画面的实时拼接预览每路1080p的NV12帧要缩放到720p再叠加OSD信息。一开始用CPU软缩放加memcpy四路加起来直接吃掉了三个大核接近80%的算力CPU温度蹭蹭往上走。后来把图像处理部分全部切到RGACPU占用率掉到不足10%画面还更流畅了。从那以后凡是芯片上带RGA的平台我基本不会再写纯CPU的图像处理路径。这篇文章适合哪类人看正在做Rockchip平台图像处理方案选型的工程师、刚拿到RK3588开发板想跑通RGA流程的新手、以及被OpenCV软件处理性能问题折磨到怀疑人生的朋友。我会把RGA的核心功能、API设计思路、实测可用的代码、以及我在项目里踩过的坑全部整理出来内容以RK3588平台为例但原理和代码对其他带RGA的Rockchip芯片同样适用。1.1 RGA在芯片里的位置和设计初衷RGA全称Raster Graphic Acceleration直译过来是光栅图形加速器。它在芯片中的位置通常在ISP和显示控制器之间也可以被CPU通过Librga库直接调用。它和GPU不是一回事GPU负责复杂的3D渲染和通用计算而RGA是一颗专注2D图像操作的专用引擎功耗低、延迟小、响应时间可预期。设计初衷非常朴素视频应用中大量图像处理操作是重复且规整的比如把1080p缩放到720p把NV12转成RGB888把图像旋转90度。这些操作如果用CPU逐像素处理效率低不说还占用宝贵的算力。如果用GPU处理则需要管理复杂的图形上下文延迟不可控功耗也高。RGA就是中间那条高速路只要把源地址、目标地址、格式、分辨率这些参数配置好硬件自动完成整块图像的处理。从架构上看RGA内部包含缩放模块、旋转模块、格式转换模块、颜色空间转换模块和alpha混合模块。这些模块以流水线方式工作所以一次调用可以同时完成多种操作比如缩放格式转换旋转一条命令搞定不需要分多次执行这也是RGA性能强劲的关键原因之一。1.2 RGA能带来多少性能收益用一个我自己实测的数据来说明。在RK3588上处理一张1920x1080的NV12图像缩放到1280x720CPU软实现NEON优化版本平均耗时约6-9毫秒大核占用率满负荷RGA硬件处理平均耗时约0.8-1.5毫秒CPU占用率基本为零如果是格式转换比如NV12转RGBA8888CPU需要处理YUV到RGB的换算逻辑耗时在10毫秒以上RGA同样在1-2毫秒内完成。在连续的视频帧处理场景下这个性能差距意味着你可以在同样的时间内多跑两三路视频流或者在24fps的实时性要求下留出大量余量做AI推理和其他业务逻辑。有一个容易被忽略的点RGA不仅仅是省CPU它还能降低系统功耗。硬件模块完成同样的工作消耗的能量远低于CPU这在电池供电的边缘设备或者需要长时间无人值守的场景下非常关键。我在RK3588的智能摄像头项目上把图像预处理全部切到RGA之后整机功耗下降了约15%发热量也明显改善。1.3 RGA的硬件能力边界先看清再动手不同型号的Rockchip芯片RGA的规格不一样。以RK3588为例最大输入分辨率8192x8192需MMU支持最大输出分辨率8192x8192支持旋转角度0度、90度、180度、270度支持镜像水平镜像、垂直镜像缩放比例1/16到16倍支持的输入输出格式RGB565、RGB888、RGBA8888、NV12、NV21、YUV420、YUV422等支持的色彩空间BT.601、BT.709、BT.2020这里列的是RK3588的参数RK3568和RK3399的RGA版本略低格式支持和最大分辨率会打折扣。开始写代码之前务必查阅对应芯片的TRMTechnical Reference Manual或者RGA的版本说明确认你需要的格式是否在支持列表里。我见过太多人栽在我的板子不支持这个格式这种问题上白费几天功夫。另外一个重要概念是MMUMemory Management Unit。带MMU的RGA可以使用普通用户空间的虚拟地址不需要物理连续内存这对开发体验来说是很大的解放。早期不带MMU的RGA如RK3288上的老版本要求输入输出buffer必须是物理连续且对齐的内存否则驱动直接报错。现在RK3588、RK3568这些主流芯片都带MMU用malloc分配的buffer也能正常工作但性能上仍有差异后面我会细讲。2. 核心功能拆解硬件能做的那些事2.1 从老API到新APILibrga库的演进RGA的用户态接口经历了两次比较大的变革。第一代API是基于rga_info_t结构体的ioctl方式需要手动设置每个字段容易出错代码写起来也很繁琐。从Rockchip发布Librga 1.x后期开始官方推出了面向对象的第二代API使用rga_buffer_t描述图像buffer用im_rect描述图像区域配合imconfig和improcess两个核心函数完成操作。第二代API的核心设计理念是区域处理。你可以把一张图像的不同区域分别配置成不同的操作对象甚至把多个buffer组合到一次调用中。这在业务层面更贴近实际需求比如视频墙的场景里需要在同一帧画面上叠加多个不同位置的OSD图层用老API你要调用多次ioctl用新API一次improcess就能完成。从工程角度我强烈建议新项目直接使用Librga 2.x版本也就是新API。不只是代码更清晰而且新版本修复了大量老驱动中的格式兼容性问题还优化了性能。获取方式有两种从Rockchip的Github仓库rockchip-linux/librga拉源码编译或者在SDK的external/librga目录下找到预编译版本。源码编译很简单标准CMake流程。2.2 我用得最多的6个功能场景图像缩放这是RGA最常用的功能。视频处理链路里摄像头输出1080pAI模型输入需要640x640这个缩放必须高效完成。用RGA一行代码搞定速度快到可以忽略不计。格式转换NV12转RGB888、RGB888转NV12、YUV420转RGBA8888常见组合RGA全部原生支持。注意YUV到RGB转换涉及色彩空间标准的选择RGA默认使用BT.601 Limited Range如果遇到颜色偏淡或偏艳的问题记得检查色彩空间配置。旋转与镜像在竖屏应用、前置摄像头预览、车载影像这类场景非常常用。RGA支持90/180/270度旋转以及水平/垂直镜像一次调用完成不像CPU实现需要大量内存搬运。裁剪从大分辨率图像中截取指定区域然后可以选择性缩放输出。这个功能在做图像ROI检测时非常有用比如只把画面中的车牌区域裁出来送OCR识别。图像叠加与透明度混合将前景图像叠加到背景图像上支持alpha通道混合。UI层和视频层合成、OSD信息叠加、水印添加都是用这个功能。RGA的alpha混合效率非常高实时视频上叠加复杂的半透明UI也不会卡顿。纯色填充与颜色填充把目标buffer填成指定颜色常用于清屏操作。看起来简单但用CPU填充一块4K的RGBA buffer也要消耗不少cycleRGA瞬间完成。2.3 必须吃透的数据结构rga_buffer_t与im_rect新API里最核心的数据结构是rga_buffer_t和im_rect。我用自己的理解把它们简化一下rga_buffer_t描述的是一个完整图像buffer包含宽高、格式、内存地址、stride步长等信息。它描述的是我有一块多大的图像数据放在哪里。与之配套的内存信息存放在rga_buffer_t内部关联的描述符中通过wrapbuffer_*系列函数来创建和填充。im_rect描述的是一个区域。它有x、y、width、height四个字段本质上是相对于buffer原点的一个矩形范围。RGA操作的粒度是可以精确到像素区域的这个设计非常实用。比如你要处理一张大图上的一小块区域直接用im_rect圈定就行RGA只会处理这块区域速度更快。代码里典型的创建方式rga_buffer_t src wrapbuffer_virtualaddr(src_ptr, src_width, src_height, src_format); rga_buffer_t dst wrapbuffer_virtualaddr(dst_ptr, dst_width, dst_height, dst_format); im_rect src_rect {0, 0, src_width, src_height}; im_rect dst_rect {0, 0, dst_width, dst_height};注意wrapbuffer_virtualaddr用于虚拟地址wrapbuffer_physicaladdr用于物理地址。如果你的buffer来自dma-buf、ION或DMA heap用wrapbuffer_fd函数传入文件描述符这能实现真正的零拷贝流程。这块我在后面性能优化部分会详细展开。2.4 格式选择与内存对齐新手最容易翻车的区域RGA对内存对齐有严格的要求。老版本要求宽高和stride必须是16或64的整数倍新版本通过参数配置可以放宽但强烈建议遵守对齐规范违背对齐要求轻则性能下降重则出现花屏甚至驱动报错。实际项目中最稳妥的做法是宽、高、stride都向16对齐。比如你的图像宽度是100像素RGB888每个像素3字节那stride就建议设置为128100*3300向16字节对齐是304不对300向上取16的倍数等于304但还要考虑宽度对齐RGB888的每行字节数需要对齐到16字节所以300 - 304但304不是64的倍数如果你想要64对齐可以取320。在RK3588上32字节对齐基本就能正常工作16字节对齐在某些场景会警告64字节对齐是性能最佳的选择。一个简单规则分配buffer时每个维度都搞成16的倍数stride显式设置不要依赖驱动自动推导。我踩过最疼的坑就是NV12图像宽度是奇数导致Y平面正常但UV平面错位花屏难排查到怀疑人生。后来统一在源头上把宽度对齐到16这种问题再也没有出现过。3. 工程实战从零跑通一个RGA任务3.1 环境准备与Librga编译开发环境我以RK3588平台配Ubuntu系统、官方SDK为例。首先确认你的内核RGA驱动是否存在ls /dev/rga如果能看到设备节点说明驱动OK。然后处理用户态库从Github仓库拉取源码git clone https://github.com/rockchip-linux/librga.git cd librga mkdir build cd build cmake .. make -j8 sudo make install安装完成后头文件在/usr/include/rga目录下主要包含RgaApi.h、rga.h、drm.h这些文件。链接时用-lrga即可。编译完毕先用官方自带的demo验证一下环境是否正常。在源码的samples目录下有rgaImDemo的示例程序直接可以调用。如果demo能正常运行并输出正确的图像文件说明RGA通路已经打通。一个细节确认Librga库的版本号。在代码里可以打印版本信息int version rga_get_version(); printf(RGA version: 0x%x\n, version);不同版本对格式的支持和性能表现差异不小我建议使用至少2.2.0以上的版本。3.2 完整的缩放与格式转换示例下面给出一段完整的、可以直接跑通的C代码。功能是读取一张NV12格式的1080p图像通过RGA缩放到720p并转换成RGB888格式输出然后保存为文件。为了简化说明代码省略了文件读取细节直接假设有NV12的buffer#include stdio.h #include stdlib.h #include string.h #include rga/RgaApi.h #define SRC_W 1920 #define SRC_H 1080 #define DST_W 1280 #define DST_H 720 int main(int argc, char **argv) { // 1. 检查RGA版本 int version rga_get_version(); printf(RGA version: 0x%x\n, version); // 2. 分配源buffer和目标buffer示例用虚拟地址 size_t src_size SRC_W * SRC_H * 3 / 2; // NV12大小 size_t dst_size DST_W * DST_H * 3; // RGB888大小 unsigned char *src_buf (unsigned char *)malloc(src_size); unsigned char *dst_buf (unsigned char *)malloc(dst_size); if (!src_buf || !dst_buf) { printf(malloc failed\n); return -1; } // 3. 填充源数据这里应该读取你的NV12图像文件 // ... 读取NV12文件到src_buf // 4. 用新API包装源和目标buffer rga_buffer_t src_handle wrapbuffer_virtualaddr(src_buf, SRC_W, SRC_H, RK_FORMAT_YCbCr_420_SP); rga_buffer_t dst_handle wrapbuffer_virtualaddr(dst_buf, DST_W, DST_H, RK_FORMAT_RGB_888); if (src_handle.handle 0 || dst_handle.handle 0) { printf(wrapbuffer failed\n); return -1; } // 5. 设置处理区域整图处理不需要裁剪 im_rect src_rect {0, 0, SRC_W, SRC_H}; im_rect dst_rect {0, 0, DST_W, DST_H}; // 6. 配置并执行操作 imconfig config; config.color {0, 0, 0, 0}; // 背景色不影响本场景 int ret improcess(src_handle, dst_handle, src_rect, dst_rect, NULL, NULL, config, 0); if (ret ! IM_STATUS_SUCCESS) { printf(RGA improcess failed: %d\n, ret); free(src_buf); free(dst_buf); return -1; } // 7. 保存目标文件以RGB888方式写入文件即可 FILE *fp fopen(output.rgb, wb); if (fp) { fwrite(dst_buf, 1, dst_size, fp); fclose(fp); } printf(RGA process done.\n); free(src_buf); free(dst_buf); return 0; }这段代码逻辑不复杂核心就三步wrapbuffer创建buffer描述im_rect设置区域improcess执行。很多人在第二步容易犯迷糊src_rect和dst_rect的宽度比不一致时RGA会自动做缩放吗答案是会的。src_rect是1920x1080dst_rect是1280x720RGA自动完成缩放不需要额外设置缩放比例参数。需要注意improcess的第八个参数是usage通常传0即可。但如果你要做旋转、镜像、透明度混合等更复杂的操作就需要通过imconfig结构体配置或者使用imrotate、imflip这些封装好的快捷函数。以90度旋转为例rga_buffer_t rotated imrotate(src_handle, IM_HAL_TRANSFORM_ROT_90); ret improcess(rotated, dst_handle, src_rect, dst_rect, NULL, NULL, NULL, 0);imrotate不产生实际数据拷贝只是生成一个旋转后的描述符真正旋转发生在improcess执行时这个设计很巧妙性能上几乎没有额外开销。3.3 与OpenCV和MPP的配合使用工程实战里很少单独使用RGA更多是跟其他模块配合。我项目里最常用的两个组合RGA OpenCV和RGA MPP。RGA OpenCVOpenCV在ARM平台上的性能问题一直被诟病尤其是resize和cvtColor这两个函数耗时高得离谱。在RK3588上完全可以只把RGA当作一个高性能的预处理引擎OpenCV负责后续的算法逻辑。关键是消除内存拷贝让RGA直接处理OpenCV Mat的数据cv::Mat src_mat cv::imread(test.jpg); // BGR888格式 // 注意OpenCV的Mat数据不能直接传给RGA因为可能有行对齐问题 // 正确的做法是先获取Mat的ptr和数据长度用wrapbuffer_virtualaddr包装 // 并把format指定为对应的格式。 rga_buffer_t src_rga wrapbuffer_virtualaddr(src_mat.data, src_mat.cols, src_mat.rows, RK_FORMAT_BGR_888); cv::Mat dst_mat(cv::Size(1280, 720), CV_8UC3); rga_buffer_t dst_rga wrapbuffer_virtualaddr(dst_mat.data, dst_mat.cols, dst_mat.rows, RK_FORMAT_BGR_888);这样RGA处理完成后数据已经直接落在OpenCV的Mat里零额外拷贝后续接cv::Mat的图像处理算法完全无缝。这种方式下视频倍速播放、摄像头预览等场景性能提升立竿见影。RGA MPPMPP是Rockchip的视频编解码库解码出来的帧通常是NV12格式的YUV数据存储在专用内存dma-buf中。传统做法是先memcpy到用户空间再做像素格式转换。更好的做法是用wrapbuffer_fd直接拿MPP解码帧的fd在RGA内部完成格式转换和缩放。// mpp_frame_get_fd获取解码帧的fd int fd mpp_frame_get_fd(frame); rga_buffer_t src_rga wrapbuffer_fd(fd, width, height, RK_FORMAT_YCbCr_420_SP, width, height, 0);使用wrapbuffer_fd时RGA驱动会直接操作那个已分配的物理连续内存或dma-buf省去了CPU参与的memcpy环节。这就是真正意义上的零拷贝流水线MPP解码输出直接进RGARGA输出直接给显示或AI模块全程CPU只管配置参数像素数据不经过CPU。这里有一个前提条件确保MPP解码帧的内存是dma-buf且RGA支持输入该内存类型。在RK3588平台实测是没有问题的其他平台需要检查SDK的配置。3.4 多路视频场景下的RGA使用策略多路视频处理是RGA最典型的应用场景之一。我做过一个八路D1分辨率704x576视频墙项目每路视频解出来之后要缩放到不同位置同时叠加中文标题。如果一路一路单独调用RGA虽然每路都很快但多次调用之间有驱动切换开销。更优的做法是充分利用RGA的region叠加功能。在一次improcess调用中传入多个src buffer和多个dst rect让RGA把多路视频画面分别缩放到一个大背景帧的不同区域。这种批量处理模式能显著降低系统调用次数和驱动开销。具体实现上新API支持improcess批处理模式需要构造rga_buffer_t数组和对应的im_rect数组。驱动内部会自动按顺序执行每个region的处理看起来就像GPU提交了一个大的draw call一样。实测在RK3588上8路D1缩放叠加到1080p大帧总耗时从逐路调用的12毫秒降到5毫秒效果显著。4. 性能优化指南把RGA榨干4.1 零拷贝从虚拟地址到dma-bufRGA性能优化第一原则尽量让像素数据不经过CPU。原始方式是用wrapbuffer_virtualaddr传用户空间虚拟地址驱动内部会通过MMU把虚拟地址映射到物理地址然后DMA读取。这个过程本身没有问题但有个性能隐患RGA访问内存时会发生cache一致性问题驱动可能需要做cache flush操作这在频繁处理大图像时开销很明显。解决方式是使用dma-buf。如果你的输入输出buffer来自DMA-BUF Heap、ION、或某个具有dma-buf导出能力模块比如MPP解码帧直接用wrapbuffer_fd传给RGA。RGA驱动会走专门的硬件路径DMA直接访问buffer绕过不必要的cache操作性能提升稳定在15%-30%之间。分配一个dma-buf的标准做法#include linux/dma-heap.h #include sys/ioctl.h int heap_fd open(/dev/dma_heap/system, O_RDWR); struct dma_heap_allocation_data data; memset(data, 0, sizeof(data)); data.len buffer_size; data.fd_flags O_RDWR | O_CLOEXEC; ioctl(heap_fd, DMA_HEAP_IOCTL_ALLOC, data); int dma_fd data.fd;拿到dma_fd之后RGA就可以用wrapbuffer_fd(dma_fd, ...)来使用它。如果你需要CPU也能访问这块buffer做数据填充或结果读取可以用mmap将其映射到用户空间。这套组合拳打下来性能基本到达板卡上限。4.2 多线程并发与RGA实例管理Librga默认是线程安全的内部有互斥锁保护驱动调用。但要注意默认情况下所有线程共享同一个RGA设备驱动内部的调度可能会让并发请求排队处理如果你的多线程业务依赖并行处理来提效需要仔细测试实际并发效果。实测RK3588上两个线程同时提交不同尺寸的RGA任务总耗时并不总是等于单线程的一半。这是因为RGA硬件引擎本身是共享的大任务会占用较长时间小任务会被排到后面。优化的做法是用批处理模式将多个小任务合成一个请求提交而不是让线程各自抢占RGA。如果多线程场景下遇到明显的性能回退尝试调整线程优先级把RGA调用线程设置为高优先级SCHED_FIFO减少调度延迟。受限于驱动实现这个方式不要指望带来质变但小幅度提升是能感知到的。4.3 避开性能陷阱常见低效写法很多性能问题不是RGA本身慢而是调用方的用法不对。几个典型场景频繁分配和释放buffer每帧都malloc/free还要经过mmap内存分配本身就有开销。正确做法是buffer池化预分配一个或多个buffer循环使用必要时做双缓冲保证RGA写当前帧的同时CPU准备下一帧。不必要的格式中间转换比如你把NV12转成RGB888存到OpenCV Mat然后OpenCV又转回YUV送编码器这中间白白多了一次RGA调用和一次内存写入。直接查一下编码器支持什么输入格式如果支持NV12就让RGA直接从NV12到NV12只做缩放省掉格式转换的步骤。在低分辨率buffer上过度对齐分配一个128x128的RGBA buffer不需要stride对齐到4096。stride过大意味着DMA读的字节多带宽浪费在空洞上。在性能关键路径上使用虚拟地址凡是帧率高、数据量大的场景优先用dma-fd。虚拟地址RGA也可以工作但始终有额外的映射和同步开销。4.4 缓存一致性机制与注意事项RGA缓存一致性是一个老话题。在带MMU的新芯片上驱动一般会自动处理cache同步但如果你同时让CPU写数据、RGA读数据、又让CPU读结果就必须注意同步时序。一个典型场景你从摄像头拿到一帧NV12数据放在dma-buf里CPU写入了这一帧然后要交给RGA缩放。这时候如果不做cache清理RGA读到的可能是CPU cache里还没写回内存的旧数据。反之RGA写完结果后CPU也要读如果cache里有旧的脏数据CPU可能读到错误内容。Librga在新版本中通常会在执行前自动处理这个同步操作但为了保险起见尤其在高帧率场景下建议显式调用// 在CPU写入数据后、调用RGA前同步内存 msync(dma_buf_mmap_ptr, buffer_size, MS_SYNC); // 或者对于虚拟地址buffer使用标准cache操作接口如果发现图像偶尔出现掉数据或者有一两帧花屏但下一帧又好了的诡异现象第一反应就检查cache同步。这种问题在低版本Librga里尤为常见升级到2.2.0以上会有明显改善。5. 高频问题排查与调试技巧5.1 一张问题速查表我在多个项目里积累的RGA常见问题排查表先上表格再逐个细说现象可能原因排查方向黑屏或空白输出色彩空间/格式配置错误或buffer未初始化检查format枚举值打印buffer首字节内容花屏、绿屏NV12 UV平面错位stride对齐错误检查宽高和stride是否对齐16UV偏移计算输出偏色色域标准不匹配BT.601 vs BT.709检查color space配置尝试切换标准旋转后尺寸错误旋转90度/270度时宽高未交换旋转操作后手动交换目标宽高偶发一帧失败cache同步问题或buffer竞争检查多线程同步确认无并发读写同一buffer性能远低于预期使用了非dma-fd buffer或者过度对齐换fd模式降低stride对齐值improcess返回错误码内存无效、格式不支持、参数越界打印im_error_msg逐一检查参数demo正常但集成到业务失败buffer生命周期管理问题检查buffer是否被提前free或mmap被释放5.2 花屏问题的深度复盘花屏是我见过最多的RGA问题尤其是NV12格式下。NV12的内存布局是先是一个完整的Y平面大小是widthheight然后是UV交错平面大小是widthheight/2U和V各占一半且交错排列。很多人在手动构造NV12 buffer或从MPP解码帧复制数据时UV平面的偏移算错了。正确计算方式是UV偏移 stride_y * height这里的stride_y是Y平面的行字节数不一定是width。如果width是1920stride对齐到64那stride_y 1920UV偏移就是19201080。但如果width是1000stride对齐到1024UV偏移就变成1024height不是1000*height。只要偏移算错UV平面错位画面就会出现典型的绿色或紫红色花屏。排查方法很简单打印NV12数据Y平面的最后几行和UV平面的开头几行如果UV平面的起始数据看起来和Y平面结尾数据内容反差不大说明偏移算错了。另外推荐使用RGA自带的imcheck函数做参数复查IM_STATUS status imcheck(src_handle, dst_handle, src_rect, dst_rect); if (status ! IM_STATUS_SUCCESS) { printf(imcheck failed: %s\n, im_status_string(status)); }imcheck会在真正执行前检查所有参数是否合法对于定位问题非常有帮助新手完全应该把这一步作为标准流程的一部分。5.3 旋转场景的宽高陷阱RGA旋转90度或270度后图像的宽高会互换。比如你有一张1920x1080的图像旋转90度后变成1080x1920。大部分人在配置dst_rect的时候忘了这个导致输出的目标区域尺寸不对RGA可能裁剪或报错。正确的做法是在旋转场景中根据旋转角度动态计算目标宽高int dst_w (rotate 90 || rotate 270) ? src_h : src_w; int dst_h (rotate 90 || rotate 270) ? src_w : src_h;另外还要注意在im_rect里设置的dst_rect宽度和高度都必须是正数宽高为负表示镜像翻转这是新API的一个冷门用法但也是镜像操作的底层实现方式。如果你要用im_rect做镜像而不是用imflip就要把宽度设为负值此时图像会沿Y轴翻转。这个用法容易踩坑不太推荐新手直接操作用封装的imflip更安全。5.4 格式支持矩阵提前确认比事后调试更高效RGA对不同格式的支持程度并不均等有的格式是完美支持有的格式是支持但有性能惩罚还有的格式在特定芯片上根本不支持。最权威的信息来源是RGA文档里的RGA_SPEC_FORMAT枚举实际上就是代码里的rga.h头文件。建议在做方案设计时就把格式支持矩阵查好而不是等代码写完再碰运气。以RK3588为例我最常用的几个格式支持情况大致如下NV12/NV21完美支持RK平台视频领域的普通话RGB888/BGR888完美支持和OpenCV配合时常用RGBA8888/BGRA8888完美支持UI叠加场景必备RGB565支持但某些缩放场景下质量一般YUV422YUYV/UYVY等支持度视版本而定建议实测确认10bit YUVP010等新版本才支持老版本会报错上面这些信息供参考实际以你的Librga版本和芯片型号为准。我习惯的做法是写一个小工具把所有常见格式组合跑一遍验证可用性后固化到项目的静态配置表中。这样后期集成新的功能时不需要反复踩这个格式能不能用的坑。6. 工程实践中的几个重要认知做RGA开发几年下来我最大的体会是RGA真正难的地方不在API而在于对整个数据流的理解。你需要清楚每一帧数据从哪里来摄像头、解码器、网络、到哪里去显示、编码器、AI推理在哪个环节需要什么格式、什么分辨率、什么颜色空间然后在最合适的位置插入RGA操作。新人在RGA上浪费的时间绝大部分不是在写RGA代码本身而是花在内存分配方式、格式对齐、数据同步这些外围细节上。这些坑踩过一次之后要养成三个习惯第一所有buffer的宽高stride强制对齐到16或64。即使RGA驱动允许非对齐对齐后的稳定性和性能会好很多。在业务逻辑层就把这个规则固化下来让上下游的buffer分配都遵守同一套对齐标准。第二在集成RGA到复杂流水线之前先做最小验证。为每个RGA操作设计一个独立的小测试确认这个操作单独执行没问题再把它接入整体流程。我在项目里会用独立的命令行小工具专门接收一张本地图片测试各种RGA转换参数。这样调试问题时可以快速用工具复现不用每次重新编译整个应用程序。第三尽量把多个RGA操作合并成一次调用。improcess的批处理能力很多开发者没用起来导致每帧都要做多次系统调用。我实测一次批处理调用比多次单次调用总耗时减少一半以上尤其在多路视频的场景下效果明显。最后再分享一个小技巧如果你在开发过程中遇到RGA的行为和文档描述不一致的情况优先查阅当前SDK版本的librga源码中的注释和驱动代码因为这些代码往往比外网文档更新更及时。Rockchip平台的东西源代码永远是最权威的说明书。我在好几个疑难问题的排查中都是通过阅读rga_drv.c驱动代码里的注释找到答案的。
阅读完成 · 觉得有帮助?
咨询建站