Cdroid驱动开发笔记1-全志T113 HAL——G2D

概述

本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 - 码云 - 开源中国

  1. 打开设备
    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);
  1. 映射fb空间
    • kbuffer 设计
      • 通过将用户态和内核态划分为buffer 和 kbuffer,可以有效的防止用户直接操作内核态内存,提高系统的稳定性
      • 用户可以提供多个fb屏幕镜像空间,用于显示性能的高度优化,不使用ion的版本,推荐用户分配至少4个fb满屏镜像缓冲空间,通过划分这些fb物理空间,可以同时渲染多个surface。
  2. 创建表面
    • 关键步骤,分配至少一块硬件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;
}
  1. Blit操作

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_hdst_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操作介绍图

  • 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:检查硬件支持,选择填充方式
  1. 软件填充模式
    ngs->kbufferngs->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 将第一行的像素值快速复制到每一行,形成整个矩形的填充效果。
  2. 硬件加速模式
    如果硬件支持(ngs->kbufferngs->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 硬件加速填充矩形区域。

总结

  • GFXFillRect 函数通过检查硬件支持情况,选择使用软件或硬件加速的方式进行矩形填充操作。
  • 在硬件加速模式下,G2D 硬件通过 G2D_CMD_FILLRECT_H 命令执行快速填充,提升性能。
  • 以上就是我为开发适配Cdroid的TINAT113产品g2d驱动所学习到的东西,感谢Cdroid作者对我的提供的帮助,教会了我许多Linux显示部分的知识,这里贴出Cdroid仓库,大家感兴趣多去给作者点个⭐:)。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值