Android硬件渲染环境初始化

Android硬件渲染环境初始化

  • 一.硬件加速渲染的开启
    • 1.ThreadedRenderer的初始化
    • 2.RenderProxy的创建
  • 二.RenderProxy中组件的初始化
    • 1.RenderThread的创建
    • 2.CanvasContext的创建
    • 3.DrawFrameTask的初始化
  • 三.RenderThread的启动
    • 1.RenderThread中组件的初始化
    • 2.RenderThread中任务的处理
    • 3.RenderThread中动画帧的处理
      • 3.1 VSync信号的请求
      • 3.2 VSync信号的分发
  • 四.Surface的绑定
  • 五.总结
    • 1.硬件渲染开启流程
    • 2.ThreadedRenderer的创建
      • 2.1 RenderProxy的创建
    • 3.ThreadedRenderer与Surface的绑定

    硬件渲染是指通过GPU进行渲染,软件渲染是指通过CPU进行渲染。

    在Android中,硬件渲染的过程是通过CPU对需要绘制的内容使用指令进行标记,再通过GPU将标记的指令转换为对应的OpenGL指令进行渲染。从Android4.0开始,UI绘制默认开启硬件加速。在Android5.0之前,Android UI线程和渲染线程是同一个线程,但在Android5.0之后,渲染过程在单独的线程中进行,这个线程就是RenderThread。

一.硬件加速渲染的开启

    在Android中,硬件渲染环境的初始化是从ViewRootImpl的setView方法开始的。

    在ViewRootImpl的setView方法中,主要做了三件事:

1)判断参数中的View是否为RootViewSurfaceTaker。如果为RootViewSurfaceTaker,则调用willYouTakeTheSurface方法,获取SurfaceHolder.Callback2,并保存到mSurfaceHolderCallback中。

2)如果mSurfaceHolderCallback不为空,说明开发者接管了当前Activity的Surface,则创建TakenSurfaceHolder,并保存在mSurfaceHolder中。

3)如果mSurfaceHolder为空,说明开发者没有接管当前Activity的Surface,则调用enableHardwareAcceleration方法开启硬件加速。
0
    当在Activity中调用Window的takeSurface方法接管Activity的Surface后,Activity的渲染会脱离Android的View体系,相当于整个Activity都是SurfaceView。

class TestActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)// 通过这种方式可以脱离Android View体系,相当于整个Activity都是SurfaceViewwindow.takeSurface(object : SurfaceHolder.Callback2 {override fun surfaceCreated(holder: SurfaceHolder) {}override fun surfaceChanged(holder: SurfaceHolder,format: Int,width: Int,height: Int) {}override fun surfaceDestroyed(holder: SurfaceHolder) {}override fun surfaceRedrawNeeded(holder: SurfaceHolder) {}})}
}

    在ViewRootImpl的enableHardwareAcceleration方法中主要做了三件事:

1)通过ThreadedRenderer的静态方法create创建ThreadedRenderer。

2)将创建的ThreadedRenderer保存到mAttachInfo的mThreadedRenderer字段上。

3)为ThreadedRenderer设置SurfaceControl。
1

1.ThreadedRenderer的初始化

    ThreadedRenderer继承自HardwareRenderer。在ThreadedRenderer的构造方法中,会直接创建ThreadedRenderer并返回。

    在HardwareRenderer的构造方法中,主要做了三件事:

1)创建Native层的RootRenderNode,作为窗口的渲染根节点,并返回对应的地址。

2)将返回的地址封装成Java层的RenderNode。

3)创建Native层的RenderProxy并返回对应的地址,RenderProxy负责从UI线程向Render线程发送渲染指令。
2

2.RenderProxy的创建

    HardwareRenderer的nCreateProxy方法对应的Native实现为android_graphics_HardwareRenderer的android_view_ThreadedRenderer_createProxy函数。

    在android_view_ThreadedRenderer_createProxy函数中,主要做了两件事:

1)获取Native层的RootRenderNode。

2)会创建RenderProxy,RenderProxy负责从UI线程向Render线程发送渲染指令。
3
    在RenderProxy的构造方法中,会创建RenderThread、CanvasContext和DrawFrameTask。

  • RenderThread:负责监听与请求VSync信号,在VSync信号到来时执行任务并请求下一次VSync信号。
  • CanvasContext:负责渲染节点的渲染与渲染管线的管理。
  • DrawFrameTask:负责硬件渲染中每一帧渲染任务的管理,包括布局的测量构建、多层级的管理等。

4

二.RenderProxy中组件的初始化

1.RenderThread的创建

    RenderThread继承自ThreadBase,ThreadBase继承自Thread。

    在RenderThread的getInstance方法中,主要做了两件事:

1)创建RenderThread。

2)并调用RenderThread的start方法启动线程。
5
    在RenderThread的父类ThreadBase的构造方法中,会创建Looper和WorkQueue。Looper用于线程的消息驱动。WorkQueue用于存储RenderThread中执行的任务,WorkQueue中存储的任务类型为WorkItem。
6
    在RenderThread的start方法中,会调用threadLoop方法开始循环执行任务。在RenderThread的threadLoop方法中,主要做了两件事:

1)调用initThreadLocals方法,初始化组件。

2)开启循环,执行任务。
7

2.CanvasContext的创建

    CanvasContext在RenderProxy中创建。RenderProxy将CanvasContext的create方法封装成任务,添加到RenderThread的WorkQueue中等待执行。

    在CanvasContext的create方法中,主要做了两件事:

1)根据指定类型创建对应的渲染管线。

2)创建CanvasContext。
8

3.DrawFrameTask的初始化

    DrawFrameTask在RenderProxy中创建,通过setContext方法初始化。在DrawFrameTask的setContext方法中,会保存CanvasContext、RootRenderNode、RenderThread。
9

三.RenderThread的启动

1.RenderThread中组件的初始化

    在RenderThread的initThreadLocals方法中,主要做了三件事:

1)调用initializeChoreographer方法,初始化VSync信号请求接收组件。

2)创建EglManager,EglManager用在初始化Open GL渲染上下文。

3)创建RenderState,RenderState用于记录渲染状态。
10
    在RenderThread的initializeChoreographer方法中,主要做了三件事:

1)创建AChoreographer。AChoreographer是Native层Choreographer的代理类,AChoreographer是对Choreographer指针的强制转换,当需要调用VSync信号相关的方法时,会将AChoreographer强制转换回Choreographer。两者关系与ACanvas和Canvas类似。Native层Choreographer继承自DisplayEventDispatcher。

2)通过向Looper添加文件描述符的方式的监听VSync信号的分发。

3)创建ChoreographerSource。ChoreographerSource是对AChoreographer的封装,用于在RenderThread中请求与接收VSync信号。ChoreographerSource继承自VSyncSource,VSyncSource定义了请求与处理VSync信号的方法。
11

2.RenderThread中任务的处理

    在RenderThread的threadLoop方法中,当组件初始化完成后,会开while启循环处理任务。在while循环中,主要做了三件事:

1)根据上一次任务队列执行完毕后返回的时间,对线程进行阻塞等待。

2)执行任务队列中的任务,并计算等待时间。

3)处理动画帧任务。
12
    waitForWork方法定义在RenderThread的父类ThreadBase中。在ThreadBase的waitForWork方法中,主要做了三件事:

1)获取队列中下一个待任务任务的开始时间。

2)通过减去当前的时间,计算出线程需要阻塞的时间。

3)阻塞线程开启休眠。
13

3.RenderThread中动画帧的处理

    当外部需要执行动画帧任务时,会调用RenderThread的postFrameCallback方法,在RenderThread的postFrameCallback方法中会对任务进行保存。
14
    在RenderThread的threadLoop方法的循环中,在处理完WorkQueue中的任务后,如果发现有需要处理的动画帧任务,会去处理动画帧任务。

    关于动画帧任务的处理,主要做了四件事:

1)对未分发的VSync信号进行分发,触发动画帧任务的执行。

2)将mPendingRegistrationFrameCallbacks中的动画帧任务转移到mFrameCallbacks中。

3)清空mPendingRegistrationFrameCallbacks。

4)请求下一次的VSync信号。
15
    在ChoreographerSource的drainPendingEvents方法中,会调用AChoreographer_handlePendingEvents函数分发VSync信号。
16

