canvas教程

Android硬件加速原理与实现简介(2)

字号+ 作者:H5之家 来源:H5之家 2017-01-26 11:05 我要评论( )

View中的draw(canvas,parent,drawingTime) - draw(canvas) - onDraw - dispachDraw - drawChild这条递归路径(下文简称Draw路径),调用了Canvas.drawXxx()方法,在软件渲染时用于实际绘制;在硬件加速时,用于构建

View中的draw(canvas,parent,drawingTime) - draw(canvas) - onDraw - dispachDraw - drawChild这条递归路径(下文简称Draw路径),调用了Canvas.drawXxx()方法,在软件渲染时用于实际绘制;在硬件加速时,用于构建DisplayList。

  • View中的updateDisplayListIfDirty - dispatchGetDisplayList - recreateChildDisplayList这条递归路径(下文简称DisplayList路径),仅在硬件加速时会经过,用于在遍历View树绘制的过程中更新DisplayList属性,并快速跳过不需要重建DisplayList的View。

    Android 6.0中,和DisplayList相关的API目前仍被标记为“@hide”不可访问,表示还不成熟,后续版本可能开放。

  • 硬件加速情况下,draw流程执行结束后DisplayList构建完成,然后通过ThreadedRenderer.nSyncAndDrawFrame()利用GPU绘制DisplayList到屏幕上。

  • 纯软件绘制 VS 硬件加速(Android 6.0)

    下面根据具体的几种场景,具体分析一下硬件加速前后的流程与加速效果。

    渲染场景 纯软件绘制 硬件加速 加速效果分析

    页面初始化 绘制所有View 创建所有DisplayList GPU分担了复杂计算任务

    在一个复杂页面调用背景透明TextView的setText(),且调用后其尺寸位置不变 重绘脏区所有View TextView及每一级父View重建DisplayList 重叠的兄弟节点不需CPU重绘,GPU会自行处理

    TextView逐帧播放Alpha / Translation / Scale动画 每帧都要重绘脏区所有View 除第一帧同场景2,之后每帧只更新TextView对应RenderNode的属性 刷新一帧性能极大提高,动画流畅度提高

    修改TextView透明度 重绘脏区所有View 直接调用RenderNode.setAlpha()更新 加速前需全页面遍历,并重绘很多View;加速后只触发DecorView.updateDisplayListIfDirty,不再往下遍历,CPU执行时间可忽略不计

  • 场景1中,无论是否加速,遍历View树并都会走Draw路径。硬件加速后Draw路径不做实际绘制工作,只是构建DisplayList,复杂的绘制计算任务被GPU分担,已经有了较大的加速效果。

  • 场景2中,TextView设置前后尺寸位置不变,不会触发重新Layout。

  • 软件绘制时,TextView所在区域即为脏区。由于TextView有透明区域,遍历View树的过程中,和脏区重叠的多数View都要重绘,包括与之重叠的兄弟节点和他们的父节点(详见后面的介绍),不需要绘制的View在draw(canvas,parent,drawingTime)方法中判断直接返回。

  • 硬件加速后,也需要遍历View树,但只有TextView及其每一层父节点需要重建DisplayList,走的是Draw路径,其他View直接走了DisplayList路径,剩下的工作都交给GPU处理。页面越复杂,两者性能差距越明显。

  • 场景3中,软件绘制每一帧都要做大量绘制工作,很容易导致动画卡顿。硬件加速后,动画过程直接走DisplayList路径更新DisplayList的属性,动画流畅度能得到极大提高。

  • 场景4中,两者的性能差距更明显。简单修改透明度,软件绘制仍然要做很多工作;硬件加速后一般直接更新RenderNode的属性,不需要触发invalidate,也不会遍历View树(除了少数View可能要对Alpha做特殊响应并在onSetAlpha()返回true,代码如下)。

  • { , to=1.0) float alpha) { ensureTransformationInfo(); if (mTransformationInfo.mAlpha != alpha) { mTransformationInfo.mAlpha = alpha; if (onSetAlpha((int) (alpha * 255))) { // ... invalidate(true); } else { // ... mRenderNode.setAlpha(getFinalAlpha()); // ... } } } alpha) { return false; } // ... } 软件绘制刷新逻辑简介

    实际阅读源码并实验,得出通常情况下的软件绘制刷新逻辑:

  • 默认情况下,View的clipChildren属性为true,即每个View绘制区域不能超出其父View的范围。如果设置一个页面根布局的clipChildren属性为false,则子View可以超出父View的绘制区域。

  • 当一个View触发invalidate,且没有播放动画、没有触发layout的情况下:

  • 对于全不透明的View,其自身会设置标志位PFLAG_DIRTY,其父View会设置标志位PFLAG_DIRTY_OPAQUE。在draw(canvas)方法中,只有这个View自身重绘。

  • 对于可能有透明区域的View,其自身和父View都会设置标志位PFLAG_DIRTY。

  • clipChildren为true时,脏区会被转换成ViewRoot中的Rect,刷新时层层向下判断,当View与脏区有重叠则重绘。如果一个View超出父View范围且与脏区重叠,但其父View不与脏区重叠,这个子View不会重绘。
  • clipChildren为false时,ViewGroup.invalidateChildInParent()中会把脏区扩大到自身整个区域,于是与这个区域重叠的所有View都会重绘。
  • 总结

    至此,硬件加速相关的内容就介绍完了,这里做个简单总结:

  • CPU更擅长复杂逻辑控制,而GPU得益于大量ALU和并行结构设计,更擅长数学运算。

  • 页面由各种基础元素(DisplayList)构成,渲染时需要进行大量浮点运算。

  • 硬件加速条件下,CPU用于控制复杂绘制逻辑、构建或更新DisplayList;GPU用于完成图形计算、渲染DisplayList。

  • 硬件加速条件下,刷新界面尤其是播放动画时,CPU只重建或更新必要的DisplayList,进一步提高渲染效率。

  • 实现同样效果,应尽量使用更简单的DisplayList,从而达到更好的性能(Shape代替Bitmap等)。

  • 参考资料与扩展阅读
  • GPU—并行计算利器

  • 显示卡的“心脏”GPU工作原理介绍

  • Matlab的GPU加速

  • 处理器体系结构:了解CPU的基本运行原理

  • CPU的内部架构和工作原理

  • 什么是异构多处理系统,为什么需要异构多处理系统

  • Android应用程序UI硬件加速渲染的Display List构建过程分析

  • Android应用程序UI硬件加速渲染的Display List渲染过程分析

  • Android Choreographer源码分析

  • Android Project Butter分析



  •  

    1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

    相关文章
    • -_--___---_-

      -_--___---_-

      2017-01-26 11:03

    • Android学习札记二

      Android学习札记二

      2017-01-26 10:02

    • 双缓冲技术实现Android 画板应用

      双缓冲技术实现Android 画板应用

      2017-01-24 15:03

    • 试图清除 Android SurfaceView/Canvas 时闪烁

      试图清除 Android SurfaceView/Canvas 时闪烁

      2017-01-22 09:14

    网友点评
    b