Android14显示系统 - ARM GPU完全剖析

文章目录

1、资料快车

1、Linux GPU物理模型 : http://joyxu.github.io/2021/05/09/gpu01/

2、Linux GPU工作流程:http://joyxu.github.io/2021/05/10/gpu02/

3、Linux 图像软件栈:http://joyxu.github.io/2021/05/11/gpu03/

4、ARM Mali GPU openGL端到端流程 : http://joyxu.github.io/2021/05/12/gpu04/

5、Linux GPU系列-05-MESA架构 :http://joyxu.github.io/2021/05/13/gpu05/

6、Rockchip MALI_GPU_driver

https://opensource.rock-chips.com/wiki_Graphics#MALI_GPU_driver

7、GPU驱动开发入门------从OpenGL到GPU硬件(以ARM Mali为例)

https://zhuanlan.zhihu.com/p/1930594532157813197

8、Rockchip RK3399-Mali-T860 GPU驱动(mesa+Panfrost)

https://www.cnblogs.com/zyly/p/17459196.html#_label2_0_1

9、Rockchip RK3588 - Mali-G610 GPU驱动(mesa+Panthor)

https://www.cnblogs.com/zyly/p/19188301

2、preface

1)一个图形的构造有哪些工作内容?

1、图形构造:画图,比如矩形、三角形、立方体、长方体等等2D图形;无非就是构造一帧图片(描绘屏幕的像素),工作最为简单;

2、图像渲染:将2D图形画面 变换 成"3D"图形(实际上还是2D图形,只是带有丰富光影效果),涉及像素的算法变换,最为复杂;

三维图形渲染显示的全过程:

https://imgtec.eetrend.com/blog/2020/100046895.html

3、图像质量(PQ) 处理:色温、对比度、锐度、Dither、Demura、gamma、od、lod等调节

色温:

4、图像合成:将多个图层合并成一个,并送显;将各个像素叠加在一起,难度次之;

5、小结:

1、经过层层处理,最终呈现出一帧图像(不容易!)难度:图形构造 < 图像合成、图像质量处理 < 图像渲染;

2、GPU可以胜任以上几种工作,但最擅长的是图像渲染,同时它也是为此而设计,那么一个图像完全由GPU承担,这不仅非常消耗GPU资源,拖慢整体的工作时间,同时也让GPU大才小用,作为嵌入式场景,当然不允许这样浪费,实际上都会通过设计其它硬件来分担此部分工作,让GPU专注于渲染工作,比如

1)图形构造 - 2D Graphic Processing

2)合成工作交给HWC;

3)PQ效果由PQ模块处理;

2、面向异构计算CPU+GPU+NPU

复制代码
随着手机、汽车芯片进入"CPU+GPU+NPU"异构时代,探索多硬件协同渲染------例如,NPU负责图像预处理(如降噪),GPU负责实时绘制,CPU负责逻辑控制,最大化利用硬件资源。

3、ARM-GPU选型

1)GPU : Graphics Processing Unit

2)GPU架构复杂程度不亚于CPU,与CPU类似,GPU的选型颇为丰富,这里重点看ARM mali GPU架构;

3)ARM mali GPU四大微架构概述(Utgard / Midgard / Bifrost / Valhall);

https://zhuanlan.zhihu.com/p/107141045

4)选型表

4、从图片渲染流程看GPU的作用

图像渲染大概经过了四个阶段,分别是应用处理阶段、几何处理阶段、光栅化阶段以及像素处理阶段 ,如下图所示,其中应用处理阶段CPU还在处理,CPU需要对图像进行操作和改变,将生成的图元信息交给GPC,之后其他阶段都是交由GPU处理。

1、CPU阶段

CPU负责将数据准备好,设置好渲染状态,然后输出图元 到 指定位置的"显存"(嵌入式中使用内存充当显存),GPU一帧一帧地渲染完成后再回传到指定内存空间,Display控制器(比如LCD控制器)再取出显示到屏幕

