mirror of
https://github.com/gpujs/gpu.js.git
synced 2026-01-18 16:04:10 +00:00
308 lines
9.0 KiB
JavaScript
308 lines
9.0 KiB
JavaScript
const utils = require('../core/utils');
|
|
const parser = require('../core/parser').parser;
|
|
|
|
/**
|
|
* @class FunctionNodeBase
|
|
*
|
|
* [INTERNAL] Represents a single function, inside JS, webGL, or openGL.
|
|
*
|
|
* This handles all the raw state, converted state, etc. Of a single function.
|
|
*
|
|
* @param functionName - {String} Name of the function
|
|
* @param jsFunction - {JS Function} The JS Function the node represents
|
|
* @param jsFunctionString - {String} jsFunction.toString()
|
|
* @param paramNames - {[String,...]} Parameter names of the function
|
|
* @param paramTypes - {[String,...]} Shader land parameters type assumption
|
|
* @param isRootKernel - {Boolean} Special indicator, for kernel function
|
|
* @param webglFunctionString - {String} webgl converted function string
|
|
* @param openglFunctionString - {String} opengl converted function string
|
|
* @param calledFunctions - {[String,...]} List of all the functions called
|
|
* @param initVariables - {[String,...]} List of variables initialized in the function
|
|
* @param readVariables - {[String,...]} List of variables read operations occur
|
|
* @param writeVariables - {[String,...]} List of variables write operations occur
|
|
*
|
|
*/
|
|
module.exports = class BaseFunctionNode {
|
|
|
|
//
|
|
// Constructor
|
|
//----------------------------------------------------------------------------------------------------
|
|
|
|
/**
|
|
* @name FunctionNodeBase
|
|
*
|
|
* @constructor Builds the function with the given JS function, and argument type array.
|
|
*
|
|
* @param gpu {GPU} The GPU instance
|
|
* @param functionName {String} Function name to assume, if its null, it attempts to extract from the function
|
|
* @param jsFunction {JS Function / String} JS Function to do conversion
|
|
* @param paramTypes {[String,...]|{variableName: Type}} Parameter type array, assumes all parameters are 'float' if null
|
|
* @param returnType {String} The return type, assumes 'float' if null
|
|
*
|
|
*/
|
|
constructor(functionName, jsFunction, options, paramTypes, returnType) {
|
|
//
|
|
// Internal vars setup
|
|
//
|
|
this.calledFunctions = [];
|
|
this.calledFunctionsArguments = {};
|
|
this.initVariables = [];
|
|
this.readVariables = [];
|
|
this.writeVariables = [];
|
|
this.addFunction = null;
|
|
this.isRootKernel = false;
|
|
this.isSubKernel = false;
|
|
this.parent = null;
|
|
this.debug = null;
|
|
this.prototypeOnly = null;
|
|
this.constants = null;
|
|
|
|
if (options) {
|
|
if (options.hasOwnProperty('debug')) {
|
|
this.debug = options.debug;
|
|
}
|
|
if (options.hasOwnProperty('prototypeOnly')) {
|
|
this.prototypeOnly = options.prototypeOnly;
|
|
}
|
|
if (options.hasOwnProperty('constants')) {
|
|
this.constants = options.constants;
|
|
}
|
|
if (options.hasOwnProperty('loopMaxIterations')) {
|
|
this.loopMaxIterations = options.loopMaxIterations;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Missing jsFunction object exception
|
|
//
|
|
if (!jsFunction) {
|
|
throw 'jsFunction, parameter is missing';
|
|
}
|
|
|
|
//
|
|
// Setup jsFunction and its string property + validate them
|
|
//
|
|
this.jsFunctionString = jsFunction.toString();
|
|
if (!utils.isFunctionString(this.jsFunctionString)) {
|
|
console.error('jsFunction, to string conversion check failed: not a function?', this.jsFunctionString);
|
|
throw 'jsFunction, to string conversion check failed: not a function?';
|
|
}
|
|
|
|
if (!utils.isFunction(jsFunction)) {
|
|
//throw 'jsFunction, is not a valid JS Function';
|
|
this.jsFunction = null;
|
|
} else {
|
|
this.jsFunction = jsFunction;
|
|
}
|
|
|
|
//
|
|
// Setup the function name property
|
|
//
|
|
this.functionName = functionName ||
|
|
(jsFunction && jsFunction.name) ||
|
|
utils.getFunctionNameFromString(this.jsFunctionString);
|
|
|
|
if (!(this.functionName)) {
|
|
throw 'jsFunction, missing name argument or value';
|
|
}
|
|
|
|
//
|
|
// Extract parameter name, and its argument types
|
|
//
|
|
this.paramNames = utils.getParamNamesFromString(this.jsFunctionString);
|
|
if (paramTypes) {
|
|
if (Array.isArray(paramTypes)) {
|
|
if (paramTypes.length !== this.paramNames.length) {
|
|
throw 'Invalid argument type array length, against function length -> (' +
|
|
paramTypes.length + ',' +
|
|
this.paramNames.length +
|
|
')';
|
|
}
|
|
this.paramTypes = paramTypes;
|
|
} else if (typeof paramTypes === 'object') {
|
|
const paramVariableNames = Object.keys(paramTypes);
|
|
if (paramTypes.hasOwnProperty('returns')) {
|
|
this.returnType = paramTypes.returns;
|
|
paramVariableNames.splice(paramVariableNames.indexOf('returns'), 1);
|
|
}
|
|
if (paramVariableNames.length > 0 && paramVariableNames.length !== this.paramNames.length) {
|
|
throw 'Invalid argument type array length, against function length -> (' +
|
|
paramVariableNames.length + ',' +
|
|
this.paramNames.length +
|
|
')';
|
|
} else {
|
|
this.paramTypes = this.paramNames.map((key) => {
|
|
if (paramTypes.hasOwnProperty(key)) {
|
|
return paramTypes[key];
|
|
} else {
|
|
return 'float';
|
|
}
|
|
});
|
|
}
|
|
}
|
|
} else {
|
|
this.paramTypes = [];
|
|
//TODO: Remove when we have proper type detection
|
|
// for (let a = 0; a < this.paramNames.length; ++a) {
|
|
// this.paramTypes.push();
|
|
// }
|
|
}
|
|
|
|
//
|
|
// Return type handling
|
|
//
|
|
if (!this.returnType) {
|
|
this.returnType = returnType || 'float';
|
|
}
|
|
}
|
|
|
|
setAddFunction(fn) {
|
|
this.addFunction = fn;
|
|
return this;
|
|
}
|
|
|
|
//
|
|
// Core function
|
|
//----------------------------------------------------------------------------------------------------
|
|
|
|
/**
|
|
* @name getJSFunction
|
|
*
|
|
* Gets and return the stored JS Function.
|
|
* Note: that this internally eval the function, if only the string was provided on construction
|
|
*
|
|
* Returns:
|
|
* {JS Function} The function object
|
|
*
|
|
*/
|
|
getJsFunction() {
|
|
if (this.jsFunction) {
|
|
return this.jsFunction;
|
|
}
|
|
|
|
if (this.jsFunctionString) {
|
|
this.jsFunction = eval(this.jsFunctionString);
|
|
return this.jsFunction;
|
|
}
|
|
|
|
throw 'Missing jsFunction, and jsFunctionString parameter';
|
|
}
|
|
|
|
/**
|
|
* @name getJS_AST
|
|
*
|
|
* Parses the class function JS, and returns its Abstract Syntax Tree object.
|
|
*
|
|
* This is used internally to convert to shader code
|
|
*
|
|
* @param inParser {JISON Parser} Parser to use, assumes in scope 'parser' if null
|
|
*
|
|
* Returns:
|
|
* {AST Object} The function AST Object, note that result is cached under this.jsFunctionAST;
|
|
*
|
|
*/
|
|
getJsAST(inParser) {
|
|
if (this.jsFunctionAST) {
|
|
return this.jsFunctionAST;
|
|
}
|
|
|
|
inParser = inParser || parser;
|
|
if (inParser === null) {
|
|
throw 'Missing JS to AST parser';
|
|
}
|
|
|
|
const prasedObj = parser.parse('var ' + this.functionName + ' = ' + this.jsFunctionString + ';');
|
|
if (prasedObj === null) {
|
|
throw 'Failed to parse JS code via JISON';
|
|
}
|
|
|
|
// take out the function object, outside the var declarations
|
|
const funcAST = prasedObj.body[0].declarations[0].init;
|
|
this.jsFunctionAST = funcAST;
|
|
|
|
return funcAST;
|
|
}
|
|
|
|
|
|
/**
|
|
* @name getFunctionString
|
|
*
|
|
* Returns the converted webgl shader function equivalent of the JS function
|
|
*
|
|
* Returns:
|
|
* {String} webgl function string, result is cached under this.webGlFunctionString
|
|
*
|
|
*/
|
|
getFunctionString() {
|
|
this.generate();
|
|
return this.functionString;
|
|
}
|
|
|
|
/**
|
|
* @name setFunctionString
|
|
*
|
|
* Set the functionString value, overwriting it
|
|
*
|
|
* @param functionString {String} Shader code string, representing the function
|
|
*
|
|
*/
|
|
setFunctionString(functionString) {
|
|
this.functionString = functionString;
|
|
}
|
|
|
|
/**
|
|
* @name getParamType
|
|
*
|
|
* Return the type of parameter sent to subKernel/Kernel.
|
|
*
|
|
* @param paramName {String} Name of the parameter
|
|
*
|
|
* Returns:
|
|
* {String} Type of the parameter
|
|
*
|
|
*/
|
|
getParamType(paramName) {
|
|
const paramIndex = this.paramNames.indexOf(paramName);
|
|
if (paramIndex === -1) return null;
|
|
if (!this.parent) return null;
|
|
if (this.paramTypes[paramIndex]) return this.paramTypes[paramIndex];
|
|
const calledFunctionArguments = this.parent.calledFunctionsArguments[this.functionName];
|
|
for (let i = 0; i < calledFunctionArguments.length; i++) {
|
|
const calledFunctionArgument = calledFunctionArguments[i];
|
|
if (calledFunctionArgument[paramIndex] !== null) {
|
|
return this.paramTypes[paramIndex] = calledFunctionArgument[paramIndex].type;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* @name getUserParamName
|
|
*
|
|
* Return the name of the *user parameter*(subKernel parameter) corresponding
|
|
* to the parameter supplied to the kernel
|
|
*
|
|
* @param paramName {String} Name of the parameter
|
|
*
|
|
* Returns:
|
|
* {String} Name of the parameter
|
|
*
|
|
*/
|
|
getUserParamName(paramName) {
|
|
const paramIndex = this.paramNames.indexOf(paramName);
|
|
if (paramIndex === -1) return null;
|
|
if (!this.parent) return null;
|
|
const calledFunctionArguments = this.parent.calledFunctionsArguments[this.functionName];
|
|
for (let i = 0; i < calledFunctionArguments.length; i++) {
|
|
const calledFunctionArgument = calledFunctionArguments[i];
|
|
if (calledFunctionArgument[paramIndex] !== null) {
|
|
return calledFunctionArgument[paramIndex].name;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
generate(options) {
|
|
throw new Error('generate not defined on BaseFunctionNode');
|
|
}
|
|
}; |