Android GPU渲染屏幕绘制显示基础概念(1)

Android GPU渲染屏幕绘制显示基础概念(1)

 

Android中的图像生产者OpenGL,Skia,Vulkan将绘制的数据存放在图像缓冲区中,Android中的图像消费SurfaceFlinger从图像缓冲区将数据取出,进行加工及合成。

SurfaceFlinger是Android最重要的图像消费者,Activity绘制的界面,都会传递到SurfaceFlinger,SurfaceFlinger的作用是接收图像缓冲区数据,然后交给HWComposer或者OpenGL做合成。

SurfaceFlinger是如何接收图像缓冲区的数据呢?先了解Layer(层)概念,一个Layer包含一个Surface,一个Surface对应一块图形缓冲区,而一个界面是由多个Surface组成的,所以它们会一一对应到SurfaceFlinger的Layer中。SurfaceFlinger通过读取Layer的缓冲数据,就相当于读取界面上Surface的图像数据。

f6311d4ad8a746239681165015c398af.webp

图像数据→CPU→显卡驱动→显卡(GPU)→显存(帧缓冲)→显示器

App绘制及SF的合成分别由对应的软件VSYNC驱动:VSYNC-app驱动App绘制;VSYNC-sf驱动SF进行合成。
VSYNC-app与VSYNC-sf按需发射,如果App要更新界面,它申请VSYNC-app,如果没有App申请VSYNC-app,那么VSYNC-app不发射。同样,当App更新界面,它会把对应的Graphic Buffer放到Buffer Queue中。Buffer Queue通知SF进行合成,此时SF申请VSYNC-sf。如果SF不申请VSYNC-sf,VSYNC-sf将不再发射。如果App持续不断的更新,它就得不断申请VSYNC-app;而对SF来说,只要有合成任务,它就得再去申请VSYNC-sf。Choreographer 可以接收系统的 VSYNC 信号,统一管理应用的输入、动画和绘制等任务的执行时机

VSYNC-app与VSYNC-sf相互独立。VSYNC-app触发App绘制,Vsync-sf触发SF合成。App绘制与SF合成都会加大CPU负载,为避免绘制与合成造成性能问题,VSYNC-app可与VSYNC-sf稍微错开。

  • Vsync offset机制: Vsync-app、Vsync-sf并不是同时通知的,Vsync-sf相对晚些,但对于App来说,可认为大约同时发生。

硬件VSYNC同步信号发送周期是固定的,既然都相互独立在自己进程里等待VSync信号到来,然后各司其职做自己的工作,那通过更改偏移量的方式把“APP进程”和“sf进程”接收到VSync信号的时间错开就可以实现:在一个硬件VSync信号周期内完成“渲染”和“合成”两件事,具体方案如下:

VSyncPhaseOffsetNs = 0,硬件VSync发生后,直接转发给app进程,让它开始绘制;
sfVSyncPhaseOffsetNs ≥1,硬件VSync发生后,延迟几毫秒再转发给sf进程,因为app已经渲染完成,sf合成刚刚渲染的图层;
好了,在一个硬件VSync周期(如熟知的16ms)内“渲染”和“合成”的工作都已经完成了,并且由于GPU性能过于快速,距离下次硬件VSync信号发送甚至还有14ms...等下一次硬件VSync信号到来时,显示框架完成,画面切换,和之前的方案比,同样是60HZ的屏幕,用户从按下按钮到看到画面更新,只需要等待1个VSync信号周期,也就是约16ms。

6f47c5129abf4a298fafadd2849a4ed9.png

SF合成的是App的上一帧,而App当前正在绘制的那一帧,要等到下一个VSYNC-sf来临再进行合成。Choreographer用于实现CPU/GPU的绘制是在VSYNC到来时开始。

GPU(Graphics Processin Unit,图形处理器),是一种专门用于图像运算的处理器,在计算机系统中通常被称为 "显卡"的核心部件就是 GPU。

2653b93885ae4a43a90f4d1569bb94d6.webp

 

UI 组件在绘制到屏幕之前,都需要经过 Rasterization(栅格化)操作,而栅格化又是一个非常耗时的操作。Rasterization 栅格化是绘制那些 Button、Shape、Path、String、Bitmap 等显示组件最基础的操作。栅格化将这些 UI 组件拆分到显示器的不同像素上进行显示。这是一个非常耗时的操作,GPU 的引入就是为了加快栅格化。

Android APP而言,GPU硬件加速绘制可以分为:

