UNPKG

@awayjs/stage

Version:
266 lines (265 loc) 11.3 kB
import { AGALTokenizer } from '../aglsl/AGALTokenizer'; import { AGLSLParser } from '../aglsl/AGLSLParser'; import { Settings } from '../Settings'; import { WEBGL_METHOD_MAP } from './WebGLDataMapping'; var TEST_PLACE = /(#define|#version|precision).*\n/gi; var ProgramWebGL = /** @class */ (function () { function ProgramWebGL(_context) { this._context = _context; this._id = ProgramWebGL.ProgramID++; this._uniformCache = new Array(16); this._focusId = 0; this._gl = _context._gl; } Object.defineProperty(ProgramWebGL.prototype, "focusId", { /** * @description Changed only after rebound a shader. Can be used for partial upload */ get: function () { return this._focusId; }, enumerable: false, configurable: true }); ProgramWebGL.prototype.upload = function (vertexProgram, fragmentProgram) { //detect whether highp can be used var vertexPrecision = this._gl.getShaderPrecisionFormat(this._gl.VERTEX_SHADER, this._gl.HIGH_FLOAT).precision; var fragmentPrecision = this._gl.getShaderPrecisionFormat(this._gl.FRAGMENT_SHADER, this._gl.HIGH_FLOAT).precision; var vertexString = ProgramWebGL._aglslParser.parse(ProgramWebGL._tokenizer.decribeAGALPart(vertexProgram), vertexPrecision ? 'highp' : 'mediump', this._context.glVersion === 2); var fragmentString = ProgramWebGL._aglslParser.parse(ProgramWebGL._tokenizer.decribeAGALPart(fragmentProgram), fragmentPrecision ? 'highp' : 'mediump', this._context.glVersion === 2); if (!this.name) { this.name = 'PROG_AGAL_' + this._id; } this.uploadRaw(vertexString, fragmentString); }; ProgramWebGL.prototype.uploadRaw = function (vertexGLSL, fragmentGLSL) { if (!this.name) { this.name = 'PROG_GLSL_' + this._id; } var key = vertexGLSL + fragmentGLSL; if (key in ProgramWebGL.programCache) { this._program = ProgramWebGL.programCache[key]; this._program.usage++; return; } vertexGLSL = this.insertName(vertexGLSL); fragmentGLSL = this.insertName(fragmentGLSL); var vertexShader = this._gl.createShader(this._gl.VERTEX_SHADER); var fragmentShader = this._gl.createShader(this._gl.FRAGMENT_SHADER); var program = this._gl.createProgram(); this._context.stats.counter.program++; this._gl.shaderSource(vertexShader, vertexGLSL); this._gl.compileShader(vertexShader); if (!this._gl.getShaderParameter(vertexShader, this._gl.COMPILE_STATUS)) throw new Error(this._gl.getShaderInfoLog(vertexShader)); this._gl.shaderSource(fragmentShader, fragmentGLSL); this._gl.compileShader(fragmentShader); if (!this._gl.getShaderParameter(fragmentShader, this._gl.COMPILE_STATUS)) throw new Error(this._gl.getShaderInfoLog(fragmentShader)); this._gl.attachShader(program, vertexShader); this._gl.attachShader(program, fragmentShader); this._gl.linkProgram(program); if (!this._gl.getProgramParameter(program, this._gl.LINK_STATUS)) throw new Error(this._gl.getProgramInfoLog(program)); // they will alive while any linked programs alive this._gl.deleteShader(vertexShader); this._gl.deleteShader(fragmentShader); this._program = { program: program, uniforms: {}, attributes: {}, usage: 1 }; ProgramWebGL.programCache[key] = this._program; this.reset(); this.grabLocationData(vertexGLSL, fragmentGLSL); }; ProgramWebGL.prototype.grabLocationData = function (vert, frag) { var rawUniforms = this._program.uniforms; var rawAttrs = this._program.attributes; var gl = this._gl; var p = this._program.program; var ucount = gl.getProgramParameter(p, gl.ACTIVE_UNIFORMS); var acount = gl.getProgramParameter(p, gl.ACTIVE_ATTRIBUTES); for (var i = 0; i < ucount; i++) { var info = gl.getActiveUniform(p, i); var idx = info.name.indexOf('['); var record = { type: info.type, size: info.size, location: gl.getUniformLocation(p, info.name) }; rawUniforms[idx === -1 ? info.name : info.name.substring(0, idx)] = record; } for (var i = 0; i < acount; i++) { var info = gl.getActiveAttrib(p, i); rawAttrs[info.name] = { type: info.type, size: info.size, location: gl.getAttribLocation(p, info.name) }; } // MAP fs{numer} to samplers name by their order in shader! // THIS is important, because chrome mobile can reorder uniforms if (Settings.UNSAFE_USE_AUTOINDEXED_SAMPLER) { var searchIndex = frag.indexOf('uniform sampler', 0); var index = 0; while (searchIndex >= 0) { var end = frag.indexOf(';', searchIndex + 2); var block = frag.substring(searchIndex, end).split(' '); var name_1 = block[block.length - 1]; if (!rawUniforms['fs' + index]) { rawUniforms['fs' + index] = rawUniforms[name_1]; } searchIndex = frag.indexOf('uniform sampler', end); index++; } } // map atributes by write order (how it used in gl source) if (Settings.UNSAFE_USE_AUTOINDEXED_ATTRIBUTES) { var searchIndex = vert.indexOf('attribute', 0); var index = 0; while (searchIndex >= 0) { var end = vert.indexOf(';', searchIndex + 2); var block = vert.substring(searchIndex, end).split(' '); var name_2 = block[block.length - 1]; if (!rawAttrs['va' + index]) { rawAttrs['va' + index] = rawAttrs[name_2]; } searchIndex = vert.indexOf('attribute', end); index++; } } }; ProgramWebGL.prototype.insertName = function (shader) { var mathes = shader.match(TEST_PLACE) || []; var last = mathes[mathes.length - 1]; var corret = last ? shader.lastIndexOf(last) + last.length : 0; return shader.substr(0, corret) + "\n#define SHADER_NAME ".concat(this.name, "\n\n") + shader.substr(corret); }; ProgramWebGL.prototype.reset = function () { this._uniformCache.fill(''); }; ProgramWebGL.prototype.getUniformLocation = function (programType, indexOrName) { if (indexOrName === void 0) { indexOrName = -1; } var isIndex = typeof indexOrName === 'number'; var name = isIndex ? ProgramWebGL._getAGALUniformName(programType, indexOrName) : indexOrName; var info = this._program.uniforms[name]; if (!info) return null; return info.location; }; ProgramWebGL.prototype.getAttribLocation = function (index) { var info = this._program.attributes['va' + index]; if (!info) return -1; return info.location; }; ProgramWebGL._getAGALUniformName = function (type, index) { if (index === void 0) { index = -1; } return (index === -1) ? this._uniformLocationNameDictionary[type] : this._uniformLocationNameDictionary[type] + index; }; ProgramWebGL.prototype._needCache = function (index, value) { var cached = this._uniformCache[index]; var hash = value instanceof Float32Array ? new Uint32Array(value.buffer).join('') : '' + value; // already uploaded return (cached && hash === cached) ? void 0 : hash; }; ProgramWebGL.prototype.uploadUniform = function (name, data) { var info = this._program.uniforms[name]; if (!info) return false; if (!WEBGL_METHOD_MAP[info.type]) { throw ('[ProgramWebGL] Unsupported uniform type:' + info.type); } var _a = WEBGL_METHOD_MAP[info.type], size = _a.size, method = _a.method; if (size === 1) { if (typeof data === 'number' || info.size === 1) { this._gl[method](info.location, typeof data === 'number' ? data : data[0]); } else { this._gl[method + 'v'](info.location, data); } return true; } var arr = data; if (arr.length !== info.size * size) { throw ("[ProgramWebGL] Invalid data length for ".concat(name, ", expected ").concat(info.size * size, ", actual ").concat(arr.length)); } if (info.type === this._gl.FLOAT_MAT2 || info.type === this._gl.FLOAT_MAT3 || info.type === this._gl.FLOAT_MAT4) { this._gl[method](info.location, false, arr); } else { this._gl[method](info.location, arr); } }; ProgramWebGL.prototype.uniform1i = function (type, index, value) { var location = this.getUniformLocation(type, index); if (!location) { return; } if (Settings.ENABLE_UNIFORM_CACHE) { var hash = this._needCache(type * 4 + index + 1, value); // return undef hash if not require to uppload; if (hash === void 0) { return; } this._uniformCache[type * 4 + index + 1] = hash; } this._gl.uniform1i(location, value); }; ProgramWebGL.prototype.uniform4fv = function (type, value) { var location = this.getUniformLocation(type); if (!location) { return; } if (Settings.ENABLE_UNIFORM_CACHE) { var hash = this._needCache(type * 4, value); // return undef hash if not require to uppload; if (hash === void 0) { return; } this._uniformCache[type * 4] = hash; } this._gl.uniform4fv(location, value); }; ProgramWebGL.prototype.dispose = function () { // not real delete progs, because maybe will be recreted in nearest future // then progs in prety small, we can store 1000 + without overhead // reupload is more expensive // this._context.stats.progs--; // this._gl.deleteProgram(this._program); this.reset(); }; ProgramWebGL.prototype.focusProgram = function () { this._focusId++; this._uniformCache.fill(''); this._gl.useProgram(this._program.program); }; Object.defineProperty(ProgramWebGL.prototype, "glProgram", { get: function () { return this._program; }, enumerable: false, configurable: true }); ProgramWebGL.programCache = {}; ProgramWebGL.ProgramID = 0; ProgramWebGL._tokenizer = new AGALTokenizer(); ProgramWebGL._aglslParser = new AGLSLParser(); ProgramWebGL._uniformLocationNameDictionary = ['fc', 'fs', 'vc']; return ProgramWebGL; }()); export { ProgramWebGL };