Source: renderer/WebGLState.js

import Class from '../core/Class';
import { isWebGL2 } from '../utils/util';

/**
 * WebGL 状态管理,减少 api 调用
 * @class
 */
const WebGLState = Class.create(/** @lends WebGLState.prototype */ {
    /**
     * @default WebGLState
     * @type {String}
     */
    className: 'WebGLState',

    /**
     * @default true
     * @type {Boolean}
     */
    isWebGLState: true,

    /**
     * 系统framebuffer
     * @default true
     * @type {null}
     */
    systemFramebuffer: null,

    /**
    * 是否是 WebGL2
    * @default false
    * @type {Boolean}
    */
    isWebGL2: false,

    /**
     * @constructs
     * @param  {WebGLRenderingContext} gl
     */
    constructor(gl) {
        /**
         * gl
         * @type {WebGLRenderingContext}
         */
        this.gl = gl;
        this.isWebGL2 = isWebGL2(gl);
        this.reset();
    },
    /**
     * 重置状态
     */
    reset() {
        this._dict = {};
        this.activeTextureIndex = null;
        this.textureUnitDict = {};
        this.currentFramebuffer = null;
        this.preFramebuffer = null;
        this._pixelStorei = {};
    },
    /**
     * enable
     * @param  {GLenum} capability
     */
    enable(capability) {
        const value = this._dict[capability];
        if (value !== true) {
            this._dict[capability] = true;
            this.gl.enable(capability);
        }
    },
    /**
     * disable
     * @param  {GLenum} capability
     */
    disable(capability) {
        const value = this._dict[capability];
        if (value !== false) {
            this._dict[capability] = false;
            this.gl.disable(capability);
        }
    },
    /**
     * bindFramebuffer
     * @param  {GLenum} target
     * @param  {WebGLFramebuffer} framebuffer
     */
    bindFramebuffer(target, framebuffer) {
        if (this.currentFramebuffer !== framebuffer) {
            this.preFramebuffer = this.currentFramebuffer;
            this.currentFramebuffer = framebuffer;
            this.gl.bindFramebuffer(target, framebuffer);
        }
    },
    /**
     * 绑定系统framebuffer
     */
    bindSystemFramebuffer() {
        this.bindFramebuffer(this.gl.FRAMEBUFFER, this.systemFramebuffer);
    },
    /**
     * useProgram
     * @param  { WebGLProgram} program
     */
    useProgram(program) {
        this.set1('useProgram', program);
    },
    /**
     * depthFunc
     * @param  {GLenum } func
     */
    depthFunc(func) {
        this.set1('depthFunc', func);
    },
    /**
     * depthMask
     * @param  {GLenum } flag
     */
    depthMask(flag) {
        this.set1('depthMask', flag);
    },
    /**
     * clear
     * @param  {Number} mask
     */
    clear(mask) {
        this.gl.clear(mask);
    },
    /**
     * depthRange
     * @param  {Number} zNear
     * @param  {Number} zFar
     */
    depthRange(zNear, zFar) {
        this.set2('depthRange', zNear, zFar);
    },
    /**
     * stencilFunc
     * @param  {GLenum} func
     * @param  {Number} ref
     * @param  {Number} mask
     */
    stencilFunc(func, ref, mask) {
        this.set3('stencilFunc', func, ref, mask);
    },
    /**
     * stencilMask
     * @param  {Number} mask
     */
    stencilMask(mask) {
        this.set1('stencilMask', mask);
    },
    /**
     * stencilOp
     * @param  {GLenum} fail
     * @param  {GLenum} zfail
     * @param  {GLenum} zpass
     */
    stencilOp(fail, zfail, zpass) {
        this.set3('stencilOp', fail, zfail, zpass);
    },
    /**
     * colorMask
     * @param  {Boolean} red
     * @param  {Boolean} green
     * @param  {Boolean} blue
     * @param  {Boolean} alpha
     */
    colorMask(red, green, blue, alpha) {
        this.set4('colorMask', red, green, blue, alpha);
    },
    /**
     * cullFace
     * @param  {GLenum} mode
     */
    cullFace(mode) {
        this.set1('cullFace', mode);
    },
    /**
     * frontFace
     * @param  {GLenum} mode
     */
    frontFace(mode) {
        this.set1('frontFace', mode);
    },
    /**
     * blendFuncSeparate
     * @param  {GLenum} srcRGB
     * @param  {GLenum} dstRGB
     * @param  {GLenum} srcAlpha
     * @param  {GLenum} dstAlpha
     */
    blendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha) {
        this.set4('blendFuncSeparate', srcRGB, dstRGB, srcAlpha, dstAlpha);
    },
    /**
     * blendEquationSeparate
     * @param  {GLenum} modeRGB
     * @param  {GLenum} modeAlpha
     */
    blendEquationSeparate(modeRGB, modeAlpha) {
        this.set2('blendEquationSeparate', modeRGB, modeAlpha);
    },
    /**
     * pixelStorei
     * @param  {GLenum} pname
     * @param  {GLenum} param
     */
    pixelStorei(pname, param) {
        const currentParam = this._pixelStorei[pname];
        if (currentParam !== param) {
            this._pixelStorei[pname] = param;
            this.gl.pixelStorei(pname, param);
        }
    },
    /**
     * viewport
     * @param  {Number} x
     * @param  {Number} y
     * @param  {Number} width
     * @param  {Number} height
     */
    viewport(x, y, width, height) {
        this.set4('viewport', x, y, width, height);
    },
    /**
     * activeTexture
     * @param  {GLenum} texture
     */
    activeTexture(texture) {
        if (this.activeTextureIndex !== texture) {
            this.activeTextureIndex = texture;
            this.gl.activeTexture(texture);
        }
    },
    /**
     * bindTexture
     * @param  {GLenum} target
     * @param  {WebGLTexture } texture
     */
    bindTexture(target, texture) {
        let textureUnit = this.getActiveTextureUnit();
        if (textureUnit[target] !== texture) {
            textureUnit[target] = texture;
            this.gl.bindTexture(target, texture);
        }
    },
    /**
     * 获取当前激活的纹理对象
     * @return {GLenum}
     */
    getActiveTextureUnit() {
        let textureUnit = this.textureUnitDict[this.activeTextureIndex];
        if (!textureUnit) {
            textureUnit = this.textureUnitDict[this.activeTextureIndex] = {};
        }
        return textureUnit;
    },
    /**
     * 调 gl 1参数方法
     * @private
     * @param  {String} name  方法名
     * @param  {Number|Object} param 方法参数
     */
    set1(name, param) {
        const value = this._dict[name];
        if (value !== param) {
            this._dict[name] = param;
            this.gl[name](param);
        }
    },
    /**
     * 调 gl 2参数方法
     * @private
     * @param  {String} name  方法名
     * @param  {Number|Object} param0 方法参数
     * @param  {Number|Object} param1 方法参数
     */
    set2(name, param0, param1) {
        let value = this._dict[name];
        if (!value) {
            value = this._dict[name] = [];
        }

        if (value[0] !== param0 || value[1] !== param1) {
            value[0] = param0;
            value[1] = param1;
            this.gl[name](param0, param1);
        }
    },
    /**
     * 调 gl 3参数方法
     * @private
     * @param  {String} name  方法名
     * @param  {Number|Object} param0 方法参数
     * @param  {Number|Object} param1 方法参数
     * @param  {Number|Object} param2 方法参数
     */
    set3(name, param0, param1, param2) {
        let value = this._dict[name];
        if (!value) {
            value = this._dict[name] = [];
        }

        if (value[0] !== param0 || value[1] !== param1 || value[2] !== param2) {
            value[0] = param0;
            value[1] = param1;
            value[2] = param2;
            this.gl[name](param0, param1, param2);
        }
    },
    /**
     * 调 gl 4参数方法
     * @private
     * @param  {String} name  方法名
     * @param  {Number|Object} param0 方法参数
     * @param  {Number|Object} param1 方法参数
     * @param  {Number|Object} param2 方法参数
     * @param  {Number|Object} param3 方法参数
     */
    set4(name, param0, param1, param2, param3) {
        let value = this._dict[name];
        if (!value) {
            value = this._dict[name] = [];
        }

        if (value[0] !== param0 || value[1] !== param1 || value[2] !== param2 || value[3] !== param3) {
            value[0] = param0;
            value[1] = param1;
            value[2] = param2;
            value[3] = param3;
            this.gl[name](param0, param1, param2, param3);
        }
    },
    get(name) {
        return this._dict[name];
    }
});

export default WebGLState;