第一阶段:APP在UI线程构建渲染的命令及数据.
第二阶段:CPU将数据上传(共享或者拷贝)给GPU,PC一般有显存,但ARM嵌入式设备内存一般是GPU-CPU共享内存.
第三阶段:通知GPU渲染,CPU一般不会阻塞等待GPU渲染结束,效率低,CPU通知结束后就返回继续执行其他任务,Fence辅助GPU CPU同步.
第四阶段:swapBuffers,通知SurfaceFlinger图层合成.
第五阶段:SurfaceFlinger合成图层,如果之前提交的GPU渲染任务没结束,则等待GPU渲染完成,再合成(Fence机制),合成依然是依赖GPU,不过这作为下一个任务.

第一阶段主要是CPU工作,这个阶段前期运行在UI线程,后期部分运行在RenderThread(渲染线程),第二个阶段主要运行在渲染线程,CPU将数据同步(共享)给GPU,之后,GPU进行渲染,CPU一般不会阻塞等待GPU渲染完毕,而是通知结束后就返回。CPU返回后,会直接将GraphicBuffer提交给SurfaceFlinger,告诉SurfaceFlinger进行合成,但是这个时候GPU可能并未完成之前的图像渲染,这时候就牵扯到一个同步,Android中,用的是Fence机制,SurfaceFlinger合成前会查询Fence,如果GPU渲染没有结束,则等待GPU渲染结束,GPU结束后,会通知SurfaceFlinger进行合成,SF合成后,提交显示,最终完成图像的渲染显示。

a73dbc500b5b47a9a725f331471a47e3.webp

GraphicBuffer是整个图形系统的核心,渲染操作都将在此对象上进行,包括同步给GPU和HWC,每当应用有显示需求时,应用会向系统申请一块GraphicBuffer内存,这块内存将会共享给GPU用于渲染工作,接着会同步给HWC用于合成和显示,可以把每一个GraphicBuffer对象看做是一个个渲染完成的图层。

在Android里,GraphicBuffer的同步主要借助Fence同步机制,最大特点是能处理GPU、CPU、HWC的同步。GPU处理一般是异步的,CPU命令并不是即刻被GPU执行的,而是被缓存在缓冲区中,而CPU可能不知道执行时机,除非CPU阻塞等待,但毫无疑问会使CPU、GPU并行处理效率降低,至少,渲染线程是被阻塞,所以,CPU提交命令后就返回,不等待GPU处理。SurfaceFlinger图形合成,SurfaceFlinger需要知道什么时候GraphicBuffer被GPU处理填充完毕,这个时候就是Fence机制发挥作用的地方。

一个GraphicBuffer对象的生命周期:

渲染阶段:应用有绘图需求了,系统分配一块内存给应用,应用调用GPU执行绘图,此时使用者是GPU。
合成阶段:GPU渲染完成后将图层传递给sf进程,sf进程决定由谁来合成,hwc或者GPU,如果使用GPU合成,那么此时GraphicBuffer的使用者依旧是GPU,如果使用hwc合成,那么此时GraphicBuffer的使用者是hwc。
显示阶段:所有的GraphicBuffer在此阶段使用者都是hwc,因为hwc控制着显示芯片。
从生命周期可以看出GraphicBuffer对象在流转的过程中,会被GPU、CPU、DPU三个不同的硬件访问。如果同一块内存能够被多个硬件设备访问,就需要一个同步机制,Android图形系统中,Fence机制就是用来不同模块访问时的数据安全,Fence的逻辑实现可参考Java的synchronized互斥锁,可把Fence理解为一把硬件的互斥锁,每个需要访问GraphicBuffer的角色,在使用前都要检查这把锁是否unlock了才能进行操作,否则就要等待(waitForever)。

BufferQueue

它是一个封装了GraphicBuffer的队列,BufferQueue对外提供了GraphicBuffer对象出列/入列的接口。BufferQueue生产者/消费者模式,大多数情况,APP作为GraphicBuffer的生产者,sf进程作为GraphicBuffer的消费者,共同操作一个buffer队列。

生产者:APP进程

1、producer->dequeueBuffer()

​ 从队列取出一个状态为“FREE”的buffer,此时该buffer状态变化为:FREE->DEQUEUED

2、producer->queueBuffer()

​ 将渲染完成的buffer入列,此时该buffer状态变化为:DEQUEUED->QUEUED

消费者:sf进程

1、consumer->acquireBuffer()

​ 从队列中取出一个状态为“QUEUED”的渲染完的buffer准备去合成送显,此时该buffer的状态变化为:QUEUED->ACQUIRED

2、consumer->releaseBuffer()

​ buffer内容已经显示过了,可以重新入列给APP使用了,此时该buffer的状态变化为:ACQUIRED->FREE

每个Buffer的一生,就是在不断地循环FREE->DEQUEUED->QUEUED->ACQUIRED->FREE这个过程,这中间有任何一个环节出现延迟,反应到屏幕上就是应用出现了卡顿。