图元是指渲染的基本图形,通俗来讲图元可以是顶点,线段,三角面等,复杂的图形可以通过渲染多个三角形来实现。

2、GPU负责处理的阶段

几何处理阶段、光栅化阶段以及像素处理阶段均由GPU处理

由于图像数据处理量极大,CPU并不适合用来处理图像数据(换言之CPU有强大的综合能力,但图形处理特性是单一且任务量巨大),因此需要专门的器件GPU来处理,嵌入式领域GPU集成到CPU中;

5、GPU物理模型概述

1)GPU/CPU基础知识: https://www.cnblogs.com/tully/p/18379114

2)GPU是一种专门为图像渲染、视频图像编解码的并行计算机任务而设计的芯片;

3)GPU器件形态:

1、在大型机器上,GPU可以是独立的扩展卡(即我们PC机器常说的显卡,显卡会集成大容量内存)

2、在嵌入式领域,也可以集成在SoC(System on a Chip 系统芯片)中

3、ARM T950D4平台GPU架构图

4、GPU逻辑功能框图

6、GPU的工作流程

1)CPU如何与GPU交互?

1)以集成SOC为例,CPU与GPU交互模型如下

1、与其它集成在SOC的IP模块不同,GPU也是处理器(并行架构),与CPU的架构不同(串行架构),它完全是独立运行的,只是需CPU分配"Job",

2、GPU需要运行程序工作,这里的可执行程序对应的二进制是GPU的机器码,通过CPU端的GPU驱动程序进行传递;

3、注意GPU软件栈编程与其它模块 有较大的差别!

4、GPU有自己独立内存(Device Memory) - GDDR;

5、几个GPU基本概念

复制代码
1、GPU context
GPU context代表GPU当前状态,每个context有自己的page table,多个context可以同时共存

2、GPU Channel
每个GPU context都有一个或者多个GPU Channel,CPU发给GPU的命令通过GPU Channel传递,一般GPU Channel是一个软件概念,通常是一个ring buffer

3、page table
和CPU的page table功能一样,用于VA到PA的映射,访问GPU的地址空间

4、CPU和GPU通信主要有几下几种方式(以嵌入式SOC集成GPU举例):
通过GPU寄存器
通过GPU的内存映射到CPU的地址空间中
通过GPU的页表把CPU的系统内存映射到GPU的地址空间中
通过MSI中断

2)GPU的程序片段和数据从哪里获得?

从交互模型来看,驱动程序有两部分工作:

1、通过寄存器配置GPU;

2、与CPU一样,GPU运行需要指令和数据,驱动负责将指令(也可称为代码片段) 和 图像数据则通过GPU内存和主存 传输;

3)谁来指挥GPU驱动工作?

1)GPU比较特殊,主要的驱动工作是在用户态,内核驱动负责物理层级的配置和传输;

2)而用户态的GL负责 GPU所需要的图形数据/渲染指令的 生成;

3)以OpenGL为例,GL需要遵循GPU的运作模式(称为GPU Pipeline),转化为对应的编程接口,并向应用程序提供封装好的API,OpenGL API主要组织好job所需的数据,为后续的shader core计算做好准备。

4)OpenGL的编程模型

4)GPU Job的概念

之前提及GPU是并行架构,既然是并行,如何管理各个并行工作任务?

这个也是GPU有较大的编程风格差异的原因,它的工作需要拆分各个Job(驱动以Job为提交单位),Job下有多个Task,GPU内有job Manager硬件模块负责此部分工作,无论是GPU驱动还是用户态的GL库,也要按照此方式进行定制编写代码

GPU Job类型

5)GPU驱动编程接口

6)小结

1、GPU渲染一帧画面整体的工作流程如下

2、结合OpenGL api观察连续帧渲染流程

7、ARM GPU驱动概述

1)preface

