文档章节

Skia深入分析9——延迟渲染和显示列表

jxt1234
 jxt1234
发布于 2015/10/15 18:06
字数 1058
阅读 11
收藏 0

概念

Android的硬件加速,是先将绘制命令存储起来,然后回放,作为软件绘制的引擎Skia中同样有这样的机制。在Android 4.4的版本中又加入了延迟渲染的Canvas,它相当于默认使用显示列表的Canvas。
先得到显示列表,再进行渲染,便有机会基于绘制API的整体情况做优化调度。比如使用GPU加速,裁剪过度绘制等。从原理上看,很可能在这一层级做比较大的效率提升,不过,由于Android既定的渲染框架限制,尽管Google在这方面做的东西很多,生效场景很少,收益也很有限。

显示列表——SkPicture

用法

SkPicture的用法如下:

const int w = 720;
const int h = 1280;
SkPictureRecorder recoder;
SkCanvas* displayCanvas =recoder.beginRecording(w, h, NULL, 0);//这里得到一个专门用来记录的SkCanvas
/*调用SkCanvas的API,但起的是记录命令的作用*/
displayCanvas->drawRect(...);
displayCanvas->drawSprite(...);
displayCanvas->clipRect(...);
displayCanvas->drawText(...);
/*终止绘制API的调用,得到SkPicture*/
SkPicture* picture = recoder.endRecording();

SkBitmap dst;
dst.allocN32Pixels(w, h);
SkCanvas canvas(dst);
c.drawPicture(picture);//用 picture->draw(&canvas)也可以,但最好用前面一种方法
/*此时内容已经在 dst 上面了*/
/*也可以用GPU的方式创建Canvas渲染,怎么用可参考上一篇,*/
picture->unref();//释放 picture

显示列表的记录

记录方案

我们先看一下beginRecording函数:

SkCanvas* SkPictureRecorder::beginRecording(int width, int height,
                                            SkBBHFactory* bbhFactory /* = NULL */,
                                            uint32_t recordFlags /* = 0 */) {
    this->reset();  // terminate any prior recording(s)
    fWidth = width;
    fHeight = height;

    const SkISize size = SkISize::Make(width, height);

    if (NULL != bbhFactory) {
        SkAutoTUnref<SkBBoxHierarchy> tree((*bbhFactory)(width, height));
        SkASSERT(NULL != tree);
        fPictureRecord = SkNEW_ARGS(SkBBoxHierarchyRecord, (size, recordFlags, tree.get()));
    } else {
        fPictureRecord = SkNEW_ARGS(SkPictureRecord, (size, recordFlags));
    }

    fPictureRecord->beginRecording();
    return this->getRecordingCanvas();
}

如上述代码所看到的,主要有两种记录方法,一种是SkBBoxHierarchyRecord,另一种是SkPictureRecord。
SkBBoxHierarchyRecord:以 R-Tree 算法组织绘制任务,这种方式需要计算绘制区域的包围盒,在记录文本绘制命令时这个计算还是相当耗时的,采用R-Tree组织的好处是绘制指定区域时可以快速找出相关的绘制任务。
SkPictureRecord:顺序记录方式,不做处理

任务记录的一些注意点

  1. 图片存储时需要判断该图片是否是可变的(immutable),若不是可变的,需要复制图片到缓存。
  2. SkPaint的Shader及各种特效只存储指针/引用。需要应用自行保证不做更改。
  3. SkPath也是复制了整个SkPath及SkPathRef中所有点到缓存,注意到需要记录和恢复的比较复杂的类,需要实现 writeToMemory 和 readFromMemory 两个方法
  4. 使用SkWriter32写入,在结束记录后将其内容全部复制到 SkPicture

回放过程

Created with Raphaël 2.1.2 SkCanvas::drawPicture GPUDevice EXPERIMENTAL_drawPicture SkPicture::draw SkPicturePlayBack::draw yes no

鉴于GPU绘图时还需要把之前存储在缓存区的bitmap、path等上传为纹理或vbo,直接以纹理/vbo建立缓存机制效率更高,所以有EXPERIMENTAL_drawPicture一条分支,不过目前看来还是实验阶段。
具体如何回放的看src/core/SkPicturePlayBack.cpp中SkPicturePlayBack::draw函数即可,不详述。

延迟渲染——SkDeferredCanvas

Google的注释很清楚地解释了这个类的作用。这个类的用法和原始的SkCanvas基本一致。只是设置了延迟属性后,需要在使用绘图结果前flush一下。

/** \class SkDeferredCanvas
    Subclass of SkCanvas that encapsulates an SkPicture or SkGPipe for deferred
    drawing. The main difference between this class and SkPictureRecord (the
    canvas provided by SkPicture) is that this is a full drop-in replacement
    for SkCanvas, while SkPictureRecord only supports draw operations.
    SkDeferredCanvas will transparently trigger the flushing of deferred
    draw operations when an attempt is made to access the pixel data.
*/

