Android软件渲染流程

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.mfbz.cn/a/633604.html

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

相关文章

Transformer系列专题(二)——multi-headed多头注意力机制

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、什么是multi-headed(多头注意力机制)二、multi-headed三、multi-headed结果四、堆叠多层总结 前言 在实践中,当给定相同…

【数据库基础】基本认识数据库--入门引导

文章目录 什么是数据库?主流数据库基本使用安装MySQL连接服务器服务器、数据库、表关系使用案例数据逻辑存储 MySQL架构SQL语句分类什么叫存储引擎 什么是数据库? 数据库是指在磁盘和内存中存储特定结构组织的数据。数据库通常用于存储于某个系统、组织或…

分布式异步框架celery + Redis 安装配置

引入 这里不对web框架做过多说明,到时候在总结一篇 python的常见web框架 django、flask、tornado、sanic、fastapi..各框架区别 - 内部集成功能的多少 django,内部提供了很多组件。 【相对大】flask、tornado、sanic、fastapi… 本身自己功能很少第…

Linux c开发线程锁和条件变量使用

#include <pthread.h> #include <stdio.h> #include <unistd.h>pthread_mutex_t mutex PTHREAD_MUTEX_INITIALIZER; pthread_cond_t cond PTHREAD_COND_INITIALIZER;void* thread_function(void* arg) {printf("线程等待唤醒,锁定互斥量...\n");…

【Unitydemo制作】音游制作—排行榜逻辑Json存储

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;就业…

查看当前Shell系统环境变量

查看当前Shell系统环境变量 查看命令 env效果 查看Shell变量(系统环境变量自定义变量函数) 命令 set效果 常用系统环境变量 变量名称含义PATH与windows环境变量PATH功能一样&#xff0c;设置命令的搜索路径&#xff0c;以冒号为分割HOME当前用户主目录&#xff1a;/rootSH…

day08-Java常用API

day08——Java常用API 一、今日内容介绍、API概述 各位同学&#xff0c;我们前面已经学习了面向对象编程&#xff0c;使用面向编程这个套路&#xff0c;我们需要自己写类&#xff0c;然后创建对象来解决问题。但是在以后的实际开发中&#xff0c;更多的时候&#xff0c;我们是…

通过 Spring 操作 Redis

要想通过 Java 操作 redis&#xff0c;首先要连接上 redis 服务器&#xff0c;推荐看通过 Java 操作 redis -- 连接 redis 创建项⽬ 勾选 NoSQL 中的 Spring Data Redis 当然, 把 Web 中的 Spring Web 也勾选⼀下.⽅便写接进⾏后续测试. 配置 redis 服务地址 在 application.…

数据库的约束 not null, unique, default, primary key, foreign key, check

约束可以理解成 数据库提供的一种针对数据的合法性进行验证的机制, 在创建表的时候使用 1. 约束类型 NOT NULL - 指示某列不能存储 NULL 值, 表里的这个内容是必填项UNIQUE - 保证某列的每行必须有唯一的值, 不能重复 每次插入/修改时, 都要先触发查询, 如果当前插入/修改的…

mysql存储比特位

一、介绍 二、SQL CREATE TABLE bits_table (id INT PRIMARY KEY AUTO_INCREMENT,bit_value BIGINT UNSIGNED );-- 插入一个 8 位的 BIT 值 INSERT INTO bits_table (bit_value) VALUES (B10101010);-- 查询并格式化输出 SELECT id,bit_value,CONCAT(b, LPAD(BIN(bit_value),…

解决小皮面版搭建php网站数据库连接不了

首先进入mysql bin目录下 并执行cmd mysql -u root -pCREATE USER userlocalhost IDENTIFIED BY pass;GRANT ALL PRIVILEGES ON *.* TO userlocalhost;GRANT SELECT, INSERT, UPDATE ON database_name.* TO xxwlocalhost;FLUSH PRIVILEGES;select host ,user from mysql.user…

pdf文件怎么编辑?分享3个专业的pdf软件!

在数字化时代&#xff0c;PDF文件已成为我们工作、学习中的得力助手。然而&#xff0c;面对需要修改的PDF文件&#xff0c;许多人却感到无从下手。今天&#xff0c;就让我们一起探索如何轻松编辑PDF文件&#xff0c;并介绍几款实用的编辑软件&#xff0c;让你轻松应对各种PDF编…

Linux DAY 6 _systemctl

systemctl命令&#xff0c;通过这个命令控制系统操作 语法&#xff1a;systemctl start | stop | status | enable | disable 服务名 start 启动 stop 关闭 status 查看状态 enable 开启开机自启 disable 关闭开机自启 服务名&#xff1a; NetworkManager 主网络服务 net…

DFE_offset失调校准

1.校准原因 *制造工艺的限制&#xff1a;晶体管在制造过程中&#xff0c;由于工艺的限制&#xff0c;不可能做到完全对称&#xff0c;这导致了输入级晶体管的性能存在微小的差异。 *输入级偏置电流的不对称&#xff1a;输入级晶体管的偏置电流也会存在差异&#xff0c;这也会…

如何在OpenHarmony上使用SeetaFace2人脸识别库?

简介 相信大部分同学们都已了解或接触过OpenAtom OpenHarmony&#xff08;以下简称“OpenHarmony”&#xff09;了&#xff0c;但你一定没在OpenHarmony上实现过人脸识别功能&#xff0c;跟着本文带你快速在OpenHarmony标准设备上基于SeetaFace2和OpenCV实现人脸识别。 项目效…

如何理解kmp的套娃式算法啊?

概念 KMP算法&#xff0c;全称Knuth Morris Pratt算法 。文章大部分内容出自《数据结构与算法之美》 核心思想 假设主串是a&#xff0c;模式串是b 在模式串与主串匹配的过程中&#xff0c;当遇到不可匹配的字符的时候&#xff0c;对已经对比过的字符&#xff0c;是否能找到…

向上调整建堆与向下调整建堆的时间复杂度 AND TopK问题

目录 前言建堆的时间复杂度TOPK问题总结 前言 本篇旨在介绍使用向上调整建堆与向下调整建堆的时间复杂度. 以及topk问题 博客主页: 酷酷学!!! 感谢关注~ 建堆的时间复杂度 堆排序是一种优于冒泡排序的算法, 那么在进行堆排序之前, 我们需要先创建堆, 为什么说堆排序的是优于…

YOLOv8绘制map曲线图

yolov8源码绘制的map曲线图不够清晰&#xff0c;python代码绘制的曲线图导入word之后清晰度也不够高&#xff0c;所以选择使用matlab来绘制曲线图&#xff0c;matlab可以直接复制图窗到word中&#xff0c;在转换成pdf也不会失真。点击编辑&#xff0c;复制图窗即可复制到word中…

【Linux】:Linux 2.6内核 调度队列和调度原理

朋友们、伙计们&#xff0c;我们又见面了&#xff0c;本期来给大家解读一下有关Linux 2.6内核 调度队列和调度原理&#xff0c;如果看完之后对你有一定的启发&#xff0c;那么请留下你的三连&#xff0c;祝大家心想事成&#xff01; C 语 言 专 栏&#xff1a;C语言&#xff1a…

做抖店需要截流吗?聊下抖店的出单玩法和运营思路

我是王路飞。 做抖店需要截流吗&#xff1f; 关于抖店的玩法&#xff0c;一直都是众说纷纭&#xff0c;谁都想发表点自己的意见。 尤其是很多新手&#xff0c;可能以前接触过淘宝等传统电商&#xff0c;对截流等玩法有个基本了解&#xff0c;就认为抖店是不是也是这样玩的。…