无论是应用层的GL库、内核态的驱动 还是硬件上的架构设计,不同平台各个厂家的实现五花八门,并且大多数闭源

这里均使用开源软件组合来展示说明,把握核心原理,以不变应万变!

2)整体框架

采用开源组合说明:skia + mesa3D(openGL) + drm + panfrost

复制代码
1、在看GPU的驱动相关资料,有几个比较不寻常的点:
1)常常看到GL将用户操作转化为GPU能识别的指令的说法,这难道不是驱动的工作?
2)从APK到GPU内核驱动,要经过复杂的中间件(SKIA,GL),甚至非专业人士都难看懂;
这是因为GPU的驱动工作被放在用户态的GL库上,内核态驱动负责简单的传送工作;

2、与其它设备驱动相比,我们习惯使用寄存器方式去操控设备;在GPU这里,我们不单要配置寄存器,还要给GPU传送可执行程序和数据!

3、由于GPU驱动主要的工作在用户态GL,因此GPU内核态驱动和其他设备驱动其实差异不大,主要就两个面:
控制面:设置state,通过fence做同步等
数据面:把数据从CPU搬到GPU,这里主要涉及到数据格式和数据layout,类似网卡的描述符,以及设备内存的管理。

4、libmali.so 一般厂家提供,闭源

5、ARM Mali架构
1)AML - T950D4 - Mali G31 MP2 - bifrost架构
2)AML - T950S - Mali-450MP2 -Utgard架构

6、ARM开发但不遵循DRM规范的GPU驱动(代码量极大)
android/vendor/amlogic/common/gpu/mali
Mali-GPU驱动-Midgard架构分析:https://blog.csdn.net/weixin_43082062/article/details/139595284

7、遵循DRM框架,且开源的GPU驱动(代码量适中)
1)DRM driver for ARM Mali 400/450 GPUs.
android/vendor/amlogic/common/kernel/common_5.4/drivers/gpu/drm/lima/
2)DRM driver for ARM Mali Midgard (T6xx, T7xx, T8xx) and Bifrost (G3x, G5x, G7x) GPUs.
android/vendor/amlogic/common/kernel/common_5.4/drivers/gpu/drm/panfrost/

8、Mesa 3D + panfrost开源项目官网: https://docs.mesa3d.org/drivers/panfrost.html


9、设备节点
1)950s-an14-基于DRM
/dev/mali
/dev/dri/card0
/dev/dri/renderD128

2)950d4-an13-传统FB架构
/dev/mali0
/dev/graphics/fb0

10、OpenCL
/android/external/ComputeLibrary/src/core/CL/OpenCL.cpp
{ "libOpenCL.so", "libGLES_mali.so", "libmali.so" }

8、panfrost-gpu驱动分析

1)数据结构

复制代码
1、
struct panfrost_device {
	struct device *dev;
	struct drm_device *ddev;
	struct platform_device *pdev;

	void __iomem *iomem;
	struct clk *clock;
	struct clk *bus_clock;
	struct regulator *regulator;
	struct reset_control *rstc;
	
	struct panfrost_features features;
	
	spinlock_t as_lock;
	unsigned long as_in_use_mask;
	unsigned long as_alloc_mask;
	struct list_head as_lru_list;
	
	struct panfrost_job_slot *js;
	
	struct panfrost_job *jobs[NUM_JOB_SLOTS];
	struct list_head scheduled_jobs;
	
	struct panfrost_perfcnt *perfcnt;
	
	struct mutex sched_lock;
	struct mutex reset_lock;
	
	struct mutex shrinker_lock;
	struct list_head shrinker_list;
	struct shrinker shrinker;
	
	struct {
		struct devfreq *devfreq;
		struct thermal_cooling_device *cooling;
		unsigned long cur_freq;
		unsigned long cur_volt;
		struct panfrost_devfreq_slot slot[NUM_JOB_SLOTS];
	} devfreq;
};

2、panfrost_job
struct panfrost_job {
    struct drm_sched_job base;
    struct kref refcount;
    
