Skip to content

Commit 27f4efb

Browse files
fix: Proper kernel.toString() unit tests (good grief, so many!) for CPU and GPU
feat: `switch` statements feat: `kernel.toString()` has as well `.getPixels()` fix: Proper fallback when arguments or constants are not supported fix: Removal of infamous `|| 'Number'` for argument types and return types fix: detect circlical logic in `FunctionBuilder.lookupReturnType()` and tests fix: Allow subKernels to get their type detected as well fix: `FunctionNode`'s `typeLookupMap` didn't have 'Float' or 'Integer' feat: Turn off context checking in kernel, via `{ checkContext: false }`, used for `kernel.toString()` fix: `GPU.upgradeDeprecatedCreateKernelSettings` to have a default return value fix: Typings fix: `kernelRunShortcut()` to better switch kernel when replacing feat: It order to flatten methods, for `kernel.toString()` `utils.flattenFunctionToString()` and light unit testing of it fix: Added sinon for testing fix: Bump gl-wiretap
1 parent 73ef03c commit 27f4efb

File tree

113 files changed

+10215
-2014
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

113 files changed

+10215
-2014
lines changed

bin/gpu-browser-core.js

Lines changed: 1137 additions & 386 deletions
Large diffs are not rendered by default.

bin/gpu-browser-core.min.js

Lines changed: 1138 additions & 387 deletions
Large diffs are not rendered by default.

bin/gpu-browser.js

Lines changed: 1137 additions & 386 deletions
Large diffs are not rendered by default.

bin/gpu-browser.min.js

Lines changed: 1138 additions & 387 deletions
Large diffs are not rendered by default.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
"dependencies": {
1717
"acorn": "^5.1.1",
1818
"gl": "^4.3.3",
19-
"gl-wiretap": "^0.3.0",
19+
"gl-wiretap": "^0.6.0",
2020
"gpu-mock.js": "^1.0.1"
2121
},
2222
"devDependencies": {
@@ -31,6 +31,7 @@
3131
"gulp-strip-comments": "^2.4.5",
3232
"merge-stream": "^1.0.1",
3333
"qunit": "^2.9.1",
34+
"sinon": "^7.3.2",
3435
"vinyl-buffer": "^1.0.0",
3536
"vinyl-source-stream": "^2.0.0"
3637
},

src/backend/cpu/function-node.js

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,31 @@ class CPUFunctionNode extends FunctionNode {
373373

374374
}
375375

