概述
本Blog为记录学习Cdroid:TINAT113开发g2d驱动而写,俺为俺自己代言😀,同时大家也多来关顾Cdroid仓库点点⭐(原作者强烈想要⭐:))
gitee仓库:https://gitee.com/houstudio/Cdroid
github仓库:https://github.com/houstudio/cdroid
抽象底层驱动
流程图
g2d操作
Blit操作
src/porting/tinat113/graph_g2d.c · houzh/cdroid - 码云 - 开源中国
- 打开设备
if(devs[0].fb>=0)return E_OK;
memset(devs,0,sizeof(devs));
FBDEVICE*dev=&devs[0];
dev->fb=open("/dev/fb0", O_RDWR);
// Get fixed screen information
if(ioctl(dev->fb, FBIOGET_FSCREENINFO, &dev->fix) == -1) {
LOGE("Error reading fixed information fd=%d",dev->fb);
return E_ERROR;
}
LOGI("fbmem.addr=%x fbmem.size=%d pitch=%d",dev->fix.smem_start,dev->fix.smem_len,dev->fix.line_length);
// Get variable screen information
if(ioctl(dev->fb, FBIOGET_VSCREENINFO, &dev->var) == -1) {
LOGE("Error reading variable information");
return E_ERROR;
}
dev->g2d=open("/dev/g2d",O_RDWR);
- 映射fb空间
- kbuffer 设计
- 通过将用户态和内核态划分为buffer 和 kbuffer,可以有效的防止用户直接操作内核态内存,提高系统的稳定性
- 用户可以提供多个fb屏幕镜像空间,用于显示性能的高度优化,不使用ion的版本,推荐用户分配至少4个fb满屏镜像缓冲空间,通过划分这些fb物理空间,可以同时渲染多个surface。
- kbuffer 设计
- 创建表面
- 关键步骤,分配至少一块硬件Surface,用于更新显示内容,也就是fb显存,其他需要显示的内容可以现在软件Surface中渲染,这就是作为多图层UI框架的好处,不需要类似lvgl的双缓冲实现,需要复杂的通知操作,并且可以分配更多的缓冲区用于性能提升。
int32_t GFXCreateSurface(int dispid,GFXHANDLE*surface,uint32_t width,uint32_t height,int32_t format,BOOL hwsurface) {
FBSURFACE*surf=getFreeSurface();
FBDEVICE*dev = &devs[dispid];
surf->dispid=dispid;
surf->width= hwsurface?dev->var.xres:width;
surf->height=hwsurface?dev->var.yres:height;
surf->format=format;
surf->ishw=hwsurface;
surf->pitch=width*4;
size_t buffer_size=surf->height*surf->pitch;
if(hwsurface) {
setfbinfo(surf);
dev->var.yoffset=0;
ioctl(dev->fb,FBIOPAN_DISPLAY,&dev->var);
} else {
if(surf->kbuffer==0){
surf->buffer=(char*)malloc(buffer_size);
}
}
surf->ishw=hwsurface;
LOGV("surface=%x buf=%p/%p size=%dx%d hw=%d",surf,surf->kbuffer,surf->buffer,width,height,hwsurface);
*surface=surf;
return E_OK;
}
- Blit操作
- Blit操作简介:BLIT 是图形学和计算机图像处理中的一种技术,全称为 BitBLT(Bit Block Transfer),有时也称作 Blitting。它用于将图像或像素数据从一个地方复制到另一个地方,通常在显示缓冲区之间传输图像数据,以加快图形渲染速度。
- Cdroid驱动层中的含义:通过将softSurface(窗口图层)复制到hardwareSurface(fb缓存)中在屏幕上显示出具体的图像
实现 GFXBlit
函数
以下是 GFXBlit
函数的实现。该函数用于在源表面和目标表面之间执行位块传输(BitBLT)操作,具体实现如下:
int32_t GFXBlit(GFXHANDLE dstsurface, int dx, int dy, GFXHANDLE srcsurface, const GFXRect* srcrect) {
unsigned int x, y, sw, sh;
FBSURFACE* ndst = (FBSURFACE*)dstsurface;
FBSURFACE* nsrc = (FBSURFACE*)srcsurface;
FBDEVICE* dev = devs + ndst->dispid;
GFXRect rs = {0, 0};
uint8_t* pbs = (uint8_t*)nsrc->buffer;
uint8_t* pbd = (uint8_t*)ndst->buffer;
rs.w = nsrc->width;
rs.h = nsrc->height;
if (srcrect) rs = *srcrect;
// 检查有效性,确保源和目标区域在屏幕范围内
if (((int)rs.w + dx <= 0) || ((int)rs.h + dy <= 0) || (dx >= (int)ndst->width) || (dy >= (int)ndst->height) || (rs.x < 0) || (rs.y < 0)) {
LOGV("dx=%d, dy=%d rs=(%d, %d-%d, %d)", dx, dy, rs.x, rs.y, rs.w, rs.h);
return E_INVALID_PARA;
}
LOGV("Blit %p[%dx%d] %d, %d-%d, %d -> %p[%dx%d] %d, %d", nsrc, nsrc->width, nsrc->height, rs.x, rs.y, rs.w, rs.h, ndst, ndst->width, ndst->height, dx, dy);
// 调整偏移和尺寸,确保内容在屏幕范围内
if (dx < 0) {
rs.x -= dx;
rs.w += dx;
dx = 0;
}
if (dy < 0) {
rs.y -= dy;
rs.h += dy;
dy = 0;
}
if (dx + rs.w > ndst->width - screenMargin.x - screenMargin.w)
rs.w = ndst->width - screenMargin.x - screenMargin.w - dx;
if (dy + rs.h > ndst->height - screenMargin.y - screenMargin.h)
rs.h = ndst->height - screenMargin.y - screenMargin.h - dy;
LOGV("Blit %p %d, %d-%d, %d -> %p %d, %d buffer=%p->%p", nsrc, rs.x, rs.y, rs.w, rs.h, ndst, dx, dy, pbs, pbd);
pbs += rs.y * nsrc->pitch + rs.x * 4;
if (ndst->ishw == 0) {
pbd += dy * ndst->pitch + dx * 4;
} else {
pbd += (dy + screenMargin.y) * ndst->pitch + (dx + screenMargin.x) * 4;
}
const int cpw = rs.w * 4;
if ((dev->g2d < 0) || ((nsrc->ishw == 0) && (nsrc->kbuffer == NULL))) { // g2d设备不可用
for (y = 0; y < rs.h; y++) {
memcpy(pbd, pbs, cpw);
pbs += nsrc->pitch;
pbd += ndst->pitch;
}
} else {
// 使用g2d硬件加速
g2d_blt_h blt;
bzero(&blt, sizeof(blt));
blt.flag_h = G2D_BLT_NONE | G2D_ROT_0;
// 设置源图像信息
blt.src_image_h.width = nsrc->width;
blt.src_image_h.height = nsrc->height;
blt.src_image_h.laddr[0] = (uintptr_t)((nsrc->kbuffer || nsrc->ishw) ? nsrc->kbuffer : nsrc->buffer);
blt.src_image_h.clip_rect.x = rs.x;
blt.src_image_h.clip_rect.y = rs.y;
blt.src_image_h.clip_rect.w = rs.w;
blt.src_image_h.clip_rect.h = rs.h;
blt.src_image_h.format = G2D_FORMAT_ARGB8888;
blt.src_image_h.alpha = 255;
blt.src_image_h.use_phy_addr = (nsrc->kbuffer != NULL);
// 设置目标图像信息
blt.dst_image_h.clip_rect.x = dx;
blt.dst_image_h.clip_rect.y = dy;
blt.dst_image_h.clip_rect.w = rs.w;
blt.dst_image_h.clip_rect.h = rs.h;
blt.dst_image_h.width = ndst->width;
blt.dst_image_h.height = ndst->height;
blt.dst_image_h.mode = G2D_GLOBAL_ALPHA;
blt.dst_image_h.laddr[0] = (uintptr_t)((ndst->kbuffer || ndst->ishw) ? ndst->kbuffer : ndst->buffer);
blt.dst_image_h.format = G2D_FORMAT_ARGB8888;
blt.dst_image_h.alpha = 255;
blt.dst_image_h.use_phy_addr = (ndst->kbuffer != NULL) || ndst->ishw;
blt.dst_image_h.color = 0xee8899;
// 执行BLIT操作
if (ioctl(dev->g2d, G2D_CMD_BITBLT_H, &blt) < 0) {
LOGE("Error: G2D CMD BITBLT H failed");
}
}
return 0;
}
G2D 操作解析
在代码中,g2d_blt_h
结构体的填充用于定义源图像和目标图像的格式及属性,然后通过 ioctl
命令执行 G2D 的硬件加速位块传输(BitBLT)操作。
-
g2d_blt_h 结构体的设置:我们通过设置
src_image_h
和dst_image_h
字段来指定源和目标图像的宽度、高度、物理地址、格式和透明度。 -
传输标志 (flag):
G2D_BLT_NONE | G2D_ROT_0
表示只执行 BLT 操作,不进行 G2D 旋转。 -
目标图像参数:目标图像的模式设置为
G2D_GLOBAL_ALPHA
,并且定义了颜色(0xee8899)用于图像合成。 -
执行 G2D 操作:调用
ioctl
函数,使用命令G2D_CMD_BITBLT_H
完成硬件加速的传输,将内容 BLIT 到帧缓冲区以显示在屏幕上。
参考
如需更多 G2D 相关信息,可以参考 全志 R128 SDK HAL 模块开发指南——G2D_g2d 转换颜色空间。
FillRect操作
- Fillrect操作简介:填充矩形区域功能可以实现对某块区域进行预订的颜色值填充,如下图就填充了 0xFF0080FF的 ARGB 值,该功能还可以通过设定数据区域大小实现画点和直线,同时也可以通过设定 flag 实现一种填充颜色和目标做 alpha 运算。
GFXFillRect
实现解析
GFXFillRect
函数用于在指定的图形表面 (surface
) 上填充一个矩形区域 (rect
) 为指定的颜色 (color
) 。该函数支持软件和硬件两种填充模式:当硬件加速(G2D)可用时,利用 G2D 实现硬件填充;否则,采用软件填充。
int32_t GFXFillRect(GFXHANDLE surface, const GFXRect* rect, uint32_t color) {
FBSURFACE* ngs = (FBSURFACE*)surface;
FBDEVICE* dev = &devs[ngs->dispid];
uint32_t x, y;
GFXRect rec = {0, 0, 0, 0};
rec.w = ngs->width;
rec.h = ngs->height;
if (rect) rec = *rect;
LOGV("FillRect %p %d,%d-%d,%d color=0x%x pitch=%d", ngs, rec.x, rec.y, rec.w, rec.h, color, ngs->pitch);
步骤 1:初始化和输入检查
- 将传入的
surface
转换为FBSURFACE*
类型,以便访问图像表面的属性。 - 根据是否传入
rect
,确定填充的矩形区域。如果未指定rect
,则默认填充整个表面。
步骤 2:检查硬件支持,选择填充方式
-
软件填充模式
当ngs->kbuffer
和ngs->ishw
都为0
时,说明当前表面没有硬件加速支持,此时使用纯软件的方式填充指定矩形区域。if ((ngs->kbuffer == 0) && (ngs->ishw == 0)) { uint32_t* fb = (uint32_t*)(ngs->buffer + ngs->pitch * rec.y + rec.x * 4); uint32_t* fbtop = fb; for (x = 0; x < rec.w; x++) fb[x] = color; const int cpw = rec.w * 4; long copied = 0; for (y = 1; y < rec.h; y++) { fb += (ngs->pitch >> 2); memcpy(fb, fbtop, cpw); copied += ngs->pitch; } }
fb
指向缓冲区中要填充的第一个像素。- 先将第一行的所有像素填充为指定颜色。
- 使用
memcpy
将第一行的像素值快速复制到每一行,形成整个矩形的填充效果。
-
硬件加速模式
如果硬件支持(ngs->kbuffer
或ngs->ishw
非零),则使用 G2D 硬件加速来填充矩形。else { g2d_fillrect_h fill; bzero(&fill, sizeof(fill)); fill.dst_image_h.mode = G2D_PIXEL_ALPHA; fill.dst_image_h.color = color; fill.dst_image_h.format = G2D_FORMAT_ARGB8888; fill.dst_image_h.clip_rect.x = rec.x; fill.dst_image_h.clip_rect.y = rec.y; fill.dst_image_h.clip_rect.w = rec.w; fill.dst_image_h.clip_rect.h = rec.h; fill.dst_image_h.width = ngs->width; fill.dst_image_h.height = ngs->height; fill.dst_image_h.align[0] = 0; fill.dst_image_h.align[1] = fill.dst_image_h.align[0]; fill.dst_image_h.align[2] = fill.dst_image_h.align[0]; fill.dst_image_h.laddr[0] = (uintptr_t)ngs->kbuffer; fill.dst_image_h.laddr[1] = (uintptr_t)0; fill.dst_image_h.laddr[2] = (uintptr_t)0; fill.dst_image_h.use_phy_addr = 1; ioctl(dev->g2d, G2D_CMD_FILLRECT_H, (uintptr_t)(&fill)); }
- g2d_fillrect_h 结构体填充:
fill
结构体用于存储填充矩形的配置信息。dst_image_h.mode
设置为G2D_PIXEL_ALPHA
,表示目标图像使用像素级透明度。dst_image_h.color
是指定的填充颜色。dst_image_h.format
设置为G2D_FORMAT_ARGB8888
,指定像素格式。dst_image_h.clip_rect
是要填充的矩形区域。dst_image_h.laddr
设置为硬件缓冲区的物理地址。
- 使用
ioctl
系统调用,传递G2D_CMD_FILLRECT_H
命令,调用 G2D 硬件加速填充矩形区域。
- g2d_fillrect_h 结构体填充:
总结
GFXFillRect
函数通过检查硬件支持情况,选择使用软件或硬件加速的方式进行矩形填充操作。- 在硬件加速模式下,G2D 硬件通过
G2D_CMD_FILLRECT_H
命令执行快速填充,提升性能。 - 以上就是我为开发适配Cdroid的TINAT113产品g2d驱动所学习到的东西,感谢Cdroid作者对我的提供的帮助,教会了我许多Linux显示部分的知识,这里贴出Cdroid仓库,大家感兴趣多去给作者点个⭐:)。
- gitee仓库:
https://gitee.com/houstudio/Cdroid
- gitee仓库: