论坛里有不少关于drawcall优化的方案,看到一个水友发的方案:
UI渲染合批 - Creator 3.x - Cocos中文社区
这个方案原理是给节点增加一个depth属性,然后在渲染batcher-2d.ts的walk方法中,按照depth重新对渲染排序,根据depth从小到大进行渲染。
例如原本渲染顺序如下,drawcall=4

采用depth方案后,给2个sprite设置depth为0

给2个label设置depth为1

那么渲染顺序变成如下,2个sprite为第一第二,2个label为第三第四,drawcall由原来的4变成2。

一部分代码如下:
    /**
     * 按指定的depth渲染
     * @param a 
     * @param b 
     * @returns 
     */
    private sortRenderFunc(a: UIRenderer | UIMeshRenderer, b: UIRenderer | UIMeshRenderer) {
        return a.depth - b.depth
    }
    private customFillBuffers() {
        let renders = this._delayFillRenderers;
        if(renders.length > 0) {
            renders.sort(this.sortRenderFunc)
            let render: UIRenderer
            for(let i = 0, len = renders.length; i < len; i++) {
                render = renders[i]
                // Render assembler update logic
                if (render.enabledInHierarchy) {
                    render.fillBuffers(this);// for rendering
                }
                let uiProps = render.node._uiProps
                if(uiProps.colorDirty) {
                    if (!render.useVertexOpacity && render.renderData && render.renderData.vertexCount > 0) {
                        let opacity = render && render.color ? render.color.a / 255 : 1;
                        opacity *= uiProps.opacity;
                        // HARD COUPLING
                        updateOpacity(render.renderData, opacity);
                        const buffer = render.renderData.getMeshBuffer();
                        if (buffer) {
                            buffer.setDirty();
                        }
                    }
                    uiProps.colorDirty = false
                }
            }
            renders.length = 0
        }
    }
    public walk (node: Node, level = 0): void {
        if (!node.activeInHierarchy) {
            return;
        }
        const children = node.children;
        const uiProps = node._uiProps;
        const render = uiProps.uiComp as UIRenderer;
        if(node.isBatchRoot) {
            this.customFillBuffers();
            this._batchRootDepth++;
        }
        // Save opacity
        const parentOpacity = this._pOpacity;
        let opacity = parentOpacity;
        // TODO Always cascade ui property's local opacity before remove it
        const selfOpacity = render && render.color ? render.color.a / 255 : 1;
        this._pOpacity = opacity *= selfOpacity * uiProps.localOpacity;
        // TODO Set opacity to ui property's opacity before remove it
        uiProps.setOpacity(opacity);
        if (!approx(opacity, 0, EPSILON)) {
            if (uiProps.colorDirty) {
            // Cascade color dirty state
                this._opacityDirty++;
                uiProps.colorDirty = false
            }
            
            if(render) {
                if(this._batchRootDepth && !node.isBatchRoot) {  //mask要马上填充
                    //延迟填充数据
                    if(this._opacityDirty) {
                        uiProps.colorDirty = true
                    }
                    this._delayFillRenderers.push(render)
                } else {
                    // Render assembler update logic
                    if (render && render.enabledInHierarchy) {
                        render.fillBuffers(this);// for rendering
                    }
                    // Update cascaded opacity to vertex buffer
                    if (this._opacityDirty && !render.useVertexOpacity && render.renderData && render.renderData.vertexCount > 0) {
                        // HARD COUPLING
                        updateOpacity(render.renderData, opacity);
                        const buffer = render.renderData.getMeshBuffer();
                        if (buffer) {
                            buffer.setDirty();
                        }
                    }
                }
            }
            if (children.length > 0 && !node._static) {
                for (let i = 0; i < children.length; ++i) {
                    const child = children[i];
                    this.walk(child, level);
                }
            }
            if (uiProps.colorDirty) {
            // Reduce cascaded color dirty state
                this._opacityDirty--;
                // Reset color dirty
                //uiProps.colorDirty = false;
            }
        }
        if(node.isBatchRoot) {
            this.customFillBuffers();
            this._batchRootDepth--;
        }
        // Restore opacity
        this._pOpacity = parentOpacity;
        // Post render assembler update logic
        // ATTENTION: Will also reset colorDirty inside postUpdateAssembler
        if (render && render.enabledInHierarchy) {
            render.postUpdateAssembler(this);
            if ((render.stencilStage === Stage.ENTER_LEVEL || render.stencilStage === Stage.ENTER_LEVEL_INVERTED)
            && (StencilManager.sharedManager!.getMaskStackSize() > 0)) {
                this.autoMergeBatches(this._currComponent!);
                this.resetRenderStates();
                StencilManager.sharedManager!.exitMask();
            }
        }
        level += 1;
    }
这个方案是侵入式的,需要修改源码,web和小游戏改ts,原生改c++。
用winmerge工具对比源码和修改后的源码,还是改了不少地方。
具体代码获取看原帖:UI渲染合批 - Creator 3.x - Cocos中文社区

实践使用中,例如一个场景或弹窗,根节点绑定一个SetBatchRoot.s,表明这个根节点下所有子节点需要按照depth排序。

背景图、来自通用大图集的图片默认depth=0就行,label都设置为depth=10,1-9预留用于灵活设置。
使用起来还是挺方便的,一个原来100+的背包界面可以优化到10以下。
                    
                
                
            
        
浙公网安备 33010602011771号