Android软件渲染流程

news/2024/7/25 3:38:46/文章来源:https://blog.csdn.net/LeeDuoZuiShuai/article/details/139119426

Android软件渲染流程

  • 一.渲染流程
    • 1.VSync信号的监听
    • 2.VSync信号触发绘制
  • 二.渲染原理
    • 1.画布的获取
      • 1.1 渲染缓存的初始化
      • 1.2 graphics::Canvas的创建
      • 1.3 graphics::Canvas与渲染缓存的绑定
        • 1.3.1 SkBitmap的初始化
        • 1.3.2 SkiaCanvas与SkBitmap的绑定
        • 1.3.3 SkCanvas的创建
    • 2.矩形的绘制
    • 3.绘制的提交
      • 3.1 图像缓存消费回调
      • 3.2 图像缓存事务的处理
        • 3.2.1 图像缓存与图像缓存事务的绑定
        • 3.2.2 layer_state_t的获取
        • 3.2.3 图像缓存事务的提交
  • 三.总结
    • 1.图像缓存的四种状态
      • 1.1 状态转化的过程
      • 1.2 ANativeWindow_Buffer
    • 2.Canvas的初始化
      • 2.1 概述
      • 2.2 过程
    • 3.软件绘制
    • 4.绘制提交

一.渲染流程

1.VSync信号的监听

    在Android中,App的渲染流程是从ViewRootImpl开始的。在回调Activity的onResume方法后,会调用ViewRootImpl的requestLayout方法触发页面中View的测量与绘制。

    在ViewRootImpl的requestLayout方法中,首先会调用checkThread方法检查当前线程是否为UI线程,如果不是,则抛出异常。接下来会调用scheduleTraversals方法。
0
    在ViewRootImpl的scheduleTraversals方法中,主要做了两件事:

1)向主线程的消息队列发送一个同步信息屏障。

2)提交callbackType类型为CALLBACK_TRAVERSAL的TraversalRunnable。
1

2.VSync信号触发绘制

    当VSync信号到来时,会执行TraversalRunnable的run方法,该方法内部会调用ViewRootImpl的doTraversal方法。
2
    在ViewRootImpl的doTraversal方法中,主要做了两件事:

1)移除主线程消息队列的同步信息屏障。

2)调用performTraversals方法。
3
    在ViewRootImpl的performDraw方法中,会调用draw方法。在ViewRootImpl的draw方法中,如果没有开启硬件渲染,会调用drawSoftware方法。
4
    在ViewRootImpl的drawSoftware方法中,主要做了三件事:

1)调用Surface的lockCanvas方法获取Canvas。

2)调用DecorView的draw方法开始绘制,draw方法内部会递归调用所有View的draw方法。

3)调用Surface的unlockCanvasAndPost方法完成绘制。
5

二.渲染原理

1.画布的获取

    在Surface创建时,会触发Canvas的创建。在Surface的lockCanvas方法中,会对Canvas进行初始化。在Surface的lockCanvas方法中,会调用最终会调用nativeLockCanvas方法。
6
    Surface的nativeLockCanvas方法对应的实现为android_view_Surface的nativeLockCanvas函数。在nativeLockCanvas函数中,主要做了五件事:

1)获取Native层Surface。

2)声明用于渲染的Buffer。

3)使用Surface对用于渲染的Buffer进行赋值与初始化。

4)创建graphics::Canvas,graphics::Canvas是对Native层Canvas的封装。

5)绑定Canvas和Buffer。
7

1.1 渲染缓存的初始化

    在Surface的lock方法中,主要做了四件事:

1)从IGraphicBufferProducer中获取一块内存ANativeWindowBuffer。

2)将ANativeWindowBuffer封装成GraphicBuffer。

3)锁定GraphicBuffer,获取存放绘制数据的虚拟地址的指针,用于后续Canvas的绘制。

4)将GraphicBuffer中的变量赋值到ANativeWindow_Buffer中。ANativeWindow_Buffer是ANativeWindowBuffer对外的屏蔽封装,用于指导Canvas在多大的长宽范围内以什么样的格式进行绘制,并提供提交绘制数据的指针。

