当前位置: 首页 > news >正文

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

 

相关文章:

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

Android GPU渲染屏幕绘制显示基础概念(1) Android中的图像生产者OpenGL,Skia,Vulkan将绘制的数据存放在图像缓冲区中,Android中的图像消费SurfaceFlinger从图像缓冲区将数据取出,进行加工及合成。 Surface…...

Mac电脑设置hosts的方法

hosts文件是什么 hosts文件是一个系统文件,通过绑定域名与ip的关系,当本机访问该域名时 从这个文件中如果找到了对应域名,则转发到对应ip;如果没有找到对应域名,则走默认的DNS公网解析。 好处: 加速访问…...

数据分析——大数据伦理风险分析

大数据伦理风险分析 前言一、大数据伦理二、大数据技术伦理风险算法安全性、可信赖性及稳定性风险及其应对算法风险的表现算法风险的危害算法风险的应对 算法的可解释性风险及其应对算法可解释性风险的内容算法可解释性风险的损害算法可解释性风险的应对 算法的决策不可预见性风…...

漫谈AI时代的手机

以chatGPT 为代表的大语言的横空出世使人们感受到AI 时代的到来,大语言模型技术的最大特点是机器开始”懂人话“,”说人话“了。如同任何一个革命性工具的出现一样,它必将改变人类生活和工作。 在这里。我谈谈AI时代的手机。 语音通信的历史…...

fatal error: ros/ros.h: 没有那个文件或目录

解决方法: 在出错的文件的包下的CMakeLists.txt文件里,加上 find_package(catkin REQUIRED COMPONENTSroscpp )include_directories(include ${catkin_INCLUDE_DIRS} )【ROS-解决问题】 fatal error: ros/ros.h: 没有那个文件或目录-CSDN博客...

苍穹外卖Day06笔记(复习了jwt的加密解密和传递)

疯玩了一个月,效率好低,今天开始捡起来苍穹外卖~ 1. 为什么不需要单独引入HttpClient的dependency? 因为我们在sky-common的pom.xml中已经引入了aliyun-sdk-oss的依赖,而这个依赖低层就引入了httpclinet的依赖,根据依…...

【ARM 嵌入式 C 字符串系列 23.9 -- strcmp 与 strncmp 在使用上的区别以及注意事项】

请阅读【嵌入式开发学习必备专栏】 文章目录 strcmp 与 strncmp 使用介绍strcmpstrncmp使用建议 strcmp 与 strncmp 使用介绍 strcmp 和 strncmp 都是 C 语言标准库中用于比较两个字符串的函数&#xff0c;它们定义在 <string.h> 头文件中。这两个函数在功能上相似&…...

行列视(RCV):企业数据处理的革新工具

在当前数据驱动的商业生态系统中&#xff0c;行列视&#xff08;RCV&#xff09;系统以其创新的企业数据处理功能&#xff0c;不断地为各行各业的企业带来变革。行列视系统能够处理大规模数据集&#xff0c;支持多达400种Excel函数&#xff0c;使得数据处理不仅限于基本的表格操…...

Oracle Patch清理

场景&#xff1a; 在对Oracle安装补丁后&#xff0c;会发现OS上被占用了大量的空间&#xff0c;本文档清理Opatch过程中的一些文件&#xff0c;释放空间 参考文档&#xff1a; Can You Delete $ORACLE_HOME/.patch_storage Directory ? (Doc ID 403218.1) How To Avoid Disk …...

Redis-三主三从高可用集群搭建

正式搭建之前&#xff0c;注意事项&#xff08;坑&#xff09;提前放到最开始&#xff0c;也可以出问题回来看&#xff0c; &#xff08;1&#xff09;第二步中最好将配置文件中的logfile自定义一个目录&#xff0c;以便于在第五步中启动出错的时候迅速定位错误。 &#xff0…...

ImageMagick

Linux 安装 sudo apt install php8.2-imagick Windows 安装 下载 ImageMagick ImageMagick – Download 安装并将 D:\Program Files\ImageMagick-7.1.1-Q16-HDRI 加入到系统环境变量 path 中&#xff0c; 或者将 CORE_RL_*.dll 复制到 c:\windows\system32 下 下载 php 扩展…...

攻防世界-web-command_execution

题目&#xff1a; 原理&#xff1a; | 的作用为将前一个命令的结果传递给后一个命令作为输入 &&的作用是前一条命令执行成功时&#xff0c;才执行后一条命令 方法一&#xff1a; 第一步&#xff1a; 1.打开浏览器&#xff0c;在文本框内输入127.0.0.1 | find / -name…...

go语言自定义排序接口Interface实现示例 sort.Sort(data Interface) 快速排序 pdqsort

go语言sort.Sort(data Interface) 排序接口自定义排序实现&#xff0c;golang里面的sort包中的Sort方法底层使用的是 pdqsort的一个快速排序算法&#xff0c; 我们可以将要排序的对象实现Interface接口后直接丢个这个函数即可自动按照我们指定的方式进行数据快速排序。 sort函…...

RIP动态路由协议详解

目录 一&#xff1a;RIP协议的基本信息 二&#xff1a;RIP协议中的更新方式 三&#xff1a;RIP协议中的计时器 定时更新器&#xff08;UPDATE timer&#xff09; 无效定时器&#xff08;invalid Timer&#xff09; 垃圾收集定时器&#xff08;garbage collection timer&a…...

ROS2 安装与测试

文章目录 ROS2 安装与测试ROS2 安装1. 设置编码2. 添加源3. 安装 ROS24. 设置环境变量 ROS2 示例测试实例一&#xff1a;命令行实例实例二&#xff1a;小海龟仿真实例 参考链接 ROS2 安装与测试 ROS2 安装 基于 Ubuntu 22.04 LTS 操作系统。 1. 设置编码 sudo apt update &…...

