前面已经介绍过同步帧状态。 硬件渲染一次任务整体流程图.jpg
继续分析绘制一帧的相关内容。
绘制一帧
从CanvasContext#draw方法开始。
void CanvasContext::draw() {
SkRect dirty;
EGLint width, height;
mEglManager.beginFrame(mEglSurface, &width, &height);
//如果宽高与OpenGLRenderer内部CanvasState保存的不同。
//重新设置到CanvasState中。
if (width != mCanvas->getViewportWidth() || height != mCanvas->getViewportHeight()) {
//初始化CanvasState的宽高。
mCanvas->setViewport(width, height);
dirty.setEmpty();
} else if (!mBufferPreserved || mHaveNewSurface) {
dirty.setEmpty();//新Surface,脏区域置空
} else {
if (!dirty.isEmpty() && !dirty.intersect(0, 0, width, height)) {
//区域与该View无交集,置空
dirty.setEmpty();
}
profiler().unionDirty(&dirty);
}
//不空,则开始刷脏区域,第一步
if (!dirty.isEmpty()) {
mCanvas->prepareDirty(dirty.fLeft, dirty.fTop,
dirty.fRight, dirty.fBottom, mOpaque);
} else {
//刷新整个视图。
mCanvas->prepare(mOpaque);
}
Rect outBounds;
//第二步,OpenGLRenderer绘制
mCanvas->drawRenderNode(mRootRenderNode.get(), outBounds);
profiler().draw(mCanvas);
bool drew = mCanvas->finish();
mCurrentFrameInfo->markSwapBuffers();
if (drew) {
//第三步
swapBuffers(dirty, width, height);
}
mCurrentFrameInfo->markFrameCompleted();
mJankTracker.addFrame(*mCurrentFrameInfo);
mRenderThread.jankTracker().addFrame(*mCurrentFrameInfo);
}
CanvasContext#initialize方法,初始化OpenGLRenderer。即内部mCanvas,draw方法中很多依赖OpenGLRenderer完成。
绘制一帧主要三个部分。
第一部分:OpenGLRenderer#prepareDirty方法。
准备刷新视图脏区域,若脏区域为空,通过prepare方法调用prepareDirty刷新视图整个宽高区域,入参包括不透明标志(不透明为true)。
第二部分:OpenGLRenderer#drawRenderNode方法。
根节点RootRenderNode绘制。入参是根节点。
第一部分 prepareDirty分析
prepareDirty方法流程图如下。 OpenGLRenderer的prepareDirty流程解析.jpgvoid OpenGLRenderer::prepareDirty(float left, float top,
float right, float bottom, bool opaque) {
setupFrameState(left, top, right, bottom, opaque);
//从CanvasState的Snapshot链表获取Snapshot对象。
if (currentSnapshot()->fbo == 0) {
mRenderState.blend().syncEnabled();
updateLayers();
} else {
//fbo存在,初始化opengl状态
startFrame();
}
}
由图可知,分两步
1:setupFrameState,初始化SaveStack。
2:从CanvasState获取当前Snapshot,判断fbo。
void OpenGLRenderer::setupFrameState(float left, float top,
float right, float bottom, bool opaque) {
//清理Cache,包括TextureCache、PathCache
//和PatchCache。
mCaches.clearGarbage();
mState.initializeSaveStack(left, top, right, bottom, mLightCenter);
mOpaque = opaque;//CanvasContext中的mOpaque
mTilingClip.set(left, top, right, bottom);
}
OpenGLRenderer内部CanvasState。
void CanvasState::initializeSaveStack(float clipLeft, float clipTop,
float clipRight, float clipBottom, const Vector3& lightCenter) {
//mSnapshot指向链表表头
mSnapshot = new Snapshot(mFirstSnapshot,
SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
mSnapshot->setClip(clipLeft, clipTop, clipRight, clipBottom);
//mCanvas是OpenGLRenderer
mSnapshot->fbo = mCanvas.getTargetFbo();
mSnapshot->setRelativeLightCenter(lightCenter);
mSaveCount = 1;//Save栈的初始数量设为1。
}
CanvasState构造方法创建第一个Snapshot,此方法再创建一个,链表表头。初始化Snapshot的fbo,帧缓存对象。
OpenGLRenderer结构关系图。
OpenGLRenderer相关结构关系图.jpg OpenGLRenderer与LayerRenderer。1:LayerRenderer继承OpenGLRenderer,OpenGLRenderer继承CanvasStateClient。他们内部均包含CanvasState,指向创建它的CanvasStateClient。
2:OpenGLRenderer/LayerRenderer构造方法创建CanvasState。
3:当前CanvasState#Snapshot的fbo由与CanvasState关联的render设置,render是LayerRenderer或OpenGLRenderer。
4:他们均重写基类CanvasStateClient#getTargetFbo方法。返回值不同。
OpenGLRenderer:返回0,因此Snapshot的fbo被设置为0。
LayerRenderer:返回内部Layer的fbo。
CanvaState的render是OpenGLRenderer,不是LayerRenderer,因此,Snapshot初始化fbo是0。
官方注释
framebuffer渲染器(fbo是0)将(使用fbo帧缓冲对象)会推迟每一个Layer的display list,等待直到第一个渲染命令,开始绘制帧。
Blend#syncEnabled同步方法根据mEnabled状态去调用glEnable(GL_BLEND)后者glDisable(GL_BLEND),然后触发OpenGLRenderer#updateLayers方法更新Layers。
Layer渲染器(fbo非0)立即开始渲染,startFrame方法。
OpenGLRenderer#updateLayers分析
void OpenGLRenderer::updateLayers() {
int count = mLayerUpdates.size();
if (count > 0) {
for (int i = 0; i < count; i++) {
Layer* layer = mLayerUpdates.itemAt(i).get();
updateLayer(layer, false);
}
if (CC_UNLIKELY(Properties::drawDeferDisabled)) {
mLayerUpdates.clear();
mRenderState.bindFramebuffer(getTargetFbo());
}
}
}
刷新Layer数组中每一个Layer。LayerRenderer未重写此方法。
在OpenGLRenderer中的Layer,就是OpenGLRenderer渲染器最终将要渲染的Layer了。
那么,问题,数组元素Layer是哪里来的?
OpenGLRenderer#pushLayerUpdate方法负责向数组push元素(保证不相等的Layer对象)。
两个地方触发该方法。
1:CanvasContext#processLayerUpdate方法。
2:RenderNode#pushLayerUpdate(TreeInfo& info)方法。
第一个方法,前面也提到过,针对TextureView视图。入参是DeferredLayerUpdater,对应上层HardwareLayer。
这里不详细探讨TextureView视图中HardwareLayer的创建过程。前面也说过,Layer无deferredUpdateScheduled标志,不会被加入OpenGLRenderer的Layer数组。
第二个方法,前面提到过,发生在每一个RenderNode节点prepareTreeImpl方法中,设置LAYER_TYPE_HARDWARE的视图,LayerRenderer负责创建RenderNode节点的内部Layer,Layer也包含RenderNode。
LayerRenderer结构关系图。
因此,内部Layer数组与RenderNode的Layer有关。
刷新每一个Layer。
bool OpenGLRenderer::updateLayer(Layer* layer, bool inFrame) {
//RenderNode内部Layer
if (layer->deferredUpdateScheduled && layer->renderer
&& layer->renderNode.get() && layer->renderNode->isRenderable()) {
if (inFrame) {
endTiling();
debugOverdraw(false, false);
}
//Layer渲染时,对drawDeferDisabled标志的判断与drawRenderNode方法一样。
//此外,这里还要判断inFrame标志。
if (CC_UNLIKELY(inFrame || Properties::drawDeferDisabled)) {
layer->render(*this);
} else {
layer->defer(*this);
}
if (inFrame) {
resumeAfterLayer();
startTilingCurrentClip();
}
layer->debugDrawUpdate = Properties::debugLayersUpdates;
layer->hasDrawnSinceUpdate = false;
return true;
}
return false;
}
刷新某一Layer应满足以下条件。
- 1:deferredUpdateScheduled标志。
注意一下:
前面文章也介绍过,在源码中,只有Layer#updateDeferred方法设置此标志。在数据同步时,RenderNode节点初始化内部Layer(上层设置条件下),此方法除了设置该标志,还会创建LayerRenderer。
这一切的前提是上层视图设置LAYER_TYPE_HARDWARE标志。
TextureView视图的底层Layer并不会调用这个方法。**
- 2:Layer内部,RenderNode存在且DisplayListData数据不空。
节点内部Layer由LayerRenderer#createRenderLayer创建。只有这种Layer才有RenderNode,将LayerRenderer设置内部渲染器。
- 3:Layer渲染器renderer存在,是LayerRenderer。
OpenGLRenderer#updateLayer方法刷新的Layer与视图TextureView的底层Layer无关。
目前介绍两种Layer,还有一种Layer,由OpenGLRenderer#的createLayer方法创建。OpenGLRenderer#saveLayer触发。上层Canvas#saveLayer时,触发一次SaveLayerOp操作,此操作会导致渲染器#saveLayer。
三种Layer如下图所示。 三种Layer.jpg综上:
第一种Layer,上层saveLayer方法通过createLayer方法创建Layer。Layer对象也会通过fboLayer标志绑定fbo。
第二种Layer,TextureView视图,LayerRenderer#createTextureLayer创建。
第三种Layer,上层视图LAYER_TYPE_HARDWARE,底层RenderNode节点pushLayerUpdate时创建Layer,Layer内部有LayerRenderer渲染器和RenderNode节点。
LayerRenderer#createRenderLayer方法创建。此Layer一定会绑定一个fbo。
fbo在FboCache中通过glGenFramebufferss生成帧缓冲区对象。
Layer渲染
OpenGLRenderer#updateLayer(Layer* layer, bool inFrame)方法,触发Layer渲染。
支持两种渲染defer与render,defer还是render?
若drawDeferDisabled禁止延迟或入参inFrame是true,render立即渲染,否则defer延迟合并。这两种情况分两个讨论,因为,每个操作命令均支持render和defer。
在OpenGLRenderer#drawLayer处,触发updateLayer时,inFrame是true。
下面先分别简单介绍。
Layer立即渲染
void Layer::render(const OpenGLRenderer& rootRenderer) {
//设置Layer的宽高,保存在CanvasState中
renderer->setViewport(layer.getWidth(), layer.getHeight());
//专门为Layer渲染的对象LayerRenderer。
//与普通渲染一样的方法逻辑。
renderer->prepareDirty(dirtyRect.left, dirtyRect.top,
dirtyRect.right, dirtyRect.bottom,!isBlend());
renderer->drawRenderNode(renderNode.get(), dirtyRect,
RenderNode::kReplayFlag_ClipChildren);
renderer->finish();
dirtyRect.setEmpty();
deferredUpdateScheduled = false;
renderNode = nullptr;
}
注意:Layer内部render是LayerRenderer。Layer中不一定会有RenderNode。
1:LayerRenderer#prepareDirty方法准备脏区域,这时触发prepareDirty的是LayerRenderer,因此,currentSnapshot的fbo>0,不会再进入updateLayers。
2:OpenGLRenderer#drawRenderNode,前面也会调用这个方法,绘制的是CanvasContext的根RenderNode节点,这里绘制Layer内部RenderNode节点。马上要分析的第二部分。
3:最后,将恢复deferredUpdateScheduled延迟标志,渲染节点renderNode置null,代表Layer渲染结束。
Layer延迟合并
void Layer::defer(const OpenGLRenderer& rootRenderer) {
const float width = layer.getWidth();
const float height = layer.getHeight();
if (dirtyRect.isEmpty() || (dirtyRect.left <= 0 && dirtyRect.top <= 0 &&
dirtyRect.right >= width && dirtyRect.bottom >= height)) {
dirtyRect.set(0, 0, width, height);//设置Layer的脏区域
}
//创建DeferredDisplayList
//封装在结构体DeferStateStruct。
deferredList.reset(new DeferredDisplayList(dirtyRect));
DeferStateStruct deferredState(*deferredList, *renderer,
RenderNode::kReplayFlag_ClipChildren);
//设置Layer的宽高,保存在CanvasState中。
renderer->setViewport(width, height);
renderer->setupFrameState(dirtyRect.left, dirtyRect.top,
dirtyRect.right, dirtyRect.bottom, !isBlend());
renderNode->computeOrdering();
renderNode->defer(deferredState, 0);
deferredUpdateScheduled = false;
}
创建一个DeferredDisplayList数据,封装在结构体DeferStateStruct,Layer内部RenderNode#refer方法。
综上:Layer渲染的本质是,利用内部LayerRenderer,渲染内部RenderNode的过程。
RenderNode渲染本质
延迟合并:本质是RenderNode#defer方法。
立即渲染:本质是LayerRenderer的drawRenderNode方法,即OpenGLRenderer的drawRenderNode方法。在该方法中,再次判断RenderNode#replay还是defer。
总结
prepareDirty方法
初始化帧状态:初始化画布状态,创建Snapshot,设置Snapshot的区域,对于帧缓存对象fbo,只有LayerRenderer才会有,普通视图暂不考虑,初始化fbo是0。初始化当前Save层值是1。
OpenGLRenderer中Layer刷新:普通视图暂不考虑。针对上层设置LAYER_TYPE_HARDWARE的RenderNode节点。
下面,就看第二部分OpenGLRenderer#drawRenderNode方法吧。
第二部分,drawRenderNode分析
每个节点代表一个视图数据,绘制视图某一个dirty区域内容。该方法真正开始绘制节点。以CanvasContext#draw方法绘制RootRenderNode根节点为主进行介绍。
//this看情况,也可能是LayerRenderer
void OpenGLRenderer::drawRenderNode(RenderNode* renderNode, Rect& dirty, int32_t replayFlags) {
//renderNode和mDisplayListData不空
if (renderNode && renderNode->isRenderable()) {
renderNode->computeOrdering();
//为true时,则禁止defer,禁止合并
if (CC_UNLIKELY(Properties::drawDeferDisabled)) {
startFrame();
ReplayStateStruct replayStruct(*this, dirty, replayFlags);
renderNode->replay(replayStruct, 0);//立即绘制
return;
}
//合并延迟
bool avoidOverdraw = !Properties::debugOverdraw;
DeferredDisplayList deferredList(mState.currentClipRect(), avoidOverdraw);
DeferStateStruct deferStruct(deferredList, *this, replayFlags);
renderNode->defer(deferStruct, 0);
flushLayers();
startFrame();
deferredList.flush(*this, dirty);
} else {
startFrame();
}
}
前提条件待绘制节点和DisplayListData信息均不空。
(有时Layer中的RenderNode是空,在Layer渲染时注意。)
RenderNode立即渲染与延迟合并流程图如下。
二者区别。
立即渲染:在遍历整个操作过程中,遇到一个DrawOp绘制操作或StateOp状态操作,立即写入Opengl。
延迟合并渲染:将操作收集DeferredDisplayList,当遇到flush时,集中批次写入Opengl。
#reply方法
void RenderNode::replay(ReplayStateStruct& replayStruct, const int level) {
ReplayOperationHandler handler(replayStruct, level);
issueOperations<ReplayOperationHandler>(replayStruct.mRenderer, handler);
}
#defer方法
void RenderNode::defer(DeferStateStruct& deferStruct, const int level) {
DeferOperationHandler handler(deferStruct, level);
issueOperations<DeferOperationHandler>(deferStruct.mRenderer, handler);
}
两者均初始化handler,ReplayOperationHandler与DeferOperationHandler,触发issueOperations方法,defer与reply模式。利用两个handler分别触发Op的reply与defer。
issueOperations方法将多次触发handler(XxxOp,saveCount,clipToBounds)方法。
两个handler内联函数如下。
//ReplayOperationHandler内联函数
inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) {
operation->replay(mReplayStruct, saveCount, mLevel, clipToBounds);
}
//DeferOperationHandler内联函数。
inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) {
operation->defer(mDeferStruct, saveCount, mLevel, clipToBounds);
}
综上
ReplayOperationHandler(XxxOp,..)的本质是XxxOp#replay方法。
DeferOperationHandler(XxxOp,..)的本质是XxxOp#defer方法。
RenderNode#issueOperations方法。
template <class T>
void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) {
//内部mLayer存在且
//mLayer的renderer与传入的OpenGLRenderer不同
const bool drawLayer = (mLayer && (&renderer != mLayer->renderer.get()));
...
LinearAllocator& alloc = handler.allocator();
int restoreTo = renderer.getSaveCount();
handler(new (alloc) SaveOp(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag),
PROPERTY_SAVECOUNT, properties().getClipToBounds());
bool quickRejected = properties().getClipToBounds()
&& renderer.quickRejectConservative(0, 0, properties().getWidth(), properties().getHeight());
if (!quickRejected) {
Matrix4 initialTransform(*(renderer.currentTransform()));
renderer.setBaseTransform(initialTransform);
//第三种Layer的情形,只考虑Layer绘制
if (drawLayer) {
handler(new (alloc) DrawLayerOp(mLayer, 0, 0),
renderer.getSaveCount() - 1, properties().getClipToBounds());
} else {//针对给View节点的操作数据
暂时省略...后面介绍
}
}
handler(new (alloc) RestoreToCountOp(restoreTo),
PROPERTY_SAVECOUNT, properties().getClipToBounds());
}
ReplayOperationHandler或DeferOperationHandler ,触发的XxxOp方法不同。
renderer是什么呢?
若是在CanvasContext#draw中绘制根节点。renderer即OpenGLRenderer。
若是在Layer#render中绘制RenderNode,renderer即LayerRenderer。
开始时有一个判断。
若RenderNode内Layer存在且LayerRenderer。前面已经介绍过,这属于第三种Layer的情况,仅考虑DrawLayerOp绘制。Layer内部render是LayerRenderer,一定存在fbo。
DrawLayerOp继承DrawOp。DrawLayerOp#defer或replay。
DrawLayerOp未重写replay,重写applyDraw。在DrawOp#replay触发子类重写的applyDraw。
DrawLayerOp#applyDraw方法。
//封装在ReplayStateStruct的OpenGLRenderer。
virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
renderer.drawLayer(mLayer, mX, mY);
}
DrawLayerOp未重写defer,父类DrawOp的defer方法。
virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level,
bool useQuickReject) override {
if (mQuickRejected && CC_LIKELY(useQuickReject)) {
return;
}
deferStruct.mDeferredList.addDrawOp(deferStruct.mRenderer, this);
}
将DrawLayerOp操作加入到DeferStateStruct结构体的DeferredList列表。其他DrawOp子类defer也是同样的逻辑。
总结
OpenGLRenderer#drawRenderNode的本质是
将绘制任务交给入参RenderNode节点的defer与replay方法完成。前提是RenderNode存在且DisplayListData数据不空。
1:RenderNode的defer与replay将触发XxxOp的对应方法。
2:不仅DrawLayerOp,每一种DrawOp子类均重写applyDraw,触发OpenGLRenderer#drawXxx。
3:Op#defer向DeferredDisplayList加入操作,Op#reply申请applyDraw绘制。
4:defer后,相关Op已保存在DeferredDisplayList中,由DeferredDisplayList#flush刷新。
reply操作,最终均走到OpenGLRenderer#drawXXX方法,然后通过OpenGLRenderer::renderGlop(const Glop& glop, GlopRenderType type),写入opengl。
defer操作
DeferStateStruct结构体的DeferredDisplayList列表存储了绘制操作。
到这里,绘制一帧基本流程介绍完成。
接下来,看一下Save与RestoreToCount操作,以及延迟合并的逻辑。
SaveOp与RestoreToCountOp操作
RenderNode#issueOperations方法开始于结束会触发一次
一次SaveOp和RestoreToCountOp操作,他们均继承StateOp。
issueOperations代码段#
{
//先获取CanvasState中的mSaveCount
int restoreTo = renderer.getSaveCount();
//根据操作符号,调用的是SaveOp的defer方法
handler(new (alloc) SaveOp(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag),
PROPERTY_SAVECOUNT, properties().getClipToBounds());
...
//根据操作符号,调用RestoreToCountOp#defer方法
handler(new (alloc) RestoreToCountOp(restoreTo),
PROPERTY_SAVECOUNT, properties().getClipToBounds());
}
下面分析这两个操作。先看SaveOp。
handler触发SaveOp#defer方法,结构体DeferStateStruct的DeferredDisplayList。
virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level,
bool useQuickReject) override {
int newSaveCount = deferStruct.mRenderer.save(mFlags);
deferStruct.mDeferredList.addSave(deferStruct.mRenderer, this, newSaveCount);
}
DeferStateStruct的mRenderer是OpenGLRenderer,OpenGLRenderer#save,触发CanvasState#save,最后调用CanvasState#saveSnapshot。
int CanvasState::saveSnapshot(int flags) {
mSnapshot = new Snapshot(mSnapshot, flags);
//先返回mSaveCount,再自增。
return mSaveCount++;
}
创建一个Snapshot,插入表头。CanvasState内部mSaveCount返回,并自增。
//newSaveCount是saveSnapshot返回的值
void DeferredDisplayList::addSave(OpenGLRenderer& renderer, SaveOp* op, int newSaveCount) {
int saveFlags = op->getFlags();
if (recordingComplexClip() && (saveFlags & SkCanvas::kClip_SaveFlag)) {
storeStateOpBarrier(renderer, op);
//进入的newSaveCount是自增前的值。也就是需要恢复的值。
mSaveStack.push(newSaveCount);
}
}
更新DeferredDisplayList数据,SaveStack和Batches。
将返回的mSaveCount值push到SaveStack数组。
void DeferredDisplayList::storeStateOpBarrier(OpenGLRenderer& renderer, StateOp* op) {
DeferredDisplayState* state = createState();
renderer.storeDisplayState(*state, getStateOpDeferFlags());
mBatches.add(new StateOpBatch(op, state));
resetBatchingState();
}
创建DeferredDisplayState,并设置DeferredDisplayState的一些值。
Batches新增一个StateOpBatch,**它封装了DeferredDisplayState与Op。
RestoreToCountOp同SaveOp类似。RestoreToCountOp#defer方法。
virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level,
bool useQuickReject) override {
deferStruct.mDeferredList.addRestoreToCount(deferStruct.mRenderer,
this, saveCount + mCount);
deferStruct.mRenderer.restoreToCount(saveCount + mCount);
}
mCount是RestoreToCountOp构建传入的参数,即CanvasState#getSaveCount当时返回的值,意思是恢复到此时的数量,saveCount值是0,PROPERTY_SAVECOUNT。
DeferredDisplayList#addRestoreToCount方法。
void DeferredDisplayList::addRestoreToCount(OpenGLRenderer& renderer, StateOp* op,
int newSaveCount) {
if (recordingComplexClip() && newSaveCount <= mComplexClipStackStart) {
mComplexClipStackStart = -1;
resetBatchingState();
}
//newSaveCount是恢复的数值
if (mSaveStack.isEmpty() || newSaveCount > mSaveStack.top()) {
return;
}
//当SaveOp时,将要恢复的值会push到mSaveStack栈顶。
while (!mSaveStack.isEmpty() && mSaveStack.top() >= newSaveCount) mSaveStack.pop();
storeRestoreToCountBarrier(renderer, op, mSaveStack.size() + FLUSH_SAVE_STACK_DEPTH);
}
第一步:若saveOp与RestoreToCountOp成对出现,则mSaveStack最上面保存的值就是自增前的mSaveCount,与要恢复的这个数值newSaveCount相等的,直接pop即可。
若newSaveCount大于mSaveStack最上面元素,说明已经有过RestoreToCountOp操作,这种情况直接退出。
若newSaveCount小于mSaveStack最上面元素,说明可能有多个SaveOp操作,则mSaveStack一直pop,直到达到我们需要恢复的层级,即栈顶等于newSaveCount。
第二步:OpenGLRenderer#restoreToCount,即在CanvasState中恢复到saveCount。
void CanvasState::restoreToCount(int saveCount) {
if (saveCount < 1) saveCount = 1;
while (mSaveCount > saveCount) {
restoreSnapshot();
}
}
逻辑基本与上面一致,若两种Op成对出现,CanvasState内部的mSaveCount值会比saveCount大1,即mSaveCount需要恢复自增前的值,若出现过多个SaveOp,mSaveCount将远大于saveCount,不停的自减且删除Snapshot节点,直到相等为止。
总之,每一次SaveOp,在CanvasState中创建一个Snapshot,并更新DeferredDisplayList的SaveStack和Batches数据。SaveStack栈顶存储CanvasState自增前值。
每一次RestoreToCountOp,删除CanvasState的Snapshot,恢复内部mSaveCount,SaveStack栈顶弹出即将恢复的值。
普通视图RenderNode节点#issueOperations方法
前面介绍过Layer时仅绘制一个DrawLayerOp,下面介绍无Layer时,一个普通视图的绘制逻辑。
RenderNode#issueOperations代码段。
//针对给View节点的操作数据,//上面暂时省略的内容
{
const int saveCountOffset = renderer.getSaveCount() - 1;
const int projectionReceiveIndex = mDisplayListData->projectionReceiveIndex;
for (size_t chunkIndex = 0; chunkIndex < mDisplayListData->getChunks().size(); chunkIndex++) {
const DisplayListData::Chunk& chunk = mDisplayListData->getChunks()[chunkIndex];
Vector<ZDrawRenderNodeOpPair> zTranslatedNodes;
//子RenderNode排序Z轴。
buildZSortedChildList(chunk, zTranslatedNodes);
issueOperationsOf3dChildren(kNegativeZChildren,
initialTransform, zTranslatedNodes, renderer, handler);//Z轴负数
for (size_t opIndex = chunk.beginOpIndex; opIndex < chunk.endOpIndex; opIndex++) {
DisplayListOp *op = mDisplayListData->displayListOps[opIndex];
handler(op, saveCountOffset, properties().getClipToBounds());
if (CC_UNLIKELY(!mProjectedNodes.isEmpty() && projectionReceiveIndex >= 0 &&
opIndex == static_cast<size_t>(projectionReceiveIndex))) {
issueOperationsOfProjectedChildren(renderer, handler);
}
}
issueOperationsOf3dChildren(kPositiveZChildren,
initialTransform, zTranslatedNodes, renderer, handler);//Z轴正数
}
}
DisplayListData内部数组Chunk。触发以下三个方法
RenderNode#buildZSortedChildList。
RenderNode#issueOperationsOf3dChildren。
RenderNode#issueOperationsOfProjectedChildren。
排序Z轴子视图
void RenderNode::buildZSortedChildList(const DisplayListData::Chunk& chunk,
Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes) {
if (chunk.beginChildIndex == chunk.endChildIndex) return;
for (unsigned int i = chunk.beginChildIndex; i < chunk.endChildIndex; i++) {
DrawRenderNodeOp* childOp = mDisplayListData->children()[i];
//获取DrawRenderNodeOp对应RenderNode
RenderNode* child = childOp->mRenderNode;
float childZ = child->properties().getZ();
//加入zTranslatedNodes数组。
if (!MathUtils::isZero(childZ) && chunk.reorderChildren) {
zTranslatedNodes.add(ZDrawRenderNodeOpPair(childZ, childOp));
//设置mSkipInOrderDraw标志。
childOp->mSkipInOrderDraw = true;
} else if (!child->properties().getProjectBackwards()) {
// regular, in order drawing DisplayList
childOp->mSkipInOrderDraw = false;
}
}
//从小到大的顺序
std::stable_sort(zTranslatedNodes.begin(), zTranslatedNodes.end());
}
DisplayListData中DrawRenderNodeOp数组mChildren。
遍历,遍历的索引范围是Chunk中beginChildIndex和endChildIndex。
从每项DrawRenderNodeOp获取RenderNode。目的是拿到RenderNode的Z轴值。
上层DisplayListCanvas绘制Renderode时(drawRenderNode方法),创建一个DrawRenderNodeOp,并将绘制的RenderNode与它绑定。
RenderNode属性RenderProperties的Z值,这是Z轴方向的距离。即etElevation() + getTranslationZ()。
如果childZ不是0并且Chunk的reorderChildren重排标志位true,即该Chunk创建前insertReorderBarrier插入的是需要重排的类型kBarrier_OutOfOrder。
创建ZDrawRenderNodeOpPair加入zTranslatedNodes数组中,封装DrawRenderNodeOp与childZ。
最后,zTranslatedNodes数组按childZ由小到大stable_sort排序。
RenderNode#issueOperationsOf3dChildren方法。
子节点DrawRenderNodeOp操作经过排序后,childZ从小到大。
先调用一次issueOperationsOf3dChildren方法
先触发childZ是负数的,这种情况比较简单是没有阴影的,直接处理所有Z轴为负数的DrawRenderNodeOp。
处理本视图绘制操作
遍历displayListOps数组
当前Chunk的beginOpIndex和endOpIndex索引指向的数组displayListOps中的一段操作执行绘制。
最后在调用一次issueOperationsOf3dChildren方法
执行childZ是非负数,从最小的非负数childZ开始,先绘制阴影issueDrawShadowOperation操作,如果下一个索引的childZ和前一个childZ差距小与一个值,则继续绘制该索引DrawRenderNodeOp对应的阴影。不满足条件则绘制DrawRenderNodeOp,当索引达到shadowIndex时候,则绘制阴影DrawShadowOp操作。每一个索引的阴影和DrawRenderNodeOp都会绘制,顺序不是按照index一次执行的。
这次Chunk就结束了,接下来是下一个Chunk。
当Vector<DrawRenderNodeOp*> mProjectedNodes投影数组不为空时,issueOperationsOfProjectedChildren操作投影DrawRenderNodeOp。遍历mProjectedNodes。
DrawRenderNodeOp操作的本质是调用内部RenderNode的defer或reply方法
延迟合并渲染
先从前面拿到延迟合并渲染的一段代码。
OpenGLRenderer#drawRenderNode方法代码段。
{
...
bool avoidOverdraw = !Properties::debugOverdraw;
DeferredDisplayList deferredList(mState.currentClipRect(), avoidOverdraw);
DeferStateStruct deferStruct(deferredList, *this, replayFlags);
//节点延迟合并渲染
renderNode->defer(deferStruct, 0);
flushLayers();
startFrame();
//刷新
deferredList.flush(*this, dirty);
}
从RenderNode#defer方法,进入issueOperations,入参handler是DeferOperationHandler。因此,执行每一个handler触发Op的defer方法。
绘制操作基类DrawOp,子类包括DrawColorOp、DrawBoundedOp、以及前面提到过的DrawLayerOp等。他们均重用DrawOp#defer方法。此方法前面已经提到过,这里重新拿来分析。
virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level,
bool useQuickReject) override {
if (mQuickRejected && CC_LIKELY(useQuickReject)) {
return;
}
deferStruct.mDeferredList.addDrawOp(deferStruct.mRenderer, this);
}
分析DeferredDisplayList的addDrawOp方法。
绘制操作DrawOp加入DeferredDisplayList。
void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) {
DeferredDisplayState* const state = createState();
...
//初始化DeferInfo信息。
DeferInfo deferInfo;
op->onDefer(renderer, deferInfo, *state);
deferInfo.mergeable &= !recordingComplexClip();
deferInfo.opaqueOverBounds &= !recordingComplexClip()
&& mSaveStack.isEmpty()
&& !state->mRoundRectClipState;
DrawBatch* targetBatch = nullptr;
//默认插入到Batch数组的尾部
int insertBatchIndex = mBatches.size();
//Batches数组集合是不空的条件下
//考虑mMergingBatches和mBatchLookup。
//MergingBatches是一个HashMap数组。
if (!mBatches.isEmpty()) {
if (state->mBounds.isEmpty()) {
// don't know the bounds for op, so add to last batch and start from scratch on next op
DrawBatch* b = new DrawBatch(deferInfo);
b->add(op, state, deferInfo.opaqueOverBounds);
mBatches.add(b);
resetBatchingState();
return;
}
//可以合并。
if (deferInfo.mergeable) {
//根据batchId和mergeId
//从MergingBatches查找batch。
if (mMergingBatches[deferInfo.batchId].get(deferInfo.mergeId, targetBatch)) {
if (!((MergingDrawBatch*) targetBatch)->canMergeWith(op, state)) {
targetBatch = nullptr;
}
}
//不可合并
} else {
//根据batchId从BatchLookup查。
targetBatch = (DrawBatch*)mBatchLookup[deferInfo.batchId];
}
//找到了目标或可合并。
if (targetBatch || deferInfo.mergeable) {
for (int i = mBatches.size() - 1; i >= mEarliestBatchIndex; i--) {
DrawBatch* overBatch = (DrawBatch*)mBatches[i];
//遍历每个DrawBatch,三种情况下break
if (overBatch == targetBatch) break;
if (deferInfo.batchId == overBatch->getBatchId()) {
insertBatchIndex = i + 1;
//可以合并的,为后面的新建找到插入位置。
//退出。
if (!targetBatch) break;
}
if (overBatch->intersects(state->mBounds)) {
targetBatch = nullptr;
break;
}
}
}
}
//目标批次不存在,创建,插入数组相应位置。
//MergingDrawBatch或DrawBatch批次。
if (!targetBatch) {
if (deferInfo.mergeable) {
targetBatch = new MergingDrawBatch(deferInfo,
renderer.getViewportWidth(), renderer.getViewportHeight());
mMergingBatches[deferInfo.batchId].put(deferInfo.mergeId, targetBatch);
} else {
targetBatch = new DrawBatch(deferInfo);
mBatchLookup[deferInfo.batchId] = targetBatch;
}
mBatches.insertAt(targetBatch, insertBatchIndex);
}
//最终,找到目标Batch,存放op。
targetBatch->add(op, state, deferInfo.opaqueOverBounds);
}
每一种DrawOp的子类均对应一个BatchId。枚举变量OpBatchId存储BatchId类型。
OpBatchId枚举变量
kOpBatch_None = 0, // Don't batch
kOpBatch_Bitmap,
kOpBatch_Patch,
kOpBatch_AlphaVertices,
kOpBatch_Vertices,
kOpBatch_AlphaMaskTexture,
kOpBatch_Text,
kOpBatch_ColorText,kOpBatch_Count, // Add other batch ids before this
mBatchLookup和mMergingBatches是一个数组Map,,枚举值数量kOpBatch_Count。
mBatchLookup:根据BatchId枚举值获取Batch。
mMergingBatches:根据BatchId枚举值获取一个Map,Key是merageId。
MergingBatches数组枚举.jpg子类重写DrawOp#onDefer方法,初始化DeferInfo结构体特定batchId,其他信息包括mergeable,mergeId等,每一个子类有所不同。
然后根据条件选择或者创建一个批次,该DrawOp加入批次。
DeferredDisplayList中有一个Batch数组。最终我们会找到目标Batch,即targetBatch。Batch#add方法将此DrawOp加入。
由DeferInfo#mergeable决定从MergingBatches还是从BatchLookup获取Batch。
若无mergeable标志。在BacthLookup中查找
1:若未找到batchId对应的目标批次,创建。
2:若找到batchId对应的目标批次,从后向前遍历Batch数组。
以下对应三种情况的break退出。
- targetBatch已经在Batch数组中了,退出循环,这就是目标批次。
- 还未找到一样的批次,却在数组中找到了batchId相等的批次,设置插入位置这个批次的后面。这里考虑到数组中找不到一样的批次,未来新建时的插入位置(仅在后面创建插入时用)。这种情况下targetBatch一直存在,继续循环。
- 若遍历的批次,操作区域与需加入的op区域有重合,则为op单独准备批次。这种情况要成立,就一定要新建批次了。退出循环。
若有mergeable标志。在MergingBatches中查找
1:根据batchId查找HashMap,再根据mergeId查找,经canMergeWith方法判断合适,即找到targetBatch。
2:根据代码可知,不管是否找到批次,均会遍历数组。
找到批次的三种break同上。
以下情况是未找到批次时。
- 永远在Batch数组中无法找到。
- 数组中找到了batchId相等的批次,同样,设置插入位置这个批次的后面。这种情况下目标批次是空,一定会新建批次,因此,设置其插入位置还是需要的。找到位置后,就没有必要继续了,退出循环。
- 若遍历的批次,操作区域与需加入的op区域有重合,则为op单独准备批次。本来就未找到,这种情况必定新建批次了。
最终,
目标批次不空:将Drawop合并到该批次事务中。
目标批次是空:创建之,并将新Batch设置成其BatchId枚举值在MergingBatches或BatchLookup中对应的Value,插入Batches数组。因此,只要在Batches数组出现的,一定在MergingBatches或BatchLookup中根据BatchId能找到原件。
Batch是什么
DrawBatch#add方法。
virtual void add(DrawOp* op, const DeferredDisplayState* state, bool opaqueOverBounds) {
mBounds.unionWith(state->mBounds);
mAllOpsOpaque &= opaqueOverBounds;
mOps.add(OpStatePair(op, state));
}
将DrawOp和DeferredDisplayState封装成一个OpStatePair,加入OpStatePair数组。
每一个Batch包含一个OpStatePair数组,一个区域Rect。新建DrawBatch的mMergeId和mBatchId由DeferInfo设置。
一个DrawOp操作对应一个OpStatePair。
MergingDrawBatch继承DrawBatch,DrawBatch继承Batch,Batch是批处理,将一系列DrawOp集合成一个批次。集中写入GPU。
批处理刷新
绘制操作已经保存在Batch批次中。
void DeferredDisplayList::flush(OpenGLRenderer& renderer, Rect& dirty) {
Caches::getInstance().fontRenderer->endPrecaching();
//说明Batches数组是空,不需要刷新
if (isEmpty()) return;
renderer.restoreToCount(1);
// save and restore so that reordering doesn't affect final state
renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
if (CC_LIKELY(mAvoidOverdraw)) {
for (unsigned int i = 1; i < mBatches.size(); i++) {
if (mBatches[i] && mBatches[i]->coversBounds(mBounds)) {
//为避免过度绘制放弃一些不需要的mBatches元素。
discardDrawingBatches(i - 1);
}
}
}
replayBatchList(mBatches, renderer, dirty);
renderer.restoreToCount(1);
clear();
}
DeferredDisplayList#replayBatchList方法负责刷新Batches数组。
static void replayBatchList(const Vector<Batch*>& batchList,
OpenGLRenderer& renderer, Rect& dirty) {
for (unsigned int i = 0; i < batchList.size(); i++) {
if (batchList[i]) {
batchList[i]->replay(renderer, dirty, i);
}
}
}
遍历数组每一项Batch。
DrawBatch#reply方法。
virtual void replay(OpenGLRenderer& renderer, Rect& dirty, int index) override {
for (unsigned int i = 0; i < mOps.size(); i++) {
DrawOp* op = mOps[i].op;
const DeferredDisplayState* state = mOps[i].state;
renderer.restoreDisplayState(*state);
op->applyDraw(renderer, dirty);
...
}
}
Batch中OpStatePair数组,遍历每一项OpStatePair。
从OpStatePair中获取DrawOp和DeferredDisplayState,触发DrawOp的applyDraw方法。
DrawOp的子类DrawRectsOp#applyDraw方法。
virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
renderer.drawRects(mRects, mCount, mPaint);
}
最终,触发OpenGLRenderer的drawXxx方法。
任重而道远
网友评论