@awayjs/stage
Version:
Stage for AwayJS
266 lines (265 loc) • 11.3 kB
JavaScript
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 };