一文读懂高通GPU驱动渲染流程

2024-08-24 07:04

本文主要是介绍一文读懂高通GPU驱动渲染流程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1. gpu command分析

1.1 gpu command概述

SM8650平台上,GLES发送给KMD(GPU驱动)的GPU命令有两种类型:同步命令和绘制命令。

绘制命令,一般都是一个个的drawcall组成的,是真正GPU程序指令,KMD会给GPU硬件进行处理。

同步命令,在KMD就能处理,无需GPU硬件参与。该指令主要是往KMD的任务队列塞同步点,用来阻塞绘制指令的处理。比如dequeueBuffer带的release fence,会被打包成一个同步命令,发送到KMD,KMD将其放到任务队列,UMD后续提给KMD的命令,就会排在该同步命令后面,就会被阻塞无法被处理,直到该同步命令的同步点被signal,同步命令从任务队列清除,排在该同步命令后面的命令才能继续被处理。

GLES给KMD发送命令时,会创建一个struct kgsl_gpu_command实例,然后把命令打包好,保存到该实例里,然后通过IOCTL_KGSL_GPU_COMMAND发送给KMD处理。

KMD把GPU命令抽象成struct kgsl_drawobj数据结构的子类。比如绘制命令抽象成struct kgsl_drawobj_cmd,同步命令抽象成struct kgsl_drawobj_sync,它们都继承于struct kgsl_drawobj。

故IOCTL_KGSL_GPU_COMMAND的实现函数kgsl_ioctl_gpu_command函数接收GLES发送的消息后,会把struct kgsl_gpu_command实例内保存的GPU命令转成struct kgsl_drawobj实例(这里的转成是指创建struct kgsl_drawobj实例,解析把struct kgsl_gpu_command内容并填充到struct kgsl_drawobj实例,而不是简单的类型强转)。绘制命令转成struct kgsl_drawobj_cmd实例, 同步命令转成struct kgsl_drawobj_sync类型的实例,然后再把这些drawobj实例,保存到context的drawqueue里面。如图:    

c202cf0b94860d2358e59e5c47cc844c.jpeg

KMD初始化时,会为每个GPU设备创建一个struct adreno_device实例,每个struct adreno_device实例会有一个struct adreno_hwsched实例,每个struct adreno_hwsched实例会有16条struct adreno_dispatch_job类型的链表,每个struct adreno_dispatch_job会关联一个context,如图:

8a3156e9f175dd0797d87fe9234e14f2.jpeg

kgsl_ioctl_gpu_command函数将struct kgsl_gpu_command实例保存的GPU命令转成struct kgsl_drawobj实例,并保存到context的drawqueue里面;同时还会创建一个struct adreno_dispatch_job实例,然后根据context的priority,将创建的struct adreno_dispatch_job实例加入到struct adreno_hwsched实例对应链表的尾部(如priority为0,则加入第0条链表尾部)。

KMD初始化时,会创建一个名为kgsl_hwsched内核线程。该线程专门负责处理struct adreno_dispatch_job实例关联的context的drawqueue里面的drawobj。

kgsl_ioctl_gpu_command函数末尾,或者GPU HW处理完任务后,或者同步命令的同步点signal后,都会唤醒kgsl_hwsched线程,kgsl_hwsched线程依次遍历struct adreno_hwsched实例的16条链接,取出struct adreno_dispatch_job实例关联的context的drawqueue里面的drawobj进行处理。如果是同步命令drawobj,就直接从drawqueue里面移除。如果是绘制命令drawobj就发送GPU HW处理。

KMD初始化时,还会创建一个名为kgsl-events的内核线程。该线程专门负责GPU同步点signal,如acquire fence signal,EGLSync/GLSync的signal。

每个context都间接拥有一个struct kgsl_event类型的链表。该链表是用来保存GPU同步点的,如acquire fence,EGLSync/GLSync等。struct kgsl_event实例有两个很关键成员变量:timestamp和func。timestamp用来判断一个该event是否signal,func是事件处理函数。

08f6896bd9abbd341767651a53ea58fd.jpeg

GPU HW硬件有个retire timestamp寄存器,专门用来记录GPU HW完成任务时的时间,GPU HW每完成一次计算任务就会更新这个retire timestamp寄存器。

每次GPU HW处理完任务后,KMD就会遍历比较context的events链表的event的timestamp和retire timestamp寄存器的时间戳,如果timestamp小于hw的retire timestamp时,KMD就会在kgsl_events线程执行event的func函数,func函数就会signal同步点。    

