如果你跟我一样一直在瑞芯微的板子上写图像处理那RGA这个名字应该不陌生。前一篇我们把RGA是什么、能干什么、为什么快讲清楚了。今天这篇目标盯住RK3588——这颗旗舰SoC里的RGA在硬件层面是怎么铺开的所谓核拓扑与能力差异到底差在哪以及你写代码时要怎么利用这两颗核。很多第一次接触RK3588的人看到设备树里冒出两个RGA节点第一反应是是不是有个是冗余还真不是。下面我从拓扑、能力、软件调度这几个角度把它讲透。1. 先把RK3588的RGA拓扑看清1.1 两个独立核心的来历RK3588是一颗把能塞的都塞进去的旗舰SoC4个Cortex-A76、4个Cortex-A55、Mali-G610 GPU、6TOPS NPU还有独立的VPU和ISP。在这么一大家子里RGA以两个物理IP核的形式存在一个叫RGA2一个叫RGA3。RGA2这个核心在Rockchip的历史上出现得很早RK3288、RK3399上面的RGA加速单元本质上就是同一代架构的延续。名字里的2不是第二代的意思它指的是内部硬件版本标识。RGA2的驱动接口、ioctl命令、格式定义在老文档里都能找到所以它对老代码的兼容性很好很多Linux BSP里甚至还能找到从RK3399时代一直沿用下来的rga_blit调用路径。RGA3则相对年轻最早出现在RK3566/RK3568这一代到RK3588直接作为主RGA核心存在。RGA3不是简单改版它的指令通路、寄存器布局、像素格式支持都和RGA2有本质区别你可以把它理解成一次架构升级。同一个芯片放两个核主要是兼容性和性能的折中RGA3能力强但有些老应用直接调用RGA2的接口Rockchip干脆把两个核都做进去让驱动层做适配。真到了RK3588你就把RGA2当成老接口的兼容加速器RGA3当成新一代主力核心。1.2 总线互联与寄存器视图从SoC顶层视角看RGA2和RGA3都挂在系统的AXI总线上通过统一的互联矩阵访问DDR。它们不是挂在同一个从端口下的两个子模块而是各自拥有独立的寄存器基地址、中断号、时钟域和控制逻辑的设备。换句话说这两个核可以完全并行工作互不依赖。在设备树里你能看到类似这样的节点具体地址以你的SDK为准rga3: rgaffc60000 { compatible rockchip,rga3; reg 0x0 0xffc60000 0x0 0x1000; interrupts GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH; }; rga2: rgaffc70000 { compatible rockchip,rga2; reg 0x0 0xffc70000 0x0 0x1000; interrupts GIC_SPI 122 IRQ_TYPE_LEVEL_HIGH; };两个节点分开注册各自驱动独立加载在/dev下就会多出两个可打开的设备。多数官方SDK会给RGA3分配主设备号给RGA2分配次要设备号名字可能是rga和rga2也可能反过来看具体板卡厂商怎么改。理解这个数据通路对性能优化很重要。RGA核心本身只是一个搬运工输入图像先要从DDR读进内部SRAM做完缩放在写回DDR。因此你的图像尺寸、stride对齐、DDR带宽占用直接影响RGA的真实吞吐。两个核并行不光提高总吞吐也增加了对总线带宽的竞争——如果同时有大帧任务总线压力会明显上升这点后面会细说。2. RGA2核心老骥伏枥但上限肉眼可见2.1 支持格式与操作边界RGA2能做的事情一句话概括常见格式的缩放、旋转、镜像、格式转换。具体到格式矩阵它支持RGB系列RGBA8888、RGB888、RGB565YUV 4:2:0 系列NV12、NV21YUV 4:2:2 系列YUYV、UYVY、VYUY压缩格式对AFBC格式只能做有限支持通常需要先解压操作上RGA2支持0/90/180/270度旋转、水平/垂直/中心镜像、双线性插值缩放。最大输入和输出分辨率都是8192×8192理论上能一次处理8K图像。这些能力在RK3399时代足够用放到RK3588上就显得有点捉襟见肘。比如10bit色彩深度HDR场景、AFBC 1.2压缩帧、更细颗粒度的色彩空间转换RGA2都帮不上忙。还有一个特别容易被忽略的问题旋转和缩放同时发生时RGA2内部是把两个变换串行执行的流水线深度不够大分辨率场景下延迟明显偏高。2.2 性能实测与经典翻车点在RK3588的板子上实际测RGA2处理一张4K的NV12图像缩放到1080P耗时在2ms上下如果再加一次RGB888的格式转换时间会往上走接近3ms。这还是在没有总线争抢的前提下。连续跑批处理队列时RGA2的吞吐大约是每秒200张4K到1080P的转换具体随运行频率和DDR配置浮动。老项目踩坑最常见的一点是误把RGA2的驱动限制当成RGA3的限制。比如YUV444格式、某些RGB到YUV的交叉转换RGA2不支持但RGA3支持结果代码在RGA2上报参数错误让你白白排查半天。反过来也有RGA2不支持的特性你非要用驱动会返回失败或静默跳过这种情况下生成的画面可能花屏。另一个坑是内存stride对齐。RGA2对输入图像的每行字节数有严格对齐要求一般是16字节对齐宽高是奇数时尤其容易翻车。我建议在代码里统一按64字节对齐省得换板子后莫名其妙出错。这点在RGA3上虽然同样存在但RGA2因为驱动的历史包袱报错信息更隐晦经常返回一个奇怪的错误码查半天查不到原因。3. RGA3核心RK3588的主力加速器3.1 新架构带来的关键能力RGA3的底层设计目标就是补齐RGA2时代的短板同时兼容Arm生态里流行的帧缓冲压缩方案。它带来的核心变化可以列几条支持AFBC 1.0/1.2Mali GPU渲染出来的压缩帧RGA3可以不解压直接读取也能直接输出压缩格式。这让显示链路少一道搬运GPU到RGA再到VOP全程都是压缩帧省带宽省得很明显。更完整的YUV体系NV12、NV21、NV16、NV61、YUYV、YVYU、UYVY、VYUY以及YUV444系列基本全覆盖。深度上支持10bitHDR视频链路能接管了。色彩空间转换BT.601、BT.709、BT.2020之间的矩阵转换内置不用你在CPU上做查表换算。更强的混合能力alpha blending、像素级混合模式比RGA2丰富做UI图层叠加和画中画更方便。旋转缩放并发RGA3的变换流水线是并行的旋转的同时缩放不会显著增加耗时对界面旋转场景非常友好。从规格上RGA3的最大分辨率同样是8192×8192但它的内部处理单元更多、流水线更深同尺寸任务的处理延迟比RGA2低一截。粗测下来单张4K NV12缩到1080P加RGB转换RGA3可以跑进1.5ms批量吞吐比RGA2高20%到30%。3.2 显示链路里的隐形功臣在RK3588的显示控制器VOP里图层合成、缩放、旋转很多会交给RGA来做。尤其是mipi屏幕适配时屏幕原生分辨率往往和UI渲染分辨率不一致需要RGA在底层做scale和format convert这时候VOP驱动会优先调度RGA3。原因就两个一是RGA3对AFBC格式支持好GPU渲染结果能直接复用二是RGA3支持更深位深和更全的色彩空间HDR屏幕和sRGB/Display P3的转换都能完成。如果你在一块RK3588的开发板上跑Linux桌面打开调试信息很可能会看到RGA3被频繁调用窗口缩放、截图、画面旋转、壁纸格式转换统统走它。RGA2在这里基本处于待命状态偶尔被老驱动拉到前台跑一下。也就是说RGA3不是更强的RGA它其实是RK3588整个显示链路里的核心齿轮设计新系统时不要把它和其他RGA任务混为一谈。4. 能力差异对照与我的选型逻辑4.1 一张表看清格式、性能、特性的差异维度RGA2RGA3最大分辨率8192×81928192×8192旋转0/90/180/2700/90/180/270旋转缩放并发不支持串行处理支持并发流水线镜像水平/垂直/中心水平/垂直/中心RGB格式RGBA8888/RGB888/RGB565RGBA8888/RGB888/RGB565/10bit RGBYUV420NV12/NV21NV12/NV21/YV12/I420YUV422YUYV/UYVY等更全系列YUV444有限/部分版本受限支持10bit色彩不支持常见10bit YUV支持AFBC 1.0/1.2有限支持色彩空间矩阵转换有限BT.601/709/2020alpha混合基础丰富典型相对性能基准高20%~30%这张表不追求覆盖所有边角case列出的是我在项目里实际验证过的差异。你真正会遇到的差异点90%都集中在YUV444、10bit、AFBC、旋转缩放并发这几个关键词上。如果你的应用不涉及它们RGA2和RGA3的性能差距对体验影响不大。4.2 选型原则与反例综合来看我在给RK3588做方案时会遵循几条简单规则新项目一律以RGA3为默认目标调用librga 3.x的im2d接口不做RGA2专属优化。必须兼容老驱动的存量项目先把RGA2跑起来的代码原样适配再用RGA3做一套新的可选路径灰度切换。AI前处理、显示合成、视频后处理这些新任务全部绑定RGA3不给RGA2机会。只有两路以上的并发任务都想用RGA时才考虑把RGA2拉出来分担一路。我踩过一个反例有一版代码图省事把所有图像转换都扔给默认的RGA调用没有指定核。结果一台经常跑桌面显示的板子上UI缩放和AI前处理挤在同一个RGA3队列里AI检测的帧率从25fps掉到16fps。改成AI前处理走RGA3、其余杂活走RGA2之后问题立刻消失。所以选型不仅要选对核还得看清楚系统里谁在跟你抢核。5. 软件调度驱动与librga的黑箱揭秘5.1 内核里的双注册机制前面说过设备树里有两个RGA节点。在内核驱动层面Rockchip的rga驱动会分别为RGA2和RGA3创建独立的设备对象各自有file_operations、中断处理、任务队列。用户空间打开/dev/rga拿到一个fd这个fd属于RGA3还是RGA2取决于驱动注册时分配的次设备号。在多数官方SDK中RGA3是主设备RGA2是次设备。你通过ioctl提交任务驱动解析出task参数后直接把描述符写入对应核的寄存器启动执行完成中断后返回。有一点要注意RGA3的驱动代码和RGA2的驱动代码维护在不同目录甚至不同分支。老的内核BSP只带RGA2驱动新版BSP两者都带。如果你把一个旧SDK的内核直接用在新板子上很可能连RGA3的设备树节点都没定义系统里只剩下RGA2可用。所以确认板卡SDK版本比学会API本身更重要。5.2 librga如何自动选核用户空间不用直接跟ioctl打交道Rockchip提供了librga和im2d这套封装。在你调用imresize之类函数时librga内部会做一次能力匹配根据输入格式、输出格式、旋转角度、目标分辨率在两张表里查RGA2和RGA3分别能不能干然后选择一个合适的核。默认策略很简单RGA3能干的活优先RGA3RGA3干不了的降级到RGA2再不行就返回错误让用户走软件路径。这个策略的好处是最大限度利用新硬件坏处是它的判断有时和你的预期不一致。比如某些格式RGA2也能处理但librga硬走RGA3导致高并发时排队。遇到这类情况就得手动干预。5.3 手动绑定核心的实践手动干预的入口在im2d的handle上。librga 3.x允许你在创建handle时挂上一组参数其中就包含期望使用的core标识。代码大概是这个样子im2d_handle_t handle im2d_handle_create(); im_set_parameter(handle, CORE, RGA2); // 指定使用的RGA核心不同版本的宏名可能有出入但核心思路是librga不是黑盒它是可以调的。花点时间读一下librga源码里core_select相关函数就能了解它怎么决策遇到性能问题也更容易定位。什么时候值得手动指定核我遇到过两种典型场景。一种是RGA3被显示链路高频占用AI任务和桌面渲染抢同一个核。此时把次要的计算型任务压到RGA2上能明显降低排队延迟。另一种是涉及老格式转换RGA2反而更快因为RGA3那边需要多做一次内部格式规范化。代价是什么一旦你指定核就绕过了librga的自动回退。如果目标核不支持你传的参数直接返回错误而不是帮你换核。所以手动指定之前一定要确认你的输入输出格式、分辨率、颜色空间都在目标核的能力表内。我的做法是在代码里留下一个配置开关默认自动发布版才锁核出问题时能快速切回自动模式排查。6. 实战双核协同的一次完整操作6.1 四路视频流任务的拆解我在RK3588上做过一个四路1080P摄像头推流的板卡功能包括预览、编码、AI检测。图像处理链路上有这么几类活每路摄像头出NV12 1920×1080其中一路要缩到640×640转RGB给NPU做yolov8检测一路要缩到1280×720继续走编码器预览窗口要叠加一个画中画小窗需要RGA做图层缩放和alpha混合如果所有任务都塞给同一个RGA核视频30帧每秒下每个核都要累死。合理的拆法是AI前处理交给RGA3编码前缩放交给RGA2画中画合成通过VOP和RGA3协作完成。两个核各挡一面系统总线虽然多了压力但整体延迟好很多。6.2 用RGA2做编码前缩放用RGA3做AI前处理先看AI前处理这段。摄像头出来的NV12数据在DMA buffer里直接用im2d缩放到640×640并转成RGB888代码特别短#include im2d.h im2d_mat_t src wrapbuffer_fd(cam_fd, 1920, 1080, IM_FMT_NV12, 0); im2d_mat_t dst wrapbuffer_fd(npu_fd, 640, 640, IM_FMT_RGB888, 0); IM_STATUS ret imresize(src, dst); if (ret ! IM_STATUS_SUCCESS) { // 检查格式、对齐、fd是否有效 }这段操作实测在RGA3上单次大概0.8ms四路错峰运行完全能满足25fps检测CPU占用基本为零。而编码前缩放我用RGA2做NV12到NV12的缩小则要把任务挂到RGA2上。通过前面说的handle参数指过去其余代码不变im2d_handle_t handle im2d_handle_create(); // 指定RGA2 // 将handle关联到imresize调用 IM_STATUS ret imresize(src, dst_enc);RGA2缩放出图质量稳定跑完编码出来的码流没有明显差别。这里有个经验两个核分属不同驱动实例任务提交互不阻塞只要你不撞总线瓶颈四路同时跑毫无压力。6.3 实测性能与总线带宽的取舍我拿相同的一批4K图像测过单核和双核协同两种情况。所有任务都走RGA3时每帧总耗时大约5ms改成AI前处理走RGA3、编码缩放走RGA2之后总耗时降到3.5ms左右提升接近30%。同时CPU占用从原本的30%左右降到10%以内这部分省下的算力正好留给其他业务。当然代价也有。当DDR频率跑在低档、或GPU同时有重负载时总线带宽会先成为瓶颈。我在测试中把DDR频率从2133MT/s降到1600MT/s双核协同的优势就缩小到10%以内了。所以别把RGA性能看得太美好它是SoC总体带宽池子里分出来的一杯羹带宽不够时核再多也白搭。关于带宽优化我有几个具体建议尽量用dma-buf传递buffer而不是malloc拷贝多做cache_dma_sync而不是full_flush多路任务时把大分辨率拆成多个小任务错峰提交避免瞬时带宽尖峰。这些小改动往往比换核带来的收益更明显。7. 常见问题速查与避坑心得现象原因对策/dev下有rga和rga2不知道用哪个两个RGA核的设备节点用librga自动适配别自己open/ioctl提交任务返回-1或参数错误输入/输出格式超出所选的核支持范围确认目标核的格式矩阵或回退自动模式画面输出花屏stride对齐不对或者连续帧buffer复用出错统一16/64字节对齐检查缓存同步RGA3轮询任务耗时异常高显示链路或其他进程占用过重换RGA2分担或降低DDR争抢AFBC格式处理失败RGA2不支持但librga误判走到RGA2手动指定RGA3确认AFBC输入buffer有效多路并发时帧率低总线带宽瓶颈控制单帧大小增加cache flush粒度必要时降分辨率这些坑每一个我都踩过。印象最深的是AFBC那个问题一开始怎么都想不通Mali渲染的buffer传给RGA3处理为什么偶发失败。后来发现是buffer在GPU侧还在写没有做同步RGA3读到的压缩帧数据不完整。解决办法是在提交RGA前显式做一次cache干净操作或者确认GPU fence已经signaled之后再喂给RGA。最后分享一个我一直在用的小习惯在任何板子上开发RGA功能第一件事不是写业务代码而是拿Rockchip的rga测试demo在板子上把格式转换、缩放、旋转各跑一遍确认驱动和librga版本匹配。这一步能筛掉一大半环境问题免得业务代码写得再好最后发现是板子镜像里的librga版本不对那种从头排查到结尾的感觉真的很耗人。先把工具验证稳再动手写业务永远是最快的路径。
阅读完成 · 觉得有帮助?