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

Android GPU的RenderThread Texture upload上传Bitmap优化prepareToDraw


Android GPU的RenderThread Texture upload上传Bitmap优化prepareToDraw

基本原理

View树中所有元素的材料最终会封装到DisplayList对象中(后期版本有用RenderNode对DisplayList又做了一层封装,实现了更好的性能),然后发送出去,这样第一阶段就完成。

有一个重要的问题:这个阶段怎么处理Bitmap?

会将Bitmap复制到下一个阶段(复制到GPU的内存中)。 现在大多数设备使用了GPU硬件加速,而GPU在渲染来自Bitmap的数据时只能读取GPU内存中的数据, 所以需要赋值Bitmap到GPU内存,这个阶段对应的名称叫Sync & upload。但是,硬件加速并不支持所有Canvas API,如果自定义View使用了不支持硬件加速的Canvas API(参考Android硬件加速文档)。

这时可能会有问题:如果Bitmap很多或者单个Bitmap尺寸很大,这个过程可能会时间比较久,那有什么办法吗?

预上传: Bitmap.prepareToDraw()(from Android 7.0 - Nougat)
使用Hardware-Only Bitmap(from Android 8.0 - Oreo)

从Android 8.0 开始,支持了Hardware-Only Bitmap类型,这种类型的Bitmap的数据只存放在GPU内存中,这样在 Sync & upload 阶段就不需要upload这个Bitmap了。使用很简单,只需要将Options.inPreferredConfig赋值为Bitmap.Config.HARDWARE即可。

这种方式能实现特定场景的极致性能,但这种Bitmap的某些操作是受限的(毕竟数据存储只存储在GPU内存中),可以查看Glide的总结文档:https://bumptech.github.io/glide/doc/hardwarebitmaps.html 

在Android3.0~Android7.0,Bitmap内存放在Java堆中,而android系统中每个进程的Java堆是有严格限制的,处理不好这些Bitmap内存,容易导致频繁GC,甚至触发Java堆的OutOfMemoryError。从Android8.0开始,bitmap的像素数据放入了native内存,于是Java Heap的内存问题暂时缓解了。

接下来就要把Bimap画出来,画出来的过程就是把前面的Bimap转化成一堆像素数据的过程,也叫栅格化,那这个活儿谁来干呢?

只有两个:
CPU: 软件绘制,使用Skia方案实现,绘制慢。
GPU: 硬件加速绘制,使用OpenGL ES或Vulkan方案实现,绘制快很多。
大部分情况下,都是GPU来干这个活儿,因为GPU真的特别快。

画在哪里

至于画在哪里,可以理解为一个缓冲(Buffer)。到此,已经画(绘制)完了图像内容,把这个内容发送出去,任务完成。

显示到屏幕

BufferQueue
Android图形架构中,使用生产者消费者模型来处理图像数据,其中的图像缓冲队列叫BufferQueue, 队列中的元素叫Graphic Buffer,队列有生产者也有消费者;每个应用通常会对应一个Surface,一个Surface对应着一个缓冲队列,每个队列中Graphic Buffer的数量不超过3个(经典模型), 上面绘制的Bitmap数据最终会放入一个Graphic Buffer,应用自身就是队列的生产者。
每个Graphic Buffer本身体积很大,在从生产者到消费者的传递过程中不会进行复制操作,用匿名共享内存的方式,通过句柄来跨进程传递。

性能优化

Android 会将Bitmap显示为 OpenGL 纹理,并且当Bitmap第一次显示在帧中时,它会上传到 GPU。在 trace 中看到此操作显示为Texture upload(id)“宽 x 高”。这可能需要几毫秒的时间,但必须使用 GPU 显示图片。


如果这些操作用时较长,首先检查跟踪Bitmap的宽度和高度数据。请确保显示的Bitmap不会明显大于其在真正显示区域的尺寸,否则会浪费上传时间和内存。
Texture upload上传纹理是一个极为耗时的过程,在1080×1920的屏幕尺寸下传一张全屏的texture耗时不少。这样的话SurfaceFlinger就不可能在正常周期下流畅绘制。因此,Android采用native buffer,将graphic buffer直接作为纹理(direct texture)进行操作。
在 Android 7.0 中,Bitmap加载代码(通常由库完成)可以调用 prepareToDraw(),以便在需要用到它之前便提前触发上传。这样,上传upload操作会在 RenderThread 处于空闲状态时提前进行。只要获得Bitmap,就可以在解码之后或将Bitmap绑定到View时执行此操作。理想情况下,Bitmap加载库会自动执行此操作,但如果要自行管理,或者想要确保在更高版本的设备上不会触发主动上传,则可以在自己的代码中调用 prepareToDraw()。


