Android GPU渲染SurfaceFlinger合成RenderThread的dequeueBuffer/queueBuffer与fence机制(2)

Android GPU渲染SurfaceFlinger合成RenderThread的dequeueBuffer/queueBuffer与fence机制(2)

 

计算fps帧率

adb shell dumpsys SurfaceFlinger --list

查询当前的SurfaceView,然后有好多行,再把要查询的行内容完整的传给

adb shell dumpsys SurfaceFlinger --latency 窗口名

输出一堆数字,第一行数字是当前的 VSYNC 间隔,单位纳秒。如果屏幕是 60Hz ,就是 16.6ms,然后下面一堆数字,总共有 127 行,每一行有 3 个数字,每个数字都是时间戳,单位是纳秒。

Android 显示一帧大致分为几个步骤
1,App 接收到 vsync-app 信号后开始工作。
2,App 主线程被Message唤醒,执行onVsync。
3,App 执行 doFrame ,处理input、animation、traversal、draw等。
4,App UIThread 跟RenderThread sync 数据。
5,App 执行DrawFrame,从SurfaceFlinger(后续简称SF) 的 BufferQueue 中 dequeueBuffer,取出一个bufffer后,执行渲染绘制,接着将绘制好的Buffer 通过queueBuffer 放回到BufferQueue中给 SF消费。
6,App queueBuffer 后, SF 中对应的 app buffer 会增加 +1。
7,Vsync-sf 到来后,SF 从BufferQueue 中 acquireBuffer一个Buffer 进行消费, 对应SF 中的 app buffer 会减 - 1 , SF 消费处理后,通过 releaseBuffer 将buffer 归还到BufferQueue 中。
8,SF 通过 bind 跟 Hardware Composer HAL(HWC) 进行通信,通过一些处理后显示到手机屏幕上。

d48e7b7a06d948899b32b527b87c66e7.webp

dequeue(生产者发起) :
当生产者需要缓冲区时,它会通过调用 dequeueBuffer() 从 BufferQueue 请求一个可用的缓冲区,并指定缓冲区的宽度、高度、像素格式和使用标记。
queue(生产者发起):
生产者填充缓冲区并通过调用 queueBuffer() 将缓冲区返回到队列。
acquire(消费者发起) :
消费者通过 acquireBuffer() 获取该缓冲区并使用该缓冲区的内容。
release(消费者发起) :
当消费者操作完成后,它会通过调用 releaseBuffer() 将该缓冲区返回到队列。

b1693a8c24824eaf8e54b84d84dba931.png
 

App 绘制的图像内容是怎么最终显示到手机屏幕?

App 要显示的内容要绘制在 Buffer 里,这个 Buffer 是从 BufferQueue 通过 dequeueBuffer() 申请。申请到 Buffer 后,App 将内容填充到 Buffer ,通过 queueBuffer() 将 Buffer 还回去交给 SurfaceFlinger 去进行合成和显示。然后,SurfaceFlinger 开始合成时候,调用 acquireBuffer() 从 BufferQueue 里面拿一个 Buffer 合成,合成完以后通过 releaseBuffer() 将 Buffer 还给 BufferQueue。

a388fd254642416d8af4e726c88f3678.webp

RenderThread 的 dequeueBuffer
dequeue 出队,dequeueBuffer 就是从队列中拿出一个 Buffer,这个队列就是 SurfaceFlinger 中的 BufferQueue。app开始渲染前,首先需要通过 Binder 调用从 SurfaceFlinger 的 BufferQueue 中获取一个 Buffer。

a089a2ede3af44a8ba2924305633f5a3.jpeg

RenderThread 的 queueBuffer
queue 入队,queueBuffer 是把 Buffer 放回到 BufferQueue,App 处理完 Buffer 后,会把这个 Buffer 通过 eglSwapBuffersWithDamageKHR -> queueBuffer ,将 Buffer 放回 BufferQueue。

759d4f6f2ef3460a924d9af08c02c9dd.jpeg