3.1 VSync信号的请求

    在RenderThread的requestVsync方法中,会调用ChoreographerSource的requestNextVsync方法。requestNextVsync方法内部又调用了AChoreographer_postVsyncCallback函数。
17
    在AChoreographer_postVsyncCallback函数中,将AChoreographer转换为Choreographer,并调用Choreographer的postFrameCallbackDelayed方法。
18
    在Choreographer的postFrameCallbackDelayed方法中,主要做了两件事:

1)将回调封装成FrameCallback并保存。

2)请求VSync信号。
19

3.2 VSync信号的分发

    当VSync信号到来时,RenderThread的Looper会从Choreographer的Fd中检测到Looper::EVENT_INPUT消息,并回调RenderThread静态方法的choreographerCallback。

    在RenderThread的静态方法choreographerCallback中,主要做了两件事:

1)获取RenderThread和AChoreographer。

2)调用AChoreographer_handlePendingEvents函数。
20
    在AChoreographer_handlePendingEvents函数中,会调用Choreographer的handleEvent方法。handleEvent方法在Choreographer的父类DisplayEventDispatcher中实现。

    在DisplayEventDispatcher的handleEvent方法中,会调用dispatchVsync方法,该方法在子类Choreographer中实现。
21
    在Choreographer的dispatchVsync方法中,主要做了两件事:

1)收集满足时间要求的FrameCallback。

2)调用FrameCallback中的回调方法。
22
    由于RenderThread在请求VSync信号时传入了静态方法extendedFrameCallback。因此当VSync信号到来时会调用RenderThread的静态方法extendedFrameCallback。

    在RenderThread的静态方法extendedFrameCallback中,主要做了两件事:

1)获取RenderThread。

2)调用RenderThread的frameCallback方法。
23
    在RenderThread的frameCallback方法中,主要做了两件事:

1)计算动画帧任务执行的目标时间点。

2)对dispatchFrameCallbacks方法进行封装,插入到WorkQueue中,等待到达目标时间点时threadLoop方法的循环任务处理。
24
    当RenderThread唤醒后,会执行任务队列中的任务,会调用dispatchFrameCallbacks方法。在RenderThread的dispatchFrameCallbacks方法中,主要做了两件事:

1)请求VSync信号。

2)执行动画帧任务。
25

四.Surface的绑定

    在ViewRootImpl的performTraversals方法中,会通过relayoutWindow方法创建Surface。当Surface创建好后,会调用ThreadedRenderer的initialize方法进行绑定。
26
    在ThreadedRenderer的initialize方法中,会调用setSurface方法。在ThreadedRenderer的setSurface方法中,会调用父类HardwareRenderer的setSurface方法。
27
    在HardwareRenderer的setSurface方法中,会调用nSetSurface方法。
28    HardwareRenderer的nSetSurface方法对应Native实现为android_graphics_HardwareRenderer的android_view_ThreadedRenderer_setSurface函数。

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

1)获取RenderProxy。

2)将Java层Surface指针封装成ANativeWindow。

3)为RenderProxy设置Surface。
29
    在RenderProxy的setSurface方法中,会对CanvasContext的setSurface方法封装,添加到RenderThread的WorkQueue中等待执行。
30
    当RenderThread执行任务时,会调用CanvasContext的setSurface方法。在CanvasContext的setSurface方法中,主要做了三件事:

1)将ANativeWindow封装成ReliableSurface,ReliableSurface用于Hook拦截ANativeWindow的方法调用。

2)初始化ReliableSurface。

3)初始化渲染管线。
31
    在CanvasContext的setupPipelineSurface方法中,主要做了两件事:

1)获取ANativeWindow。

2)为渲染管线设置Surface。
32

五.总结

1.硬件渲染开启流程

    硬件渲染的开启发生在ViewRootImpl中。开启硬件渲染会进行两步操作:第一步是ThreadedRenderer的创建,第二步是ThreadedRenderer与Surface的绑定。

2.ThreadedRenderer的创建

    ThreadedRenderer的创建会触发Native层RootRenderNode和RenderProxy的创建。RootRenderNode是窗口渲染根节点。RenderProxy负责从UI线程向Render线程发送渲染指令。

