Skip to content

Commit abdb7a6

Browse files
feat: Finally add the missing json with gpu-core feature!
And add unit tests
1 parent d57f49f commit abdb7a6

File tree

10 files changed

+167
-25
lines changed

10 files changed

+167
-25
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ yarn add gpu.js
8282
Download the latest version of GPU.js and include the files in your HTML page using the following tags:
8383

8484
```html
85-
<script src="/path/to/js/gpu.min.js"></script>
85+
<script src="/path/to/js/gpu-browser.min.js"></script>
8686
```
8787

8888
In JavaScript, initialize the library:

src/backend/cpu/kernel.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,12 @@ class CPUKernel extends Kernel {
407407
}
408408

409409
static destroyContext(context) {}
410+
411+
toJSON() {
412+
const json = super.toJSON();
413+
json.functionNodes = FunctionBuilder.fromKernel(this, CPUFunctionNode).toJSON();
414+
return json;
415+
}
410416
}
411417

412418
module.exports = {

src/backend/function-builder.js

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,19 @@ class FunctionBuilder {
4242
output,
4343
}, extraNodeOptions || {});
4444

45-
const rootNode = new FunctionNode(kernel.source, Object.assign({}, nodeOptions, {
45+
const rootNodeOptions = Object.assign({}, nodeOptions, {
4646
isRootKernel: true,
4747
name: 'kernel',
4848
argumentNames,
4949
argumentTypes,
5050
argumentSizes,
51-
}));
51+
});
52+
53+
if (typeof kernel.source === 'object' && kernel.source.functionNodes) {
54+
return new FunctionBuilder().fromJSON(kernel.source.functionNodes, FunctionNode);
55+
}
56+
57+
const rootNode = new FunctionNode(kernel.source, rootNodeOptions);
5258