MySQL数据分组技术深度解析及实践

在处理大量数据库记录时,数据分组是一种强大的工具,它允许我们按照特定列的值将数据划分为逻辑上的集合,进而对每个集合执行聚合操作,如计数、求和或平均值等。MySQL中的​​GROUP BY​​​子句正是实现这一功能的关键所在。本文将详细介绍​​GROUP BY​​的使用方法,结合…...

【敦煌网注册/登录安全分析报告】

敦煌网注册/登录安全分析报告 前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大…...

Python读取ASC文件并转换成Excel文件(坐标)

import pandas as pd# 读取asc文件&#xff0c;指定空格为分隔符 df pd.read_csv(out_view2.asc, sep , headerNone)# 去掉空列 df df.dropna(howall, axis1)# 将数据保存到Excel文件 df.to_excel(out_view2.xlsx, indexFalse, headerFalse)效果图...

Rust 的 Warp 库编写的 restful api 参数传递与解析方法

Warp是一个用 Rust 编写的流行的异步 web 框架。在使用 warp 构建 RESTful API 时&#xff0c;可以通过多种方式传递参数到你的处理函数中。 以下是一些常见的方法&#xff0c;说明如何在 warp 中传递参数&#xff1a; 路径参数&#xff1a; 你可以使用 warp::path 和 warp::…...

关不掉的弹窗

这里分两种方式 第一种 #include<Windows.h> int main(){system("mode 15,14");while(1){MessageBox(NULL,TEXT("关不掉吧!"),TEXT("中病毒啦~~你这个SB!"),MB_OK);}} 实际上不是关不掉&#xff0c;而是关不完 解决方法&#xff1a;找…...

【JVM】类加载机制及双亲委派模型

目录 一、类加载过程 1. 加载 2. 连接 a. 验证 b. 准备 c. 解析 3. 初始化 二、双亲委派模型 类加载器 双亲委派模型的工作过程 双亲委派模型的优点 一、类加载过程 JVM的类加载机制是JVM在运行时&#xff0c;将 .class 文件加载到内存中并转换为Java类的过程。它…...

WordPress插件:链接自动识别转为超链接

WordPress插件&#xff1a;链接自动识别转为超链接 <?phpfunction open_links_in_new_tab() {add_filter(the_content, make_clickable);function autoblank($text) {$return str_replace(<a, <a target"_blank", $text);return $return;}add_filter(th…...

Java----数组的定义和使用

1.数组的定义 在Java中&#xff0c;数组是一种相同数据类型的集合。数组在内存中是一段连续的空间。 2.数组的创建和初始化 2.1数组的创建 在Java中&#xff0c;数组创建的形式与C语言又所不同。 Java中数组创建的形式 T[] 数组名 new T[N]; 1.T表示数组存放的数据类型…...

【C++】-QT多线程-006

1【QT】多线程 #ifndef MYWIDGET_H #define MYWIDGET_H#include <QWidget>namespace Ui { class MyWidget; }class MyWidget : public QWidget {Q_OBJECTpublic:explicit MyWidget(QWidget *parent 0);~MyWidget();/* 5 自定义信号*/ /*所有的信号函数只声明不定义&…...

vscode go语言开发中在任意包运行和调试代码 Example使用方法

一般情况下我们在进行go语言开发的时候我们都需要创建一个main方法和main包才能运行go代码&#xff0c; 针对这个问题&#xff0c;go语言给我们内置了功能强大的testing测试框架&#xff0c; 其中一个很有意思的Example测试就非常的方便使用。 他不管你在什么包&#xff0c;也…...

数据库查询--条件查询

目录 1.关系运算条件的查询 2.逻辑运算符条件的查询 3.带关键字IN的查询 4.带BETWEEN AND关键字的查询 5.空值查询 6.带LIKE关键字的模糊查询 1.关系运算条件的查询 在SELECT语句中&#xff0c;最常见的是使用WHERE字句指定关系运算条件对数据进行过滤。 语法格式&#x…...

用 Python 和 AkShare 进行个股数据清洗:源码剖析和建议优化

这是《个股清洗源码》一个获取股票买卖盘信息并将其打印到控制台并保存到文件的脚本。 下面我们来对源码进行剖析 先复习一下源码 import os import akshare as ak from akshare import stock_bid_ask_em from datetime import datetime import pandas as pd from io import …...

颍川诞生了两个帝王的仲父

伯、仲、叔、季是古代兄弟的长幼排行顺序&#xff0c;《释名释亲属》载&#xff1a;“父之弟曰仲父……仲父之弟曰叔父”。也就是古代称父亲的兄弟为仲父&#xff0c;多用于帝王对宰相重臣的尊称。 历史上最有名的、有正史记载的帝王“仲父”有两位&#xff0c;而且都出自颍川…...

SpringAMQP发布、订阅——Fanout Exchange交换机代码模拟

发布订阅模型: MQ提供了很多交换机模型 其中常用的有下边三个: Fanout:广播 Direct:路由 Topic:话题 转换器只负责消息路由&#xff0c;不是存储&#xff0c;路由失败则消息丢失 Fanout Exchange:会将接收到的消息路由导每一个跟其绑定的queue. 利用SpringAMQP演示Fanout…...

js原生三种弹框

第一种&#xff1a; alert("提示内容")&#xff1a;提示弹框&#xff1b; alert("提示"); 第二种&#xff1a; prompt("内容","输入框默认值")&#xff1a;输入弹框&#xff0c;第一个值输入框提示内容&#xff0c;第二个值输入框默…...