1.2 同步命令处理分析

1.2.1 同步命令类型

KMD根据同步点的实现差异,将同步命令分为三种类型:

(1)fence syncpoint command:主要是fence类型的同步点,如release fence。对应的数据结构struct kgsl_cmd_syncpoint_fence

(2)timestamp syncpoint command:未知(没能通过gles api分析出什么情况下会下发该种同步点命令,应该是在不支持android fence机制的系统使用的)。对应的数据结构struct kgsl_cmd_syncpoint_timestamp

(3)timeline syncpoint command:未知(没能通过gles api分析出什么情况下会下发该种同步点命令)。对应的数据结构struct kgsl_cmd_syncpoint_timeline

但是这三种同步点最终都会换转成struct kgls_drawobj_sync_event实例,然后保存到struc kgsl_drawobj_sync实例的synclist变量内。看下图:    

877f5d592433055633d17b2f323ece1c.jpeg

1.2.2 fence syncpoint command处理分析

根据第一章节的概述,我们可以把GPU命令处理一般分为二个步骤:

(1)预处理,该步骤主要解析用户空间传进来的GPU命令,然后转化成对应的drawobj实例并保存到context的drawqueque里面,同时创建一个struct adreno_dispatch_job实例加入到struct adreno_hwsched实例的链表内。

(2)ready后,kgsl_hwsched内核线程取出drawobj处理。

故对于fence syncpoint command处理分析。我们也分成这两个步骤:

(1)首先分析同步命令如何转成struc kgsl_drawobj_sync实例。上面小节的UML图已经很直观的展示这一个转化过程。这里我们再补一张这一流程的时序图:    

cdd86bb5c30b723a4a2fc48f6e3de372.png

(2)当fence signal后,触发drawobj_sync_expire执行,drawobj_sync_expire函数会再次创建一个struct adreno_dispatch_job实例,并根据context的priority,将创建的struct adreno_dispatch_job实例加入到struct adreno_hwsched实例对应链表的尾部(个人认为这个步骤是多余的,因为上面已经做了这个动作),然后唤醒kgsl_hwsched线程进行处理。时序如下:

e5b1a96ca09fc45aa069b0b3237f3215.png

kgsl_hwsched线程取出drawobj处理,对于fence sync drawobj而言,kgsl_hwsched线程不做任何特殊处理,仅仅移出drawqueue,避免阻塞后面的drawobj:    

f1dfa7d9a89cbbc83d5490d43b12b53a.png    

下图是一次处理过程中的ftrace打印:

bcf56684d247c8e1ed5be73b861fd82e.png

1.2.3 timestamp syncpoint command处理分析

timestamp syncpoint的处理流程和fence syncpoint处理流程类似,同样分为两个步骤。区别一个使用的是kgsl events框架,一个使用的dma fence框架。

(1)首先解析用户空间传进来的同步命令,转成struct kgsl_drawobj_sync实例,然后把实例保存到context的drawqueque里面,同时也会创建一个struc kgsl_event实例,并保存到context的events链表里。时序图如下:

29ce43cb362f1f5ca39a4aa75e7e75ee.png

(2)当event的timestamp小于hw的retire timestamp时,唤醒ksgl_events线程执行event的事件处理函数

_kgsl_event_worker。_kgsl_event_worker执行drawobj_sync_func,drawobj_sync_func执行drawobj_sync_expire:

ce2bba9612ff93055ecdaf21a2cffa35.png

drawobj_sync_expire函数唤醒kgsl_hwsched线程进行处理。流程和fence syncpoint command处理流程一样,这里就不赘述了。

1.3 绘制命令处理分析

和上面一样,绘制命令处理,我们同样分为两个步骤进行分析。

(1)解析用户空间传进来的绘制命令,然后转化成struct kgsl_drawobj_cmd实例并保存到context的drawqueque里面,同时创建一个struct adreno_dispatch_job实例加入到struct adreno_hwsched实例的链表内。

同样,在讲解struct kgsl_drawobj_cmd生成过程前,先梳理出其相关的数据结构关系:

e9380cfd65518566bd71a80d4cd178ca.jpeg

struct kgsl_drawobj_cmd里面保存了两种类型的数据:    

(i)一个是指令数据,保存在cmlist变量里。由struct kgsl_gpu_command的cmdlist解析获得。

