Skip to content

Commit 6050e6a

Browse files
fix: When literals are returned from kernel, freeze their type
feat: Parse GLSL and get return type and argument types feat: Properly look up native function return types and cast if needed feat: Properly look up native function argument types and cast if needed fix: Move this.chooseKernel() to beginning of GPU instantiation As well as test all the above
1 parent 5f59354 commit 6050e6a

File tree

17 files changed

+1120
-114
lines changed

17 files changed

+1120
-114
lines changed

src/backend/cpu/kernel.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,14 @@ class CPUKernel extends Kernel {
4141
return 'cpu';
4242
}
4343

44+
static nativeFunctionArgumentTypes() {
45+
return null;
46+
}
47+
48+
static nativeFunctionReturnType() {
49+
return null;
50+
}
51+
4452
constructor(source, settings) {
4553
super(source, settings);
4654

src/backend/function-builder.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,33 @@ class FunctionBuilder {
3333
})));
3434
};
3535

36+
const parsedReturnTypes = {};
3637
const lookupReturnType = (functionName) => {
38+
if (parsedReturnTypes[functionName]) return parsedReturnTypes[functionName];
39+
const source = functionBuilder.nativeFunctions[functionName];
40+
if (source) {
41+
return parsedReturnTypes[functionName] = kernel.constructor.nativeFunctionReturnType(source);
42+
}
3743
return functionBuilder.lookupReturnType(functionName);
3844
};
3945