    struct panfrost_device *pfdev;
    struct panfrost_file_priv *file_priv;
    
    /* Optional fences userspace can pass in for the job to depend on. */
    struct dma_fence **in_fences;
    u32 in_fence_count;
    
    /* Fence to be signaled by IRQ handler when the job is complete. */
    struct dma_fence *done_fence;
    
    __u64 jc;
    __u32 requirements;
    __u32 flush_id;
    
    /* Exclusive fences we have taken from the BOs to wait for */
    struct dma_fence **implicit_fences;
    struct panfrost_gem_mapping **mappings;
    struct drm_gem_object **bos;
    u32 bo_count;
    
    /* Fence to be signaled by drm-sched once its done with the job */
    struct dma_fence *render_done_fence;
}

2)驱动框架

复制代码
1、点的目录源码
panfrost/
├── panfrost_drv.c          # 驱动主入口,IOCTL 接口
├── panfrost_device.c       # 设备初始化和管理
├── panfrost_gpu.c          # GPU 硬件操作和特性检测
├── panfrost_job.c          # 任务调度系统
├── panfrost_mmu.c          # MMU 地址空间管理
├── panfrost_gem.c          # GEM 对象管理
├── panfrost_devfreq.c      # 动态频率调节
├── panfrost_perfcnt.c      # 性能计数器
└── panfrost_features.h     # GPU 特性定义

2、支持的GPU设备
static const struct of_device_id dt_match[] = {
	{ .compatible = "arm,mali-t604" },
	{ .compatible = "arm,mali-t624" },
	{ .compatible = "arm,mali-t628" },
	{ .compatible = "arm,mali-t720" },
	{ .compatible = "arm,mali-t760" },
	{ .compatible = "arm,mali-t820" },
	{ .compatible = "arm,mali-t830" },
	{ .compatible = "arm,mali-t860" },
	{ .compatible = "arm,mali-t880" },
	{}
};
MODULE_DEVICE_TABLE(of, dt_match);

3、ioctl
static const struct drm_ioctl_desc panfrost_drm_driver_ioctls[] = {
#define PANFROST_IOCTL(n, func, flags) \
	DRM_IOCTL_DEF_DRV(PANFROST_##n, panfrost_ioctl_##func, flags)

	PANFROST_IOCTL(SUBMIT,		submit,		DRM_RENDER_ALLOW | DRM_AUTH),
	PANFROST_IOCTL(WAIT_BO,		wait_bo,	DRM_RENDER_ALLOW),
	PANFROST_IOCTL(CREATE_BO,	create_bo,	DRM_RENDER_ALLOW),
	PANFROST_IOCTL(MMAP_BO,		mmap_bo,	DRM_RENDER_ALLOW),
	PANFROST_IOCTL(GET_PARAM,	get_param,	DRM_RENDER_ALLOW),
	PANFROST_IOCTL(GET_BO_OFFSET,	get_bo_offset,	DRM_RENDER_ALLOW),
	PANFROST_IOCTL(PERFCNT_ENABLE,	perfcnt_enable,	DRM_RENDER_ALLOW),
	PANFROST_IOCTL(PERFCNT_DUMP,	perfcnt_dump,	DRM_RENDER_ALLOW),
	PANFROST_IOCTL(MADVISE,		madvise,	DRM_RENDER_ALLOW),
};

drmIoctl(DRM_IOCTL_PANFROST_SUBMIT)
--panfrost_ioctl_submit

4、
android\vendor\amlogic\common\kernel\common_5.4\drivers\gpu\drm\panfrost\panfrost_drv.c
panfrost_probe()
--drm_dev_register(ddev, 0) //调用drm接口创建设备节点/dev/dri/renderD128


5、job提交流程
panfrost_ioctl_submit(strcut drm_device *dev, void *data, struct drm_file *file)
--struct panfrost_job *job = kzmalloc(sizeof(*job), GFP_KERNEL) //创建job对象
--panfrost_copy_in_sync(dev, file, args, job); //解析同步对象
--panfrost_lookup_bos(dev, file, args, job); //查找并引用GEM对象(BOs)
--panfrost_job_push(job); //push
--drm_syncobj_replace_fence(sync_out, job->render_done_fence);


6、gpu硬件操作
android\vendor\amlogic\common\kernel\common_5.4\drivers\gpu\drm\panfrost\panfrost_gpu.c
int panfrost_gpu_init(struct panfrost_device *pfdev)
--panfrost_gpu_soft_reset(pfdev);
--panfrost_gpu_init_features(pfdev);
--devm_request_irq(pfdev->dev, irq, panfrost_gpu_irq_handler,
			       IRQF_SHARED, "gpu", pfdev);
--panfrost_gpu_power_on(pfdev);

7、寄存器读写接口
gpu_read()
gpu_write()

9、使用GL库画一个静态彩色三角形流程

在学习了晦涩的GPU理论知识和驱动之后,回归实际应用,看一个用户程序使用GPU的例子来深化一下

1)CPP代码