8

1.2 graphics::Canvas的创建

    在graphics::Canvas的构造方法中,会调用android_canvas的ACanvas_getNativeHandleFromJava函数,将Native层对应的Canvas转换为ACanvas,并保存到graphics::Canvas的mCanvas字段中。

    ACanvas是对Native层Canvas的代理,ACanvas是一个没有任何方法的结构体,当需要调用Native层Canvas时,会再将ACanvas强转为Canvas。
9
    在ACanvas_getNativeHandleFromJava函数中,主要做了两件事:

1)获取Native层Canvas,实际是SkiaCanvas,SkiaCanvas是对SkCanvas的封装。

2)将Canvas转换为ACanvas。
10

1.3 graphics::Canvas与渲染缓存的绑定

    在graphics::Canvas的setBuffer方法中,会调用android_canvas的ACanvas_setBuffer函数。
11
    在ACanvas_setBuffer函数中,主要做了四件事:
1)声明SkBitmap。SKBitmap是对ANativeWindow_Buffer的封装,SKBitmap会根据ANativeWindow_Buffer的指导,按照指定的大小和格式,向指定的绘制地址写入绘制数据。

2)对SkBitmap进行初始化。

3)通过ACanvas获取Canvas,实际是SkiaCanvas。

4)将SkiaCanvas和SkBitmap绑定。
12

1.3.1 SkBitmap的初始化

    在convert函数中,主要做了三件事:

1)将ANativeWindow_Buffer封装成SkImageInfo。

2)绑定SkImageInfo到SkBitmap中。

3)设置用于存放绘制数据的虚拟地址的指针。
13

1.3.2 SkiaCanvas与SkBitmap的绑定

    在SkiaCanvas的setBitmap方法中,主要做了三件事:

1)将SkBitmap封装成SkCanvas。

2)保存SkCanvas对应的指针。

3)通过SkCanvas指针获取SkCanvas并保存。

14

1.3.3 SkCanvas的创建

    在SkCanvas的构造方法中,主要做了两件事:

1)将SkBitmap封装为SkBitmapDevice。

2)保存SkBitmapDevice。
15

2.矩形的绘制

    在软件绘制过程中,会调用Canvas的drawRect方法。在Canvas的drawRect方法中,会调用nDrawRect方法。
16
    Canvas的nDrawRect方法对应的Native实现为android_graphics_Canvas的drawRect函数。在drawRect函数中,主要做了两件事:

1)获取Native层Canvas。

2)通过Canvas绘制矩形。
17
    在SkiaCanvas的drawRect方法中,会调用SkCanvas的drawRect方法。最终会绘制到SKBitmap上。SKBitmap封装了图像绘制缓冲,实际会绘制到图像缓冲上。
18

3.绘制的提交

    在软件渲染中,当调用Surface的unlockCanvasAndPost方法时,会将渲染好的图像缓冲提交到SurafceFlinger中。

    在Surface的unlockCanvasAndPost方法中,会调用unlockSwCanvasAndPost方法。在Surface的unlockSwCanvasAndPost方法中,又回调用nativeUnlockCanvasAndPost方法。
19
    Surface的nativeUnlockCanvasAndPost方法对应的Native实现为android_view_Surface的nativeUnlockCanvasAndPost函数。在nativeUnlockCanvasAndPost函数中,主要做了四件事:

1)获取Native层Surface。

2)创建graphics::Canvas,graphics::Canvas是对Native层Canvas的封装。

3)解除Canvas与Surface的关联。

4)提交图像缓冲。
20
    在Surface的unlockAndPost方法中,主要做了三件事:

1)解除GraphicBuffer锁定。

2)将渲染完的图像缓冲添加到IGraphicBufferProducer中。

3)清除对GraphicBuffer的引用。
21
    在Surface的queueBuffer方法中,主要做了两件事:

1)获取缓存位置。