(ii)一个是对象数据,是输入给指令处理的,保存在memlist变量里和profiling_buf_entry变量里。由struct kgsl_gpu_command的objlist解析获得。SM8650基本上都是profiling_buf_entry。

生成struct kgsl_drawobj_cmd实例的时序图如下:

5eddf3f4e3a8b312cc3d0979f67acefc.png

(2)struct kgsl_drawobj_cmd实例处理的关键从gen7_hwsched_submit_drawobj函数开始。gen7_hwsched_submit_drawobj函数先把struct kgsl_drawobj_cmd转成struct hfi_submit_cmd实例和struct hfi_submit_cmd实例,struct hfi_submit_cmd实例和struct hfi_submit_cmd实例都是在一块内存里面。然后在调用gen7_gmu_context_queue_write函数将该内存的数据写到显存里面,GPU FW就会取出送给GPU HW处理。    

60a7a3139f6524372427207ede9a5422.jpeg

代码时序和详细如下图:    

9b01441e9189b70381f72b8a636e2d55.png    

0f39957a84632eef6cfb8025a836cabf.png

3fbd0c50c3ab62b174806ab6a382d23a.png    

89abd924cb242881c9f5e524c853a11f.png

2. gpu fence分析

gpu fence,即gpu创建的fence,由gpu进行signal。queuebuffer代入的fence就是gpu fence,EGLSync/GLsync等也是一个gpu fence。

gpu fence在KMD的数据结构为struct kgsl_sync_fence,每个struct kgsl_sync_fence实例会间接关联一个struct kgsl_event实例。

每个context都有个struct kgsl_sync_timeline实例,struct kgsl_sync_timeline实例有一个struct kgsl_sync_fence链表。context创建的gpu fence都保存在这个链表内。

gpu fence都是通过IOCTL_KGSL_TIMESTAMP_EVENT命令通知KMD创建的,该命令的实现函数为kgsl_ioctl_timestamp_event。kgsl_ioctl_timestamp_event创建gpu fence时,会生成一个fence fd,然后把这个fd保存到参数priv变量里带到用户空间。如图:    

4b2c075a1eb946978af713d9b6f53c47.jpeg

创建gpu fence的时序图:

36e506b552aad4a8fd4210dde15de0f7.png

gpu fence借助kgsl events模块signal的,根据上面的分析,我们知道每个gpu fence会关联一个struct kgsl_event实例,该实例timestamp和gpu fence是同一个。该实例signal就是gpu fence signal。struct kgsl_event实例signal流程就不分析了,上面分析过。

struct kgsl_event实例signal,kgsl-events线程被唤醒,执行_kgsl_event_worker函数

执行kgsl_sync_fence_event_cb函数,kgsl_sync_fence_event_cb执行kgsl_sync_timeline_signal函数,kgsl_sync_timeline_signal函数在执行dma fence的回调函数,完成signal操作。时序图如下:    

7238aa324f43fe556fd69175fa32e7d9.png

我们可以通过ftrace的kgsl_fire_event进行判断:

d6f521649863cc034df9537a67e78b07.png    

3. 一次完整渲染流程分析

在kgsl_ioctl函数加trace,一次eglSwapBuffers,会发起三次ioctl:

c98937ed5443a8b777aee63233c547b3.png

59bf2a3c6adaaeb5fbbfa1d854cdc8d0.png

第一次ioctl,fence syncpoint command

第二次ioctl,绘制命令

第三次ioctl,创建gpu fence

我们可以通过ftrace进行分析整个渲染流程:    

e89505db8b99de0a308443127ce73f4e.png

syncpoint_fence:表示解析fence syncpoint command完成

第一个adreno_cmdbatch_queued:表示fence syncpoint command进入待处理队列

第二个adreno_cmdbatch_queued:表示绘制命令进入待处理队列

kgsl_register_event: 表示在创建gpu fence

syncpoint_fence_expire:表示release fence signal

adreno_cmdbatch_submitted:表示指令提交到了GPU HW

kgsl_fire_event:表示gpu fence signal

4. 总结

本文是基于SM8650, Android系统,GLES API进行分析的,并不能适用所有高通平台,系统,图形接口,有一定的局限性。如有疏漏,在所难免,还请读者朋友海涵。         

以上所有的代码,都来自开源网站:

https://git.codelinaro.org/clo/la/platform/vendor/qcom/opensource/graphics-kernel/-/tree/gfx-kernel.lnx.14.0.r6-rel?ref_type=heads