2.1 RenderProxy的创建

    RenderProxy的创建会触发RenderThread、CanvasContext、DrawFrameTask的创建。RenderThread负责监听与请求VSync信号,在VSync信号到来时执行任务并请求下一次VSync信号。CanvasContext负责渲染节点的渲染与渲染管线的管理。DrawFrameTask负责硬件渲染中每一帧渲染任务的管理,包括布局的测量构建、多层级的管理等。

3.ThreadedRenderer与Surface的绑定

    ThreadedRenderer与Surface的绑定本质上是将Surface与CanvasContext中渲染管线的绑定。Java层Surface在进入Native层后会被封装成ANativeWindow。

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

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

相关文章

arXiv AI 综述列表(2024.05.20~2024.05.24)

公众号:EDPJ(进 Q 交流群:922230617 或加 VX:CV_EDPJ 进 V 交流群) 每周末更新,完整版进群获取。 Q 群在群文件,VX 群每周末更新。 目录 1. Beyond Traditional Single Object Tracking: A …

基于yolov2深度学习网络的昆虫检测算法matlab仿真,并输出昆虫数量和大小判决

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022A 3.部分核心程序 .......................................................... for i 1:12 % 遍历结…

播兔短剧模板:图鸟UI在前端短剧平台中的应用与实践

一、引言 随着移动互联网的快速发展,短剧平台因其短小精悍、内容丰富的特点,逐渐成为用户休闲娱乐的新宠。为了满足短剧平台对前端技术的需求,图鸟播兔短剧模板应运而生。该模板基于图鸟UI进行开发,采用纯前端技术,支…

【C语言回顾】文件操作

前言1. 文件打开模式2. 示例代码2.1 打开和关闭文件2.2 读写文件2.3 二进制文件操作 结语 #include<GUIQU.h> int main { 上期回顾: 【C语言回顾】动态内存管理 个人主页&#xff1a;C_GUIQU 专栏&#xff1a;【C语言学习】 return 一键三连; } 前言 各位小伙伴大家好&…

分享:怎么才能保证大数据查询的准确性?

随着大数据应用到金融风控领域&#xff0c;大数据越来越重要了&#xff0c;很多朋友在查大数据的时候都会遇到一个问题&#xff0c;那就是自己查询的大数据什么信息都没有&#xff0c;要么就是很少&#xff0c;这是什么原因呢?要怎么才能保证大数据查询的准确性呢?下面小编就…

Java进阶学习笔记3——static修饰成员方法

成员方法的分类&#xff1a; 类方法&#xff1a;有static修饰的成员方法&#xff0c;属于类&#xff1a; 成员方法&#xff1a;无static修饰的成员方法&#xff0c;属于对象。 Student类&#xff1a; package cn.ensource.d2_staticmethod;public class Student {double scor…

双拼打字,可以尝试这个练习平台

本文将介绍由本人开发的双拼练习平台打字侠&#xff0c;以及一些对新手学习双拼的建议。课程一共233课&#xff0c;共分为两大部分&#xff1a; 韵母键位&#xff0c;主要练习双拼中韵母对应的键盘键位&#xff0c;同时提供可视化的键盘指法示意图。 实战练习&#xff0c;同时…

Selenium常用命令(python版)

日升时奋斗&#xff0c;日落时自省 目录 1、Selenium 2、常见问题 1、Selenium 安装Python和配置环境没有涉及 注&#xff1a;如有侵权&#xff0c;立即删除 首先安装selenium包&#xff0c;安装方式很简单 pip install selenium 注:我这里已经安装好了&#xff0c;所以…

怎么一键消除路人?教你三个消除方法

怎么一键消除路人&#xff1f;在数字时代&#xff0c;摄影已成为我们记录生活、表达情感的重要方式。然而&#xff0c;完美的照片背后往往隐藏着一些不那么完美的元素——比如那些不经意间闯入镜头的路人。他们或许只是匆匆过客&#xff0c;但却足以破坏你精心构图的美好瞬间。…

分割文本文件

分割一个.txt文件&#xff0c;可以选择在命令行中使用split指令&#xff0c;或者编写一段脚本进行操作。以下是一个简单的Python脚本来分割文本文件&#xff1a; def split_file(file, lines):# Open source filewith open(file, r) as source:count 0atEOF Falsewhile not …