复制代码
Android上的图形库有OpenGL-ES(轻量级应用)、Vulkan(深度定制,未来趋势),这里使用OpenGL-ES举例

1.完整代码
https://github.com/LinuxMadGuy/opengl-misc/blob/master/OpenGL/triangle/triangle.cpp

2.重点代码
void main() {
    float vertices[] = {
        -0.5f, -0.5f, 0.0f, // left
        0.5f, -0.5f, 0.0f, // right
        0.0f,  0.5f, 0.0f  // top
    };
    //1.GPU端生成一个顶点数组对象(VAO)和一个顶点缓冲对象(VBO),这是存放和管理顶点数据的基础。
    unsigned int VBO, VAO;
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);
    // bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s).
    //绑定VAO
    glBindVertexArray(VAO);
    
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); //准备Vertext数据
    
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(1);
    
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);
    
    // render loop
    // -----------
    while (!glfwWindowShouldClose(window))
    {
        //2.render
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f); //设置清屏背景颜色
        glClear(GL_COLOR_BUFFER_BIT);//开始清屏
    
        // draw our first triangle
        glUseProgram(shaderProgram);
        glBindVertexArray(VAO); // seeing as we only have a single VAO there's no need to bind it every time, but we'll do so to keep things a bit more organized
        glDrawArrays(GL_TRIANGLES, 0, 3);  //开始GPU进行绘制三角形
    
        //3.glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
        glfwSwapBuffers(window); //交换前后帧缓存区
        glfwPollEvents(); //处理IO事件
    }
}

3.小结
准备好绘制数据(顶点数据、命令id等) -> GPU工作 -> 显示

2)GL库之准备Vertext数据

复制代码
glBufferData
/android/external/mesa3d/src/mesa/main/bufferobj.c
mesa_BufferData()
--_mesa_buffer_data()
---buffer_data_error()
-----buffer_data()
------ctx->Driver.BufferData()
/android/external/mesa3d/src/mesa/state_tracker/st_cb_bufferobjects.c
--------st_bufferobj_data()
----------bufferobj_data()
/android/external/mesa3d/src/gallium/auxiliary/util/u_inlines.h
------------pipe_buffer_write()
--------------pctx->buffer_subdata()
/android/external/mesa3d/src/gallium/drivers/panfrost/pan_resource.c
----------------u_default_buffer_subdata()
------------------pipe->transfer_map() //各GPU厂商实现,panfrost使用MESA公共函数

/android/external/mesa3d/src/gallium/drivers/panfrost/pan_resource.c
pctx->buffer_subdata = u_default_buffer_subdata;

3)GL库之执行绘制