df7ceb876a1d0ee061666309492b4102.jpeg

Linux内存管理中锁使用分析及典型优化案例总结

Binder驱动中的流程详解

2024年Arm最新处理器架构分析——X925和A725

cd3907258efc384917e0348e990563e2.gif

长按关注内核工匠微信

Linux内核黑科技| 技术文章| 精选教程  

这篇关于一文读懂高通GPU驱动渲染流程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1101778

相关文章

一文深入详解Python的secrets模块

《一文深入详解Python的secrets模块》在构建涉及用户身份认证、权限管理、加密通信等系统时,开发者最不能忽视的一个问题就是“安全性”,Python在3.6版本中引入了专门面向安全用途的secr... 目录引言一、背景与动机:为什么需要 secrets 模块?二、secrets 模块的核心功能1. 基

一文详解MySQL如何设置自动备份任务

《一文详解MySQL如何设置自动备份任务》设置自动备份任务可以确保你的数据库定期备份,防止数据丢失,下面我们就来详细介绍一下如何使用Bash脚本和Cron任务在Linux系统上设置MySQL数据库的自... 目录1. 编写备份脚本1.1 创建并编辑备份脚本1.2 给予脚本执行权限2. 设置 Cron 任务2

SpringBoot整合Flowable实现工作流的详细流程

《SpringBoot整合Flowable实现工作流的详细流程》Flowable是一个使用Java编写的轻量级业务流程引擎,Flowable流程引擎可用于部署BPMN2.0流程定义,创建这些流程定义的... 目录1、流程引擎介绍2、创建项目3、画流程图4、开发接口4.1 Java 类梳理4.2 查看流程图4

一文详解如何在idea中快速搭建一个Spring Boot项目

《一文详解如何在idea中快速搭建一个SpringBoot项目》IntelliJIDEA作为Java开发者的‌首选IDE‌,深度集成SpringBoot支持,可一键生成项目骨架、智能配置依赖,这篇文... 目录前言1、创建项目名称2、勾选需要的依赖3、在setting中检查maven4、编写数据源5、开启热

java Long 与long之间的转换流程

《javaLong与long之间的转换流程》Long类提供了一些方法,用于在long和其他数据类型(如String)之间进行转换,本文将详细介绍如何在Java中实现Long和long之间的转换,感... 目录概述流程步骤1:将long转换为Long对象步骤2:将Longhttp://www.cppcns.c

一文全面详解Python变量作用域

《一文全面详解Python变量作用域》变量作用域是Python中非常重要的概念,它决定了在哪里可以访问变量,下面我将用通俗易懂的方式,结合代码示例和图表,带你全面了解Python变量作用域,需要的朋友... 目录一、什么是变量作用域?二、python的四种作用域作用域查找顺序图示三、各作用域详解1. 局部作

一文彻底搞懂Java 中的 SPI 是什么

《一文彻底搞懂Java中的SPI是什么》:本文主要介绍Java中的SPI是什么,本篇文章将通过经典题目、实战解析和面试官视角,帮助你从容应对“SPI”相关问题,赢得技术面试的加分项,需要的朋... 目录一、面试主题概述二、高频面试题汇总三、重点题目详解✅ 面试题1:Java 的 SPI 是什么?如何实现一个

conda安装GPU版pytorch默认却是cpu版本

《conda安装GPU版pytorch默认却是cpu版本》本文主要介绍了遇到Conda安装PyTorchGPU版本却默认安装CPU的问题,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的... 目录一、问题描述二、网上解决方案罗列【此节为反面方案罗列!!!】三、发现的根本原因[独家]3.1 p

spring-gateway filters添加自定义过滤器实现流程分析(可插拔)

《spring-gatewayfilters添加自定义过滤器实现流程分析(可插拔)》:本文主要介绍spring-gatewayfilters添加自定义过滤器实现流程分析(可插拔),本文通过实例图... 目录需求背景需求拆解设计流程及作用域逻辑处理代码逻辑需求背景公司要求,通过公司网络代理访问的请求需要做请

使用JavaConfig配置Spring的流程步骤

《使用JavaConfig配置Spring的流程步骤》JavaConfig是Spring框架提供的一种基于Java的配置方式,它通过使用@Configuration注解标记的类来替代传统的XML配置文... 目录一、什么是 JavaConfig?1. 核心注解2. 与 XML 配置的对比二、JavaConf