jxt1234and2010的专栏

概念

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:顺序记录方式,不做处理

任务记录的一些注意点回放过程

Created with Raphal 2.1.2SkCanvas::drawPictureGPUDeviceEXPERIMENTAL_drawPictureSkPicture::drawSkPicturePlayBack::drawyesno

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

延迟渲染——SkDeferredCanvas

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

/** \class SkDeferredCanvasSubclass of SkCanvas that encapsulates an SkPicture or SkGPipe for deferreddrawing. The main difference between this class and SkPictureRecord (thecanvas provided by SkPicture) is that this is a full drop-in replacementfor SkCanvas, while SkPictureRecord only supports draw operations.SkDeferredCanvas will transparently trigger the flushing of deferreddraw 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() {}//当前绘制任务被取消掉时调用,可以用来清除额外的资源 };

没有什么可凭仗,只有他的好身体,没有地方可去,

jxt1234and2010的专栏

相关文章:

你感兴趣的文章:

标签云: