From 5605504d92aa0641caf6b39f33c9ec0db2dceff9 Mon Sep 17 00:00:00 2001 From: Robert Plummer Date: Thu, 11 Jul 2019 13:18:33 -0400 Subject: [PATCH] fix: Upgrade gpu-mock.js to latest 100% code coverage tested fix: Added tests for building kernels with "dev" mode feat: Added a `.toArray()` method on `Input`, for usage with "dev" mode fix: `Input.size` to reflect exactly what was sent in, rather than make up dimensions that aren't there fix: `KernelArgument.dimensions` to fill in missing dimensions from size fix: Only safe declarations when using a literal --- dist/gpu-browser-core.js | 327 ++++++++++++++--- dist/gpu-browser-core.min.js | 331 ++++++++++++++---- dist/gpu-browser.js | 327 ++++++++++++++--- dist/gpu-browser.min.js | 331 ++++++++++++++---- package.json | 7 +- src/backend/web-gl/function-node.js | 12 +- .../kernel-value/dynamic-single-input.js | 3 +- .../kernel-value/dynamic-unsigned-input.js | 3 +- .../web-gl/kernel-value/single-input.js | 3 +- .../web-gl/kernel-value/unsigned-input.js | 3 +- .../kernel-value/dynamic-single-input.js | 3 +- src/gpu.js | 3 +- src/input.js | 39 ++- test/all.html | 1 + test/features/dev-mode.js | 147 ++++++++ test/features/input.js | 22 ++ test/internal/math.random.js | 10 +- 17 files changed, 1311 insertions(+), 261 deletions(-) create mode 100644 test/features/dev-mode.js diff --git a/dist/gpu-browser-core.js b/dist/gpu-browser-core.js index ac0539c7..6edd00b7 100644 --- a/dist/gpu-browser-core.js +++ b/dist/gpu-browser-core.js @@ -4,8 +4,8 @@ * * GPU Accelerated JavaScript * - * @version 2.0.0-rc.20 - * @date Fri Jul 05 2019 11:02:54 GMT-0400 (Eastern Daylight Time) + * @version 2.0.0-rc.21 + * @date Thu Jul 11 2019 13:18:11 GMT-0400 (Eastern Daylight Time) * * @license MIT * The MIT License @@ -373,85 +373,262 @@ if (typeof window !== 'undefined') { } },{}],3:[function(require,module,exports){ -'use strict'; +function setupArguments(args) { + const newArguments = new Array(args.length); + for (let i = 0; i < args.length; i++) { + const arg = args[i]; + if (arg.toArray) { + newArguments[i] = arg.toArray(); + } else { + newArguments[i] = arg; + } + } + return newArguments; +} function mock1D() { - const row = []; + const args = setupArguments(arguments); + const row = new Float32Array(this.output.x); for (let x = 0; x < this.output.x; x++) { this.thread.x = x; this.thread.y = 0; this.thread.z = 0; - row.push(this._fn.apply(this, arguments)); + row[x] = this._fn.apply(this, args); } return row; } function mock2D() { - const matrix = []; + const args = setupArguments(arguments); + const matrix = new Array(this.output.y); for (let y = 0; y < this.output.y; y++) { - const row = []; + const row = new Float32Array(this.output.x); for (let x = 0; x < this.output.x; x++) { this.thread.x = x; this.thread.y = y; this.thread.z = 0; - row.push(this._fn.apply(this, arguments)); + row[x] = this._fn.apply(this, args); } - matrix.push(row); + matrix[y] = row; } return matrix; } +function mock2DGraphical() { + const args = setupArguments(arguments); + for (let y = 0; y < this.output.y; y++) { + for (let x = 0; x < this.output.x; x++) { + this.thread.x = x; + this.thread.y = y; + this.thread.z = 0; + this._fn.apply(this, args); + } + } +} + function mock3D() { - const cube = []; + const args = setupArguments(arguments); + const cube = new Array(this.output.z); for (let z = 0; z < this.output.z; z++) { - const matrix = []; + const matrix = new Array(this.output.y); for (let y = 0; y < this.output.y; y++) { - const row = []; + const row = new Float32Array(this.output.x); for (let x = 0; x < this.output.x; x++) { this.thread.x = x; this.thread.y = y; this.thread.z = z; - row.push(this._fn.apply(this, arguments)); + row[x] = this._fn.apply(this, args); } - matrix.push(row); + matrix[y] = row; } - cube.push(matrix); + cube[z] = matrix; } return cube; } -module.exports = function gpuMock(fn, options) { - let contextOutput = null; - if (options.output.length) { - if (options.output.length === 3) { - contextOutput = { x: options.output[0], y: options.output[1], z: options.output[2] }; - } else if (options.output.length === 2) { - contextOutput = { x: options.output[0], y: options.output[1] }; - } else { - contextOutput = { x: options.output[0] }; +function apiDecorate(kernel) { + kernel.setOutput = (output) => { + kernel.output = setupOutput(output); + if (kernel.graphical) { + setupGraphical(kernel); + } + }; + kernel.toJSON = () => { + throw new Error('Not usable with gpuMock'); + }; + kernel.setConstants = (flag) => { + kernel.constants = flag; + return kernel; + }; + kernel.setGraphical = (flag) => { + kernel.graphical = flag; + return kernel; + }; + kernel.setCanvas = (flag) => { + kernel.canvas = flag; + return kernel; + }; + kernel.setContext = (flag) => { + kernel.context = flag; + return kernel; + }; + kernel.exec = function() { + return new Promise((resolve, reject) => { + try { + resolve(kernel.apply(kernel, arguments)); + } catch(e) { + reject(e); + } + }); + }; + kernel.getPixels = (flip) => { + const {x, y} = kernel.output; + return flip ? flipPixels(kernel._imageData.data, x, y) : kernel._imageData.data.slice(0); + }; + kernel.color = function(r, g, b, a) { + if (typeof a === 'undefined') { + a = 1; } + + r = Math.floor(r * 255); + g = Math.floor(g * 255); + b = Math.floor(b * 255); + a = Math.floor(a * 255); + + const width = kernel.output.x; + const height = kernel.output.y; + + const x = kernel.thread.x; + const y = height - kernel.thread.y - 1; + + const index = x + y * width; + + kernel._colorData[index * 4 + 0] = r; + kernel._colorData[index * 4 + 1] = g; + kernel._colorData[index * 4 + 2] = b; + kernel._colorData[index * 4 + 3] = a; + }; + + kernel.setWarnVarUsage = () => { + return kernel; + }; + kernel.setOptimizeFloatMemory = () => { + return kernel; + }; + kernel.setArgumentTypes = () => { + return kernel; + }; + kernel.setDebug = () => { + return kernel; + }; + kernel.setLoopMaxIterations = () => { + return kernel; + }; + kernel.setPipeline = () => { + return kernel; + }; + kernel.setPrecision = () => { + return kernel; + }; + kernel.setImmutable = () => { + return kernel; + }; + kernel.setFunctions = () => { + return kernel; + }; + kernel.addSubKernel = () => { + return kernel; + }; + kernel.destroy = () => {}; + kernel.validateSettings = () => {}; + if (kernel.graphical && kernel.output) { + setupGraphical(kernel); + } + return kernel; +} + +function setupGraphical(kernel) { + const {x, y} = kernel.output; + if (kernel.context && kernel.context.createImageData) { + const data = new Uint8ClampedArray(x * y * 4); + kernel._imageData = kernel.context.createImageData(x, y); + kernel._colorData = data; } else { - contextOutput = options.output; + const data = new Uint8ClampedArray(x * y * 4); + kernel._imageData = { data }; + kernel._colorData = data; } +} - const context = { - _fn: fn, - constants: options.constants, - output: contextOutput, - thread: { - x: 0, - y: 0, - z: 0 +function setupOutput(output) { + let result = null; + if (output.length) { + if (output.length === 3) { + const [x,y,z] = output; + result = { x, y, z }; + } else if (output.length === 2) { + const [x,y] = output; + result = { x, y }; + } else { + const [x] = output; + result = { x }; } + } else { + result = output; + } + return result; +} + +function gpuMock(fn, settings = {}) { + const output = settings.output ? setupOutput(settings.output) : null; + function kernel() { + if (kernel.output.z) { + return mock3D.apply(kernel, arguments); + } else if (kernel.output.y) { + if (kernel.graphical) { + return mock2DGraphical.apply(kernel, arguments); + } + return mock2D.apply(kernel, arguments); + } else { + return mock1D.apply(kernel, arguments); + } + } + kernel._fn = fn; + kernel.constants = settings.constants || null; + kernel.context = settings.context || null; + kernel.canvas = settings.canvas || null; + kernel.graphical = settings.graphical || false; + kernel._imageData = null; + kernel._colorData = null; + kernel.output = output; + kernel.thread = { + x: 0, + y: 0, + z: 0 }; + return apiDecorate(kernel); +} - if (contextOutput.z) { - return mock3D.bind(context); - } else if (contextOutput.y) { - return mock2D.bind(context); - } else { - return mock1D.bind(context); +function flipPixels(pixels, width, height) { + const halfHeight = height / 2 | 0; + const bytesPerRow = width * 4; + const temp = new Uint8ClampedArray(width * 4); + const result = pixels.slice(0); + for (let y = 0; y < halfHeight; ++y) { + const topOffset = y * bytesPerRow; + const bottomOffset = (height - y - 1) * bytesPerRow; + + temp.set(result.subarray(topOffset, topOffset + bytesPerRow)); + + result.copyWithin(topOffset, bottomOffset, bottomOffset + bytesPerRow); + + result.set(temp, bottomOffset); } + return result; +} + +module.exports = { + gpuMock }; },{}],4:[function(require,module,exports){ @@ -6654,11 +6831,19 @@ class WebGLFunctionNode extends FunctionNode { if (forNode.init) { this.pushState('in-for-loop-init'); this.astGeneric(forNode.init, initArr); - for (let i = 0; i < initArr.length; i++) { - if (initArr[i].includes && initArr[i].includes(',')) { + const { declarations } = forNode.init; + for (let i = 0; i < declarations.length; i++) { + if (declarations[i].init && declarations[i].init.type !== 'Literal') { isSafe = false; } } + if (isSafe) { + for (let i = 0; i < initArr.length; i++) { + if (initArr[i].includes && initArr[i].includes(',')) { + isSafe = false; + } + } + } this.popState('in-for-loop-init'); } else { isSafe = false; @@ -7694,7 +7879,8 @@ class WebGLKernelValueDynamicSingleInput extends WebGLKernelValueSingleInput { } updateValue(value) { - this.dimensions = value.size; + let [w, h, d] = value.size; + this.dimensions = new Int32Array([w || 1, h || 1, d || 1]); this.textureSize = utils.getMemoryOptimizedFloatTextureSize(this.dimensions, this.bitRatio); this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * this.bitRatio; this.uploadValue = new Float32Array(this.uploadArrayLength); @@ -7750,7 +7936,8 @@ class WebGLKernelValueDynamicUnsignedInput extends WebGLKernelValueUnsignedInput } updateValue(value) { - this.dimensions = value.size; + let [w, h, d] = value.size; + this.dimensions = new Int32Array([w || 1, h || 1, d || 1]); this.textureSize = utils.getMemoryOptimizedPackedTextureSize(this.dimensions, this.bitRatio); this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * (4 / this.bitRatio); const Type = this.getTransferArrayType(value.value); @@ -8126,7 +8313,8 @@ class WebGLKernelValueSingleInput extends WebGLKernelValue { super(value, settings); this.requestTexture(); this.bitRatio = 4; - this.dimensions = value.size; + let [w, h, d] = value.size; + this.dimensions = new Int32Array([w || 1, h || 1, d || 1]); this.textureSize = utils.getMemoryOptimizedFloatTextureSize(this.dimensions, this.bitRatio); this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * this.bitRatio; this.uploadValue = new Float32Array(this.uploadArrayLength); @@ -8231,7 +8419,8 @@ class WebGLKernelValueUnsignedInput extends WebGLKernelValue { super(value, settings); this.requestTexture(); this.bitRatio = this.getBitRatio(value); - this.dimensions = value.size; + const [w, h, d] = value.size; + this.dimensions = new Int32Array([w || 1, h || 1, d || 1]); this.textureSize = utils.getMemoryOptimizedPackedTextureSize(this.dimensions, this.bitRatio); this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * (4 / this.bitRatio); this.TranserArrayType = this.getTransferArrayType(value.value); @@ -9969,7 +10158,6 @@ void main(void) { module.exports = { fragmentShader }; - },{}],59:[function(require,module,exports){ const { WebGLFunctionNode } = require('../web-gl/function-node'); @@ -10251,7 +10439,8 @@ class WebGL2KernelValueDynamicSingleInput extends WebGL2KernelValueSingleInput { } updateValue(value) { - this.dimensions = value.size; + let [w, h, d] = value.size; + this.dimensions = new Int32Array([w || 1, h || 1, d || 1]); this.textureSize = utils.getMemoryOptimizedFloatTextureSize(this.dimensions, this.bitRatio); this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * this.bitRatio; this.uploadValue = new Float32Array(this.uploadArrayLength); @@ -11201,7 +11390,7 @@ for (const p in lib) { } module.exports = GPU; },{"./index":84}],83:[function(require,module,exports){ -const gpuMock = require('gpu-mock.js'); +const { gpuMock } = require('gpu-mock.js'); const { utils } = require('./utils'); const { CPUKernel } = require('./backend/cpu/kernel'); const { HeadlessGLKernel } = require('./backend/headless-gl/kernel'); @@ -11556,6 +11745,7 @@ class GPU { } destroy() { + if (!this.kernels) return; setTimeout(() => { for (let i = 0; i < this.kernels.length; i++) { this.kernels[i].destroy(true); @@ -11642,24 +11832,45 @@ module.exports = { class Input { constructor(value, size) { this.value = value; - this.size = new Int32Array(3); if (Array.isArray(size)) { - for (let i = 0; i < this.size.length; i++) { - this.size[i] = size[i] || 1; - } + this.size = size; } else { + this.size = new Int32Array(3); if (size.z) { this.size = new Int32Array([size.x, size.y, size.z]); } else if (size.y) { - this.size = new Int32Array([size.x, size.y, 1]); + this.size = new Int32Array([size.x, size.y]); } else { - this.size = new Int32Array([size.x, 1, 1]); + this.size = new Int32Array([size.x]); } } - const [h, w, d] = this.size; - if (this.value.length !== (h * w * d)) { - throw new Error(`Input size ${this.value.length} does not match ${w} * ${h} * ${d} = ${(h * w * d)}`); + const [w, h, d] = this.size; + if (d) { + if (this.value.length !== (w * h * d)) { + throw new Error(`Input size ${this.value.length} does not match ${w} * ${h} * ${d} = ${(h * w * d)}`); + } + } else if (h) { + if (this.value.length !== (w * h)) { + throw new Error(`Input size ${this.value.length} does not match ${w} * ${h} = ${(h * w)}`); + } + } else { + if (this.value.length !== w) { + throw new Error(`Input size ${this.value.length} does not match ${w}`); + } + } + + } + + toArray() { + const { utils } = require('./utils'); + const [w, h, d] = this.size; + if (d) { + return utils.erectMemoryOptimized3DFloat(this.value.subarray ? this.value : new Float32Array(this.value), w, h, d); + } else if (h) { + return utils.erectMemoryOptimized2DFloat(this.value.subarray ? this.value : new Float32Array(this.value), w, h); + } else { + return this.value; } } } @@ -11672,7 +11883,7 @@ module.exports = { Input, input }; -},{}],86:[function(require,module,exports){ +},{"./utils":89}],86:[function(require,module,exports){ const { utils } = require('./utils'); function kernelRunShortcut(kernel) { diff --git a/dist/gpu-browser-core.min.js b/dist/gpu-browser-core.min.js index 9d40381f..c9d91c4a 100644 --- a/dist/gpu-browser-core.min.js +++ b/dist/gpu-browser-core.min.js @@ -4,8 +4,8 @@ * * GPU Accelerated JavaScript * - * @version 2.0.0-rc.20 - * @date Fri Jul 05 2019 11:02:56 GMT-0400 (Eastern Daylight Time) + * @version 2.0.0-rc.21 + * @date Thu Jul 11 2019 13:18:13 GMT-0400 (Eastern Daylight Time) * * @license MIT * The MIT License @@ -17,8 +17,8 @@ * * GPU Accelerated JavaScript * - * @version 2.0.0-rc.20 - * @date Fri Jul 05 2019 11:02:54 GMT-0400 (Eastern Daylight Time) + * @version 2.0.0-rc.21 + * @date Thu Jul 11 2019 13:18:11 GMT-0400 (Eastern Daylight Time) * * @license MIT * The MIT License @@ -386,85 +386,262 @@ if (typeof window !== 'undefined') { } },{}],3:[function(require,module,exports){ -'use strict'; +function setupArguments(args) { + const newArguments = new Array(args.length); + for (let i = 0; i < args.length; i++) { + const arg = args[i]; + if (arg.toArray) { + newArguments[i] = arg.toArray(); + } else { + newArguments[i] = arg; + } + } + return newArguments; +} function mock1D() { - const row = []; + const args = setupArguments(arguments); + const row = new Float32Array(this.output.x); for (let x = 0; x < this.output.x; x++) { this.thread.x = x; this.thread.y = 0; this.thread.z = 0; - row.push(this._fn.apply(this, arguments)); + row[x] = this._fn.apply(this, args); } return row; } function mock2D() { - const matrix = []; + const args = setupArguments(arguments); + const matrix = new Array(this.output.y); for (let y = 0; y < this.output.y; y++) { - const row = []; + const row = new Float32Array(this.output.x); for (let x = 0; x < this.output.x; x++) { this.thread.x = x; this.thread.y = y; this.thread.z = 0; - row.push(this._fn.apply(this, arguments)); + row[x] = this._fn.apply(this, args); } - matrix.push(row); + matrix[y] = row; } return matrix; } +function mock2DGraphical() { + const args = setupArguments(arguments); + for (let y = 0; y < this.output.y; y++) { + for (let x = 0; x < this.output.x; x++) { + this.thread.x = x; + this.thread.y = y; + this.thread.z = 0; + this._fn.apply(this, args); + } + } +} + function mock3D() { - const cube = []; + const args = setupArguments(arguments); + const cube = new Array(this.output.z); for (let z = 0; z < this.output.z; z++) { - const matrix = []; + const matrix = new Array(this.output.y); for (let y = 0; y < this.output.y; y++) { - const row = []; + const row = new Float32Array(this.output.x); for (let x = 0; x < this.output.x; x++) { this.thread.x = x; this.thread.y = y; this.thread.z = z; - row.push(this._fn.apply(this, arguments)); + row[x] = this._fn.apply(this, args); } - matrix.push(row); + matrix[y] = row; } - cube.push(matrix); + cube[z] = matrix; } return cube; } -module.exports = function gpuMock(fn, options) { - let contextOutput = null; - if (options.output.length) { - if (options.output.length === 3) { - contextOutput = { x: options.output[0], y: options.output[1], z: options.output[2] }; - } else if (options.output.length === 2) { - contextOutput = { x: options.output[0], y: options.output[1] }; - } else { - contextOutput = { x: options.output[0] }; +function apiDecorate(kernel) { + kernel.setOutput = (output) => { + kernel.output = setupOutput(output); + if (kernel.graphical) { + setupGraphical(kernel); + } + }; + kernel.toJSON = () => { + throw new Error('Not usable with gpuMock'); + }; + kernel.setConstants = (flag) => { + kernel.constants = flag; + return kernel; + }; + kernel.setGraphical = (flag) => { + kernel.graphical = flag; + return kernel; + }; + kernel.setCanvas = (flag) => { + kernel.canvas = flag; + return kernel; + }; + kernel.setContext = (flag) => { + kernel.context = flag; + return kernel; + }; + kernel.exec = function() { + return new Promise((resolve, reject) => { + try { + resolve(kernel.apply(kernel, arguments)); + } catch(e) { + reject(e); + } + }); + }; + kernel.getPixels = (flip) => { + const {x, y} = kernel.output; + return flip ? flipPixels(kernel._imageData.data, x, y) : kernel._imageData.data.slice(0); + }; + kernel.color = function(r, g, b, a) { + if (typeof a === 'undefined') { + a = 1; } + + r = Math.floor(r * 255); + g = Math.floor(g * 255); + b = Math.floor(b * 255); + a = Math.floor(a * 255); + + const width = kernel.output.x; + const height = kernel.output.y; + + const x = kernel.thread.x; + const y = height - kernel.thread.y - 1; + + const index = x + y * width; + + kernel._colorData[index * 4 + 0] = r; + kernel._colorData[index * 4 + 1] = g; + kernel._colorData[index * 4 + 2] = b; + kernel._colorData[index * 4 + 3] = a; + }; + + kernel.setWarnVarUsage = () => { + return kernel; + }; + kernel.setOptimizeFloatMemory = () => { + return kernel; + }; + kernel.setArgumentTypes = () => { + return kernel; + }; + kernel.setDebug = () => { + return kernel; + }; + kernel.setLoopMaxIterations = () => { + return kernel; + }; + kernel.setPipeline = () => { + return kernel; + }; + kernel.setPrecision = () => { + return kernel; + }; + kernel.setImmutable = () => { + return kernel; + }; + kernel.setFunctions = () => { + return kernel; + }; + kernel.addSubKernel = () => { + return kernel; + }; + kernel.destroy = () => {}; + kernel.validateSettings = () => {}; + if (kernel.graphical && kernel.output) { + setupGraphical(kernel); + } + return kernel; +} + +function setupGraphical(kernel) { + const {x, y} = kernel.output; + if (kernel.context && kernel.context.createImageData) { + const data = new Uint8ClampedArray(x * y * 4); + kernel._imageData = kernel.context.createImageData(x, y); + kernel._colorData = data; } else { - contextOutput = options.output; + const data = new Uint8ClampedArray(x * y * 4); + kernel._imageData = { data }; + kernel._colorData = data; } +} - const context = { - _fn: fn, - constants: options.constants, - output: contextOutput, - thread: { - x: 0, - y: 0, - z: 0 +function setupOutput(output) { + let result = null; + if (output.length) { + if (output.length === 3) { + const [x,y,z] = output; + result = { x, y, z }; + } else if (output.length === 2) { + const [x,y] = output; + result = { x, y }; + } else { + const [x] = output; + result = { x }; } + } else { + result = output; + } + return result; +} + +function gpuMock(fn, settings = {}) { + const output = settings.output ? setupOutput(settings.output) : null; + function kernel() { + if (kernel.output.z) { + return mock3D.apply(kernel, arguments); + } else if (kernel.output.y) { + if (kernel.graphical) { + return mock2DGraphical.apply(kernel, arguments); + } + return mock2D.apply(kernel, arguments); + } else { + return mock1D.apply(kernel, arguments); + } + } + kernel._fn = fn; + kernel.constants = settings.constants || null; + kernel.context = settings.context || null; + kernel.canvas = settings.canvas || null; + kernel.graphical = settings.graphical || false; + kernel._imageData = null; + kernel._colorData = null; + kernel.output = output; + kernel.thread = { + x: 0, + y: 0, + z: 0 }; + return apiDecorate(kernel); +} - if (contextOutput.z) { - return mock3D.bind(context); - } else if (contextOutput.y) { - return mock2D.bind(context); - } else { - return mock1D.bind(context); +function flipPixels(pixels, width, height) { + const halfHeight = height / 2 | 0; + const bytesPerRow = width * 4; + const temp = new Uint8ClampedArray(width * 4); + const result = pixels.slice(0); + for (let y = 0; y < halfHeight; ++y) { + const topOffset = y * bytesPerRow; + const bottomOffset = (height - y - 1) * bytesPerRow; + + temp.set(result.subarray(topOffset, topOffset + bytesPerRow)); + + result.copyWithin(topOffset, bottomOffset, bottomOffset + bytesPerRow); + + result.set(temp, bottomOffset); } + return result; +} + +module.exports = { + gpuMock }; },{}],4:[function(require,module,exports){ @@ -6667,11 +6844,19 @@ class WebGLFunctionNode extends FunctionNode { if (forNode.init) { this.pushState('in-for-loop-init'); this.astGeneric(forNode.init, initArr); - for (let i = 0; i < initArr.length; i++) { - if (initArr[i].includes && initArr[i].includes(',')) { + const { declarations } = forNode.init; + for (let i = 0; i < declarations.length; i++) { + if (declarations[i].init && declarations[i].init.type !== 'Literal') { isSafe = false; } } + if (isSafe) { + for (let i = 0; i < initArr.length; i++) { + if (initArr[i].includes && initArr[i].includes(',')) { + isSafe = false; + } + } + } this.popState('in-for-loop-init'); } else { isSafe = false; @@ -7707,7 +7892,8 @@ class WebGLKernelValueDynamicSingleInput extends WebGLKernelValueSingleInput { } updateValue(value) { - this.dimensions = value.size; + let [w, h, d] = value.size; + this.dimensions = new Int32Array([w || 1, h || 1, d || 1]); this.textureSize = utils.getMemoryOptimizedFloatTextureSize(this.dimensions, this.bitRatio); this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * this.bitRatio; this.uploadValue = new Float32Array(this.uploadArrayLength); @@ -7763,7 +7949,8 @@ class WebGLKernelValueDynamicUnsignedInput extends WebGLKernelValueUnsignedInput } updateValue(value) { - this.dimensions = value.size; + let [w, h, d] = value.size; + this.dimensions = new Int32Array([w || 1, h || 1, d || 1]); this.textureSize = utils.getMemoryOptimizedPackedTextureSize(this.dimensions, this.bitRatio); this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * (4 / this.bitRatio); const Type = this.getTransferArrayType(value.value); @@ -8139,7 +8326,8 @@ class WebGLKernelValueSingleInput extends WebGLKernelValue { super(value, settings); this.requestTexture(); this.bitRatio = 4; - this.dimensions = value.size; + let [w, h, d] = value.size; + this.dimensions = new Int32Array([w || 1, h || 1, d || 1]); this.textureSize = utils.getMemoryOptimizedFloatTextureSize(this.dimensions, this.bitRatio); this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * this.bitRatio; this.uploadValue = new Float32Array(this.uploadArrayLength); @@ -8244,7 +8432,8 @@ class WebGLKernelValueUnsignedInput extends WebGLKernelValue { super(value, settings); this.requestTexture(); this.bitRatio = this.getBitRatio(value); - this.dimensions = value.size; + const [w, h, d] = value.size; + this.dimensions = new Int32Array([w || 1, h || 1, d || 1]); this.textureSize = utils.getMemoryOptimizedPackedTextureSize(this.dimensions, this.bitRatio); this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * (4 / this.bitRatio); this.TranserArrayType = this.getTransferArrayType(value.value); @@ -9982,7 +10171,6 @@ void main(void) { module.exports = { fragmentShader }; - },{}],59:[function(require,module,exports){ const { WebGLFunctionNode } = require('../web-gl/function-node'); @@ -10264,7 +10452,8 @@ class WebGL2KernelValueDynamicSingleInput extends WebGL2KernelValueSingleInput { } updateValue(value) { - this.dimensions = value.size; + let [w, h, d] = value.size; + this.dimensions = new Int32Array([w || 1, h || 1, d || 1]); this.textureSize = utils.getMemoryOptimizedFloatTextureSize(this.dimensions, this.bitRatio); this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * this.bitRatio; this.uploadValue = new Float32Array(this.uploadArrayLength); @@ -11214,7 +11403,7 @@ for (const p in lib) { } module.exports = GPU; },{"./index":84}],83:[function(require,module,exports){ -const gpuMock = require('gpu-mock.js'); +const { gpuMock } = require('gpu-mock.js'); const { utils } = require('./utils'); const { CPUKernel } = require('./backend/cpu/kernel'); const { HeadlessGLKernel } = require('./backend/headless-gl/kernel'); @@ -11569,6 +11758,7 @@ class GPU { } destroy() { + if (!this.kernels) return; setTimeout(() => { for (let i = 0; i < this.kernels.length; i++) { this.kernels[i].destroy(true); @@ -11655,24 +11845,45 @@ module.exports = { class Input { constructor(value, size) { this.value = value; - this.size = new Int32Array(3); if (Array.isArray(size)) { - for (let i = 0; i < this.size.length; i++) { - this.size[i] = size[i] || 1; - } + this.size = size; } else { + this.size = new Int32Array(3); if (size.z) { this.size = new Int32Array([size.x, size.y, size.z]); } else if (size.y) { - this.size = new Int32Array([size.x, size.y, 1]); + this.size = new Int32Array([size.x, size.y]); } else { - this.size = new Int32Array([size.x, 1, 1]); + this.size = new Int32Array([size.x]); } } - const [h, w, d] = this.size; - if (this.value.length !== (h * w * d)) { - throw new Error(`Input size ${this.value.length} does not match ${w} * ${h} * ${d} = ${(h * w * d)}`); + const [w, h, d] = this.size; + if (d) { + if (this.value.length !== (w * h * d)) { + throw new Error(`Input size ${this.value.length} does not match ${w} * ${h} * ${d} = ${(h * w * d)}`); + } + } else if (h) { + if (this.value.length !== (w * h)) { + throw new Error(`Input size ${this.value.length} does not match ${w} * ${h} = ${(h * w)}`); + } + } else { + if (this.value.length !== w) { + throw new Error(`Input size ${this.value.length} does not match ${w}`); + } + } + + } + + toArray() { + const { utils } = require('./utils'); + const [w, h, d] = this.size; + if (d) { + return utils.erectMemoryOptimized3DFloat(this.value.subarray ? this.value : new Float32Array(this.value), w, h, d); + } else if (h) { + return utils.erectMemoryOptimized2DFloat(this.value.subarray ? this.value : new Float32Array(this.value), w, h); + } else { + return this.value; } } } @@ -11685,7 +11896,7 @@ module.exports = { Input, input }; -},{}],86:[function(require,module,exports){ +},{"./utils":89}],86:[function(require,module,exports){ const { utils } = require('./utils'); function kernelRunShortcut(kernel) { diff --git a/dist/gpu-browser.js b/dist/gpu-browser.js index 6e21a38d..48a1a921 100644 --- a/dist/gpu-browser.js +++ b/dist/gpu-browser.js @@ -4,8 +4,8 @@ * * GPU Accelerated JavaScript * - * @version 2.0.0-rc.20 - * @date Fri Jul 05 2019 11:02:54 GMT-0400 (Eastern Daylight Time) + * @version 2.0.0-rc.21 + * @date Thu Jul 11 2019 13:18:11 GMT-0400 (Eastern Daylight Time) * * @license MIT * The MIT License @@ -5137,85 +5137,262 @@ if (typeof window !== 'undefined') { } },{}],4:[function(require,module,exports){ -'use strict'; +function setupArguments(args) { + const newArguments = new Array(args.length); + for (let i = 0; i < args.length; i++) { + const arg = args[i]; + if (arg.toArray) { + newArguments[i] = arg.toArray(); + } else { + newArguments[i] = arg; + } + } + return newArguments; +} function mock1D() { - const row = []; + const args = setupArguments(arguments); + const row = new Float32Array(this.output.x); for (let x = 0; x < this.output.x; x++) { this.thread.x = x; this.thread.y = 0; this.thread.z = 0; - row.push(this._fn.apply(this, arguments)); + row[x] = this._fn.apply(this, args); } return row; } function mock2D() { - const matrix = []; + const args = setupArguments(arguments); + const matrix = new Array(this.output.y); for (let y = 0; y < this.output.y; y++) { - const row = []; + const row = new Float32Array(this.output.x); for (let x = 0; x < this.output.x; x++) { this.thread.x = x; this.thread.y = y; this.thread.z = 0; - row.push(this._fn.apply(this, arguments)); + row[x] = this._fn.apply(this, args); } - matrix.push(row); + matrix[y] = row; } return matrix; } +function mock2DGraphical() { + const args = setupArguments(arguments); + for (let y = 0; y < this.output.y; y++) { + for (let x = 0; x < this.output.x; x++) { + this.thread.x = x; + this.thread.y = y; + this.thread.z = 0; + this._fn.apply(this, args); + } + } +} + function mock3D() { - const cube = []; + const args = setupArguments(arguments); + const cube = new Array(this.output.z); for (let z = 0; z < this.output.z; z++) { - const matrix = []; + const matrix = new Array(this.output.y); for (let y = 0; y < this.output.y; y++) { - const row = []; + const row = new Float32Array(this.output.x); for (let x = 0; x < this.output.x; x++) { this.thread.x = x; this.thread.y = y; this.thread.z = z; - row.push(this._fn.apply(this, arguments)); + row[x] = this._fn.apply(this, args); } - matrix.push(row); + matrix[y] = row; } - cube.push(matrix); + cube[z] = matrix; } return cube; } -module.exports = function gpuMock(fn, options) { - let contextOutput = null; - if (options.output.length) { - if (options.output.length === 3) { - contextOutput = { x: options.output[0], y: options.output[1], z: options.output[2] }; - } else if (options.output.length === 2) { - contextOutput = { x: options.output[0], y: options.output[1] }; - } else { - contextOutput = { x: options.output[0] }; +function apiDecorate(kernel) { + kernel.setOutput = (output) => { + kernel.output = setupOutput(output); + if (kernel.graphical) { + setupGraphical(kernel); + } + }; + kernel.toJSON = () => { + throw new Error('Not usable with gpuMock'); + }; + kernel.setConstants = (flag) => { + kernel.constants = flag; + return kernel; + }; + kernel.setGraphical = (flag) => { + kernel.graphical = flag; + return kernel; + }; + kernel.setCanvas = (flag) => { + kernel.canvas = flag; + return kernel; + }; + kernel.setContext = (flag) => { + kernel.context = flag; + return kernel; + }; + kernel.exec = function() { + return new Promise((resolve, reject) => { + try { + resolve(kernel.apply(kernel, arguments)); + } catch(e) { + reject(e); + } + }); + }; + kernel.getPixels = (flip) => { + const {x, y} = kernel.output; + return flip ? flipPixels(kernel._imageData.data, x, y) : kernel._imageData.data.slice(0); + }; + kernel.color = function(r, g, b, a) { + if (typeof a === 'undefined') { + a = 1; } + + r = Math.floor(r * 255); + g = Math.floor(g * 255); + b = Math.floor(b * 255); + a = Math.floor(a * 255); + + const width = kernel.output.x; + const height = kernel.output.y; + + const x = kernel.thread.x; + const y = height - kernel.thread.y - 1; + + const index = x + y * width; + + kernel._colorData[index * 4 + 0] = r; + kernel._colorData[index * 4 + 1] = g; + kernel._colorData[index * 4 + 2] = b; + kernel._colorData[index * 4 + 3] = a; + }; + + kernel.setWarnVarUsage = () => { + return kernel; + }; + kernel.setOptimizeFloatMemory = () => { + return kernel; + }; + kernel.setArgumentTypes = () => { + return kernel; + }; + kernel.setDebug = () => { + return kernel; + }; + kernel.setLoopMaxIterations = () => { + return kernel; + }; + kernel.setPipeline = () => { + return kernel; + }; + kernel.setPrecision = () => { + return kernel; + }; + kernel.setImmutable = () => { + return kernel; + }; + kernel.setFunctions = () => { + return kernel; + }; + kernel.addSubKernel = () => { + return kernel; + }; + kernel.destroy = () => {}; + kernel.validateSettings = () => {}; + if (kernel.graphical && kernel.output) { + setupGraphical(kernel); + } + return kernel; +} + +function setupGraphical(kernel) { + const {x, y} = kernel.output; + if (kernel.context && kernel.context.createImageData) { + const data = new Uint8ClampedArray(x * y * 4); + kernel._imageData = kernel.context.createImageData(x, y); + kernel._colorData = data; } else { - contextOutput = options.output; + const data = new Uint8ClampedArray(x * y * 4); + kernel._imageData = { data }; + kernel._colorData = data; } +} - const context = { - _fn: fn, - constants: options.constants, - output: contextOutput, - thread: { - x: 0, - y: 0, - z: 0 +function setupOutput(output) { + let result = null; + if (output.length) { + if (output.length === 3) { + const [x,y,z] = output; + result = { x, y, z }; + } else if (output.length === 2) { + const [x,y] = output; + result = { x, y }; + } else { + const [x] = output; + result = { x }; } + } else { + result = output; + } + return result; +} + +function gpuMock(fn, settings = {}) { + const output = settings.output ? setupOutput(settings.output) : null; + function kernel() { + if (kernel.output.z) { + return mock3D.apply(kernel, arguments); + } else if (kernel.output.y) { + if (kernel.graphical) { + return mock2DGraphical.apply(kernel, arguments); + } + return mock2D.apply(kernel, arguments); + } else { + return mock1D.apply(kernel, arguments); + } + } + kernel._fn = fn; + kernel.constants = settings.constants || null; + kernel.context = settings.context || null; + kernel.canvas = settings.canvas || null; + kernel.graphical = settings.graphical || false; + kernel._imageData = null; + kernel._colorData = null; + kernel.output = output; + kernel.thread = { + x: 0, + y: 0, + z: 0 }; + return apiDecorate(kernel); +} - if (contextOutput.z) { - return mock3D.bind(context); - } else if (contextOutput.y) { - return mock2D.bind(context); - } else { - return mock1D.bind(context); +function flipPixels(pixels, width, height) { + const halfHeight = height / 2 | 0; + const bytesPerRow = width * 4; + const temp = new Uint8ClampedArray(width * 4); + const result = pixels.slice(0); + for (let y = 0; y < halfHeight; ++y) { + const topOffset = y * bytesPerRow; + const bottomOffset = (height - y - 1) * bytesPerRow; + + temp.set(result.subarray(topOffset, topOffset + bytesPerRow)); + + result.copyWithin(topOffset, bottomOffset, bottomOffset + bytesPerRow); + + result.set(temp, bottomOffset); } + return result; +} + +module.exports = { + gpuMock }; },{}],5:[function(require,module,exports){ @@ -11418,11 +11595,19 @@ class WebGLFunctionNode extends FunctionNode { if (forNode.init) { this.pushState('in-for-loop-init'); this.astGeneric(forNode.init, initArr); - for (let i = 0; i < initArr.length; i++) { - if (initArr[i].includes && initArr[i].includes(',')) { + const { declarations } = forNode.init; + for (let i = 0; i < declarations.length; i++) { + if (declarations[i].init && declarations[i].init.type !== 'Literal') { isSafe = false; } } + if (isSafe) { + for (let i = 0; i < initArr.length; i++) { + if (initArr[i].includes && initArr[i].includes(',')) { + isSafe = false; + } + } + } this.popState('in-for-loop-init'); } else { isSafe = false; @@ -12458,7 +12643,8 @@ class WebGLKernelValueDynamicSingleInput extends WebGLKernelValueSingleInput { } updateValue(value) { - this.dimensions = value.size; + let [w, h, d] = value.size; + this.dimensions = new Int32Array([w || 1, h || 1, d || 1]); this.textureSize = utils.getMemoryOptimizedFloatTextureSize(this.dimensions, this.bitRatio); this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * this.bitRatio; this.uploadValue = new Float32Array(this.uploadArrayLength); @@ -12514,7 +12700,8 @@ class WebGLKernelValueDynamicUnsignedInput extends WebGLKernelValueUnsignedInput } updateValue(value) { - this.dimensions = value.size; + let [w, h, d] = value.size; + this.dimensions = new Int32Array([w || 1, h || 1, d || 1]); this.textureSize = utils.getMemoryOptimizedPackedTextureSize(this.dimensions, this.bitRatio); this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * (4 / this.bitRatio); const Type = this.getTransferArrayType(value.value); @@ -12890,7 +13077,8 @@ class WebGLKernelValueSingleInput extends WebGLKernelValue { super(value, settings); this.requestTexture(); this.bitRatio = 4; - this.dimensions = value.size; + let [w, h, d] = value.size; + this.dimensions = new Int32Array([w || 1, h || 1, d || 1]); this.textureSize = utils.getMemoryOptimizedFloatTextureSize(this.dimensions, this.bitRatio); this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * this.bitRatio; this.uploadValue = new Float32Array(this.uploadArrayLength); @@ -12995,7 +13183,8 @@ class WebGLKernelValueUnsignedInput extends WebGLKernelValue { super(value, settings); this.requestTexture(); this.bitRatio = this.getBitRatio(value); - this.dimensions = value.size; + const [w, h, d] = value.size; + this.dimensions = new Int32Array([w || 1, h || 1, d || 1]); this.textureSize = utils.getMemoryOptimizedPackedTextureSize(this.dimensions, this.bitRatio); this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * (4 / this.bitRatio); this.TranserArrayType = this.getTransferArrayType(value.value); @@ -14733,7 +14922,6 @@ void main(void) { module.exports = { fragmentShader }; - },{}],60:[function(require,module,exports){ const { WebGLFunctionNode } = require('../web-gl/function-node'); @@ -15015,7 +15203,8 @@ class WebGL2KernelValueDynamicSingleInput extends WebGL2KernelValueSingleInput { } updateValue(value) { - this.dimensions = value.size; + let [w, h, d] = value.size; + this.dimensions = new Int32Array([w || 1, h || 1, d || 1]); this.textureSize = utils.getMemoryOptimizedFloatTextureSize(this.dimensions, this.bitRatio); this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * this.bitRatio; this.uploadValue = new Float32Array(this.uploadArrayLength); @@ -15965,7 +16154,7 @@ for (const p in lib) { } module.exports = GPU; },{"./index":85}],84:[function(require,module,exports){ -const gpuMock = require('gpu-mock.js'); +const { gpuMock } = require('gpu-mock.js'); const { utils } = require('./utils'); const { CPUKernel } = require('./backend/cpu/kernel'); const { HeadlessGLKernel } = require('./backend/headless-gl/kernel'); @@ -16320,6 +16509,7 @@ class GPU { } destroy() { + if (!this.kernels) return; setTimeout(() => { for (let i = 0; i < this.kernels.length; i++) { this.kernels[i].destroy(true); @@ -16406,24 +16596,45 @@ module.exports = { class Input { constructor(value, size) { this.value = value; - this.size = new Int32Array(3); if (Array.isArray(size)) { - for (let i = 0; i < this.size.length; i++) { - this.size[i] = size[i] || 1; - } + this.size = size; } else { + this.size = new Int32Array(3); if (size.z) { this.size = new Int32Array([size.x, size.y, size.z]); } else if (size.y) { - this.size = new Int32Array([size.x, size.y, 1]); + this.size = new Int32Array([size.x, size.y]); } else { - this.size = new Int32Array([size.x, 1, 1]); + this.size = new Int32Array([size.x]); } } - const [h, w, d] = this.size; - if (this.value.length !== (h * w * d)) { - throw new Error(`Input size ${this.value.length} does not match ${w} * ${h} * ${d} = ${(h * w * d)}`); + const [w, h, d] = this.size; + if (d) { + if (this.value.length !== (w * h * d)) { + throw new Error(`Input size ${this.value.length} does not match ${w} * ${h} * ${d} = ${(h * w * d)}`); + } + } else if (h) { + if (this.value.length !== (w * h)) { + throw new Error(`Input size ${this.value.length} does not match ${w} * ${h} = ${(h * w)}`); + } + } else { + if (this.value.length !== w) { + throw new Error(`Input size ${this.value.length} does not match ${w}`); + } + } + + } + + toArray() { + const { utils } = require('./utils'); + const [w, h, d] = this.size; + if (d) { + return utils.erectMemoryOptimized3DFloat(this.value.subarray ? this.value : new Float32Array(this.value), w, h, d); + } else if (h) { + return utils.erectMemoryOptimized2DFloat(this.value.subarray ? this.value : new Float32Array(this.value), w, h); + } else { + return this.value; } } } @@ -16436,7 +16647,7 @@ module.exports = { Input, input }; -},{}],87:[function(require,module,exports){ +},{"./utils":90}],87:[function(require,module,exports){ const { utils } = require('./utils'); function kernelRunShortcut(kernel) { diff --git a/dist/gpu-browser.min.js b/dist/gpu-browser.min.js index d7f9e6a9..e7712e37 100644 --- a/dist/gpu-browser.min.js +++ b/dist/gpu-browser.min.js @@ -4,8 +4,8 @@ * * GPU Accelerated JavaScript * - * @version 2.0.0-rc.20 - * @date Fri Jul 05 2019 11:02:56 GMT-0400 (Eastern Daylight Time) + * @version 2.0.0-rc.21 + * @date Thu Jul 11 2019 13:18:13 GMT-0400 (Eastern Daylight Time) * * @license MIT * The MIT License @@ -17,8 +17,8 @@ * * GPU Accelerated JavaScript * - * @version 2.0.0-rc.20 - * @date Fri Jul 05 2019 11:02:54 GMT-0400 (Eastern Daylight Time) + * @version 2.0.0-rc.21 + * @date Thu Jul 11 2019 13:18:11 GMT-0400 (Eastern Daylight Time) * * @license MIT * The MIT License @@ -5150,85 +5150,262 @@ if (typeof window !== 'undefined') { } },{}],4:[function(require,module,exports){ -'use strict'; +function setupArguments(args) { + const newArguments = new Array(args.length); + for (let i = 0; i < args.length; i++) { + const arg = args[i]; + if (arg.toArray) { + newArguments[i] = arg.toArray(); + } else { + newArguments[i] = arg; + } + } + return newArguments; +} function mock1D() { - const row = []; + const args = setupArguments(arguments); + const row = new Float32Array(this.output.x); for (let x = 0; x < this.output.x; x++) { this.thread.x = x; this.thread.y = 0; this.thread.z = 0; - row.push(this._fn.apply(this, arguments)); + row[x] = this._fn.apply(this, args); } return row; } function mock2D() { - const matrix = []; + const args = setupArguments(arguments); + const matrix = new Array(this.output.y); for (let y = 0; y < this.output.y; y++) { - const row = []; + const row = new Float32Array(this.output.x); for (let x = 0; x < this.output.x; x++) { this.thread.x = x; this.thread.y = y; this.thread.z = 0; - row.push(this._fn.apply(this, arguments)); + row[x] = this._fn.apply(this, args); } - matrix.push(row); + matrix[y] = row; } return matrix; } +function mock2DGraphical() { + const args = setupArguments(arguments); + for (let y = 0; y < this.output.y; y++) { + for (let x = 0; x < this.output.x; x++) { + this.thread.x = x; + this.thread.y = y; + this.thread.z = 0; + this._fn.apply(this, args); + } + } +} + function mock3D() { - const cube = []; + const args = setupArguments(arguments); + const cube = new Array(this.output.z); for (let z = 0; z < this.output.z; z++) { - const matrix = []; + const matrix = new Array(this.output.y); for (let y = 0; y < this.output.y; y++) { - const row = []; + const row = new Float32Array(this.output.x); for (let x = 0; x < this.output.x; x++) { this.thread.x = x; this.thread.y = y; this.thread.z = z; - row.push(this._fn.apply(this, arguments)); + row[x] = this._fn.apply(this, args); } - matrix.push(row); + matrix[y] = row; } - cube.push(matrix); + cube[z] = matrix; } return cube; } -module.exports = function gpuMock(fn, options) { - let contextOutput = null; - if (options.output.length) { - if (options.output.length === 3) { - contextOutput = { x: options.output[0], y: options.output[1], z: options.output[2] }; - } else if (options.output.length === 2) { - contextOutput = { x: options.output[0], y: options.output[1] }; - } else { - contextOutput = { x: options.output[0] }; +function apiDecorate(kernel) { + kernel.setOutput = (output) => { + kernel.output = setupOutput(output); + if (kernel.graphical) { + setupGraphical(kernel); + } + }; + kernel.toJSON = () => { + throw new Error('Not usable with gpuMock'); + }; + kernel.setConstants = (flag) => { + kernel.constants = flag; + return kernel; + }; + kernel.setGraphical = (flag) => { + kernel.graphical = flag; + return kernel; + }; + kernel.setCanvas = (flag) => { + kernel.canvas = flag; + return kernel; + }; + kernel.setContext = (flag) => { + kernel.context = flag; + return kernel; + }; + kernel.exec = function() { + return new Promise((resolve, reject) => { + try { + resolve(kernel.apply(kernel, arguments)); + } catch(e) { + reject(e); + } + }); + }; + kernel.getPixels = (flip) => { + const {x, y} = kernel.output; + return flip ? flipPixels(kernel._imageData.data, x, y) : kernel._imageData.data.slice(0); + }; + kernel.color = function(r, g, b, a) { + if (typeof a === 'undefined') { + a = 1; } + + r = Math.floor(r * 255); + g = Math.floor(g * 255); + b = Math.floor(b * 255); + a = Math.floor(a * 255); + + const width = kernel.output.x; + const height = kernel.output.y; + + const x = kernel.thread.x; + const y = height - kernel.thread.y - 1; + + const index = x + y * width; + + kernel._colorData[index * 4 + 0] = r; + kernel._colorData[index * 4 + 1] = g; + kernel._colorData[index * 4 + 2] = b; + kernel._colorData[index * 4 + 3] = a; + }; + + kernel.setWarnVarUsage = () => { + return kernel; + }; + kernel.setOptimizeFloatMemory = () => { + return kernel; + }; + kernel.setArgumentTypes = () => { + return kernel; + }; + kernel.setDebug = () => { + return kernel; + }; + kernel.setLoopMaxIterations = () => { + return kernel; + }; + kernel.setPipeline = () => { + return kernel; + }; + kernel.setPrecision = () => { + return kernel; + }; + kernel.setImmutable = () => { + return kernel; + }; + kernel.setFunctions = () => { + return kernel; + }; + kernel.addSubKernel = () => { + return kernel; + }; + kernel.destroy = () => {}; + kernel.validateSettings = () => {}; + if (kernel.graphical && kernel.output) { + setupGraphical(kernel); + } + return kernel; +} + +function setupGraphical(kernel) { + const {x, y} = kernel.output; + if (kernel.context && kernel.context.createImageData) { + const data = new Uint8ClampedArray(x * y * 4); + kernel._imageData = kernel.context.createImageData(x, y); + kernel._colorData = data; } else { - contextOutput = options.output; + const data = new Uint8ClampedArray(x * y * 4); + kernel._imageData = { data }; + kernel._colorData = data; } +} - const context = { - _fn: fn, - constants: options.constants, - output: contextOutput, - thread: { - x: 0, - y: 0, - z: 0 +function setupOutput(output) { + let result = null; + if (output.length) { + if (output.length === 3) { + const [x,y,z] = output; + result = { x, y, z }; + } else if (output.length === 2) { + const [x,y] = output; + result = { x, y }; + } else { + const [x] = output; + result = { x }; } + } else { + result = output; + } + return result; +} + +function gpuMock(fn, settings = {}) { + const output = settings.output ? setupOutput(settings.output) : null; + function kernel() { + if (kernel.output.z) { + return mock3D.apply(kernel, arguments); + } else if (kernel.output.y) { + if (kernel.graphical) { + return mock2DGraphical.apply(kernel, arguments); + } + return mock2D.apply(kernel, arguments); + } else { + return mock1D.apply(kernel, arguments); + } + } + kernel._fn = fn; + kernel.constants = settings.constants || null; + kernel.context = settings.context || null; + kernel.canvas = settings.canvas || null; + kernel.graphical = settings.graphical || false; + kernel._imageData = null; + kernel._colorData = null; + kernel.output = output; + kernel.thread = { + x: 0, + y: 0, + z: 0 }; + return apiDecorate(kernel); +} - if (contextOutput.z) { - return mock3D.bind(context); - } else if (contextOutput.y) { - return mock2D.bind(context); - } else { - return mock1D.bind(context); +function flipPixels(pixels, width, height) { + const halfHeight = height / 2 | 0; + const bytesPerRow = width * 4; + const temp = new Uint8ClampedArray(width * 4); + const result = pixels.slice(0); + for (let y = 0; y < halfHeight; ++y) { + const topOffset = y * bytesPerRow; + const bottomOffset = (height - y - 1) * bytesPerRow; + + temp.set(result.subarray(topOffset, topOffset + bytesPerRow)); + + result.copyWithin(topOffset, bottomOffset, bottomOffset + bytesPerRow); + + result.set(temp, bottomOffset); } + return result; +} + +module.exports = { + gpuMock }; },{}],5:[function(require,module,exports){ @@ -11431,11 +11608,19 @@ class WebGLFunctionNode extends FunctionNode { if (forNode.init) { this.pushState('in-for-loop-init'); this.astGeneric(forNode.init, initArr); - for (let i = 0; i < initArr.length; i++) { - if (initArr[i].includes && initArr[i].includes(',')) { + const { declarations } = forNode.init; + for (let i = 0; i < declarations.length; i++) { + if (declarations[i].init && declarations[i].init.type !== 'Literal') { isSafe = false; } } + if (isSafe) { + for (let i = 0; i < initArr.length; i++) { + if (initArr[i].includes && initArr[i].includes(',')) { + isSafe = false; + } + } + } this.popState('in-for-loop-init'); } else { isSafe = false; @@ -12471,7 +12656,8 @@ class WebGLKernelValueDynamicSingleInput extends WebGLKernelValueSingleInput { } updateValue(value) { - this.dimensions = value.size; + let [w, h, d] = value.size; + this.dimensions = new Int32Array([w || 1, h || 1, d || 1]); this.textureSize = utils.getMemoryOptimizedFloatTextureSize(this.dimensions, this.bitRatio); this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * this.bitRatio; this.uploadValue = new Float32Array(this.uploadArrayLength); @@ -12527,7 +12713,8 @@ class WebGLKernelValueDynamicUnsignedInput extends WebGLKernelValueUnsignedInput } updateValue(value) { - this.dimensions = value.size; + let [w, h, d] = value.size; + this.dimensions = new Int32Array([w || 1, h || 1, d || 1]); this.textureSize = utils.getMemoryOptimizedPackedTextureSize(this.dimensions, this.bitRatio); this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * (4 / this.bitRatio); const Type = this.getTransferArrayType(value.value); @@ -12903,7 +13090,8 @@ class WebGLKernelValueSingleInput extends WebGLKernelValue { super(value, settings); this.requestTexture(); this.bitRatio = 4; - this.dimensions = value.size; + let [w, h, d] = value.size; + this.dimensions = new Int32Array([w || 1, h || 1, d || 1]); this.textureSize = utils.getMemoryOptimizedFloatTextureSize(this.dimensions, this.bitRatio); this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * this.bitRatio; this.uploadValue = new Float32Array(this.uploadArrayLength); @@ -13008,7 +13196,8 @@ class WebGLKernelValueUnsignedInput extends WebGLKernelValue { super(value, settings); this.requestTexture(); this.bitRatio = this.getBitRatio(value); - this.dimensions = value.size; + const [w, h, d] = value.size; + this.dimensions = new Int32Array([w || 1, h || 1, d || 1]); this.textureSize = utils.getMemoryOptimizedPackedTextureSize(this.dimensions, this.bitRatio); this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * (4 / this.bitRatio); this.TranserArrayType = this.getTransferArrayType(value.value); @@ -14746,7 +14935,6 @@ void main(void) { module.exports = { fragmentShader }; - },{}],60:[function(require,module,exports){ const { WebGLFunctionNode } = require('../web-gl/function-node'); @@ -15028,7 +15216,8 @@ class WebGL2KernelValueDynamicSingleInput extends WebGL2KernelValueSingleInput { } updateValue(value) { - this.dimensions = value.size; + let [w, h, d] = value.size; + this.dimensions = new Int32Array([w || 1, h || 1, d || 1]); this.textureSize = utils.getMemoryOptimizedFloatTextureSize(this.dimensions, this.bitRatio); this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * this.bitRatio; this.uploadValue = new Float32Array(this.uploadArrayLength); @@ -15978,7 +16167,7 @@ for (const p in lib) { } module.exports = GPU; },{"./index":85}],84:[function(require,module,exports){ -const gpuMock = require('gpu-mock.js'); +const { gpuMock } = require('gpu-mock.js'); const { utils } = require('./utils'); const { CPUKernel } = require('./backend/cpu/kernel'); const { HeadlessGLKernel } = require('./backend/headless-gl/kernel'); @@ -16333,6 +16522,7 @@ class GPU { } destroy() { + if (!this.kernels) return; setTimeout(() => { for (let i = 0; i < this.kernels.length; i++) { this.kernels[i].destroy(true); @@ -16419,24 +16609,45 @@ module.exports = { class Input { constructor(value, size) { this.value = value; - this.size = new Int32Array(3); if (Array.isArray(size)) { - for (let i = 0; i < this.size.length; i++) { - this.size[i] = size[i] || 1; - } + this.size = size; } else { + this.size = new Int32Array(3); if (size.z) { this.size = new Int32Array([size.x, size.y, size.z]); } else if (size.y) { - this.size = new Int32Array([size.x, size.y, 1]); + this.size = new Int32Array([size.x, size.y]); } else { - this.size = new Int32Array([size.x, 1, 1]); + this.size = new Int32Array([size.x]); } } - const [h, w, d] = this.size; - if (this.value.length !== (h * w * d)) { - throw new Error(`Input size ${this.value.length} does not match ${w} * ${h} * ${d} = ${(h * w * d)}`); + const [w, h, d] = this.size; + if (d) { + if (this.value.length !== (w * h * d)) { + throw new Error(`Input size ${this.value.length} does not match ${w} * ${h} * ${d} = ${(h * w * d)}`); + } + } else if (h) { + if (this.value.length !== (w * h)) { + throw new Error(`Input size ${this.value.length} does not match ${w} * ${h} = ${(h * w)}`); + } + } else { + if (this.value.length !== w) { + throw new Error(`Input size ${this.value.length} does not match ${w}`); + } + } + + } + + toArray() { + const { utils } = require('./utils'); + const [w, h, d] = this.size; + if (d) { + return utils.erectMemoryOptimized3DFloat(this.value.subarray ? this.value : new Float32Array(this.value), w, h, d); + } else if (h) { + return utils.erectMemoryOptimized2DFloat(this.value.subarray ? this.value : new Float32Array(this.value), w, h); + } else { + return this.value; } } } @@ -16449,7 +16660,7 @@ module.exports = { Input, input }; -},{}],87:[function(require,module,exports){ +},{"./utils":90}],87:[function(require,module,exports){ const { utils } = require('./utils'); function kernelRunShortcut(kernel) { diff --git a/package.json b/package.json index fd4d20e8..01aa0641 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,14 @@ { "name": "gpu.js", - "version": "2.0.0-rc.20", + "version": "2.0.0-rc.21", "description": "GPU Accelerated JavaScript", "engines": { "node": ">=10.0.0" }, "main": "./src/index.js", "files": [ - "src", "dist" + "src", + "dist" ], "unpkg": "./dist/gpu-browser.min.js", "jsdelivr": "./dist/gpu-browser.min.js", @@ -19,7 +20,7 @@ "acorn": "^5.1.1", "gl": "^4.3.3", "gl-wiretap": "^0.6.0", - "gpu-mock.js": "^1.0.1" + "gpu-mock.js": "^1.1.0" }, "devDependencies": { "benchmark": "^2.1.4", diff --git a/src/backend/web-gl/function-node.js b/src/backend/web-gl/function-node.js index 4551076d..042d06d0 100644 --- a/src/backend/web-gl/function-node.js +++ b/src/backend/web-gl/function-node.js @@ -593,11 +593,19 @@ class WebGLFunctionNode extends FunctionNode { if (forNode.init) { this.pushState('in-for-loop-init'); this.astGeneric(forNode.init, initArr); - for (let i = 0; i < initArr.length; i++) { - if (initArr[i].includes && initArr[i].includes(',')) { + const { declarations } = forNode.init; + for (let i = 0; i < declarations.length; i++) { + if (declarations[i].init && declarations[i].init.type !== 'Literal') { isSafe = false; } } + if (isSafe) { + for (let i = 0; i < initArr.length; i++) { + if (initArr[i].includes && initArr[i].includes(',')) { + isSafe = false; + } + } + } this.popState('in-for-loop-init'); } else { isSafe = false; diff --git a/src/backend/web-gl/kernel-value/dynamic-single-input.js b/src/backend/web-gl/kernel-value/dynamic-single-input.js index 1db18f17..f8d532a8 100644 --- a/src/backend/web-gl/kernel-value/dynamic-single-input.js +++ b/src/backend/web-gl/kernel-value/dynamic-single-input.js @@ -11,7 +11,8 @@ class WebGLKernelValueDynamicSingleInput extends WebGLKernelValueSingleInput { } updateValue(value) { - this.dimensions = value.size; + let [w, h, d] = value.size; + this.dimensions = new Int32Array([w || 1, h || 1, d || 1]); this.textureSize = utils.getMemoryOptimizedFloatTextureSize(this.dimensions, this.bitRatio); this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * this.bitRatio; this.uploadValue = new Float32Array(this.uploadArrayLength); diff --git a/src/backend/web-gl/kernel-value/dynamic-unsigned-input.js b/src/backend/web-gl/kernel-value/dynamic-unsigned-input.js index 66d01e0f..94ee8a4b 100644 --- a/src/backend/web-gl/kernel-value/dynamic-unsigned-input.js +++ b/src/backend/web-gl/kernel-value/dynamic-unsigned-input.js @@ -11,7 +11,8 @@ class WebGLKernelValueDynamicUnsignedInput extends WebGLKernelValueUnsignedInput } updateValue(value) { - this.dimensions = value.size; + let [w, h, d] = value.size; + this.dimensions = new Int32Array([w || 1, h || 1, d || 1]); this.textureSize = utils.getMemoryOptimizedPackedTextureSize(this.dimensions, this.bitRatio); this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * (4 / this.bitRatio); const Type = this.getTransferArrayType(value.value); diff --git a/src/backend/web-gl/kernel-value/single-input.js b/src/backend/web-gl/kernel-value/single-input.js index 03d036f2..e6dc9704 100644 --- a/src/backend/web-gl/kernel-value/single-input.js +++ b/src/backend/web-gl/kernel-value/single-input.js @@ -6,7 +6,8 @@ class WebGLKernelValueSingleInput extends WebGLKernelValue { super(value, settings); this.requestTexture(); this.bitRatio = 4; - this.dimensions = value.size; + let [w, h, d] = value.size; + this.dimensions = new Int32Array([w || 1, h || 1, d || 1]); this.textureSize = utils.getMemoryOptimizedFloatTextureSize(this.dimensions, this.bitRatio); this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * this.bitRatio; this.uploadValue = new Float32Array(this.uploadArrayLength); diff --git a/src/backend/web-gl/kernel-value/unsigned-input.js b/src/backend/web-gl/kernel-value/unsigned-input.js index 98b84151..78a0b64a 100644 --- a/src/backend/web-gl/kernel-value/unsigned-input.js +++ b/src/backend/web-gl/kernel-value/unsigned-input.js @@ -6,7 +6,8 @@ class WebGLKernelValueUnsignedInput extends WebGLKernelValue { super(value, settings); this.requestTexture(); this.bitRatio = this.getBitRatio(value); - this.dimensions = value.size; + const [w, h, d] = value.size; + this.dimensions = new Int32Array([w || 1, h || 1, d || 1]); this.textureSize = utils.getMemoryOptimizedPackedTextureSize(this.dimensions, this.bitRatio); this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * (4 / this.bitRatio); this.TranserArrayType = this.getTransferArrayType(value.value); diff --git a/src/backend/web-gl2/kernel-value/dynamic-single-input.js b/src/backend/web-gl2/kernel-value/dynamic-single-input.js index 5826d04d..dccfbd5e 100644 --- a/src/backend/web-gl2/kernel-value/dynamic-single-input.js +++ b/src/backend/web-gl2/kernel-value/dynamic-single-input.js @@ -11,7 +11,8 @@ class WebGL2KernelValueDynamicSingleInput extends WebGL2KernelValueSingleInput { } updateValue(value) { - this.dimensions = value.size; + let [w, h, d] = value.size; + this.dimensions = new Int32Array([w || 1, h || 1, d || 1]); this.textureSize = utils.getMemoryOptimizedFloatTextureSize(this.dimensions, this.bitRatio); this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * this.bitRatio; this.uploadValue = new Float32Array(this.uploadArrayLength); diff --git a/src/gpu.js b/src/gpu.js index f9150a09..f7be83ae 100644 --- a/src/gpu.js +++ b/src/gpu.js @@ -1,4 +1,4 @@ -const gpuMock = require('gpu-mock.js'); +const { gpuMock } = require('gpu-mock.js'); const { utils } = require('./utils'); const { CPUKernel } = require('./backend/cpu/kernel'); const { HeadlessGLKernel } = require('./backend/headless-gl/kernel'); @@ -477,6 +477,7 @@ class GPU { * @desc Destroys all memory associated with gpu.js & the webGl if we created it */ destroy() { + if (!this.kernels) return; // perform on next run loop - for some reason we dont get lose context events // if webGl is created and destroyed in the same run loop. setTimeout(() => { diff --git a/src/input.js b/src/input.js index 4f42d36f..12f0c252 100644 --- a/src/input.js +++ b/src/input.js @@ -1,24 +1,45 @@ class Input { constructor(value, size) { this.value = value; - this.size = new Int32Array(3); if (Array.isArray(size)) { - for (let i = 0; i < this.size.length; i++) { - this.size[i] = size[i] || 1; - } + this.size = size; } else { + this.size = new Int32Array(3); if (size.z) { this.size = new Int32Array([size.x, size.y, size.z]); } else if (size.y) { - this.size = new Int32Array([size.x, size.y, 1]); + this.size = new Int32Array([size.x, size.y]); } else { - this.size = new Int32Array([size.x, 1, 1]); + this.size = new Int32Array([size.x]); + } + } + + const [w, h, d] = this.size; + if (d) { + if (this.value.length !== (w * h * d)) { + throw new Error(`Input size ${this.value.length} does not match ${w} * ${h} * ${d} = ${(h * w * d)}`); + } + } else if (h) { + if (this.value.length !== (w * h)) { + throw new Error(`Input size ${this.value.length} does not match ${w} * ${h} = ${(h * w)}`); + } + } else { + if (this.value.length !== w) { + throw new Error(`Input size ${this.value.length} does not match ${w}`); } } - const [h, w, d] = this.size; - if (this.value.length !== (h * w * d)) { - throw new Error(`Input size ${this.value.length} does not match ${w} * ${h} * ${d} = ${(h * w * d)}`); + } + + toArray() { + const { utils } = require('./utils'); + const [w, h, d] = this.size; + if (d) { + return utils.erectMemoryOptimized3DFloat(this.value.subarray ? this.value : new Float32Array(this.value), w, h, d); + } else if (h) { + return utils.erectMemoryOptimized2DFloat(this.value.subarray ? this.value : new Float32Array(this.value), w, h); + } else { + return this.value; } } } diff --git a/test/all.html b/test/all.html index 55f4c454..c432ec15 100644 --- a/test/all.html +++ b/test/all.html @@ -159,6 +159,7 @@ + diff --git a/test/features/dev-mode.js b/test/features/dev-mode.js new file mode 100644 index 00000000..07480e31 --- /dev/null +++ b/test/features/dev-mode.js @@ -0,0 +1,147 @@ +const { assert, skip, test, module: describe, only } = require('qunit'); +const { GPU, input } = require('../../src'); + +describe('features: dev mode'); + +test('works with integer', () => { + const gpu = new GPU({ mode: 'dev' }); + const kernel = gpu.createKernel(function(value) { + return value; + }, { output: [1] }); + assert.deepEqual(kernel(1), new Float32Array([1])); + gpu.destroy(); +}); + +test('works with float', () => { + const gpu = new GPU({ mode: 'dev' }); + const kernel = gpu.createKernel(function(value) { + return value; + }, { output: [1] }); + assert.deepEqual(kernel(1.5), new Float32Array([1.5])); + gpu.destroy(); +}); + +test('works with array', () => { + const gpu = new GPU({ mode: 'dev' }); + const kernel = gpu.createKernel(function(value) { + return value[this.thread.x]; + }, { output: [4] }); + assert.deepEqual(kernel([1,2,3,4]), new Float32Array([1,2,3,4])); + gpu.destroy(); +}); + +test('works with matrix', () => { + const gpu = new GPU({ mode: 'dev' }); + const kernel = gpu.createKernel(function(value) { + return value[this.thread.y][this.thread.x]; + }, { output: [4, 2] }); + assert.deepEqual(kernel( + [ + [1,2,3,4], + [5,6,7,8] + ] + ), [ + new Float32Array([1,2,3,4]), + new Float32Array([5,6,7,8]), + ]); + gpu.destroy(); +}); + +test('works with cube', () => { + const gpu = new GPU({ mode: 'dev' }); + const kernel = gpu.createKernel(function(value) { + return value[this.thread.z][this.thread.y][this.thread.x]; + }, { output: [4, 2, 2] }); + assert.deepEqual(kernel( + [ + [ + [1,2,3,4], + [5,6,7,8] + ], + [ + [9,10,11,12], + [13,14,15,16] + ] + ] + ), [ + [ + new Float32Array([1,2,3,4]), + new Float32Array([5,6,7,8]), + ],[ + new Float32Array([9,10,11,12]), + new Float32Array([13,14,15,16]), + ] + ]); + gpu.destroy(); +}); + +test('works with input array', () => { + const gpu = new GPU({ mode: 'dev' }); + const kernel = gpu.createKernel(function(value) { + return value[this.thread.x]; + }, { output: [4] }); + assert.deepEqual(kernel(input([1,2,3,4], [4])), new Float32Array([1,2,3,4])); + gpu.destroy(); +}); + +test('works with input matrix', () => { + const gpu = new GPU({ mode: 'dev' }); + const kernel = gpu.createKernel(function(value) { + return value[this.thread.y][this.thread.x]; + }, { output: [4, 2] }); + assert.deepEqual(kernel(input([1,2,3,4,5,6,7,8], [4, 2])), [ + new Float32Array([1,2,3,4]), + new Float32Array([5,6,7,8]), + ]); + gpu.destroy(); +}); + +test('works with input cube', () => { + const gpu = new GPU({ mode: 'dev' }); + const kernel = gpu.createKernel(function(value) { + return value[this.thread.z][this.thread.y][this.thread.x]; + }, { output: [4, 2, 2] }); + assert.deepEqual(kernel( + input([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16], [4,2,2]) + ), [ + [ + new Float32Array([1,2,3,4]), + new Float32Array([5,6,7,8]), + ],[ + new Float32Array([9,10,11,12]), + new Float32Array([13,14,15,16]), + ] + ]); + gpu.destroy(); +}); + +test('works with texture', () => { + const texture = ((new GPU()).createKernel(function (cube) { + return cube[this.thread.z][this.thread.y][this.thread.x]; + }, { output: [4,2,2], pipeline: true }))([ + [ + new Float32Array([1,2,3,4]), + new Float32Array([5,6,7,8]), + ],[ + new Float32Array([9,10,11,12]), + new Float32Array([13,14,15,16]), + ] + ]); + assert.ok(texture.constructor.name.match('Texture')); + const gpu = new GPU({ mode: 'dev' }); + const kernel = gpu.createKernel(function(value) { + return value[this.thread.z][this.thread.y][this.thread.x]; + }, { output: [4, 2, 2] }); + assert.deepEqual(kernel( + texture + ), [ + [ + new Float32Array([1,2,3,4]), + new Float32Array([5,6,7,8]), + ],[ + new Float32Array([9,10,11,12]), + new Float32Array([13,14,15,16]), + ] + ]); + gpu.destroy(); +}); diff --git a/test/features/input.js b/test/features/input.js index 2f6dcd17..150557b6 100644 --- a/test/features/input.js +++ b/test/features/input.js @@ -312,3 +312,25 @@ test("inputInt32ArrayX gpu", () => { test("inputInt32ArrayX cpu", () => { inputInt32ArrayX('cpu'); }); + +test('.toArray() with array', () => { + assert.deepEqual(input([1,2,3,4], [4]).toArray(), [1,2,3,4]); +}); +test('.toArray() with matrix', () => { + assert.deepEqual(input([1,2,3,4,5,6,7,8], [4,2]).toArray(), [new Float32Array([1,2,3,4]), new Float32Array([5,6,7,8])]); +}); +test('.toArray() with grid', () => { + assert.deepEqual( + input([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16], [4,2,2]).toArray(), + [ + [ + new Float32Array([1,2,3,4]), + new Float32Array([5,6,7,8]), + ], + [ + new Float32Array([9,10,11,12]), + new Float32Array([13,14,15,16]) + ] + ] + ); +}); diff --git a/test/internal/math.random.js b/test/internal/math.random.js index 0f83cbe4..b1d11c9a 100644 --- a/test/internal/math.random.js +++ b/test/internal/math.random.js @@ -83,22 +83,22 @@ test('never above 1 every time auto', () => { mathRandomNeverAboveOne(); }); -test('never above 1 every time gpu', () => { +test('never above 1 every time gpu', () => { mathRandomNeverAboveOne('gpu'); }); -(GPU.isWebGLSupported ? test : skip)('never above 1 every time webgl', () => { +(GPU.isWebGLSupported ? test : skip)('never above 1 every time webgl', () => { mathRandomNeverAboveOne('webgl'); }); -(GPU.isWebGL2Supported ? test : skip)('never above 1 every time webgl2', () => { +(GPU.isWebGL2Supported ? test : skip)('never above 1 every time webgl2', () => { mathRandomNeverAboveOne('webgl2'); }); -(GPU.isHeadlessGLSupported ? test : skip)('never above 1 every time headlessgl', () => { +(GPU.isHeadlessGLSupported ? test : skip)('never above 1 every time headlessgl', () => { mathRandomNeverAboveOne('headlessgl'); }); -test('never above 1 every time cpu', () => { +test('never above 1 every time cpu', () => { mathRandomNeverAboveOne('cpu'); });