shell笔记脚本3

执行脚本文件demo2.sh, 观察打印VAR4效果 执行脚本文件后, 在交互式Shell环境打印VAR4, 观察打印VAR4效果 结论 全局变量在当前Shell环境与子Shell环境中可用, 父Shell环境中不可用 小结 自定义变量的分类 自定义局部变量: 就是在一个脚本文件内部使用 var_namevalue 自…

128天的创意之旅:从初心到成就,我的博客创作纪念日回顾

文章目录 &#x1f680;机缘&#xff1a;初心的种子——回望创作之旅的启航&#x1f308;收获&#xff1a;成长的果实——128天创作之旅的宝贵馈赠❤️日常&#xff1a;创作与生活的交织&#x1f44a;成就&#xff1a;代码的艺术&#x1f6b2;憧憬&#xff1a;未来的蓝图 &…

Mujoco仿真【xml文件的学习 3】

在学习Mujoco仿真的过程中&#xff0c;为了与时俱进&#xff0c;之前的mujoco210版本不再使用&#xff0c;改用了mujoco-3.1.4版本&#xff0c;下面继续mujoco仿真的学习&#xff01; 先前关于mujoco的学习博客汇总如下&#xff1a; 强化学习&#xff1a;MuJoCo机器人强化学习…

2024最新(PC+WEB+IOS+Android)即时通讯系统客户端仿默往IM源码下载

2024最新(PCWEBIOSAndroid)即时通讯系统客户端仿默往IM源码下载(总大小约2.4G&#xff09; 系统功能配置灵活、海量并发、稳定可靠、数据安全&#xff0c;2小时快速部署、数据安全、单聊群聊、系统通知等通信功能&#xff0c;支持App、PC、Web等多端快速接入。 群功能&#xf…

Linux 软件包管理器 yum的下载、功能介绍及使用

&#x1fa90;&#x1fa90;&#x1fa90;欢迎来到程序员餐厅&#x1f4ab;&#x1f4ab;&#x1f4ab; 主厨&#xff1a;邪王真眼 主厨的主页&#xff1a;Chef‘s blog 所属专栏&#xff1a;青果大战linux 总有光环在陨落&#xff0c;总有新星在闪烁 Linux下的三种软件安装方…

panic对defer语句的执行的影响

1.主线程中的panic会直接导致所有正在运行的go协程无法执行,还会导致声明在它之后的defer语句无法执行。 package mainimport ("fmt""time" )func main() {defer fmt.Println("defer1") //声明在panic之前的defer会执行go func() {defer fmt.Pri…

蓝桥杯Web开发【大赛大纲】15届

一、 组别 Web应用开发分为&#xff1a;大学组和职业院校组。 每位选手只能申请参加其中一个组别的竞赛。各个组别单独评奖。 研究生和本科生只能报大学组。 其它高职高专院校可自行选择报任意组别。 二. 竞赛赛程 省赛时长&#xff1a;4小时。 决赛时长&#xff1a;4小…

LeetCode:279.完全平方数

class Solution:def numSquares(self, n: int) -> int:dp[i for i in range(n1)]for i in range(2,n1):for j in range(1,int(i**(0.5))1):dp[i]min(dp[i],dp[i-j*j]1)return dp[-1]代码解释 初始化 DP 数组&#xff1a; dp [i for i in range(n1)] 这里&#xff0c;dp[i]…

五分钟”手撕“图书管理系统

前言&#xff1a; 图书馆管理系统需要结合JavaSE的绝大部分知识&#xff0c;是一个很好的训练项目。 为了让大家更加方便的查阅与学习&#xff0c;我把代码放开头&#xff0c;供大家查询。 还有对代码的分析&#xff0c;我将以类为单位分开讲解。 目录 全部代码 Main类 Us…

如何排查hpet导致的CPU高负载——《OceanBase诊断系列》之十

1. 前言 我在OceanBase问答社区协助用户排查了一个CPU占用率过高的问题&#xff0c;帖子原文是&#xff1a; 《刚刚新安装的OceanBase集群&#xff0c;没有任何数据&#xff0c;CPU占用非常高&#xff0c;这正常吗&#xff1f;》。从这个场景出发&#xff0c;来分享相关的诊断…