mirror of
https://github.com/gpujs/gpu.js.git
synced 2026-01-25 16:08:02 +00:00
make uniforms only set if their values indeed change, and add tests
This commit is contained in:
parent
2f37e8762d
commit
5850030e81
@ -5,7 +5,7 @@
|
||||
* GPU Accelerated JavaScript
|
||||
*
|
||||
* @version 1.0.6
|
||||
* @date Sat Mar 03 2018 14:24:37 GMT-0500 (EST)
|
||||
* @date Tue Mar 06 2018 09:55:56 GMT-0500 (EST)
|
||||
*
|
||||
* @license MIT
|
||||
* The MIT License
|
||||
|
||||
2
bin/gpu-core.min.js
vendored
2
bin/gpu-core.min.js
vendored
@ -5,7 +5,7 @@
|
||||
* GPU Accelerated JavaScript
|
||||
*
|
||||
* @version 1.0.6
|
||||
* @date Sat Mar 03 2018 14:24:37 GMT-0500 (EST)
|
||||
* @date Tue Mar 06 2018 09:55:56 GMT-0500 (EST)
|
||||
*
|
||||
* @license MIT
|
||||
* The MIT License
|
||||
|
||||
116
bin/gpu.js
116
bin/gpu.js
@ -5,7 +5,7 @@
|
||||
* GPU Accelerated JavaScript
|
||||
*
|
||||
* @version 1.0.6
|
||||
* @date Sat Mar 03 2018 14:24:37 GMT-0500 (EST)
|
||||
* @date Tue Mar 06 2018 09:55:57 GMT-0500 (EST)
|
||||
*
|
||||
* @license MIT
|
||||
* The MIT License
|
||||
@ -2780,7 +2780,7 @@ function removeNoise(str) {
|
||||
}
|
||||
|
||||
module.exports = function (gpuKernel, name) {
|
||||
return '() => {\n ' + kernelRunShortcut.toString() + ';\n const utils = {\n allPropertiesOf: ' + removeNoise(utils.allPropertiesOf.toString()) + ',\n clone: ' + removeNoise(utils.clone.toString()) + ',\n splitArray: ' + removeNoise(utils.splitArray.toString()) + ',\n getArgumentType: ' + removeNoise(utils.getArgumentType.toString()) + ',\n getDimensions: ' + removeNoise(utils.getDimensions.toString()) + ',\n dimToTexSize: ' + removeNoise(utils.dimToTexSize.toString()) + ',\n flattenTo: ' + removeNoise(utils.flattenTo.toString()) + ',\n flatten2dArrayTo: ' + removeNoise(utils.flatten2dArrayTo.toString()) + ',\n flatten3dArrayTo: ' + removeNoise(utils.flatten3dArrayTo.toString()) + ',\n systemEndianness: \'' + removeNoise(utils.systemEndianness()) + '\',\n initWebGl: ' + removeNoise(utils.initWebGl.toString()) + ',\n isArray: ' + removeNoise(utils.isArray.toString()) + '\n };\n const Utils = utils;\n const canvases = [];\n const maxTexSizes = {};\n class ' + (name || 'Kernel') + ' {\n constructor() {\n this.maxTexSize = null;\n this.argumentsLength = 0;\n this._canvas = null;\n this._webGl = null;\n this.built = false;\n this.program = null;\n this.paramNames = ' + JSON.stringify(gpuKernel.paramNames) + ';\n this.paramTypes = ' + JSON.stringify(gpuKernel.paramTypes) + ';\n this.texSize = ' + JSON.stringify(gpuKernel.texSize) + ';\n this.output = ' + JSON.stringify(gpuKernel.output) + ';\n this.compiledFragShaderString = `' + gpuKernel.compiledFragShaderString + '`;\n\t\t this.compiledVertShaderString = `' + gpuKernel.compiledVertShaderString + '`;\n\t\t this.programUniformLocationCache = {};\n\t\t this.textureCache = {};\n\t\t this.subKernelOutputTextures = null;\n\t\t this.subKernelOutputVariableNames = null;\n }\n ' + removeFnNoise(gpuKernel._getFragShaderString.toString()) + '\n ' + removeFnNoise(gpuKernel._getVertShaderString.toString()) + '\n validateOptions() {}\n setupParams() {}\n setCanvas(canvas) { this._canvas = canvas; return this; }\n setWebGl(webGl) { this._webGl = webGl; return this; }\n ' + removeFnNoise(gpuKernel.getUniformLocation.toString()) + '\n ' + removeFnNoise(gpuKernel.setupParams.toString()) + '\n ' + removeFnNoise(gpuKernel.build.toString()) + '\n\t\t ' + removeFnNoise(gpuKernel.run.toString()) + '\n\t\t ' + removeFnNoise(gpuKernel._addArgument.toString()) + '\n\t\t ' + removeFnNoise(gpuKernel.getArgumentTexture.toString()) + '\n\t\t ' + removeFnNoise(gpuKernel.getTextureCache.toString()) + '\n\t\t ' + removeFnNoise(gpuKernel.getOutputTexture.toString()) + '\n\t\t ' + removeFnNoise(gpuKernel.renderOutput.toString()) + '\n\t\t ' + removeFnNoise(gpuKernel.updateMaxTexSize.toString()) + '\n\t\t ' + removeFnNoise(gpuKernel._setupOutputTexture.toString()) + '\n\t\t ' + removeFnNoise(gpuKernel.detachTextureCache.toString()) + '\n };\n return kernelRunShortcut(new Kernel());\n };';
|
||||
return '() => {\n ' + kernelRunShortcut.toString() + ';\n const utils = {\n allPropertiesOf: ' + removeNoise(utils.allPropertiesOf.toString()) + ',\n clone: ' + removeNoise(utils.clone.toString()) + ',\n splitArray: ' + removeNoise(utils.splitArray.toString()) + ',\n getArgumentType: ' + removeNoise(utils.getArgumentType.toString()) + ',\n getDimensions: ' + removeNoise(utils.getDimensions.toString()) + ',\n dimToTexSize: ' + removeNoise(utils.dimToTexSize.toString()) + ',\n flattenTo: ' + removeNoise(utils.flattenTo.toString()) + ',\n flatten2dArrayTo: ' + removeNoise(utils.flatten2dArrayTo.toString()) + ',\n flatten3dArrayTo: ' + removeNoise(utils.flatten3dArrayTo.toString()) + ',\n systemEndianness: \'' + removeNoise(utils.systemEndianness()) + '\',\n initWebGl: ' + removeNoise(utils.initWebGl.toString()) + ',\n isArray: ' + removeNoise(utils.isArray.toString()) + '\n };\n const Utils = utils;\n const canvases = [];\n const maxTexSizes = {};\n class ' + (name || 'Kernel') + ' {\n constructor() {\n this.maxTexSize = null;\n this.argumentsLength = 0;\n this._canvas = null;\n this._webGl = null;\n this.built = false;\n this.program = null;\n this.paramNames = ' + JSON.stringify(gpuKernel.paramNames) + ';\n this.paramTypes = ' + JSON.stringify(gpuKernel.paramTypes) + ';\n this.texSize = ' + JSON.stringify(gpuKernel.texSize) + ';\n this.output = ' + JSON.stringify(gpuKernel.output) + ';\n this.compiledFragShaderString = `' + gpuKernel.compiledFragShaderString + '`;\n\t\t this.compiledVertShaderString = `' + gpuKernel.compiledVertShaderString + '`;\n\t\t this.programUniformLocationCache = {};\n\t\t this.textureCache = {};\n\t\t this.subKernelOutputTextures = null;\n\t\t this.subKernelOutputVariableNames = null;\n\t\t this.uniform1fCache = {};\n\t\t this.uniform1iCache = {};\n\t\t this.uniform2fCache = {};\n\t\t this.uniform2fvCache = {};\n\t\t this.uniform3fvCache = {};\n }\n ' + removeFnNoise(gpuKernel._getFragShaderString.toString()) + '\n ' + removeFnNoise(gpuKernel._getVertShaderString.toString()) + '\n validateOptions() {}\n setupParams() {}\n setCanvas(canvas) { this._canvas = canvas; return this; }\n setWebGl(webGl) { this._webGl = webGl; return this; }\n ' + removeFnNoise(gpuKernel.getUniformLocation.toString()) + '\n ' + removeFnNoise(gpuKernel.setupParams.toString()) + '\n ' + removeFnNoise(gpuKernel.build.toString()) + '\n\t\t ' + removeFnNoise(gpuKernel.run.toString()) + '\n\t\t ' + removeFnNoise(gpuKernel._addArgument.toString()) + '\n\t\t ' + removeFnNoise(gpuKernel.getArgumentTexture.toString()) + '\n\t\t ' + removeFnNoise(gpuKernel.getTextureCache.toString()) + '\n\t\t ' + removeFnNoise(gpuKernel.getOutputTexture.toString()) + '\n\t\t ' + removeFnNoise(gpuKernel.renderOutput.toString()) + '\n\t\t ' + removeFnNoise(gpuKernel.updateMaxTexSize.toString()) + '\n\t\t ' + removeFnNoise(gpuKernel._setupOutputTexture.toString()) + '\n\t\t ' + removeFnNoise(gpuKernel.detachTextureCache.toString()) + '\n\t\t ' + removeFnNoise(gpuKernel.setUniform1f.toString()) + '\n\t\t ' + removeFnNoise(gpuKernel.setUniform1i.toString()) + '\n\t\t ' + removeFnNoise(gpuKernel.setUniform2f.toString()) + '\n\t\t ' + removeFnNoise(gpuKernel.setUniform2fv.toString()) + '\n\t\t ' + removeFnNoise(gpuKernel.setUniform3fv.toString()) + ' \n };\n return kernelRunShortcut(new Kernel());\n };';
|
||||
};
|
||||
},{"../../core/utils":25,"../kernel-run-shortcut":9}],14:[function(require,module,exports){
|
||||
'use strict';
|
||||
@ -2828,6 +2828,11 @@ module.exports = function (_KernelBase) {
|
||||
_this.extDrawBuffersMap = null;
|
||||
_this.outputTexture = null;
|
||||
_this.maxTexSize = null;
|
||||
_this.uniform1fCache = {};
|
||||
_this.uniform1iCache = {};
|
||||
_this.uniform2fCache = {};
|
||||
_this.uniform2fvCache = {};
|
||||
_this.uniform3fvCache = {};
|
||||
if (!_this._webGl) _this._webGl = utils.initWebGl(_this.getCanvas());
|
||||
return _this;
|
||||
}
|
||||
@ -3010,14 +3015,11 @@ module.exports = function (_KernelBase) {
|
||||
gl.scissor(0, 0, texSize[0], texSize[1]);
|
||||
|
||||
if (!this.hardcodeConstants) {
|
||||
var uOutputDimLoc = this.getUniformLocation('uOutputDim');
|
||||
gl.uniform3fv(uOutputDimLoc, this.threadDim);
|
||||
var uTexSizeLoc = this.getUniformLocation('uTexSize');
|
||||
gl.uniform2fv(uTexSizeLoc, texSize);
|
||||
this.setUniform3fv('uOutputDim', this.threadDim);
|
||||
this.setUniform2fv('uTexSize', texSize);
|
||||
}
|
||||
|
||||
var ratioLoc = this.getUniformLocation('ratio');
|
||||
gl.uniform2f(ratioLoc, texSize[0] / this.maxTexSize[0], texSize[1] / this.maxTexSize[1]);
|
||||
this.setUniform2f('ratio', texSize[0] / this.maxTexSize[0], texSize[1] / this.maxTexSize[1]);
|
||||
|
||||
this.argumentsLength = 0;
|
||||
for (var texIndex = 0; texIndex < paramNames.length; texIndex++) {
|
||||
@ -3189,6 +3191,71 @@ module.exports = function (_KernelBase) {
|
||||
value: function detachTextureCache(name) {
|
||||
delete this.textureCache[name];
|
||||
}
|
||||
}, {
|
||||
key: 'setUniform1f',
|
||||
value: function setUniform1f(name, value) {
|
||||
if (this.uniform1fCache.hasOwnProperty(name)) {
|
||||
var cache = this.uniform1fCache[name];
|
||||
if (value === cache) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.uniform1fCache[name] = value;
|
||||
var loc = this.getUniformLocation(name);
|
||||
this._webGl.uniform1f(loc, value);
|
||||
}
|
||||
}, {
|
||||
key: 'setUniform1i',
|
||||
value: function setUniform1i(name, value) {
|
||||
if (this.uniform1iCache.hasOwnProperty(name)) {
|
||||
var cache = this.uniform1iCache[name];
|
||||
if (value === cache) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.uniform1iCache[name] = value;
|
||||
var loc = this.getUniformLocation(name);
|
||||
this._webGl.uniform1i(loc, value);
|
||||
}
|
||||
}, {
|
||||
key: 'setUniform2f',
|
||||
value: function setUniform2f(name, value1, value2) {
|
||||
if (this.uniform2fCache.hasOwnProperty(name)) {
|
||||
var cache = this.uniform2fCache[name];
|
||||
if (value1 === cache[0] && value2 === cache[1]) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.uniform2fCache[name] = [value1, value2];
|
||||
var loc = this.getUniformLocation(name);
|
||||
this._webGl.uniform2f(loc, value1, value2);
|
||||
}
|
||||
}, {
|
||||
key: 'setUniform2fv',
|
||||
value: function setUniform2fv(name, value) {
|
||||
if (this.uniform2fvCache.hasOwnProperty(name)) {
|
||||
var cache = this.uniform2fvCache[name];
|
||||
if (value[0] === cache[0] && value[1] === cache[1]) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.uniform2fvCache[name] = value;
|
||||
var loc = this.getUniformLocation(name);
|
||||
this._webGl.uniform2fv(loc, value);
|
||||
}
|
||||
}, {
|
||||
key: 'setUniform3fv',
|
||||
value: function setUniform3fv(name, value) {
|
||||
if (this.uniform3fvCache.hasOwnProperty(name)) {
|
||||
var cache = this.uniform3fvCache[name];
|
||||
if (value[0] === cache[0] && value[1] === cache[1] && value[2] === cache[2]) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.uniform3fvCache[name] = value;
|
||||
var loc = this.getUniformLocation(name);
|
||||
this._webGl.uniform3fv(loc, value);
|
||||
}
|
||||
|
||||
|
||||
}, {
|
||||
@ -3261,21 +3328,16 @@ module.exports = function (_KernelBase) {
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, size[0], size[1], 0, gl.RGBA, gl.UNSIGNED_BYTE, buffer);
|
||||
}
|
||||
|
||||
var loc = this.getUniformLocation('user_' + name);
|
||||
var locSize = this.getUniformLocation('user_' + name + 'Size');
|
||||
var dimLoc = this.getUniformLocation('user_' + name + 'Dim');
|
||||
|
||||
if (!this.hardcodeConstants) {
|
||||
gl.uniform3fv(dimLoc, dim);
|
||||
gl.uniform2fv(locSize, size);
|
||||
this.setUniform3fv('user_' + name + 'Dim', dim);
|
||||
this.setUniform2fv('user_' + name + 'Size', size);
|
||||
}
|
||||
gl.uniform1i(loc, this.argumentsLength);
|
||||
this.setUniform1i('user_' + name, this.argumentsLength);
|
||||
break;
|
||||
}
|
||||
case 'Number':
|
||||
{
|
||||
var _loc = this.getUniformLocation('user_' + name);
|
||||
gl.uniform1f(_loc, value);
|
||||
this.setUniform1f('user_' + name, value);
|
||||
break;
|
||||
}
|
||||
case 'Input':
|
||||
@ -3310,15 +3372,11 @@ module.exports = function (_KernelBase) {
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, _size[0], _size[1], 0, gl.RGBA, gl.UNSIGNED_BYTE, _buffer);
|
||||
}
|
||||
|
||||
var _loc2 = this.getUniformLocation('user_' + name);
|
||||
var _locSize = this.getUniformLocation('user_' + name + 'Size');
|
||||
var _dimLoc = this.getUniformLocation('user_' + name + 'Dim');
|
||||
|
||||
if (!this.hardcodeConstants) {
|
||||
gl.uniform3fv(_dimLoc, _dim);
|
||||
gl.uniform2fv(_locSize, _size);
|
||||
this.setUniform3fv('user_' + name + 'Dim', _dim);
|
||||
this.setUniform2fv('user_' + name + 'Size', _size);
|
||||
}
|
||||
gl.uniform1i(_loc2, this.argumentsLength);
|
||||
this.setUniform1i('user_' + name, this.argumentsLength);
|
||||
break;
|
||||
}
|
||||
case 'Texture':
|
||||
@ -3330,13 +3388,9 @@ module.exports = function (_KernelBase) {
|
||||
gl.activeTexture(gl.TEXTURE0 + this.argumentsLength);
|
||||
gl.bindTexture(gl.TEXTURE_2D, inputTexture.texture);
|
||||
|
||||
var _loc3 = this.getUniformLocation('user_' + name);
|
||||
var _locSize2 = this.getUniformLocation('user_' + name + 'Size');
|
||||
var _dimLoc2 = this.getUniformLocation('user_' + name + 'Dim');
|
||||
|
||||
gl.uniform3fv(_dimLoc2, _dim2);
|
||||
gl.uniform2fv(_locSize2, _size2);
|
||||
gl.uniform1i(_loc3, this.argumentsLength);
|
||||
this.setUniform3fv('user_' + name + 'Dim', _dim2);
|
||||
this.setUniform2fv('user_' + name + 'Size', _size2);
|
||||
this.setUniform1i('user_' + name, this.argumentsLength);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
||||
12
bin/gpu.min.js
vendored
12
bin/gpu.min.js
vendored
File diff suppressed because one or more lines are too long
2
dist/backend/web-gl/kernel-string.js
vendored
2
dist/backend/web-gl/kernel-string.js
vendored
@ -15,5 +15,5 @@ function removeNoise(str) {
|
||||
}
|
||||
|
||||
module.exports = function (gpuKernel, name) {
|
||||
return '() => {\n ' + kernelRunShortcut.toString() + ';\n const utils = {\n allPropertiesOf: ' + removeNoise(utils.allPropertiesOf.toString()) + ',\n clone: ' + removeNoise(utils.clone.toString()) + ',\n splitArray: ' + removeNoise(utils.splitArray.toString()) + ',\n getArgumentType: ' + removeNoise(utils.getArgumentType.toString()) + ',\n getDimensions: ' + removeNoise(utils.getDimensions.toString()) + ',\n dimToTexSize: ' + removeNoise(utils.dimToTexSize.toString()) + ',\n flattenTo: ' + removeNoise(utils.flattenTo.toString()) + ',\n flatten2dArrayTo: ' + removeNoise(utils.flatten2dArrayTo.toString()) + ',\n flatten3dArrayTo: ' + removeNoise(utils.flatten3dArrayTo.toString()) + ',\n systemEndianness: \'' + removeNoise(utils.systemEndianness()) + '\',\n initWebGl: ' + removeNoise(utils.initWebGl.toString()) + ',\n isArray: ' + removeNoise(utils.isArray.toString()) + '\n };\n const Utils = utils;\n const canvases = [];\n const maxTexSizes = {};\n class ' + (name || 'Kernel') + ' {\n constructor() {\n this.maxTexSize = null;\n this.argumentsLength = 0;\n this._canvas = null;\n this._webGl = null;\n this.built = false;\n this.program = null;\n this.paramNames = ' + JSON.stringify(gpuKernel.paramNames) + ';\n this.paramTypes = ' + JSON.stringify(gpuKernel.paramTypes) + ';\n this.texSize = ' + JSON.stringify(gpuKernel.texSize) + ';\n this.output = ' + JSON.stringify(gpuKernel.output) + ';\n this.compiledFragShaderString = `' + gpuKernel.compiledFragShaderString + '`;\n\t\t this.compiledVertShaderString = `' + gpuKernel.compiledVertShaderString + '`;\n\t\t this.programUniformLocationCache = {};\n\t\t this.textureCache = {};\n\t\t this.subKernelOutputTextures = null;\n\t\t this.subKernelOutputVariableNames = null;\n }\n ' + removeFnNoise(gpuKernel._getFragShaderString.toString()) + '\n ' + removeFnNoise(gpuKernel._getVertShaderString.toString()) + '\n validateOptions() {}\n setupParams() {}\n setCanvas(canvas) { this._canvas = canvas; return this; }\n setWebGl(webGl) { this._webGl = webGl; return this; }\n ' + removeFnNoise(gpuKernel.getUniformLocation.toString()) + '\n ' + removeFnNoise(gpuKernel.setupParams.toString()) + '\n ' + removeFnNoise(gpuKernel.build.toString()) + '\n\t\t ' + removeFnNoise(gpuKernel.run.toString()) + '\n\t\t ' + removeFnNoise(gpuKernel._addArgument.toString()) + '\n\t\t ' + removeFnNoise(gpuKernel.getArgumentTexture.toString()) + '\n\t\t ' + removeFnNoise(gpuKernel.getTextureCache.toString()) + '\n\t\t ' + removeFnNoise(gpuKernel.getOutputTexture.toString()) + '\n\t\t ' + removeFnNoise(gpuKernel.renderOutput.toString()) + '\n\t\t ' + removeFnNoise(gpuKernel.updateMaxTexSize.toString()) + '\n\t\t ' + removeFnNoise(gpuKernel._setupOutputTexture.toString()) + '\n\t\t ' + removeFnNoise(gpuKernel.detachTextureCache.toString()) + '\n };\n return kernelRunShortcut(new Kernel());\n };';
|
||||
return '() => {\n ' + kernelRunShortcut.toString() + ';\n const utils = {\n allPropertiesOf: ' + removeNoise(utils.allPropertiesOf.toString()) + ',\n clone: ' + removeNoise(utils.clone.toString()) + ',\n splitArray: ' + removeNoise(utils.splitArray.toString()) + ',\n getArgumentType: ' + removeNoise(utils.getArgumentType.toString()) + ',\n getDimensions: ' + removeNoise(utils.getDimensions.toString()) + ',\n dimToTexSize: ' + removeNoise(utils.dimToTexSize.toString()) + ',\n flattenTo: ' + removeNoise(utils.flattenTo.toString()) + ',\n flatten2dArrayTo: ' + removeNoise(utils.flatten2dArrayTo.toString()) + ',\n flatten3dArrayTo: ' + removeNoise(utils.flatten3dArrayTo.toString()) + ',\n systemEndianness: \'' + removeNoise(utils.systemEndianness()) + '\',\n initWebGl: ' + removeNoise(utils.initWebGl.toString()) + ',\n isArray: ' + removeNoise(utils.isArray.toString()) + '\n };\n const Utils = utils;\n const canvases = [];\n const maxTexSizes = {};\n class ' + (name || 'Kernel') + ' {\n constructor() {\n this.maxTexSize = null;\n this.argumentsLength = 0;\n this._canvas = null;\n this._webGl = null;\n this.built = false;\n this.program = null;\n this.paramNames = ' + JSON.stringify(gpuKernel.paramNames) + ';\n this.paramTypes = ' + JSON.stringify(gpuKernel.paramTypes) + ';\n this.texSize = ' + JSON.stringify(gpuKernel.texSize) + ';\n this.output = ' + JSON.stringify(gpuKernel.output) + ';\n this.compiledFragShaderString = `' + gpuKernel.compiledFragShaderString + '`;\n\t\t this.compiledVertShaderString = `' + gpuKernel.compiledVertShaderString + '`;\n\t\t this.programUniformLocationCache = {};\n\t\t this.textureCache = {};\n\t\t this.subKernelOutputTextures = null;\n\t\t this.subKernelOutputVariableNames = null;\n\t\t this.uniform1fCache = {};\n\t\t this.uniform1iCache = {};\n\t\t this.uniform2fCache = {};\n\t\t this.uniform2fvCache = {};\n\t\t this.uniform3fvCache = {};\n }\n ' + removeFnNoise(gpuKernel._getFragShaderString.toString()) + '\n ' + removeFnNoise(gpuKernel._getVertShaderString.toString()) + '\n validateOptions() {}\n setupParams() {}\n setCanvas(canvas) { this._canvas = canvas; return this; }\n setWebGl(webGl) { this._webGl = webGl; return this; }\n ' + removeFnNoise(gpuKernel.getUniformLocation.toString()) + '\n ' + removeFnNoise(gpuKernel.setupParams.toString()) + '\n ' + removeFnNoise(gpuKernel.build.toString()) + '\n\t\t ' + removeFnNoise(gpuKernel.run.toString()) + '\n\t\t ' + removeFnNoise(gpuKernel._addArgument.toString()) + '\n\t\t ' + removeFnNoise(gpuKernel.getArgumentTexture.toString()) + '\n\t\t ' + removeFnNoise(gpuKernel.getTextureCache.toString()) + '\n\t\t ' + removeFnNoise(gpuKernel.getOutputTexture.toString()) + '\n\t\t ' + removeFnNoise(gpuKernel.renderOutput.toString()) + '\n\t\t ' + removeFnNoise(gpuKernel.updateMaxTexSize.toString()) + '\n\t\t ' + removeFnNoise(gpuKernel._setupOutputTexture.toString()) + '\n\t\t ' + removeFnNoise(gpuKernel.detachTextureCache.toString()) + '\n\t\t ' + removeFnNoise(gpuKernel.setUniform1f.toString()) + '\n\t\t ' + removeFnNoise(gpuKernel.setUniform1i.toString()) + '\n\t\t ' + removeFnNoise(gpuKernel.setUniform2f.toString()) + '\n\t\t ' + removeFnNoise(gpuKernel.setUniform2fv.toString()) + '\n\t\t ' + removeFnNoise(gpuKernel.setUniform3fv.toString()) + ' \n };\n return kernelRunShortcut(new Kernel());\n };';
|
||||
};
|
||||
112
dist/backend/web-gl/kernel.js
vendored
112
dist/backend/web-gl/kernel.js
vendored
@ -66,6 +66,11 @@ module.exports = function (_KernelBase) {
|
||||
_this.extDrawBuffersMap = null;
|
||||
_this.outputTexture = null;
|
||||
_this.maxTexSize = null;
|
||||
_this.uniform1fCache = {};
|
||||
_this.uniform1iCache = {};
|
||||
_this.uniform2fCache = {};
|
||||
_this.uniform2fvCache = {};
|
||||
_this.uniform3fvCache = {};
|
||||
if (!_this._webGl) _this._webGl = utils.initWebGl(_this.getCanvas());
|
||||
return _this;
|
||||
}
|
||||
@ -280,14 +285,11 @@ module.exports = function (_KernelBase) {
|
||||
gl.scissor(0, 0, texSize[0], texSize[1]);
|
||||
|
||||
if (!this.hardcodeConstants) {
|
||||
var uOutputDimLoc = this.getUniformLocation('uOutputDim');
|
||||
gl.uniform3fv(uOutputDimLoc, this.threadDim);
|
||||
var uTexSizeLoc = this.getUniformLocation('uTexSize');
|
||||
gl.uniform2fv(uTexSizeLoc, texSize);
|
||||
this.setUniform3fv('uOutputDim', this.threadDim);
|
||||
this.setUniform2fv('uTexSize', texSize);
|
||||
}
|
||||
|
||||
var ratioLoc = this.getUniformLocation('ratio');
|
||||
gl.uniform2f(ratioLoc, texSize[0] / this.maxTexSize[0], texSize[1] / this.maxTexSize[1]);
|
||||
this.setUniform2f('ratio', texSize[0] / this.maxTexSize[0], texSize[1] / this.maxTexSize[1]);
|
||||
|
||||
this.argumentsLength = 0;
|
||||
for (var texIndex = 0; texIndex < paramNames.length; texIndex++) {
|
||||
@ -533,6 +535,71 @@ module.exports = function (_KernelBase) {
|
||||
value: function detachTextureCache(name) {
|
||||
delete this.textureCache[name];
|
||||
}
|
||||
}, {
|
||||
key: 'setUniform1f',
|
||||
value: function setUniform1f(name, value) {
|
||||
if (this.uniform1fCache.hasOwnProperty(name)) {
|
||||
var cache = this.uniform1fCache[name];
|
||||
if (value === cache) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.uniform1fCache[name] = value;
|
||||
var loc = this.getUniformLocation(name);
|
||||
this._webGl.uniform1f(loc, value);
|
||||
}
|
||||
}, {
|
||||
key: 'setUniform1i',
|
||||
value: function setUniform1i(name, value) {
|
||||
if (this.uniform1iCache.hasOwnProperty(name)) {
|
||||
var cache = this.uniform1iCache[name];
|
||||
if (value === cache) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.uniform1iCache[name] = value;
|
||||
var loc = this.getUniformLocation(name);
|
||||
this._webGl.uniform1i(loc, value);
|
||||
}
|
||||
}, {
|
||||
key: 'setUniform2f',
|
||||
value: function setUniform2f(name, value1, value2) {
|
||||
if (this.uniform2fCache.hasOwnProperty(name)) {
|
||||
var cache = this.uniform2fCache[name];
|
||||
if (value1 === cache[0] && value2 === cache[1]) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.uniform2fCache[name] = [value1, value2];
|
||||
var loc = this.getUniformLocation(name);
|
||||
this._webGl.uniform2f(loc, value1, value2);
|
||||
}
|
||||
}, {
|
||||
key: 'setUniform2fv',
|
||||
value: function setUniform2fv(name, value) {
|
||||
if (this.uniform2fvCache.hasOwnProperty(name)) {
|
||||
var cache = this.uniform2fvCache[name];
|
||||
if (value[0] === cache[0] && value[1] === cache[1]) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.uniform2fvCache[name] = value;
|
||||
var loc = this.getUniformLocation(name);
|
||||
this._webGl.uniform2fv(loc, value);
|
||||
}
|
||||
}, {
|
||||
key: 'setUniform3fv',
|
||||
value: function setUniform3fv(name, value) {
|
||||
if (this.uniform3fvCache.hasOwnProperty(name)) {
|
||||
var cache = this.uniform3fvCache[name];
|
||||
if (value[0] === cache[0] && value[1] === cache[1] && value[2] === cache[2]) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.uniform3fvCache[name] = value;
|
||||
var loc = this.getUniformLocation(name);
|
||||
this._webGl.uniform3fv(loc, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @memberOf WebGLKernel#
|
||||
@ -641,21 +708,16 @@ module.exports = function (_KernelBase) {
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, size[0], size[1], 0, gl.RGBA, gl.UNSIGNED_BYTE, buffer);
|
||||
}
|
||||
|
||||
var loc = this.getUniformLocation('user_' + name);
|
||||
var locSize = this.getUniformLocation('user_' + name + 'Size');
|
||||
var dimLoc = this.getUniformLocation('user_' + name + 'Dim');
|
||||
|
||||
if (!this.hardcodeConstants) {
|
||||
gl.uniform3fv(dimLoc, dim);
|
||||
gl.uniform2fv(locSize, size);
|
||||
this.setUniform3fv('user_' + name + 'Dim', dim);
|
||||
this.setUniform2fv('user_' + name + 'Size', size);
|
||||
}
|
||||
gl.uniform1i(loc, this.argumentsLength);
|
||||
this.setUniform1i('user_' + name, this.argumentsLength);
|
||||
break;
|
||||
}
|
||||
case 'Number':
|
||||
{
|
||||
var _loc = this.getUniformLocation('user_' + name);
|
||||
gl.uniform1f(_loc, value);
|
||||
this.setUniform1f('user_' + name, value);
|
||||
break;
|
||||
}
|
||||
case 'Input':
|
||||
@ -690,15 +752,11 @@ module.exports = function (_KernelBase) {
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, _size[0], _size[1], 0, gl.RGBA, gl.UNSIGNED_BYTE, _buffer);
|
||||
}
|
||||
|
||||
var _loc2 = this.getUniformLocation('user_' + name);
|
||||
var _locSize = this.getUniformLocation('user_' + name + 'Size');
|
||||
var _dimLoc = this.getUniformLocation('user_' + name + 'Dim');
|
||||
|
||||
if (!this.hardcodeConstants) {
|
||||
gl.uniform3fv(_dimLoc, _dim);
|
||||
gl.uniform2fv(_locSize, _size);
|
||||
this.setUniform3fv('user_' + name + 'Dim', _dim);
|
||||
this.setUniform2fv('user_' + name + 'Size', _size);
|
||||
}
|
||||
gl.uniform1i(_loc2, this.argumentsLength);
|
||||
this.setUniform1i('user_' + name, this.argumentsLength);
|
||||
break;
|
||||
}
|
||||
case 'Texture':
|
||||
@ -710,13 +768,9 @@ module.exports = function (_KernelBase) {
|
||||
gl.activeTexture(gl.TEXTURE0 + this.argumentsLength);
|
||||
gl.bindTexture(gl.TEXTURE_2D, inputTexture.texture);
|
||||
|
||||
var _loc3 = this.getUniformLocation('user_' + name);
|
||||
var _locSize2 = this.getUniformLocation('user_' + name + 'Size');
|
||||
var _dimLoc2 = this.getUniformLocation('user_' + name + 'Dim');
|
||||
|
||||
gl.uniform3fv(_dimLoc2, _dim2);
|
||||
gl.uniform2fv(_locSize2, _size2);
|
||||
gl.uniform1i(_loc3, this.argumentsLength);
|
||||
this.setUniform3fv('user_' + name + 'Dim', _dim2);
|
||||
this.setUniform2fv('user_' + name + 'Size', _size2);
|
||||
this.setUniform1i('user_' + name, this.argumentsLength);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
||||
@ -52,6 +52,11 @@ module.exports = function(gpuKernel, name) {
|
||||
this.textureCache = {};
|
||||
this.subKernelOutputTextures = null;
|
||||
this.subKernelOutputVariableNames = null;
|
||||
this.uniform1fCache = {};
|
||||
this.uniform1iCache = {};
|
||||
this.uniform2fCache = {};
|
||||
this.uniform2fvCache = {};
|
||||
this.uniform3fvCache = {};
|
||||
}
|
||||
${ removeFnNoise(gpuKernel._getFragShaderString.toString()) }
|
||||
${ removeFnNoise(gpuKernel._getVertShaderString.toString()) }
|
||||
@ -71,6 +76,11 @@ module.exports = function(gpuKernel, name) {
|
||||
${ removeFnNoise(gpuKernel.updateMaxTexSize.toString()) }
|
||||
${ removeFnNoise(gpuKernel._setupOutputTexture.toString()) }
|
||||
${ removeFnNoise(gpuKernel.detachTextureCache.toString()) }
|
||||
${ removeFnNoise(gpuKernel.setUniform1f.toString()) }
|
||||
${ removeFnNoise(gpuKernel.setUniform1i.toString()) }
|
||||
${ removeFnNoise(gpuKernel.setUniform2f.toString()) }
|
||||
${ removeFnNoise(gpuKernel.setUniform2fv.toString()) }
|
||||
${ removeFnNoise(gpuKernel.setUniform3fv.toString()) }
|
||||
};
|
||||
return kernelRunShortcut(new Kernel());
|
||||
};`;
|
||||
|
||||
@ -53,6 +53,11 @@ module.exports = class WebGLKernel extends KernelBase {
|
||||
this.extDrawBuffersMap = null;
|
||||
this.outputTexture = null;
|
||||
this.maxTexSize = null;
|
||||
this.uniform1fCache = {};
|
||||
this.uniform1iCache = {};
|
||||
this.uniform2fCache = {};
|
||||
this.uniform2fvCache = {};
|
||||
this.uniform3fvCache = {};
|
||||
if (!this._webGl) this._webGl = utils.initWebGl(this.getCanvas());
|
||||
}
|
||||
|
||||
@ -268,14 +273,11 @@ module.exports = class WebGLKernel extends KernelBase {
|
||||
gl.scissor(0, 0, texSize[0], texSize[1]);
|
||||
|
||||
if (!this.hardcodeConstants) {
|
||||
const uOutputDimLoc = this.getUniformLocation('uOutputDim');
|
||||
gl.uniform3fv(uOutputDimLoc, this.threadDim);
|
||||
const uTexSizeLoc = this.getUniformLocation('uTexSize');
|
||||
gl.uniform2fv(uTexSizeLoc, texSize);
|
||||
this.setUniform3fv('uOutputDim', this.threadDim);
|
||||
this.setUniform2fv('uTexSize', texSize);
|
||||
}
|
||||
|
||||
const ratioLoc = this.getUniformLocation('ratio');
|
||||
gl.uniform2f(ratioLoc, texSize[0] / this.maxTexSize[0], texSize[1] / this.maxTexSize[1]);
|
||||
this.setUniform2f('ratio', texSize[0] / this.maxTexSize[0], texSize[1] / this.maxTexSize[1]);
|
||||
|
||||
this.argumentsLength = 0;
|
||||
for (let texIndex = 0; texIndex < paramNames.length; texIndex++) {
|
||||
@ -501,6 +503,76 @@ module.exports = class WebGLKernel extends KernelBase {
|
||||
delete this.textureCache[name];
|
||||
}
|
||||
|
||||
setUniform1f(name, value) {
|
||||
if (this.uniform1fCache.hasOwnProperty(name)) {
|
||||
const cache = this.uniform1fCache[name];
|
||||
if (value === cache) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.uniform1fCache[name] = value;
|
||||
const loc = this.getUniformLocation(name);
|
||||
this._webGl.uniform1f(loc, value);
|
||||
}
|
||||
|
||||
setUniform1i(name, value) {
|
||||
if (this.uniform1iCache.hasOwnProperty(name)) {
|
||||
const cache = this.uniform1iCache[name];
|
||||
if (value === cache) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.uniform1iCache[name] = value;
|
||||
const loc = this.getUniformLocation(name);
|
||||
this._webGl.uniform1i(loc, value);
|
||||
}
|
||||
|
||||
setUniform2f(name, value1, value2) {
|
||||
if (this.uniform2fCache.hasOwnProperty(name)) {
|
||||
const cache = this.uniform2fCache[name];
|
||||
if (
|
||||
value1 === cache[0] &&
|
||||
value2 === cache[1]
|
||||
) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.uniform2fCache[name] = [value1, value2];
|
||||
const loc = this.getUniformLocation(name);
|
||||
this._webGl.uniform2f(loc, value1, value2);
|
||||
}
|
||||
|
||||
setUniform2fv(name, value) {
|
||||
if (this.uniform2fvCache.hasOwnProperty(name)) {
|
||||
const cache = this.uniform2fvCache[name];
|
||||
if (
|
||||
value[0] === cache[0] &&
|
||||
value[1] === cache[1]
|
||||
) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.uniform2fvCache[name] = value;
|
||||
const loc = this.getUniformLocation(name);
|
||||
this._webGl.uniform2fv(loc, value);
|
||||
}
|
||||
|
||||
setUniform3fv(name, value) {
|
||||
if (this.uniform3fvCache.hasOwnProperty(name)) {
|
||||
const cache = this.uniform3fvCache[name];
|
||||
if (
|
||||
value[0] === cache[0] &&
|
||||
value[1] === cache[1] &&
|
||||
value[2] === cache[2]
|
||||
) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.uniform3fvCache[name] = value;
|
||||
const loc = this.getUniformLocation(name);
|
||||
this._webGl.uniform3fv(loc, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @memberOf WebGLKernel#
|
||||
* @function
|
||||
@ -599,21 +671,16 @@ module.exports = class WebGLKernel extends KernelBase {
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, size[0], size[1], 0, gl.RGBA, gl.UNSIGNED_BYTE, buffer);
|
||||
}
|
||||
|
||||
const loc = this.getUniformLocation('user_' + name);
|
||||
const locSize = this.getUniformLocation('user_' + name + 'Size');
|
||||
const dimLoc = this.getUniformLocation('user_' + name + 'Dim');
|
||||
|
||||
if (!this.hardcodeConstants) {
|
||||
gl.uniform3fv(dimLoc, dim);
|
||||
gl.uniform2fv(locSize, size);
|
||||
this.setUniform3fv(`user_${name}Dim`, dim);
|
||||
this.setUniform2fv(`user_${name}Size`, size);
|
||||
}
|
||||
gl.uniform1i(loc, this.argumentsLength);
|
||||
this.setUniform1i(`user_${name}`, this.argumentsLength);
|
||||
break;
|
||||
}
|
||||
case 'Number':
|
||||
{
|
||||
const loc = this.getUniformLocation('user_' + name);
|
||||
gl.uniform1f(loc, value);
|
||||
this.setUniform1f(`user_${name}`, value);
|
||||
break;
|
||||
}
|
||||
case 'Input':
|
||||
@ -648,15 +715,11 @@ module.exports = class WebGLKernel extends KernelBase {
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, size[0], size[1], 0, gl.RGBA, gl.UNSIGNED_BYTE, buffer);
|
||||
}
|
||||
|
||||
const loc = this.getUniformLocation('user_' + name);
|
||||
const locSize = this.getUniformLocation('user_' + name + 'Size');
|
||||
const dimLoc = this.getUniformLocation('user_' + name + 'Dim');
|
||||
|
||||
if (!this.hardcodeConstants) {
|
||||
gl.uniform3fv(dimLoc, dim);
|
||||
gl.uniform2fv(locSize, size);
|
||||
this.setUniform3fv(`user_${name}Dim`, dim);
|
||||
this.setUniform2fv(`user_${name}Size`, size);
|
||||
}
|
||||
gl.uniform1i(loc, this.argumentsLength);
|
||||
this.setUniform1i(`user_${name}`, this.argumentsLength);
|
||||
break;
|
||||
}
|
||||
case 'Texture':
|
||||
@ -668,13 +731,9 @@ module.exports = class WebGLKernel extends KernelBase {
|
||||
gl.activeTexture(gl.TEXTURE0 + this.argumentsLength);
|
||||
gl.bindTexture(gl.TEXTURE_2D, inputTexture.texture);
|
||||
|
||||
const loc = this.getUniformLocation('user_' + name);
|
||||
const locSize = this.getUniformLocation('user_' + name + 'Size');
|
||||
const dimLoc = this.getUniformLocation('user_' + name + 'Dim');
|
||||
|
||||
gl.uniform3fv(dimLoc, dim);
|
||||
gl.uniform2fv(locSize, size);
|
||||
gl.uniform1i(loc, this.argumentsLength);
|
||||
this.setUniform3fv(`user_${name}Dim`, dim);
|
||||
this.setUniform2fv(`user_${name}Size`, size);
|
||||
this.setUniform1i(`user_${name}`, this.argumentsLength);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
||||
@ -28,4 +28,115 @@ QUnit.test('KernelBase paramTypes WebGL', function(assert) {
|
||||
} catch (e) {}
|
||||
assert.equal(kernel.paramTypes.length, 1);
|
||||
assert.equal(kernel.paramTypes[0], 'Array');
|
||||
});
|
||||
|
||||
QUnit.test('WebGLKernel.setUniform1f only calls webgl when values change', () => {
|
||||
const kernel = new GPU.WebGLKernel('', { output: [1] });
|
||||
let throws = false;
|
||||
kernel._webGl = {
|
||||
uniform1f: () => {
|
||||
if (throws) new Error('This should not get called');
|
||||
},
|
||||
getUniformLocation: (name) => {
|
||||
return name;
|
||||
}
|
||||
};
|
||||
kernel.setUniform1f('test', 1);
|
||||
QUnit.assert.equal(kernel.uniform1fCache['test'], 1);
|
||||
|
||||
throws = true;
|
||||
kernel.setUniform1f('test', 1);
|
||||
QUnit.assert.equal(kernel.uniform1fCache['test'], 1);
|
||||
|
||||
throws = false;
|
||||
kernel.setUniform1f('test', 2);
|
||||
QUnit.assert.equal(kernel.uniform1fCache['test'], 2);
|
||||
});
|
||||
QUnit.test('WebGLKernel.setUniform1i only calls webgl when values change', () => {
|
||||
const kernel = new GPU.WebGLKernel('', { output: [1] });
|
||||
let throws = false;
|
||||
kernel._webGl = {
|
||||
uniform1i: () => {
|
||||
if (throws) new Error('This should not get called');
|
||||
},
|
||||
getUniformLocation: (name) => {
|
||||
return name;
|
||||
}
|
||||
};
|
||||
kernel.setUniform1i('test', 1);
|
||||
QUnit.assert.equal(kernel.uniform1iCache['test'], 1);
|
||||
|
||||
throws = true;
|
||||
kernel.setUniform1i('test', 1);
|
||||
QUnit.assert.equal(kernel.uniform1iCache['test'], 1);
|
||||
|
||||
throws = false;
|
||||
kernel.setUniform1i('test', 2);
|
||||
QUnit.assert.equal(kernel.uniform1iCache['test'], 2);
|
||||
});
|
||||
QUnit.test('WebGLKernel.setUniform2f only calls webgl when values change', () => {
|
||||
const kernel = new GPU.WebGLKernel('', { output: [1] });
|
||||
let throws = false;
|
||||
kernel._webGl = {
|
||||
uniform2f: () => {
|
||||
if (throws) new Error('This should not get called');
|
||||
},
|
||||
getUniformLocation: (name) => {
|
||||
return name;
|
||||
}
|
||||
};
|
||||
kernel.setUniform2f('test', 1, 2);
|
||||
QUnit.assert.deepEqual(kernel.uniform2fCache['test'], [1, 2]);
|
||||
|
||||
throws = true;
|
||||
kernel.setUniform2f('test', 1, 2);
|
||||
QUnit.assert.deepEqual(kernel.uniform2fCache['test'], [1, 2]);
|
||||
|
||||
throws = false;
|
||||
kernel.setUniform2f('test', 3, 4);
|
||||
QUnit.assert.deepEqual(kernel.uniform2fCache['test'], [3, 4]);
|
||||
});
|
||||
QUnit.test('WebGLKernel.setUniform2fv only calls webgl when values change', () => {
|
||||
const kernel = new GPU.WebGLKernel('', { output: [1] });
|
||||
let throws = false;
|
||||
kernel._webGl = {
|
||||
uniform2fv: () => {
|
||||
if (throws) new Error('This should not get called');
|
||||
},
|
||||
getUniformLocation: (name) => {
|
||||
return name;
|
||||
}
|
||||
};
|
||||
kernel.setUniform2fv('test', [1, 2]);
|
||||
QUnit.assert.deepEqual(kernel.uniform2fvCache['test'], [1, 2]);
|
||||
|
||||
throws = true;
|
||||
kernel.setUniform2fv('test', [1, 2]);
|
||||
QUnit.assert.deepEqual(kernel.uniform2fvCache['test'], [1, 2]);
|
||||
|
||||
throws = false;
|
||||
kernel.setUniform2fv('test', [2, 3]);
|
||||
QUnit.assert.deepEqual(kernel.uniform2fvCache['test'], [2, 3]);
|
||||
});
|
||||
QUnit.test('WebGLKernel.setUniform3fv only calls webgl when values change', () => {
|
||||
const kernel = new GPU.WebGLKernel('', { output: [1] });
|
||||
let throws = false;
|
||||
kernel._webGl = {
|
||||
uniform3fv: () => {
|
||||
if (throws) new Error('This should not get called');
|
||||
},
|
||||
getUniformLocation: (name) => {
|
||||
return name;
|
||||
}
|
||||
};
|
||||
kernel.setUniform3fv('test', [1, 2, 3]);
|
||||
QUnit.assert.deepEqual(kernel.uniform3fvCache['test'], [1, 2, 3]);
|
||||
|
||||
throws = true;
|
||||
kernel.setUniform3fv('test', [1, 2, 3]);
|
||||
QUnit.assert.deepEqual(kernel.uniform3fvCache['test'], [1, 2, 3]);
|
||||
|
||||
throws = false;
|
||||
kernel.setUniform3fv('test', [2, 3, 4]);
|
||||
QUnit.assert.deepEqual(kernel.uniform3fvCache['test'], [2, 3, 4]);
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user