RGA 2D Graphics Acceleration
This chapter explains how to use the RK182X RGA (Raster Graphic Acceleration) hardware 2D engine.
Overall block diagram
Camera (V4L2)
│ dma_fd
↓
RGA format conversion (NV12 → RGB)
│ same fd or a new fd
↓
NPU inference (RKNN)
│ results
↓
RGA composition (overlay bounding boxes)
↓
Encoder (MPP) / displayRGA is a hardware 2D graphics engine built into the Host SoC (e.g. RK3588). The RK182X coprocessor itself has no RGA; it works through the Host SoC's RGA over PCIe and can efficiently perform image format conversion, scaling, rotation, cropping, composition, and other operations.
1. RGA Capabilities
The RGA hardware 2D engine supports six operations: format conversion, scaling, rotation, cropping, multi-layer composition, and solid / gradient fill.
| Operation | Description | Typical use |
|---|---|---|
| Format conversion | NV12↔RGB / YUV↔RGB / arbitrary conversions | Camera→NPU input preprocessing |
| Scaling | Arbitrary-ratio scaling (1/8x ~ 8x) | Resolution adaptation, thumbnails |
| Rotation | 0° / 90° / 180° / 270° / mirroring | Portrait camera correction |
| Cropping | Arbitrary rectangular region extraction | ROI extraction |
| Composition | Multi-layer alpha-blended overlay | OSD watermarks, picture-in-picture |
| Fill | Solid / gradient fill | Background fill |
2. RGA Initialization
Call c_RkRgaInit() to initialize the RGA device; returns 0 on success, <0 on failure (check whether /dev/rga* exists).
Verified:
/dev/rgaexists (the Host SoC RK3588 has a built-in RGA), so the RGA device is available.
#include <rga/RgaApi.h>
#include <stdio.h>
int ret = c_RkRgaInit();
if (ret < 0) {
printf("RGA init failed: %d\n", ret);
return -1;
}
printf("RGA initialized.\n");3. NV12 → RGB888 Format Conversion
Fill rga_info_t src (fd + NV12 format + rect) and rga_info_t dst (fd + RGB888 format + rect), then call c_RkRgaBlit(&src, &dst, NULL) to perform the conversion.
#include <rga/RgaApi.h>
#include <rga/drmrga.h> /* rga_info_t / rga_rect_t */
#include <rga/rga.h> /* RK_FORMAT_* */
#include <stdio.h>
#include <string.h>
int main(void) {
// source image (NV12)
rga_info_t src = {0};
src.fd = -1; // replace with the real dma fd by the caller
src.mmuFlag = 1;
src.rect.xoffset = 0;
src.rect.yoffset = 0;
src.rect.width = 1920;
src.rect.height = 1080;
src.rect.wstride = 1920;
src.rect.hstride = 1080;
src.format = RK_FORMAT_YCbCr_420_SP;
src.rect.format = RK_FORMAT_YCbCr_420_SP; // older drivers read this one
// destination image (RGB888)
rga_info_t dst = {0};
dst.fd = -1;
dst.mmuFlag = 1;
dst.rect.xoffset = 0;
dst.rect.yoffset = 0;
dst.rect.width = 1920;
dst.rect.height = 1080;
dst.rect.wstride = 1920;
dst.rect.hstride = 1080;
dst.format = RK_FORMAT_RGB_888;
dst.rect.format = RK_FORMAT_RGB_888;
int ret = c_RkRgaBlit(&src, &dst, NULL);
if (ret < 0) printf("RGA blit failed: %d\n", ret);
else printf("NV12 -> RGB888 done.\n");
return 0;
}3.1 Key API Points
| Item | Description |
|---|---|
rga_rect_t fields | xoffset / yoffset (not x / y) |
rga_info_t.format | Set both the top-level format and rect.format (older drivers only read rect.format) |
| Conversion function | c_RkRgaBlit() or the RgaBlit() macro (there is no rga_blit()) |
4. Scaling and Rotation
IM2D API path (recommended): im2d_single.h:147 imrotate(), im2d_single.h:519 imrotate_t().
#include <rga/im2d.h>
#include <rga/im2d_single.h>
#include <rga/im2d_type.h>
rga_info_t src = {0}; /* same NV12 1920x1080 as above */
rga_info_t dst = {0}; /* 640x480 RGB */
int ret = c_RkRgaBlit(&src, &dst, NULL);
if (ret < 0) return ret;
/* rotation constants from im2d_single.h:136-139 */
ret = imrotate(src, dst, IM_HAL_TRANSFORM_ROT_90);5. Multi-Layer Composition (OSD Watermark Overlay)
Multi-layer composition goes through the IM2D API, using rga_image_t (im2d_type.h:336, which has the global_alpha field; rga_info_t does not).
#include <rga/im2d.h>
#include <rga/im2d_type.h>
rga_image_t layer0 = {0};
layer0.buffer.fd = video_fd;
layer0.width = 1920;
layer0.height = 1080;
layer0.format = RK_FORMAT_YCbCr_420_SP;
layer0.global_alpha = 0xff; // global alpha
rga_image_t layer1 = {0};
layer1.buffer.fd = logo_fd;
layer1.width = 280;
layer1.height = 80;
layer1.format = RK_FORMAT_RGBA_8888;
layer1.global_alpha = 200; // semi-transparent
rga_image_t dst_img = {0};
dst_img.buffer.fd = dst_fd;
dst_img.width = 1920;
dst_img.height = 1080;
dst_img.format = RK_FORMAT_YCbCr_420_SP;
int ret = imcomposite(dst_img, layer0, layer1);
if (ret != IM_STATUS_SUCCESS) printf("composite failed\n");6. RGA API Verification
RGA C API function declarations:
grep -n "c_RkRgaInit\|c_RkRgaBlit" /usr/include/rga/RgaApi.h /usr/include/rga/drmrga.hOutput:
/usr/include/rga/RgaApi.h:49: ret = c_RkRgaInit(); \
/usr/include/rga/RgaApi.h:57:#define RgaBlit(...) c_RkRgaBlit(__VA_ARGS__)
/usr/include/rga/RgaApi.h:61:int c_RkRgaInit();
/usr/include/rga/RgaApi.h:64:int c_RkRgaBlit(rga_info_t *src, rga_info_t *dst, rga_info_t *src1);Full macros and function declarations in RgaApi.h:
sed -n '40,65p' /usr/include/rga/RgaApi.hOutput:
/*
* Compatible with the old version of C interface.The new
* version of the C interface no longer requires users to
* initialize rga, so RgaInit and RgaDeInit are just for
* compatibility with the old C interface, so please do
* not use ctx, because it is usually a NULL.
*/
#define RgaInit(ctx) ({ \
int ret = 0; \
ret = c_RkRgaInit(); \
c_RkRgaGetContext(ctx); \
ret;\
})
#define RgaDeInit(ctx) { \
(void)ctx; /* unused */ \
c_RkRgaDeInit(); \
}
#define RgaBlit(...) c_RkRgaBlit(__VA_ARGS__)
#define RgaCollorFill(...) c_RkRgaColorFill(__VA_ARGS__)
#define RgaFlush() c_RkRgaFlush()
int c_RkRgaInit();
void c_RkRgaDeInit();
void c_RkRgaGetContext(void **ctx);
int c_RkRgaBlit(rga_info_t *src, rga_info_t *dst, rga_info_t *src1);
int c_RkRgaColorFill(rga_info_t *dst);RGA C interface verification:
c_RkRgaInit()returns int (RgaApi.h:61)c_RkRgaBlit()takes 3 arguments:rga_info_t *src, *dst, *src1(RgaApi.h:64)RgaBlit()is a macro wrappingc_RkRgaBlit()
The rga_rect_t struct definition:
sed -n '114,125p' /usr/include/rga/drmrga.hOutput:
typedef struct rga_rect {
int xoffset;
int yoffset;
int width;
int height;
int wstride;
int hstride;
int format;
int size;
} rga_rect_t;
rga_rect_tfields confirmed:xoffset/yoffset/width/height/wstride/hstride/format/size
The rga_info_t struct definition (partial):
sed -n '266,280p' /usr/include/rga/drmrga.hOutput:
typedef struct rga_info {
int fd;
void *virAddr;
void *phyAddr;
#ifndef ANDROID /* LINUX */
unsigned hnd;
#else /* Android */
buffer_handle_t hnd;
#endif
int format;
rga_rect_t rect;
unsigned int blend;The imrotate / imrotate_t function declarations:
grep -n "imrotate\|IM_HAL_TRANSFORM_ROT_" /usr/include/rga/im2d_single.h | head -10Output:
137: * IM_HAL_TRANSFORM_ROT_90
138: * IM_HAL_TRANSFORM_ROT_180
139: * IM_HAL_TRANSFORM_ROT_270
147:IM_API IM_STATUS imrotate(const rga_buffer_t src, rga_buffer_t dst, int rotation, int sync = 1, int *release_fence_fd = NULL);
519:IM_C_API IM_STATUS imrotate_t(const rga_buffer_t src, rga_buffer_t dst, int rotation, int sync);Actual rotation constant definitions:
sed -n '46,55p' /usr/include/rga/im2d_type.hOutput:
typedef enum {
/* Rotation */
IM_HAL_TRANSFORM_ROT_90 = 1 << 0,
IM_HAL_TRANSFORM_ROT_180 = 1 << 1,
IM_HAL_TRANSFORM_ROT_270 = 1 << 2,
IM_HAL_TRANSFORM_FLIP_H = 1 << 3,
IM_HAL_TRANSFORM_FLIP_V = 1 << 4,
IM_HAL_TRANSFORM_FLIP_H_V = 1 << 5,Rotation constant verification (
im2d_type.h:50-52):
IM_HAL_TRANSFORM_ROT_90 = 1<<0IM_HAL_TRANSFORM_ROT_180 = 1<<1IM_HAL_TRANSFORM_ROT_270 = 1<<2
IM2D composition-related structures:
grep -n "rga_image_t\|global_alpha\|imcomposite" /usr/include/rga/im2d_type.h | head -10Output:
336: int global_alpha; /* global_alpha, the default should be 0xff */Full definition of the rga_buffer_t struct:
sed -n '323,351p' /usr/include/rga/im2d_type.hOutput:
typedef struct {
void* vir_addr; /* virtual address */
void* phy_addr; /* physical address */
int fd; /* shared fd */
int width; /* width */
int height; /* height */
int wstride; /* wstride */
int hstride; /* hstride */
int format; /* format */
int color_space_mode; /* color_space_mode */
union {
int global_alpha; /* global_alpha, the default should be 0xff */
struct {
uint16_t alpha0;
uint16_t alpha1;
} alpha_bit; /* alpha bit(e.g. RGBA5551), 0: alpha0, 1: alpha1 */
};
int rd_mode;
/* legacy */
int color; /* color, used by color fill */
im_colorkey_range colorkey_range; /* range value of color key */
im_nn_t nn;
int rop_code;
rga_buffer_handle_t handle; /* buffer handle */
} rga_buffer_t;The imcomposite function signature:
grep -n "imcomposite" /usr/include/rga/im2d_single.h | head -3Output:
212:IM_API IM_STATUS imcomposite(const rga_buffer_t srcA, const rga_buffer_t srcB, rga_buffer_t dst, int mode = IM_ALPHA_BLEND_SRC_OVER, int sync = 1, int *release_fence_fd = NULL);imcomposite(srcA, srcB, dst, mode=IM_ALPHA_BLEND_SRC_OVER, sync=1, release_fence_fd=NULL)You must use the
rga_buffer_ttype, notrga_image_t.
RK_FORMAT pixel format macros:
grep -n "RK_FORMAT_RGBA_8888 \|RK_FORMAT_RGB_888 \|RK_FORMAT_BGR_888 \|RK_FORMAT_YCbCr_422_SP \|RK_FORMAT_YCbCr_422_P \|RK_FORMAT_YCbCr_420_SP \|RK_FORMAT_ARGB_8888 " /usr/include/rga/rga.hOutput:
30: RK_FORMAT_RGBA_8888 = 0x0 << 8, /* [0:31] R:G:B:A 8:8:8:8 little endian */
32: RK_FORMAT_RGB_888 = 0x2 << 8, /* [0:23] R:G:B 8:8:8 little endian */
37: RK_FORMAT_BGR_888 = 0x7 << 8, /* [0:23] B:G:R 8:8:8 little endian */
39: RK_FORMAT_YCbCr_422_SP = 0x8 << 8, /* 2 plane YCbCr little endian
42: RK_FORMAT_YCbCr_422_P = 0x9 << 8, /* 3 plane YCbCr little endian
46: RK_FORMAT_YCbCr_420_SP = 0xa << 8, /* 2 plane YCbCr little endian
120: RK_FORMAT_ARGB_8888 = 0x28 << 8, /* [0:31] A:R:G:B 8:8:8:8 little endian */7. Which Pixel Formats Does RGA Support?
| Format | Macro | Notes |
|---|---|---|
| NV12 | RK_FORMAT_YCbCr_420_SP | YUV420 semi-planar |
| NV16 | RK_FORMAT_YCbCr_422_SP | YUV422 semi-planar |
| RGB888 | RK_FORMAT_RGB_888 | — |
| RGBA8888 | RK_FORMAT_RGBA_8888 | — |
| BGR888 | RK_FORMAT_BGR_888 | — |
| YUV422P | RK_FORMAT_YCbCr_422_P | YUV422 planar |
| ARGB8888 | RK_FORMAT_ARGB_8888 | — |
8. RGA Zero-Copy Data Flow
The camera / V4L2, RKNN, and MPP each obtain an fd via IOCTL_VIDIOC_xxx / rknn3_* / mpp_buffer_get_fd(); these fds can be reused directly in RGA's src.fd / dst.fd with zero memcpy throughout.
Camera (V4L2)
│ dma_fd
↓
RGA format conversion (NV12 → RGB)
│ same fd or a new fd
↓
NPU inference (RKNN)
│ results
↓
RGA composition (overlay bounding boxes)
↓
Encoder (MPP) / display9. FAQ
| Symptom | Cause | Fix |
|---|---|---|
RGA init failed: -1 | /dev/rga* missing | Install librga / load the rga kernel module |
RGA blit failed | Invalid fd / format mismatch | Check the fd and format fields |
xoffset/yoffset has no effect | Written as x/y | Use xoffset/yoffset instead |
rect.format not read | Older driver | Set both info.format and rect.format |
imrotate compile error about rga_image_t | Wrong type used | Change to rga_buffer_t |
| Rotation direction reversed | Wrong constant | IM_HAL_TRANSFORM_ROT_90/180/270 |
10. Next Steps
- MPP Multimedia Framework — the multimedia framework
- VPU Codec — video encoding/decoding
- NPU Overview — combining AI inference with multimedia