376+
astSwitchStatement(ast, retArr) {
377+
const { discriminant, cases } = ast;
378+
retArr.push('switch (');
379+
this.astGeneric(discriminant, retArr);
380+
retArr.push(') {\n');
381+
for (let i = 0; i < cases.length; i++) {
382+
if (cases[i].test === null) {
383+
retArr.push('default:\n');
384+
this.astGeneric(cases[i].consequent, retArr);
385+
if (cases[i].consequent && cases[i].consequent.length > 0) {
386+
retArr.push('break;\n');
387+
}
388+
continue;
389+
}
390+
retArr.push('case ');
391+
this.astGeneric(cases[i].test, retArr);
392+
retArr.push(':\n');
393+
if (cases[i].consequent && cases[i].consequent.length > 0) {
394+
this.astGeneric(cases[i].consequent, retArr);
395+
retArr.push('break;\n');
396+
}
397+
}
398+
retArr.push('\n}');
399+
}
400+
376401
/**
377402
* @desc Parses the abstract syntax tree for *This* expression
378403
* @param {Object} tNode - An ast Node
@@ -486,11 +511,19 @@ class CPUFunctionNode extends FunctionNode {
486511
case 'ArrayTexture(4)':
487512
case 'HTMLImage':
488513
default:
489-
const isInput = this.isInput(synonymName || name);
514+
let size;
515+
let isInput;
516+
if (origin === 'constants') {
517+
const constant = this.constants[name];
518+
isInput = this.constantTypes[name] === 'Input';
519+
size = isInput ? constant.size : null;
520+
} else {
521+
isInput = this.isInput(synonymName || name);
522+
size = isInput ? this.argumentSizes[this.argumentNames.indexOf(name)] : null;
523+
}
490524
retArr.push(`${ markupName }`);
491525
if (zProperty && yProperty) {
492526
if (isInput) {
493-
const size = this.argumentSizes[this.argumentNames.indexOf(name)];
494527
retArr.push('[(');
495528
this.astGeneric(zProperty, retArr);
496529
retArr.push(`*${ size[1] * size[0]})+(`);
@@ -511,7 +544,6 @@ class CPUFunctionNode extends FunctionNode {
511544
}
512545
} else if (yProperty) {
513546
if (isInput) {
514-
const size = this.argumentSizes[this.argumentNames.indexOf(name)];
515547
retArr.push('[(');
516548
this.astGeneric(yProperty, retArr);
517549
retArr.push(`*${ size[0] })+`);

src/backend/cpu/kernel-string.js

Lines changed: 136 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,146 @@
11
const { utils } = require('../../utils');
2-
const { kernelRunShortcut } = require('../../kernel-run-shortcut');
2+
const { Input } = require('../../input');
33

4-
function removeFnNoise(fn) {
5-
if (/^function /.test(fn)) {
6-
fn = fn.substring(9);
4+
function constantsToString(constants) {
5+
const results = [];
6+
for (const p in constants) {
7+
const constant = constants[p];
8+
switch (typeof constant) {
9+
case 'number':
10+
case 'boolean':
11+
results.push(`${p}:${constant}`);
12+
}
713
}
8-
return fn.replace(/[_]typeof/g, 'typeof');
9-
}
10-
11-
function removeNoise(str) {
12-
return str
13-
.replace(/^[A-Za-z]+/, 'function')
14-
.replace(/[_]typeof/g, 'typeof');
14+
return `{ ${ results.join() } }`;
1515
}
1616

1717
function cpuKernelString(cpuKernel, name) {
18-
return `() => {
19-
${ kernelRunShortcut.toString() };
20-
const utils = {
21-
allPropertiesOf: ${ removeNoise(utils.allPropertiesOf.toString()) },
22-
clone: ${ removeNoise(utils.clone.toString()) },
23-
isArray: ${ removeNoise(utils.isArray.toString()) },
24-
};
25-
let Input = function() {};
26-
class ${ name || 'Kernel' } {
27-
constructor() {
28-
this.canvas = null;
29-
this.context = null;
30-
this.built = false;
31-
this.program = null;
32-
this.argumentNames = ${ JSON.stringify(cpuKernel.argumentNames) };
33-
this.argumentTypes = ${ JSON.stringify(cpuKernel.argumentTypes) };
34-
this.argumentSizes = ${ JSON.stringify(cpuKernel.argumentSizes) };
35-
this.output = ${ JSON.stringify(cpuKernel.output) };
36-
this._kernelString = \`${ cpuKernel._kernelString }\`;
37-
this.output = ${ JSON.stringify(cpuKernel.output) };
38-
this.run = function() {
39-
this.run = null;
40-
this.build(arguments);
41-
return this.run.apply(this, arguments);
42-
}.bind(this);
43-
this.thread = {
44-
x: 0,
45-
y: 0,
46-
z: 0
47-
};
18+
const header = [];
19+
const thisProperties = [];
20+
const beforeReturn = [];
21+
22+
const useFunctionKeyword = !/^function/.test(cpuKernel.color.toString());
23+
24+
header.push(
25+
' const { context, canvas, constants } = settings;',
26+
` const output = new Int32Array(${JSON.stringify(Array.from(cpuKernel.output))});`,
27+
` const _constants = ${constantsToString(cpuKernel.constants)};`,
28+
);
29+
30+
thisProperties.push(
31+
' constants: _constants,',
32+
' context,',
33+
' output,',
34+
' thread: {x: 0, y: 0, z: 0},',
35+
);
36+
37+
if (cpuKernel.graphical) {
38+
header.push(` const _imageData = context.createImageData(${cpuKernel.output[0]}, ${cpuKernel.output[1]});`);
39+
header.push(` const _colorData = new Uint8ClampedArray(${cpuKernel.output[0]} * ${cpuKernel.output[1]} * 4);`);
40+
41+
const colorFn = utils.flattenFunctionToString((useFunctionKeyword ? 'function ' : '') + cpuKernel.color.toString(), {
42+
thisLookup: (propertyName) => {
43+
switch (propertyName) {
44+
case '_colorData':
45+
return '_colorData';
46+
case '_imageData':
47+
return '_imageData';
48+
case 'output':
49+
return 'output';
50+
case 'thread':
51+
return 'this.thread';
52+
}
53+
return JSON.stringify(cpuKernel[propertyName]);
54+
},
55+
findDependency: (object, name) => {
56+
return null;
4857
}
49-
setCanvas(canvas) { this.canvas = canvas; return this; }
50-
setContext(context) { this.context = context; return this; }
51-
setInput(Type) { Input = Type; }
52-
${ removeFnNoise(cpuKernel.build.toString()) }
53-
setupArguments() {}
54-
${ removeFnNoise(cpuKernel.setupConstants.toString()) }
55-
translateSource() {}
56-
pickRenderStrategy() {}
57-
run () { ${ cpuKernel.kernelString } }
58-
getKernelString() { return this._kernelString; }
59-
${ removeFnNoise(cpuKernel.validateSettings.toString()) }
60-
${ removeFnNoise(cpuKernel.checkOutput.toString()) }
61-
};
62-
return kernelRunShortcut(new Kernel());
63-
};`;
58+
});
59+
60+
const getPixelsFn = utils.flattenFunctionToString((useFunctionKeyword ? 'function ' : '') + cpuKernel.getPixels.toString(), {
61+
thisLookup: (propertyName) => {
62+
switch (propertyName) {
63+
case '_colorData':
64+
return '_colorData';
65+
case '_imageData':
66+
return '_imageData';
67+
case 'output':
68+
return 'output';
69+
case 'thread':
70+
return 'this.thread';
71+
}
72+
return JSON.stringify(cpuKernel[propertyName]);
73+
},
74+
findDependency: () => {
75+
return null;
76+
}
77+
});
78+
79+
thisProperties.push(
80+
' _imageData,',
81+
' _colorData,',
82+
` color: ${colorFn},`,
83+
);
84+
85+
beforeReturn.push(
86+
` kernel.getPixels = ${getPixelsFn};`
87+
);
88+
}
89+
90+
const constantTypes = [];
91+
const constantKeys = Object.keys(cpuKernel.constantTypes);
92+
for (let i = 0; i < constantKeys.length; i++) {
93+
constantTypes.push(cpuKernel.constantTypes[constantKeys]);
94+
}
95+
if (cpuKernel.argumentTypes.indexOf('HTMLImageArray') !== -1 || constantTypes.indexOf('HTMLImageArray') !== -1) {
96+
const flattenedImageTo3DArray = utils.flattenFunctionToString((useFunctionKeyword ? 'function ' : '') + cpuKernel._imageTo3DArray.toString(), {
97+
doNotDefine: ['canvas'],
98+
findDependency: (object, name) => {
99+
if (object === 'this') {
100+
return (useFunctionKeyword ? 'function ' : '') + cpuKernel[name].toString();
101+
}
102+
return null;
103+
},
104+
thisLookup: (propertyName) => {
105+
switch (propertyName) {
106+
case 'canvas':
107+
return;
108+
case 'context':
109+
return 'context';
110+
}
111+
}
112+
})
113+
beforeReturn.push(flattenedImageTo3DArray);
114+
thisProperties.push(` _imageTo2DArray,`);
115+
thisProperties.push(` _imageTo3DArray,`);
116+
} else if (cpuKernel.argumentTypes.indexOf('HTMLImage') !== -1 || constantTypes.indexOf('HTMLImage') !== -1) {
117+
const flattenedImageTo2DArray = utils.flattenFunctionToString((useFunctionKeyword ? 'function ' : '') + cpuKernel._imageTo2DArray.toString(), {
118+
findDependency: () => {
119+
debugger;
120+
}
121+
});
122+
beforeReturn.push(flattenedImageTo2DArray);
123+
thisProperties.push(` _imageTo2DArray,`);
124+
}
125+
126+
return `function(settings) {
127+
${ header.join('\n') }
128+
for (const p in constants) {
129+
const constant = constants[p];
130+
switch (typeof constant) {
131+
case 'number':
132+
case 'boolean':
133+
continue;
134+
}
135+
_constants[p] = constant;
136+
}
137+
const kernel = (function() {
138+
${cpuKernel._kernelString}
139+
})
140+
.apply({ ${thisProperties.join('\n')} });
141+
${ beforeReturn.join('\n') }
142+
return kernel;
143+
}`;
64144
}
65145

66146
module.exports = {

src/backend/function-builder.js

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ class FunctionBuilder {
7575

7676
const onNestedFunction = (fnString, returnType) => {
7777
functionBuilder.addFunctionNode(new FunctionNode(fnString, Object.assign({}, nodeOptions, {
78-
returnType: returnType || 'Number', // TODO: I think this needs removed
78+
returnType: returnType,
7979
lookupReturnType,
8080
lookupArgumentType,
8181
lookupFunctionArgumentTypes,
@@ -161,7 +161,6 @@ class FunctionBuilder {
161161
name,
162162
isSubKernel: true,
163163
isRootKernel: false,
164-
returnType: 'Number', // TODO: I think this needs removed
165164
}));
166165
});
167166
}
@@ -378,25 +377,36 @@ class FunctionBuilder {
378377

379378
lookupArgumentType(argumentName, requestingNode) {
380379
const index = requestingNode.argumentNames.indexOf(argumentName);
381-
if (index === -1) return null;
382-
if (this.lookupChain.length === 0) return null;
380+
if (index === -1) {
381+
return null;
382+
}
383+
if (this.lookupChain.length === 0) {
384+
return null;
385+
}
383386
let link = this.lookupChain[this.lookupChain.length - 1 - this.argumentChain.length];
384-
if (!link) return null;
387+
if (!link) {
388+
return null;
389+
}
385390
const {
386391
ast,
387392
requestingNode: parentRequestingNode
388393
} = link;
389-
if (ast.arguments.length === 0) return null;
390-
const usedVariable = ast.arguments[index];
391-
if (!usedVariable) return null;
394+
if (ast.arguments.length === 0) {
395+
return null;
396+
}
397+
const usedArgument = ast.arguments[index];
398+
if (!usedArgument) {
399+
return null;
400+
}
401+
392402
this.argumentChain.push(argumentName);
393-
const type = parentRequestingNode.getType(usedVariable);
403+
404+
const type = parentRequestingNode.getType(usedArgument);
394405
this.argumentChain.pop();
395406
return type;
396407
}
397408

398409
lookupReturnType(functionName, ast, requestingNode) {
399-
// TODO: track circlical logic
400410
if (ast.type !== 'CallExpression') {
401411
throw new Error(`expected ast type of "CallExpression", but is ${ ast.type }`);
402412
}
@@ -407,6 +417,22 @@ class FunctionBuilder {
407417
if (node.returnType) {
408418
return node.returnType;
409419
} else {
420+
for (let i = 0; i < this.lookupChain.length; i++) {
421+
// detect circlical logic
422+
if (this.lookupChain[i].ast === ast) {
423+
// detect if arguments have not resolved, preventing a return type
424+
// if so, go ahead and resolve them, so we can resolve the return type
425+
if (node.argumentTypes.length === 0 && ast.arguments.length > 0) {
426+
const args = ast.arguments;
427+
for (let j = 0; j < args.length; j++) {
428+
node.argumentTypes[j] = requestingNode.getType(args[j]);
429+
}
430+
return node.returnType = node.getType(node.getJsAST());
431+
}
432+
433+
throw new Error('circlical logic detected!');
434+
}
435+
}
410436
// get ready for a ride!
411437
this.lookupChain.push({
412438
name: requestingNode.name,
@@ -533,7 +559,12 @@ class FunctionBuilder {
533559
}
534560

535561
getKernelResultType() {
536-
return this.rootNode.getType(this.rootNode.ast);
562+
return this.rootNode.returnType || this.rootNode.getType(this.rootNode.ast);
563+
}
564+
565+
getSubKernelResultType(index) {
566+
const subKernelNode = this.subKernelNodes[index];
567+
return subKernelNode.returnType || subKernelNode.getType(subKernelNode.getJsAST());
537568
}
538569

539570
getReturnTypes() {

0 commit comments

Comments
 (0)