Google开发人员对Bitmap的prepareToDraw优化建议( https://github.com/facebook/fresco/issues/1756 ):

ChrisCraik
opened on May 9, 2017
Description


Hey, I work on UI Toolkit Graphics in Android. Wanted to pass on an optimization you can enable for displaying Bitmaps.
Android displays Bitmaps as OpenGL textures, and the first time a Bitmap is displayed in a frame, it’s uploaded to the GPU. That can take several milliseconds, but it’s necessary to display the image with the GPU.
In Android N, we added behavior to Bitmap#prepareToDraw() to send an async message to RenderThread to pre-upload. RenderThread uploads these to the GPU when it expects to be idle between frames, instead of while drawing the first frame that uses the Bitmap. If you can do this after decoding, you can make it much less likely that frames are dropped due to uploading.
Reproduction
In Systrace, you can see the problem when any Bitmap is displayed by looking for sections labeled ‘Upload <w>x<h> Texture’. If those are happening in ‘DrawFrame’ on the RenderThread, inside ‘syncFrameState,’ they haven’t been pre-uploaded, and are on the critical path for the frame.
Solution
Call Bitmap#prepareToDraw as early as possible when you think a Bitmap will be displayed soon. The most aggressive way to do this is any time you decode an image (especially any immutable image).
The drawback is that if you call it on a bitmap before you modify it, or if it doesn’t get drawn, you’ll be wasting the time spent uploading. If you’re reasonably sure the Bitmap in question will be displayed, it’s still a great way to prefetch the upload work on RenderThread.
Additional Information
Behavior added in Android N:

https://android.googlesource.com/platform/frameworks/base/+/4387190d8ec9fe4e953fcfeb093a644b82cf85ed

For reference, Glide implemented this here: bumptech/glide@dce9550
It's safe to call on older versions, so you don't need a version check. The method existed prior to N, but was a noop for normal Bitmaps.

相关:

https://developer.android.com/topic/performance/vitals/render?hl=zh-cn

https://blog.csdn.net/zhangphil/article/details/145608757

https://bumptech.github.io/glide/doc/hardwarebitmaps.html

http://www.dtcms.com/a/524007.html

相关文章:

  • 10.1 网络规划与设计——结构化布线系统
  • 国产麒麟、uos在线编辑数据库中的文件
  • 从零开始的C++学习生活 15:哈希表的使用和封装unordered_map/set
  • 【图像处理基石】通过立体视觉重建建筑高度:原理、实操与代码实现
  • 金融培训网站源码国内可以做的国外兼职网站
  • 东莞网站设计制作网站个人网页设计需求分析
  • 率先发布!浙人医基于KingbaseES构建多院区异构多活容灾新架构
  • CSS 样式用法大全
  • Chrome旧版本下载
  • 浙江省建设网站首页html网站源代码
  • 厦门行业网站建设怎样建立自己的销售网站
  • 网站建设丿选择金手指排名15企业网站的制作公司
  • 结合MAML算法元强化学习
  • 重组蛋白表达的几种类型介绍
  • STM32之TM1638数码管及键盘驱动
  • Windows 10 安装 Docker Desktop
  • 数据的存储
  • GJOI 10.20/10.22 题解
  • Linux:权限(完结)|权限管理|修改权限chmod chown charp|文件类型|拓展
  • (一)仓库创建与配置 - .git 目录的结构与作用
  • Office 2010 64位 补丁 officesp2010-kb2687455 安装步骤详解(附安装包)
  • 建免费网站建设银行网站能不能注销卡
  • springboot中的怎么用JUnit进行测试的?
  • LeetCode:695. 岛屿的最大面积
  • 传奇手游可以使用云手机挂机搬砖吗
  • 2025 OSCAR丨与创新者同频!Apache RocketMQ 邀您共赴开源之约
  • Dify配置本地部署的音频识别模型
  • C# .NET Core中Chart图表绘制与PDF导出
  • 相机拍照的图片怎么做网站呀国内互联网公司排名
  • 微信怎么建设自己网站在单机安装wordpress