egret_渲染源码阅读

记一次对egret性能优化 - drawcall , 先了解其渲染流程 和 合批 机制, 才能对症下药. 这里说的是 WebGL, 主要针对Texture合批
阅读之后发现其webgl渲染流程和 cocos 3.x 版本的渲染流程是极其相似的. 可以参考之前总结的cocos绘制流程: https://blog.csdn.net/yangxuan0261/article/details/49981347
egret GitHub 源码: https://github.com/egret-labs/egret-core


断点调试 绘制

如果了解 cocos 3.x 渲染流程的话, 知道其关键 合批 的操作是在 渲染命令中做的.
快速了解渲染流程的话, 可以设个断点, 通过调用栈了解 及其周边代码.
F12 展开调试面板, 在 /libs/modules/egret/egret.web.js 中断点

看其调用栈


源码阅读

绘制合批

上图是断点 渲染合批的关键方法, 渲染命令, 这里只看 贴图的合批, 渲染命名管理器类

export class WebGLDrawCmdManager {
...
public pushDrawTexture(texture:any, count:number = 2, filter?:any, textureWidth?:number, textureHeight?:number):void {
            if(filter) {
                // 目前有滤镜的情况下不会合并绘制, 这个filter会打断合批, 不止是滤镜, 颜色也会, 在egret修改颜色就是通过这个 filter 去修改的
               ...
            } else {

                if (this.drawDataLen == 0 || this.drawData[this.drawDataLen - 1].type != DRAWABLE_TYPE.TEXTURE || texture != this.drawData[this.drawDataLen - 1].texture || this.drawData[this.drawDataLen - 1].filter) { //这里是决定不合批的地方, 只要又一个条件不符合, 就会新建一个批次, 可以看到 相邻绘制命令 下贴图是同一张也是其中一个条件
            ...
            }
        }
  • 里面的 this.drawData 相当与 cocos 中的 renderqueue (没记错的话)

绘制

把绘制命令中的所有 data(批次) 执行一遍绘制

export class WebGLRenderContext {
...
 public $drawWebGL() {
        let length = this.drawCmdManager.drawDataLen;
            let offset = 0;
            for (let i = 0; i < length; i++) {
                let data = this.drawCmdManager.drawData[i]; //合好批次的绘制命名(其实数据data), 并没有像 cocos 一样提供回调函数, 然用户自己写绘制的代码
                offset = this.drawData(data, offset);

使用对应的 shader 绘制该批次

export class WebGLRenderContext {
...
    private drawData(data: any, offset: number) {
        ...
        switch (data.type) {
                case DRAWABLE_TYPE.TEXTURE:
                    if (filter) {
                        if (filter.type === "custom") {
                            program = EgretWebGLProgram.getProgram(gl, filter.$vertexSrc, filter.$fragmentSrc, filter.$shaderKey); // 使用了 滤镜 等特殊效果的, 会用不同shader来绘制这一批次
            ...
            offset += this.drawTextureElements(data, offset); // 执行一次绘制

提交绘制数据给图形显卡

        private drawTextureElements(data: any, offset: number): number {
            let gl: any = this.context;
            gl.bindTexture(gl.TEXTURE_2D, data.texture);
            let size = data.count * 3;
            gl.drawElements(gl.TRIANGLES, size, gl.UNSIGNED_SHORT, offset * 2);
            return size;
        }

中断合批的各种操作

主要看 WebGLDrawCmdManager.tsthis.drawData 这个 绘制数据队列 会不会增加.

    export const enum DRAWABLE_TYPE {
        TEXTURE, // 除这个之外的所有操作都会造成合批中断
        RECT, // 举行
        PUSH_MASK, // 遮罩
        POP_MASK,
        BLEND, // 混合
        RESIZE_TARGET,
        CLEAR_COLOR, 
        ACT_BUFFER,
        ENABLE_SCISSOR, // 剪裁
        DISABLE_SCISSOR,
        SMOOTHING
    }

还有 修改颜色 (filter, 滤镜等效果), 系统字


修改颜色

会中断 Texture 的合批

    export function setColorMatrix(obj: fairygui.GObject | egret.DisplayObject, matrix: number[]) {
        let edo = obj instanceof fairygui.GObject ? obj.displayObject : obj;
        let filters = (edo.filters || []).filter(f => !(f instanceof egret.ColorMatrixFilter));

        filters.push(new egret.ColorMatrixFilter(matrix));
        edo.filters = filters; // 这个filters 会中断合批, 所以对dc有要求的话,这个要慎用
    }

系统字

会中断 Texture 的合批, 且相邻节点的 系统字并不会合批, 也就是说 每一个系统字节点都是一个批次, 其会打断 Texture 的合批

export class WebGLRenderer implements sys.SystemRenderer {
...
 private renderText(node: sys.TextNode, buffer: WebGLRenderBuffer): void {
     ...
     // 拷贝canvas到texture
     let texture = node.$texture;
     if (!texture) {
         texture = buffer.context.createTexture(surface);
         node.$texture = texture;
     } else {
         // 重新拷贝新的图像
         buffer.context.updateTexture(texture, surface);
     }
        ...
        buffer.context.drawTexture(node.$texture, 0, 0, textureWidth, textureHeight, 0, 0, textureWidth / canvasScaleX, textureHeight / canvasScaleY, textureWidth, textureHeight);

你可能感兴趣的:(H5)