上面流程有一个问题,在 App 绘制完通过 queueBuffer() 将 Buffer 还回时候,此时仅仅只是 CPU 侧完成,GPU 实际上有没有真正完成,CPU并不知道。因此如果此时GPU拿这个 Buffer 去合成/显示,就会有问题(Buffer 可能还没有完全绘制完)。由于 CPU 和 GPU 是异步的,因此CPU在代码里执行一系列绘图函数调用后,看上去函数已经返回,实际上,具体什么时候被GPU真正执行完毕 CPU 不知道,除非阻塞等待这些命令完全执行完,但这样会带来严重的性能问题,因为这样使得 CPU 和 GPU 的并行完全丧失,CPU 会在 GPU 完成之前一直处于空等状态。因此,需要一种机制,在不需要对 Buffer 进行读写时候,大家各干各的;当需要对 Buffer 进行读写时候,CPU可以知道此时 Buffer 在 GPU 的状态,必要时候等一下。

fence 是这样的同步机制——“栅栏”,把东西拦住。fence 要拦住什么东西呢?就是 Buffer。Buffer 在整个绘制、合成、显示过程中,一直在 CPU,GPU 和 HWC 之间传递,某一方要使用 Buffer 前,需要检查之前的使用者是否已经移交 Buffer 的“使用权”。而这里的“使用权”,就是 fence。当 fence 释放(即 signal)时候,说明 Buffer 的上一个使用者已经交出了使用权,对于 Buffer 进行操作是安全的。

Android 总共有三类 fence —— acquire fence,release fence 和 present fence。

•acquire fence
    App 将 Buffer 通过 queueBuffer() 还给 BufferQueue 的时候,此时该 Buffer 的 GPU 侧其实未必完成,此时会带上一个 fence,这个 fence 就是 acquire fence。当 SurfaceFlinger/ HWC 要读取 Buffer 以进行合成操作的时候,需要等 acquire fence 释放之后才行。
•release fence
    当 App 通过 dequeueBuffer() 从 BufferQueue 申请 Buffer,要对 Buffer 进行绘制时候,需要保证 HWC 已经不再需要这个 Buffer 了,即需要等 release fence signal 才能对 Buffer 进行写操作。
•present fence
    当前帧成功显示到屏幕的时候,present fence 就会 signal。

d80f0e75cf0e4332b213b47447829bd9.png

每一个buffer都一个Fence状态,代表这块buffer是否还在被上一个使用者使用完,并且在转移时都会携带当前Fence的fd,然后可以调用Fence的wait或者waitForever查询Fence状态,如果还在使用则等待,否则就可以使用。Fence按作用大体分两种:acquireFence和releaseFence。前者用于生产者通知消费者生产已完成,后者用于消费者通知生产者消费已完成。

Fence保证GraphicBuffer在App, GPU和HWC三者间流转时数据读写同步(不同进程 or 不同硬件间同步)。
从 SurfaceFlinger 的角度来看,App 部分主要负责生产 SurfaceFlinger 合成所需要的 Surface。
App 与 SurfaceFlinger 的交互主要集中在三点
1 Vsync 信号的接收和处理
2 RenderThread 的 dequeueBuffer
3 RenderThread 的 queueBuffer

384e0ea30071463c943b7132e063a0e3.jpeg
 

从 dumpsys SurfaceFlinger --latency 获取最新 127 帧的 present fence 的 signal time,当某帧 present fence 被 signal 时候,说明这一帧已经被显示到屏幕上。因此,可以通过判断1秒内有多少个 present fence signal ,反推出一秒内有多少帧被刷新(显示)到屏幕上,从而计算出 fps。

统计一秒内 App 往屏幕刷了多少帧,在 Android 里,每一帧显示到屏幕的标志是:present fence signal ,因此计算 App 的 fps 就可以转换为:一秒内 App 的 Layer 有多少个有效 present fence signal 。

 

 

 

Android adb shell命令捕获systemtrace_android 抓trace-CSDN博客文章浏览阅读1.7k次,点赞2次,收藏5次。Android ADB调试真机设备Android ADB(Andorid Debug Bridge),是Android开发中有用的测试和调试工具。使用Android ADB调试设备,直接在Windows的dos命令窗口输入命名adb即可,如图:为什么执行adb命令后是这样?Android ADB(Andorid Debug Bridge)调试真机设备_adb在线执行器_zhangphil的博客-CSDN博客。-t 时长,20s,20秒的trace文件。-o 保存文件路径。_android 抓tracehttps://blog.csdn.net/zhangphil/article/details/131249820

