mirror of
https://github.com/gpujs/gpu.js.git
synced 2026-01-25 16:08:02 +00:00
477 lines
14 KiB
JavaScript
477 lines
14 KiB
JavaScript
'use strict';
|
|
|
|
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
|
|
|
|
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
|
|
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
|
|
|
|
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
|
|
|
|
var utils = require('./utils');
|
|
var WebGLRunner = require('../backend/web-gl/runner');
|
|
var WebGL2Runner = require('../backend/web-gl2/runner');
|
|
var CPURunner = require('../backend/cpu/runner');
|
|
var WebGLValidatorKernel = require('../backend/web-gl/validator-kernel');
|
|
var WebGL2ValidatorKernel = require('../backend/web-gl2/validator-kernel');
|
|
var GPUCore = require("./gpu-core");
|
|
|
|
/**
|
|
* Initialises the GPU.js library class which manages the webGlContext for the created functions.
|
|
* @class
|
|
* @extends GPUCore
|
|
*/
|
|
|
|
var GPU = function (_GPUCore) {
|
|
_inherits(GPU, _GPUCore);
|
|
|
|
/**
|
|
* Creates an instance of GPU.
|
|
* @param {any} settings - Settings to set mode, andother properties. See #GPUCore
|
|
* @memberOf GPU#
|
|
*/
|
|
function GPU(settings) {
|
|
_classCallCheck(this, GPU);
|
|
|
|
var _this = _possibleConstructorReturn(this, (GPU.__proto__ || Object.getPrototypeOf(GPU)).call(this, settings));
|
|
|
|
settings = settings || {};
|
|
_this._canvas = settings.canvas || null;
|
|
_this._webGl = settings.webGl || null;
|
|
var mode = settings.mode;
|
|
var detectedMode = void 0;
|
|
if (!utils.isWebGlSupported()) {
|
|
if (mode && mode !== 'cpu') {
|
|
throw new Error('A requested mode of "' + mode + '" and is not supported');
|
|
} else {
|
|
console.warn('Warning: gpu not supported, falling back to cpu support');
|
|
detectedMode = 'cpu';
|
|
}
|
|
} else {
|
|
detectedMode = mode || 'gpu';
|
|
}
|
|
_this.kernels = [];
|
|
|
|
var runnerSettings = {
|
|
canvas: _this._canvas,
|
|
webGl: _this._webGl
|
|
};
|
|
|
|
switch (detectedMode) {
|
|
// public options
|
|
case 'cpu':
|
|
_this._runner = new CPURunner(runnerSettings);
|
|
break;
|
|
case 'gpu':
|
|
var Runner = _this.getGPURunner();
|
|
_this._runner = new Runner(runnerSettings);
|
|
break;
|
|
|
|
// private explicit options for testing
|
|
case 'webgl2':
|
|
_this._runner = new WebGL2Runner(runnerSettings);
|
|
break;
|
|
case 'webgl':
|
|
_this._runner = new WebGLRunner(runnerSettings);
|
|
break;
|
|
|
|
// private explicit options for internal
|
|
case 'webgl2-validator':
|
|
_this._runner = new WebGL2Runner(runnerSettings);
|
|
_this._runner.Kernel = WebGL2ValidatorKernel;
|
|
break;
|
|
case 'webgl-validator':
|
|
_this._runner = new WebGLRunner(runnerSettings);
|
|
_this._runner.Kernel = WebGLValidatorKernel;
|
|
break;
|
|
default:
|
|
throw new Error('"' + mode + '" mode is not defined');
|
|
}
|
|
return _this;
|
|
}
|
|
/**
|
|
*
|
|
* This creates a callable function object to call the kernel function with the argument parameter set
|
|
*
|
|
* @name createKernel
|
|
* @function
|
|
* @memberOf GPU##
|
|
*
|
|
* @param {Function} fn - The calling to perform the conversion
|
|
* @param {Object} [settings] - The parameter configuration object
|
|
* @property {String} settings.dimensions - Thread dimension array (Defaults to [1024])
|
|
* @property {String} settings.mode - CPU / GPU configuration mode (Defaults to null)
|
|
*
|
|
* The following modes are supported
|
|
* *'falsey'* : Attempts to build GPU mode, else fallbacks
|
|
* *'gpu'* : Attempts to build GPU mode, else fallbacks
|
|
* *'cpu'* : Forces JS fallback mode only
|
|
*
|
|
*
|
|
* @returns {Function} callable function to run
|
|
*
|
|
*/
|
|
|
|
|
|
_createClass(GPU, [{
|
|
key: 'createKernel',
|
|
value: function createKernel(fn, settings) {
|
|
//
|
|
// basic parameters safety checks
|
|
//
|
|
if (typeof fn === 'undefined') {
|
|
throw 'Missing fn parameter';
|
|
}
|
|
if (!utils.isFunction(fn) && typeof fn !== 'string') {
|
|
throw 'fn parameter not a function';
|
|
}
|
|
|
|
var kernel = this._runner.buildKernel(fn, settings || {});
|
|
|
|
//if canvas didn't come from this, propagate from kernel
|
|
if (!this._canvas) {
|
|
this._canvas = kernel.getCanvas();
|
|
}
|
|
if (!this._runner.canvas) {
|
|
this._runner.canvas = kernel.getCanvas();
|
|
}
|
|
|
|
this.kernels.push(kernel);
|
|
|
|
return kernel;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* Create a super kernel which executes sub kernels
|
|
* and saves their output to be used with the next sub kernel.
|
|
* This can be useful if we want to save the output on one kernel,
|
|
* and then use it as an input to another kernel. *Machine Learning*
|
|
*
|
|
* @name createKernelMap
|
|
* @function
|
|
* @memberOf GPU#
|
|
*
|
|
* @param {Object|Array} subKernels - Sub kernels for this kernel
|
|
* @param {Function} rootKernel - Root kernel
|
|
*
|
|
* @returns {Function} callable kernel function
|
|
*
|
|
* @example
|
|
* const megaKernel = gpu.createKernelMap({
|
|
* addResult: function add(a, b) {
|
|
* return a[this.thread.x] + b[this.thread.x];
|
|
* },
|
|
* multiplyResult: function multiply(a, b) {
|
|
* return a[this.thread.x] * b[this.thread.x];
|
|
* },
|
|
* }, function(a, b, c) {
|
|
* return multiply(add(a, b), c);
|
|
* });
|
|
*
|
|
* megaKernel(a, b, c);
|
|
*
|
|
* Note: You can also define subKernels as an array of functions.
|
|
* > [add, multiply]
|
|
*
|
|
*/
|
|
|
|
}, {
|
|
key: 'createKernelMap',
|
|
value: function createKernelMap() {
|
|
var fn = void 0;
|
|
var settings = void 0;
|
|
if (typeof arguments[arguments.length - 2] === 'function') {
|
|
fn = arguments[arguments.length - 2];
|
|
settings = arguments[arguments.length - 1];
|
|
} else {
|
|
fn = arguments[arguments.length - 1];
|
|
}
|
|
|
|
if (!utils.isWebGlDrawBuffersSupported()) {
|
|
this._runner = new CPURunner(settings);
|
|
}
|
|
|
|
var kernel = this.createKernel(fn, settings);
|
|
if (Array.isArray(arguments[0])) {
|
|
var functions = arguments[0];
|
|
for (var i = 0; i < functions.length; i++) {
|
|
kernel.addSubKernel(functions[i]);
|
|
}
|
|
} else {
|
|
var _functions = arguments[0];
|
|
for (var p in _functions) {
|
|
if (!_functions.hasOwnProperty(p)) continue;
|
|
kernel.addSubKernelProperty(p, _functions[p]);
|
|
}
|
|
}
|
|
|
|
return kernel;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* Combine different kernels into one super Kernel,
|
|
* useful to perform multiple operations inside one
|
|
* kernel without the penalty of data transfer between
|
|
* cpu and gpu.
|
|
*
|
|
* The number of kernel functions sent to this method can be variable.
|
|
* You can send in one, two, etc.
|
|
*
|
|
* @name combineKernels
|
|
* @function
|
|
* @memberOf GPU#
|
|
*
|
|
* @param {Function} subKernels - Kernel function(s) to combine.
|
|
* @param {Function} rootKernel - Root kernel to combine kernels into
|
|
*
|
|
* @example
|
|
* combineKernels(add, multiply, function(a,b,c){
|
|
* return add(multiply(a,b), c)
|
|
* })
|
|
*
|
|
* @returns {Function} Callable kernel function
|
|
*
|
|
*/
|
|
|
|
}, {
|
|
key: 'combineKernels',
|
|
value: function combineKernels() {
|
|
var lastKernel = arguments[arguments.length - 2];
|
|
var combinedKernel = arguments[arguments.length - 1];
|
|
if (this.getMode() === 'cpu') return combinedKernel;
|
|
|
|
var canvas = arguments[0].getCanvas();
|
|
var webGl = arguments[0].getWebGl();
|
|
|
|
for (var i = 0; i < arguments.length - 1; i++) {
|
|
arguments[i].setCanvas(canvas).setWebGl(webGl).setOutputToTexture(true);
|
|
}
|
|
|
|
return function () {
|
|
combinedKernel.apply(null, arguments);
|
|
var texSize = lastKernel.texSize;
|
|
var gl = lastKernel.getWebGl();
|
|
var threadDim = lastKernel.threadDim;
|
|
var result = void 0;
|
|
if (lastKernel.floatOutput) {
|
|
var w = texSize[0];
|
|
var h = Math.ceil(texSize[1] / 4);
|
|
result = new Float32Array(w * h * 4);
|
|
gl.readPixels(0, 0, w, h, gl.RGBA, gl.FLOAT, result);
|
|
} else {
|
|
var bytes = new Uint8Array(texSize[0] * texSize[1] * 4);
|
|
gl.readPixels(0, 0, texSize[0], texSize[1], gl.RGBA, gl.UNSIGNED_BYTE, bytes);
|
|
result = new Float32Array(bytes.buffer);
|
|
}
|
|
|
|
result = result.subarray(0, threadDim[0] * threadDim[1] * threadDim[2]);
|
|
|
|
if (lastKernel.output.length === 1) {
|
|
return result;
|
|
} else if (lastKernel.output.length === 2) {
|
|
return utils.splitArray(result, lastKernel.output[0]);
|
|
} else if (lastKernel.output.length === 3) {
|
|
var cube = utils.splitArray(result, lastKernel.output[0] * lastKernel.output[1]);
|
|
return cube.map(function (x) {
|
|
return utils.splitArray(x, lastKernel.output[0]);
|
|
});
|
|
}
|
|
};
|
|
}
|
|
}, {
|
|
key: 'getGPURunner',
|
|
value: function getGPURunner() {
|
|
if (typeof WebGL2RenderingContext !== 'undefined') return WebGL2Runner;
|
|
if (typeof WebGLRenderingContext !== 'undefined') return WebGLRunner;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* Adds additional functions, that the kernel may call.
|
|
*
|
|
* @name addFunction
|
|
* @function
|
|
* @memberOf GPU#
|
|
*
|
|
* @param {Function|String} fn - JS Function to do conversion
|
|
* @param {String[]|Object} paramTypes - Parameter type array, assumes all parameters are 'float' if null
|
|
* @param {String} returnType - The return type, assumes 'float' if null
|
|
*
|
|
* @returns {GPU} returns itself
|
|
*
|
|
*/
|
|
|
|
}, {
|
|
key: 'addFunction',
|
|
value: function addFunction(fn, paramTypes, returnType) {
|
|
this._runner.functionBuilder.addFunction(null, fn, paramTypes, returnType);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* Adds additional native functions, that the kernel may call.
|
|
*
|
|
* @name addNativeFunction
|
|
* @function
|
|
* @memberOf GPU#
|
|
*
|
|
* @param {String} name - native function name, used for reverse lookup
|
|
* @param {String} nativeFunction - the native function implementation, as it would be defined in it's entirety
|
|
*
|
|
* @returns {GPU} returns itself
|
|
*
|
|
*/
|
|
|
|
}, {
|
|
key: 'addNativeFunction',
|
|
value: function addNativeFunction(name, nativeFunction) {
|
|
this._runner.functionBuilder.addNativeFunction(name, nativeFunction);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* Return the current mode in which gpu.js is executing.
|
|
* @name getMode
|
|
* @function
|
|
* @memberOf GPU#
|
|
*
|
|
* @returns {String} The current mode, "cpu", "webgl", etc.
|
|
*
|
|
*/
|
|
|
|
}, {
|
|
key: 'getMode',
|
|
value: function getMode() {
|
|
return this._runner.getMode();
|
|
}
|
|
|
|
/**
|
|
*
|
|
* Return TRUE, if browser supports WebGl AND Canvas
|
|
*
|
|
* @name get isWebGlSupported
|
|
* @function
|
|
* @memberOf GPU#
|
|
*
|
|
* Note: This function can also be called directly `GPU.isWebGlSupported()`
|
|
*
|
|
* @returns {Boolean} TRUE if browser supports webGl
|
|
*
|
|
*/
|
|
|
|
}, {
|
|
key: 'isWebGlSupported',
|
|
value: function isWebGlSupported() {
|
|
return utils.isWebGlSupported();
|
|
}
|
|
|
|
/**
|
|
*
|
|
* Return TRUE, if system has integer division accuracy issue
|
|
*
|
|
* @name get hasIntegerDivisionAccuracyBug
|
|
* @function
|
|
* @memberOf GPU#
|
|
*
|
|
* Note: This function can also be called directly `GPU.hasIntegerDivisionAccuracyBug()`
|
|
*
|
|
* @returns {Boolean} TRUE if system has integer division accuracy issue
|
|
*
|
|
*
|
|
*/
|
|
|
|
}, {
|
|
key: 'hasIntegerDivisionAccuracyBug',
|
|
value: function hasIntegerDivisionAccuracyBug() {
|
|
return utils.hasIntegerDivisionAccuracyBug();
|
|
}
|
|
|
|
/**
|
|
*
|
|
* Return the canvas object bound to this gpu instance.
|
|
*
|
|
* @name getCanvas
|
|
* @function
|
|
* @memberOf GPU#
|
|
*
|
|
* @returns {Object} Canvas object if present
|
|
*
|
|
*/
|
|
|
|
}, {
|
|
key: 'getCanvas',
|
|
value: function getCanvas() {
|
|
return this._canvas;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* Return the webGl object bound to this gpu instance.
|
|
*
|
|
* @name getWebGl
|
|
* @function
|
|
* @memberOf GPU#
|
|
*
|
|
* @returns {Object} WebGl object if present
|
|
*
|
|
*/
|
|
|
|
}, {
|
|
key: 'getWebGl',
|
|
value: function getWebGl() {
|
|
return this._webGl;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* Destroys all memory associated with gpu.js & the webGl if we created it
|
|
*
|
|
* @name destroy
|
|
* @function
|
|
* @memberOf GPU#
|
|
*
|
|
*
|
|
*/
|
|
|
|
}, {
|
|
key: 'destroy',
|
|
value: function destroy() {
|
|
var _this2 = this;
|
|
|
|
// perform on next runloop - for some reason we dont get lose context events
|
|
// if webGl is created and destroyed in the same run loop.
|
|
setTimeout(function () {
|
|
var kernels = _this2.kernels;
|
|
|
|
var destroyWebGl = !_this2._webGl && kernels.length && kernels[0]._webGl;
|
|
for (var i = 0; i < _this2.kernels.length; i++) {
|
|
_this2.kernels[i].destroy(true); // remove canvas if exists
|
|
}
|
|
|
|
if (destroyWebGl) {
|
|
destroyWebGl.OES_texture_float = null;
|
|
destroyWebGl.OES_texture_float_linear = null;
|
|
destroyWebGl.OES_element_index_uint = null;
|
|
var loseContextExt = destroyWebGl.getExtension('WEBGL_lose_context');
|
|
if (loseContextExt) {
|
|
loseContextExt.loseContext();
|
|
}
|
|
}
|
|
}, 0);
|
|
}
|
|
}]);
|
|
|
|
return GPU;
|
|
}(GPUCore);
|
|
|
|
;
|
|
|
|
// This ensure static methods are "inherited"
|
|
// See: https://stackoverflow.com/questions/5441508/how-to-inherit-static-methods-from-base-class-in-javascript
|
|
Object.assign(GPU, GPUCore);
|
|
|
|
module.exports = GPU; |