美文网首页
硬件渲染-绘制一帧

硬件渲染-绘制一帧

作者: gczxbb | 来源:发表于2018-10-01 19:36 被阅读47次

    前面已经介绍过同步帧状态。 硬件渲染一次任务整体流程图.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流程解析.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状态
            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结构关系图。

    LayerRenderer结构关系图.jpg

    因此,内部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立即渲染与延迟合并流程图如下。

    RenderNode立即绘制与延迟合并流程图.jpg

    二者区别。
    立即渲染:在遍历整个操作过程中,遇到一个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。

    BatchLookup数组枚举.jpg

    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。

    Batch结构图.jpg

    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方法。


    任重而道远

    相关文章

      网友评论

          本文标题:硬件渲染-绘制一帧

          本文链接:https://www.haomeiwen.com/subject/ewunoftx.html