Android GPU渲染屏幕绘制显示基础概念(1)-CSDN博客文章浏览阅读696次,点赞20次,收藏12次。CPU返回后,会直接将GraphicBuffer提交给SurfaceFlinger,告诉SurfaceFlinger进行合成,但是这个时候GPU可能并未完成之前的图像渲染,这时候就牵扯到一个同步,Android中,用的是Fence机制,SurfaceFlinger合成前会查询Fence,如果GPU渲染没有结束,则等待GPU渲染结束,GPU结束后,会通知SurfaceFlinger进行合成,SF合成后,提交显示,最终完成图像的渲染显示。而对SF来说,只要有合成任务,它就得再去申请VSYNC-sf。https://blog.csdn.net/zhangphil/article/details/138585120Android性能:Double Buffer双缓冲/Triple Buffer三缓冲丢帧Jank与无丢帧No Jank-CSDN博客文章浏览阅读858次,点赞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

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/9791.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

算法训练Day35 | ● 343. 整数拆分 ● 96.不同的二叉搜索树

343. 整数拆分 class Solution { public:int integerBreak(int n) {vector<int> dp(n1, 0);dp[2] 1;for(int i3; i<n1; i){for(int j 1; j<i/2; j){dp[i] max(dp[i], max(j*(i-j), j*dp[i-j]));}}return dp[n];} };参考文章&#xff1a;代码随想录-343. 整数拆分…

找不到msvcp140.dll无法执行代码的原因分析及修复方法

当用户在尝试运行某些应用程序或游戏时&#xff0c;可能会遇到系统弹出错误提示&#xff0c;显示“找不到msvcp140.dll无法执行代码”这一错误信息&#xff0c;它会导致程序无法正常启动。为了解决这个问题&#xff0c;我经过多次尝试和总结&#xff0c;找到了以下五种解决方法…

hadoop启动后没有namenode,datanode等解决方法

之前用的是虚拟机&#xff0c;在虚拟机上安装的hadoop&#xff0c;但是后来&#xff0c;电脑恢复出厂设置了&#xff0c;什么都重新开始。就在本地安装 Linux 子系统。 但是&#xff0c;有时候start-dfs.sh后&#xff0c;jps出现错误。 像这种拒绝连接 解决办法就是如下&…

我的创作纪念日1460天(4年)

机缘 作为一名技术爱好者&#xff0c;我最初成为创作者的初心源于对知识的渴望和对分享的热情。在参与多个实战项目的过程中&#xff0c;我积累了丰富的经验&#xff0c;这些经验不仅仅是代码和解决方案&#xff0c;更多的是对问题本质的理解和解决问题的思维方式。我意识到&a…

题目----力扣--移除链表元素

题目 给你一个链表的头节点 head 和一个整数 val &#xff0c;请你删除链表中所有满足 Node.val val 的节点&#xff0c;并返回 新的头节点 。 示例 1&#xff1a; 输入&#xff1a;head [1,2,6,3,4,5,6], val 6 输出&#xff1a;[1,2,3,4,5]示例 2&#xff1a; 输入&…

如何编译不同目录下的两个文件

1.直接编译 2.打包成动静态库进行链接

【智能优化算法】蜜獾优化算法(Honey Badger Algorithm,HBA)

蜜獾优化算法(Honey Badger Algorithm,HBA)是期刊“MATHEMATICS AND COMPUTERS IN SIMULATION”&#xff08;IF 3.6&#xff09;的2022年智能优化算法 01.引言 蜜獾优化算法(Honey Badger Algorithm,HBA)受蜜獾智能觅食行为的启发&#xff0c;从数学上发展出一种求解优化问题的…

【AMBA Bus ACE 总线 9 -- Non-cache IO device】