2)提交缓存。
22
    通过Surface的初始化流程,可以知道这里Surface的实际类型为BBQSurface,因此IGraphicBufferProducer对应的类型为BBQBufferQueueProducer。

    在BBQBufferQueueProducer的queueBuffer方法中,主要做了四件事:

1)创建FenceTime。

2)创建渲染帧BufferItem。

3)对BufferItem进行初始化设置。

4)通知消费者消费渲染帧。
23

3.1 图像缓存消费回调

    通过Surface的初始化流程,可以知道这里的mCore的类型为BufferQueueCore。BufferQueueCore的IConsumerListener的类型为BufferQueue::ProxyConsumerListener。

    调用BufferQueue::ProxyConsumerListener的onFrameAvailable方法,最终会调用BLASTBufferQueue的onFrameAvailable方法。
24

3.2 图像缓存事务的处理

    在BLASTBufferQueue的onFrameAvailable方法中,如果需要提交本次绘制,会调用acquireNextBufferLocked方法。

    在BLASTBufferQueue中,所有向SurfacaeFlinger的请求都被抽象成了Transaction。通过Transaction,BLASTBufferQueue可以将多次绘制的缓冲进行合并提交。
25
    在BLASTBufferQueue的acquireNextBufferLocked方法中,主要做了四件事:

1)获取渲染帧BufferItem。

2)通过BufferItem获取绘制缓冲GraphicBuffer。

3)设置Transaction的属性。

4)提交本次Transaction到SurfaceFlinger中等待合成。
26

3.2.1 图像缓存与图像缓存事务的绑定

    在Transaction的setBuffer方法中,主要做了三件事:

1)获取layer_state_t,layer_state_t用于保存记录当前Layer的信息。

2)创建BufferData,保存GraphicBuffer。

3)保存BufferData到layer_state_t中。
27

3.2.2 layer_state_t的获取

    在Transaction的getLayerState方法中,主要做了两件事:

1)获取LayerHandle。

2)通过LayerHandle获取ComposerState,通过ComposerState获取layer_state_t。如果layer_state_t不存在(首次获取),则创建一个新的ComposerState并保存,ComposerState内部持有layer_state_t。
28

3.2.3 图像缓存事务的提交

    在Transaction的apply方法中,主要做了三件事:

1)创建ComposeState列表,收集ComposerState,ComposerState中保存了layer_state_t。

2)获取远端服务ISurfaceComposer。

3)提交到远端服务。
29
    ComposerService是一个单例类,持有ISurfaceComposer。ISurfaceComposer是一个Binder类,它的Bp端实现为BpSurfaceComposer,Bn端的最终实现为SurfaceFlinger。
30
    最终会调用SurfaceFlinger的setTransactionState方法,提交本次的图像缓存。

三.总结

1.图像缓存的四种状态

  • 生产消费模型中的图像缓存:ANativeWindowBuffer
  • Surface中管理的图像缓存:GraphicBuffer
  • Surface暴露给Canvas的图像缓存:ANativeWindow_Buffer
  • 提交给消费者处理的图像缓存:BufferItem

1.1 状态转化的过程

    ANativeWindowBuffer是生产消费模型中最原始的图像缓存。在从生产消费模型获取后,为了方便管理,Surface会将ANativeWindowBuffer封装成GraphicBuffer。当需要暴露给Canvas时,Surface会将GraphicBuffer中重要的参数封装成ANativeWindow_Buffer。在绘制完成后,GraphicBuffer会被生产消费模型封装成BufferItem交给消费者处理。

1.2 ANativeWindow_Buffer

    ANativeWindow_Buffer是Surface为了防止外部直接操作图像缓存而对外提供的协议,这个协议规定了要在多长多宽的区域内,按照什么样的格式,向哪一个地址去写入绘制数据。

2.Canvas的初始化

2.1 概述

    Canvas是对Surface提供的图像缓存的抽象封装。

    Surface对Canvas初始化的过程,就是Surface从生产消费模型中为Canvas分配一块图像缓存的过程。一个Surface同一时间只能提供一个图像缓存。

