硬件渲染-繪制一幀


前面已經(jīng)介紹過同步幀狀態(tài)稠集。

一次硬件渲染任務(wù)整流程圖

繼續(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方法流程圖如下治力。
OpenGLRenderer的prepareDirty流程解析.jpg
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)系圖豆村。

OpenGLRenderer相關(guān)結(jié)構(gòu)關(guān)系圖.jpg
OpenGLRenderer與LayerRenderer液兽。
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)系圖。

LayerRenderer結(jié)構(gòu)關(guān)系圖.jpg

因此隶糕,內(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.jpg

綜上:
第一種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立即渲染與延遲合并流程圖如下。

RenderNode立即繪制與延遲合并流程圖.jpg

二者區(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命雀。

BatchLookup數(shù)組枚舉.jpg

mMergingBatches:根據(jù)BatchId枚舉值獲取一個(gè)Map蒜哀,Key是merageId。

MergingBatches數(shù)組枚舉.jpg

子類重寫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邑狸。

Batch結(jié)構(gòu)圖.jpg

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)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市遵绰,隨后出現(xiàn)的幾起案子辽幌,更是在濱河造成了極大的恐慌,老刑警劉巖椿访,帶你破解...
    沈念sama閱讀 219,270評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件乌企,死亡現(xiàn)場離奇詭異,居然都是意外死亡成玫,警方通過查閱死者的電腦和手機(jī)加酵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評論 3 395
  • 文/潘曉璐 我一進(jìn)店門端辱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人虽画,你說我怎么就攤上這事舞蔽。” “怎么了码撰?”我有些...
    開封第一講書人閱讀 165,630評論 0 356
  • 文/不壞的土叔 我叫張陵渗柿,是天一觀的道長。 經(jīng)常有香客問我脖岛,道長朵栖,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,906評論 1 295
  • 正文 為了忘掉前任柴梆,我火速辦了婚禮陨溅,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘绍在。我一直安慰自己门扇,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,928評論 6 392
  • 文/花漫 我一把揭開白布偿渡。 她就那樣靜靜地躺著臼寄,像睡著了一般。 火紅的嫁衣襯著肌膚如雪溜宽。 梳的紋絲不亂的頭發(fā)上吉拳,一...
    開封第一講書人閱讀 51,718評論 1 305
  • 那天,我揣著相機(jī)與錄音适揉,去河邊找鬼留攒。 笑死,一個(gè)胖子當(dāng)著我的面吹牛嫉嘀,可吹牛的內(nèi)容都是我干的炼邀。 我是一名探鬼主播,決...
    沈念sama閱讀 40,442評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼吃沪,長吁一口氣:“原來是場噩夢啊……” “哼汤善!你這毒婦竟也來了什猖?” 一聲冷哼從身側(cè)響起票彪,我...
    開封第一講書人閱讀 39,345評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎不狮,沒想到半個(gè)月后降铸,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,802評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡摇零,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,984評論 3 337
  • 正文 我和宋清朗相戀三年推掸,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,117評論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡谅畅,死狀恐怖登渣,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情毡泻,我是刑警寧澤胜茧,帶...
    沈念sama閱讀 35,810評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站仇味,受9級特大地震影響呻顽,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜丹墨,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,462評論 3 331
  • 文/蒙蒙 一廊遍、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧贩挣,春花似錦喉前、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至搪搏,卻和暖如春狭握,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背疯溺。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評論 1 272
  • 我被黑心中介騙來泰國打工论颅, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人囱嫩。 一個(gè)月前我還...
    沈念sama閱讀 48,377評論 3 373
  • 正文 我出身青樓恃疯,卻偏偏與公主長得像,于是被迫代替她去往敵國和親墨闲。 傳聞我的和親對象是個(gè)殘疾皇子今妄,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,060評論 2 355

推薦閱讀更多精彩內(nèi)容