请阅读【AMBA Bus ACE 总线与Cache 专栏 】 欢迎学习:【嵌入式开发学习必备专栏】 文章目录 ACE Non-cache IO device非缓存I/O的工作原理在ARM中配置非缓存I/O示例场景Non-cache IO device Cache 访问ACE Non-cache IO device 在ARM架构中,ACE(AXI Coherency Extension,…

Flask 统一拦截器

import osfrom flask import Flask, request, sessionapp Flask(__name__) app.config[SECRET_KEY] os.urandom(24) # 生成24位的随机数种子&#xff0c;用于产生SESSION IDapp.route(/article/<int:article_id>) def test(article_id):"""路由地址参数…

变量的细节

如何打印不同类型的整数常量 相似于我们需要去声明类型 public class Var {public static void main(String[] args) {// 1就是int类型常量System.out.println(1);// 120后面加一个L(l)表示他是一个long型的整数System.out.println(120l);} }如何打印不同类型的浮点数常量 与…

解决电脑睡眠后,主机ping不通VMware虚拟机

文章目录 问题解决方法方法一方法二注意 问题 原因&#xff1a;电脑休眠一段时间&#xff0c;再次打开电脑就ping不通VMware虚拟机。 解决方法 方法一 重启电脑即可&#xff0c;凡是遇到电脑有毛病&#xff0c;重启能解决90%问题。但是重启电脑比较慢&#xff0c;而且重启…

C++用类模板封装容器

要实现输出不同容器的值&#xff0c;且各容器包含的数据类型也不同&#xff0c;可以使用类模板和函数模板来实现。 示例代码如下&#xff1a; #include <iostream> #include <vector> #include <list>template <typename T> class Container { privat…

算法训练Day36 | ● 01背包问题 ● 416. 分割等和子集

01背包问题 #include<iostream> #include<vector> using namespace std;int main(){int M;int N;cin>>M>>N;vector<int> weight(M, 0);vector<int> value(M, 0);for(int i0; i<M; i){cin>>weight[i];}for(int i0; i<M; i){ci…

Web3工具集合 - 00

使用 React 和 Material-UI 构建的 Web3 工具集合 大家好&#xff01; 我很高兴向大家介绍我最近刚启动了一个项目&#xff1a;Web3 工具集合。 这个项目的目的是一个集成各种 Web3 工具的网站&#xff0c;旨在为开发人员和加密货币爱好者提供便捷的工具和资源。 特点&#…

基于SSM的文化遗产的保护与旅游开发系统(有报告)。Javaee项目。ssm项目。

演示视频&#xff1a; 基于SSM的文化遗产的保护与旅游开发系统&#xff08;有报告&#xff09;。Javaee项目。ssm项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&#xff0c;…

基于ESP32和ESP8266的物联网开发过程(二)

在做这个项目前&#xff0c;也做了一些调研。项目的初衷是想要用于智能家居。我比较了小米IoT、阿里云、ESPHOME、巴沙云、点灯科技和ONENET等几个平台。最终选择了Onenet&#xff0c;部分原因是之前用过它的多协议版本&#xff0c;但现在这个版本已经下线了。 小米IoT的公测名…

C++ 类和对象:面向对象编程基础

目录标题 1. 什么是类&#xff1f;2. 什么是对象&#xff1f;3. 如何定义一个类&#xff1f;4. 如何创建对象&#xff1f;5. 类的构造函数6. 类的析构函数7. 数据封装和访问修饰符8. 示例&#xff1a;一个简单的BankAccount类9. 使用g编译10. 再来一个简单的C程序11. 定义书籍类…

Linux修炼之路之初识操作系统+基础指令(1)

目录 引言 一&#xff1a;对操作系统(OS)的简单了解 1.操作系统(OS) 是什么 2.操作系统好坏的衡量标准 3.操作系统存在的重要性 4.理解所有在计算机上的操作 二&#xff1a;Linux与windows操作的特点区别 三&#xff1a;基础指令 1.ls 指令 1.使用 2.常用选项 2.…

AI时代,为什么还要学习编程

在AI时代的浪潮之下&#xff0c;人工智能正逐渐渗透到我们生活的每一个角落&#xff0c;从自动化的客服系统到智能家居控制&#xff0c;再到先进的机器学习和数据分析技术。这一切进步无疑都在提问一个根本性问题&#xff1a;在AI如此先进的今天&#xff0c;为什么我们还需要学…

ICode国际青少年编程竞赛- Python-3级训练场-if else练习

ICode国际青少年编程竞赛- Python-3级训练场-if else练习 1、 for i in range(6):Dev.step(2)Dev.turnLeft()if i ! 1:Dev.step(2)Dev.step(-2)else:Dev.step(-3)Dev.step(3)Dev.turnRight()2、 Dev.step() for i in range(5):Dev.step(2)Dev.turnRight()if i < 1:Dev.s…