2.2 过程

    在软件绘制中,Java层Canvas对应的Native层结构为SkiaCanvas。

    在从Surface获取到ANativeWindow_Buffer后,ANativeWindow_Buffer会被封装成SkBitmap。SkBitmap会与
SkiaCanvas进行关联。SkiaCanvas会将SkBitmap封装成SkCanvas来进行管理。

3.软件绘制

    软件绘制就是通过Canvas向SkBitmap中写入数据,SkBitmap按照ANativeWindow_Buffer的要求将绘制数据发送到提供存放绘制数据的地址。

4.绘制提交

    绘制提交的本质就是将Canvas与SkBitmap断开,然后将绘制数据提交到生产消费模型中处理,最后有消费者提交到SurfaceFlinger中进行合成。

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

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

相关文章

Window Linux 权限提升

#基础点: 0、为什么我们要学习权限提升转移技术: 简单来说就是达到目的过程中需要用到它 心里要想着我是谁 我在哪 我要去哪里 1、具体有哪些权限需要我们了解掌握的: 后台权限,数据库权限,Web权限,用户权…

红外成像人员检测数据集VOC+YOLO格式5838张1类别

数据集格式:Pascal VOC格式YOLO格式(不包含分割路径的txt文件,仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数):5838 标注数量(xml文件个数):5838 标注数量(txt文件个数):5838 标注…

FTP协议——Pure-Ftpd安装(Linux)

1、简介 Pure-FTPd是一个高效、免费且开源的FTP服务器软件,广泛应用于各种Unix/Linux系统。它以其易用性、高安全性和功能丰富而闻名,适用于个人和企业的文件传输需求。 2、步骤 环境:Ubuntu 22.04.4 下载地址:Index of /pub/p…

文盘Rust -- 生命周期问题引发的 static hashmap 锁

100编程书屋_孔夫子旧书网 2021年上半年,撸了个rust cli开发的框架,基本上把交互模式,子命令提示这些cli该有的常用功能做进去了。项目地址:https://github.com/jiashiwen/interactcli-rs。 春节以前看到axum已经0.4.x了,于是想看看能不能用rust做个服务端的框架。 春节…

【Java】Java中类的初始化顺序(静态方法,静态块,非静态块,最后有流程图)

📝个人主页:哈__ 期待您的关注 目录 一、无继承关系类的初始化 1、静态变量k被初始化 2、静态变量t1初始化 3、静态变量 t2初始化 4、静态变量i初始化 5、静态变量n初始化 6、静态块初始化 7、非静态块初始化 8、非静态属性初始化 9、执行构造…

triton之paged attention

一 原理 图解大模型计算加速系列之:vLLM核心技术PagedAttention原理 - 知乎 (zhihu.com)https://zhuanlan.zhihu.com/p/691038809 二 源码分析 1 测试参数设置 test_paged_attention(num_seqs32,num_heads(64, 64),head_size64,block_size16,dtypetorch.float16,…