5359
let functionNodes = null;
5460
if (kernel.functions) {
@@ -200,7 +206,9 @@ class FunctionBuilder {
200206
* @returns {Array} The full string, of all the various functions. Trace optimized if functionName given
201207
*/
202208
getPrototypes(functionName) {
203-
this.rootNode.toString();
209+
if (this.rootNode) {
210+
this.rootNode.toString();
211+
}
204212
if (functionName) {
205213
return this.getPrototypesFromFunctionNames(this.traceFunctionCalls(functionName, []).reverse());
206214
}
@@ -245,6 +253,30 @@ class FunctionBuilder {
245253
return ret;
246254
}
247255

256+
toJSON() {
257+
return this.traceFunctionCalls(this.rootNode.name).reverse().map(name => {
258+
if (this.nativeFunctions[name]) {
259+
return {
260+
name,
261+
source: this.nativeFunctions[name]
262+
};
263+
} else if (this.functionMap[name]) {
264+
return this.functionMap[name].toJSON();
265+
} else {
266+
throw new Error(`function ${ name } not found`);
267+
}
268+
});
269+
}
270+
271+
fromJSON(jsonFunctionNodes, FunctionNode) {
272+
this.functionMap = {};
273+
for (let i = 0; i < jsonFunctionNodes.length; i++) {
274+
const jsonFunctionNode = jsonFunctionNodes[i];
275+
this.functionMap[jsonFunctionNode.settings.name] = new FunctionNode(jsonFunctionNode.ast, jsonFunctionNode.settings);
276+
}
277+
return this;
278+
}
279+
248280
/**
249281
* @desc Get string for a particular function name
250282
* @param {String} functionName - Function name to trace from. If null, it returns the WHOLE builder stack

src/backend/function-node.js

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ class FunctionNode {
2121
settings = settings || {};
2222

2323
this.source = source;
24-
this.name = settings.isRootKernel ?
24+
this.name = typeof source === 'string' ? settings.isRootKernel ?
2525
'kernel' :
26-
(settings.name || utils.getFunctionNameFromString(source));
26+
(settings.name || utils.getFunctionNameFromString(source)) : null;
2727
this.calledFunctions = [];
2828
this.calledFunctionsArguments = {};
2929
this.constants = {};
@@ -33,14 +33,12 @@ class FunctionNode {
3333
this.parent = null;
3434
this.debug = null;
3535
this.prototypeOnly = null;
36-
this.constants = null;
37-
this.output = null;
3836
this.declarations = {};
3937
this.states = [];
4038
this.lookupReturnType = null;
4139
this.onNestedFunction = null;
4240
this.loopMaxIterations = null;
43-
this.argumentNames = utils.getArgumentNamesFromString(this.source);
41+
this.argumentNames = (typeof this.source === 'string' ? utils.getArgumentNamesFromString(this.source) : null);
4442
this.argumentTypes = {};
4543
this.argumentSizes = [];
4644
this.returnType = null;
@@ -175,8 +173,8 @@ class FunctionNode {
175173
* @returns {Object} The function AST Object, note that result is cached under this.ast;
176174
*/
177175
getJsAST(inParser) {
178-
if (this.ast) {
179-
return this.ast;
176+
if (typeof this.source === 'object') {
177+
return this.ast = this.source;
180178
}
181179

182180
inParser = inParser || acorn;
@@ -261,6 +259,30 @@ class FunctionNode {
261259
throw new Error(`"toString" not defined on ${ this.constructor.name }`);
262260
}
263261

262+
toJSON() {
263+
const settings = {
264+
source: this.source,
265+
name: this.name,
266+
constants: this.constants,
267+
constantTypes: this.constantTypes,
268+
isRootKernel: this.isRootKernel,
269+
isSubKernel: this.isSubKernel,
270+
debug: this.debug,
271+
prototypeOnly: this.prototypeOnly,
272+
output: this.output,
273+
loopMaxIterations: this.loopMaxIterations,
274+
argumentNames: this.argumentNames,
275+
argumentTypes: this.argumentTypes,
276+
argumentSizes: this.argumentSizes,
277+
returnType: this.returnType
278+
};
279+
280+
return {
281+
ast: this.ast,
282+
settings
283+
};
284+
}
285+
264286
/**
265287
* @desc Parses the abstract syntax tree for generically to its respective function
266288
* @param {Object} ast - the AST object to parse

src/backend/kernel.js

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,22 +24,26 @@ class Kernel {
2424

2525
/**
2626
*
27-
* @param {string} source
27+
* @param {string|object} source
2828
* @param [settings]
2929
*/
3030
constructor(source, settings) {
31-
if (typeof source !== 'string') {
32-
throw new Error('source not a string');
33-
}
34-
if (!utils.isFunctionString(source)) {
35-
throw new Error('source not a function string');
31+
if (typeof source === 'object') {
32+
settings = source.settings;
33+
} else {
34+
if (typeof source !== 'string') {
35+
throw new Error('source not a string');
36+
}
37+
if (!utils.isFunctionString(source)) {
38+
throw new Error('source not a function string');
39+
}
3640
}
3741

3842
/**
3943
* Name of the arguments found from parsing source argument
4044
* @type {String[]}
4145
*/
42-
this.argumentNames = utils.getArgumentNamesFromString(source);
46+
this.argumentNames = typeof source === 'string' ? utils.getArgumentNamesFromString(source) : null;
4347
this.argumentTypes = null;
4448
this.argumentSizes = null;
4549

@@ -193,6 +197,10 @@ class Kernel {
193197
this.argumentTypes.push(utils.getVariableType(arg));
194198
this.argumentSizes.push(arg.constructor === Input ? arg.size : null);
195199
}
200+
201+
if (this.argumentNames.length !== args.length) {
202+
throw new Error(`arguments are miss-aligned`);
203+
}
196204
}
197205

198206
/**
@@ -403,8 +411,25 @@ class Kernel {
403411
}
404412
}
405413
}
414+
415+
toJSON() {
416+
const settings = {
417+
output: this.output,
418+
threadDim: this.threadDim,
419+
outputToTexture: this.outputToTexture,
420+
argumentNames: this.argumentNames,
421+
argumentsTypes: this.argumentTypes,
422+
argumentsLength: this.argumentsLength,
423+
constants: this.constants,
424+
constantsLength: this.constantsLength,
425+
maxTexSize: this.maxTexSize,
426+
};
427+
return {
428+
settings
429+
};
430+
}
406431
}
407432

408433
module.exports = {
409434
Kernel
410-
};
435+
};

src/backend/web-gl/kernel.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1255,7 +1255,7 @@ class WebGLKernel extends GLKernel {
12551255
} else if (type === 'Integer' || type === 'Float') {
12561256
result.push(`uniform float user_${name}`);
12571257
} else {
1258-
throw new Error(`Param type ${name} not supported in WebGL, only WebGL2`);
1258+
throw new Error(`Param type ${type} not supported in WebGL`);
12591259
}
12601260
}
12611261
}
@@ -1493,6 +1493,12 @@ class WebGLKernel extends GLKernel {
14931493
extension.loseContext();
14941494
}
14951495
}
1496+
1497+
toJSON() {
1498+
const json = super.toJSON();
1499+
json.functionNodes = FunctionBuilder.fromKernel(this, WebGLFunctionNode).toJSON();
1500+
return json;
1501+
}
14961502
}
14971503

14981504
module.exports = {

src/backend/web-gl2/kernel.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -945,6 +945,12 @@ class WebGL2Kernel extends WebGLKernel {
945945
this.extensions.EXT_color_buffer_float = null;
946946
this.extensions.OES_texture_float_linear = null;
947947
}
948+
949+
toJSON() {
950+
const json = super.toJSON();
951+
json.functionNodes = FunctionBuilder.fromKernel(this, WebGL2FunctionNode).toJSON();
952+
return json;
953+
}
948954
}
949955

950956
module.exports = {

src/gpu.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ class GPU {
167167
*
168168
* @desc This creates a callable function object to call the kernel function with the argument parameter set
169169
*
170-
* @param {Function|String} source - The calling to perform the conversion
170+
* @param {Function|String|object} source - The calling to perform the conversion
171171
* @param {Object} [settings] - The parameter configuration object
172172
* @property {String} settings.dimensions - Thread dimension array (Defaults to [1024])
173173
* @property {String} settings.mode - CPU / GPU configuration mode (Defaults to null)
@@ -181,13 +181,13 @@ class GPU {
181181
*/
182182
createKernel(source, settings) {
183183
if (typeof source === 'undefined') {
184-
throw 'Missing source parameter';
184+
throw new Error('Missing source parameter');
185185
}
186-
if (!utils.isFunction(source) && typeof source !== 'string') {
187-
throw 'source parameter not a function';
186+
if (typeof source !== 'object' && !utils.isFunction(source) && typeof source !== 'string') {
187+
throw new Error('source parameter not a function');
188188
}
189189

190-
source = typeof source !== 'string' ? source.toString() : source;
190+
source = typeof source === 'function' ? source.toString() : source;
191191
const mergedSettings = Object.assign({
192192
context: this.context,
193193
canvas: this.canvas,

test/all.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
<script type="module" src="features/image-array.js"></script>
5050
<script type="module" src="features/infinity.js"></script>
5151
<script type="module" src="features/input.js"></script>
52+
<script type="module" src="features/json.js"></script>
5253
<script type="module" src="features/loops.js"></script>
5354
<script type="module" src="features/math-object.js"></script>
5455
<script type="module" src="features/nested-function.js"></script>

test/features/json.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
const { assert, skip, test, module: describe } = require('qunit');
2+
const { GPU } = require('../../src');
3+
4+
describe('json serialize');
5+
6+
function testJSONSerialize(mode) {
7+
const gpu = new GPU({mode});
8+
9+
const kernel = gpu.createKernel(function (value) {
10+
return value;
11+
}, {output: [1]});
12+
13+
kernel(1);
14+
15+
const json = kernel.toJSON();
16+
17+
const jsonKernel = gpu.createKernel(json);
18+
19+
assert.equal(jsonKernel(3)[0], 3);
20+
}
21+
22+
test('auto', () => {
23+
testJSONSerialize();
24+
});
25+
26+
test('gpu', () => {
27+
testJSONSerialize('gpu');
28+
});
29+
30+
(GPU.isWebGLSupported ? test : skip)('webgl', () => {
31+
testJSONSerialize('webgl');
32+
});
33+
34+
(GPU.isWebGL2Supported ? test : skip)('webgl2', () => {
35+
testJSONSerialize('webgl2');
36+
});
37+
38+
(GPU.isHeadlessGLSupported ? test : skip)('headlessgl', () => {
39+
testJSONSerialize('headlessgl');
40+
});
41+
42+
test('cpu', () => {
43+
testJSONSerialize('cpu');
44+
});

0 commit comments

Comments
 (0)