Source: renderer/capabilities.js

import extensions from './extensions';
import {
    isWebGL2
} from '../utils/util';

/**
 * WebGL 能力
 * @namespace capabilities
 * @type {Object}
 */
const capabilities = {
    /**
     * 是否是 WebGL2
     * @type {boolean}
     */
    isWebGL2: false,
    /**
     * 最大纹理数量
     * @type {Number}
     */
    MAX_TEXTURE_INDEX: null,

    /**
     * 最高着色器精度, 可以是以下值:highp, mediump, lowp
     * @type {String}
     */
    MAX_PRECISION: null,

    /**
     * 最高顶点着色器精度, 可以是以下值:highp, mediump, lowp
     * @type {String}
     */
    MAX_VERTEX_PRECISION: null,

    /**
     * 最高片段着色器精度, 可以是以下值:highp, mediump, lowp
     * @type {String}
     */
    MAX_FRAGMENT_PRECISION: null,

    /**
     * 顶点浮点数纹理
     * @type {Boolean}
     */
    VERTEX_TEXTURE_FLOAT: null,

    /**
     * 片段浮点数纹理
     * @type {Boolean}
     */
    FRAGMENT_TEXTURE_FLOAT: null,

    /**
     * MAX_TEXTURE_MAX_ANISOTROPY
     * @type {Number}
     */
    MAX_TEXTURE_MAX_ANISOTROPY: 1,

    /**
     * @type {number}
     */
    MAX_RENDERBUFFER_SIZE: undefined,

    /**
     * @type {number}
     */
    MAX_COMBINED_TEXTURE_IMAGE_UNITS: undefined,

    /**
     * @type {number}
     */
    MAX_CUBE_MAP_TEXTURE_SIZE: undefined,

    /**
     * @type {number}
     */
    MAX_FRAGMENT_UNIFORM_VECTORS: undefined,

    /**
     * @type {number}
     */
    MAX_TEXTURE_IMAGE_UNITS: undefined,

    /**
     * @type {number}
     */
    MAX_TEXTURE_SIZE: undefined,

    /**
     * @type {number}
     */
    MAX_VARYING_VECTORS: undefined,

    /**
     * @type {number}
     */
    MAX_VERTEX_ATTRIBS: undefined,

    /**
     * @type {number}
     */
    MAX_VERTEX_TEXTURE_IMAGE_UNITS: undefined,

    /**
     * @type {number}
     */
    MAX_VERTEX_UNIFORM_VECTORS: undefined,

    /**
     * 初始化
     * @param {WebGLRenderingContext} gl
     */
    init(gl) {
        this.gl = gl;
        this.isWebGL2 = isWebGL2(gl);
        const arr = [
            'MAX_RENDERBUFFER_SIZE',
            'MAX_COMBINED_TEXTURE_IMAGE_UNITS',
            'MAX_CUBE_MAP_TEXTURE_SIZE',
            'MAX_FRAGMENT_UNIFORM_VECTORS',
            'MAX_TEXTURE_IMAGE_UNITS',
            'MAX_TEXTURE_SIZE',
            'MAX_VARYING_VECTORS',
            'MAX_VERTEX_ATTRIBS',
            'MAX_VERTEX_TEXTURE_IMAGE_UNITS',
            'MAX_VERTEX_UNIFORM_VECTORS',
        ];

        arr.forEach((name) => {
            this.get(name);
        });

        this.MAX_TEXTURE_INDEX = this.MAX_COMBINED_TEXTURE_IMAGE_UNITS - 1;
        this.MAX_VERTEX_PRECISION = this._getMaxSupportPrecision(gl.VERTEX_SHADER);
        this.MAX_FRAGMENT_PRECISION = this._getMaxSupportPrecision(gl.FRAGMENT_SHADER);
        this.MAX_PRECISION = this.getMaxPrecision(this.MAX_FRAGMENT_PRECISION, this.MAX_VERTEX_PRECISION);

        this.VERTEX_TEXTURE_FLOAT = !!extensions.texFloat && this.MAX_VERTEX_TEXTURE_IMAGE_UNITS > 0;
        this.FRAGMENT_TEXTURE_FLOAT = !!extensions.texFloat;
        this.FRAG_DEPTH = !!extensions.fragDepth;

        this.SHADER_TEXTURE_LOD = !!extensions.shaderTextureLod;
        this.DRAW_BUFFERS = !!extensions.drawBuffers;

        if (extensions.textureFilterAnisotropic) {
            this.MAX_TEXTURE_MAX_ANISOTROPY = gl.getParameter(extensions.textureFilterAnisotropic.MAX_TEXTURE_MAX_ANISOTROPY_EXT);
        }
    },
    /**
     * 获取 WebGL 能力
     * @param  {String} name
     * @return {Number|String}
     */
    get(name) {
        const gl = this.gl;
        let value = this[name];
        if (value === undefined) {
            value = this[name] = gl.getParameter(gl[name]);
        }

        return value;
    },
    _getMaxSupportPrecision(shaderType) {
        const gl = this.gl;

        let maxPrecision = 'lowp';

        if (gl.getShaderPrecisionFormat) {
            const precisions = [{
                name: 'highp',
                type: gl.HIGH_FLOAT,
            }, {
                name: 'mediump',
                type: gl.MEDIUM_FLOAT
            }];

            for (let i = 0; i < precisions.length; i++) {
                const precision = precisions[i];
                const precisionFormat = gl.getShaderPrecisionFormat(shaderType, precision.type) || {};
                if (precisionFormat.precision > 0) {
                    maxPrecision = precision.name;
                    break;
                }
            }
        } else {
            maxPrecision = 'mediump';
        }

        return maxPrecision;
    },
    /**
     * 获取最大支持精度
     * @param  {String} a
     * @param  {String} b
     * @return {String}
     */
    getMaxPrecision(a, b) {
        if (a === 'highp' || (a === 'mediump' && b === 'lowp')) {
            return b;
        }

        return a;
    }
};

export default capabilities;