用法如下:

SkSurface* surface =  SkSurface::NewRasterPMColor(720, 1280);
SkDeferredCanvas* canvas = SkDeferredCanvas::Create(surface);
canvas->clear(0x0);
canavs->setDeferredDrawing(true);
/*......*/
canvas->drawRect(...);
canvas->drawText(...);
/*......*/
canvas->flush();//需要这一步来保持绘制完成
canvas->unref();
surface->unref();

另外可以为SkDeferredCanvas设置一个监听器NotificationClient:
class NotificationClient {
public:
virtual ~NotificationClient() {}
virtual void prepareForDraw() {}//准备开始渲染时调用
virtual void storageAllocatedForRecordingChanged(
size_t /newAllocatedStorage/) {}
virtual void flushedDrawCommands() {}//当前绘制任务清空时调用,可以是拿去编码/显示/后处理等
virtual void skippedPendingDrawCommands() {}//当前绘制任务被取消掉时调用,可以用来清除额外的资源
};

版权声明:本文为博主原创文章,未经博主允许不得转载。

© 著作权归作者所有

共有 人打赏支持
jxt1234
粉丝 5
博文 36
码字总数 41634
作品 0
杭州
HWUI(硬件加速绘制UI)简介

原址 简介:hwui主要是android用于2d硬件绘图而加入的一个模块,在hwui之前,android主要是用skia来进行软件绘制,后由于绘制性能等问题,现在android的绘图几乎都是使用了hwui硬件加速绘图。...

u010164190
04/27
0
0
万万没想到——flutter这样外接纹理

前言 记得在13年做群视频通话的时候,多路视频渲染成为了端上一个非常大的性能瓶颈。原因是每一路画面的高速上屏(PresentRenderBuffer or SwapBuffer 就是讲渲染缓冲区的渲染结果呈现到屏幕...

闲鱼技术
08/22
0
0
Skia图片解码模块流程分析

我在在PPAPI插件中使用Skia绘图中说可以在PPAPI插件内使用Skia来绘图。这里面会有一个与色彩空间(像素格式)相关的问题。在那篇文章里我们在PPAPI中使用PPBImageData创建2D图像缓冲区时使用...

foruok
2016/02/22
0
0
我们是如何构建Twitter Lite的

摘要:非常激动向大家介绍 Twitter Lite ,一个已经能在mobile.twitter.com上体验的渐进式网页应用。 Twitter Lite速度更快,应用了响应式设计,流量消耗更少,占用的存储空间也更小,并且在...

众成翻译
01/02
0
0
谷歌开发者大会2018实录——Flutter篇

摘要 一年一度的google开发者大会即将开始,今年的大会上,flutter会以什么样的形态展现在大家面前,1.0版本何时发布?有哪些应用场景?性能的表现如何?国内的flutter开发者生态又是怎样的?...

阿里云云栖社区
09/27
0
0

没有更多内容

加载失败,请刷新页面

加载更多

这周撸了两款小程序,分享下关键点。

本周撸了两款小程序,在这里总结下开发过程中的小经验,希望对大家有用。 小程序端 我们先说小程序要注意的地方。 ##默认入口转发问题 当一个小程序Page的js文件中存在 onShareAppMessage 方...

阿北2017
29分钟前
2
0
物联网技术很新吗?不!都是旧技术

通常,当我们想到物联网时,我们会想到新的、令人兴奋的现代技术。毕竟,还有什么比不用起床就能通过智能手机告诉咖啡机开始煮晨杯的“未来”更重要呢? 多亏了物联网,我们可以在世界任何地方...

linuxCool
36分钟前
2
0
利用责任链模式设计一个拦截器

前言 近期在做 Cicada 的拦截器功能,正好用到了责任链模式。 这个设计模式在日常使用中频率还是挺高的,借此机会来分析分析。 责任链模式 先来看看什么是责任链模式。 引用一段维基百科对其...

crossoverJie
54分钟前
2
0
属性动画

透明度 alpha 平移translationX/translationY 旋转 rotation 缩放 scaleX/scaleY 多个动画一起AnimatorSet 透明度 ObjectAnimator oa = ObjectAnimator.ofFloat(iv,"alpha" ......

lanyu96
55分钟前
1
0
Docker和Kubernetes如何让DevOps更具效力

缩短time-to-makrt对于任何一家企业都至关重要,这直接决定了客户满意度、市场竞争力乃至盈利能力。但在部署应用时,大多数企业内的IT团队都或多或少会遇到Dev和Ops之间的问题,这两个部门围...

好雨云帮
今天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部