复制代码
glDrawArrays
/android/external/mesa3d/src/mesa/main/draw.c
--_mesa_DrawArrays()
----_mesa_draw_arrays()
------gl_context.Driver.Draw()
/android/external/mesa3d/src/mesa/state_tracker/st_draw.c
--------st_draw_vbo()
/android/external/mesa3d/src/gallium/auxiliary/cso_cache/cso_context.c
----------cso_draw_vbo(st->cso_context, &info);
/android/external/mesa3d/src/gallium/auxiliary/util/u_vbuf.c
------------u_vbuf_draw_vbo(vbuf, info);
--------------pipe->draw_vbo(pipe, &new_info);
/android/external/mesa3d/src/gallium/drivers/panfrost/pan_context.c
----------------panfrost_draw_emit_vertext()
/android/external/mesa3d/src/gallium/drivers/panfrost/pan_context.c
------------------pan_emit_draw_descs(batch, &cfg, PIPE_SHADER_VERTEX);

4)GL库之切换Framebuffer

复制代码
glfwSwapBuffers
  main::_mesa_Flush
    gl_context::Driver.Flush
      state_tracker::st_glFlush
        draw_do_flush
      panfrost::panfrost_flush
        panfrost::panfrost_batch_submit_jobs
          panfrost::panfrost_batch_submit_ioctl
            drm::drmIoctl(DRM_IOCTL_PANFROST_SUBMIT)

进入kernel panfrost驱动
/android/vendor/amlogic/common/kernel/common_5.4/drivers/gpu/drm/panfrost/panfrost_drv.c            
panfrost_ioctl_submit
  panfrost_job_push
    drm_sched_entity_push_job
      drm_sched_rq_add_entity
        panfrost_job_run
      panfrost_job_hw_submit
        panfrost_job_write_affinity (设置job和core的亲和性)
          job_write (写job manager寄存器)

10、GPU的内存分配和拷贝

glGenbuffers,glBufferData过程中,涉及到gpu内存的分配和拷贝。

1)panfrost内存分配

基于mali panfrost来分析的话,用户态的代码在mesa的src/panfrost/lib/pan_bo.c中。

复制代码
panfrost_bo_create
  panfrost_bo_alloc
    drmIoctl  PANFROST_CREATE_BO

之后陷入kernel之后的流程:

复制代码
panfrost_ioctl_create_bo
  drm_gem_shmem_create

之后再用户态就可以用普通的memcpy来拷贝了。

为了减少和kernel态的交互,mesa panfrost代码中也实现了一个内存pool,详细代码在
src/panfrost/lib/pan_pool.c中,使用最多的函数是panfrost_pool_alloc_aligned。

相关推荐
17(无规则自律)3 小时前
深入浅出 Linux 内核模块,写一个内核版的 Hello World
linux·arm开发·嵌入式硬件
梁洪飞15 小时前
内核的schedule和SMP多核处理器启动协议
linux·arm开发·嵌入式硬件·arm
代码游侠1 天前
学习笔记——Linux字符设备驱动
linux·运维·arm开发·嵌入式硬件·学习·架构
syseptember2 天前
Linux网络基础
linux·网络·arm开发
代码游侠2 天前
学习笔记——Linux字符设备驱动开发
linux·arm开发·驱动开发·单片机·嵌入式硬件·学习·算法
程序猿阿伟2 天前
《Apple Silicon与Windows on ARM:引擎原生构建与模拟层底层运作深度解析》
arm开发·windows
wkm9562 天前
在arm64 ubuntu系统安装Qt后编译时找不到Qt3DExtras头文件
开发语言·arm开发·qt
unicrom_深圳市由你创科技2 天前
基于ARM+DSP+FPGA异构计算架构的高速ADC采集卡定制方案
arm开发·fpga开发
松涛和鸣2 天前
DAY69 Practical Guide to Linux Character Device Drivers
linux·服务器·arm开发·数据库·单片机·嵌入式硬件
松涛和鸣2 天前
69、Linux字符设备驱动实战
linux·服务器·网络·arm开发·数据库·驱动开发