46+
const nativeFunctionReturnTypes = {};
47+
const nativeFunctionArgumentTypes = {};
48+
49+
if (kernel.nativeFunctions) {
50+
for (let i = 0; i < kernel.nativeFunctions.length; i++) {
51+
const nativeFunction = kernel.nativeFunctions[i];
52+
nativeFunctionReturnTypes[nativeFunction.name] = nativeFunction.returnType;
53+
nativeFunctionArgumentTypes[nativeFunction.name] = nativeFunction.argumentTypes;
54+
}
55+
}
56+
4057
const nodeOptions = Object.assign({
4158
isRootKernel: false,
4259
onNestedFunction,
4360
lookupReturnType,
61+
nativeFunctionReturnTypes,
62+
nativeFunctionArgumentTypes,
4463
constants,
4564
constantTypes,
4665
debug,

src/backend/function-node.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ class FunctionNode {
3535
this.declarations = {};
3636
this.states = [];
3737
this.lookupReturnType = null;
38+
this.nativeFunctionReturnTypes = null;
39+
this.nativeFunctionArgumentTypes = null;
3840
this.onNestedFunction = null;
3941
this.loopMaxIterations = null;
4042
this.argumentNames = (typeof this.source === 'string' ? utils.getArgumentNamesFromString(this.source) : null);
@@ -43,6 +45,7 @@ class FunctionNode {
4345
this.returnType = null;
4446
this.output = [];
4547
this.plugins = null;
48+
this.literalTypes = {};
4649

4750
if (settings) {
4851
for (const p in settings) {
@@ -302,6 +305,10 @@ class FunctionNode {
302305
case 'ArrayExpression':
303306
return `Array(${ ast.elements.length })`;
304307
case 'Literal':
308+
const literalKey = `${ast.start},${ast.end}`;
309+
if (this.literalTypes[literalKey]) {
310+
return this.literalTypes[literalKey];
311+
}
305312
if (Number.isInteger(ast.value)) {
306313
return 'LiteralInteger';
307314
} else {
@@ -311,6 +318,10 @@ class FunctionNode {
311318
if (this.isAstMathFunction(ast)) {
312319
return 'Number';
313320
}
321+
if (!ast.callee || !ast.callee.name) return null;
322+
if (this.nativeFunctionReturnTypes && this.nativeFunctionReturnTypes[ast.callee.name]) {
323+
return this.nativeFunctionReturnTypes[ast.callee.name];
324+
}
314325
return ast.callee && ast.callee.name && this.lookupReturnType ? this.lookupReturnType(ast.callee.name) : null;
315326
case 'BinaryExpression':
316327
// modulos is Number
@@ -753,6 +764,7 @@ class FunctionNode {
753764
return retArr;
754765
}
755766
astLiteral(ast, retArr) {
767+
this.literalTypes[`${ast.start},${ast.end}`] = 'Number';
756768
return retArr;
757769
}
758770
astBinaryExpression(ast, retArr) {

src/backend/gl-kernel.js

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,139 @@ class GLKernel extends Kernel {
113113
this.floatOutputForce = null;
114114
this.fixIntegerDivisionAccuracy = null;
115115
}
116+
117+
/**
118+
* A highly readable very forgiving micro-parser for a glsl function that gets argument types
119+
* @param {String} source
120+
* @returns {{types: String[], names: String[]}}
121+
*/
122+
static nativeFunctionArgumentTypes(source) {
123+
const types = [];
124+
const names = [];
125+
const states = [];
126+
const isStartingVariableName = /^[a-zA-Z_]/;
127+
const isVariableChar = /[a-zA-Z_0-9]/;
128+
let i = 0;
129+
let name = null;
130+
let type = null;
131+
while (i < source.length) {
132+
const char = source[i];
133+
const nextChar = source[i + 1];
134+
const state = states.length > 0 ? states[states.length - 1] : null;
135+
136+
// begin MULTI_LINE_COMMENT handling
137+
if (state === 'FUNCTION_ARGUMENTS' && char === '/' && nextChar === '*') {
138+
states.push('MULTI_LINE_COMMENT');
139+
i += 2;
140+
continue;
141+
} else if (state === 'MULTI_LINE_COMMENT' && char === '*' && nextChar === '/') {
142+
states.pop();
143+
i += 2;
144+
continue;
145+
}
146+
// end MULTI_LINE_COMMENT handling
147+
148+
// begin COMMENT handling
149+
else if (state === 'FUNCTION_ARGUMENTS' && char === '/' && nextChar === '/') {
150+
states.push('COMMENT');
151+
i += 2;
152+
continue;
153+
} else if (state === 'COMMENT' && char === '\n') {
154+
states.pop();
155+
i++;
156+
continue;
157+
}
158+
// end COMMENT handling
159+
160+
// being FUNCTION_ARGUMENTS handling
161+
else if (state === null && char === '(') {
162+
states.push('FUNCTION_ARGUMENTS');
163+
i++;
164+
continue;
165+
} else if (state === 'FUNCTION_ARGUMENTS') {
166+
if (char === ')') {
167+
states.pop();
168+
break;
169+
}
170+
if (char === 'f' && nextChar === 'l' && source[i + 2] === 'o' && source[i + 3] === 'a' && source[i + 4] === 't' && source[i + 5] === ' ') {
171+
states.push('DECLARE_VARIABLE');
172+
type = 'float';
173+
name = '';
174+
i += 6;
175+
continue;
176+
} else if (char === 'i' && nextChar === 'n' && source[i + 2] === 't' && source[i + 3] === ' ') {
177+
states.push('DECLARE_VARIABLE');
178+
type = 'int';
179+
name = '';
180+
i += 4;
181+
continue;
182+
} else if (char === 'v' && nextChar === 'e' && source[i + 2] === 'c' && source[i + 3] === '2' && source[i + 4] === ' ') {
183+
states.push('DECLARE_VARIABLE');
184+
type = 'vec2';
185+
name = '';
186+
i += 5;
187+
continue;
188+
} else if (char === 'v' && nextChar === 'e' && source[i + 2] === 'c' && source[i + 3] === '3' && source[i + 4] === ' ') {
189+
states.push('DECLARE_VARIABLE');
190+
type = 'vec3';
191+
name = '';
192+
i += 5;
193+
continue;
194+
} else if (char === 'v' && nextChar === 'e' && source[i + 2] === 'c' && source[i + 3] === '4' && source[i + 4] === ' ') {
195+
states.push('DECLARE_VARIABLE');
196+
type = 'vec4';
197+
name = '';
198+
i += 5;
199+
continue;
200+
}
201+
}
202+
// end FUNCTION_ARGUMENTS handling
203+
204+
// begin DECLARE_VARIABLE handling
205+
else if (state === 'DECLARE_VARIABLE') {
206+
if (name === '') {
207+
if (char === ' ') {
208+
i++;
209+
continue;
210+
}
211+
if (!isStartingVariableName.test(char)) {
212+
throw new Error('variable name is not expected string');
213+
}
214+
}
215+
name += char;
216+
if (!isVariableChar.test(nextChar)) {
217+
states.pop();
218+
names.push(name);
219+
types.push(typeMap[type]);
220+
}
221+
}
222+
// end DECLARE_VARIABLE handling
223+
224+
// Progress to next character
225+
i++;
226+
}
227+
if (states.length > 0) {
228+
throw new Error('GLSL function was not parsable');
229+
}
230+
return {
231+
names,
232+
types
233+
};
234+
}
235+
236+
static nativeFunctionReturnType(source) {
237+
return typeMap[source.match(/int|float|vec[2-4]/)[0]];
238+
}
116239
}
117240

241+
const typeMap = {
242+
int: 'Integer',
243+
float: 'Number',
244+
vec2: 'Array(2)',
245+
vec3: 'Array(3)',
246+
vec4: 'Array(4)',
247+
};
248+
118249
module.exports = {
119250
GLKernel
120251
};

src/backend/headless-gl/kernel.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// const exokit = require('exokit');
12
const getContext = require('gl');
23
const {
34
WebGLKernel

src/backend/kernel.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,14 @@ class Kernel {
2828
throw new Error(`"destroyContext" called on ${ this.name }`);
2929
}
3030

31+
static nativeFunctionArgumentTypes() {
32+
throw new Error(`"nativeFunctionArgumentTypes" called on ${ this.name }`);
33+
}
34+
35+
static nativeFunctionReturnType() {
36+
throw new Error(`"nativeFunctionReturnType" called on ${ this.name }`);
37+
}
38+
3139
/**
3240
*
3341
* @param {string|object} source
@@ -109,7 +117,7 @@ class Kernel {
109117

110118
/**
111119
*
112-
* @type {INativeFunctionList[]}
120+
* @type {IGPUNativeFunction[]}
113121
*/
114122
this.nativeFunctions = null;
115123

0 commit comments

Comments
 (0)