前面已經(jīng)介紹過同步幀狀態(tài)稠集。
繼續(xù)分析繪制一幀的相關(guān)內(nèi)容箱蝠。
繪制一幀
從CanvasContext#draw方法開始泌绣。
void CanvasContext::draw() {
SkRect dirty;
EGLint width, height;
mEglManager.beginFrame(mEglSurface, &width, &height);
//如果寬高與OpenGLRenderer內(nèi)部CanvasState保存的不同述暂。
//重新設(shè)置到CanvasState中。
if (width != mCanvas->getViewportWidth() || height != mCanvas->getViewportHeight()) {
//初始化CanvasState的寬高。
mCanvas->setViewport(width, height);
dirty.setEmpty();
} else if (!mBufferPreserved || mHaveNewSurface) {
dirty.setEmpty();//新Surface,臟區(qū)域置空
} else {
if (!dirty.isEmpty() && !dirty.intersect(0, 0, width, height)) {
//區(qū)域與該View無交集绰精,置空
dirty.setEmpty();
}
profiler().unionDirty(&dirty);
}
//不空趴泌,則開始刷臟區(qū)域,第一步
if (!dirty.isEmpty()) {
mCanvas->prepareDirty(dirty.fLeft, dirty.fTop,
dirty.fRight, dirty.fBottom, mOpaque);
} else {
//刷新整個(gè)視圖杀狡。
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轩缤。即內(nèi)部mCanvas,draw方法中很多依賴OpenGLRenderer完成贩绕。
繪制一幀主要三個(gè)部分火的。
第一部分:OpenGLRenderer#prepareDirty方法。
準(zhǔn)備刷新視圖臟區(qū)域淑倾,若臟區(qū)域?yàn)榭樟蠛祝ㄟ^prepare方法調(diào)用prepareDirty刷新視圖整個(gè)寬高區(qū)域,入?yún)ú煌该鳂?biāo)志(不透明為true)娇哆。
第二部分:OpenGLRenderer#drawRenderNode方法湃累。
根節(jié)點(diǎn)RootRenderNode繪制。入?yún)⑹歉?jié)點(diǎn)碍讨。
第一部分 prepareDirty分析
prepareDirty方法流程圖如下治力。void 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狀態(tài)
startFrame();
}
}
由圖可知琴许,分兩步
1:setupFrameState,初始化SaveStack溉躲。
2:從CanvasState獲取當(dāng)前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內(nèi)部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棧的初始數(shù)量設(shè)為1辩块。
}
CanvasState構(gòu)造方法創(chuàng)建第一個(gè)Snapshot蛔六,此方法再創(chuàng)建一個(gè),鏈表表頭废亭。初始化Snapshot的fbo国章,幀緩存對象。
OpenGLRenderer結(jié)構(gòu)關(guān)系圖豆村。
1:LayerRenderer繼承OpenGLRenderer,OpenGLRenderer繼承CanvasStateClient掌动。他們內(nèi)部均包含CanvasState四啰,指向創(chuàng)建它的CanvasStateClient。
2:OpenGLRenderer/LayerRenderer構(gòu)造方法創(chuàng)建CanvasState粗恢。
3:當(dāng)前CanvasState#Snapshot的fbo由與CanvasState關(guān)聯(lián)的render設(shè)置柑晒,render是LayerRenderer或OpenGLRenderer。
4:他們均重寫基類CanvasStateClient#getTargetFbo方法眷射。返回值不同匙赞。
OpenGLRenderer:返回0,因此Snapshot的fbo被設(shè)置為0妖碉。
LayerRenderer:返回內(nèi)部Layer的fbo罚屋。
CanvaState的render是OpenGLRenderer,不是LayerRenderer嗅绸,因此,Snapshot初始化fbo是0撕彤。
官方注釋
framebuffer渲染器(fbo是0)將(使用fbo幀緩沖對象)會(huì)推遲每一個(gè)Layer的display list鱼鸠,等待直到第一個(gè)渲染命令,開始繪制幀羹铅。
Blend#syncEnabled同步方法根據(jù)mEnabled狀態(tài)去調(diào)用glEnable(GL_BLEND)后者glDisable(GL_BLEND)蚀狰,然后觸發(fā)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數(shù)組中每一個(gè)Layer。LayerRenderer未重寫此方法焊切。
在OpenGLRenderer中的Layer扮授,就是OpenGLRenderer渲染器最終將要渲染的Layer了芳室。
那么,問題刹勃,數(shù)組元素Layer是哪里來的?
OpenGLRenderer#pushLayerUpdate方法負(fù)責(zé)向數(shù)組push元素(保證不相等的Layer對象)堪侯。
兩個(gè)地方觸發(fā)該方法。
1:CanvasContext#processLayerUpdate方法荔仁。
2:RenderNode#pushLayerUpdate(TreeInfo& info)方法伍宦。
第一個(gè)方法,前面也提到過乏梁,針對TextureView視圖次洼。入?yún)⑹荄eferredLayerUpdater,對應(yīng)上層HardwareLayer遇骑。
這里不詳細(xì)探討TextureView視圖中HardwareLayer的創(chuàng)建過程卖毁。前面也說過,Layer無deferredUpdateScheduled標(biāo)志质蕉,不會(huì)被加入OpenGLRenderer的Layer數(shù)組势篡。
第二個(gè)方法,前面提到過模暗,發(fā)生在每一個(gè)RenderNode節(jié)點(diǎn)prepareTreeImpl方法中禁悠,設(shè)置LAYER_TYPE_HARDWARE的視圖,LayerRenderer負(fù)責(zé)創(chuàng)建RenderNode節(jié)點(diǎn)的內(nèi)部Layer兑宇,Layer也包含RenderNode碍侦。
LayerRenderer結(jié)構(gòu)關(guān)系圖。
因此隶糕,內(nèi)部Layer數(shù)組與RenderNode的Layer有關(guān)瓷产。
刷新每一個(gè)Layer。
bool OpenGLRenderer::updateLayer(Layer* layer, bool inFrame) {
//RenderNode內(nèi)部Layer
if (layer->deferredUpdateScheduled && layer->renderer
&& layer->renderNode.get() && layer->renderNode->isRenderable()) {
if (inFrame) {
endTiling();
debugOverdraw(false, false);
}
//Layer渲染時(shí)枚驻,對drawDeferDisabled標(biāo)志的判斷與drawRenderNode方法一樣濒旦。
//此外,這里還要判斷inFrame標(biāo)志再登。
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應(yīng)滿足以下條件尔邓。
- 1:deferredUpdateScheduled標(biāo)志。
注意一下:
前面文章也介紹過锉矢,在源碼中梯嗽,只有Layer#updateDeferred方法設(shè)置此標(biāo)志。在數(shù)據(jù)同步時(shí)沽损,RenderNode節(jié)點(diǎn)初始化內(nèi)部Layer(上層設(shè)置條件下)灯节,此方法除了設(shè)置該標(biāo)志,還會(huì)創(chuàng)建LayerRenderer。
這一切的前提是上層視圖設(shè)置LAYER_TYPE_HARDWARE標(biāo)志炎疆。
TextureView視圖的底層Layer并不會(huì)調(diào)用這個(gè)方法卡骂。**
- 2:Layer內(nèi)部,RenderNode存在且DisplayListData數(shù)據(jù)不空磷雇。
節(jié)點(diǎn)內(nèi)部Layer由LayerRenderer#createRenderLayer創(chuàng)建偿警。只有這種Layer才有RenderNode,將LayerRenderer設(shè)置內(nèi)部渲染器唯笙。
- 3:Layer渲染器renderer存在螟蒸,是LayerRenderer。
OpenGLRenderer#updateLayer方法刷新的Layer與視圖TextureView的底層Layer無關(guān)崩掘。
目前介紹兩種Layer七嫌,還有一種Layer,由OpenGLRenderer#的createLayer方法創(chuàng)建苞慢。OpenGLRenderer#saveLayer觸發(fā)诵原。上層Canvas#saveLayer時(shí),觸發(fā)一次SaveLayerOp操作挽放,此操作會(huì)導(dǎo)致渲染器#saveLayer绍赛。
三種Layer如下圖所示。綜上:
第一種Layer辑畦,上層saveLayer方法通過createLayer方法創(chuàng)建Layer吗蚌。Layer對象也會(huì)通過fboLayer標(biāo)志綁定fbo。
第二種Layer纯出,TextureView視圖蚯妇,LayerRenderer#createTextureLayer創(chuàng)建。
第三種Layer暂筝,上層視圖LAYER_TYPE_HARDWARE箩言,底層RenderNode節(jié)點(diǎn)pushLayerUpdate時(shí)創(chuàng)建Layer,Layer內(nèi)部有LayerRenderer渲染器和RenderNode節(jié)點(diǎn)焕襟。
LayerRenderer#createRenderLayer方法創(chuàng)建陨收。此Layer一定會(huì)綁定一個(gè)fbo。
fbo在FboCache中通過glGenFramebufferss生成幀緩沖區(qū)對象鸵赖。
Layer渲染
OpenGLRenderer#updateLayer(Layer* layer, bool inFrame)方法务漩,觸發(fā)Layer渲染。
支持兩種渲染defer與render卫漫,defer還是render?
若drawDeferDisabled禁止延遲或入?yún)nFrame是true,render立即渲染肾砂,否則defer延遲合并列赎。這兩種情況分兩個(gè)討論,因?yàn)椋總€(gè)操作命令均支持render和defer包吝。
在OpenGLRenderer#drawLayer處饼煞,觸發(fā)updateLayer時(shí),inFrame是true诗越。
下面先分別簡單介紹砖瞧。
Layer立即渲染
void Layer::render(const OpenGLRenderer& rootRenderer) {
//設(shè)置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內(nèi)部render是LayerRenderer。Layer中不一定會(huì)有RenderNode床未。
1:LayerRenderer#prepareDirty方法準(zhǔn)備臟區(qū)域竭翠,這時(shí)觸發(fā)prepareDirty的是LayerRenderer,因此薇搁,currentSnapshot的fbo>0斋扰,不會(huì)再進(jìn)入updateLayers。
2:OpenGLRenderer#drawRenderNode啃洋,前面也會(huì)調(diào)用這個(gè)方法传货,繪制的是CanvasContext的根RenderNode節(jié)點(diǎn),這里繪制Layer內(nèi)部RenderNode節(jié)點(diǎn)宏娄。馬上要分析的第二部分问裕。
3:最后,將恢復(fù)deferredUpdateScheduled延遲標(biāo)志绝编,渲染節(jié)點(diǎn)renderNode置null僻澎,代表Layer渲染結(jié)束。
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);//設(shè)置Layer的臟區(qū)域
}
//創(chuàng)建DeferredDisplayList
//封裝在結(jié)構(gòu)體DeferStateStruct十饥。
deferredList.reset(new DeferredDisplayList(dirtyRect));
DeferStateStruct deferredState(*deferredList, *renderer,
RenderNode::kReplayFlag_ClipChildren);
//設(shè)置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;
}
創(chuàng)建一個(gè)DeferredDisplayList數(shù)據(jù)逗堵,封裝在結(jié)構(gòu)體DeferStateStruct秉氧,Layer內(nèi)部RenderNode#refer方法。
綜上:Layer渲染的本質(zhì)是蜒秤,利用內(nèi)部LayerRenderer汁咏,渲染內(nèi)部RenderNode的過程。
RenderNode渲染本質(zhì)
延遲合并:本質(zhì)是RenderNode#defer方法作媚。
立即渲染:本質(zhì)是LayerRenderer的drawRenderNode方法攘滩,即OpenGLRenderer的drawRenderNode方法。在該方法中纸泡,再次判斷RenderNode#replay還是defer漂问。
總結(jié)
prepareDirty方法
初始化幀狀態(tài):初始化畫布狀態(tài),創(chuàng)建Snapshot,設(shè)置Snapshot的區(qū)域蚤假,對于幀緩存對象fbo栏饮,只有LayerRenderer才會(huì)有,普通視圖暫不考慮磷仰,初始化fbo是0袍嬉。初始化當(dāng)前Save層值是1。
OpenGLRenderer中Layer刷新:普通視圖暫不考慮灶平。針對上層設(shè)置LAYER_TYPE_HARDWARE的RenderNode節(jié)點(diǎn)伺通。
下面,就看第二部分OpenGLRenderer#drawRenderNode方法吧民逼。
第二部分泵殴,drawRenderNode分析
每個(gè)節(jié)點(diǎn)代表一個(gè)視圖數(shù)據(jù),繪制視圖某一個(gè)dirty區(qū)域內(nèi)容拼苍。該方法真正開始繪制節(jié)點(diǎn)笑诅。以CanvasContext#draw方法繪制RootRenderNode根節(jié)點(diǎn)為主進(jìn)行介紹。
//this看情況疮鲫,也可能是LayerRenderer
void OpenGLRenderer::drawRenderNode(RenderNode* renderNode, Rect& dirty, int32_t replayFlags) {
//renderNode和mDisplayListData不空
if (renderNode && renderNode->isRenderable()) {
renderNode->computeOrdering();
//為true時(shí)吆你,則禁止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();
}
}
前提條件待繪制節(jié)點(diǎn)和DisplayListData信息均不空。
(有時(shí)Layer中的RenderNode是空俊犯,在Layer渲染時(shí)注意妇多。)
RenderNode立即渲染與延遲合并流程圖如下。
二者區(qū)別燕侠。
立即渲染:在遍歷整個(gè)操作過程中者祖,遇到一個(gè)DrawOp繪制操作或StateOp狀態(tài)操作,立即寫入Opengl绢彤。
延遲合并渲染:將操作收集DeferredDisplayList七问,當(dāng)遇到flush時(shí),集中批次寫入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,觸發(fā)issueOperations方法饶氏,defer與reply模式讥耗。利用兩個(gè)handler分別觸發(fā)Op的reply與defer。
issueOperations方法將多次觸發(fā)handler(XxxOp疹启,saveCount古程,clipToBounds)方法。
兩個(gè)handler內(nèi)聯(lián)函數(shù)如下喊崖。
//ReplayOperationHandler內(nèi)聯(lián)函數(shù)
inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) {
operation->replay(mReplayStruct, saveCount, mLevel, clipToBounds);
}
//DeferOperationHandler內(nèi)聯(lián)函數(shù)挣磨。
inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) {
operation->defer(mDeferStruct, saveCount, mLevel, clipToBounds);
}
綜上
ReplayOperationHandler(XxxOp菲宴,..)的本質(zhì)是XxxOp#replay方法。
DeferOperationHandler(XxxOp趋急,..)的本質(zhì)是XxxOp#defer方法。
RenderNode#issueOperations方法势誊。
template <class T>
void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) {
//內(nèi)部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節(jié)點(diǎn)的操作數(shù)據(jù)
暫時(shí)省略...后面介紹
}
}
handler(new (alloc) RestoreToCountOp(restoreTo),
PROPERTY_SAVECOUNT, properties().getClipToBounds());
}
ReplayOperationHandler或DeferOperationHandler ,觸發(fā)的XxxOp方法不同粟耻。
renderer是什么呢?
若是在CanvasContext#draw中繪制根節(jié)點(diǎn)查近。renderer即OpenGLRenderer。
若是在Layer#render中繪制RenderNode挤忙,renderer即LayerRenderer霜威。
開始時(shí)有一個(gè)判斷。
若RenderNode內(nèi)Layer存在且LayerRenderer册烈。前面已經(jīng)介紹過戈泼,這屬于第三種Layer的情況,僅考慮DrawLayerOp繪制赏僧。Layer內(nèi)部render是LayerRenderer大猛,一定存在fbo。
DrawLayerOp繼承DrawOp淀零。DrawLayerOp#defer或replay挽绩。
DrawLayerOp未重寫replay,重寫applyDraw驾中。在DrawOp#replay觸發(fā)子類重寫的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結(jié)構(gòu)體的DeferredList列表此改。其他DrawOp子類defer也是同樣的邏輯趾撵。
總結(jié)
OpenGLRenderer#drawRenderNode的本質(zhì)是
將繪制任務(wù)交給入?yún)enderNode節(jié)點(diǎn)的defer與replay方法完成。前提是RenderNode存在且DisplayListData數(shù)據(jù)不空共啃。
1:RenderNode的defer與replay將觸發(fā)XxxOp的對應(yīng)方法占调。
2:不僅DrawLayerOp,每一種DrawOp子類均重寫applyDraw移剪,觸發(fā)OpenGLRenderer#drawXxx究珊。
3:Op#defer向DeferredDisplayList加入操作,Op#reply申請applyDraw繪制纵苛。
4:defer后剿涮,相關(guān)Op已保存在DeferredDisplayList中言津,由DeferredDisplayList#flush刷新。
reply操作取试,最終均走到OpenGLRenderer#drawXXX方法悬槽,然后通過OpenGLRenderer::renderGlop(const Glop& glop, GlopRenderType type),寫入opengl瞬浓。
defer操作
DeferStateStruct結(jié)構(gòu)體的DeferredDisplayList列表存儲了繪制操作初婆。
到這里,繪制一幀基本流程介紹完成猿棉。
接下來磅叛,看一下Save與RestoreToCount操作,以及延遲合并的邏輯萨赁。
SaveOp與RestoreToCountOp操作
RenderNode#issueOperations方法開始于結(jié)束會(huì)觸發(fā)一次
一次SaveOp和RestoreToCountOp操作弊琴,他們均繼承StateOp。
issueOperations代碼段#
{
//先獲取CanvasState中的mSaveCount
int restoreTo = renderer.getSaveCount();
//根據(jù)操作符號杖爽,調(diào)用的是SaveOp的defer方法
handler(new (alloc) SaveOp(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag),
PROPERTY_SAVECOUNT, properties().getClipToBounds());
...
//根據(jù)操作符號敲董,調(diào)用RestoreToCountOp#defer方法
handler(new (alloc) RestoreToCountOp(restoreTo),
PROPERTY_SAVECOUNT, properties().getClipToBounds());
}
下面分析這兩個(gè)操作。先看SaveOp慰安。
handler觸發(fā)SaveOp#defer方法臣缀,結(jié)構(gòu)體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精置,觸發(fā)CanvasState#save,最后調(diào)用CanvasState#saveSnapshot锣杂。
int CanvasState::saveSnapshot(int flags) {
mSnapshot = new Snapshot(mSnapshot, flags);
//先返回mSaveCount脂倦,再自增。
return mSaveCount++;
}
創(chuàng)建一個(gè)Snapshot元莫,插入表頭赖阻。CanvasState內(nèi)部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);
//進(jìn)入的newSaveCount是自增前的值火欧。也就是需要恢復(fù)的值。
mSaveStack.push(newSaveCount);
}
}
更新DeferredDisplayList數(shù)據(jù)茎截,SaveStack和Batches苇侵。
將返回的mSaveCount值push到SaveStack數(shù)組。
void DeferredDisplayList::storeStateOpBarrier(OpenGLRenderer& renderer, StateOp* op) {
DeferredDisplayState* state = createState();
renderer.storeDisplayState(*state, getStateOpDeferFlags());
mBatches.add(new StateOpBatch(op, state));
resetBatchingState();
}
創(chuàng)建DeferredDisplayState企锌,并設(shè)置DeferredDisplayState的一些值榆浓。
Batches新增一個(gè)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構(gòu)建傳入的參數(shù),即CanvasState#getSaveCount當(dāng)時(shí)返回的值萍鲸,意思是恢復(fù)到此時(shí)的數(shù)量闷叉,saveCount值是0,PROPERTY_SAVECOUNT脊阴。
DeferredDisplayList#addRestoreToCount方法片习。
void DeferredDisplayList::addRestoreToCount(OpenGLRenderer& renderer, StateOp* op,
int newSaveCount) {
if (recordingComplexClip() && newSaveCount <= mComplexClipStackStart) {
mComplexClipStackStart = -1;
resetBatchingState();
}
//newSaveCount是恢復(fù)的數(shù)值
if (mSaveStack.isEmpty() || newSaveCount > mSaveStack.top()) {
return;
}
//當(dāng)SaveOp時(shí),將要恢復(fù)的值會(huì)push到mSaveStack棧頂蹬叭。
while (!mSaveStack.isEmpty() && mSaveStack.top() >= newSaveCount) mSaveStack.pop();
storeRestoreToCountBarrier(renderer, op, mSaveStack.size() + FLUSH_SAVE_STACK_DEPTH);
}
第一步:若saveOp與RestoreToCountOp成對出現(xiàn),則mSaveStack最上面保存的值就是自增前的mSaveCount状知,與要恢復(fù)的這個(gè)數(shù)值newSaveCount相等的秽五,直接pop即可。
若newSaveCount大于mSaveStack最上面元素饥悴,說明已經(jīng)有過RestoreToCountOp操作坦喘,這種情況直接退出。
若newSaveCount小于mSaveStack最上面元素西设,說明可能有多個(gè)SaveOp操作瓣铣,則mSaveStack一直pop,直到達(dá)到我們需要恢復(fù)的層級贷揽,即棧頂?shù)扔趎ewSaveCount棠笑。
第二步:OpenGLRenderer#restoreToCount,即在CanvasState中恢復(fù)到saveCount禽绪。
void CanvasState::restoreToCount(int saveCount) {
if (saveCount < 1) saveCount = 1;
while (mSaveCount > saveCount) {
restoreSnapshot();
}
}
邏輯基本與上面一致蓖救,若兩種Op成對出現(xiàn),CanvasState內(nèi)部的mSaveCount值會(huì)比saveCount大1印屁,即mSaveCount需要恢復(fù)自增前的值循捺,若出現(xiàn)過多個(gè)SaveOp,mSaveCount將遠(yuǎn)大于saveCount雄人,不停的自減且刪除Snapshot節(jié)點(diǎn)从橘,直到相等為止。
總之础钠,每一次SaveOp恰力,在CanvasState中創(chuàng)建一個(gè)Snapshot,并更新DeferredDisplayList的SaveStack和Batches數(shù)據(jù)旗吁。SaveStack棧頂存儲CanvasState自增前值牺勾。
每一次RestoreToCountOp,刪除CanvasState的Snapshot阵漏,恢復(fù)內(nèi)部mSaveCount驻民,SaveStack棧頂彈出即將恢復(fù)的值翻具。
普通視圖RenderNode節(jié)點(diǎn)#issueOperations方法
前面介紹過Layer時(shí)僅繪制一個(gè)DrawLayerOp,下面介紹無Layer時(shí)回还,一個(gè)普通視圖的繪制邏輯裆泳。
RenderNode#issueOperations代碼段。
//針對給View節(jié)點(diǎn)的操作數(shù)據(jù)柠硕,//上面暫時(shí)省略的內(nèi)容
{
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軸負(fù)數(shù)
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軸正數(shù)
}
}
DisplayListData內(nèi)部數(shù)組Chunk。觸發(fā)以下三個(gè)方法
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對應(yīng)RenderNode
RenderNode* child = childOp->mRenderNode;
float childZ = child->properties().getZ();
//加入zTranslatedNodes數(shù)組癣丧。
if (!MathUtils::isZero(childZ) && chunk.reorderChildren) {
zTranslatedNodes.add(ZDrawRenderNodeOpPair(childZ, childOp));
//設(shè)置mSkipInOrderDraw標(biāo)志槽畔。
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數(shù)組mChildren。
遍歷胁编,遍歷的索引范圍是Chunk中beginChildIndex和endChildIndex厢钧。
從每項(xiàng)DrawRenderNodeOp獲取RenderNode。目的是拿到RenderNode的Z軸值嬉橙。
上層DisplayListCanvas繪制Renderode時(shí)(drawRenderNode方法)早直,創(chuàng)建一個(gè)DrawRenderNodeOp,并將繪制的RenderNode與它綁定市框。
RenderNode屬性RenderProperties的Z值霞扬,這是Z軸方向的距離。即etElevation() + getTranslationZ()枫振。
如果childZ不是0并且Chunk的reorderChildren重排標(biāo)志位true祥得,即該Chunk創(chuàng)建前insertReorderBarrier插入的是需要重排的類型kBarrier_OutOfOrder。
創(chuàng)建ZDrawRenderNodeOpPair加入zTranslatedNodes數(shù)組中蒋得,封裝DrawRenderNodeOp與childZ级及。
最后,zTranslatedNodes數(shù)組按childZ由小到大stable_sort排序额衙。
回去看一下Chunk的結(jié)構(gòu)于內(nèi)容
RenderNode#issueOperationsOf3dChildren方法饮焦。
子節(jié)點(diǎn)DrawRenderNodeOp操作經(jīng)過排序后,childZ從小到大窍侧。
先調(diào)用一次issueOperationsOf3dChildren方法
先觸發(fā)childZ是負(fù)數(shù)的县踢,這種情況比較簡單是沒有陰影的,直接處理所有Z軸為負(fù)數(shù)的DrawRenderNodeOp伟件。
處理本視圖繪制操作
遍歷displayListOps數(shù)組
當(dāng)前Chunk的beginOpIndex和endOpIndex索引指向的數(shù)組displayListOps中的一段操作執(zhí)行繪制硼啤。
最后在調(diào)用一次issueOperationsOf3dChildren方法
執(zhí)行childZ是非負(fù)數(shù),從最小的非負(fù)數(shù)childZ開始斧账,先繪制陰影issueDrawShadowOperation操作谴返,如果下一個(gè)索引的childZ和前一個(gè)childZ差距小與一個(gè)值煞肾,則繼續(xù)繪制該索引DrawRenderNodeOp對應(yīng)的陰影。不滿足條件則繪制DrawRenderNodeOp嗓袱,當(dāng)索引達(dá)到shadowIndex時(shí)候籍救,則繪制陰影DrawShadowOp操作。每一個(gè)索引的陰影和DrawRenderNodeOp都會(huì)繪制渠抹,順序不是按照index一次執(zhí)行的蝙昙。
這次Chunk就結(jié)束了,接下來是下一個(gè)Chunk梧却。
當(dāng)Vector<DrawRenderNodeOp*> mProjectedNodes投影數(shù)組不為空時(shí)奇颠,issueOperationsOfProjectedChildren操作投影DrawRenderNodeOp。遍歷mProjectedNodes放航。
DrawRenderNodeOp操作的本質(zhì)是調(diào)用內(nèi)部RenderNode的defer或reply方法
延遲合并渲染
先從前面拿到延遲合并渲染的一段代碼烈拒。
OpenGLRenderer#drawRenderNode方法代碼段。
{
...
bool avoidOverdraw = !Properties::debugOverdraw;
DeferredDisplayList deferredList(mState.currentClipRect(), avoidOverdraw);
DeferStateStruct deferStruct(deferredList, *this, replayFlags);
//節(jié)點(diǎn)延遲合并渲染
renderNode->defer(deferStruct, 0);
flushLayers();
startFrame();
//刷新
deferredList.flush(*this, dirty);
}
從RenderNode#defer方法三椿,進(jìn)入issueOperations,入?yún)andler是DeferOperationHandler葫辐。因此搜锰,執(zhí)行每一個(gè)handler觸發(fā)Op的defer方法。
繪制操作基類DrawOp耿战,子類包括DrawColorOp蛋叼、DrawBoundedOp、以及前面提到過的DrawLayerOp等剂陡。他們均重用DrawOp#defer方法狈涮。此方法前面已經(jīng)提到過,這里重新拿來分析鸭栖。
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;
//默認(rèn)插入到Batch數(shù)組的尾部
int insertBatchIndex = mBatches.size();
//Batches數(shù)組集合是不空的條件下
//考慮mMergingBatches和mBatchLookup松却。
//MergingBatches是一個(gè)HashMap數(shù)組。
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) {
//根據(jù)batchId和mergeId
//從MergingBatches查找batch晓锻。
if (mMergingBatches[deferInfo.batchId].get(deferInfo.mergeId, targetBatch)) {
if (!((MergingDrawBatch*) targetBatch)->canMergeWith(op, state)) {
targetBatch = nullptr;
}
}
//不可合并
} else {
//根據(jù)batchId從BatchLookup查。
targetBatch = (DrawBatch*)mBatchLookup[deferInfo.batchId];
}
//找到了目標(biāo)或可合并飞几。
if (targetBatch || deferInfo.mergeable) {
for (int i = mBatches.size() - 1; i >= mEarliestBatchIndex; i--) {
DrawBatch* overBatch = (DrawBatch*)mBatches[i];
//遍歷每個(gè)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;
}
}
}
}
//目標(biāo)批次不存在躁锁,創(chuàng)建纷铣,插入數(shù)組相應(yīng)位置。
//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);
}
//最終关炼,找到目標(biāo)Batch,存放op匣吊。
targetBatch->add(op, state, deferInfo.opaqueOverBounds);
}
每一種DrawOp的子類均對應(yīng)一個(gè)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是一個(gè)數(shù)組Map色鸳,社痛,枚舉值數(shù)量kOpBatch_Count。
mBatchLookup:根據(jù)BatchId枚舉值獲取Batch命雀。
mMergingBatches:根據(jù)BatchId枚舉值獲取一個(gè)Map蒜哀,Key是merageId。
子類重寫DrawOp#onDefer方法吏砂,初始化DeferInfo結(jié)構(gòu)體特定batchId撵儿,其他信息包括mergeable,mergeId等狐血,每一個(gè)子類有所不同淀歇。
然后根據(jù)條件選擇或者創(chuàng)建一個(gè)批次,該DrawOp加入批次匈织。
DeferredDisplayList中有一個(gè)Batch數(shù)組浪默。最終我們會(huì)找到目標(biāo)Batch,即targetBatch缀匕。Batch#add方法將此DrawOp加入纳决。
由DeferInfo#mergeable決定從MergingBatches還是從BatchLookup獲取Batch。
若無mergeable標(biāo)志乡小。在BacthLookup中查找
1:若未找到batchId對應(yīng)的目標(biāo)批次阔加,創(chuàng)建。
2:若找到batchId對應(yīng)的目標(biāo)批次满钟,從后向前遍歷Batch數(shù)組掸哑。
以下對應(yīng)三種情況的break退出。
- targetBatch已經(jīng)在Batch數(shù)組中了零远,退出循環(huán)苗分,這就是目標(biāo)批次活尊。
- 還未找到一樣的批次锌钮,卻在數(shù)組中找到了batchId相等的批次,設(shè)置插入位置這個(gè)批次的后面襟交。這里考慮到數(shù)組中找不到一樣的批次,未來新建時(shí)的插入位置(僅在后面創(chuàng)建插入時(shí)用)择浊。這種情況下targetBatch一直存在戴卜,繼續(xù)循環(huán)。
- 若遍歷的批次琢岩,操作區(qū)域與需加入的op區(qū)域有重合投剥,則為op單獨(dú)準(zhǔn)備批次。這種情況要成立担孔,就一定要新建批次了江锨。退出循環(huán)。
若有mergeable標(biāo)志糕篇。在MergingBatches中查找
1:根據(jù)batchId查找HashMap啄育,再根據(jù)mergeId查找,經(jīng)canMergeWith方法判斷合適拌消,即找到targetBatch挑豌。
2:根據(jù)代碼可知,不管是否找到批次墩崩,均會(huì)遍歷數(shù)組氓英。
找到批次的三種break同上。
以下情況是未找到批次時(shí)鹦筹。
- 永遠(yuǎn)在Batch數(shù)組中無法找到铝阐。
- 數(shù)組中找到了batchId相等的批次,同樣盛龄,設(shè)置插入位置這個(gè)批次的后面饰迹。這種情況下目標(biāo)批次是空芳誓,一定會(huì)新建批次余舶,因此,設(shè)置其插入位置還是需要的锹淌。找到位置后匿值,就沒有必要繼續(xù)了,退出循環(huán)赂摆。
- 若遍歷的批次挟憔,操作區(qū)域與需加入的op區(qū)域有重合,則為op單獨(dú)準(zhǔn)備批次烟号。本來就未找到绊谭,這種情況必定新建批次了。
最終汪拥,
目標(biāo)批次不空:將Drawop合并到該批次事務(wù)中达传。
目標(biāo)批次是空:創(chuàng)建之,并將新Batch設(shè)置成其BatchId枚舉值在MergingBatches或BatchLookup中對應(yīng)的Value,插入Batches數(shù)組宪赶。因此宗弯,只要在Batches數(shù)組出現(xiàn)的,一定在MergingBatches或BatchLookup中根據(jù)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封裝成一個(gè)OpStatePair,加入OpStatePair數(shù)組欲主。
每一個(gè)Batch包含一個(gè)OpStatePair數(shù)組邓厕,一個(gè)區(qū)域Rect。新建DrawBatch的mMergeId和mBatchId由DeferInfo設(shè)置岛蚤。
一個(gè)DrawOp操作對應(yīng)一個(gè)OpStatePair邑狸。
MergingDrawBatch繼承DrawBatch,DrawBatch繼承Batch涤妒,Batch是批處理单雾,將一系列DrawOp集合成一個(gè)批次。集中寫入GPU她紫。
批處理刷新
繪制操作已經(jīng)保存在Batch批次中硅堆。
void DeferredDisplayList::flush(OpenGLRenderer& renderer, Rect& dirty) {
Caches::getInstance().fontRenderer->endPrecaching();
//說明Batches數(shù)組是空,不需要刷新
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方法負(fù)責(zé)刷新Batches數(shù)組渐逃。
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);
}
}
}
遍歷數(shù)組每一項(xiàng)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數(shù)組茄菊,遍歷每一項(xiàng)OpStatePair。
從OpStatePair中獲取DrawOp和DeferredDisplayState赊堪,觸發(fā)DrawOp的applyDraw方法面殖。
DrawOp的子類DrawRectsOp#applyDraw方法。
virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
renderer.drawRects(mRects, mCount, mPaint);
}
最終哭廉,觸發(fā)OpenGLRenderer的drawXxx方法脊僚。
任重而道遠(yuǎn)