八股文(C#篇)

C#中的数值类型 堆和栈 值类型的数据被保存在栈(stack)上,而引用类型的数据被保存在堆(heap)上,当值类型作为参数传递给函数时,会将其复制到新的内存空间中,因此在函数中对该值类型的修改不会影…

Elasticsearch集群许可证过期问题解决方法汇总

最近在使用elasticsearch的过程中,使用elastic-head进行可视化展示集群的状态和信息,从2024年5月18日突然elastic-head无法现在集群的状态界面啦,elasticsearch集群状态是正常,命令如下: curl -X GET "localhost:9200/_cluster/health?pretty" 在google页面上通过…

给pdf加水印,python实现

from PyPDF2 import PdfReader, PdfWriterdef add_watermark(pdf_file_in, pdf_file_mark, pdf_file_out):"""把水印添加到pdf中"""pdf_output PdfWriter()input_stream open(pdf_file_in, rb)pdf_input PdfReader(input_stream, strictFalse…

for循环绑定id,更新html页面的文字内容

需求&#xff1a;将方法中内容对齐 实现方式 给for循环中每个方法添加一个动态的id在DOM结果渲染完后&#xff0c;更新页面数据&#xff0c;否则会报错&#xff0c;找不到对应节点或对应节点为空 <view v-for"(item, index) in itemList" :key"index"…

DSP开发入门

视频&#xff1a; 创龙TI 最新DSP CPU核心架构 C66x 以及 KeyStone I 架构 DSP TMS320C6655/57以及TMS320C6678视频教程全集_哔哩哔哩_bilibili 2024年硬汉科技手把手教您学DSP28335视频教程持续更新中_哔哩哔哩_bilibili DSP芯片介绍 DSP选型 TI的DSP 分为三大系列&#…

如何连接SharePoint?

知行之桥EDI系统支持连接SharePoint&#xff0c;通过在成熟的SharePoint端口&#xff08;知行之桥EDI系统中的端口是指功能模块&#xff09;的可视化界面中进行简单配置&#xff0c;即可创建连接。 创建一个SharePoint 端口 本操作指南基于知行之桥EDI系统2024版&#xff0c;…

java配置文件解析yml/xml/properties文件

XML 以mybatis.xml:获取所有Environment中的数据库并连接session为例 import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException;import javax.xml.parsers.DocumentBuilder; impo…

23ai中的True Cache到底能做啥?

最近&#xff0c;Oracle的产品管理总监在Oracle数据库内幕中介绍了True Cache。 原文链接如下&#xff1a; https://blogs.oracle.com/database/post/introducing-oracle-true-cache 由于这篇文章比较火爆&#xff0c;我们国内已经有很多的数据库爱好者完整的翻译这篇文章&am…

水电自动抄表系统是什么?

1.简述&#xff1a;水电自动抄表系统 水电自动抄表系统是一种现代化计量检定解决方法&#xff0c;为提升公用事业服务项目的效率和精确性。传统式手动抄水表方法已经被这类高效率、精准的自动化系统所替代&#xff0c;它能够实时、远程控制地收集解决水电使用数据。 2.系统原…

【二叉树】非递归实现前中后序遍历

目录 前言 算法思想 非递归实现前序遍历 过程分析 代码 非递归实现中序遍历 过程分析 代码 非递归实现后序遍历 过程分析 代码 前言 1&#xff09;前序&#xff1a;根 左子树 右子树 2&#xff09;中序&#xff1a;左子树 根 右子树 3&#xff09;后序&#xff1…

3W 1.5KVDC、3KVDC 隔离,宽电压输入 DC/DC 电源模块——TP03DA 系列

TP03DA系列电源模块额定输出功率为3W&#xff0c;外形尺寸为31.75*20.32*10.65&#xff0c;应用于2:1及4:1宽电压输入范围 4.5-9V、9V-18V、18V-36V、36V-72V、9V-36V和18-72VDC的输入电压环境&#xff0c;输出电压精度可达1%&#xff0c;具有输出短路保护等功能&#xff0c;可…

室内也可以用北斗定位?还能用RTK?

室内卫星顾名思义&#xff0c;就是在室内有遮挡环境中的卫星定位技术&#xff0c;众所周知&#xff0c;目前全球几大GNSS定位系统已经很完善&#xff0c;但是GNSS有个致命的弱点&#xff0c;就是地面如果有遮挡就没有信号&#xff0c;在这样的条件下&#xff0c;在室内定位场景…

结合Django和Vue.js构建现代Web应用

文章目录 1. 创建Django项目2. 配置Django后端3. 创建Vue.js前端4. 连接Django和Vue.js5. 构建和部署 在现代Web开发中&#xff0c;结合后端框架和前端框架是非常常见的&#xff0c;其中Django作为一种流行的Python后端框架&#xff0c;而Vue.js则是一种灵活强大的前端框架。本…

Pyinstaller打包exe文件解决指南

打包命令 打包 Python 文件 输入如下格式的命令即可 默认命令 Pyinstaller 文件名.py Pyinstaller -option1 -option2 -... 要打包的文件 Pyinstaller 文件名.pyPyinstaller -option1 -option2 -... 要打包的文件 参数选项比较多&#xff0c;这里我列一个表&#xff1a;…