本文来自OPPO互联网技术团队,转载请注名作者。同时欢送关注咱们的公众号:OPPO_tech,与你分享OPPO前沿互联网技术及流动。
Flutter是目前风行的高性能跨平台UI框架,图形库skia是其跨平台的基石。本文将深入分析skia的图形、字体、图片的渲染原理,如何开掘硬件个性,为UI性能优化提供思路。
1. 引言
Flutter是目前十分风行的跨平台UI开发框架,不仅反对Android、iOS,还反对Windows、Linux等操作系统。Flutter的性能十分高,领有120fps的刷新率。那么flutter是如何实现在不同平台上高性能绘制图形图像的呢?首先咱们剖析下Flutter App和原生Android App、原生iOS App的UI绘制原理。
挪动App的整体UI框架大抵分成上面4个档次:
1)UI库
跟Android、iOS原生开发相似,Flutter用dart语言实现一整套UI控件。Flutter先将控件树转成渲染树,而后交由skia库绘制界面。
2)图形库
Skia图形库跟iOS平台的CoreAnimation等库性能相似,不仅提供了图形渲染性能,还提供文字绘制和图片显示性能。高级图形图像库将须要绘制的图形转成点、线、三角形等图元,再调用底层图形接口实现绘制。
3)低级图形接口
OpenGL是应用最广的低级图形接口,兼容性最好,基本上反对市面上的所有GPU。Vulkan是最近几年新推出的图形API,除了iPhone的GPU,其余厂家的GPU根本都反对。Metal是苹果新推出的图形API,只反对自家GPU。
4)硬件设施层
目前的挪动设施出于性能思考,大部分图形都是通过GPU渲染,多数状况也会应用CPU渲染,后文会介绍skia应用CPU和GPU渲染的具体场景。
iPhone 在A11芯片以前应用power vr系列GPU,之后采纳自研GPU。安卓手机大部分采纳高通Adreno GPU或ARM mail GPU。GPU渲染完一帧图像后送FrameBuffer,最初在适合的机会展现在屏幕上。
Skia利用宽泛并且跨平台,不仅用于Flutter和Android操作系统,还用于Google Chrome浏览器,同时反对windows、Mac、iOS操作系统。Skia由C++编写,代码开源,通过钻研skia有助于了解图形图像的绘制原理,为UI性能优化提供思路。
2. skia 框架剖析
2.1 Skia内部组件依赖
Skia依赖的第三方库泛滥,包含字体解析库freeType,图片编解码库libjpeg-turbo、libpng、libgifocode、libwebp和pdf文档解决库fpdfemb等。Skia反对多种软硬件平台,既反对ARM和x86指令集,也反对OpenGL、Metal和Vulkan低级图形接口。
2.2 Skia 档次剖析
Skia在结构上大抵分成三层:画布层,渲染设施层和封装适配层。
2.2.1 画布层
画布层能够了解成提供给开发者在一个设施无关的画布,能够在下面绘制各种图形,且不必关怀硬件细节,性能如下:
类别 | 函数名 | 含意 |
---|---|---|
画图形 | drawPoints | 画点 |
画图形 | drawRect | 画矩形 |
画图形 | drawVertices | 画多边形 |
画图形 | drawRoundRect | 画圆角矩形 |
画图形 | drawArc | 画圆弧 |
画图形 | drawOval | 画椭圆 |
画图形 | drawPath | 画矢量图 |
绘制文字 | drawText | 显示文字 |
显示图片 | drawBitmap | 显示位图 |
2.2.2 渲染设施层
渲染设施层负责画布层的硬件实现,skia将设施封装成上面三个类:
1)SKBitmapDevice
CPU渲染模式绘图,用于没有显卡或者显卡驱动的设施。此模式下,最初会将须要绘制的图形转成位图数据(RGB)写入指定内存,故称为BitmapDevice。写内存操作通过AVX或者NEON指令集实现。
2)SKGPUDevice
GPU渲染形式绘图。目前大部分挪动设施和个人电脑都有GPU,GPU比CPU的运算单元多,并行计算能力强,通过GPU绘图可升高CPU占用,性能更好。Flutter、最新版本的chrome和android零碎默认设置为GPU渲染模式。Chrome中的配置截图如下,可看到默认采纳GPU渲染。
3)SKPDFDevice
选用此设施时,渲染后果不是输入到显示器的画面,而是输入为pdf文件。
能够通过skia官网在线体验不同设施的渲染后果:https://fiddle.skia.org/c/@sh...
2.2.3封装适配层
Skia为了屏蔽不同依赖库的接口差别,对依赖库进行了封装和适配。例如基于图片编解码库libjpeg-turbo、libpng、libwebp 封装了类SKJpegCodec、SKPngCodec、SKWebpCodec。基于底层图形库OpenGL、Metal、Vulkan封装了GrGLOpsRenderPass, GrMTOpsRenderPass, GrVKOpsRenderPass三个类。基于苹果平台CoreText字体库和开源字体FreeType封装了类SkScalerContext_Mac和SkScalerContext_FreeType。
Skia的内部依赖和层级构造解说结束,上面重点解说skia的图形、文字和图片的绘制原理。
3. 图形绘制原理
Skia反对绘制的图形泛滥,包含圆形、椭圆、矩形、贝塞尔曲线等。下文重点剖析图形的CPU和GPU两种渲染模式的原理。
3.1 图形CPU渲染原理
曲线的绘制波及的数学知识较多,本文不再开展,上面以绘制实心矩形为例阐明原理进行分析。
1)调用画布的绘图API
应用层调用画布SKCanvas的drawRect函数,传入左上角和右下角顶点坐标。
2)选用对应的设施的绘图API
因为抉择的是CPU渲染模式,故调用SKBitmapDevice的矩形绘图函数drawRect实现。
3)图形示意
所有的图形可分解成上面几种根本矢量图形的组合,矩形可示意成四条直线的组合。
曲线类型 | 参数 | 用处 |
---|---|---|
直线(一次贝塞尔曲线) | 终点坐标,起点坐标 | 可示意绘制三角形、四边形等多边形 |
圆锥曲线 | 终点坐标,起点坐标,椭圆参数 | 示意椭圆、圆弧、圆形 |
二次贝塞尔曲线 | 三个控制点 | 示意TrueType字体、抛物线等曲线 |
三次贝塞尔曲线 | 四个控制点 | 示意OpenType字体和其余曲线 |
4)绘制算法实现
矢量图转成位图的过程称为光栅化。带填充的矩形光栅化过程比较简单,能够分解成绘制多条横线。
5)横线线绘制算法
每条横向的画法通过SKBlitter:: blitH实现。接口定义如下:
virtual void blitH(int x, int y, int width);
性能:从坐标x,y开始,间断写入宽度为width的RGB色彩值。
6)内存中写色彩数据
通过追踪代码,发现上文中的横线绘制函数调用的是memsetT函数(内存赋值)实现。参数如下:
static void memsetT(T buffer[], T value, int count)
目前x86和ARM处理器是32或者64位,一般的指令一次最多写入32位 或者64位数据,一个带通明通道的点通常占4个字节,相当于一次只能绘制1到2个点,效率比拟低。Skia从性能角度思考,采纳的SIMD指令集来减速内存操作。
在X86平台,调用SSE、AVX、AVX2等指令集实现内存赋值,SSE反对一次操作128位操作,AVX/AVX2反对一次操作256位数据,ARM处理器的NEON指令集反对一次操作128位数据。
3.2 图形GPU渲染原理
GPU的并行运算能力强,目前大部分挪动设施都采纳的是GPU渲染。
skia GPU渲染流程如下:
1)发动绘图,先调用SKCanvas的绘图函数drawRect,传入左上角和右下角顶点坐标。
2)调用GPU设施的绘图函数SKGPUDevice::drawRect。
3)采纳命令模式,将GPU绘图操作封装成类GrOpsTask的实例。
4)依据软硬件平台的不同选用不同的底层API。
OpenGL(Open Graphics Library”)是目前应用最宽泛的跨平台图形变成接口,跨平台个性好,大部分操作系统和GPU。Skia在大部分平台采纳OpenGL实现GPU绘图,少部分平台调用Metal和vulkan。
Metal是苹果公司2014年推出的和 OpenGL相似的面向底层的图形编程接口,只反对iOS。对软硬件有要求,要求硬件苹果A7及当前,操作系统iOS 10及以上。Metal实践上性能比OpenGL性能强,故新设施中开启Metal可进步性能。例如Flutter中已启用了metal反对,详情参考https://github.com/flutter/fl...。
Vulkan是新一代跨平台的2D和3D绘图利用程序接口(API),旨在取代OpenGL,实践上性能强于OpenGL。自 Android 7.0 开发者预览版开始,Google便在零碎平台中增加了对Vulkan的API反对。目前Skia的GPU渲染模式已用vulkan实现了一套,但存在一些bug。具体参考https://skia.org/user/special...。
Skia对上述三种图形接口进行了封装,屏蔽了不同底层图形API接口的差别。OpenGL接口的封为GrGLOpsRenderPass,Metal的封装层为GrMTOpsRenderPass,Vuklan的封装层为 GrVKOpsRenderPass。
5)通过GPU实现残余绘图操作。
上面以OpenGL为例阐明。接口封装层调用OpenGL glDrawArray绘制矩形,之后在渲染流水线中实现顶点变换、光栅化和着色,最初送帧缓冲显示。渲染流水线如下图所示:
Metal、vulkan的渲染流水线这里不再开展。
4. 字体绘制原理
字体无奈间接显示在屏幕上,须要解析成位图或者矢量图能力绘制。Skia的字体解析实现跟进平台差别有所不同,mac和iOS平台调用coreText库,安卓平台调用开源库freeType。
FreeType是一个用C语言实现的,收费的高质量可移植字体引擎,反对点阵字体PCF、BDF和矢量字体TrueType、freeType等字体。
4.1 skia点阵字体绘制原理
Skia反对的点阵字体有PCF、BDF格局。点阵存储的是多张位图,常见的有1616,2424,32*32等尺寸,解码和显示简略,毛病是放大后有锯齿。
1) skia点阵文字显示代码:
SkFont font;font.setEdging(SkFont::Edging::kAlias);font.setSize(40);const char text[] = "Click this link!";size_t byteLength = strlen(static_cast<const char*>(text));canvas->drawSimpleText(text, byteLength, SkTextEncoding::kUTF8, x, y, font, SkPaint());
文字绘制流程如下:
点阵字体最初解析成了位图,而后依据平台不同选用CPU或者GPU渲染进去。Skia为了进步字体显示速度,对字体的解析后果做了内存缓存。
4.2 矢量字体绘制原理
矢量字体次要通过贝塞尔曲线形容字体,存储空间小,但渲染简单,还须要导入字体库文件。Skia反对的矢量字体有tff(true type font)和otf(open true type)格局。前者采纳二次贝塞尔曲线示意,后者采纳三次贝塞尔曲线示意。Skia中矢量文字绘制代码如下:
SkPaint p; p.setStyle(SkPaint::kStroke_Style); p.setStrokeWidth(10); p.setARGB(0xff, 0xbb, 0x00, 0x00); sk_sp<SkTypeface> ttf = MakeResourceAsTypeface("fonts/Stroking.ttf");SkFont font(ttf, 100);if (ttf) { SkFont font(ttf, 100); canvas->drawString("○◉ ", 10, 100, font, p);}
绘制流程如下:
矢量字体的绘制流程跟点阵字体大部分一样,不同之处在于解析后果为贝塞尔曲线。贝塞尔曲线的渲染算法略微简单,参考文章https://www3.cs.stonybrook.ed...
5. 图片绘制原理
5.1 Skia位图绘制原理
skia提供了showBitmap函数可间接显示位图。位图渲染模式跟矢量图形相似,分为CPU渲染和GPU渲染。位图的CPU渲染跟实心矩形的渲染原理相似,通过SIMD指令集将位图内存一行一行拷贝到指定内存缓存中。GPU渲染模式通过调用OpenGL、Metal、vulkan的纹理贴图函数实现。
5.2 Skia压缩格局图片绘制原理
位图因为占用空间大,应用频率低,大部分状况下应用压缩格局图片。Skia反对的压缩格局图片如下:
格局 | 长处 | 毛病 | 场景 | 依赖解码库 |
---|---|---|---|---|
gif | 文件小,反对动画、通明,无兼容性问题 | 只反对种颜色,且透明度只有1位,有白边和锯齿 | 简略的动图 | libgifcodec |
jpg | 反对位真彩色,压缩率高 | 有损压缩,不反对通明通道 | 色调丰盛的图片 | libjpeg-turbo |
png | 无损压缩,反对通明,简略图片尺寸小 | 不反对动画,压缩率低 | logo/icon/透明图 | libpng |
webp | 比jpeg压缩率更高,反对有损和无损压缩,反对动画、通明通道 | 谷歌自研格局,局部平台不反对。 | 反对有损和无损压缩格局,反对动画 | libwebp |
压缩格局图片应用代码如下:
SkCanvas c(dst); SkBitmap src; SkImageDecoder::DecodeFile(“test.jpg”, &src);// 图片解码 c.drawBitmap(src, 0, 0, NULL); //图片显示
显示流程如下图所示:
读取文件后,先通过文件头判断图片类型,而后送相应的图片库解码成位图图像后,再通过CPU或者GPU渲染。
6. skia小结
Skia是一个功能强大的跨平台图形库,能绘制矩形、圆形、贝塞尔曲线等矢量图,绘制点阵字体和矢量字体,显示jpeg、png、gif、webp等图片,同时性能好,从算法和硬件两个层面进行了优化。skia反对多种软硬件平台,除了Android、chrome、Flutter等产品间接将其作为图形引擎,也反对iOS、windows等操作系统。Skia性能较多,还反对lottie动画,图像特效,还引入了两头语言SKGL,限于篇幅,这里不再开展。
参考文档:
iOS高性能绘图:https://medium.com/@almalehde...
Core Animation 编程指南:https://developer.apple.com/l...
skia编译办法:https://skia.org/user/build
Skia技术路线:
https://docs.google.com/docum...
SKGL阐明:https://github.com/google/ski...。
Skia源码:https://skia.googlesource.com...
Skia 百科:https://zh.wikipedia.org/zh-c...
字体介绍:http://www.klayge.org/wiki/index.php/%E5%AD%97%E4%BD%93%E7%B3%BB%E7%BB%9F
FreeType官网:https://www.freetype.org/
png压缩原理:https://www.jianshu.com/p/5ad...
GPU渲染流水线:https://zhuanlan.zhihu.com/p/...
Vukan介绍:https://www.khronos.org/asset...
ARM Mali GPU介绍:https://developer.arm.com/sol...
Vulkan和OpenGL ES比拟:https://community.arm.com/dev...
Qualcomm发表Adreno 530 GPU反对vulkan:https://www.qualcomm.cn/news/...
https://www.adobe.com/content...