Android HWUI
Android HWUI(Hardware Accelerated Rendering Engine for UI)是Android系统中用于处理UI渲染的硬件加速引擎。它的主要作用是利用GPU(图形处理单元)来加速UI的渲染过程,从而提高渲染效率和流畅度。以下是Android HWUI工作的主要方式和步骤:
一、基本工作原理
传统软件的UI绘制是依靠CPU来完成的,硬件加速就是将绘制任务交由GPU来执行。
HWUI基于GPU加速,通过Skia Backend与GPU进行交互,将UI绘制任务从CPU转移到GPU上执行。这种方式可以显著提高绘制性能,特别是在高分辨率屏幕和复杂UI场景下。
- GPU相比CPU更加适合完成光栅化、动画变换等耗时任务,在移动设备上比起使用CPU来完成这些任务,GPU会更加省电,带来的用户体验也会更佳。
- 现代移动GPU支持可编程管线,可以高效地开发应用界面的一些特效(吸入、渐变、模糊、阴影)
Skia Backend有两种:SkiaGl和SkiaVk,分别对应后端OpenGLES和Vulkan
二、核心组件
- Skia图形库:
- Skia是HWUI的核心图形库,提供了基础的绘制功能,如图形、文本、位图等。
- 它支持硬件加速渲染,能够充分利用GPU进行并发计算,加快UI界面的渲染速度。
- 代码示例
// 创建画布 Canvas canvas = new Canvas(bitmap); // 绘制矩形 Paint paint = new Paint(); final int color = Color.BLUE; paint.setColor(color); RectF rect = new RectF(0, 0, bitmap.getWidth(), bitmap.getHeight()); canvas.drawRoundRect(rect, 32, 32, paint);
- OpenGL ES/Vulkan:
- OpenGL ES/Vulkan是HWUI与GPU之间的桥梁,负责将Skia生成的绘制命令转化为GPU能够执行的指令序列。
- OpenGL ES 3.0是HWUI的默认版本,具有更好的功能和性能表现。未来HWUI的Backend默认会切换到Vulkan。
- DisplayList:
- DisplayList是HWUI的渲染列表,用于记录绘制操作以及它们的位置、大小等信息。
- 这些操作被显式地缓存起来,以便后续可以更快速地进行处理,并允许视图树中相同的部分在多个帧之间重复使用,从而节约内存和带宽。
- 代码示例
// 创建DisplayList DisplayList displayList = new DisplayList("MyList"); displayList.start(512, 512); //设置宽高 // 添加绘制操作 Paint paint = new Paint(); paint.setColor(Color.RED); paint.setStyle(Paint.Style.FILL); displayList.drawRect(64, 64, 256, 256, paint); // 结束DisplayList displayList.end();
- RenderNode:
- RenderNode是HWUI的渲染节点,对应于视图层次结构中的一个节点。
- 每个View持有一个RenderNode,这些RenderNode组成树形结构,用于管理和组织渲染任务。
- RenderNode负责保存和管理View绘制过程中使用的各种属性,如透明度和边框等。
- 代码示例
// 创建RenderNode RenderNode renderNode = new RenderNode("MyNode"); // 更新RenderNode属性 View view = findViewById(R.id.myView); renderNode.setPosition(view.getLeft(), view.getTop()); renderNode.setElevation(view.getElevation()); Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.icon); renderNode.setBitmapCache(bitmap); // 添加子节点 RenderNode child = new RenderNode("MyChild"); //更新child属性 renderNode.addChild(child);
- Layers
- Layer是HWUI的另一个重要概念,用于实现UI的专业效果和动画效果。
- 在Android 10及以前的系统中,所有视图都隶属于共享的系统组层级。但是在Android 11及以后的系统中,每个窗口/活动/碎片都将拥有自己的独立图层级别,从而增强了性能和隐私管理。
- 代码示例
// 创建Layer Layer layer = mSurface.getLayer(); layer.setX(100); layer.setY(100); layer.setAlpha(0.5f); // 在Layer上绘制图像和文字 Canvas canvas = layer.lockCanvas(); canvas.drawBitmap(bitmap, 0, 0, paint); canvas.drawText(text, 30, 100, paint); layer.unlockCanvasAndPost(canvas);
- HardwareComposer
- HardwareComposer是Android系统中的硬件合成器,作为HWUI的主要接口之一。
- 它的作用是在GPU输出图像到屏幕之前,对多个应用程序的渲染结果进行混合和组合,从而形成最终的显示内容。
- 在硬件合成情况下,不需要将应用程序的UI渲染成一个帧缓存(FrameBuffer)并上传到系统RAM,从而减少内存和总线带宽的负担。
- 代码示例
RoutingTable routingTable = hwcomposer.createRoutingTable(); routingTable.setOutputConfig(new OutputConfiguration(mDisplayToken, Display.DEFAULT_DISPLAY)); Destination destination = routingTable.createDestination(); destination.setColorMode(ColorMode.SRGB); destination.setBufferStream(bufferStream); surfaceFlinger.setTransactionState(routingTable.getTransaction()); surfaceFlinger.applyTransaction();
- Threaded Rendering(多线程渲染):
- 为了最大化利用CPU性能,HWUI采用了多线程渲染模式。
- 通常包括UI线程和RenderThread(RT线程)。UI线程负责View的绘制逻辑和将绘制命令打包成Skia的绘制命令存储到DisplayList;RT线程则负责取出这些绘制命令并执行实际的渲染操作。
三、工作流程
- UI线程绘制:
- 当View需要被绘制时,UI线程会调用相应的绘制方法(如onDraw())。
- 这些绘制方法通过Canvas类提供的接口进行绘制操作,Canvas的绘制命令会被转化为Skia的绘制命令并存储到DisplayList中。
- RenderThread渲染:
- RenderThread从DisplayList中取出绘制命令,并通过OpenGL ES接口将这些命令发送给GPU执行。
- GPU完成绘制后,将渲染结果输出到屏幕上。
- 性能优化:
- HWUI还采用了多种性能优化技术,如延时渲染列表(Deferred Display List)、绘制命令合并(Draw Op Batching)等。
- 这些技术通过减少GPU的调用次数、优化渲染状态切换等方式来提高渲染效率。
四、Debug Commands
开关HWUI
强制开HWUI(by default HWUI is enabled):
$ adb shell setprop persist.sys.ui.hw true
$ adb shell setprop debug.viewroot.disableHW false
$ adb shell stop
$ adb shell start
强制关HWUI:
$ adb shell setprop persist.sys.ui.hw false
$ adb shell setprop debug.viewroot.disableHW true
$ adb shell stop
$ adb shell start
改变HWUI Skia backend
$ adb shell setprop debug.hwui.renderer skiagl
$ adb shell stop
$ adb shell start
可选的skia backend选项有skiagl skiavk opengl
其他Commands
$ adb shell setprop debug.hwui.show_dirty_regions true
启用后每隔一帧绘制一次dirty_regions。true为enable, false为disable.
$ adb shell setprop debug.hwui.skip_empty_damage true
设置此属性将启用或禁用丢弃empty damage的帧。true为enable, false为disable.
$ adb shell setprop debug.hwui.use_buffer_age true
控制 HWUI 是否使用 EGL_EXT_buffer_age 扩展来做partial invalidate. true为enable, false为恢复使用 BUFFER_PRESERVED.
$adb shell setprop debug.hwui.use_partial_updates true
将其设置为“false”将强制 HWUI 始终对表面进行完全重绘。
这将禁用 EGL_EXT_buffer_age 和 BUFFER_PRESERVED 的使用
$ adb shell setprop debug.hwui.use_gpu_pixel_buffers true
指示 PBO 是否可用于支持像素缓冲区。
可接受的值为“true”和“false”。默认为 true。
$ adb shell service call SurfaceFlinger 1008 i32 1
强制使用GLES进行layer合成,不用HardwareComposer
$ adb shell setprop vendor.hwc.force_to_gpu 1
强制GPU合成
五、总结
Android HWUI通过利用GPU进行硬件加速渲染,显著提高了Android系统的UI渲染性能和流畅度。它通过Skia图形库、OpenGL ES接口、DisplayList、RenderNode以及多线程渲染等技术手段实现了高效的UI渲染过程。同时,HWUI还不断引入新的性能优化技术来进一步提升渲染效率。
Tips:
Android15查看skia的backend
adb shell dumpsys gfxinfo | grep Pipe
Peek相关代码
219 RenderPipelineType Properties::peekRenderPipelineType() { 220 // If sRenderPipelineType has been locked, just return the locked type immediately. 221 if (sRenderPipelineType != RenderPipelineType::NotInitialized) { 222 return sRenderPipelineType; 223 } 224 bool useVulkan = use_vulkan().value_or(false); 225 std::string rendererProperty = base::GetProperty(PROPERTY_RENDERER, useVulkan ? "skiavk" : "skiagl"); 226 if (rendererProperty == "skiavk") { 227 return RenderPipelineType::SkiaVulkan; 228 } 229 return RenderPipelineType::SkiaGL; 230 }