无论App使用哪种API进行图形开发绘制,在绘制流程结束后,APP作为图层的生产者总是会调用BufferQueue的queueBuffer()方法将GraphicBuffer入列,一旦有新的图层加入队列,就意味着作为图层消费者的SF进程可以开始工作了。

当APP端的Surface发生变化以后,Layer的onFrameAvailable()方法会被调用,经过层层转发,最终由MessageQueue#requestNextVSync()执行VSync信号的请求。
APP进程中的每一个Surface对象,对应SF进程当中的一个Layer对象,它俩共享一个BufferQueue,
Surface作为图层的生产者,封装了出列入列的操作,
Layer作为图层的消费者,封装了获取渲染图层和释放图层的操作。

 

一个APP完整的显示流程大致分为三个阶段
app-请求
APP页面元素一旦发生变化,调用invalidate()/requestLayout()方法请求下一次VSync信号,此时sf什么都不做。
app-VSync & sf-请求
app-VSync信号到来后,APP进程执行绘图三部曲,绘图流程结束后,sf收到onFrameAvailable(),sf进程请求VSync。
sf-VSync
sf-VSync信号到来,sf进程执行合成,接着将结果提交给hwc,等待下次硬件VSync信号发生,切换Framebuffer展示给用户。

 

 

 

 

Android性能:Double Buffer双缓冲/Triple Buffer三缓冲丢帧Jank与无丢帧No Jank-CSDN博客文章浏览阅读850次,点赞6次,收藏13次。Android ADB调试真机设备Android ADB(Andorid Debug Bridge),是Android开发中有用的测试和调试工具。使用Android ADB调试设备,直接在Windows的dos命令窗口输入命名adb即可,如图:为什么执行adb命令后是这样?_android 抓trace。三Buffer轮转情况下,基本不会有这种情况的发生,渲染线程一般在 dequeueBuffer 时,都可以顺利拿到可用的 Buffer (如果 dequeueBuffer 本身耗时那就也会拉长时间)。https://blog.csdn.net/zhangphil/article/details/138213964

 

### 回答1: 要获取 Android 设备的 GPU 使用率,你可以使用 Android Debug Bridge (ADB) 命令行工具。首先,你需要在电脑上安装 ADB。然后,在命令行窗口中输入以下命令: ``` adb shell dumpsys gfxinfo ``` 这将会显示有关设备 GPU 的信息,包括 GPU 进程使用情况、渲染帧数以及帧速率。要查看更多信息,你可以使用以下命令: ``` adb shell dumpsys gfxinfo <package_name> ``` 其中 `<package_name>` 是你想要查看的应用的包名。 如果你想在应用内查看 GPU 使用率,你可以使用 Android Profiler 工具,它可以在 Android Studio 中找到。在 Android Profiler 中,你可以选择“GPU”选项卡来查看 GPU 使用情况。 此外,你也可以使用第三方应用来查看 GPU 使用情况,比如 GPU-Z 和 AIDA64。 ### 回答2: 要获取Android设备的GPU使用率,可以使用adb命令或者通过编写应用程序来实现。 1. 使用adb命令获取GPU使用率: - 首先,连接Android设备到计算机上,并确保设备已开发者模式和USB调试已打开。 - 打开命令提示符或终端,并执行以下adb命令:adb shell dumpsys gfxinfo | grep "Total frames rendered" - 这将返回每秒渲染的帧数和总帧数。 - 解析返回的结果,可以计算出GPU使用率。例如,通过比较两个时间点的帧数差值,可以计算出GPU使用率。 2. 通过编写应用程序获取GPU使用率: - 创建一个Android应用程序,并确保在AndroidManifest.xml文件中添加获取获取GPU状态的权限:`<uses-permission android:name="android.permission.DUMP" />` - 在应用程序中,使用`SurfaceHolder.Callback`接口来获取GPU的使用率。可以继承自`SurfaceView`类并实现相应的方法。 - 在`surfaceCreated`方法中,获取`Surface`对象,并使用`Canvas.getMaximumBitmapWidth()`和`Canvas.getMaximumBitmapHeight()`方法获取GPU用于渲染的最大位图宽度和高度。 - 在`surfaceChanged`方法中,获取`Canvas`对象并使用`Canvas.isHardwareAccelerated()`判断当前绘制是否使用了GPU加速。 - 通过以上方法可以获取GPU使用率的相关信息,并进行相应的计算和展示。 总之,要获取Android设备的GPU使用率,可以通过使用adb命令或者编写应用程序来实现。使用adb命令可以直接获取到GPU的帧数信息,解析该信息可以计算出GPU使用率。而编写应用程序可以通过使用相应的接口和方法来获取GPU的状态信息,并进行相关计算和展示。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhangphil

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值