/** * gpu.js * http://gpu.rocks/ * * GPU Accelerated JavaScript * * @version 2.0.0-rc.14 * @date Mon May 13 2019 06:05:50 GMT-0400 (Eastern Daylight Time) * * @license MIT * The MIT License * * Copyright (c) 2019 gpu.js Team *//** * gpu.js * http://gpu.rocks/ * * GPU Accelerated JavaScript * * @version 2.0.0-rc.14 * @date Mon May 13 2019 06:05:48 GMT-0400 (Eastern Daylight Time) * * @license MIT * The MIT License * * Copyright (c) 2019 gpu.js Team */(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i 0) { recording.pop(); } } function insertVariable(name, value) { variables[name] = value; } function getEntity(value) { const name = entityNames[value]; if (name) { return contextName + '.' + name; } return value; } function setIndent(spaces) { indent = ' '.repeat(spaces); } function addVariable(value, source) { const variableName = `${contextName}Variable${contextVariables.length}`; recording.push(`${indent}const ${variableName} = ${source};`); contextVariables.push(value); return variableName; } function writePPM(width, height) { const sourceVariable = `${contextName}Variable${contextVariables.length}`; const imageVariable = `imageDatum${imageCount}`; recording.push(`${indent}let ${imageVariable} = ["P3\\n# ${readPixelsFile}.ppm\\n", ${width}, ' ', ${height}, "\\n255\\n"].join("");`); recording.push(`${indent}for (let i = 0; i < ${imageVariable}.length; i += 4) {`); recording.push(`${indent} ${imageVariable} += ${sourceVariable}[i] + ' ' + ${sourceVariable}[i + 1] + ' ' + ${sourceVariable}[i + 2] + ' ';`); recording.push(`${indent}}`); recording.push(`${indent}if (typeof require !== "undefined") {`); recording.push(`${indent} require('fs').writeFileSync('./${readPixelsFile}.ppm', ${imageVariable});`); recording.push(`${indent}}`); imageCount++; } function addComment(value) { recording.push(`${indent}// ${value}`); } function checkThrowError() { recording.push(`${indent}(() => { ${indent}const error = ${contextName}.getError(); ${indent}if (error !== ${contextName}.NONE) { ${indent} const names = Object.getOwnPropertyNames(gl); ${indent} for (let i = 0; i < names.length; i++) { ${indent} const name = names[i]; ${indent} if (${contextName}[name] === error) { ${indent} throw new Error('${contextName} threw ' + name); ${indent} } ${indent} } ${indent}} ${indent}})();`); } function methodCallToString(method, args) { return `${contextName}.${method}(${argumentsToString(args, { contextName, contextVariables, getEntity, addVariable, variables })})`; } function getVariableName(value) { if (variables) { for (const name in variables) { if (variables[name] === value) { return name; } } } return null; } } function glExtensionWiretap(extension, options) { const proxy = new Proxy(extension, { get: listen }); const extensionEntityNames = {}; const { contextName, contextVariables, getEntity, useTrackablePrimitives, recording, variables, indent, } = options; return proxy; function listen(obj, property) { if (typeof obj[property] === 'function') { return function() { switch (property) { case 'drawBuffersWEBGL': recording.push(`${indent}${contextName}.drawBuffersWEBGL([${argumentsToString(arguments[0], { contextName, contextVariables, getEntity: getExtensionEntity, addVariable, variables })}]);`); return extension.drawBuffersWEBGL(arguments[0]); } let result = extension[property].apply(extension, arguments); switch (typeof result) { case 'undefined': recording.push(`${indent}${methodCallToString(property, arguments)};`); return; case 'number': case 'boolean': if (useTrackablePrimitives && contextVariables.indexOf(trackablePrimitive(result)) === -1) { recording.push(`${indent}const ${contextName}Variable${contextVariables.length} = ${methodCallToString(property, arguments)};`); contextVariables.push(result = trackablePrimitive(result)); } else { recording.push(`${indent}const ${contextName}Variable${contextVariables.length} = ${methodCallToString(property, arguments)};`); contextVariables.push(result); } break; default: if (result === null) { recording.push(`${methodCallToString(property, arguments)};`); } else { recording.push(`${indent}const ${contextName}Variable${contextVariables.length} = ${methodCallToString(property, arguments)};`); } contextVariables.push(result); } return result; }; } extensionEntityNames[extension[property]] = property; return extension[property]; } function getExtensionEntity(value) { if (extensionEntityNames.hasOwnProperty(value)) { return `${contextName}.${extensionEntityNames[value]}`; } return getEntity(value); } function methodCallToString(method, args) { return `${contextName}.${method}(${argumentsToString(args, { contextName, contextVariables, getEntity: getExtensionEntity, addVariable, variables })})`; } function addVariable(value, source) { const variableName = `${contextName}Variable${contextVariables.length}`; contextVariables.push(value); recording.push(`${indent}const ${variableName} = ${source};`); return variableName; } } function argumentsToString(args, options) { const { variables } = options; return (Array.from(args).map((arg) => { const variableName = getVariableName(arg); if (variableName) { return variableName; } return argumentToString(arg, options); }).join(', ')); function getVariableName(value) { if (variables) { for (const name in variables) { if (variables[name] === value) { return name; } } } return null; } } function argumentToString(arg, options) { const { contextName, contextVariables, getEntity, addVariable } = options; if (typeof arg === 'undefined') { return 'undefined'; } if (arg === null) { return 'null'; } const i = contextVariables.indexOf(arg); if (i > -1) { return `${contextName}Variable${i}`; } switch (arg.constructor.name) { case 'String': const hasLines = /\n/.test(arg); const hasSingleQuotes = /'/.test(arg); const hasDoubleQuotes = /"/.test(arg); if (hasLines) { return '`' + arg + '`'; } else if (hasSingleQuotes && !hasDoubleQuotes) { return '"' + arg + '"'; } else if (!hasSingleQuotes && hasDoubleQuotes) { return "'" + arg + "'"; } else { return '\'' + arg + '\''; } case 'Number': return getEntity(arg); case 'Boolean': return getEntity(arg); case 'Array': return addVariable(arg, `new ${arg.constructor.name}(${Array.from(arg).join(',')})`); case 'Float32Array': case 'Uint8Array': case 'Uint16Array': case 'Int32Array': return addVariable(arg, `new ${arg.constructor.name}(${JSON.stringify(Array.from(arg))})`); default: throw new Error('unrecognized argument'); } } function trackablePrimitive(value) { return new value.constructor(value); } if (typeof module !== 'undefined') { module.exports = { glWiretap, glExtensionWiretap }; } if (typeof window !== 'undefined') { glWiretap.glExtensionWiretap = glExtensionWiretap; window.glWiretap = glWiretap; } },{}],2:[function(require,module,exports){ },{}],3:[function(require,module,exports){ 'use strict'; function mock1D() { const row = []; 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)); } return row; } function mock2D() { const matrix = []; for (let y = 0; y < this.output.y; y++) { const row = []; 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)); } matrix.push(row); } return matrix; } function mock3D() { const cube = []; for (let z = 0; z < this.output.z; z++) { const matrix = []; for (let y = 0; y < this.output.y; y++) { const row = []; 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)); } matrix.push(row); } cube.push(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] }; } } else { contextOutput = options.output; } const context = { _fn: fn, constants: options.constants, output: contextOutput, thread: { x: 0, y: 0, z: 0 } }; if (contextOutput.z) { return mock3D.bind(context); } else if (contextOutput.y) { return mock2D.bind(context); } else { return mock1D.bind(context); } }; },{}],4:[function(require,module,exports){ const { utils } = require('./utils'); function alias(name, source) { const fnString = source.toString(); return new Function(`return function ${ name } (${ utils.getArgumentNamesFromString(fnString).join(', ') }) { ${ utils.getFunctionBodyFromString(fnString) } }`)(); } module.exports = { alias }; },{"./utils":69}],5:[function(require,module,exports){ const { FunctionNode } = require('../function-node'); class CPUFunctionNode extends FunctionNode { astFunctionExpression(ast, retArr) { if (!this.isRootKernel) { retArr.push('function'); retArr.push(' '); retArr.push(this.name); retArr.push('('); for (let i = 0; i < this.argumentNames.length; ++i) { const argumentName = this.argumentNames[i]; if (i > 0) { retArr.push(', '); } retArr.push('user_'); retArr.push(argumentName); } retArr.push(') {\n'); } for (let i = 0; i < ast.body.body.length; ++i) { this.astGeneric(ast.body.body[i], retArr); retArr.push('\n'); } if (!this.isRootKernel) { retArr.push('}\n'); } return retArr; } astReturnStatement(ast, retArr) { if (this.isRootKernel) { retArr.push(this.leadingReturnStatement); this.astGeneric(ast.argument, retArr); retArr.push(';\n'); retArr.push(this.followingReturnStatement); retArr.push('continue;\n'); } else if (this.isSubKernel) { retArr.push(`subKernelResult_${ this.name } = `); this.astGeneric(ast.argument, retArr); retArr.push(';'); retArr.push(`return subKernelResult_${ this.name };`); } else { retArr.push('return '); this.astGeneric(ast.argument, retArr); retArr.push(';'); } return retArr; } astLiteral(ast, retArr) { if (isNaN(ast.value)) { throw this.astErrorOutput( 'Non-numeric literal not supported : ' + ast.value, ast ); } retArr.push(ast.value); return retArr; } astBinaryExpression(ast, retArr) { retArr.push('('); this.astGeneric(ast.left, retArr); retArr.push(ast.operator); this.astGeneric(ast.right, retArr); retArr.push(')'); return retArr; } astIdentifierExpression(idtNode, retArr) { if (idtNode.type !== 'Identifier') { throw this.astErrorOutput( 'IdentifierExpression - not an Identifier', idtNode ); } switch (idtNode.name) { case 'Infinity': retArr.push('Infinity'); break; default: if (this.constants && this.constants.hasOwnProperty(idtNode.name)) { retArr.push('constants_' + idtNode.name); } else { const name = this.getKernelArgumentName(idtNode.name); if (name) { retArr.push('user_' + name); } else { retArr.push('user_' + idtNode.name); } } } return retArr; } astForStatement(forNode, retArr) { if (forNode.type !== 'ForStatement') { throw this.astErrorOutput('Invalid for statement', forNode); } const initArr = []; const testArr = []; const updateArr = []; const bodyArr = []; let isSafe = null; 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(',')) { isSafe = false; } } this.popState('in-for-loop-init'); } else { isSafe = false; } if (forNode.test) { this.astGeneric(forNode.test, testArr); } else { isSafe = false; } if (forNode.update) { this.astGeneric(forNode.update, updateArr); } else { isSafe = false; } if (forNode.body) { this.pushState('loop-body'); this.astGeneric(forNode.body, bodyArr); this.popState('loop-body'); } if (isSafe === null) { isSafe = this.isSafe(forNode.init) && this.isSafe(forNode.test); } if (isSafe) { retArr.push(`for (${initArr.join('')};${testArr.join('')};${updateArr.join('')}){\n`); retArr.push(bodyArr.join('')); retArr.push('}\n'); } else { const iVariableName = this.getInternalVariableName('safeI'); if (initArr.length > 0) { retArr.push(initArr.join(''), ';\n'); } retArr.push(`for (let ${iVariableName}=0;${iVariableName} 0) { retArr.push(`if (!${testArr.join('')}) break;\n`); } retArr.push(bodyArr.join('')); retArr.push(`\n${updateArr.join('')};`); retArr.push('}\n'); } return retArr; } astWhileStatement(whileNode, retArr) { if (whileNode.type !== 'WhileStatement') { throw this.astErrorOutput( 'Invalid while statement', whileNode ); } retArr.push('for (let i = 0; i < LOOP_MAX; i++) {'); retArr.push('if ('); this.astGeneric(whileNode.test, retArr); retArr.push(') {\n'); this.astGeneric(whileNode.body, retArr); retArr.push('} else {\n'); retArr.push('break;\n'); retArr.push('}\n'); retArr.push('}\n'); return retArr; } astDoWhileStatement(doWhileNode, retArr) { if (doWhileNode.type !== 'DoWhileStatement') { throw this.astErrorOutput( 'Invalid while statement', doWhileNode ); } retArr.push('for (let i = 0; i < LOOP_MAX; i++) {'); this.astGeneric(doWhileNode.body, retArr); retArr.push('if (!'); this.astGeneric(doWhileNode.test, retArr); retArr.push(') {\n'); retArr.push('break;\n'); retArr.push('}\n'); retArr.push('}\n'); return retArr; } astAssignmentExpression(assNode, retArr) { this.astGeneric(assNode.left, retArr); retArr.push(assNode.operator); this.astGeneric(assNode.right, retArr); return retArr; } astBlockStatement(bNode, retArr) { if (this.isState('loop-body')) { this.pushState('block-body'); for (let i = 0; i < bNode.body.length; i++) { this.astGeneric(bNode.body[i], retArr); } this.popState('block-body'); } else { retArr.push('{\n'); for (let i = 0; i < bNode.body.length; i++) { this.astGeneric(bNode.body[i], retArr); } retArr.push('}\n'); } return retArr; } astVariableDeclaration(varDecNode, retArr) { if (varDecNode.kind === 'var') { this.varWarn(); } retArr.push(`${varDecNode.kind} `); const firstDeclaration = varDecNode.declarations[0]; const type = this.getType(firstDeclaration.init); for (let i = 0; i < varDecNode.declarations.length; i++) { this.declarations[varDecNode.declarations[i].id.name] = { type: type === 'LiteralInteger' ? 'Number' : type, dependencies: { constants: [], arguments: [] }, isUnsafe: false }; if (i > 0) { retArr.push(','); } this.astGeneric(varDecNode.declarations[i], retArr); } if (!this.isState('in-for-loop-init')) { retArr.push(';'); } return retArr; } astIfStatement(ifNode, retArr) { retArr.push('if ('); this.astGeneric(ifNode.test, retArr); retArr.push(')'); if (ifNode.consequent.type === 'BlockStatement') { this.astGeneric(ifNode.consequent, retArr); } else { retArr.push(' {\n'); this.astGeneric(ifNode.consequent, retArr); retArr.push('\n}\n'); } if (ifNode.alternate) { retArr.push('else '); if (ifNode.alternate.type === 'BlockStatement') { this.astGeneric(ifNode.alternate, retArr); } else { retArr.push(' {\n'); this.astGeneric(ifNode.alternate, retArr); retArr.push('\n}\n'); } } return retArr; } astThisExpression(tNode, retArr) { retArr.push('_this'); return retArr; } astMemberExpression(mNode, retArr) { const { signature, type, property, xProperty, yProperty, zProperty, name, origin } = this.getMemberExpressionDetails(mNode); switch (signature) { case 'this.thread.value': retArr.push(`_this.thread.${ name }`); return retArr; case 'this.output.value': switch (name) { case 'x': retArr.push(this.output[0]); break; case 'y': retArr.push(this.output[1]); break; case 'z': retArr.push(this.output[2]); break; default: throw this.astErrorOutput('Unexpected expression', mNode); } return retArr; case 'value': throw this.astErrorOutput('Unexpected expression', mNode); case 'value[]': case 'value[][]': case 'value[][][]': case 'value.value': if (origin === 'Math') { retArr.push(Math[name]); return retArr; } switch (property) { case 'r': retArr.push(`user_${ name }[0]`); return retArr; case 'g': retArr.push(`user_${ name }[1]`); return retArr; case 'b': retArr.push(`user_${ name }[2]`); return retArr; case 'a': retArr.push(`user_${ name }[3]`); return retArr; } break; case 'this.constants.value': case 'this.constants.value[]': case 'this.constants.value[][]': case 'this.constants.value[][][]': break; case 'fn()[]': this.astGeneric(mNode.object, retArr); retArr.push('['); this.astGeneric(mNode.property, retArr); retArr.push(']'); return retArr; default: throw this.astErrorOutput('Unexpected expression', mNode); } switch (type) { case 'Number': case 'Integer': case 'Float': case 'Boolean': retArr.push(`${ origin }_${ name}`); return retArr; } const synonymName = this.getKernelArgumentName(name); const markupName = `${origin}_${synonymName || name}`; switch (type) { case 'Array(2)': case 'Array(3)': case 'Array(4)': case 'HTMLImageArray': case 'ArrayTexture(1)': case 'ArrayTexture(2)': case 'ArrayTexture(3)': case 'ArrayTexture(4)': case 'HTMLImage': default: const isInput = this.isInput(synonymName || name); retArr.push(`${ markupName }`); if (zProperty && yProperty) { if (isInput) { const size = this.argumentSizes[this.argumentNames.indexOf(name)]; retArr.push('[('); this.astGeneric(zProperty, retArr); retArr.push(`*${ size[1] * size[0]})+(`); this.astGeneric(yProperty, retArr); retArr.push(`*${ size[0] })+`); this.astGeneric(xProperty, retArr); retArr.push(']'); } else { retArr.push('['); this.astGeneric(zProperty, retArr); retArr.push(']'); retArr.push('['); this.astGeneric(yProperty, retArr); retArr.push(']'); retArr.push('['); this.astGeneric(xProperty, retArr); retArr.push(']'); } } else if (yProperty) { if (isInput) { const size = this.argumentSizes[this.argumentNames.indexOf(name)]; retArr.push('[('); this.astGeneric(yProperty, retArr); retArr.push(`*${ size[0] })+`); this.astGeneric(xProperty, retArr); retArr.push(']'); } else { retArr.push('['); this.astGeneric(yProperty, retArr); retArr.push(']'); retArr.push('['); this.astGeneric(xProperty, retArr); retArr.push(']'); } } else { retArr.push('['); this.astGeneric(xProperty, retArr); retArr.push(']'); } } return retArr; } astCallExpression(ast, retArr) { if (ast.callee) { let funcName = this.astMemberExpressionUnroll(ast.callee); if (this.calledFunctions.indexOf(funcName) < 0) { this.calledFunctions.push(funcName); } if (!this.calledFunctionsArguments[funcName]) { this.calledFunctionsArguments[funcName] = []; } const functionArguments = []; this.calledFunctionsArguments[funcName].push(functionArguments); retArr.push(funcName); retArr.push('('); for (let i = 0; i < ast.arguments.length; ++i) { const argument = ast.arguments[i]; if (i > 0) { retArr.push(', '); } this.astGeneric(argument, retArr); const argumentType = this.getType(argument); if (argumentType) { functionArguments.push({ name: argument.name || null, type: argumentType }); } else { functionArguments.push(null); } } retArr.push(')'); return retArr; } throw this.astErrorOutput( 'Unknown CallExpression', ast ); } astArrayExpression(arrNode, retArr) { const arrLen = arrNode.elements.length; retArr.push('['); for (let i = 0; i < arrLen; ++i) { if (i > 0) { retArr.push(', '); } const subNode = arrNode.elements[i]; this.astGeneric(subNode, retArr) } retArr.push(']'); return retArr; } astDebuggerStatement(arrNode, retArr) { retArr.push('debugger;'); return retArr; } } module.exports = { CPUFunctionNode }; },{"../function-node":9}],6:[function(require,module,exports){ const { utils } = require('../../utils'); const { kernelRunShortcut } = require('../../kernel-run-shortcut'); function removeFnNoise(fn) { if (/^function /.test(fn)) { fn = fn.substring(9); } return fn.replace(/[_]typeof/g, 'typeof'); } function removeNoise(str) { return str .replace(/^[A-Za-z]+/, 'function') .replace(/[_]typeof/g, 'typeof'); } function cpuKernelString(cpuKernel, name) { return `() => { ${ kernelRunShortcut.toString() }; const utils = { allPropertiesOf: ${ removeNoise(utils.allPropertiesOf.toString()) }, clone: ${ removeNoise(utils.clone.toString()) }, }; let Input = function() {}; class ${ name || 'Kernel' } { constructor() { this.argumentsLength = 0; this.canvas = null; this.context = null; this.built = false; this.program = null; this.argumentNames = ${ JSON.stringify(cpuKernel.argumentNames) }; this.argumentTypes = ${ JSON.stringify(cpuKernel.argumentTypes) }; this.argumentSizes = ${ JSON.stringify(cpuKernel.argumentSizes) }; this.output = ${ JSON.stringify(cpuKernel.output) }; this._kernelString = \`${ cpuKernel._kernelString }\`; this.output = ${ JSON.stringify(cpuKernel.output) }; this.run = function() { this.run = null; this.build(arguments); return this.run.apply(this, arguments); }.bind(this); this.thread = { x: 0, y: 0, z: 0 }; } setCanvas(canvas) { this.canvas = canvas; return this; } setContext(context) { this.context = context; return this; } setInput(Type) { Input = Type; } ${ removeFnNoise(cpuKernel.build.toString()) } setupArguments() {} ${ removeFnNoise(cpuKernel.setupConstants.toString()) } translateSource() {} pickRenderStrategy() {} run () { ${ cpuKernel.kernelString } } getKernelString() { return this._kernelString; } ${ removeFnNoise(cpuKernel.validateSettings.toString()) } ${ removeFnNoise(cpuKernel.checkOutput.toString()) } }; return kernelRunShortcut(new Kernel()); };`; } module.exports = { cpuKernelString }; },{"../../kernel-run-shortcut":66,"../../utils":69}],7:[function(require,module,exports){ const { Kernel } = require('../kernel'); const { FunctionBuilder } = require('../function-builder'); const { CPUFunctionNode } = require('./function-node'); const { utils } = require('../../utils'); const { cpuKernelString } = require('./kernel-string'); class CPUKernel extends Kernel { static getFeatures() { return this.features; } static get features() { return Object.freeze({ kernelMap: true, isIntegerDivisionAccurate: true }); } static get isSupported() { return true; } static isContextMatch(context) { return false; } static get mode() { return 'cpu'; } static nativeFunctionArguments() { return null; } static nativeFunctionReturnType() { return null; } static combineKernels(combinedKernel) { return combinedKernel; } constructor(source, settings) { super(source, settings); this.mergeSettings(source.settings || settings); this._imageData = null; this._colorData = null; this._kernelString = null; this.thread = { x: 0, y: 0, z: 0 }; this.translatedSources = null; this.run = function() { this.run = null; this.build.apply(this, arguments); return this.run.apply(this, arguments); }.bind(this); } initCanvas() { if (typeof document !== 'undefined') { return document.createElement('canvas'); } else if (typeof OffscreenCanvas !== 'undefined') { return new OffscreenCanvas(0, 0); } } initContext() { if (!this.canvas) return null; return this.canvas.getContext('2d'); } initPlugins(settings) { return []; } validateSettings() { if (!this.output || this.output.length === 0) { if (arguments.length !== 1) { throw 'Auto dimensions only supported for kernels with only one input'; } const argType = utils.getVariableType(arguments[0], this.strictIntegers); if (argType === 'Array') { this.output = utils.getDimensions(argType); } else if (argType === 'NumberTexture' || argType === 'ArrayTexture(4)') { this.output = arguments[0].output; } else { throw 'Auto dimensions not supported for input type: ' + argType; } } if (this.graphical) { if (this.output.length !== 2) { throw new Error('Output must have 2 dimensions on graphical mode'); } } this.checkOutput(); } translateSource() { this.leadingReturnStatement = this.output.length > 1 ? 'resultX[x] = ' : 'result[x] = '; if (this.subKernels) { const followingReturnStatement = [] for (let i = 0; i < this.subKernels.length; i++) { const { name } = this.subKernels[i]; followingReturnStatement.push(this.output.length > 1 ? `resultX_${ name }[x] = subKernelResult_${ name };\n` : `result_${ name }[x] = subKernelResult_${ name };\n`); } this.followingReturnStatement = followingReturnStatement.join(''); } const functionBuilder = FunctionBuilder.fromKernel(this, CPUFunctionNode); this.translatedSources = functionBuilder.getPrototypes('kernel'); if (!this.graphical && !this.returnType) { this.returnType = functionBuilder.getKernelResultType(); } } build() { this.setupConstants(); this.setupArguments(arguments); this.validateSettings(); this.translateSource(); if (this.graphical) { const { canvas, output } = this; if (!canvas) { throw new Error('no canvas available for using graphical output'); } const width = output[0]; const height = output[1] || 1; canvas.width = width; canvas.height = height; this._imageData = this.context.createImageData(width, height); this._colorData = new Uint8ClampedArray(width * height * 4); } const kernelString = this.getKernelString(); this.kernelString = kernelString; if (this.debug) { console.log('Function output:'); console.log(kernelString); } try { this.run = new Function([], kernelString).bind(this)(); } catch (e) { console.error('An error occurred compiling the javascript: ', e); } } color(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 = this.output[0]; const height = this.output[1]; const x = this.thread.x; const y = height - this.thread.y - 1; const index = x + y * width; this._colorData[index * 4 + 0] = r; this._colorData[index * 4 + 1] = g; this._colorData[index * 4 + 2] = b; this._colorData[index * 4 + 3] = a; } getKernelString() { if (this._kernelString !== null) return this._kernelString; let kernelThreadString = null; let { translatedSources } = this; if (translatedSources.length > 1) { translatedSources = translatedSources.filter(fn => { if (/^function/.test(fn)) return fn; kernelThreadString = fn; return false; }) } else { kernelThreadString = translatedSources.shift(); } const kernelString = this._kernelString = ` const LOOP_MAX = ${ this._getLoopMaxString() } const constants = this.constants; const _this = this; return (${ this.argumentNames.map(argumentName => 'user_' + argumentName).join(', ') }) => { ${ this._processConstants() } ${ this._processArguments() } ${ this.graphical ? this._graphicalKernelBody(kernelThreadString) : this._resultKernelBody(kernelThreadString) } ${ translatedSources.length > 0 ? translatedSources.join('\n') : '' } };`; return kernelString; } toString() { return cpuKernelString(this); } _getLoopMaxString() { return ( this.loopMaxIterations ? ` ${ parseInt(this.loopMaxIterations) };` : ' 1000;' ); } _processConstants() { if (!this.constants) return ''; const result = []; for (let p in this.constants) { const type = this.constantTypes[p]; switch (type) { case 'HTMLImage': result.push(` const constants_${p} = this._imageTo2DArray(this.constants.${p});\n`); break; case 'HTMLImageArray': result.push(` const constants_${p} = this._imageTo3DArray(this.constants.${p});\n`); break; case 'Input': result.push(` const constants_${p} = this.constants.${p}.value;\n`); break; default: result.push(` const constants_${p} = this.constants.${p};\n`); } } return result.join(''); } _processArguments() { const result = []; for (let i = 0; i < this.argumentTypes.length; i++) { switch (this.argumentTypes[i]) { case 'HTMLImage': result.push(` user_${this.argumentNames[i]} = this._imageTo2DArray(user_${this.argumentNames[i]});\n`); break; case 'HTMLImageArray': result.push(` user_${this.argumentNames[i]} = this._imageTo3DArray(user_${this.argumentNames[i]});\n`); break; case 'Input': result.push(` user_${this.argumentNames[i]} = user_${this.argumentNames[i]}.value;\n`); break; } } return result.join(''); } _imageTo2DArray(image) { const canvas = this.canvas; if (canvas.width < image.width) { canvas.width = image.width; } if (canvas.height < image.height) { canvas.height = image.height; } const ctx = this.context; ctx.drawImage(image, 0, 0, image.width, image.height); const pixelsData = ctx.getImageData(0, 0, image.width, image.height).data; const imageArray = new Array(image.height); let index = 0; for (let y = image.height - 1; y >= 0; y--) { const row = imageArray[y] = new Array(image.width); for (let x = 0; x < image.width; x++) { const pixel = new Float32Array(4); pixel[0] = pixelsData[index++] / 255; pixel[1] = pixelsData[index++] / 255; pixel[2] = pixelsData[index++] / 255; pixel[3] = pixelsData[index++] / 255; row[x] = pixel; } } return imageArray; } getPixels() { const [width, height] = this.output; const halfHeight = height / 2 | 0; const bytesPerRow = width * 4; const temp = new Uint8Array(width * 4); const pixels = this._imageData.data.slice(0); for (let y = 0; y < halfHeight; ++y) { var topOffset = y * bytesPerRow; var bottomOffset = (height - y - 1) * bytesPerRow; temp.set(pixels.subarray(topOffset, topOffset + bytesPerRow)); pixels.copyWithin(topOffset, bottomOffset, bottomOffset + bytesPerRow); pixels.set(temp, bottomOffset); } return pixels; } _imageTo3DArray(images) { const imagesArray = new Array(images.length); for (let i = 0; i < images.length; i++) { imagesArray[i] = this._imageTo2DArray(images[i]); } return imagesArray; } _resultKernelBody(kernelString) { switch (this.output.length) { case 1: return this._resultKernel1DLoop(kernelString) + this._kernelOutput(); case 2: return this._resultKernel2DLoop(kernelString) + this._kernelOutput(); case 3: return this._resultKernel3DLoop(kernelString) + this._kernelOutput(); default: throw new Error('unsupported size kernel'); } } _graphicalKernelBody(kernelThreadString) { switch (this.output.length) { case 2: return this._graphicalKernel2DLoop(kernelThreadString) + this._graphicalOutput(); default: throw new Error('unsupported size kernel'); } } _graphicalOutput() { return ` this._imageData.data.set(this._colorData); this.context.putImageData(this._imageData, 0, 0); return;` } _getKernelResultTypeConstructorString() { switch (this.returnType) { case 'LiteralInteger': case 'Number': case 'Integer': case 'Float': return 'Float32Array'; case 'Array(2)': case 'Array(3)': case 'Array(4)': return 'Array'; default: if (this.graphical) { return 'Float32Array'; } throw new Error(`unhandled returnType ${ this.returnType }`); } } _resultKernel1DLoop(kernelString) { const { output } = this; const constructorString = this._getKernelResultTypeConstructorString(); return `const result = new ${constructorString}(${ output[0] }); ${ this._mapSubKernels(subKernel => `const result_${ subKernel.name } = new ${constructorString}(${ output[0] });\n`).join(' ') } ${ this._mapSubKernels(subKernel => `let subKernelResult_${ subKernel.name };\n`).join(' ') } for (let x = 0; x < ${ output[0] }; x++) { this.thread.x = x; this.thread.y = 0; this.thread.z = 0; ${ kernelString } }`; } _resultKernel2DLoop(kernelString) { const { output } = this; const constructorString = this._getKernelResultTypeConstructorString(); return `const result = new Array(${ output[1] }); ${ this._mapSubKernels(subKernel => `const result_${ subKernel.name } = new Array(${ output[1] });\n`).join(' ') } ${ this._mapSubKernels(subKernel => `let subKernelResult_${ subKernel.name };\n`).join(' ') } for (let y = 0; y < ${ output[1] }; y++) { this.thread.z = 0; this.thread.y = y; const resultX = result[y] = new ${constructorString}(${ output[0] }); ${ this._mapSubKernels(subKernel => `const resultX_${ subKernel.name } = result_${subKernel.name}[y] = new ${constructorString}(${ output[0] });\n`).join('') } for (let x = 0; x < ${ output[0] }; x++) { this.thread.x = x; ${ kernelString } } }`; } _graphicalKernel2DLoop(kernelString) { const { output } = this; const constructorString = this._getKernelResultTypeConstructorString(); return ` ${ this._mapSubKernels(subKernel => `const result_${ subKernel.name } = new Array(${ output[1] });\n`).join(' ') } ${ this._mapSubKernels(subKernel => `let subKernelResult_${ subKernel.name };\n`).join(' ') } for (let y = 0; y < ${ output[1] }; y++) { this.thread.z = 0; this.thread.y = y; ${ this._mapSubKernels(subKernel => `const resultX_${ subKernel.name } = result_${subKernel.name}[y] = new ${constructorString}(${ output[0] });\n`).join('') } for (let x = 0; x < ${ output[0] }; x++) { this.thread.x = x; ${ kernelString } } }`; } _resultKernel3DLoop(kernelString) { const { output } = this; const constructorString = this._getKernelResultTypeConstructorString(); return `const result = new Array(${ output[2] }); ${ this._mapSubKernels(subKernel => `const result_${ subKernel.name } = new Array(${ output[2] });\n`).join(' ') } ${ this._mapSubKernels(subKernel => `let subKernelResult_${ subKernel.name };\n`).join(' ') } for (let z = 0; z < ${ output[2] }; z++) { this.thread.z = z; const resultY = result[z] = new Array(${ output[1] }); ${ this._mapSubKernels(subKernel => `const resultY_${ subKernel.name } = result_${subKernel.name}[z] = new Array(${ output[1] });\n`).join(' ') } for (let y = 0; y < ${ output[1] }; y++) { this.thread.y = y; const resultX = resultY[y] = new ${constructorString}(${ output[0] }); ${ this._mapSubKernels(subKernel => `const resultX_${ subKernel.name } = resultY_${subKernel.name}[y] = new ${constructorString}(${ output[0] });\n`).join(' ') } for (let x = 0; x < ${ output[0] }; x++) { this.thread.x = x; ${ kernelString } } } }`; } _kernelOutput() { if (!this.subKernels) { return '\n return result;'; } return `\n return { result: result, ${ this.subKernels.map(subKernel => `${ subKernel.property }: result_${ subKernel.name }`).join(',\n ') } };`; } _mapSubKernels(fn) { return this.subKernels === null ? [''] : this.subKernels.map(fn); } destroy(removeCanvasReference) { if (removeCanvasReference) { delete this.canvas; } } static destroyContext(context) {} toJSON() { const json = super.toJSON(); json.functionNodes = FunctionBuilder.fromKernel(this, CPUFunctionNode).toJSON(); return json; } } module.exports = { CPUKernel }; },{"../../utils":69,"../function-builder":8,"../kernel":14,"./function-node":5,"./kernel-string":6}],8:[function(require,module,exports){ class FunctionBuilder { static fromKernel(kernel, FunctionNode, extraNodeOptions) { const { kernelArguments, argumentNames, argumentTypes, argumentSizes, argumentBitRatios, constants, constantTypes, constantBitRatios, debug, loopMaxIterations, nativeFunctions, output, optimizeFloatMemory, precision, plugins, source, subKernels, functions, leadingReturnStatement, followingReturnStatement, } = kernel; const lookupReturnType = (functionName, ast, requestingNode) => { return functionBuilder.lookupReturnType(functionName, ast, requestingNode); }; const lookupArgumentType = (argumentName, requestingNode) => { return functionBuilder.lookupArgumentType(argumentName, requestingNode); }; const lookupFunctionArgumentTypes = (functionName) => { return functionBuilder.lookupFunctionArgumentTypes(functionName); }; const lookupFunctionArgumentName = (functionName, argumentIndex) => { return functionBuilder.lookupFunctionArgumentName(functionName, argumentIndex); }; const lookupFunctionArgumentBitRatio = (functionName, argumentName) => { return functionBuilder.lookupFunctionArgumentBitRatio(functionName, argumentName); }; const triggerImplyArgumentType = (functionName, i, argumentType, requestingNode) => { functionBuilder.assignArgumentType(functionName, i, argumentType, requestingNode); }; const triggerTrackArgumentSynonym = (functionName, argumentName, calleeFunctionName, argumentIndex) => { functionBuilder.trackArgumentSynonym(functionName, argumentName, calleeFunctionName, argumentIndex); }; const lookupArgumentSynonym = (originFunctionName, functionName, argumentName) => { return functionBuilder.lookupArgumentSynonym(originFunctionName, functionName, argumentName); }; const onFunctionCall = (functionName, calleeFunctionName) => { functionBuilder.trackFunctionCall(functionName, calleeFunctionName); }; const onNestedFunction = (fnString, returnType) => { functionBuilder.addFunctionNode(new FunctionNode(fnString, Object.assign({}, nodeOptions, { returnType: returnType || 'Number', lookupReturnType, lookupArgumentType, lookupFunctionArgumentTypes, lookupFunctionArgumentName, lookupFunctionArgumentBitRatio, triggerImplyArgumentType, triggerTrackArgumentSynonym, lookupArgumentSynonym, onFunctionCall }))); }; const nodeOptions = Object.assign({ isRootKernel: false, onNestedFunction, lookupReturnType, lookupArgumentType, lookupFunctionArgumentTypes, lookupFunctionArgumentName, lookupFunctionArgumentBitRatio, triggerImplyArgumentType, triggerTrackArgumentSynonym, lookupArgumentSynonym, onFunctionCall, optimizeFloatMemory, precision, constants, constantTypes, constantBitRatios, debug, loopMaxIterations, output, plugins, }, extraNodeOptions || {}); const rootNodeOptions = Object.assign({}, nodeOptions, { isRootKernel: true, name: 'kernel', argumentNames, argumentTypes, argumentSizes, argumentBitRatios, leadingReturnStatement, followingReturnStatement, }); if (typeof source === 'object' && source.functionNodes) { return new FunctionBuilder().fromJSON(source.functionNodes, FunctionNode); } const rootNode = new FunctionNode(source, rootNodeOptions); let functionNodes = null; if (functions) { functionNodes = functions.map((fn) => new FunctionNode(fn.source, { returnType: fn.returnType, argumentTypes: fn.argumentTypes, output, plugins, constants, constantTypes, constantBitRatios, optimizeFloatMemory, precision, lookupReturnType, lookupArgumentType, lookupFunctionArgumentTypes, lookupFunctionArgumentName, lookupFunctionArgumentBitRatio, triggerImplyArgumentType, triggerTrackArgumentSynonym, lookupArgumentSynonym, onFunctionCall, })); } let subKernelNodes = null; if (subKernels) { subKernelNodes = subKernels.map((subKernel) => { const { name, source } = subKernel; return new FunctionNode(source, Object.assign({}, nodeOptions, { name, isSubKernel: true, isRootKernel: false, returnType: 'Number', })); }); } const functionBuilder = new FunctionBuilder({ kernel, rootNode, functionNodes, nativeFunctions, subKernelNodes }); return functionBuilder; } constructor(settings) { settings = settings || {}; this.kernel = settings.kernel; this.rootNode = settings.rootNode; this.functionNodes = settings.functionNodes || []; this.subKernelNodes = settings.subKernelNodes || []; this.nativeFunctions = settings.nativeFunctions || []; this.functionMap = {}; this.nativeFunctionNames = []; this.lookupChain = []; this.argumentChain = []; this.functionNodeDependencies = {}; if (this.rootNode) { this.functionMap['kernel'] = this.rootNode; } if (this.functionNodes) { for (let i = 0; i < this.functionNodes.length; i++) { this.functionMap[this.functionNodes[i].name] = this.functionNodes[i]; } } if (this.subKernelNodes) { for (let i = 0; i < this.subKernelNodes.length; i++) { this.functionMap[this.subKernelNodes[i].name] = this.subKernelNodes[i]; } } if (this.nativeFunctions) { for (let i = 0; i < this.nativeFunctions.length; i++) { const nativeFunction = this.nativeFunctions[i]; this.nativeFunctionNames.push(nativeFunction.name); } } } addFunctionNode(functionNode) { this.functionMap[functionNode.name] = functionNode; if (functionNode.isRootKernel) { this.rootNode = functionNode; } } traceFunctionCalls(functionName, retList) { functionName = functionName || 'kernel'; retList = retList || []; if (this.nativeFunctionNames.indexOf(functionName) > -1) { if (retList.indexOf(functionName) === -1) { retList.push(functionName); } return retList; } const functionNode = this.functionMap[functionName]; if (functionNode) { const functionIndex = retList.indexOf(functionName); if (functionIndex === -1) { retList.push(functionName); functionNode.toString(); for (let i = 0; i < functionNode.calledFunctions.length; ++i) { this.traceFunctionCalls(functionNode.calledFunctions[i], retList); } } else { const dependantFunctionName = retList.splice(functionIndex, 1)[0]; retList.push(dependantFunctionName); } } return retList; } getPrototypeString(functionName) { return this.getPrototypes(functionName).join('\n'); } getPrototypes(functionName) { if (this.rootNode) { this.rootNode.toString(); } if (functionName) { return this.getPrototypesFromFunctionNames(this.traceFunctionCalls(functionName, []).reverse()); } return this.getPrototypesFromFunctionNames(Object.keys(this.functionMap)); } getStringFromFunctionNames(functionList) { const ret = []; for (let i = 0; i < functionList.length; ++i) { const node = this.functionMap[functionList[i]]; if (node) { ret.push(this.functionMap[functionList[i]].toString()); } } return ret.join('\n'); } getPrototypesFromFunctionNames(functionList) { const ret = []; for (let i = 0; i < functionList.length; ++i) { const functionName = functionList[i]; const functionIndex = this.nativeFunctionNames.indexOf(functionName); if (functionIndex > -1) { ret.push(this.nativeFunctions[functionIndex].source); continue; } const node = this.functionMap[functionName]; if (node) { ret.push(node.toString()); } } return ret; } toJSON() { return this.traceFunctionCalls(this.rootNode.name).reverse().map(name => { const nativeIndex = this.nativeFunctions.indexOf(name); if (nativeIndex > -1) { return { name, source: this.nativeFunctions[nativeIndex].source }; } else if (this.functionMap[name]) { return this.functionMap[name].toJSON(); } else { throw new Error(`function ${ name } not found`); } }); } fromJSON(jsonFunctionNodes, FunctionNode) { this.functionMap = {}; for (let i = 0; i < jsonFunctionNodes.length; i++) { const jsonFunctionNode = jsonFunctionNodes[i]; this.functionMap[jsonFunctionNode.settings.name] = new FunctionNode(jsonFunctionNode.ast, jsonFunctionNode.settings); } return this; } getString(functionName) { if (functionName) { return this.getStringFromFunctionNames(this.traceFunctionCalls(functionName).reverse()); } return this.getStringFromFunctionNames(Object.keys(this.functionMap)); } lookupArgumentType(argumentName, requestingNode) { const index = requestingNode.argumentNames.indexOf(argumentName); if (index === -1) return null; if (this.lookupChain.length === 0) return null; let link = this.lookupChain[this.lookupChain.length - 1 - this.argumentChain.length]; if (!link) return null; const { ast, requestingNode: parentRequestingNode } = link; if (ast.arguments.length === 0) return null; const usedVariable = ast.arguments[index]; if (!usedVariable) return null; this.argumentChain.push(argumentName); const type = parentRequestingNode.getType(usedVariable); this.argumentChain.pop(); return type; } lookupReturnType(functionName, ast, requestingNode) { if (ast.type !== 'CallExpression') { throw new Error(`expected ast type of "CallExpression", but is ${ ast.type }`); } if (this._isNativeFunction(functionName)) { return this._lookupNativeFunctionReturnType(functionName); } else if (this._isFunction(functionName)) { const node = this._getFunction(functionName); if (node.returnType) { return node.returnType; } else { this.lookupChain.push({ name: requestingNode.name, ast, requestingNode }); const type = node.getType(node.getJsAST()); this.lookupChain.pop(); return node.returnType = type; } } return null; } _getFunction(functionName) { if (!this._isFunction(functionName)) { new Error(`Function ${functionName} not found`); } return this.functionMap[functionName]; } _isFunction(functionName) { return Boolean(this.functionMap[functionName]); } _getNativeFunction(functionName) { for (let i = 0; i < this.nativeFunctions.length; i++) { if (this.nativeFunctions[i].name === functionName) return this.nativeFunctions[i]; } return null; } _isNativeFunction(functionName) { return Boolean(this._getNativeFunction(functionName)); } _lookupNativeFunctionReturnType(functionName) { let nativeFunction = this._getNativeFunction(functionName); if (nativeFunction) { return nativeFunction.returnType; } throw new Error(`Native function ${ functionName } not found`); } lookupFunctionArgumentTypes(functionName) { if (this._isNativeFunction(functionName)) { return this._getNativeFunction(functionName).argumentTypes; } else if (this._isFunction(functionName)) { return this._getFunction(functionName).argumentTypes; } return null; } lookupFunctionArgumentName(functionName, argumentIndex) { return this._getFunction(functionName).argumentNames[argumentIndex]; } lookupFunctionArgumentBitRatio(functionName, argumentName) { if (!this._isFunction(functionName)) { throw new Error('function not found'); } if (this.rootNode.name === functionName) { const i = this.rootNode.argumentNames.indexOf(argumentName); if (i !== -1) { return this.rootNode.argumentBitRatios[i]; } else { throw new Error('argument bit ratio not found'); } } else { const node = this._getFunction(functionName); const argumentSynonym = node.argumentSynonym[node.synonymIndex]; if (!argumentSynonym) { throw new Error('argument synonym not found'); } return this.lookupFunctionArgumentBitRatio(argumentSynonym.functionName, argumentSynonym.argumentName); } } assignArgumentType(functionName, i, argumentType, requestingNode) { if (!this._isFunction(functionName)) return; this._getFunction(functionName).argumentTypes[i] = argumentType; } trackArgumentSynonym(functionName, argumentName, calleeFunctionName, argumentIndex) { if (!this._isFunction(calleeFunctionName)) return; const node = this._getFunction(calleeFunctionName); if (!node.argumentSynonym) { node.argumentSynonym = {}; } const calleeArgumentName = node.argumentNames[argumentIndex]; if (!node.argumentSynonym[calleeArgumentName]) { node.argumentSynonym[calleeArgumentName] = {}; } node.synonymIndex++; node.argumentSynonym[node.synonymIndex] = { functionName, argumentName, calleeArgumentName, calleeFunctionName, }; } lookupArgumentSynonym(originFunctionName, functionName, argumentName) { if (originFunctionName === functionName) return argumentName; if (!this._isFunction(functionName)) return null; const node = this._getFunction(functionName); const argumentSynonym = node.argumentSynonym[node.synonymUseIndex]; if (!argumentSynonym) return null; if (argumentSynonym.calleeArgumentName !== argumentName) return null; node.synonymUseIndex++; if (originFunctionName !== functionName) { return this.lookupArgumentSynonym(originFunctionName, argumentSynonym.functionName, argumentSynonym.argumentName); } return argumentSynonym.argumentName; } trackFunctionCall(functionName, calleeFunctionName) { if (!this.functionNodeDependencies[functionName]) { this.functionNodeDependencies[functionName] = new Set(); } this.functionNodeDependencies[functionName].add(calleeFunctionName); } getKernelResultType() { return this.rootNode.getType(this.rootNode.ast); } getReturnTypes() { const result = { [this.rootNode.name]: this.rootNode.getType(this.rootNode.ast), }; const list = this.traceFunctionCalls(this.rootNode.name); for (let i = 0; i < list.length; i++) { const functionName = list[i]; const functionNode = this.functionMap[functionName]; result[functionName] = functionNode.getType(functionNode.ast); } return result; } } module.exports = { FunctionBuilder }; },{}],9:[function(require,module,exports){ const { utils } = require('../utils'); const acorn = require('acorn'); class FunctionNode { constructor(source, settings) { if (!source) { throw new Error('source parameter is missing'); } settings = settings || {}; this.source = source; this.name = typeof source === 'string' ? settings.isRootKernel ? 'kernel' : (settings.name || utils.getFunctionNameFromString(source)) : null; this.calledFunctions = []; this.calledFunctionsArguments = {}; this.constants = {}; this.constantTypes = {}; this.constantBitRatios = {}; this.isRootKernel = false; this.isSubKernel = false; this.debug = null; this.declarations = {}; this.states = []; this.lookupReturnType = null; this.lookupArgumentType = null; this.lookupFunctionArgumentTypes = null; this.lookupFunctionArgumentBitRatio = null; this.triggerImplyArgumentType = null; this.triggerTrackArgumentSynonym = null; this.lookupArgumentSynonym = null; this.onNestedFunction = null; this.onFunctionCall = null; this.optimizeFloatMemory = null; this.precision = null; this.loopMaxIterations = null; this.argumentNames = (typeof this.source === 'string' ? utils.getArgumentNamesFromString(this.source) : null); this.argumentTypes = []; this.argumentSizes = []; this.argumentBitRatios = null; this.returnType = null; this.output = []; this.plugins = null; this.leadingReturnStatement = null; this.followingReturnStatement = null; if (settings) { for (const p in settings) { if (!settings.hasOwnProperty(p)) continue; if (!this.hasOwnProperty(p)) continue; this[p] = settings[p]; } } this.synonymIndex = -1; this.synonymUseIndex = 0; this.argumentSynonym = {}; this.literalTypes = {}; if (this.isRootKernel && !this.returnType) { this.returnType = 'Number'; } this.validate(); this._string = null; this._internalVariableNames = {}; } validate() { if (typeof this.source !== 'string') { throw new Error('this.source not a string'); } if (!utils.isFunctionString(this.source)) { throw new Error('this.source not a function string'); } if (!this.name) { throw new Error('this.name could not be set'); } if (this.argumentTypes.length > 0 && this.argumentTypes.length !== this.argumentNames.length) { throw new Error(`argumentTypes count of ${ this.argumentTypes.length } exceeds ${ this.argumentNames.length }`); } if (this.output.length < 1) { throw new Error('this.output is not big enough'); } } isIdentifierConstant(name) { if (!this.constants) return false; return this.constants.hasOwnProperty(name); } isInput(argumentName) { return this.argumentTypes[this.argumentNames.indexOf(argumentName)] === 'Input'; } pushState(state) { this.states.push(state); } popState(state) { if (this.state !== state) { throw new Error(`Cannot popState ${ state } when in ${ this.state }`); } this.states.pop(); } isState(state) { return this.state === state; } get state() { return this.states[this.states.length - 1]; } astMemberExpressionUnroll(ast) { if (ast.type === 'Identifier') { return ast.name; } else if (ast.type === 'ThisExpression') { return 'this'; } if (ast.type === 'MemberExpression') { if (ast.object && ast.property) { if (ast.object.hasOwnProperty('name') && ast.object.name[0] === '_') { return this.astMemberExpressionUnroll(ast.property); } return ( this.astMemberExpressionUnroll(ast.object) + '.' + this.astMemberExpressionUnroll(ast.property) ); } } if (ast.hasOwnProperty('expressions')) { const firstExpression = ast.expressions[0]; if (firstExpression.type === 'Literal' && firstExpression.value === 0 && ast.expressions.length === 2) { return this.astMemberExpressionUnroll(ast.expressions[1]); } } throw this.astErrorOutput('Unknown astMemberExpressionUnroll', ast); } getJsAST(inParser) { if (typeof this.source === 'object') { return this.ast = this.source; } if (this.ast) { return this.ast; } inParser = inParser || acorn; if (inParser === null) { throw 'Missing JS to AST parser'; } const ast = Object.freeze(inParser.parse(`const parser_${ this.name } = ${ this.source };`, { locations: true })); const functionAST = ast.body[0].declarations[0].init; if (!ast) { throw new Error('Failed to parse JS code'); } return this.ast = functionAST; } getVariableType(name) { let type = null; const argumentIndex = this.argumentNames.indexOf(name); if (argumentIndex === -1) { if (this.declarations[name]) { return this.declarations[name].type; } } else { const argumentType = this.argumentTypes[argumentIndex]; if (argumentType) { type = argumentType; } else if (this.lookupArgumentType) { type = this.argumentTypes[argumentIndex] = this.lookupArgumentType(name, this); } } if (!type) { } return type; } getConstantType(constantName) { if (this.constantTypes[constantName]) { const type = this.constantTypes[constantName]; if (type === 'Float') { return 'Number'; } else { return type; } } throw new Error(`Type for constant "${ constantName }" not declared`); } getKernelArgumentName(name) { if (!this.lookupArgumentSynonym) return null; const argumentIndex = this.argumentNames.indexOf(name); if (argumentIndex === -1) return null; return this.lookupArgumentSynonym('kernel', this.name, name); } toString() { if (this._string) return this._string; return this._string = this.astGeneric(this.getJsAST(), []).join('').trim(); } toJSON() { const settings = { source: this.source, name: this.name, constants: this.constants, constantTypes: this.constantTypes, isRootKernel: this.isRootKernel, isSubKernel: this.isSubKernel, debug: this.debug, output: this.output, loopMaxIterations: this.loopMaxIterations, argumentNames: this.argumentNames, argumentTypes: this.argumentTypes, argumentSizes: this.argumentSizes, returnType: this.returnType, leadingReturnStatement: this.leadingReturnStatement, followingReturnStatement: this.followingReturnStatement, }; return { ast: this.ast, settings }; } getType(ast) { if (Array.isArray(ast)) { return this.getType(ast[ast.length - 1]); } switch (ast.type) { case 'BlockStatement': return this.getType(ast.body); case 'ArrayExpression': return `Array(${ ast.elements.length })`; case 'Literal': const literalKey = `${ast.start},${ast.end}`; if (this.literalTypes[literalKey]) { return this.literalTypes[literalKey]; } if (Number.isInteger(ast.value)) { return 'LiteralInteger'; } else if (ast.value === true || ast.value === false) { return 'Boolean'; } else { return 'Number'; } case 'AssignmentExpression': return this.getType(ast.left); case 'CallExpression': if (this.isAstMathFunction(ast)) { return 'Number'; } if (!ast.callee || !ast.callee.name) { if (ast.callee.type === 'SequenceExpression' && ast.callee.expressions[ast.callee.expressions.length - 1].property.name) { return this.lookupReturnType(ast.callee.expressions[ast.callee.expressions.length - 1].property.name, ast, this); } throw this.astErrorOutput('Unknown call expression', ast); } if (ast.callee && ast.callee.name) { return this.lookupReturnType(ast.callee.name, ast, this); } throw this.astErrorOutput(`Unhandled getType Type "${ ast.type }"`, ast); case 'BinaryExpression': switch (ast.operator) { case '%': return 'Number'; case '>': case '<': return 'Boolean'; } const type = this.getType(ast.left); return typeLookupMap[type] || type; case 'UpdateExpression': return this.getType(ast.argument); case 'UnaryExpression': return this.getType(ast.argument); case 'VariableDeclaration': return this.getType(ast.declarations[0]); case 'VariableDeclarator': return this.getType(ast.id); case 'Identifier': if (this.isAstVariable(ast)) { const signature = this.getVariableSignature(ast); if (signature === 'value') { if (this.argumentNames.indexOf(ast.name) > -1) { return this.getVariableType(ast.name); } else if (this.declarations[ast.name]) { return this.declarations[ast.name].type; } } } if (ast.name === 'Infinity') { return 'Number'; } const origin = this.findIdentifierOrigin(ast); if (origin && origin.init) { return this.getType(origin.init); } return null; case 'ReturnStatement': return this.getType(ast.argument); case 'MemberExpression': if (this.isAstMathFunction(ast)) { switch (ast.property.name) { case 'ceil': return 'Integer'; case 'floor': return 'Integer'; case 'round': return 'Integer'; } return 'Number'; } if (this.isAstVariable(ast)) { const variableSignature = this.getVariableSignature(ast); switch (variableSignature) { case 'value[]': return typeLookupMap[this.getVariableType(ast.object.name)]; case 'value[][]': return typeLookupMap[this.getVariableType(ast.object.object.name)]; case 'value[][][]': return typeLookupMap[this.getVariableType(ast.object.object.object.name)]; case 'value[][][][]': return typeLookupMap[this.getVariableType(ast.object.object.object.object.name)]; case 'this.thread.value': return 'Integer'; case 'this.output.value': return 'LiteralInteger'; case 'this.constants.value': return this.getConstantType(ast.property.name); case 'this.constants.value[]': return typeLookupMap[this.getConstantType(ast.object.property.name)]; case 'this.constants.value[][]': return typeLookupMap[this.getConstantType(ast.object.object.property.name)]; case 'this.constants.value[][][]': return typeLookupMap[this.getConstantType(ast.object.object.object.property.name)]; case 'this.constants.value[][][][]': return typeLookupMap[this.getConstantType(ast.object.object.object.object.property.name)]; case 'fn()[]': return typeLookupMap[this.getType(ast.object)]; case 'fn()[][]': return typeLookupMap[this.getType(ast.object)]; case 'fn()[][][]': return typeLookupMap[this.getType(ast.object)]; case 'value.value': if (this.isAstMathVariable(ast)) { return 'Number'; } switch (ast.property.name) { case 'r': return typeLookupMap[this.getVariableType(ast.object.name)]; case 'g': return typeLookupMap[this.getVariableType(ast.object.name)]; case 'b': return typeLookupMap[this.getVariableType(ast.object.name)]; case 'a': return typeLookupMap[this.getVariableType(ast.object.name)]; } case '[][]': return 'Number'; } throw this.astErrorOutput('Unhandled getType MemberExpression', ast); } throw this.astErrorOutput('Unhandled getType MemberExpression', ast); case 'ConditionalExpression': return this.getType(ast.consequent); case 'FunctionDeclaration': case 'FunctionExpression': const lastReturn = this.findLastReturn(ast.body); if (lastReturn) { return this.getType(lastReturn); } return null; case 'IfStatement': return this.getType(ast.consequent); default: throw this.astErrorOutput(`Unhandled getType Type "${ ast.type }"`, ast); } } isAstMathVariable(ast) { const mathProperties = [ 'E', 'PI', 'SQRT2', 'SQRT1_2', 'LN2', 'LN10', 'LOG2E', 'LOG10E', ]; return ast.type === 'MemberExpression' && ast.object && ast.object.type === 'Identifier' && ast.object.name === 'Math' && ast.property && ast.property.type === 'Identifier' && mathProperties.indexOf(ast.property.name) > -1; } isAstMathFunction(ast) { const mathFunctions = [ 'abs', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'exp', 'floor', 'log', 'log2', 'max', 'min', 'pow', 'random', 'round', 'sign', 'sin', 'sqrt', 'tan', ]; return ast.type === 'CallExpression' && ast.callee && ast.callee.type === 'MemberExpression' && ast.callee.object && ast.callee.object.type === 'Identifier' && ast.callee.object.name === 'Math' && ast.callee.property && ast.callee.property.type === 'Identifier' && mathFunctions.indexOf(ast.callee.property.name) > -1; } isAstVariable(ast) { return ast.type === 'Identifier' || ast.type === 'MemberExpression'; } isSafe(ast) { return this.isSafeDependencies(this.getDependencies(ast)); } isSafeDependencies(dependencies) { return dependencies && dependencies.every ? dependencies.every(dependency => dependency.isSafe) : true; } getDependencies(ast, dependencies, isNotSafe) { if (!dependencies) { dependencies = []; } if (!ast) return null; if (Array.isArray(ast)) { for (let i = 0; i < ast.length; i++) { this.getDependencies(ast[i], dependencies, isNotSafe); } return dependencies; } switch (ast.type) { case 'Literal': dependencies.push({ origin: 'literal', value: ast.value, isSafe: isNotSafe === true ? false : ast.value > -Infinity && ast.value < Infinity && !isNaN(ast.value) }); break; case 'VariableDeclarator': return this.getDependencies(ast.init, dependencies, isNotSafe); case 'Identifier': if (this.declarations[ast.name]) { dependencies.push({ name: ast.name, origin: 'declaration', isSafe: isNotSafe ? false : this.isSafeDependencies(this.declarations[ast.name].dependencies), }); } else if (this.argumentNames.indexOf(ast.name) > -1) { dependencies.push({ name: ast.name, origin: 'argument', isSafe: false, }); } break; case 'FunctionDeclaration': return this.getDependencies(ast.body.body[ast.body.body.length - 1], dependencies, isNotSafe); case 'ReturnStatement': return this.getDependencies(ast.argument, dependencies); case 'BinaryExpression': isNotSafe = (ast.operator === '/' || ast.operator === '*'); this.getDependencies(ast.left, dependencies, isNotSafe); this.getDependencies(ast.right, dependencies, isNotSafe); return dependencies; case 'UnaryExpression': case 'UpdateExpression': return this.getDependencies(ast.argument, dependencies, isNotSafe); case 'VariableDeclaration': return this.getDependencies(ast.declarations, dependencies, isNotSafe); case 'ArrayExpression': dependencies.push({ origin: 'declaration', isSafe: true, }); return dependencies; case 'CallExpression': dependencies.push({ origin: 'function', isSafe: true, }); return dependencies; case 'MemberExpression': const details = this.getMemberExpressionDetails(ast); if (details) { return details.type; } default: throw this.astErrorOutput(`Unhandled type ${ ast.type } in getAllVariables`, ast); } return dependencies; } getVariableSignature(ast) { if (!this.isAstVariable(ast)) { throw new Error(`ast of type "${ ast.type }" is not a variable signature`); } if (ast.type === 'Identifier') { return 'value'; } const signature = []; while (true) { if (!ast) break; if (ast.computed) { signature.push('[]'); } else if (ast.type === 'ThisExpression') { signature.unshift('this'); } else if (ast.property && ast.property.name) { if ( ast.property.name === 'x' || ast.property.name === 'y' || ast.property.name === 'z' ) { signature.unshift('.value'); } else if ( ast.property.name === 'constants' || ast.property.name === 'thread' || ast.property.name === 'output' ) { signature.unshift('.' + ast.property.name); } else { signature.unshift('.value'); } } else if (ast.name) { signature.unshift('value'); } else if (ast.callee && ast.callee.name) { signature.unshift('fn()'); } else if (ast.elements) { signature.unshift('[]'); } else { signature.unshift('unknown'); } ast = ast.object; } const signatureString = signature.join(''); const allowedExpressions = [ 'value', 'value[]', 'value[][]', 'value[][][]', 'value[][][][]', 'value.value', 'this.thread.value', 'this.output.value', 'this.constants.value', 'this.constants.value[]', 'this.constants.value[][]', 'this.constants.value[][][]', 'this.constants.value[][][][]', 'fn()[]', 'fn()[][]', 'fn()[][][]', '[][]', ]; if (allowedExpressions.indexOf(signatureString) > -1) { return signatureString; } return null; } build() { return this.toString().length > 0; } astGeneric(ast, retArr) { if (ast === null) { throw this.astErrorOutput('NULL ast', ast); } else { if (Array.isArray(ast)) { for (let i = 0; i < ast.length; i++) { this.astGeneric(ast[i], retArr); } return retArr; } switch (ast.type) { case 'FunctionDeclaration': return this.astFunctionDeclaration(ast, retArr); case 'FunctionExpression': return this.astFunctionExpression(ast, retArr); case 'ReturnStatement': return this.astReturnStatement(ast, retArr); case 'Literal': return this.astLiteral(ast, retArr); case 'BinaryExpression': return this.astBinaryExpression(ast, retArr); case 'Identifier': return this.astIdentifierExpression(ast, retArr); case 'AssignmentExpression': return this.astAssignmentExpression(ast, retArr); case 'ExpressionStatement': return this.astExpressionStatement(ast, retArr); case 'EmptyStatement': return this.astEmptyStatement(ast, retArr); case 'BlockStatement': return this.astBlockStatement(ast, retArr); case 'IfStatement': return this.astIfStatement(ast, retArr); case 'BreakStatement': return this.astBreakStatement(ast, retArr); case 'ContinueStatement': return this.astContinueStatement(ast, retArr); case 'ForStatement': return this.astForStatement(ast, retArr); case 'WhileStatement': return this.astWhileStatement(ast, retArr); case 'DoWhileStatement': return this.astDoWhileStatement(ast, retArr); case 'VariableDeclaration': return this.astVariableDeclaration(ast, retArr); case 'VariableDeclarator': return this.astVariableDeclarator(ast, retArr); case 'ThisExpression': return this.astThisExpression(ast, retArr); case 'SequenceExpression': return this.astSequenceExpression(ast, retArr); case 'UnaryExpression': return this.astUnaryExpression(ast, retArr); case 'UpdateExpression': return this.astUpdateExpression(ast, retArr); case 'LogicalExpression': return this.astLogicalExpression(ast, retArr); case 'MemberExpression': return this.astMemberExpression(ast, retArr); case 'CallExpression': return this.astCallExpression(ast, retArr); case 'ArrayExpression': return this.astArrayExpression(ast, retArr); case 'DebuggerStatement': return this.astDebuggerStatement(ast, retArr); case 'ConditionalExpression': return this.astConditionalExpression(ast, retArr); } throw this.astErrorOutput('Unknown ast type : ' + ast.type, ast); } } astErrorOutput(error, ast) { if (typeof this.source !== 'string') { return new Error(error); } const debugString = utils.getAstString(this.source, ast); const leadingSource = this.source.substr(ast.start); const splitLines = leadingSource.split(/\n/); const lineBefore = splitLines.length > 0 ? splitLines[splitLines.length - 1] : 0; return new Error(`${error} on line ${ splitLines.length }, position ${ lineBefore.length }:\n ${ debugString }`); } astDebuggerStatement(arrNode, retArr) { return retArr; } astConditionalExpression(ast, retArr) { if (ast.type !== 'ConditionalExpression') { throw this.astErrorOutput('Not a conditional expression', ast); } retArr.push('('); this.astGeneric(ast.test, retArr); retArr.push('?'); this.astGeneric(ast.consequent, retArr); retArr.push(':'); this.astGeneric(ast.alternate, retArr); retArr.push(')'); return retArr; } astFunctionDeclaration(ast, retArr) { if (this.onNestedFunction) { let returnType = this.getType(ast); if (returnType === 'LiteralInteger') { returnType = 'Number'; } this.onNestedFunction(utils.getAstString(this.source, ast), returnType); } return retArr; } astFunctionExpression(ast, retArr) { return retArr; } astReturnStatement(ast, retArr) { return retArr; } astLiteral(ast, retArr) { this.literalTypes[`${ast.start},${ast.end}`] = 'Number'; return retArr; } astBinaryExpression(ast, retArr) { return retArr; } astIdentifierExpression(ast, retArr) { return retArr; } astAssignmentExpression(ast, retArr) { return retArr; } astExpressionStatement(esNode, retArr) { this.astGeneric(esNode.expression, retArr); retArr.push(';'); return retArr; } astEmptyStatement(eNode, retArr) { return retArr; } astBlockStatement(ast, retArr) { return retArr; } astIfStatement(ast, retArr) { return retArr; } astBreakStatement(brNode, retArr) { retArr.push('break;'); return retArr; } astContinueStatement(crNode, retArr) { retArr.push('continue;\n'); return retArr; } astForStatement(ast, retArr) { return retArr; } astWhileStatement(ast, retArr) { return retArr; } astDoWhileStatement(ast, retArr) { return retArr; } astVariableDeclaration(varDecNode, retArr) { const declarations = varDecNode.declarations; if (!declarations || !declarations[0] || !declarations[0].init) { throw this.astErrorOutput('Unexpected expression', varDecNode); } const result = []; const firstDeclaration = declarations[0]; const init = firstDeclaration.init; let type = this.isState('in-for-loop-init') ? 'Integer' : this.getType(init); if (type === 'LiteralInteger') { type = 'Number'; } const markupType = typeMap[type]; if (!markupType) { throw this.astErrorOutput(`Markup type ${ markupType } not handled`, varDecNode); } let dependencies = this.getDependencies(firstDeclaration.init); this.declarations[firstDeclaration.id.name] = Object.freeze({ type, dependencies, isSafe: dependencies.every(dependency => dependency.isSafe) }); const initResult = [`${type} user_${firstDeclaration.id.name}=`]; this.astGeneric(init, initResult); result.push(initResult.join('')); for (let i = 1; i < declarations.length; i++) { const declaration = declarations[i]; dependencies = this.getDependencies(declaration); this.declarations[declaration.id.name] = Object.freeze({ type, dependencies, isSafe: false }); this.astGeneric(declaration, result); } retArr.push(retArr, result.join(',')); retArr.push(';'); return retArr; } astVariableDeclarator(iVarDecNode, retArr) { this.astGeneric(iVarDecNode.id, retArr); if (iVarDecNode.init !== null) { retArr.push('='); this.astGeneric(iVarDecNode.init, retArr); } return retArr; } astThisExpression(ast, retArr) { return retArr; } astSequenceExpression(sNode, retArr) { for (let i = 0; i < sNode.expressions.length; i++) { if (i > 0) { retArr.push(','); } this.astGeneric(sNode.expressions, retArr); } return retArr; } astUnaryExpression(uNode, retArr) { if (uNode.prefix) { retArr.push(uNode.operator); this.astGeneric(uNode.argument, retArr); } else { this.astGeneric(uNode.argument, retArr); retArr.push(uNode.operator); } return retArr; } astUpdateExpression(uNode, retArr) { if (uNode.prefix) { retArr.push(uNode.operator); this.astGeneric(uNode.argument, retArr); } else { this.astGeneric(uNode.argument, retArr); retArr.push(uNode.operator); } return retArr; } astLogicalExpression(logNode, retArr) { retArr.push('('); this.astGeneric(logNode.left, retArr); retArr.push(logNode.operator); this.astGeneric(logNode.right, retArr); retArr.push(')'); return retArr; } astMemberExpression(ast, retArr) { return retArr; } astCallExpression(ast, retArr) { return retArr; } astArrayExpression(ast, retArr) { return retArr; } getMemberExpressionDetails(ast) { if (ast.type !== 'MemberExpression') { throw this.astErrorOutput(`Expression ${ ast.type } not a MemberExpression`, ast); } let name = null; let type = null; const variableSignature = this.getVariableSignature(ast); switch (variableSignature) { case 'value': return null; case 'this.thread.value': case 'this.output.value': return { signature: variableSignature, type: 'Integer', name: ast.property.name }; case 'value[]': if (typeof ast.object.name !== 'string') { throw this.astErrorOutput('Unexpected expression', ast); } name = ast.object.name; return { name, origin: 'user', signature: variableSignature, type: this.getVariableType(name), xProperty: ast.property }; case 'value[][]': if (typeof ast.object.object.name !== 'string') { throw this.astErrorOutput('Unexpected expression', ast); } name = ast.object.object.name; return { name, origin: 'user', signature: variableSignature, type: this.getVariableType(name), yProperty: ast.object.property, xProperty: ast.property, }; case 'value[][][]': if (typeof ast.object.object.object.name !== 'string') { throw this.astErrorOutput('Unexpected expression', ast); } name = ast.object.object.object.name; return { name, origin: 'user', signature: variableSignature, type: this.getVariableType(name), zProperty: ast.object.object.property, yProperty: ast.object.property, xProperty: ast.property, }; case 'value[][][][]': if (typeof ast.object.object.object.object.name !== 'string') { throw this.astErrorOutput('Unexpected expression', ast); } name = ast.object.object.object.object.name; return { name, origin: 'user', signature: variableSignature, type: this.getVariableType(name), zProperty: ast.object.object.property, yProperty: ast.object.property, xProperty: ast.property, }; case 'value.value': if (typeof ast.property.name !== 'string') { throw this.astErrorOutput('Unexpected expression', ast); } if (this.isAstMathVariable(ast)) { name = ast.property.name; return { name, origin: 'Math', type: 'Number', signature: variableSignature, }; } switch (ast.property.name) { case 'r': case 'g': case 'b': case 'a': name = ast.object.name; return { name, property: ast.property.name, origin: 'user', signature: variableSignature, type: 'Number' }; default: throw this.astErrorOutput('Unexpected expression', ast); } case 'this.constants.value': if (typeof ast.property.name !== 'string') { throw this.astErrorOutput('Unexpected expression', ast); } name = ast.property.name; type = this.getConstantType(name); if (!type) { throw this.astErrorOutput('Constant has no type', ast); } return { name, type, origin: 'constants', signature: variableSignature, }; case 'this.constants.value[]': if (typeof ast.object.property.name !== 'string') { throw this.astErrorOutput('Unexpected expression', ast); } name = ast.object.property.name; type = this.getConstantType(name); if (!type) { throw this.astErrorOutput('Constant has no type', ast); } return { name, type, origin: 'constants', signature: variableSignature, xProperty: ast.property, }; case 'this.constants.value[][]': { if (typeof ast.object.object.property.name !== 'string') { throw this.astErrorOutput('Unexpected expression', ast); } name = ast.object.object.property.name; type = this.getConstantType(name); if (!type) { throw this.astErrorOutput('Constant has no type', ast); } return { name, type, origin: 'constants', signature: variableSignature, yProperty: ast.object.property, xProperty: ast.property, }; } case 'this.constants.value[][][]': { if (typeof ast.object.object.object.property.name !== 'string') { throw this.astErrorOutput('Unexpected expression', ast); } name = ast.object.object.object.property.name; type = this.getConstantType(name); if (!type) { throw this.astErrorOutput('Constant has no type', ast); } return { name, type, origin: 'constants', signature: variableSignature, zProperty: ast.object.object.property, yProperty: ast.object.property, xProperty: ast.property, }; } case 'fn()[]': case '[][]': return { signature: variableSignature, property: ast.property, }; default: throw this.astErrorOutput('Unexpected expression', ast); } } findIdentifierOrigin(astToFind) { const stack = [this.ast]; while (stack.length > 0) { const atNode = stack[0]; if (atNode.type === 'VariableDeclarator' && atNode.id && atNode.id.name && atNode.id.name === astToFind.name) { return atNode; } stack.shift(); if (atNode.argument) { stack.push(atNode.argument); } else if (atNode.body) { stack.push(atNode.body); } else if (atNode.declarations) { stack.push(atNode.declarations); } else if (Array.isArray(atNode)) { for (let i = 0; i < atNode.length; i++) { stack.push(atNode[i]); } } } return null; } findLastReturn(ast) { const stack = [ast || this.ast]; while (stack.length > 0) { const atNode = stack.pop(); if (atNode.type === 'ReturnStatement') { return atNode; } if (atNode.argument) { stack.push(atNode.argument); } else if (atNode.body) { stack.push(atNode.body); } else if (atNode.declarations) { stack.push(atNode.declarations); } else if (Array.isArray(atNode)) { for (let i = 0; i < atNode.length; i++) { stack.push(atNode[i]); } } else if (atNode.consequent) { stack.push(atNode.consequent); } } return null; } getInternalVariableName(name) { if (!this._internalVariableNames.hasOwnProperty(name)) { this._internalVariableNames[name] = 0; } this._internalVariableNames[name]++; if (this._internalVariableNames[name] === 1) { return name; } return name + this._internalVariableNames[name]; } varWarn() { console.warn('var declarations are deprecated, weird things happen when falling back to CPU because var scope differs in javascript than in most languages. Use const or let'); } } const typeLookupMap = { 'Array': 'Number', 'Array(2)': 'Number', 'Array(3)': 'Number', 'Array(4)': 'Number', 'Array2D': 'Number', 'Array3D': 'Number', 'Input': 'Number', 'HTMLImage': 'Array(4)', 'HTMLImageArray': 'Array(4)', 'NumberTexture': 'Number', 'MemoryOptimizedNumberTexture': 'Number', 'ArrayTexture(1)': 'Number', 'ArrayTexture(2)': 'Array(2)', 'ArrayTexture(3)': 'Array(3)', 'ArrayTexture(4)': 'Array(4)', }; module.exports = { FunctionNode }; },{"../utils":69,"acorn":2}],10:[function(require,module,exports){ const { glWiretap } = require('gl-wiretap'); const { utils } = require('../../utils'); const { Texture } = require('../../texture'); function toStringWithoutUtils(fn) { return fn.toString().replace(/utils[.]/g, '/*utils.*/'); } function glKernelString(Kernel, args, originKernel, setupContextString, destroyContextString) { const context = glWiretap(originKernel.context, { useTrackablePrimitives: true }); const { source, canvas, output, pipeline, graphical, loopMaxIterations, constants, optimizeFloatMemory, precision, fixIntegerDivisionAccuracy, functions, nativeFunctions, subKernels, immutable, } = originKernel; const kernel = new Kernel(source, { canvas, context, output, pipeline, graphical, loopMaxIterations, constants, optimizeFloatMemory, precision, fixIntegerDivisionAccuracy, functions, nativeFunctions, subKernels, immutable, }); let result = []; context.setIndent(2); kernel.build.apply(kernel, args); result.push(context.toString()); context.reset(); kernel.kernelArguments.forEach(kernelArgument => { context.insertVariable(`uploadValue_${kernelArgument.name}`, kernelArgument.uploadValue); }); result.push('/** start of injected functions **/'); result.push(`function ${toStringWithoutUtils(utils.flattenTo)}`); result.push(`function ${toStringWithoutUtils(utils.flatten2dArrayTo)}`); result.push(`function ${toStringWithoutUtils(utils.flatten3dArrayTo)}`); result.push(`function ${toStringWithoutUtils(utils.isArray)}`); if (kernel.renderOutput === kernel.renderTexture) { result.push(Texture.toString()); result.push( ` const renderOutput = function ${ kernel.renderOutput.toString() .replace(`this.outputTexture`, 'null') .replace('this.texSize', `new Int32Array(${JSON.stringify(Array.from(kernel.texSize))})`) .replace('this.threadDim', `new Int32Array(${JSON.stringify(Array.from(kernel.threadDim))})`) .replace('this.output', `new Int32Array(${JSON.stringify(this.output)})`) .replace('this.context', 'gl') .replace('this.gpu', 'null') .replace('this.getReturnTextureType()', `'${kernel.getReturnTextureType()}'`) };` ); } else { result.push( ` const renderOutput = function ${kernel.renderOutput.toString() .replace('() {', '(pixels) {') .replace(' const pixels = this.readFloatPixelsToFloat32Array();\n', '') .replace('this.readPackedPixelsToFloat32Array()', 'new Float32Array(pixels.buffer)') .replace('this.output;', JSON.stringify(kernel.output) + ';') };` ); } kernel.kernelArguments.forEach(kernelArgument => { kernelArgument.context = originKernel.context; }); result.push('/** end of injected functions **/'); result.push(` return function (${kernel.kernelArguments.map(kernelArgument => kernelArgument.name).join(', ')}) {`); context.setIndent(4); kernel.run.apply(kernel, args); result.push('/** start setup uploads for kernel values **/'); kernel.kernelArguments.forEach(kernelArgument => { result.push(kernelArgument.getStringValueHandler()); }); result.push('/** end setup uploads for kernel values **/'); result.push(context.toString()); result.push(` ${destroyContextString ? '\n' + destroyContextString + ' ': ''}return renderOutput(${context.getReadPixelsVariableName});`); result.push(' };'); return `function kernel(context = null) { ${setupContextString ? setupContextString : ''}${result.join('\n')} }`; } module.exports = { glKernelString }; },{"../../texture":68,"../../utils":69,"gl-wiretap":1}],11:[function(require,module,exports){ const { Kernel } = require('../kernel'); const { Texture } = require('../../texture'); const { utils } = require('../../utils'); class GLKernel extends Kernel { static get mode() { return 'gpu'; } static getIsFloatRead() { const kernelString = `function kernelFunction() { return 1; }`; const kernel = new this(kernelString, { context: this.testContext, canvas: this.testCanvas, validate: false, output: [1], precision: 'single', floatOutputForce: true, returnType: 'Number' }); const result = kernel.run(); kernel.destroy(true); return result[0] === 1; } static getIsIntegerDivisionAccurate() { function kernelFunction(v1, v2) { return v1[this.thread.x] / v2[this.thread.x]; } const kernel = new this(kernelFunction.toString(), { context: this.testContext, canvas: this.testCanvas, validate: false, output: [2], returnType: 'Number', precision: 'unsigned', }); const result = kernel.run([6, 6030401], [3, 3991]); kernel.destroy(true); return result[0] === 2 && result[1] === 1511; } static get testCanvas() { throw new Error(`"testCanvas" not defined on ${ this.name }`); } static get testContext() { throw new Error(`"testContext" not defined on ${ this.name }`); } static get features() { throw new Error(`"features" not defined on ${ this.name }`); } static setupFeatureChecks() { throw new Error(`"setupFeatureChecks" not defined on ${ this.name }`); } setFixIntegerDivisionAccuracy(fix) { this.fixIntegerDivisionAccuracy = fix; return this; } setPrecision(flag) { this.precision = flag; return this; } setFloatOutputForce(flag) { this.floatOutputForce = flag; return this; } setFloatTextures(flag) { utils.warnDeprecated('method', 'setFloatTextures', 'setOptimizeFloatMemory'); this.floatTextures = flag; return this; } static nativeFunctionArguments(source) { const argumentTypes = []; const argumentNames = []; const states = []; const isStartingVariableName = /^[a-zA-Z_]/; const isVariableChar = /[a-zA-Z_0-9]/; let i = 0; let argumentName = null; let argumentType = null; while (i < source.length) { const char = source[i]; const nextChar = source[i + 1]; const state = states.length > 0 ? states[states.length - 1] : null; if (state === 'FUNCTION_ARGUMENTS' && char === '/' && nextChar === '*') { states.push('MULTI_LINE_COMMENT'); i += 2; continue; } else if (state === 'MULTI_LINE_COMMENT' && char === '*' && nextChar === '/') { states.pop(); i += 2; continue; } else if (state === 'FUNCTION_ARGUMENTS' && char === '/' && nextChar === '/') { states.push('COMMENT'); i += 2; continue; } else if (state === 'COMMENT' && char === '\n') { states.pop(); i++; continue; } else if (state === null && char === '(') { states.push('FUNCTION_ARGUMENTS'); i++; continue; } else if (state === 'FUNCTION_ARGUMENTS') { if (char === ')') { states.pop(); break; } if (char === 'f' && nextChar === 'l' && source[i + 2] === 'o' && source[i + 3] === 'a' && source[i + 4] === 't' && source[i + 5] === ' ') { states.push('DECLARE_VARIABLE'); argumentType = 'float'; argumentName = ''; i += 6; continue; } else if (char === 'i' && nextChar === 'n' && source[i + 2] === 't' && source[i + 3] === ' ') { states.push('DECLARE_VARIABLE'); argumentType = 'int'; argumentName = ''; i += 4; continue; } else if (char === 'v' && nextChar === 'e' && source[i + 2] === 'c' && source[i + 3] === '2' && source[i + 4] === ' ') { states.push('DECLARE_VARIABLE'); argumentType = 'vec2'; argumentName = ''; i += 5; continue; } else if (char === 'v' && nextChar === 'e' && source[i + 2] === 'c' && source[i + 3] === '3' && source[i + 4] === ' ') { states.push('DECLARE_VARIABLE'); argumentType = 'vec3'; argumentName = ''; i += 5; continue; } else if (char === 'v' && nextChar === 'e' && source[i + 2] === 'c' && source[i + 3] === '4' && source[i + 4] === ' ') { states.push('DECLARE_VARIABLE'); argumentType = 'vec4'; argumentName = ''; i += 5; continue; } } else if (state === 'DECLARE_VARIABLE') { if (argumentName === '') { if (char === ' ') { i++; continue; } if (!isStartingVariableName.test(char)) { throw new Error('variable name is not expected string'); } } argumentName += char; if (!isVariableChar.test(nextChar)) { states.pop(); argumentNames.push(argumentName); argumentTypes.push(typeMap[argumentType]); } } i++; } if (states.length > 0) { throw new Error('GLSL function was not parsable'); } return { argumentNames, argumentTypes, }; } static nativeFunctionReturnType(source) { return typeMap[source.match(/int|float|vec[2-4]/)[0]]; } static combineKernels(combinedKernel, lastKernel) { combinedKernel.apply(null, arguments); const { texSize, context, threadDim } = lastKernel.texSize; let result; if (lastKernel.precision === 'single') { const w = texSize[0]; const h = Math.ceil(texSize[1] / 4); result = new Float32Array(w * h * 4 * 4); context.readPixels(0, 0, w, h * 4, context.RGBA, context.FLOAT, result); } else { const bytes = new Uint8Array(texSize[0] * texSize[1] * 4); context.readPixels(0, 0, texSize[0], texSize[1], context.RGBA, context.UNSIGNED_BYTE, bytes); result = new Float32Array(bytes.buffer); } result = result.subarray(0, threadDim[0] * threadDim[1] * threadDim[2]); if (lastKernel.output.length === 1) { return result; } else if (lastKernel.output.length === 2) { return utils.splitArray(result, lastKernel.output[0]); } else if (lastKernel.output.length === 3) { const cube = utils.splitArray(result, lastKernel.output[0] * lastKernel.output[1]); return cube.map(function(x) { return utils.splitArray(x, lastKernel.output[0]); }); } } constructor(source, settings) { super(source, settings); this.texSize = null; this.floatOutputForce = null; this.fixIntegerDivisionAccuracy = null; this.translatedSource = null; this.renderStrategy = null; this.compiledFragmentShader = null; this.compiledVertexShader = null; } translateSource() { throw new Error(`"translateSource" not defined on ${this.constructor.name}`); } pickRenderStrategy(args) { if (this.graphical) return; if (this.precision === 'unsigned') { switch (this.returnType) { case 'LiteralInteger': case 'Float': case 'Number': case 'Integer': if (this.pipeline) { this.renderStrategy = renderStrategy.PackedTexture; this.renderOutput = this.renderTexture; } else if (this.output[2] > 0) { this.renderStrategy = renderStrategy.PackedPixelTo3DFloat; this.renderOutput = this.render3DPackedFloat; } else if (this.output[1] > 0) { this.renderStrategy = renderStrategy.PackedPixelTo2DFloat; this.renderOutput = this.render2DPackedFloat; } else { this.renderStrategy = renderStrategy.PackedPixelToFloat; this.renderOutput = this.renderPackedFloat; } return true; case 'Array(2)': case 'Array(3)': case 'Array(4)': this.onRequestFallback(args); return false; } } else if (this.precision === 'single') { if (this.pipeline) { this.renderStrategy = renderStrategy.FloatTexture; this.renderOutput = this.renderTexture; return true; } switch (this.returnType) { case 'LiteralInteger': case 'Float': case 'Number': case 'Integer': if (this.output[2] > 0) { if (this.optimizeFloatMemory) { this.renderStrategy = renderStrategy.MemoryOptimizedFloatPixelToMemoryOptimized3DFloat; this.renderOutput = this.renderMemoryOptimized3DFloat; } else { this.renderStrategy = renderStrategy.FloatPixelTo3DFloat; this.renderOutput = this.render3DFloat; } } else if (this.output[1] > 0) { if (this.optimizeFloatMemory) { this.renderStrategy = renderStrategy.MemoryOptimizedFloatPixelToMemoryOptimized2DFloat; this.renderOutput = this.renderMemoryOptimized2DFloat; } else { this.renderStrategy = renderStrategy.FloatPixelTo2DFloat; this.renderOutput = this.render2DFloat; } } else { if (this.optimizeFloatMemory) { this.renderStrategy = renderStrategy.MemoryOptimizedFloatPixelToMemoryOptimizedFloat; this.renderOutput = this.renderMemoryOptimizedFloat; } else { this.renderStrategy = renderStrategy.FloatPixelToFloat; this.renderOutput = this.renderFloat; } } return true; case 'Array(2)': if (this.output[2] > 0) { this.renderStrategy = renderStrategy.FloatPixelTo3DArray2; this.renderOutput = this.render3DArray2; } else if (this.output[1] > 0) { this.renderStrategy = renderStrategy.FloatPixelTo2DArray2; this.renderOutput = this.render2DArray2; } else { this.renderStrategy = renderStrategy.FloatPixelToArray2; this.renderOutput = this.renderArray2; } return true; case 'Array(3)': if (this.output[2] > 0) { this.renderStrategy = renderStrategy.FloatPixelTo3DArray3; this.renderOutput = this.render3DArray3; } else if (this.output[1] > 0) { this.renderStrategy = renderStrategy.FloatPixelTo2DArray3; this.renderOutput = this.render2DArray3; } else { this.renderStrategy = renderStrategy.FloatPixelToArray3; this.renderOutput = this.renderArray3; } return true; case 'Array(4)': if (this.output[2] > 0) { this.renderStrategy = renderStrategy.FloatPixelTo3DArray4; this.renderOutput = this.render3DArray4; } else if (this.output[1] > 0) { this.renderStrategy = renderStrategy.FloatPixelTo2DArray4; this.renderOutput = this.render2DArray4; } else { this.renderStrategy = renderStrategy.FloatPixelToArray4; this.renderOutput = this.renderArray4; } return true; } } else { throw new Error(`unhandled precision of "${this.precision}"`); } throw new Error(`unhandled return type "${this.returnType}"`); } getKernelString() { throw new Error(`abstract method call`); } getMainResultTexture() { switch (this.returnType) { case 'LiteralInteger': case 'Float': case 'Integer': case 'Number': return this.getMainResultNumberTexture(); case 'Array(2)': return this.getMainResultArray2Texture(); case 'Array(3)': return this.getMainResultArray3Texture(); case 'Array(4)': return this.getMainResultArray4Texture(); default: throw new Error(`unhandled returnType type ${ this.returnType }`); } } getMainResultKernelNumberTexture() { throw new Error(`abstract method call`); } getMainResultSubKernelNumberTexture() { throw new Error(`abstract method call`); } getMainResultKernelArray2Texture() { throw new Error(`abstract method call`); } getMainResultSubKernelArray2Texture() { throw new Error(`abstract method call`); } getMainResultKernelArray3Texture() { throw new Error(`abstract method call`); } getMainResultSubKernelArray3Texture() { throw new Error(`abstract method call`); } getMainResultKernelArray4Texture() { throw new Error(`abstract method call`); } getMainResultSubKernelArray4Texture() { throw new Error(`abstract method call`); } getMainResultGraphical() { throw new Error(`abstract method call`); } getMainResultMemoryOptimizedFloats() { throw new Error(`abstract method call`); } getMainResultPackedPixels() { throw new Error(`abstract method call`); } getMainResultString() { if (this.graphical) { return this.getMainResultGraphical(); } else if (this.precision === 'single') { if (this.optimizeFloatMemory) { return this.getMainResultMemoryOptimizedFloats(); } return this.getMainResultTexture(); } else { return this.getMainResultPackedPixels(); } } getMainResultNumberTexture() { return utils.linesToString(this.getMainResultKernelNumberTexture()) + utils.linesToString(this.getMainResultSubKernelNumberTexture()); } getMainResultArray2Texture() { return utils.linesToString(this.getMainResultKernelArray2Texture()) + utils.linesToString(this.getMainResultSubKernelArray2Texture()); } getMainResultArray3Texture() { return utils.linesToString(this.getMainResultKernelArray3Texture()) + utils.linesToString(this.getMainResultSubKernelArray3Texture()); } getMainResultArray4Texture() { return utils.linesToString(this.getMainResultKernelArray4Texture()) + utils.linesToString(this.getMainResultSubKernelArray4Texture()); } getReturnTextureType() { if (this.graphical) { return 'ArrayTexture(4)'; } if (this.precision === 'single') { switch (this.returnType) { case 'Float': case 'Number': case 'Integer': if (this.optimizeFloatMemory) { return 'MemoryOptimizedNumberTexture'; } else { return 'ArrayTexture(1)'; } case 'Array(2)': return 'ArrayTexture(2)'; case 'Array(3)': return 'ArrayTexture(3)'; case 'Array(4)': return 'ArrayTexture(4)'; default: throw new Error(`unsupported returnType ${this.returnType}`); } } else { switch (this.returnType) { case 'Float': case 'Number': case 'Integer': return 'NumberTexture'; case 'Array(2)': case 'Array(3)': case 'Array(4)': default: throw new Error(`unsupported returnType ${ this.returnType }`); } } } renderTexture() { return new Texture({ texture: this.outputTexture, size: this.texSize, dimensions: this.threadDim, output: this.output, context: this.context, gpu: this.gpu, type: this.getReturnTextureType(), }); } readPackedPixelsToUint8Array() { if (this.precision !== 'unsigned') throw new Error('Requires this.precision to be "unsigned"'); const { texSize, context: gl } = this; const result = new Uint8Array(texSize[0] * texSize[1] * 4); gl.readPixels(0, 0, texSize[0], texSize[1], gl.RGBA, gl.UNSIGNED_BYTE, result); return result; } readPackedPixelsToFloat32Array() { return new Float32Array(this.readPackedPixelsToUint8Array().buffer); } readFloatPixelsToFloat32Array() { if (this.precision !== 'single') throw new Error('Requires this.precision to be "single"'); const { texSize, context: gl } = this; const w = texSize[0]; const h = texSize[1]; const result = new Float32Array(w * h * 4); gl.readPixels(0, 0, w, h, gl.RGBA, gl.FLOAT, result); return result; } readMemoryOptimizedFloatPixelsToFloat32Array() { if (this.precision !== 'single') throw new Error('Requires this.precision to be "single"'); const { texSize, context: gl } = this; const w = texSize[0]; const h = texSize[1]; const result = new Float32Array(w * h * 4); gl.readPixels(0, 0, w, h, gl.RGBA, gl.FLOAT, result); return result; } renderPackedFloat() { const [xMax] = this.output; return this.readPackedPixelsToFloat32Array().subarray(0, xMax); } render2DPackedFloat() { const pixels = this.readPackedPixelsToFloat32Array(); const [xMax, yMax] = this.output; const yResults = new Array(yMax); for (let y = 0; y < yMax; y++) { const xStart = y * xMax; const xEnd = xStart + xMax; yResults[y] = pixels.subarray(xStart, xEnd); } return yResults; } render3DPackedFloat() { const pixels = this.readPackedPixelsToFloat32Array(); const [xMax, yMax, zMax] = this.output; const zResults = new Array(zMax); for (let z = 0; z < zMax; z++) { const yResults = new Array(yMax); for (let y = 0; y < yMax; y++) { const xStart = (z * yMax * xMax) + y * xMax; const xEnd = xStart + xMax; yResults[y] = pixels.subarray(xStart, xEnd); } zResults[z] = yResults; } return zResults; } renderFloat() { const pixels = this.readFloatPixelsToFloat32Array(); const [xMax] = this.output; const xResults = new Float32Array(xMax); let i = 0; for (let x = 0; x < xMax; x++) { xResults[x] = pixels[i]; i += 4; } return xResults; } renderMemoryOptimizedFloat() { const pixels = this.readMemoryOptimizedFloatPixelsToFloat32Array(); const [xMax] = this.output; return pixels.subarray(0, xMax); } render2DFloat() { const pixels = this.readFloatPixelsToFloat32Array(); const [xMax, yMax] = this.output; const yResults = new Array(yMax); let i = 0; for (let y = 0; y < yMax; y++) { const xResults = new Float32Array(xMax); for (let x = 0; x < xMax; x++) { xResults[x] = pixels[i]; i += 4; } yResults[y] = xResults; } return yResults; } renderMemoryOptimized2DFloat() { const pixels = this.readFloatPixelsToFloat32Array(); const [xMax, yMax] = this.output; const yResults = new Array(yMax); for (let y = 0; y < yMax; y++) { const offset = y * xMax; yResults[y] = pixels.subarray(offset, offset + xMax); } return yResults; } render3DFloat() { const pixels = this.readFloatPixelsToFloat32Array(); const [xMax, yMax, zMax] = this.output; const zResults = new Array(zMax); let i = 0; for (let z = 0; z < zMax; z++) { const yResults = new Array(yMax); for (let y = 0; y < yMax; y++) { const xResults = new Float32Array(xMax); for (let x = 0; x < xMax; x++) { xResults[x] = pixels[i]; i += 4; } yResults[y] = xResults; } zResults[z] = yResults; } return zResults; } renderMemoryOptimized3DFloat() { const pixels = this.readFloatPixelsToFloat32Array(); const [xMax, yMax, zMax] = this.output; const zResults = new Array(zMax); for (let z = 0; z < zMax; z++) { const yResults = new Array(yMax); for (let y = 0; y < yMax; y++) { const offset = (z * yMax * xMax) + (y * xMax); yResults[y] = pixels.subarray(offset, offset + xMax); } zResults[z] = yResults; } return zResults; } renderArray2() { const pixels = this.readFloatPixelsToFloat32Array(); const [xMax] = this.output; const xResults = new Array(xMax); const xResultsMax = xMax * 4; let i = 0; for (let x = 0; x < xResultsMax; x += 4) { xResults[i++] = pixels.subarray(x, x + 2); } return xResults; } render2DArray2() { const pixels = this.readFloatPixelsToFloat32Array(); const [xMax, yMax] = this.output; const yResults = new Array(yMax); const XResultsMax = xMax * 4; for (let y = 0; y < yMax; y++) { const xResults = new Array(xMax); const offset = y * XResultsMax; let i = 0; for (let x = 0; x < XResultsMax; x += 4) { xResults[i++] = pixels.subarray(x + offset, x + offset + 2); } yResults[y] = xResults; } return yResults; } render3DArray2() { const pixels = this.readFloatPixelsToFloat32Array(); const [xMax, yMax, zMax] = this.output; const xResultsMax = xMax * 4; const zResults = new Array(zMax); for (let z = 0; z < zMax; z++) { const yResults = new Array(yMax); for (let y = 0; y < yMax; y++) { const xResults = new Array(xMax); const offset = (z * xResultsMax * yMax) + (y * xResultsMax); let i = 0; for (let x = 0; x < xResultsMax; x += 4) { xResults[i++] = pixels.subarray(x + offset, x + offset + 2); } yResults[y] = xResults; } zResults[z] = yResults; } return zResults; } renderArray3() { const pixels = this.readFloatPixelsToFloat32Array(); const [xMax] = this.output; const xResults = new Array(xMax); const xResultsMax = xMax * 4; let i = 0; for (let x = 0; x < xResultsMax; x += 4) { xResults[i++] = pixels.subarray(x, x + 3); } return xResults; } render2DArray3() { const pixels = this.readFloatPixelsToFloat32Array(); const [xMax, yMax] = this.output; const xResultsMax = xMax * 4; const yResults = new Array(yMax); for (let y = 0; y < yMax; y++) { const xResults = new Array(xMax); const offset = y * xResultsMax; let i = 0; for (let x = 0; x < xResultsMax; x += 4) { xResults[i++] = pixels.subarray(x + offset, x + offset + 3); } yResults[y] = xResults; } return yResults; } render3DArray3() { const pixels = this.readFloatPixelsToFloat32Array(); const [xMax, yMax, zMax] = this.output; const xResultsMax = xMax * 4; const zResults = new Array(zMax); for (let z = 0; z < zMax; z++) { const yResults = new Array(yMax); for (let y = 0; y < yMax; y++) { const xResults = new Array(xMax); const offset = (z * xResultsMax * yMax) + (y * xResultsMax); let i = 0; for (let x = 0; x < xResultsMax; x += 4) { xResults[i++] = pixels.subarray(x + offset, x + offset + 3); } yResults[y] = xResults; } zResults[z] = yResults; } return zResults; } renderArray4() { const pixels = this.readFloatPixelsToFloat32Array(); const [xMax] = this.output; const xResults = new Array(xMax); const xResultsMax = xMax * 4; let i = 0; for (let x = 0; x < xResultsMax; x += 4) { xResults[i++] = pixels.subarray(x, x + 4); } return xResults; } render2DArray4() { const pixels = this.readFloatPixelsToFloat32Array(); const [xMax, yMax] = this.output; const xResultsMax = xMax * 4; const yResults = new Array(yMax); for (let y = 0; y < yMax; y++) { const xResults = new Array(xMax); const offset = y * xResultsMax; let i = 0; for (let x = 0; x < xResultsMax; x += 4) { xResults[i++] = pixels.subarray(x + offset, x + offset + 4); } yResults[y] = xResults; } return yResults; } render3DArray4() { const pixels = this.readFloatPixelsToFloat32Array(); const [xMax, yMax, zMax] = this.output; const xResultsMax = xMax * 4; const zResults = new Array(zMax); for (let z = 0; z < zMax; z++) { const yResults = new Array(yMax); for (let y = 0; y < yMax; y++) { const xResults = new Array(xMax); const offset = (z * xResultsMax * yMax) + (y * xResultsMax); let i = 0; for (let x = 0; x < xResultsMax; x += 4) { xResults[i++] = pixels.subarray(x + offset, x + offset + 4); } yResults[y] = xResults; } zResults[z] = yResults; } return zResults; } getPixels() { const { context: gl, output } = this; const [width, height] = output; const pixels = new Uint8Array(width * height * 4); gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels); return pixels; } } const renderStrategy = Object.freeze({ PackedPixelToUint8Array: Symbol('PackedPixelToUint8Array'), PackedPixelToFloat: Symbol('PackedPixelToFloat'), PackedPixelTo2DFloat: Symbol('PackedPixelTo2DFloat'), PackedPixelTo3DFloat: Symbol('PackedPixelTo3DFloat'), PackedTexture: Symbol('PackedTexture'), FloatPixelToFloat32Array: Symbol('FloatPixelToFloat32Array'), FloatPixelToFloat: Symbol('FloatPixelToFloat'), FloatPixelTo2DFloat: Symbol('FloatPixelTo2DFloat'), FloatPixelTo3DFloat: Symbol('FloatPixelTo3DFloat'), FloatPixelToArray2: Symbol('FloatPixelToArray2'), FloatPixelTo2DArray2: Symbol('FloatPixelTo2DArray2'), FloatPixelTo3DArray2: Symbol('FloatPixelTo3DArray2'), FloatPixelToArray3: Symbol('FloatPixelToArray3'), FloatPixelTo2DArray3: Symbol('FloatPixelTo2DArray3'), FloatPixelTo3DArray3: Symbol('FloatPixelTo3DArray3'), FloatPixelToArray4: Symbol('FloatPixelToArray4'), FloatPixelTo2DArray4: Symbol('FloatPixelTo2DArray4'), FloatPixelTo3DArray4: Symbol('FloatPixelTo3DArray4'), FloatTexture: Symbol('FloatTexture'), MemoryOptimizedFloatPixelToMemoryOptimizedFloat: Symbol('MemoryOptimizedFloatPixelToFloat'), MemoryOptimizedFloatPixelToMemoryOptimized2DFloat: Symbol('MemoryOptimizedFloatPixelTo2DFloat'), MemoryOptimizedFloatPixelToMemoryOptimized3DFloat: Symbol('MemoryOptimizedFloatPixelTo3DFloat'), }); const typeMap = { int: 'Integer', float: 'Number', vec2: 'Array(2)', vec3: 'Array(3)', vec4: 'Array(4)', }; module.exports = { GLKernel, renderStrategy }; },{"../../texture":68,"../../utils":69,"../kernel":14}],12:[function(require,module,exports){ const getContext = require('gl'); const { WebGLKernel } = require('../web-gl/kernel'); const { glKernelString } = require('../gl/kernel-string'); let isSupported = null; let testCanvas = null; let testContext = null; let testExtensions = null; let features = null; class HeadlessGLKernel extends WebGLKernel { static get isSupported() { if (isSupported !== null) return isSupported; this.setupFeatureChecks(); isSupported = testContext !== null; return isSupported; } static setupFeatureChecks() { testCanvas = null; testExtensions = null; if (typeof getContext !== 'function') return; try { testContext = getContext(2, 2, { preserveDrawingBuffer: true }); if (!testContext || !testContext.getExtension) return; testExtensions = { STACKGL_resize_drawingbuffer: testContext.getExtension('STACKGL_resize_drawingbuffer'), STACKGL_destroy_context: testContext.getExtension('STACKGL_destroy_context'), OES_texture_float: testContext.getExtension('OES_texture_float'), OES_texture_float_linear: testContext.getExtension('OES_texture_float_linear'), OES_element_index_uint: testContext.getExtension('OES_element_index_uint'), WEBGL_draw_buffers: testContext.getExtension('WEBGL_draw_buffers'), }; features = this.getFeatures(); } catch (e) {} } static isContextMatch(context) { try { return context.getParameter(context.RENDERER) === 'ANGLE'; } catch (e) { return false; } } static getFeatures() { const isDrawBuffers = this.getIsDrawBuffers(); return Object.freeze({ isFloatRead: this.getIsFloatRead(), isIntegerDivisionAccurate: this.getIsIntegerDivisionAccurate(), isTextureFloat: this.getIsTextureFloat(), isDrawBuffers, kernelMap: isDrawBuffers }); } static getIsTextureFloat() { return Boolean(testExtensions.OES_texture_float); } static getIsDrawBuffers() { return Boolean(testExtensions.WEBGL_draw_buffers); } static get testCanvas() { return testCanvas; } static get testContext() { return testContext; } static get features() { return features; } initCanvas() { return {}; } initContext() { const context = getContext(2, 2, { preserveDrawingBuffer: true }); return context; } initExtensions() { this.extensions = { STACKGL_resize_drawingbuffer: this.context.getExtension('STACKGL_resize_drawingbuffer'), STACKGL_destroy_context: this.context.getExtension('STACKGL_destroy_context'), OES_texture_float: this.context.getExtension('OES_texture_float'), OES_texture_float_linear: this.context.getExtension('OES_texture_float_linear'), OES_element_index_uint: this.context.getExtension('OES_element_index_uint'), WEBGL_draw_buffers: this.context.getExtension('WEBGL_draw_buffers'), }; } destroyExtensions() { this.extensions.STACKGL_resize_drawingbuffer = null; this.extensions.STACKGL_destroy_context = null; this.extensions.OES_texture_float = null; this.extensions.OES_texture_float_linear = null; this.extensions.OES_element_index_uint = null; this.extensions.WEBGL_draw_buffers = null; } static destroyContext(context) { const extension = context.getExtension('STACKGL_destroy_context'); if (extension && extension.destroy) { extension.destroy(); } } toString() { const setupContextString = `const gl = context || require('gl')(1, 1);\n`; const destroyContextString = `if (!context) { gl.getExtension('STACKGL_destroy_context').destroy(); }\n`; return glKernelString(this.constructor, arguments, this, setupContextString, destroyContextString); } } module.exports = { HeadlessGLKernel }; },{"../gl/kernel-string":10,"../web-gl/kernel":36,"gl":2}],13:[function(require,module,exports){ const { utils } = require('../utils'); class KernelValue { constructor(value, settings) { const { name, index, kernel, context, contextHandle, origin, strictIntegers, } = settings; if (!name) { throw new Error('name not set'); } if (!origin) { throw new Error('origin not set'); } if (origin !== 'user' && origin !== 'constants') { throw new Error(`origin must be "user" or "constants" value is "${ origin }"`); } this.name = name; this.origin = origin; this.id = `${this.origin}_${name}`; this.kernel = kernel; this.strictIntegers = strictIntegers; this.type = utils.getVariableType(value, strictIntegers); this.size = value.size || null; this.index = index; this.context = context; this.contextHandle = contextHandle; } getSource() { throw new Error(`"getSource" not defined on ${ this.constructor.name }`); } updateValue(value) { throw new Error(`"updateValue" not defined on ${ this.constructor.name }`); } } module.exports = { KernelValue }; },{"../utils":69}],14:[function(require,module,exports){ const { utils } = require('../utils'); const { Input } = require('../input'); class Kernel { static get isSupported() { throw new Error(`"isSupported" not implemented on ${ this.name }`); } static isContextMatch(context) { throw new Error(`"isContextMatch" not implemented on ${ this.name }`); } static getFeatures() { throw new Error(`"getFeatures" not implemented on ${ this.name }`); } static destroyContext(context) { throw new Error(`"destroyContext" called on ${ this.name }`); } static nativeFunctionArguments() { throw new Error(`"nativeFunctionArguments" called on ${ this.name }`); } static nativeFunctionReturnType() { throw new Error(`"nativeFunctionReturnType" called on ${ this.name }`); } static combineKernels() { throw new Error(`"combineKernels" called on ${ this.name }`); } constructor(source, settings) { if (typeof source !== 'object') { if (typeof source !== 'string') { throw new Error('source not a string'); } if (!utils.isFunctionString(source)) { throw new Error('source not a function string'); } } this.onRequestFallback = null; this.argumentNames = typeof source === 'string' ? utils.getArgumentNamesFromString(source) : null; this.argumentTypes = null; this.argumentSizes = null; this.argumentBitRatios = null; this.kernelArguments = null; this.argumentsLength = 0; this.constantsLength = 0; this.kernelConstants = null; this.source = source; this.output = null; this.debug = false; this.graphical = false; this.loopMaxIterations = 0; this.constants = null; this.constantTypes = null; this.constantBitRatios = null; this.hardcodeConstants = false; this.canvas = null; this.context = null; this.gpu = null; this.functions = null; this.nativeFunctions = null; this.subKernels = null; this.validate = true; this.immutable = false; this.pipeline = false; this.precision = null; this.plugins = null; this.returnType = null; this.leadingReturnStatement = null; this.followingReturnStatement = null; this.optimizeFloatMemory = null; this.strictIntegers = false; } mergeSettings(settings) { for (let p in settings) { if (!settings.hasOwnProperty(p) || !this.hasOwnProperty(p)) continue; switch (p) { case 'output': if (!Array.isArray(settings.output)) { this.setOutput(settings.output); continue; } break; case 'functions': if (typeof settings.functions[0] === 'function') { this.functions = settings.functions.map(source => utils.functionToIFunction(source)); continue; } break; case 'graphical': if (settings[p] && !settings.hasOwnProperty('precision')) { this.precision = 'unsigned'; } this[p] = settings[p]; continue; } this[p] = settings[p]; } if (!this.canvas) this.canvas = this.initCanvas(); if (!this.context) this.context = this.initContext(); if (!this.plugins) this.plugins = this.initPlugins(settings); } build() { throw new Error(`"build" not defined on ${ this.constructor.name }`); } run() { throw new Error(`"run" not defined on ${ this.constructor.name }`) } initCanvas() { throw new Error(`"initCanvas" not defined on ${ this.constructor.name }`); } initContext() { throw new Error(`"initContext" not defined on ${ this.constructor.name }`); } initPlugins(settings) { throw new Error(`"initPlugins" not defined on ${ this.constructor.name }`); } setupArguments(args) { if (!this.argumentTypes) { this.kernelArguments = []; this.argumentTypes = []; for (let i = 0; i < args.length; i++) { const argType = utils.getVariableType(args[i], this.strictIntegers); this.argumentTypes.push(argType === 'Integer' ? 'Number' : argType); } } this.argumentSizes = new Array(args.length); this.argumentBitRatios = new Int32Array(args.length); for (let i = 0; i < args.length; i++) { const arg = args[i]; this.argumentSizes[i] = arg.constructor === Input ? arg.size : null; this.argumentBitRatios[i] = this.getBitRatio(arg); } if (this.argumentNames.length !== args.length) { throw new Error(`arguments are miss-aligned`); } } setupConstants() { this.kernelConstants = []; this.constantTypes = {}; this.constantBitRatios = {}; if (this.constants) { for (let p in this.constants) { this.constantTypes[p] = utils.getVariableType(this.constants[p], this.strictIntegers); this.constantBitRatios[p] = this.getBitRatio(this.constants[p]); } } } setOptimizeFloatMemory(flag) { this.optimizeFloatMemory = flag; return this; } setOutput(output) { if (output.hasOwnProperty('x')) { if (output.hasOwnProperty('y')) { if (output.hasOwnProperty('z')) { this.output = [output.x, output.y, output.z]; } else { this.output = [output.x, output.y]; } } else { this.output = [output.x]; } } else { this.output = output; } return this; } setDebug(flag) { this.debug = flag; return this; } setGraphical(flag) { this.graphical = flag; this.precision = 'unsigned'; return this; } setLoopMaxIterations(max) { this.loopMaxIterations = max; return this; } setConstants(constants) { this.constants = constants; return this; } setFunctions(functions) { if (typeof functions[0] === 'function') { this.functions = functions.map(source => utils.functionToIFunction(source)); } else { this.functions = functions; } return this; } setPipeline(flag) { this.pipeline = flag; return this; } setPrecision(flag) { this.precision = flag; return this; } setOutputToTexture(flag) { utils.warnDeprecated('method', 'setOutputToTexture', 'setPipeline'); this.pipeline = flag; return this; } setImmutable(flag) { this.immutable = flag; return this; } setCanvas(canvas) { this.canvas = canvas; return this; } getCanvas() { utils.warnDeprecated('method', 'getCanvas'); return this.canvas; } getWebGl() { utils.warnDeprecated('method', 'getWebGl'); return this.context; } setContext(context) { this.context = context; return this; } setArgumentTypes(argumentTypes) { this.argumentTypes = argumentTypes; return this; } requestFallback(args) { if (!this.onRequestFallback) { throw new Error(`"onRequestFallback" not defined on ${ this.constructor.name }`); } return this.onRequestFallback(args); } validateSettings() { throw new Error(`"validateSettings" not defined on ${ this.constructor.name }`); } exec() { const args = (arguments.length === 1 ? [arguments[0]] : Array.apply(null, arguments)); return new Promise((accept, reject) => { try { accept(this.run.apply(this, args)); } catch (e) { reject(e); } }); } addSubKernel(subKernel) { if (this.subKernels === null) { this.subKernels = []; } if (!subKernel.source) throw new Error('subKernel missing "source" property'); if (!subKernel.property && isNaN(subKernel.property)) throw new Error('subKernel missing "property" property'); if (!subKernel.name) throw new Error('subKernel missing "name" property'); this.subKernels.push(subKernel); return this; } destroy(removeCanvasReferences) { throw new Error(`"destroy" called on ${ this.constructor.name }`); } getBitRatio(value) { if (this.precision === 'single') { return 4; } else if (Array.isArray(value[0])) { return this.getBitRatio(value[0]); } else if (value.constructor === Input) { return this.getBitRatio(value.value); } switch (value.constructor) { case Uint8Array: case Int8Array: return 1; case Uint16Array: case Int16Array: return 2; case Float32Array: case Int32Array: default: return 4; } } getPixels() { throw new Error(`"getPixels" called on ${ this.constructor.name }`); } checkOutput() { if (!this.output || !Array.isArray(this.output)) throw new Error('kernel.output not an array'); if (this.output.length < 1) throw new Error('kernel.output is empty, needs at least 1 value'); for (let i = 0; i < this.output.length; i++) { if (isNaN(this.output[i]) || this.output[i] < 1) { throw new Error(`${ this.constructor.name }.output[${ i }] incorrectly defined as \`${ this.output[i] }\`, needs to be numeric, and greater than 0`); } } } toJSON() { const settings = { output: this.output, threadDim: this.threadDim, pipeline: this.pipeline, argumentNames: this.argumentNames, argumentsTypes: this.argumentTypes, argumentsLength: this.argumentsLength, constants: this.constants, constantsLength: this.constantsLength, pluginNames: this.plugins ? this.plugins.map(plugin => plugin.name) : null, returnType: this.returnType, }; return { settings }; } } module.exports = { Kernel }; },{"../input":65,"../utils":69}],15:[function(require,module,exports){ const fragmentShader = `__HEADER__; precision highp float; precision highp int; precision highp sampler2D; const int LOOP_MAX = __LOOP_MAX__; __PLUGINS__; __CONSTANTS__; varying vec2 vTexCoord; vec4 round(vec4 x) { return floor(x + 0.5); } float round(float x) { return floor(x + 0.5); } vec2 integerMod(vec2 x, float y) { vec2 res = floor(mod(x, y)); return res * step(1.0 - floor(y), -res); } vec3 integerMod(vec3 x, float y) { vec3 res = floor(mod(x, y)); return res * step(1.0 - floor(y), -res); } vec4 integerMod(vec4 x, vec4 y) { vec4 res = floor(mod(x, y)); return res * step(1.0 - floor(y), -res); } float integerMod(float x, float y) { float res = floor(mod(x, y)); return res * (res > floor(y) - 1.0 ? 0.0 : 1.0); } int integerMod(int x, int y) { return x - (y * int(x / y)); } __DIVIDE_WITH_INTEGER_CHECK__; // Here be dragons! // DO NOT OPTIMIZE THIS CODE // YOU WILL BREAK SOMETHING ON SOMEBODY\'S MACHINE // LEAVE IT AS IT IS, LEST YOU WASTE YOUR OWN TIME const vec2 MAGIC_VEC = vec2(1.0, -256.0); const vec4 SCALE_FACTOR = vec4(1.0, 256.0, 65536.0, 0.0); const vec4 SCALE_FACTOR_INV = vec4(1.0, 0.00390625, 0.0000152587890625, 0.0); // 1, 1/256, 1/65536 float decode32(vec4 texel) { __DECODE32_ENDIANNESS__; texel *= 255.0; vec2 gte128; gte128.x = texel.b >= 128.0 ? 1.0 : 0.0; gte128.y = texel.a >= 128.0 ? 1.0 : 0.0; float exponent = 2.0 * texel.a - 127.0 + dot(gte128, MAGIC_VEC); float res = exp2(round(exponent)); texel.b = texel.b - 128.0 * gte128.x; res = dot(texel, SCALE_FACTOR) * exp2(round(exponent-23.0)) + res; res *= gte128.y * -2.0 + 1.0; return res; } float decode16(vec4 texel, int index) { int channel = integerMod(index, 2); if (channel == 0) return texel.r * 255.0 + texel.g * 65280.0; if (channel == 1) return texel.b * 255.0 + texel.a * 65280.0; return 0.0; } float decode8(vec4 texel, int index) { int channel = integerMod(index, 4); if (channel == 0) return texel.r * 255.0; if (channel == 1) return texel.g * 255.0; if (channel == 2) return texel.b * 255.0; if (channel == 3) return texel.a * 255.0; return 0.0; } vec4 encode32(float f) { float F = abs(f); float sign = f < 0.0 ? 1.0 : 0.0; float exponent = floor(log2(F)); float mantissa = (exp2(-exponent) * F); // exponent += floor(log2(mantissa)); vec4 texel = vec4(F * exp2(23.0-exponent)) * SCALE_FACTOR_INV; texel.rg = integerMod(texel.rg, 256.0); texel.b = integerMod(texel.b, 128.0); texel.a = exponent*0.5 + 63.5; texel.ba += vec2(integerMod(exponent+127.0, 2.0), sign) * 128.0; texel = floor(texel); texel *= 0.003921569; // 1/255 __ENCODE32_ENDIANNESS__; return texel; } // Dragons end here int index; ivec3 threadId; ivec3 indexTo3D(int idx, ivec3 texDim) { int z = int(idx / (texDim.x * texDim.y)); idx -= z * int(texDim.x * texDim.y); int y = int(idx / texDim.x); int x = int(integerMod(idx, texDim.x)); return ivec3(x, y, z); } float get32(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) { ivec3 xyz = ivec3(x, y, z); int index = xyz.x + texDim.x * (xyz.y + texDim.y * xyz.z); int w = texSize.x; vec2 st = vec2(float(integerMod(index, w)), float(index / w)) + 0.5; vec4 texel = texture2D(tex, st / vec2(texSize)); return decode32(texel); } float get16(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) { ivec3 xyz = ivec3(x, y, z); int index = xyz.x + texDim.x * (xyz.y + texDim.y * xyz.z); int w = texSize.x * 2; vec2 st = vec2(float(integerMod(index, w)), float(index / w)) + 0.5; vec4 texel = texture2D(tex, st / vec2(texSize.x * 2, texSize.y)); return decode16(texel, index); } float get8(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) { ivec3 xyz = ivec3(x, y, z); int index = xyz.x + texDim.x * (xyz.y + texDim.y * xyz.z); int w = texSize.x * 4; vec2 st = vec2(float(integerMod(index, w)), float(index / w)) + 0.5; vec4 texel = texture2D(tex, st / vec2(texSize.x * 4, texSize.y)); return decode8(texel, index); } float getMemoryOptimized32(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) { ivec3 xyz = ivec3(x, y, z); int index = xyz.x + texDim.x * (xyz.y + texDim.y * xyz.z); int channel = integerMod(index, 4); index = index / 4; int w = texSize.x; vec2 st = vec2(float(integerMod(index, w)), float(index / w)) + 0.5; vec4 texel = texture2D(tex, st / vec2(texSize)); if (channel == 0) return texel.r; if (channel == 1) return texel.g; if (channel == 2) return texel.b; if (channel == 3) return texel.a; return 0.0; } vec4 getImage2D(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) { ivec3 xyz = ivec3(x, y, z); int index = xyz.x + texDim.x * (xyz.y + texDim.y * xyz.z); int w = texSize.x; vec2 st = vec2(float(integerMod(index, w)), float(index / w)) + 0.5; return texture2D(tex, st / vec2(texSize)); } float getFloatFromSampler2D(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) { vec4 result = getImage2D(tex, texSize, texDim, z, y, x); return result[0]; } vec2 getVec2FromSampler2D(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) { vec4 result = getImage2D(tex, texSize, texDim, z, y, x); return vec2(result[0], result[1]); } vec3 getVec3FromSampler2D(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) { vec4 result = getImage2D(tex, texSize, texDim, z, y, x); return vec3(result[0], result[1], result[2]); } vec4 getVec4FromSampler2D(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) { return getImage2D(tex, texSize, texDim, z, y, x); } vec4 actualColor; void color(float r, float g, float b, float a) { actualColor = vec4(r,g,b,a); } void color(float r, float g, float b) { color(r,g,b,1.0); } void color(sampler2D image) { actualColor = texture2D(image, vTexCoord); } __MAIN_CONSTANTS__; __MAIN_ARGUMENTS__; __KERNEL__; void main(void) { index = int(vTexCoord.s * float(uTexSize.x)) + int(vTexCoord.t * float(uTexSize.y)) * uTexSize.x; __MAIN_RESULT__; }`; module.exports = { fragmentShader }; },{}],16:[function(require,module,exports){ const { FunctionNode } = require('../function-node'); const jsMathPrefix = 'Math.'; const localPrefix = 'this.'; class WebGLFunctionNode extends FunctionNode { constructor(source, settings) { super(source, settings); this.fixIntegerDivisionAccuracy = null; if (settings && settings.hasOwnProperty('fixIntegerDivisionAccuracy')) { this.fixIntegerDivisionAccuracy = settings.fixIntegerDivisionAccuracy; } } astFunctionExpression(ast, retArr) { if (this.isRootKernel) { retArr.push('void'); } else { if (!this.returnType) { const lastReturn = this.findLastReturn(); if (lastReturn) { this.returnType = this.getType(ast.body); if (this.returnType === 'LiteralInteger') { this.returnType = 'Number'; } } } const { returnType } = this; if (!returnType) { retArr.push('void'); } else { const type = typeMap[returnType]; if (!type) { throw new Error(`unknown type ${returnType}`); } retArr.push(type); } } retArr.push(' '); retArr.push(this.name); retArr.push('('); if (!this.isRootKernel) { for (let i = 0; i < this.argumentNames.length; ++i) { const argumentName = this.argumentNames[i]; if (i > 0) { retArr.push(', '); } let argumentType = this.getVariableType(argumentName); if (!argumentType || argumentType === 'LiteralInteger') { argumentType = 'Number'; } const type = typeMap[argumentType]; if (!type) { throw this.astErrorOutput('Unexpected expression', ast); } retArr.push(type); retArr.push(' '); retArr.push('user_'); retArr.push(argumentName); } } retArr.push(') {\n'); for (let i = 0; i < ast.body.body.length; ++i) { this.astGeneric(ast.body.body[i], retArr); retArr.push('\n'); } retArr.push('}\n'); return retArr; } astReturnStatement(ast, retArr) { if (!ast.argument) throw this.astErrorOutput('Unexpected return statement', ast); const type = this.getType(ast.argument); const result = []; if (!this.returnType) { if (this.isRootKernel) { this.returnType = 'Number'; } else { this.returnType = type; } } switch (this.returnType) { case 'LiteralInteger': case 'Number': case 'Float': switch (type) { case 'Integer': result.push('float('); this.astGeneric(ast.argument, result); result.push(')'); break; case 'LiteralInteger': this.pushState('casting-to-float'); this.astGeneric(ast.argument, result); this.popState('casting-to-float'); if (this.getType(ast) === 'Integer') { result.unshift('float('); result.push(')'); } break; default: this.astGeneric(ast.argument, result); } break; case 'Integer': switch (type) { case 'Float': case 'Number': this.pushState('casting-to-integer'); result.push('int('); this.astGeneric(ast.argument, result); result.push(')'); this.popState('casting-to-integer'); break; case 'LiteralInteger': this.pushState('casting-to-integer'); this.astGeneric(ast.argument, result); this.popState('casting-to-integer'); break; default: this.astGeneric(ast.argument, result); } break; case 'Array(4)': case 'Array(3)': case 'Array(2)': case 'Input': this.astGeneric(ast.argument, result); break; default: throw this.astErrorOutput(`unhandled return type ${this.returnType}`, ast); } if (this.isRootKernel) { retArr.push(`kernelResult = ${ result.join('') };`); retArr.push('return;'); } else if (this.isSubKernel) { retArr.push(`subKernelResult_${ this.name } = ${ result.join('') };`); retArr.push(`return subKernelResult_${ this.name };`); } else { retArr.push(`return ${ result.join('') };`); } return retArr; } astLiteral(ast, retArr) { if (isNaN(ast.value)) { throw this.astErrorOutput( 'Non-numeric literal not supported : ' + ast.value, ast ); } if (Number.isInteger(ast.value)) { if (this.isState('in-for-loop-init') || this.isState('casting-to-integer')) { this.literalTypes[`${ast.start},${ast.end}`] = 'Integer'; retArr.push(`${ast.value}`); } else if (this.isState('casting-to-float')) { this.literalTypes[`${ast.start},${ast.end}`] = 'Number'; retArr.push(`${ast.value}.0`); } else { this.literalTypes[`${ast.start},${ast.end}`] = 'Number'; retArr.push(`${ast.value}.0`); } } else if (this.isState('casting-to-integer')) { this.literalTypes[`${ast.start},${ast.end}`] = 'Integer'; retArr.push(parseInt(ast.raw)); } else { this.literalTypes[`${ast.start},${ast.end}`] = 'Number'; retArr.push(`${ast.value}`); } return retArr; } astBinaryExpression(ast, retArr) { if (ast.operator === '%') { retArr.push('mod('); const leftType = this.getType(ast.left); if (leftType === 'Integer') { retArr.push('float('); this.astGeneric(ast.left, retArr); retArr.push(')'); } else if (leftType === 'LiteralInteger') { this.pushState('casting-to-float'); this.astGeneric(ast.left, retArr); this.popState('casting-to-float'); } else { this.astGeneric(ast.left, retArr); } retArr.push(','); const rightType = this.getType(ast.right); if (rightType === 'Integer') { retArr.push('float('); this.astGeneric(ast.right, retArr); retArr.push(')'); } else if (rightType === 'LiteralInteger') { this.pushState('casting-to-float'); this.astGeneric(ast.right, retArr); this.popState('casting-to-float'); } else { this.astGeneric(ast.right, retArr); } retArr.push(')'); return retArr; } retArr.push('('); if (this.fixIntegerDivisionAccuracy && ast.operator === '/') { retArr.push('div_with_int_check('); switch (this.getType(ast.left)) { case 'Integer': retArr.push('float('); this.pushState('casting-to-float'); this.astGeneric(ast.left, retArr); this.popState('casting-to-float'); retArr.push(')'); break; case 'LiteralInteger': this.pushState('casting-to-float'); this.astGeneric(ast.left, retArr); this.popState('casting-to-float'); break; default: this.astGeneric(ast.left, retArr); } retArr.push(', '); switch (this.getType(ast.right)) { case 'Integer': retArr.push('float('); this.pushState('casting-to-float'); this.astGeneric(ast.right, retArr); this.popState('casting-to-float'); retArr.push(')'); break; case 'LiteralInteger': this.pushState('casting-to-float'); this.astGeneric(ast.right, retArr); this.popState('casting-to-float'); break; default: this.astGeneric(ast.right, retArr); } retArr.push(')'); } else { const leftType = this.getType(ast.left) || 'Number'; const rightType = this.getType(ast.right) || 'Number'; if (!leftType || !rightType) { throw this.astErrorOutput(`Unhandled binary expression`, ast); } const key = leftType + ' & ' + rightType; switch (key) { case 'Integer & Integer': this.astGeneric(ast.left, retArr); retArr.push(operatorMap[ast.operator] || ast.operator); this.astGeneric(ast.right, retArr); break; case 'Number & Float': case 'Float & Number': case 'Float & Float': case 'Number & Number': this.astGeneric(ast.left, retArr); retArr.push(operatorMap[ast.operator] || ast.operator); this.astGeneric(ast.right, retArr); break; case 'LiteralInteger & LiteralInteger': this.pushState('casting-to-float'); this.astGeneric(ast.left, retArr); retArr.push(operatorMap[ast.operator] || ast.operator); this.astGeneric(ast.right, retArr); this.popState('casting-to-float'); break; case 'Integer & Float': case 'Integer & Number': if (ast.operator === '>' || ast.operator === '<' && ast.right.type === 'Literal') { if (!Number.isInteger(ast.right.value)) { this.pushState('casting-to-float'); retArr.push('float('); this.astGeneric(ast.left, retArr); retArr.push(')'); this.popState('casting-to-float'); retArr.push(operatorMap[ast.operator] || ast.operator); this.astGeneric(ast.right, retArr); break; } } this.astGeneric(ast.left, retArr); retArr.push(operatorMap[ast.operator] || ast.operator); this.pushState('casting-to-integer'); if (ast.right.type === 'Literal') { const literalResult = []; this.astGeneric(ast.right, literalResult); const literalType = this.getType(ast.right); if (literalType === 'Integer') { retArr.push(literalResult.join('')); } else { throw this.astErrorOutput(`Unhandled binary expression with literal`, ast); } } else { retArr.push('int('); this.astGeneric(ast.right, retArr); retArr.push(')'); } this.popState('casting-to-integer'); break; case 'Integer & LiteralInteger': this.astGeneric(ast.left, retArr); retArr.push(operatorMap[ast.operator] || ast.operator); this.pushState('casting-to-integer'); this.astGeneric(ast.right, retArr); this.popState('casting-to-integer'); break; case 'Number & Integer': this.astGeneric(ast.left, retArr); retArr.push(operatorMap[ast.operator] || ast.operator); this.pushState('casting-to-float'); retArr.push('float('); this.astGeneric(ast.right, retArr); retArr.push(')'); this.popState('casting-to-float'); break; case 'Float & LiteralInteger': case 'Number & LiteralInteger': if (this.isState('in-for-loop-test')) { retArr.push('int('); this.astGeneric(ast.left, retArr); retArr.push(')'); retArr.push(operatorMap[ast.operator] || ast.operator); this.pushState('casting-to-integer'); this.astGeneric(ast.right, retArr); this.popState('casting-to-integer'); } else { this.astGeneric(ast.left, retArr); retArr.push(operatorMap[ast.operator] || ast.operator); this.pushState('casting-to-float'); this.astGeneric(ast.right, retArr); this.popState('casting-to-float'); } break; case 'LiteralInteger & Float': case 'LiteralInteger & Number': if (this.isState('in-for-loop-test') || this.isState('in-for-loop-init') || this.isState('casting-to-integer')) { this.pushState('casting-to-integer'); this.astGeneric(ast.left, retArr); retArr.push(operatorMap[ast.operator] || ast.operator); retArr.push('int('); this.astGeneric(ast.right, retArr); retArr.push(')'); this.popState('casting-to-integer'); } else { this.astGeneric(ast.left, retArr); retArr.push(operatorMap[ast.operator] || ast.operator); this.pushState('casting-to-float'); this.astGeneric(ast.right, retArr); this.popState('casting-to-float'); } break; case 'LiteralInteger & Integer': this.pushState('casting-to-integer'); this.astGeneric(ast.left, retArr); this.popState('casting-to-integer'); retArr.push(operatorMap[ast.operator] || ast.operator); this.astGeneric(ast.right, retArr); break; case 'Boolean & Boolean': this.astGeneric(ast.left, retArr); retArr.push(operatorMap[ast.operator] || ast.operator); this.astGeneric(ast.right, retArr); break; case 'Float & Integer': this.astGeneric(ast.left, retArr); retArr.push(operatorMap[ast.operator] || ast.operator); this.pushState('casting-to-integer'); retArr.push('float('); this.astGeneric(ast.right, retArr); retArr.push(')'); this.popState('casting-to-integer'); break; default: throw this.astErrorOutput(`Unhandled binary expression between ${key}`, ast); } } retArr.push(')'); return retArr; } astIdentifierExpression(idtNode, retArr) { if (idtNode.type !== 'Identifier') { throw this.astErrorOutput('IdentifierExpression - not an Identifier', idtNode); } const type = this.getType(idtNode); if (idtNode.name === 'Infinity') { retArr.push('3.402823466e+38'); } else if (type === 'Boolean') { if (this.argumentNames.indexOf(idtNode.name) > -1) { retArr.push(`bool(user_${idtNode.name})`); } else { retArr.push(`user_${idtNode.name}`); } } else { const userArgumentName = this.getKernelArgumentName(idtNode.name); if (userArgumentName) { retArr.push(`user_${userArgumentName}`); } else { retArr.push(`user_${idtNode.name}`); } } return retArr; } astForStatement(forNode, retArr) { if (forNode.type !== 'ForStatement') { throw this.astErrorOutput('Invalid for statement', forNode); } const initArr = []; const testArr = []; const updateArr = []; const bodyArr = []; let isSafe = null; 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(',')) { isSafe = false; } } this.popState('in-for-loop-init'); } else { isSafe = false; } if (forNode.test) { this.pushState('in-for-loop-test'); this.astGeneric(forNode.test, testArr); this.popState('in-for-loop-test'); } else { isSafe = false; } if (forNode.update) { this.astGeneric(forNode.update, updateArr); } else { isSafe = false; } if (forNode.body) { this.pushState('loop-body'); this.astGeneric(forNode.body, bodyArr); this.popState('loop-body'); } if (isSafe === null) { isSafe = this.isSafe(forNode.init) && this.isSafe(forNode.test); } if (isSafe) { retArr.push(`for (${initArr.join('')};${testArr.join('')};${updateArr.join('')}){\n`); retArr.push(bodyArr.join('')); retArr.push('}\n'); } else { const iVariableName = this.getInternalVariableName('safeI'); if (initArr.length > 0) { retArr.push(initArr.join(''), ';\n'); } retArr.push(`for (int ${iVariableName}=0;${iVariableName} 0) { retArr.push(`if (!${testArr.join('')}) break;\n`); } retArr.push(bodyArr.join('')); retArr.push(`\n${updateArr.join('')};`); retArr.push('}\n'); } return retArr; } astWhileStatement(whileNode, retArr) { if (whileNode.type !== 'WhileStatement') { throw this.astErrorOutput('Invalid while statement', whileNode); } const iVariableName = this.getInternalVariableName('safeI'); retArr.push(`for (int ${iVariableName}=0;${iVariableName} 0) { for (let i = 0; i < this.plugins.length; i++) { const plugin = this.plugins[i]; if (plugin.functionMatch === 'Math.random()' && plugin.functionReplace) { retArr.push(plugin.functionReplace); return retArr; } } } if (this.onFunctionCall) { this.onFunctionCall(this.name, functionName); } retArr.push(functionName); retArr.push('('); if (isMathFunction) { for (let i = 0; i < ast.arguments.length; ++i) { const argument = ast.arguments[i]; const argumentType = this.getType(argument); if (i > 0) { retArr.push(', '); } switch (argumentType) { case 'Integer': this.pushState('casting-to-float'); retArr.push('float('); this.astGeneric(argument, retArr); retArr.push(')'); this.popState('casting-to-float'); break; default: this.astGeneric(argument, retArr); break; } } } else { const targetTypes = this.lookupFunctionArgumentTypes(functionName) || []; for (let i = 0; i < ast.arguments.length; ++i) { const argument = ast.arguments[i]; let targetType = targetTypes[i]; if (i > 0) { retArr.push(', '); } const argumentType = this.getType(argument); if (!targetType) { this.triggerImplyArgumentType(functionName, i, argumentType, this); targetType = argumentType; } switch (argumentType) { case 'Number': case 'Float': if (targetType === 'Integer') { retArr.push('int('); this.astGeneric(argument, retArr); retArr.push(')'); continue; } else if (targetType === 'Number' || targetType === 'Float') { this.astGeneric(argument, retArr); continue; } else if (targetType === 'LiteralInteger') { this.pushState('casting-to-float'); this.astGeneric(argument, retArr); this.popState('casting-to-float'); continue; } break; case 'Integer': if (targetType === 'Number' || targetType === 'Float') { retArr.push('float('); this.astGeneric(argument, retArr); retArr.push(')'); continue; } else if (targetType === 'Integer') { this.astGeneric(argument, retArr); continue; } break; case 'LiteralInteger': if (targetType === 'Integer') { this.pushState('casting-to-integer'); this.astGeneric(argument, retArr); this.popState('casting-to-integer'); continue; } else if (targetType === 'Number' || targetType === 'Float') { this.pushState('casting-to-float'); this.astGeneric(argument, retArr); this.popState('casting-to-float'); continue; } else if (targetType === 'LiteralInteger') { this.astGeneric(argument, retArr); continue; } break; case 'Array(2)': case 'Array(3)': case 'Array(4)': if (targetType === argumentType) { this.astGeneric(argument, retArr); continue; } break; case 'Array': case 'Input': if (targetType === argumentType) { this.triggerTrackArgumentSynonym(this.name, argument.name, functionName, i); this.astGeneric(argument, retArr); continue; } break; } throw this.astErrorOutput(`Unhandled argument combination of ${ argumentType } and ${ targetType }`, ast); } } retArr.push(')'); return retArr; } astArrayExpression(arrNode, retArr) { const arrLen = arrNode.elements.length; retArr.push('vec' + arrLen + '('); for (let i = 0; i < arrLen; ++i) { if (i > 0) { retArr.push(', '); } const subNode = arrNode.elements[i]; this.astGeneric(subNode, retArr) } retArr.push(')'); return retArr; } memberExpressionXYZ(x, y, z, retArr) { if (z) { retArr.push(this.memberExpressionPropertyMarkup(z), ', '); } else { retArr.push('0, '); } if (y) { retArr.push(this.memberExpressionPropertyMarkup(y), ', '); } else { retArr.push('0, '); } retArr.push(this.memberExpressionPropertyMarkup(x)); return retArr; } memberExpressionPropertyMarkup(property) { if (!property) { throw new Error('Property not set'); } const type = this.getType(property); const result = []; switch (type) { case 'Number': case 'Float': this.pushState('casting-to-integer'); result.push('int('); this.astGeneric(property, result); result.push(')'); this.popState('casting-to-integer'); break; case 'LiteralInteger': this.pushState('casting-to-integer'); this.astGeneric(property, result); this.popState('casting-to-integer'); break; default: this.astGeneric(property, result); } return result.join(''); } } const typeMap = { 'Array': 'sampler2D', 'Array(2)': 'vec2', 'Array(3)': 'vec3', 'Array(4)': 'vec4', 'Array2D': 'sampler2D', 'Array3D': 'sampler2D', 'Boolean': 'bool', 'Float': 'float', 'Input': 'sampler2D', 'Integer': 'int', 'Number': 'float', 'LiteralInteger': 'float', 'NumberTexture': 'sampler2D', 'MemoryOptimizedNumberTexture': 'sampler2D', 'ArrayTexture(1)': 'sampler2D', 'ArrayTexture(2)': 'sampler2D', 'ArrayTexture(3)': 'sampler2D', 'ArrayTexture(4)': 'sampler2D', }; const operatorMap = { '===': '==', '!==': '!=' }; module.exports = { WebGLFunctionNode }; },{"../function-node":9}],17:[function(require,module,exports){ const { WebGLKernelValueBoolean } = require('./kernel-value/boolean'); const { WebGLKernelValueFloat } = require('./kernel-value/float'); const { WebGLKernelValueInteger } = require('./kernel-value/integer'); const { WebGLKernelValueHTMLImage } = require('./kernel-value/html-image'); const { WebGLKernelValueDynamicHTMLImage } = require('./kernel-value/dynamic-html-image'); const { WebGLKernelValueSingleInput } = require('./kernel-value/single-input'); const { WebGLKernelValueDynamicSingleInput } = require('./kernel-value/dynamic-single-input'); const { WebGLKernelValueUnsignedInput } = require('./kernel-value/unsigned-input'); const { WebGLKernelValueDynamicUnsignedInput } = require('./kernel-value/dynamic-unsigned-input'); const { WebGLKernelValueMemoryOptimizedNumberTexture } = require('./kernel-value/memory-optimized-number-texture'); const { WebGLKernelValueDynamicMemoryOptimizedNumberTexture } = require('./kernel-value/dynamic-memory-optimized-number-texture'); const { WebGLKernelValueNumberTexture } = require('./kernel-value/number-texture'); const { WebGLKernelValueDynamicNumberTexture } = require('./kernel-value/dynamic-number-texture'); const { WebGLKernelValueSingleArray } = require('./kernel-value/single-array'); const { WebGLKernelValueDynamicSingleArray } = require('./kernel-value/dynamic-single-array'); const { WebGLKernelValueUnsignedArray } = require('./kernel-value/unsigned-array'); const { WebGLKernelValueDynamicUnsignedArray } = require('./kernel-value/dynamic-unsigned-array'); const kernelValueMaps = { unsigned: { dynamic: { 'Boolean': WebGLKernelValueBoolean, 'Interger': WebGLKernelValueInteger, 'Float': WebGLKernelValueFloat, 'Array': WebGLKernelValueDynamicUnsignedArray, 'Input': WebGLKernelValueDynamicUnsignedInput, 'NumberTexture': WebGLKernelValueDynamicNumberTexture, 'ArrayTexture(1)': WebGLKernelValueDynamicNumberTexture, 'ArrayTexture(2)': WebGLKernelValueDynamicNumberTexture, 'ArrayTexture(3)': WebGLKernelValueDynamicNumberTexture, 'ArrayTexture(4)': WebGLKernelValueDynamicNumberTexture, 'MemoryOptimizedNumberTexture': WebGLKernelValueDynamicMemoryOptimizedNumberTexture, 'HTMLImage': WebGLKernelValueDynamicHTMLImage, 'HTMLImageArray': false, }, static: { 'Boolean': WebGLKernelValueBoolean, 'Interger': WebGLKernelValueInteger, 'Float': WebGLKernelValueFloat, 'Integer': WebGLKernelValueInteger, 'Array': WebGLKernelValueUnsignedArray, 'Input': WebGLKernelValueUnsignedInput, 'NumberTexture': WebGLKernelValueNumberTexture, 'ArrayTexture(1)': WebGLKernelValueNumberTexture, 'ArrayTexture(2)': WebGLKernelValueNumberTexture, 'ArrayTexture(3)': WebGLKernelValueNumberTexture, 'ArrayTexture(4)': WebGLKernelValueNumberTexture, 'MemoryOptimizedNumberTexture': WebGLKernelValueDynamicMemoryOptimizedNumberTexture, 'HTMLImage': WebGLKernelValueHTMLImage, 'HTMLImageArray': false, } }, single: { dynamic: { 'Boolean': WebGLKernelValueBoolean, 'Interger': WebGLKernelValueInteger, 'Float': WebGLKernelValueFloat, 'Array': WebGLKernelValueDynamicSingleArray, 'Input': WebGLKernelValueDynamicSingleInput, 'NumberTexture': WebGLKernelValueDynamicNumberTexture, 'ArrayTexture(1)': WebGLKernelValueDynamicNumberTexture, 'ArrayTexture(2)': WebGLKernelValueDynamicNumberTexture, 'ArrayTexture(3)': WebGLKernelValueDynamicNumberTexture, 'ArrayTexture(4)': WebGLKernelValueDynamicNumberTexture, 'MemoryOptimizedNumberTexture': WebGLKernelValueDynamicMemoryOptimizedNumberTexture, 'HTMLImage': WebGLKernelValueDynamicHTMLImage, 'HTMLImageArray': false, }, static: { 'Boolean': WebGLKernelValueBoolean, 'Interger': WebGLKernelValueInteger, 'Float': WebGLKernelValueFloat, 'Integer': WebGLKernelValueInteger, 'Array': WebGLKernelValueSingleArray, 'Input': WebGLKernelValueSingleInput, 'NumberTexture': WebGLKernelValueNumberTexture, 'ArrayTexture(1)': WebGLKernelValueNumberTexture, 'ArrayTexture(2)': WebGLKernelValueNumberTexture, 'ArrayTexture(3)': WebGLKernelValueNumberTexture, 'ArrayTexture(4)': WebGLKernelValueNumberTexture, 'MemoryOptimizedNumberTexture': WebGLKernelValueMemoryOptimizedNumberTexture, 'HTMLImage': WebGLKernelValueHTMLImage, 'HTMLImageArray': false, } }, }; function lookupKernelValueType(type, dynamic, precision) { if (!type) { throw new Error('type missing'); } if (!dynamic) { throw new Error('dynamic missing'); } if (!precision) { throw new Error('precision missing'); } const types = kernelValueMaps[precision][dynamic]; if (types[type] === false) { return null; } else if (types[type] === undefined) { throw new Error(`Could not find a KernelValue for ${ type }`); } return types[type]; } module.exports = { lookupKernelValueType }; },{"./kernel-value/boolean":18,"./kernel-value/dynamic-html-image":19,"./kernel-value/dynamic-memory-optimized-number-texture":20,"./kernel-value/dynamic-number-texture":21,"./kernel-value/dynamic-single-array":22,"./kernel-value/dynamic-single-input":23,"./kernel-value/dynamic-unsigned-array":24,"./kernel-value/dynamic-unsigned-input":25,"./kernel-value/float":26,"./kernel-value/html-image":27,"./kernel-value/integer":29,"./kernel-value/memory-optimized-number-texture":30,"./kernel-value/number-texture":31,"./kernel-value/single-array":32,"./kernel-value/single-input":33,"./kernel-value/unsigned-array":34,"./kernel-value/unsigned-input":35}],18:[function(require,module,exports){ const { utils } = require('../../../utils'); const { WebGLKernelValue } = require('./index'); class WebGLKernelValueBoolean extends WebGLKernelValue { constructor(value, settings) { super(value, settings); this.uploadValue = value; } getSource(value) { if (this.origin === 'constants') { return `const bool ${this.id} = ${value};\n`; } return `uniform bool ${this.id};\n`; } getStringValueHandler() { return `const uploadValue_${this.name} = ${this.name};\n`; } updateValue(value) { if (this.origin === 'constants') return; this.kernel.setUniform1i(this.id, this.uploadValue = value); } } module.exports = { WebGLKernelValueBoolean }; },{"../../../utils":69,"./index":28}],19:[function(require,module,exports){ const { utils } = require('../../../utils'); const { WebGLKernelValueHTMLImage } = require('./html-image'); class WebGLKernelValueDynamicInput extends WebGLKernelValueHTMLImage { getSource() { return utils.linesToString([ `uniform sampler2D ${this.id}`, `uniform ivec2 ${this.sizeId}`, `uniform ivec3 ${this.dimensionsId}`, ]); } updateValue(value) { const { width, height } = value; this.dimensions = [width, height, 1]; this.textureSize = [width, height]; this.kernel.setUniform3iv(this.dimensionsId, this.dimensions); this.kernel.setUniform2iv(this.sizeId, this.textureSize); super.updateValue(value); } } module.exports = { WebGLKernelValueDynamicInput }; },{"../../../utils":69,"./html-image":27}],20:[function(require,module,exports){ const { utils } = require('../../../utils'); const { WebGLKernelValueMemoryOptimizedNumberTexture } = require('./memory-optimized-number-texture'); class WebGLKernelValueDynamicMemoryOptimizedNumberTexture extends WebGLKernelValueMemoryOptimizedNumberTexture { getSource() { return utils.linesToString([ `uniform sampler2D ${this.id}`, `uniform ivec2 ${this.sizeId}`, `uniform ivec3 ${this.dimensionsId}`, ]); } updateValue(inputTexture) { this.dimensions = inputTexture.dimensions; this.textureSize = inputTexture.size; this.kernel.setUniform3iv(this.dimensionsId, this.dimensions); this.kernel.setUniform2iv(this.sizeId, this.textureSize); super.updateValue(inputTexture); } } module.exports = { WebGLKernelValueDynamicMemoryOptimizedNumberTexture }; },{"../../../utils":69,"./memory-optimized-number-texture":30}],21:[function(require,module,exports){ const { utils } = require('../../../utils'); const { WebGLKernelValueNumberTexture } = require('./number-texture'); class WebGLKernelValueDynamicNumberTexture extends WebGLKernelValueNumberTexture { getSource() { return utils.linesToString([ `uniform sampler2D ${this.id}`, `uniform ivec2 ${this.sizeId}`, `uniform ivec3 ${this.dimensionsId}`, ]); } updateValue(value) { this.dimensions = inputTexture.dimensions; this.textureSize = inputTexture.size; this.kernel.setUniform3iv(this.dimensionsId, this.dimensions); this.kernel.setUniform2iv(this.sizeId, this.textureSize); super.updateValue(value); } } module.exports = { WebGLKernelValueDynamicNumberTexture }; },{"../../../utils":69,"./number-texture":31}],22:[function(require,module,exports){ const { utils } = require('../../../utils'); const { WebGLKernelValueSingleArray } = require('./single-array'); class WebGLKernelValueDynamicSingleArray extends WebGLKernelValueSingleArray { getSource() { return utils.linesToString([ `uniform sampler2D ${this.id}`, `uniform ivec2 ${this.sizeId}`, `uniform ivec3 ${this.dimensionsId}`, ]); } updateValue(value) { this.dimensions = utils.getDimensions(value, true); this.textureSize = utils.getMemoryOptimizedFloatTextureSize(this.dimensions, this.bitRatio); this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * this.bitRatio; this.uploadValue = new Float32Array(this.uploadArrayLength); this.kernel.setUniform3iv(this.dimensionsId, this.dimensions); this.kernel.setUniform2iv(this.sizeId, this.textureSize); super.updateValue(value); } } module.exports = { WebGLKernelValueDynamicSingleArray }; },{"../../../utils":69,"./single-array":32}],23:[function(require,module,exports){ const { utils } = require('../../../utils'); const { WebGLKernelValueSingleInput } = require('./single-input'); class WebGLKernelValueDynamicSingleInput extends WebGLKernelValueSingleInput { getSource() { return utils.linesToString([ `uniform sampler2D ${this.id}`, `uniform ivec2 ${this.sizeId}`, `uniform ivec3 ${this.dimensionsId}`, ]); } updateValue(value) { this.dimensions = value.size; this.textureSize = utils.getMemoryOptimizedFloatTextureSize(this.dimensions, this.bitRatio); this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * (4 / this.bitRatio); this.uploadValue = new Float32Array(this.uploadArrayLength); this.kernel.setUniform3iv(this.dimensionsId, this.dimensions); this.kernel.setUniform2iv(this.sizeId, this.textureSize); super.updateValue(value); } } module.exports = { WebGLKernelValueDynamicSingleInput }; },{"../../../utils":69,"./single-input":33}],24:[function(require,module,exports){ const { utils } = require('../../../utils'); const { WebGLKernelValueUnsignedArray } = require('./unsigned-array'); class WebGLKernelValueDynamicUnsignedArray extends WebGLKernelValueUnsignedArray { getSource() { return utils.linesToString([ `uniform sampler2D ${this.id}`, `uniform ivec2 ${this.sizeId}`, `uniform ivec3 ${this.dimensionsId}`, ]); } updateValue(value) { this.dimensions = utils.getDimensions(value, true); this.textureSize = utils.getMemoryOptimizedPackedTextureSize(this.dimensions, this.bitRatio); this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * (4 / this.bitRatio); const Type = this.getTransferArrayType(value); this.preUploadValue = new Type(this.uploadArrayLength); this.uploadValue = new Uint8Array(this.preUploadValue.buffer); this.kernel.setUniform3iv(this.dimensionsId, this.dimensions); this.kernel.setUniform2iv(this.sizeId, this.textureSize); super.updateValue(value); } } module.exports = { WebGLKernelValueDynamicUnsignedArray }; },{"../../../utils":69,"./unsigned-array":34}],25:[function(require,module,exports){ const { utils } = require('../../../utils'); const { WebGLKernelValueUnsignedInput } = require('./unsigned-input'); class WebGLKernelValueDynamicUnsignedInput extends WebGLKernelValueUnsignedInput { getSource() { return utils.linesToString([ `uniform sampler2D ${this.id}`, `uniform ivec2 ${this.sizeId}`, `uniform ivec3 ${this.dimensionsId}`, ]); } updateValue(value) { this.dimensions = value.size; 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); this.preUploadValue = new Type(this.uploadArrayLength); this.uploadValue = new Uint8Array(this.preUploadValue.buffer); this.kernel.setUniform3iv(this.dimensionsId, this.dimensions); this.kernel.setUniform2iv(this.sizeId, this.textureSize); super.updateValue(value); } } module.exports = { WebGLKernelValueDynamicUnsignedInput }; },{"../../../utils":69,"./unsigned-input":35}],26:[function(require,module,exports){ const { utils } = require('../../../utils'); const { WebGLKernelValue } = require('./index'); class WebGLKernelValueFloat extends WebGLKernelValue { constructor(value, settings) { super(value, settings); this.uploadValue = value; } getSource(value) { if (this.origin === 'constants') { if (Number.isInteger(value)) { return `const float ${this.id} = ${value}.0;\n`; } return `const float ${this.id} = ${value};\n`; } return `uniform float ${this.id};\n`; } updateValue(value) { if (this.origin === 'constants') return; this.kernel.setUniform1f(this.id, this.uploadValue = value); } } module.exports = { WebGLKernelValueFloat }; },{"../../../utils":69,"./index":28}],27:[function(require,module,exports){ const { utils } = require('../../../utils'); const { WebGLKernelValue } = require('./index'); class WebGLKernelValueHTMLImage extends WebGLKernelValue { constructor(value, settings) { super(value, settings); const { width, height } = value; this.dimensions = [width, height, 1]; this.requestTexture(); this.textureSize = [width, height]; this.uploadValue = value; } getSource() { return utils.linesToString([ `uniform sampler2D ${this.id}`, `ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`, `ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`, ]); } updateValue(inputImage) { const { context: gl } = this; gl.activeTexture(this.contextHandle); gl.bindTexture(gl.TEXTURE_2D, this.texture); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this.uploadValue = inputImage); this.kernel.setUniform1i(this.id, this.index); } } module.exports = { WebGLKernelValueHTMLImage }; },{"../../../utils":69,"./index":28}],28:[function(require,module,exports){ const { utils } = require('../../../utils'); const { Input } = require('../../../input'); const { KernelValue } = require('../../kernel-Value'); class WebGLKernelValue extends KernelValue { constructor(value, settings) { super(value, settings); this.dimensionsId = null; this.sizeId = null; this.onRequestTexture = settings.onRequestTexture; this.uploadValue = null; } requestTexture() { this.dimensionsId = this.id + 'Dim'; this.sizeId = this.id + 'Size'; this.texture = this.onRequestTexture(); } getTransferArrayType(value) { if (Array.isArray(value[0])) { return this.getTransferArrayType(value[0]); } if (value.constructor === Array) { return Float32Array; } return value.constructor; } formatArrayTransfer(value, length, Type) { if (utils.isArray(value[0]) || this.optimizeFloatMemory) { const valuesFlat = new Float32Array(length); utils.flattenTo(value, valuesFlat); return valuesFlat; } else { switch (value.constructor) { case Uint8Array: case Int8Array: case Uint16Array: case Int16Array: case Float32Array: case Int32Array: const valuesFlat = new(Type || value.constructor)(length); utils.flattenTo(value, valuesFlat); return valuesFlat; default: { const valuesFlat = new Float32Array(length); utils.flattenTo(value, valuesFlat); return valuesFlat; } } } } getBitRatio(value) { if (Array.isArray(value[0])) { return this.getBitRatio(value[0]); } else if (value.constructor === Input) { return this.getBitRatio(value.value); } switch (value.constructor) { case Uint8Array: case Int8Array: return 1; case Uint16Array: case Int16Array: return 2; case Float32Array: case Int32Array: default: return 4; } } getStringValueHandler() { throw new Error(`"getStringValueHandler" not implemented on ${this.constructor.name}`); } } module.exports = { WebGLKernelValue }; },{"../../../input":65,"../../../utils":69,"../../kernel-Value":13}],29:[function(require,module,exports){ const { utils } = require('../../../utils'); const { WebGLKernelValue } = require('./index'); class WebGLKernelValueInteger extends WebGLKernelValue { constructor(value, settings) { super(value, settings); this.uploadValue = value; } getSource(value) { if (this.origin === 'constants') { return `const int ${this.id} = ${ parseInt(value) };\n`; } return `uniform int ${this.id};\n`; } updateValue(value) { if (this.origin === 'constants') return; this.kernel.setUniform1i(this.id, this.uploadValue = value); } } module.exports = { WebGLKernelValueInteger }; },{"../../../utils":69,"./index":28}],30:[function(require,module,exports){ const { utils } = require('../../../utils'); const { WebGLKernelValue } = require('./index'); class WebGLKernelValueMemoryOptimizedNumberTexture extends WebGLKernelValue { constructor(value, settings) { super(value, settings); this.requestTexture(); this.dimensions = value.dimensions; this.textureSize = value.size; this.uploadValue = value.texture; } getSource() { return utils.linesToString([ `uniform sampler2D ${this.id}`, `ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`, `ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`, ]); } updateValue(inputTexture) { if (inputTexture.context !== this.context) { throw new Error(`Value ${this.name} (${this.type }) must be from same context`); } const { context: gl } = this; gl.activeTexture(this.contextHandle); gl.bindTexture(gl.TEXTURE_2D, this.uploadValue = inputTexture.texture); this.kernel.setUniform1i(this.id, this.index); } } module.exports = { WebGLKernelValueMemoryOptimizedNumberTexture }; },{"../../../utils":69,"./index":28}],31:[function(require,module,exports){ const { utils } = require('../../../utils'); const { WebGLKernelValue } = require('./index'); class WebGLKernelValueNumberTexture extends WebGLKernelValue { constructor(value, settings) { super(value, settings); this.requestTexture(); const { size: textureSize, dimensions } = value; this.bitRatio = this.getBitRatio(value); this.dimensions = dimensions; this.textureSize = textureSize; this.uploadValue = value.texture; } getStringValueHandler() { return `const uploadValue_${this.name} = ${this.name};\n`; } getSource() { return utils.linesToString([ `uniform sampler2D ${this.id}`, `ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`, `ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`, ]); } updateValue(inputTexture) { if (inputTexture.context !== this.context) { throw new Error(`Value ${this.name} (${this.type}) must be from same context`); } const { context: gl } = this; gl.activeTexture(this.contextHandle); gl.bindTexture(gl.TEXTURE_2D, this.uploadValue = inputTexture.texture); } } module.exports = { WebGLKernelValueNumberTexture }; },{"../../../utils":69,"./index":28}],32:[function(require,module,exports){ const { utils } = require('../../../utils'); const { WebGLKernelValue } = require('./index'); class WebGLKernelValueSingleArray extends WebGLKernelValue { constructor(value, settings) { super(value, settings); this.requestTexture(); this.bitRatio = 4; this.dimensions = utils.getDimensions(value, true); this.textureSize = utils.getMemoryOptimizedFloatTextureSize(this.dimensions, this.bitRatio); this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * this.bitRatio; this.uploadValue = new Float32Array(this.uploadArrayLength); } getStringValueHandler() { return utils.linesToString([ `const uploadValue_${this.name} = new Float32Array(${this.uploadArrayLength})`, `flattenTo(${this.name}, uploadValue_${this.name})`, ]); } getSource() { return utils.linesToString([ `uniform sampler2D ${this.id}`, `ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`, `ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`, ]); } updateValue(value) { const { context: gl } = this; utils.flattenTo(value, this.uploadValue); gl.activeTexture(this.contextHandle); gl.bindTexture(gl.TEXTURE_2D, this.texture); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.textureSize[0], this.textureSize[1], 0, gl.RGBA, gl.FLOAT, this.uploadValue); this.kernel.setUniform1i(this.id, this.index); } } module.exports = { WebGLKernelValueSingleArray }; },{"../../../utils":69,"./index":28}],33:[function(require,module,exports){ const { utils } = require('../../../utils'); const { WebGLKernelValue } = require('./index'); class WebGLKernelValueSingleInput extends WebGLKernelValue { constructor(value, settings) { super(value, settings); this.requestTexture(); this.bitRatio = 4; this.dimensions = value.size; this.textureSize = utils.getMemoryOptimizedFloatTextureSize(this.dimensions, this.bitRatio); this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * this.bitRatio; this.uploadValue = new Float32Array(this.uploadArrayLength); } getStringValueHandler() { return utils.linesToString([ `const uploadValue_${this.name} = new Float32Array(${this.uploadArrayLength})`, `flattenTo(${this.name}.value, uploadValue_${this.name})`, ]); } getSource() { return utils.linesToString([ `uniform sampler2D ${this.id}`, `ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`, `ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`, ]); } updateValue(input) { const { context: gl } = this; utils.flattenTo(input.value, this.uploadValue); gl.activeTexture(this.contextHandle); gl.bindTexture(gl.TEXTURE_2D, this.texture); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.textureSize[0], this.textureSize[1], 0, gl.RGBA, gl.FLOAT, this.uploadValue); this.kernel.setUniform1i(this.id, this.index); } } module.exports = { WebGLKernelValueSingleInput }; },{"../../../utils":69,"./index":28}],34:[function(require,module,exports){ const { utils } = require('../../../utils'); const { WebGLKernelValue } = require('./index'); class WebGLKernelValueUnsignedArray extends WebGLKernelValue { constructor(value, settings) { super(value, settings); this.requestTexture(); this.bitRatio = this.getBitRatio(value); this.dimensions = utils.getDimensions(value, true); this.textureSize = utils.getMemoryOptimizedPackedTextureSize(this.dimensions, this.bitRatio); this.uploadArrayLength = this.textureSize[0] * this.textureSize[1] * (4 / this.bitRatio); this.TranserArrayType = this.getTransferArrayType(value); this.preUploadValue = new this.TranserArrayType(this.uploadArrayLength); this.uploadValue = new Uint8Array(this.preUploadValue.buffer); } getStringValueHandler() { return utils.linesToString([ `const preUploadValue_${this.name} = new ${this.TranserArrayType.name}(${this.uploadArrayLength})`, `const uploadValue_${this.name} = new Uint8Array(preUploadValue_${this.name}.buffer)`, `flattenTo(${this.name}, preUploadValue_${this.name})`, ]); } getSource() { return utils.linesToString([ `uniform sampler2D ${this.id}`, `ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`, `ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`, ]); } updateValue(value) { const { context: gl } = this; utils.flattenTo(value, this.preUploadValue); gl.activeTexture(this.contextHandle); gl.bindTexture(gl.TEXTURE_2D, this.texture); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.textureSize[0], this.textureSize[1], 0, gl.RGBA, gl.UNSIGNED_BYTE, this.uploadValue); this.kernel.setUniform1i(this.id, this.index); } } module.exports = { WebGLKernelValueUnsignedArray }; },{"../../../utils":69,"./index":28}],35:[function(require,module,exports){ const { utils } = require('../../../utils'); const { WebGLKernelValue } = require('./index'); class WebGLKernelValueUnsignedInput extends WebGLKernelValue { constructor(value, settings) { super(value, settings); this.requestTexture(); this.bitRatio = this.getBitRatio(value); this.dimensions = value.size; 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); this.preUploadValue = new Type(this.uploadArrayLength); this.uploadValue = new Uint8Array(this.preUploadValue.buffer); } getStringValueHandler() { return utils.linesToString([ `const preUploadValue_${this.name} = new ${this.TranserArrayType.name}(${this.uploadArrayLength})`, `const uploadValue_${this.name} = new Uint8Array(preUploadValue_${this.name}.buffer)`, `flattenTo(${this.name}, preUploadValue_${this.name})`, ]); } getSource() { return utils.linesToString([ `uniform sampler2D ${this.id}`, `ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`, `ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`, ]); } updateValue(input) { const { context: gl } = this; utils.flattenTo(input.value, this.preUploadValue); gl.activeTexture(this.contextHandle); gl.bindTexture(gl.TEXTURE_2D, this.texture); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.textureSize[0], this.textureSize[1], 0, gl.RGBA, gl.UNSIGNED_BYTE, this.uploadValue); this.kernel.setUniform1i(this.id, this.index); } } module.exports = { WebGLKernelValueUnsignedInput }; },{"../../../utils":69,"./index":28}],36:[function(require,module,exports){ const { GLKernel } = require('../gl/kernel'); const { FunctionBuilder } = require('../function-builder'); const { WebGLFunctionNode } = require('./function-node'); const { utils } = require('../../utils'); const { Texture } = require('../../texture'); const triangleNoise = require('../../plugins/triangle-noise'); const { fragmentShader } = require('./fragment-shader'); const { vertexShader } = require('./vertex-shader'); const { glKernelString } = require('../gl/kernel-string'); const { lookupKernelValueType } = require('./kernel-value-maps'); let isSupported = null; let testCanvas = null; let testContext = null; let testExtensions = null; let features = null; const plugins = [triangleNoise]; const canvases = []; const maxTexSizes = {}; class WebGLKernel extends GLKernel { static get isSupported() { if (isSupported !== null) { return isSupported; } this.setupFeatureChecks(); isSupported = this.isContextMatch(testContext); return isSupported; } static setupFeatureChecks() { if (typeof document !== 'undefined') { testCanvas = document.createElement('canvas'); } else if (typeof OffscreenCanvas !== 'undefined') { testCanvas = new OffscreenCanvas(0, 0); } if (!testCanvas) return; testContext = testCanvas.getContext('webgl') || testCanvas.getContext('experimental-webgl'); if (!testContext || !testContext.getExtension) return; testExtensions = { OES_texture_float: testContext.getExtension('OES_texture_float'), OES_texture_float_linear: testContext.getExtension('OES_texture_float_linear'), OES_element_index_uint: testContext.getExtension('OES_element_index_uint'), WEBGL_draw_buffers: testContext.getExtension('WEBGL_draw_buffers'), }; features = this.getFeatures(); } static isContextMatch(context) { if (typeof WebGLRenderingContext !== 'undefined') { return context instanceof WebGLRenderingContext; } return false; } static getFeatures() { const isDrawBuffers = this.getIsDrawBuffers(); return Object.freeze({ isFloatRead: this.getIsFloatRead(), isIntegerDivisionAccurate: this.getIsIntegerDivisionAccurate(), isTextureFloat: this.getIsTextureFloat(), isDrawBuffers, kernelMap: isDrawBuffers, channelCount: this.getChannelCount(), }); } static getIsTextureFloat() { return Boolean(testExtensions.OES_texture_float); } static getIsDrawBuffers() { return Boolean(testExtensions.WEBGL_draw_buffers); } static getChannelCount() { return testExtensions.WEBGL_draw_buffers ? testExtensions.WEBGL_draw_buffers.MAX_DRAW_BUFFERS_WEBGL : 1; } static lookupKernelValueType(type, dynamic, precision) { return lookupKernelValueType(type, dynamic, precision); } static get testCanvas() { return testCanvas; } static get testContext() { return testContext; } static get features() { return features; } static get fragmentShader() { return fragmentShader; } static get vertexShader() { return vertexShader; } constructor(source, settings) { super(source, settings); this.textureCache = {}; this.threadDim = {}; this.programUniformLocationCache = {}; this.framebuffer = null; this.buffer = null; this.program = null; this.pipeline = settings.pipeline; this.endianness = utils.systemEndianness(); this.extensions = {}; this.subKernelOutputTextures = null; this.kernelArguments = null; this.argumentsLength = 0; this.constantsLength = 0; this.compiledFragmentShader = null; this.compiledVertexShader = null; this.fragShader = null; this.vertShader = null; this.drawBuffersMap = null; this.outputTexture = null; this.maxTexSize = null; this.uniform1fCache = {}; this.uniform1iCache = {}; this.uniform2fCache = {}; this.uniform2fvCache = {}; this.uniform2ivCache = {}; this.uniform3fvCache = {}; this.uniform3ivCache = {}; this.mergeSettings(source.settings || settings); } initCanvas() { if (typeof document !== 'undefined') { const canvas = document.createElement('canvas'); canvas.width = 2; canvas.height = 2; return canvas; } else if (typeof OffscreenCanvas !== 'undefined') { return new OffscreenCanvas(0, 0); } } initContext() { const settings = { alpha: false, depth: false, antialias: false }; return this.canvas.getContext('webgl', settings) || this.canvas.getContext('experimental-webgl', settings); } initPlugins(settings) { const pluginsToUse = []; const { source } = this; if (typeof source === 'string') { for (let i = 0; i < plugins.length; i++) { const plugin = plugins[i]; if (source.match(plugin.functionMatch)) { pluginsToUse.push(plugin); } } } else if (typeof source === 'object') { if (settings.pluginNames) { for (let i = 0; i < plugins.length; i++) { const plugin = plugins[i]; const usePlugin = settings.pluginNames.some(pluginName => pluginName === plugin.name); if (usePlugin) { pluginsToUse.push(plugin); } } } } return pluginsToUse; } initExtensions() { this.extensions = { OES_texture_float: this.context.getExtension('OES_texture_float'), OES_texture_float_linear: this.context.getExtension('OES_texture_float_linear'), OES_element_index_uint: this.context.getExtension('OES_element_index_uint'), WEBGL_draw_buffers: this.context.getExtension('WEBGL_draw_buffers'), WEBGL_color_buffer_float: this.context.getExtension('WEBGL_color_buffer_float'), }; } validateSettings() { if (!this.validate) { this.texSize = utils.dimToTexSize({ floatTextures: this.optimizeFloatMemory, floatOutput: this.precision === 'single', }, this.output, true); return; } const { features } = this.constructor; if (this.optimizeFloatMemory === true && !features.isTextureFloat) { throw new Error('Float textures are not supported'); } else if (this.precision === 'single' && this.floatOutputForce !== true && !features.isFloatRead) { throw new Error('Single precision not supported'); } else if (!this.graphical && this.precision === null && features.isTextureFloat) { this.precision = features.isFloatRead ? 'single' : 'unsigned'; } if (this.subKernels && this.subKernels.length > 0 && !this.extensions.WEBGL_draw_buffers) { throw new Error('could not instantiate draw buffers extension'); } if (this.fixIntegerDivisionAccuracy === null) { this.fixIntegerDivisionAccuracy = !features.isIntegerDivisionAccurate; } else if (this.fixIntegerDivisionAccuracy && features.isIntegerDivisionAccurate) { this.fixIntegerDivisionAccuracy = false; } this.checkOutput(); if (!this.output || this.output.length === 0) { if (arguments.length !== 1) { throw new Error('Auto output only supported for kernels with only one input'); } const argType = utils.getVariableType(arguments[0], this.strictIntegers); if (argType === 'Array') { this.output = utils.getDimensions(argType); } else if (argType === 'NumberTexture' || argType === 'ArrayTexture(4)') { this.output = arguments[0].output; } else { throw new Error('Auto output not supported for input type: ' + argType); } } if (this.graphical) { if (this.output.length !== 2) { throw new Error('Output must have 2 dimensions on graphical mode'); } if (this.precision === 'precision') { this.precision = 'unsigned'; console.warn('Cannot use graphical mode and single precision at the same time'); } this.texSize = utils.clone(this.output); return; } else if (this.precision === null && features.isTextureFloat) { this.precision = 'single'; } this.texSize = utils.dimToTexSize({ floatTextures: this.floatTextures, floatOutput: this.precision === 'single' }, this.output, true); } updateMaxTexSize() { const { texSize, canvas } = this; if (this.maxTexSize === null) { let canvasIndex = canvases.indexOf(canvas); if (canvasIndex === -1) { canvasIndex = canvases.length; canvases.push(canvas); maxTexSizes[canvasIndex] = [texSize[0], texSize[1]]; } this.maxTexSize = maxTexSizes[canvasIndex]; } if (this.maxTexSize[0] < texSize[0]) { this.maxTexSize[0] = texSize[0]; } if (this.maxTexSize[1] < texSize[1]) { this.maxTexSize[1] = texSize[1]; } } _oldtranslateSource() { const functionBuilder = FunctionBuilder.fromKernel(this, WebGLFunctionNode, { fixIntegerDivisionAccuracy: this.fixIntegerDivisionAccuracy }); const translatedSource = functionBuilder.getPrototypeString('kernel'); if (!this.returnType) { this.returnType = functionBuilder.getKernelResultType(); } let requiredChannels = 0; const returnTypes = functionBuilder.getReturnTypes(); for (let i = 0; i < returnTypes.length; i++) { switch (returnTypes[i]) { case 'Float': case 'Number': case 'Integer': requiredChannels++; break; case 'Array(2)': requiredChannels += 2; break; case 'Array(3)': requiredChannels += 3; break; case 'Array(4)': requiredChannels += 4; break; } } if (features && requiredChannels > features.channelCount) { throw new Error('Too many channels!'); } return this.translatedSource = translatedSource; } setupArguments(args) { this.kernelArguments = []; this.argumentsLength = 0; this.argumentTypes = []; this.argumentSizes = []; this.argumentBitRatios = []; if (!this.precision) { } const { context: gl } = this; for (let index = 0; index < args.length; index++) { const value = args[index]; const name = this.argumentNames[index]; const type = utils.getVariableType(value, this.strictIntegers); this.argumentTypes.push(type); const KernelValue = this.constructor.lookupKernelValueType(type, this.dynamicArgument ? 'dynamic' : 'static', this.precision); if (KernelValue === null) { throw new Error('unsupported argument'); continue; } const kernelArgument = new KernelValue(value, { name, origin: 'user', index, context: this.context, kernel: this, contextHandle: gl.TEXTURE0 + this.constantsLength + index, strictIntegers: this.strictIntegers, onRequestTexture: () => { this.argumentsLength++; return this.context.createTexture(); } }); this.kernelArguments.push(kernelArgument); this.argumentSizes.push(kernelArgument.textureSize); this.argumentBitRatios[index] = kernelArgument.bitRatio; } } setupConstants() { const { context: gl } = this; this.kernelConstants = []; this.constantTypes = {}; this.constantBitRatios = {}; let index = 0; for (const name in this.constants) { const value = this.constants[name]; const type = utils.getVariableType(value, this.strictIntegers); this.constantTypes[name] = type; const KernelValue = this.constructor.lookupKernelValueType(type, this.dynamicArgument ? 'dynamic' : 'static', this.precision); if (KernelValue === null) { throw new Error(`unsupported constant ${ type }`); continue; } const kernelValue = new KernelValue(value, { name, origin: 'constants', index, context: this.context, kernel: this, contextHandle: gl.TEXTURE0 + index, strictIntegers: this.strictIntegers, onRequestTexture: () => { this.constantsLength++; return this.context.createTexture(); } }); this.constantBitRatios[name] = kernelValue.bitRatio; this.kernelConstants.push(kernelValue); } } build() { this.initExtensions(); this.validateSettings(); this.setupConstants(); this.setupArguments(arguments); this.updateMaxTexSize(); this.translateSource(); this.pickRenderStrategy(); const { texSize, context: gl, canvas } = this; gl.enable(gl.SCISSOR_TEST); if (this.pipeline && this.precision === 'single') { gl.viewport(0, 0, this.maxTexSize[0], this.maxTexSize[1]); canvas.width = this.maxTexSize[0]; canvas.height = this.maxTexSize[1]; } else { gl.viewport(0, 0, this.maxTexSize[0], this.maxTexSize[1]); canvas.width = this.maxTexSize[0]; canvas.height = this.maxTexSize[1]; } const threadDim = this.threadDim = Array.from(this.output); while (threadDim.length < 3) { threadDim.push(1); } const compiledVertexShader = this.getVertexShader(arguments); const vertShader = gl.createShader(gl.VERTEX_SHADER); gl.shaderSource(vertShader, compiledVertexShader); gl.compileShader(vertShader); this.vertShader = vertShader; const compiledFragmentShader = this.getFragmentShader(arguments); const fragShader = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(fragShader, compiledFragmentShader); gl.compileShader(fragShader); this.fragShader = fragShader; if (this.debug) { console.log('GLSL Shader Output:'); console.log(compiledFragmentShader); } if (!gl.getShaderParameter(vertShader, gl.COMPILE_STATUS)) { throw new Error('Error compiling vertex shader: ' + gl.getShaderInfoLog(vertShader)); } if (!gl.getShaderParameter(fragShader, gl.COMPILE_STATUS)) { throw new Error('Error compiling fragment shader: ' + gl.getShaderInfoLog(fragShader)); } const program = this.program = gl.createProgram(); gl.attachShader(program, vertShader); gl.attachShader(program, fragShader); gl.linkProgram(program); this.framebuffer = gl.createFramebuffer(); this.framebuffer.width = texSize[0]; this.framebuffer.height = texSize[1]; const vertices = new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1 ]); const texCoords = new Float32Array([ 0, 0, 1, 0, 0, 1, 1, 1 ]); const texCoordOffset = vertices.byteLength; let buffer = this.buffer; if (!buffer) { buffer = this.buffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, buffer); gl.bufferData(gl.ARRAY_BUFFER, vertices.byteLength + texCoords.byteLength, gl.STATIC_DRAW); } else { gl.bindBuffer(gl.ARRAY_BUFFER, buffer); } gl.bufferSubData(gl.ARRAY_BUFFER, 0, vertices); gl.bufferSubData(gl.ARRAY_BUFFER, texCoordOffset, texCoords); const aPosLoc = gl.getAttribLocation(this.program, 'aPos'); gl.enableVertexAttribArray(aPosLoc); gl.vertexAttribPointer(aPosLoc, 2, gl.FLOAT, false, 0, 0); const aTexCoordLoc = gl.getAttribLocation(this.program, 'aTexCoord'); gl.enableVertexAttribArray(aTexCoordLoc); gl.vertexAttribPointer(aTexCoordLoc, 2, gl.FLOAT, false, 0, texCoordOffset); gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer); let i = 0; gl.useProgram(this.program); for (let p in this.constants) { this.kernelConstants[i++].updateValue(this.constants[p]); } if (!this.immutable) { this._setupOutputTexture(); if ( this.subKernels !== null && this.subKernels.length > 0 ) { this._setupSubOutputTextures(this.subKernels.length); } } } translateSource() { const functionBuilder = FunctionBuilder.fromKernel(this, WebGLFunctionNode, { fixIntegerDivisionAccuracy: this.fixIntegerDivisionAccuracy }); this.translatedSource = functionBuilder.getPrototypeString('kernel'); if (!this.graphical && !this.returnType) { this.returnType = functionBuilder.getKernelResultType(); } } run() { if (this.program === null) { this.build.apply(this, arguments); } const { kernelArguments } = this; const texSize = this.texSize; const gl = this.context; gl.useProgram(this.program); gl.scissor(0, 0, texSize[0], texSize[1]); if (!this.hardcodeConstants) { this.setUniform3iv('uOutputDim', this.threadDim); this.setUniform2iv('uTexSize', texSize); } this.setUniform2f('ratio', texSize[0] / this.maxTexSize[0], texSize[1] / this.maxTexSize[1]); for (let i = 0; i < kernelArguments.length; i++) { kernelArguments[i].updateValue(arguments[i]); } if (this.plugins) { for (let i = 0; i < this.plugins.length; i++) { const plugin = this.plugins[i]; if (plugin.onBeforeRun) { plugin.onBeforeRun(this); } } } if (this.graphical) { if (this.pipeline) { gl.bindRenderbuffer(gl.RENDERBUFFER, null); gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer); if (!this.outputTexture || this.immutable) { this._setupOutputTexture(); } gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); return new Texture({ texture: this.outputTexture, size: texSize, dimensions: this.threadDim, output: this.output, context: this.context, gpu: this.gpu, type: this.getReturnTextureType(), }); } gl.bindRenderbuffer(gl.RENDERBUFFER, null); gl.bindFramebuffer(gl.FRAMEBUFFER, null); gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); return; } gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer); if (this.immutable) { this._setupOutputTexture(); } if (this.subKernels !== null) { if (this.immutable) { this.subKernelOutputTextures = []; this._setupSubOutputTextures(this.subKernels.length); } this.extensions.WEBGL_draw_buffers.drawBuffersWEBGL(this.drawBuffersMap); } gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); if (this.subKernelOutputTextures !== null) { if (this.subKernels !== null) { const output = { result: this.renderOutput(), }; if (this.pipeline) { for (let i = 0; i < this.subKernels.length; i++) { output[this.subKernels[i].property] = new Texture({ texture: this.subKernelOutputTextures[i], size: texSize, dimensions: this.threadDim, output: this.output, context: this.context, gpu: this.gpu, type: this.getReturnTextureType(), }); } } else { for (let i = 0; i < this.subKernels.length; i++) { output[this.subKernels[i].property] = new Texture({ texture: this.subKernelOutputTextures[i], size: texSize, dimensions: this.threadDim, output: this.output, context: this.context, gpu: this.gpu, type: this.getReturnTextureType(), }).toArray(); } } return output; } } return this.renderOutput(); } getOutputTexture() { return this.outputTexture; } _setupOutputTexture() { const gl = this.context; const texSize = this.texSize; const texture = this.outputTexture = this.context.createTexture(); gl.activeTexture(gl.TEXTURE0 + this.constantsLength + this.argumentNames.length); gl.bindTexture(gl.TEXTURE_2D, texture); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); if (this.precision === 'single') { if (this.pipeline) { switch (this.returnType) { case 'Number': case 'Float': case 'Integer': if (this.optimizeFloatMemory) { gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, texSize[0], texSize[1], 0, gl.RGBA, gl.FLOAT, null); } else { gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, texSize[0], texSize[1], 0, gl.RGBA, gl.FLOAT, null); } break; case 'Array(2)': gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, texSize[0], texSize[1], 0, gl.RGBA, gl.FLOAT, null); break; case 'Array(3)': gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, texSize[0], texSize[1], 0, gl.RGBA, gl.FLOAT, null); break; case 'Array(4)': gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, texSize[0], texSize[1], 0, gl.RGBA, gl.FLOAT, null); break; default: if (!this.graphical) { throw new Error('Unhandled return type'); } } } else { gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, texSize[0], texSize[1], 0, gl.RGBA, gl.FLOAT, null); } } else { gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, texSize[0], texSize[1], 0, gl.RGBA, gl.UNSIGNED_BYTE, null); } gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); } _setupSubOutputTextures(length) { const gl = this.context; const texSize = this.texSize; const drawBuffersMap = this.drawBuffersMap = [gl.COLOR_ATTACHMENT0]; const textures = this.subKernelOutputTextures = []; for (let i = 0; i < length; i++) { const texture = this.context.createTexture(); textures.push(texture); drawBuffersMap.push(gl.COLOR_ATTACHMENT0 + i + 1); gl.activeTexture(gl.TEXTURE0 + this.constantsLength + this.argumentNames.length + i); gl.bindTexture(gl.TEXTURE_2D, texture); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); if (this.precision === 'single') { gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, texSize[0], texSize[1], 0, gl.RGBA, gl.FLOAT, null); } else { gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, texSize[0], texSize[1], 0, gl.RGBA, gl.UNSIGNED_BYTE, null); } gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + i + 1, gl.TEXTURE_2D, texture, 0); } } getArgumentTexture(name) { return this.getTextureCache(`ARGUMENT_${name}`); } getTextureCache(name) { if (this.textureCache.hasOwnProperty(name)) { return this.textureCache[name]; } return this.textureCache[name] = this.context.createTexture(); } detachTextureCache(name) { delete this.textureCache[name]; } setUniform1f(name, value) { if (this.uniform1fCache.hasOwnProperty(name)) { const cache = this.uniform1fCache[name]; if (value === cache) { return; } } this.uniform1fCache[name] = value; const loc = this.getUniformLocation(name); this.context.uniform1f(loc, value); } setUniform1i(name, value) { if (this.uniform1iCache.hasOwnProperty(name)) { const cache = this.uniform1iCache[name]; if (value === cache) { return; } } this.uniform1iCache[name] = value; const loc = this.getUniformLocation(name); this.context.uniform1i(loc, value); } setUniform2f(name, value1, value2) { if (this.uniform2fCache.hasOwnProperty(name)) { const cache = this.uniform2fCache[name]; if ( value1 === cache[0] && value2 === cache[1] ) { return; } } this.uniform2fCache[name] = [value1, value2]; const loc = this.getUniformLocation(name); this.context.uniform2f(loc, value1, value2); } setUniform2fv(name, value) { if (this.uniform2fvCache.hasOwnProperty(name)) { const cache = this.uniform2fvCache[name]; if ( value[0] === cache[0] && value[1] === cache[1] ) { return; } } this.uniform2fvCache[name] = value; const loc = this.getUniformLocation(name); this.context.uniform2fv(loc, value); } setUniform2iv(name, value) { if (this.uniform2ivCache.hasOwnProperty(name)) { const cache = this.uniform2ivCache[name]; if ( value[0] === cache[0] && value[1] === cache[1] ) { return; } } this.uniform2ivCache[name] = value; const loc = this.getUniformLocation(name); this.context.uniform2iv(loc, value); } setUniform3fv(name, value) { if (this.uniform3fvCache.hasOwnProperty(name)) { const cache = this.uniform3fvCache[name]; if ( value[0] === cache[0] && value[1] === cache[1] && value[2] === cache[2] ) { return; } } this.uniform3fvCache[name] = value; const loc = this.getUniformLocation(name); this.context.uniform3fv(loc, value); } setUniform3iv(name, value) { if (this.uniform3ivCache.hasOwnProperty(name)) { const cache = this.uniform3ivCache[name]; if ( value[0] === cache[0] && value[1] === cache[1] && value[2] === cache[2] ) { return; } } this.uniform3ivCache[name] = value; const loc = this.getUniformLocation(name); this.context.uniform3iv(loc, value); } getUniformLocation(name) { if (this.programUniformLocationCache.hasOwnProperty(name)) { return this.programUniformLocationCache[name]; } return this.programUniformLocationCache[name] = this.context.getUniformLocation(this.program, name); } _getFragShaderArtifactMap(args) { return { HEADER: this._getHeaderString(), LOOP_MAX: this._getLoopMaxString(), PLUGINS: this._getPluginsString(), CONSTANTS: this._getConstantsString(), DECODE32_ENDIANNESS: this._getDecode32EndiannessString(), ENCODE32_ENDIANNESS: this._getEncode32EndiannessString(), DIVIDE_WITH_INTEGER_CHECK: this._getDivideWithIntegerCheckString(), MAIN_CONSTANTS: this._getMainConstantsString(), MAIN_ARGUMENTS: this._getMainArgumentsString(args), KERNEL: this.getKernelString(), MAIN_RESULT: this.getMainResultString() }; } _getHeaderString() { return ( this.subKernels !== null ? '#extension GL_EXT_draw_buffers : require\n' : '' ); } _getLoopMaxString() { return ( this.loopMaxIterations ? ` ${parseInt(this.loopMaxIterations)};\n` : ' 1000;\n' ); } _getPluginsString() { if (!this.plugins) return '\n'; return this.plugins.map(plugin => plugin.source && this.source.match(plugin.functionMatch) ? plugin.source : '').join('\n'); } _getConstantsString() { const result = []; const { threadDim, texSize } = this; if (this.hardcodeConstants) { result.push( `ivec3 uOutputDim = ivec3(${threadDim[0]}, ${threadDim[1]}, ${threadDim[2]})`, `ivec2 uTexSize = ivec2(${texSize[0]}, ${texSize[1]})` ); } else { result.push( 'uniform ivec3 uOutputDim', 'uniform ivec2 uTexSize' ); } return utils.linesToString(result); } _getTextureCoordinate() { const subKernels = this.subKernels; if (subKernels === null || subKernels.length < 1) { return 'varying vec2 vTexCoord;\n'; } else { return 'out vec2 vTexCoord;\n'; } } _getDecode32EndiannessString() { return ( this.endianness === 'LE' ? '' : ' texel.rgba = texel.abgr;\n' ); } _getEncode32EndiannessString() { return ( this.endianness === 'LE' ? '' : ' texel.rgba = texel.abgr;\n' ); } _getDivideWithIntegerCheckString() { return this.fixIntegerDivisionAccuracy ? `float div_with_int_check(float x, float y) { if (floor(x) == x && floor(y) == y && integerMod(x, y) == 0.0) { return float(int(x)/int(y)); } return x / y; }` : ''; } _getMainArgumentsString(args) { const results = []; const { argumentNames } = this; for (let i = 0; i < argumentNames.length; i++) { results.push(this.kernelArguments[i].getSource(args[i])); } return results.join(''); } _getMainConstantsString() { const result = []; const { constants } = this; if (constants) { let i = 0; for (const name in constants) { result.push(this.kernelConstants[i++].getSource(this.constants[name])); } } return result.join(''); } getKernelString() { let kernelResultDeclaration; switch (this.returnType) { case 'Array(2)': kernelResultDeclaration = 'vec2 kernelResult'; break; case 'Array(3)': kernelResultDeclaration = 'vec3 kernelResult'; break; case 'Array(4)': kernelResultDeclaration = 'vec4 kernelResult'; break; case 'LiteralInteger': case 'Float': case 'Number': case 'Integer': kernelResultDeclaration = 'float kernelResult'; break; default: if (this.graphical) { kernelResultDeclaration = 'float kernelResult'; } else { throw new Error(`unrecognized output type "${ this.returnType }"`); } } const result = []; const subKernels = this.subKernels; if (subKernels !== null) { result.push( kernelResultDeclaration ); for (let i = 0; i < subKernels.length; i++) { result.push( `float subKernelResult_${ subKernels[i].name } = 0.0` ); } } else { result.push( kernelResultDeclaration ); } return utils.linesToString(result) + this.translatedSource; } getMainResultGraphical() { return utils.linesToString([ ' threadId = indexTo3D(index, uOutputDim)', ' kernel()', ' gl_FragColor = actualColor', ]); } getMainResultPackedPixels() { switch (this.returnType) { case 'LiteralInteger': case 'Number': case 'Integer': case 'Float': return utils.linesToString(this.getMainResultKernelPackedPixels()) + utils.linesToString(this.getMainResultSubKernelPackedPixels()); default: throw new Error(`packed output only usable with Numbers, "${this.returnType}" specified`); } } getMainResultKernelPackedPixels() { return [ ' threadId = indexTo3D(index, uOutputDim)', ' kernel()', ' gl_FragData[0] = encode32(kernelResult)' ]; } getMainResultSubKernelPackedPixels() { const result = []; if (!this.subKernels) return result; for (let i = 0; i < this.subKernels.length; i++) { result.push( ` gl_FragData[${i + 1}] = encode32(subKernelResult_${this.subKernels[i].name})` ); } return result; } getMainResultMemoryOptimizedFloats() { const result = [ ' index *= 4', ]; switch (this.returnType) { case 'Number': case 'Integer': case 'Float': const channels = ['r', 'g', 'b', 'a']; for (let i = 0; i < channels.length; i++) { const channel = channels[i]; this.getMainResultKernelMemoryOptimizedFloats(result, channel); this.getMainResultSubKernelMemoryOptimizedFloats(result, channel); if (i + 1 < channels.length) { result.push(' index += 1'); } } break; default: throw new Error(`optimized output only usable with Numbers, ${this.returnType} specified`); } return utils.linesToString(result); } getMainResultKernelMemoryOptimizedFloats(result, channel) { result.push( ' threadId = indexTo3D(index, uOutputDim)', ' kernel()', ` gl_FragData[0].${channel} = kernelResult`, ); } getMainResultSubKernelMemoryOptimizedFloats(result, channel) { if (!this.subKernels) return result; for (let i = 0; i < this.subKernels.length; i++) { result.push( ` gl_FragData[${i + 1}].${channel} = subKernelResult_${this.subKernels[i].name}`, ); } } getMainResultKernelNumberTexture() { return [ ' threadId = indexTo3D(index, uOutputDim)', ' kernel()', ' gl_FragData[0][0] = kernelResult', ]; } getMainResultSubKernelNumberTexture() { const result = []; if (!this.subKernels) return result; for (let i = 0; i < this.subKernels.length; ++i) { result.push( ` gl_FragData[${i + 1}][0] = subKernelResult_${this.subKernels[i].name}`, ); } return result; } getMainResultKernelArray2Texture() { return [ ' threadId = indexTo3D(index, uOutputDim)', ' kernel()', ' gl_FragData[0][0] = kernelResult[0]', ' gl_FragData[0][1] = kernelResult[1]', ]; } getMainResultSubKernelArray2Texture() { const result = []; if (!this.subKernels) return result; for (let i = 0; i < this.subKernels.length; ++i) { result.push( ` gl_FragData[${i + 1}][0] = subKernelResult_${this.subKernels[i].name}[0]`, ` gl_FragData[${i + 1}][1] = subKernelResult_${this.subKernels[i].name}[1]`, ); } return result; } getMainResultKernelArray3Texture() { return [ ' threadId = indexTo3D(index, uOutputDim)', ' kernel()', ' gl_FragData[0][0] = kernelResult[0]', ' gl_FragData[0][1] = kernelResult[1]', ' gl_FragData[0][2] = kernelResult[2]', ]; } getMainResultSubKernelArray3Texture() { const result = []; if (!this.subKernels) return result; for (let i = 0; i < this.subKernels.length; ++i) { result.push( ` gl_FragData[${i + 1}][0] = subKernelResult_${this.subKernels[i].name}[0]`, ` gl_FragData[${i + 1}][1] = subKernelResult_${this.subKernels[i].name}[1]`, ` gl_FragData[${i + 1}][2] = subKernelResult_${this.subKernels[i].name}[2]`, ); } return result; } getMainResultKernelArray4Texture() { return [ ' threadId = indexTo3D(index, uOutputDim)', ' kernel()', ' gl_FragData[0] = kernelResult', ]; } getMainResultSubKernelArray4Texture() { const result = []; if (!this.subKernels) return result; for (let i = 0; i < this.subKernels.length; ++i) { result.push( ` gl_FragData[${i + 1}] = subKernelResult_${this.subKernels[i].name}`, ); } return result; } _getMainResultString() { const { subKernels, precision, floatTextures, graphical, pipeline } = this; const result = []; if (precision === 'single') { result.push(' index *= 4'); } if (graphical) { result.push( ' threadId = indexTo3D(index, uOutputDim)', ' kernel()', ' gl_FragColor = actualColor', ); } else if (precision === 'single') { const channels = ['r', 'g', 'b', 'a']; for (let i = 0; i < channels.length; ++i) { result.push(' threadId = indexTo3D(index, uOutputDim)'); result.push(' kernel()'); if (subKernels) { result.push(` gl_FragData[0].${channels[i]} = kernelResult`); for (let j = 0; j < subKernels.length; ++j) { result.push(` gl_FragData[${j + 1}].${channels[i]} = subKernelResult_${subKernels[j].name}`); } } else { result.push(` gl_FragColor.${channels[i]} = kernelResult`); } if (i < channels.length - 1) { result.push(' index += 1'); } } } else if (subKernels !== null) { result.push(' threadId = indexTo3D(index, uOutputDim)'); result.push(' kernel()'); result.push(' gl_FragData[0] = encode32(kernelResult)'); for (let i = 0; i < subKernels.length; i++) { result.push(` gl_FragData[${i + 1}] = encode32(subKernelResult_${subKernels[i].name})`); } } else { result.push( ' threadId = indexTo3D(index, uOutputDim)', ' kernel()', ' gl_FragColor = encode32(kernelResult)', ); } return utils.linesToString(result); } replaceArtifacts(src, map) { return src.replace(/[ ]*__([A-Z]+[0-9]*([_]?[A-Z])*)__;\n/g, (match, artifact) => { if (map.hasOwnProperty(artifact)) { return map[artifact]; } throw `unhandled artifact ${artifact}`; }); } getFragmentShader(args) { if (this.compiledFragmentShader !== null) { return this.compiledFragmentShader; } return this.compiledFragmentShader = this.replaceArtifacts(this.constructor.fragmentShader, this._getFragShaderArtifactMap(args)); } getVertexShader(args) { if (this.compiledVertexShader !== null) { return this.compiledVertexShader; } return this.compiledVertexShader = this.constructor.vertexShader; } toString() { const setupContextString = utils.linesToString([ `const gl = context`, ]); return glKernelString(this.constructor, arguments, this, setupContextString); } destroy(removeCanvasReferences) { if (this.outputTexture) { this.context.deleteTexture(this.outputTexture); } if (this.buffer) { this.context.deleteBuffer(this.buffer); } if (this.framebuffer) { this.context.deleteFramebuffer(this.framebuffer); } if (this.vertShader) { this.context.deleteShader(this.vertShader); } if (this.fragShader) { this.context.deleteShader(this.fragShader); } if (this.program) { this.context.deleteProgram(this.program); } const keys = Object.keys(this.textureCache); for (let i = 0; i < keys.length; i++) { const name = keys[i]; this.context.deleteTexture(this.textureCache[name]); } if (this.subKernelOutputTextures) { for (let i = 0; i < this.subKernelOutputTextures.length; i++) { this.context.deleteTexture(this.subKernelOutputTextures[i]); } } if (removeCanvasReferences) { const idx = canvases.indexOf(this.canvas); if (idx >= 0) { canvases[idx] = null; maxTexSizes[idx] = null; } } this.destroyExtensions(); delete this.context; delete this.canvas; } destroyExtensions() { this.extensions.OES_texture_float = null; this.extensions.OES_texture_float_linear = null; this.extensions.OES_element_index_uint = null; this.extensions.WEBGL_draw_buffers = null; } static destroyContext(context) { const extension = context.getExtension('WEBGL_lose_context'); if (extension) { extension.loseContext(); } } toJSON() { const json = super.toJSON(); json.functionNodes = FunctionBuilder.fromKernel(this, WebGLFunctionNode).toJSON(); return json; } } module.exports = { WebGLKernel }; },{"../../plugins/triangle-noise":67,"../../texture":68,"../../utils":69,"../function-builder":8,"../gl/kernel":11,"../gl/kernel-string":10,"./fragment-shader":15,"./function-node":16,"./kernel-value-maps":17,"./vertex-shader":37}],37:[function(require,module,exports){ const vertexShader = `precision highp float; precision highp int; precision highp sampler2D; attribute vec2 aPos; attribute vec2 aTexCoord; varying vec2 vTexCoord; uniform vec2 ratio; void main(void) { gl_Position = vec4((aPos + vec2(1)) * ratio + vec2(-1), 0, 1); vTexCoord = aTexCoord; }`; module.exports = { vertexShader }; },{}],38:[function(require,module,exports){ const fragmentShader = `#version 300 es __HEADER__; precision highp float; precision highp int; precision highp sampler2D; const int LOOP_MAX = __LOOP_MAX__; __PLUGINS__; __CONSTANTS__; in vec2 vTexCoord; vec2 integerMod(vec2 x, float y) { vec2 res = floor(mod(x, y)); return res * step(1.0 - floor(y), -res); } vec3 integerMod(vec3 x, float y) { vec3 res = floor(mod(x, y)); return res * step(1.0 - floor(y), -res); } vec4 integerMod(vec4 x, vec4 y) { vec4 res = floor(mod(x, y)); return res * step(1.0 - floor(y), -res); } float integerMod(float x, float y) { float res = floor(mod(x, y)); return res * (res > floor(y) - 1.0 ? 0.0 : 1.0); } int integerMod(int x, int y) { return x - (y * int(x/y)); } __DIVIDE_WITH_INTEGER_CHECK__; // Here be dragons! // DO NOT OPTIMIZE THIS CODE // YOU WILL BREAK SOMETHING ON SOMEBODY\'S MACHINE // LEAVE IT AS IT IS, LEST YOU WASTE YOUR OWN TIME const vec2 MAGIC_VEC = vec2(1.0, -256.0); const vec4 SCALE_FACTOR = vec4(1.0, 256.0, 65536.0, 0.0); const vec4 SCALE_FACTOR_INV = vec4(1.0, 0.00390625, 0.0000152587890625, 0.0); // 1, 1/256, 1/65536 float decode32(vec4 texel) { __DECODE32_ENDIANNESS__; texel *= 255.0; vec2 gte128; gte128.x = texel.b >= 128.0 ? 1.0 : 0.0; gte128.y = texel.a >= 128.0 ? 1.0 : 0.0; float exponent = 2.0 * texel.a - 127.0 + dot(gte128, MAGIC_VEC); float res = exp2(round(exponent)); texel.b = texel.b - 128.0 * gte128.x; res = dot(texel, SCALE_FACTOR) * exp2(round(exponent-23.0)) + res; res *= gte128.y * -2.0 + 1.0; return res; } float decode16(vec4 texel, int index) { int channel = integerMod(index, 2); return texel[channel*2] * 255.0 + texel[channel*2 + 1] * 65280.0; } float decode8(vec4 texel, int index) { int channel = integerMod(index, 4); return texel[channel] * 255.0; } vec4 encode32(float f) { float F = abs(f); float sign = f < 0.0 ? 1.0 : 0.0; float exponent = floor(log2(F)); float mantissa = (exp2(-exponent) * F); // exponent += floor(log2(mantissa)); vec4 texel = vec4(F * exp2(23.0 - exponent)) * SCALE_FACTOR_INV; texel.rg = integerMod(texel.rg, 256.0); texel.b = integerMod(texel.b, 128.0); texel.a = exponent * 0.5 + 63.5; texel.ba += vec2(integerMod(exponent+127.0, 2.0), sign) * 128.0; texel = floor(texel); texel *= 0.003921569; // 1/255 __ENCODE32_ENDIANNESS__; return texel; } // Dragons end here int index; ivec3 threadId; ivec3 indexTo3D(int idx, ivec3 texDim) { int z = int(idx / (texDim.x * texDim.y)); idx -= z * int(texDim.x * texDim.y); int y = int(idx / texDim.x); int x = int(integerMod(idx, texDim.x)); return ivec3(x, y, z); } float get32(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) { ivec3 xyz = ivec3(x, y, z); int index = xyz.x + texDim.x * (xyz.y + texDim.y * xyz.z); int w = texSize.x; vec2 st = vec2(float(integerMod(index, w)), float(index / w)) + 0.5; vec4 texel = texture(tex, st / vec2(texSize)); return decode32(texel); } float get16(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) { ivec3 xyz = ivec3(x, y, z); int index = xyz.x + (texDim.x * (xyz.y + (texDim.y * xyz.z))); int w = texSize.x * 2; vec2 st = vec2(float(integerMod(index, w)), float(index / w)) + 0.5; vec4 texel = texture(tex, st / vec2(texSize.x * 2, texSize.y)); return decode16(texel, index); } float get8(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) { ivec3 xyz = ivec3(x, y, z); int index = xyz.x + (texDim.x * (xyz.y + (texDim.y * xyz.z))); int w = texSize.x * 4; vec2 st = vec2(float(integerMod(index, w)), float(index / w)) + 0.5; vec4 texel = texture(tex, st / vec2(texSize.x * 4, texSize.y)); return decode8(texel, index); } float getMemoryOptimized32(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) { ivec3 xyz = ivec3(x, y, z); int index = xyz.x + (texDim.x * (xyz.y + (texDim.y * xyz.z))); int channel = integerMod(index, 4); index = index / 4; int w = texSize.x; vec2 st = vec2(float(integerMod(index, w)), float(index / w)) + 0.5; index = index / 4; vec4 texel = texture(tex, st / vec2(texSize)); return texel[channel]; } vec4 getImage2D(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) { ivec3 xyz = ivec3(x, y, z); int index = xyz.x + texDim.x * (xyz.y + texDim.y * xyz.z); int w = texSize.x; vec2 st = vec2(float(integerMod(index, w)), float(index / w)) + 0.5; return texture(tex, st / vec2(texSize)); } vec4 getImage3D(sampler2DArray tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) { ivec3 xyz = ivec3(x, y, z); int index = xyz.x + texDim.x * (xyz.y + texDim.y * xyz.z); int w = texSize.x; vec2 st = vec2(float(integerMod(index, w)), float(index / w)) + 0.5; return texture(tex, vec3(st / vec2(texSize), z)); } float getFloatFromSampler2D(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) { vec4 result = getImage2D(tex, texSize, texDim, z, y, x); return result[0]; } vec2 getVec2FromSampler2D(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) { vec4 result = getImage2D(tex, texSize, texDim, z, y, x); return vec2(result[0], result[1]); } vec3 getVec3FromSampler2D(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) { vec4 result = getImage2D(tex, texSize, texDim, z, y, x); return vec3(result[0], result[1], result[2]); } vec4 getVec4FromSampler2D(sampler2D tex, ivec2 texSize, ivec3 texDim, int z, int y, int x) { return getImage2D(tex, texSize, texDim, z, y, x); } vec4 actualColor; void color(float r, float g, float b, float a) { actualColor = vec4(r,g,b,a); } void color(float r, float g, float b) { color(r,g,b,1.0); } __MAIN_CONSTANTS__; __MAIN_ARGUMENTS__; __KERNEL__; void main(void) { index = int(vTexCoord.s * float(uTexSize.x)) + int(vTexCoord.t * float(uTexSize.y)) * uTexSize.x; __MAIN_RESULT__; }`; module.exports = { fragmentShader }; },{}],39:[function(require,module,exports){ const { WebGLFunctionNode } = require('../web-gl/function-node'); class WebGL2FunctionNode extends WebGLFunctionNode { astIdentifierExpression(idtNode, retArr) { if (idtNode.type !== 'Identifier') { throw this.astErrorOutput( 'IdentifierExpression - not an Identifier', idtNode ); } const type = this.getType(idtNode); if (idtNode.name === 'Infinity') { retArr.push('intBitsToFloat(2139095039)'); } else if (type === 'Boolean') { if (this.argumentNames.indexOf(idtNode.name) > -1) { retArr.push(`bool(user_${idtNode.name})`); } else { retArr.push(`user_${idtNode.name}`); } } else { const userArgumentName = this.getKernelArgumentName(idtNode.name); if (userArgumentName) { retArr.push(`user_${userArgumentName}`); } else { retArr.push(`user_${idtNode.name}`); } } return retArr; } } module.exports = { WebGL2FunctionNode }; },{"../web-gl/function-node":16}],40:[function(require,module,exports){ const { WebGL2KernelValueBoolean } = require('./kernel-value/boolean'); const { WebGL2KernelValueFloat } = require('./kernel-value/float'); const { WebGL2KernelValueInteger } = require('./kernel-value/integer'); const { WebGL2KernelValueHTMLImage } = require('./kernel-value/html-image'); const { WebGL2KernelValueDynamicHTMLImage } = require('./kernel-value/dynamic-html-image'); const { WebGL2KernelValueHtmlImageArray } = require('./kernel-value/html-image-array'); const { WebGL2KernelValueDynamicHtmlImageArray } = require('./kernel-value/dynamic-html-image-array'); const { WebGL2KernelValueSingleInput } = require('./kernel-value/single-input'); const { WebGL2KernelValueDynamicSingleInput } = require('./kernel-value/dynamic-single-input'); const { WebGL2KernelValueUnsignedInput } = require('./kernel-value/unsigned-input'); const { WebGL2KernelValueDynamicUnsignedInput } = require('./kernel-value/dynamic-unsigned-input'); const { WebGL2KernelValueMemoryOptimizedNumberTexture } = require('./kernel-value/memory-optimized-number-texture'); const { WebGL2KernelValueDynamicMemoryOptimizedNumberTexture } = require('./kernel-value/dynamic-memory-optimized-number-texture'); const { WebGL2KernelValueNumberTexture } = require('./kernel-value/number-texture'); const { WebGL2KernelValueDynamicNumberTexture } = require('./kernel-value/dynamic-number-texture'); const { WebGL2KernelValueSingleArray } = require('./kernel-value/single-array'); const { WebGL2KernelValueDynamicSingleArray } = require('./kernel-value/dynamic-single-array'); const { WebGL2KernelValueUnsignedArray } = require('./kernel-value/unsigned-array'); const { WebGL2KernelValueDynamicUnsignedArray } = require('./kernel-value/dynamic-unsigned-array'); const kernelValueMaps = { unsigned: { dynamic: { 'Boolean': WebGL2KernelValueBoolean, 'Interger': WebGL2KernelValueInteger, 'Float': WebGL2KernelValueFloat, 'Array': WebGL2KernelValueDynamicUnsignedArray, 'Input': WebGL2KernelValueDynamicUnsignedInput, 'NumberTexture': WebGL2KernelValueDynamicNumberTexture, 'ArrayTexture(1)': WebGL2KernelValueDynamicNumberTexture, 'ArrayTexture(2)': WebGL2KernelValueDynamicNumberTexture, 'ArrayTexture(3)': WebGL2KernelValueDynamicNumberTexture, 'ArrayTexture(4)': WebGL2KernelValueDynamicNumberTexture, 'MemoryOptimizedNumberTexture': WebGL2KernelValueDynamicMemoryOptimizedNumberTexture, 'HTMLImage': WebGL2KernelValueDynamicHTMLImage, 'HTMLImageArray': WebGL2KernelValueDynamicHtmlImageArray, }, static: { 'Boolean': WebGL2KernelValueBoolean, 'Interger': WebGL2KernelValueInteger, 'Float': WebGL2KernelValueFloat, 'Integer': WebGL2KernelValueInteger, 'Array': WebGL2KernelValueUnsignedArray, 'Input': WebGL2KernelValueUnsignedInput, 'NumberTexture': WebGL2KernelValueNumberTexture, 'ArrayTexture(1)': WebGL2KernelValueNumberTexture, 'ArrayTexture(2)': WebGL2KernelValueNumberTexture, 'ArrayTexture(3)': WebGL2KernelValueNumberTexture, 'ArrayTexture(4)': WebGL2KernelValueNumberTexture, 'MemoryOptimizedNumberTexture': WebGL2KernelValueDynamicMemoryOptimizedNumberTexture, 'HTMLImage': WebGL2KernelValueHTMLImage, 'HTMLImageArray': WebGL2KernelValueHtmlImageArray, } }, single: { dynamic: { 'Boolean': WebGL2KernelValueBoolean, 'Interger': WebGL2KernelValueInteger, 'Float': WebGL2KernelValueFloat, 'Array': WebGL2KernelValueDynamicSingleArray, 'Input': WebGL2KernelValueDynamicSingleInput, 'NumberTexture': WebGL2KernelValueDynamicNumberTexture, 'ArrayTexture(1)': WebGL2KernelValueDynamicNumberTexture, 'ArrayTexture(2)': WebGL2KernelValueDynamicNumberTexture, 'ArrayTexture(3)': WebGL2KernelValueDynamicNumberTexture, 'ArrayTexture(4)': WebGL2KernelValueDynamicNumberTexture, 'MemoryOptimizedNumberTexture': WebGL2KernelValueDynamicMemoryOptimizedNumberTexture, 'HTMLImage': WebGL2KernelValueDynamicHTMLImage, 'HTMLImageArray': WebGL2KernelValueDynamicHtmlImageArray, }, static: { 'Boolean': WebGL2KernelValueBoolean, 'Interger': WebGL2KernelValueInteger, 'Float': WebGL2KernelValueFloat, 'Integer': WebGL2KernelValueInteger, 'Array': WebGL2KernelValueSingleArray, 'Input': WebGL2KernelValueSingleInput, 'NumberTexture': WebGL2KernelValueNumberTexture, 'ArrayTexture(1)': WebGL2KernelValueNumberTexture, 'ArrayTexture(2)': WebGL2KernelValueNumberTexture, 'ArrayTexture(3)': WebGL2KernelValueNumberTexture, 'ArrayTexture(4)': WebGL2KernelValueNumberTexture, 'MemoryOptimizedNumberTexture': WebGL2KernelValueMemoryOptimizedNumberTexture, 'HTMLImage': WebGL2KernelValueHTMLImage, 'HTMLImageArray': WebGL2KernelValueHtmlImageArray, } }, }; function lookupKernelValueType(type, dynamic, precision) { if (!type) { throw new Error('type missing'); } if (!dynamic) { throw new Error('dynamic missing'); } if (!precision) { throw new Error('precision missing'); } const types = kernelValueMaps[precision][dynamic]; if (types[type] === false) { return null; } else if (types[type] === undefined) { throw new Error(`Could not find a KernelValue for ${ type }`); } return types[type]; } module.exports = { lookupKernelValueType }; },{"./kernel-value/boolean":41,"./kernel-value/dynamic-html-image":43,"./kernel-value/dynamic-html-image-array":42,"./kernel-value/dynamic-memory-optimized-number-texture":44,"./kernel-value/dynamic-number-texture":45,"./kernel-value/dynamic-single-array":46,"./kernel-value/dynamic-single-input":47,"./kernel-value/dynamic-unsigned-array":48,"./kernel-value/dynamic-unsigned-input":49,"./kernel-value/float":50,"./kernel-value/html-image":52,"./kernel-value/html-image-array":51,"./kernel-value/integer":53,"./kernel-value/memory-optimized-number-texture":54,"./kernel-value/number-texture":55,"./kernel-value/single-array":56,"./kernel-value/single-input":57,"./kernel-value/unsigned-array":58,"./kernel-value/unsigned-input":59}],41:[function(require,module,exports){ const { WebGLKernelValueBoolean } = require('../../web-gl/kernel-value/boolean'); class WebGL2KernelValueBoolean extends WebGLKernelValueBoolean {} module.exports = { WebGL2KernelValueBoolean }; },{"../../web-gl/kernel-value/boolean":18}],42:[function(require,module,exports){ const { WebGL2KernelValueHtmlImageArray } = require('./html-image-array'); class WebGL2KernelValueDynamicHtmlImageArray extends WebGL2KernelValueHtmlImageArray { getSource() { return utils.linesToString([ `uniform highp sampler2DArray ${this.id}`, `uniform highp ivec2 ${this.sizeId}`, `uniform highp ivec3 ${this.dimensionsId}`, ]); } updateValue(images) { this.dimensions = [images[0].width, images[0].height, images.length]; this.textureSize = [images[0].width, images[0].height]; this.kernel.setUniform3iv(this.dimensionsId, this.dimensions); this.kernel.setUniform2iv(this.sizeId, this.textureSize); super.updateValue(images); } } module.exports = { WebGL2KernelValueDynamicHtmlImageArray }; },{"./html-image-array":51}],43:[function(require,module,exports){ const { utils } = require('../../../utils'); const { WebGLKernelValueDynamicInput } = require('../../web-gl/kernel-value/dynamic-html-image'); class WebGL2KernelValueDynamicInput extends WebGLKernelValueDynamicInput { getSource() { return utils.linesToString([ `uniform highp sampler2D ${this.id}`, `uniform highp ivec2 ${this.sizeId}`, `uniform highp ivec3 ${this.dimensionsId}`, ]); } } module.exports = { WebGL2KernelValueDynamicInput }; },{"../../../utils":69,"../../web-gl/kernel-value/dynamic-html-image":19}],44:[function(require,module,exports){ const { utils } = require('../../../utils'); const { WebGLKernelValueDynamicMemoryOptimizedNumberTexture } = require('../../web-gl/kernel-value/dynamic-memory-optimized-number-texture'); class WebGL2KernelValueDynamicMemoryOptimizedNumberTexture extends WebGLKernelValueDynamicMemoryOptimizedNumberTexture { getSource() { return utils.linesToString([ `uniform sampler2D ${this.id}`, `uniform ivec2 ${this.sizeId}`, `uniform ivec3 ${this.dimensionsId}`, ]); } } module.exports = { WebGL2KernelValueDynamicMemoryOptimizedNumberTexture }; },{"../../../utils":69,"../../web-gl/kernel-value/dynamic-memory-optimized-number-texture":20}],45:[function(require,module,exports){ const { utils } = require('../../../utils'); const { WebGLKernelValueDynamicNumberTexture } = require('../../web-gl/kernel-value/dynamic-number-texture'); class WebGL2KernelValueDynamicNumberTexture extends WebGLKernelValueDynamicNumberTexture { getSource() { return utils.linesToString([ `uniform highp sampler2D ${this.id}`, `uniform highp ivec2 ${this.sizeId}`, `uniform highp ivec3 ${this.dimensionsId}`, ]); } } module.exports = { WebGL2KernelValueDynamicNumberTexture }; },{"../../../utils":69,"../../web-gl/kernel-value/dynamic-number-texture":21}],46:[function(require,module,exports){ const { utils } = require('../../../utils'); const { WebGLKernelValueDynamicSingleArray } = require('../../web-gl/kernel-value/dynamic-single-array'); class WebGL2KernelValueDynamicSingleArray extends WebGLKernelValueDynamicSingleArray { getSource() { return utils.linesToString([ `uniform highp sampler2D ${this.id}`, `uniform highp ivec2 ${this.sizeId}`, `uniform highp ivec3 ${this.dimensionsId}`, ]); } } module.exports = { WebGL2KernelValueDynamicSingleArray }; },{"../../../utils":69,"../../web-gl/kernel-value/dynamic-single-array":22}],47:[function(require,module,exports){ const { utils } = require('../../../utils'); const { WebGLKernelValueDynamicSingleInput } = require('../../web-gl/kernel-value/dynamic-single-input'); class WebGL2KernelValueDynamicSingleInput extends WebGLKernelValueDynamicSingleInput { getSource() { return utils.linesToString([ `uniform highp sampler2D ${this.id}`, `uniform highp ivec2 ${this.sizeId}`, `uniform highp ivec3 ${this.dimensionsId}`, ]); } } module.exports = { WebGL2KernelValueDynamicSingleInput }; },{"../../../utils":69,"../../web-gl/kernel-value/dynamic-single-input":23}],48:[function(require,module,exports){ const { utils } = require('../../../utils'); const { WebGLKernelValueDynamicUnsignedArray } = require('../../web-gl/kernel-value/dynamic-unsigned-array'); class WebGL2KernelValueDynamicUnsignedArray extends WebGLKernelValueDynamicUnsignedArray { getSource() { return utils.linesToString([ `uniform highp sampler2D ${this.id}`, `uniform highp ivec2 ${this.sizeId}`, `uniform highp ivec3 ${this.dimensionsId}`, ]); } } module.exports = { WebGL2KernelValueDynamicUnsignedArray }; },{"../../../utils":69,"../../web-gl/kernel-value/dynamic-unsigned-array":24}],49:[function(require,module,exports){ const { utils } = require('../../../utils'); const { WebGLKernelValueDynamicUnsignedInput } = require('../../web-gl/kernel-value/dynamic-unsigned-input'); class WebGL2KernelValueDynamicUnsignedInput extends WebGLKernelValueDynamicUnsignedInput { getSource() { return utils.linesToString([ `uniform highp sampler2D ${this.id}`, `uniform highp ivec2 ${this.sizeId}`, `uniform highp ivec3 ${this.dimensionsId}`, ]); } } module.exports = { WebGL2KernelValueDynamicUnsignedInput }; },{"../../../utils":69,"../../web-gl/kernel-value/dynamic-unsigned-input":25}],50:[function(require,module,exports){ const { utils } = require('../../../utils'); const { WebGLKernelValueFloat } = require('../../web-gl/kernel-value/float'); class WebGL2KernelValueFloat extends WebGLKernelValueFloat {} module.exports = { WebGL2KernelValueFloat }; },{"../../../utils":69,"../../web-gl/kernel-value/float":26}],51:[function(require,module,exports){ const { utils } = require('../../../utils'); const { WebGLKernelValue } = require('../../web-gl/kernel-value/index'); class WebGL2KernelValueHtmlImageArray extends WebGLKernelValue { constructor(value, settings) { super(value, settings); this.requestTexture(); this.dimensions = [value[0].width, value[0].height, value.length]; this.textureSize = [value[0].width, value[0].height]; } getSource() { return utils.linesToString([ `uniform highp sampler2DArray ${this.id}`, `highp ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`, `highp ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`, ]); } updateValue(images) { const { context: gl } = this; gl.activeTexture(this.contextHandle); gl.bindTexture(gl.TEXTURE_2D_ARRAY, this.texture); gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MAG_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MIN_FILTER, gl.NEAREST); gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); gl.texImage3D( gl.TEXTURE_2D_ARRAY, 0, gl.RGBA, images[0].width, images[0].height, images.length, 0, gl.RGBA, gl.UNSIGNED_BYTE, null ); for (let i = 0; i < images.length; i++) { const xOffset = 0; const yOffset = 0; const imageDepth = 1; gl.texSubImage3D( gl.TEXTURE_2D_ARRAY, 0, xOffset, yOffset, i, images[i].width, images[i].height, imageDepth, gl.RGBA, gl.UNSIGNED_BYTE, this.uploadValue = images[i] ); } this.kernel.setUniform1i(this.id, this.index); } } module.exports = { WebGL2KernelValueHtmlImageArray }; },{"../../../utils":69,"../../web-gl/kernel-value/index":28}],52:[function(require,module,exports){ const { utils } = require('../../../utils'); const { WebGLKernelValueHTMLImage } = require('../../web-gl/kernel-value/html-image'); class WebGL2KernelValueHTMLImage extends WebGLKernelValueHTMLImage { getSource() { return utils.linesToString([ `uniform highp sampler2D ${this.id}`, `highp ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`, `highp ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`, ]); } } module.exports = { WebGL2KernelValueHTMLImage }; },{"../../../utils":69,"../../web-gl/kernel-value/html-image":27}],53:[function(require,module,exports){ const { utils } = require('../../../utils'); const { WebGLKernelValueInteger } = require('../../web-gl/kernel-value/integer'); class WebGL2KernelValueInteger extends WebGLKernelValueInteger { getSource(value) { if (this.origin === 'constants') { return `const highp int ${this.id} = ${ parseInt(value) };\n`; } return `uniform highp int ${this.id};\n`; } updateValue(value) { if (this.origin === 'constants') return; this.kernel.setUniform1i(this.id, this.uploadValue = value); } } module.exports = { WebGL2KernelValueInteger }; },{"../../../utils":69,"../../web-gl/kernel-value/integer":29}],54:[function(require,module,exports){ const { utils } = require('../../../utils'); const { WebGLKernelValueMemoryOptimizedNumberTexture } = require('../../web-gl/kernel-value/memory-optimized-number-texture'); class WebGL2KernelValueMemoryOptimizedNumberTexture extends WebGLKernelValueMemoryOptimizedNumberTexture { getSource() { return utils.linesToString([ `uniform sampler2D ${this.id}`, `highp ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`, `highp ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`, ]); } } module.exports = { WebGL2KernelValueMemoryOptimizedNumberTexture }; },{"../../../utils":69,"../../web-gl/kernel-value/memory-optimized-number-texture":30}],55:[function(require,module,exports){ const { utils } = require('../../../utils'); const { WebGLKernelValueNumberTexture } = require('../../web-gl/kernel-value/number-texture'); class WebGL2KernelValueNumberTexture extends WebGLKernelValueNumberTexture { getSource() { return utils.linesToString([ `uniform highp sampler2D ${this.id}`, `highp ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`, `highp ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`, ]); } } module.exports = { WebGL2KernelValueNumberTexture }; },{"../../../utils":69,"../../web-gl/kernel-value/number-texture":31}],56:[function(require,module,exports){ const { utils } = require('../../../utils'); const { WebGLKernelValueSingleArray } = require('../../web-gl/kernel-value/single-array'); class WebGL2KernelValueSingleArray extends WebGLKernelValueSingleArray { getSource() { return utils.linesToString([ `uniform highp sampler2D ${this.id}`, `highp ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`, `highp ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`, ]); } updateValue(value) { const { context: gl } = this; utils.flattenTo(value, this.uploadValue); gl.activeTexture(this.contextHandle); gl.bindTexture(gl.TEXTURE_2D, this.texture); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA32F, this.textureSize[0], this.textureSize[1], 0, gl.RGBA, gl.FLOAT, this.uploadValue); this.kernel.setUniform1i(this.id, this.index); } } module.exports = { WebGL2KernelValueSingleArray }; },{"../../../utils":69,"../../web-gl/kernel-value/single-array":32}],57:[function(require,module,exports){ const { utils } = require('../../../utils'); const { WebGLKernelValueSingleInput } = require('../../web-gl/kernel-value/single-input'); class WebGL2KernelValueSingleInput extends WebGLKernelValueSingleInput { getSource() { return utils.linesToString([ `uniform highp sampler2D ${this.id}`, `highp ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`, `highp ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`, ]); } updateValue(input) { const { context: gl } = this; utils.flattenTo(input.value, this.uploadValue); gl.activeTexture(this.contextHandle); gl.bindTexture(gl.TEXTURE_2D, this.texture); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA32F, this.textureSize[0], this.textureSize[1], 0, gl.RGBA, gl.FLOAT, this.uploadValue); this.kernel.setUniform1i(this.id, this.index); } } module.exports = { WebGL2KernelValueSingleInput }; },{"../../../utils":69,"../../web-gl/kernel-value/single-input":33}],58:[function(require,module,exports){ const { utils } = require('../../../utils'); const { WebGLKernelValueUnsignedArray } = require('../../web-gl/kernel-value/unsigned-array'); class WebGL2KernelValueUnsignedArray extends WebGLKernelValueUnsignedArray { getSource() { return utils.linesToString([ `uniform highp sampler2D ${this.id}`, `highp ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`, `highp ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`, ]); } } module.exports = { WebGL2KernelValueUnsignedArray }; },{"../../../utils":69,"../../web-gl/kernel-value/unsigned-array":34}],59:[function(require,module,exports){ const { utils } = require('../../../utils'); const { WebGLKernelValueUnsignedInput } = require('../../web-gl/kernel-value/unsigned-input'); class WebGL2KernelValueUnsignedInput extends WebGLKernelValueUnsignedInput { getSource() { return utils.linesToString([ `uniform highp sampler2D ${this.id}`, `highp ivec2 ${this.sizeId} = ivec2(${this.textureSize[0]}, ${this.textureSize[1]})`, `highp ivec3 ${this.dimensionsId} = ivec3(${this.dimensions[0]}, ${this.dimensions[1]}, ${this.dimensions[2]})`, ]); } } module.exports = { WebGL2KernelValueUnsignedInput }; },{"../../../utils":69,"../../web-gl/kernel-value/unsigned-input":35}],60:[function(require,module,exports){ const { WebGLKernel } = require('../web-gl/kernel'); const { WebGL2FunctionNode } = require('./function-node'); const { FunctionBuilder } = require('../function-builder'); const { utils } = require('../../utils'); const { Texture } = require('../../texture'); const { fragmentShader } = require('./fragment-shader'); const { vertexShader } = require('./vertex-shader'); const { lookupKernelValueType } = require('./kernel-value-maps'); let isSupported = null; let testCanvas = null; let testContext = null; let testExtensions = null; let features = null; class WebGL2Kernel extends WebGLKernel { static get isSupported() { if (isSupported !== null) { return isSupported; } this.setupFeatureChecks(); isSupported = this.isContextMatch(testContext); return isSupported; } static setupFeatureChecks() { if (typeof document !== 'undefined') { testCanvas = document.createElement('canvas'); } else if (typeof OffscreenCanvas !== 'undefined') { testCanvas = new OffscreenCanvas(0, 0); } if (!testCanvas) return; testContext = testCanvas.getContext('webgl2'); if (!testContext || !testContext.getExtension) return; testExtensions = { EXT_color_buffer_float: testContext.getExtension('EXT_color_buffer_float'), OES_texture_float_linear: testContext.getExtension('OES_texture_float_linear'), }; features = this.getFeatures(); } static isContextMatch(context) { if (typeof WebGL2RenderingContext !== 'undefined') { return context instanceof WebGL2RenderingContext; } return false; } static getFeatures() { return Object.freeze({ isFloatRead: this.getIsFloatRead(), isIntegerDivisionAccurate: this.getIsIntegerDivisionAccurate(), kernelMap: true, isTextureFloat: true, }); } static getIsTextureFloat() { return true; } static getIsIntegerDivisionAccurate() { return super.getIsIntegerDivisionAccurate(); } static lookupKernelValueType(type, dynamic, precision) { return lookupKernelValueType(type, dynamic, precision); } static get testCanvas() { return testCanvas; } static get testContext() { return testContext; } static get features() { return features; } static get fragmentShader() { return fragmentShader; } static get vertexShader() { return vertexShader; } initContext() { const settings = { alpha: false, depth: false, antialias: false }; const context = this.canvas.getContext('webgl2', settings); return context; } initExtensions() { this.extensions = { EXT_color_buffer_float: this.context.getExtension('EXT_color_buffer_float'), OES_texture_float_linear: this.context.getExtension('OES_texture_float_linear'), }; } validateSettings() { if (!this.validate) { this.texSize = utils.dimToTexSize({ floatTextures: this.optimizeFloatMemory, floatOutput: this.precision === 'single', }, this.output, true); return; } const features = this.constructor.features; if (this.precision === 'single' && this.floatOutputForce !== true && !features.isFloatRead) { throw new Error('Float texture outputs are not supported'); } else if (!this.graphical && this.precision === null) { this.precision = features.isFloatRead ? 'single' : 'unsigned'; } if (this.fixIntegerDivisionAccuracy === null) { this.fixIntegerDivisionAccuracy = !features.isIntegerDivisionAccurate; } else if (this.fixIntegerDivisionAccuracy && features.isIntegerDivisionAccurate) { this.fixIntegerDivisionAccuracy = false; } this.checkOutput(); if (!this.output || this.output.length === 0) { if (arguments.length !== 1) { throw new Error('Auto output only supported for kernels with only one input'); } const argType = utils.getVariableType(arguments[0], this.strictIntegers); switch (argType) { case 'Array': this.output = utils.getDimensions(argType); break; case 'NumberTexture': case 'MemoryOptimizedNumberTexture': case 'ArrayTexture(1)': case 'ArrayTexture(2)': case 'ArrayTexture(3)': case 'ArrayTexture(4)': this.output = arguments[0].output; break; default: throw new Error('Auto output not supported for input type: ' + argType); } } if (this.graphical) { if (this.output.length !== 2) { throw new Error('Output must have 2 dimensions on graphical mode'); } if (this.precision === 'single') { console.warn('Cannot use graphical mode and single precision at the same time'); this.precision = 'unsigned'; } this.texSize = utils.clone(this.output); return; } else if (!this.graphical && this.precision === null && features.isTextureFloat) { this.precision = 'single'; } this.texSize = utils.dimToTexSize({ floatTextures: !this.optimizeFloatMemory, floatOutput: this.precision === 'single', }, this.output, true); if (this.precision === 'single' || this.floatOutputForce) { this.context.getExtension('EXT_color_buffer_float'); } } translateSource() { const functionBuilder = FunctionBuilder.fromKernel(this, WebGL2FunctionNode, { fixIntegerDivisionAccuracy: this.fixIntegerDivisionAccuracy }); this.translatedSource = functionBuilder.getPrototypeString('kernel'); if (!this.graphical && !this.returnType) { this.returnType = functionBuilder.getKernelResultType(); } } run() { if (this.program === null) { this.build.apply(this, arguments); } const { argumentNames, argumentTypes, texSize } = this; const gl = this.context; gl.useProgram(this.program); gl.scissor(0, 0, texSize[0], texSize[1]); if (!this.hardcodeConstants) { this.setUniform3iv('uOutputDim', new Int32Array(this.threadDim)); this.setUniform2iv('uTexSize', texSize); } this.setUniform2f('ratio', texSize[0] / this.maxTexSize[0], texSize[1] / this.maxTexSize[1]); for (let texIndex = 0; texIndex < argumentNames.length; texIndex++) { this.kernelArguments[texIndex].updateValue(arguments[texIndex]); } if (this.plugins) { for (let i = 0; i < this.plugins.length; i++) { const plugin = this.plugins[i]; if (plugin.onBeforeRun) { plugin.onBeforeRun(this); } } } if (this.graphical) { if (this.pipeline) { gl.bindRenderbuffer(gl.RENDERBUFFER, null); gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer); if (!this.outputTexture || this.immutable) { this._setupOutputTexture(); } gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); return new Texture({ texture: this.outputTexture, size: texSize, dimensions: this.threadDim, output: this.output, context: this.context, gpu: this.gpu, type: this.getReturnTextureType(), }); } gl.bindRenderbuffer(gl.RENDERBUFFER, null); gl.bindFramebuffer(gl.FRAMEBUFFER, null); gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); return; } gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer); if (this.immutable) { this._setupOutputTexture(); } if (this.subKernels !== null) { if (this.immutable) { this.subKernelOutputTextures = []; this._setupSubOutputTextures(this.subKernels.length); } gl.drawBuffers(this.drawBuffersMap); } gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); if (this.subKernelOutputTextures !== null) { if (this.subKernels !== null) { const output = { result: this.renderOutput() }; if (this.pipeline) { for (let i = 0; i < this.subKernels.length; i++) { output[this.subKernels[i].property] = new Texture({ texture: this.subKernelOutputTextures[i], size: texSize, dimensions: this.threadDim, output: this.output, context: this.context, gpu: this.gpu, type: this.getReturnTextureType(), }); } } else { for (let i = 0; i < this.subKernels.length; i++) { output[this.subKernels[i].property] = new Texture({ texture: this.subKernelOutputTextures[i], size: texSize, dimensions: this.threadDim, output: this.output, context: this.context, gpu: this.gpu, type: this.getReturnTextureType(), }).toArray(); } } return output; } } return this.renderOutput(); } drawBuffers() { this.context.drawBuffers(this.drawBuffersMap); } getOutputTexture() { return this.outputTexture; } _setupOutputTexture() { const { texSize } = this; const gl = this.context; const texture = this.outputTexture = gl.createTexture(); gl.activeTexture(gl.TEXTURE0 + this.constantsLength + this.argumentNames.length); gl.bindTexture(gl.TEXTURE_2D, texture); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); if (this.precision === 'single') { if (this.pipeline) { switch (this.returnType) { case 'Number': case 'Float': case 'Integer': if (this.optimizeFloatMemory) { gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA32F, texSize[0], texSize[1], 0, gl.RGBA, gl.FLOAT, null); } else { gl.texImage2D(gl.TEXTURE_2D, 0, gl.R32F, texSize[0], texSize[1], 0, gl.RED, gl.FLOAT, null); } break; case 'Array(2)': gl.texImage2D(gl.TEXTURE_2D, 0, gl.RG32F, texSize[0], texSize[1], 0, gl.RG, gl.FLOAT, null); break; case 'Array(3)': gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB32F, texSize[0], texSize[1], 0, gl.RGB, gl.FLOAT, null); break; case 'Array(4)': gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA32F, texSize[0], texSize[1], 0, gl.RGBA, gl.FLOAT, null); break; default: throw new Error('Unhandled return type'); } } else { gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA32F, texSize[0], texSize[1], 0, gl.RGBA, gl.FLOAT, null); } } else { gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, texSize[0], texSize[1], 0, gl.RGBA, gl.UNSIGNED_BYTE, null); } gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); } _setupSubOutputTextures(length) { const { texSize } = this; const gl = this.context; this.drawBuffersMap = [gl.COLOR_ATTACHMENT0]; const textures = this.subKernelOutputTextures = []; for (let i = 0; i < length; i++) { const texture = this.context.createTexture(); textures.push(texture); this.drawBuffersMap.push(gl.COLOR_ATTACHMENT0 + i + 1); gl.activeTexture(gl.TEXTURE0 + this.constantsLength + this.argumentNames.length + i); gl.bindTexture(gl.TEXTURE_2D, texture); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); if (this.precision === 'single') { gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA32F, texSize[0], texSize[1], 0, gl.RGBA, gl.FLOAT, null); } else { gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, texSize[0], texSize[1], 0, gl.RGBA, gl.UNSIGNED_BYTE, null); } gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + i + 1, gl.TEXTURE_2D, texture, 0); } } _getHeaderString() { return ''; } _getTextureCoordinate() { const subKernels = this.subKernels; if (subKernels === null || subKernels.length < 1) { return 'in highp vec2 vTexCoord;\n'; } else { return 'out highp vec2 vTexCoord;\n'; } } _getMainArgumentsString(args) { const result = []; const argumentNames = this.argumentNames; for (let i = 0; i < argumentNames.length; i++) { result.push(this.kernelArguments[i].getSource(args[i])); } return result.join(''); } getKernelString() { let kernelResultDeclaration; switch (this.returnType) { case 'Array(2)': kernelResultDeclaration = 'vec2 kernelResult'; break; case 'Array(3)': kernelResultDeclaration = 'vec3 kernelResult'; break; case 'Array(4)': kernelResultDeclaration = 'vec4 kernelResult'; break; case 'LiteralInteger': case 'Float': case 'Number': case 'Integer': kernelResultDeclaration = 'float kernelResult'; break; default: if (this.graphical) { kernelResultDeclaration = 'float kernelResult'; } else { throw new Error(`unrecognized output type "${ this.returnType }"`); } } const result = []; const subKernels = this.subKernels; if (subKernels !== null) { result.push( kernelResultDeclaration, 'layout(location = 0) out vec4 data0' ); for (let i = 0; i < subKernels.length; i++) { result.push( `float subKernelResult_${ subKernels[i].name } = 0.0`, `layout(location = ${ i + 1 }) out vec4 data${ i + 1 }` ); } } else { result.push( 'out vec4 data0', kernelResultDeclaration ); } return utils.linesToString(result) + this.translatedSource; } getMainResultGraphical() { return utils.linesToString([ ' threadId = indexTo3D(index, uOutputDim)', ' kernel()', ' data0 = actualColor', ]); } getMainResultPackedPixels() { switch (this.returnType) { case 'LiteralInteger': case 'Number': case 'Integer': case 'Float': return utils.linesToString(this.getMainResultKernelPackedPixels()) + utils.linesToString(this.getMainResultSubKernelPackedPixels()); default: throw new Error(`packed output only usable with Numbers, "${this.returnType}" specified`); } } getMainResultKernelPackedPixels() { return [ ' threadId = indexTo3D(index, uOutputDim)', ' kernel()', ' data0 = encode32(kernelResult)' ]; } getMainResultSubKernelPackedPixels() { const result = []; if (!this.subKernels) return result; for (let i = 0; i < this.subKernels.length; i++) { result.push( ` data${i + 1} = encode32(subKernelResult_${this.subKernels[i].name})` ); } return result; } getMainResultMemoryOptimizedFloats() { const result = [ ' index *= 4', ]; switch (this.returnType) { case 'Number': case 'Integer': case 'Float': const channels = ['r', 'g', 'b', 'a']; for (let i = 0; i < channels.length; i++) { const channel = channels[i]; this.getMainResultKernelMemoryOptimizedFloats(result, channel); this.getMainResultSubKernelMemoryOptimizedFloats(result, channel); if (i + 1 < channels.length) { result.push(' index += 1'); } } break; default: throw new Error(`optimized output only usable with Numbers, ${this.returnType} specified`); } return utils.linesToString(result); } getMainResultKernelMemoryOptimizedFloats(result, channel) { result.push( ' threadId = indexTo3D(index, uOutputDim)', ' kernel()', ` data0.${channel} = kernelResult`, ); } getMainResultSubKernelMemoryOptimizedFloats(result, channel) { if (!this.subKernels) return result; for (let i = 0; i < this.subKernels.length; i++) { result.push( ` data${i + 1}.${channel} = subKernelResult_${this.subKernels[i].name}`, ); } } getMainResultKernelNumberTexture() { return [ ' threadId = indexTo3D(index, uOutputDim)', ' kernel()', ' data0[0] = kernelResult', ]; } getMainResultSubKernelNumberTexture() { const result = []; if (!this.subKernels) return result; for (let i = 0; i < this.subKernels.length; ++i) { result.push( ` data${i + 1}[0] = subKernelResult_${this.subKernels[i].name}`, ); } return result; } getMainResultKernelArray2Texture() { return [ ' threadId = indexTo3D(index, uOutputDim)', ' kernel()', ' data0[0] = kernelResult[0]', ' data0[1] = kernelResult[1]', ]; } getMainResultSubKernelArray2Texture() { const result = []; if (!this.subKernels) return result; for (let i = 0; i < this.subKernels.length; ++i) { result.push( ` data${i + 1}[0] = subKernelResult_${this.subKernels[i].name}[0]`, ` data${i + 1}[1] = subKernelResult_${this.subKernels[i].name}[1]`, ); } return result; } getMainResultKernelArray3Texture() { return [ ' threadId = indexTo3D(index, uOutputDim)', ' kernel()', ' data0[0] = kernelResult[0]', ' data0[1] = kernelResult[1]', ' data0[2] = kernelResult[2]', ]; } getMainResultSubKernelArray3Texture() { const result = []; if (!this.subKernels) return result; for (let i = 0; i < this.subKernels.length; ++i) { result.push( ` data${i + 1}[0] = subKernelResult_${this.subKernels[i].name}[0]`, ` data${i + 1}[1] = subKernelResult_${this.subKernels[i].name}[1]`, ` data${i + 1}[2] = subKernelResult_${this.subKernels[i].name}[2]`, ); } return result; } getMainResultKernelArray4Texture() { return [ ' threadId = indexTo3D(index, uOutputDim)', ' kernel()', ' data0 = kernelResult', ]; } getMainResultSubKernelArray4Texture() { const result = []; if (!this.subKernels) return result; for (let i = 0; i < this.subKernels.length; ++i) { result.push( ` data${i + 1} = subKernelResult_${this.subKernels[i].name}`, ); } return result; } getFragmentShader(args) { if (this.compiledFragmentShader !== null) { return this.compiledFragmentShader; } return this.compiledFragmentShader = this.replaceArtifacts(this.constructor.fragmentShader, this._getFragShaderArtifactMap(args)); } getVertexShader(args) { if (this.compiledVertexShader !== null) { return this.compiledVertexShader; } return this.compiledVertexShader = this.constructor.vertexShader; } destroyExtensions() { this.extensions.EXT_color_buffer_float = null; this.extensions.OES_texture_float_linear = null; } toJSON() { const json = super.toJSON(); json.functionNodes = FunctionBuilder.fromKernel(this, WebGL2FunctionNode).toJSON(); return json; } } module.exports = { WebGL2Kernel }; },{"../../texture":68,"../../utils":69,"../function-builder":8,"../web-gl/kernel":36,"./fragment-shader":38,"./function-node":39,"./kernel-value-maps":40,"./vertex-shader":61}],61:[function(require,module,exports){ const vertexShader = `#version 300 es precision highp float; precision highp int; precision highp sampler2D; in vec2 aPos; in vec2 aTexCoord; out vec2 vTexCoord; uniform vec2 ratio; void main(void) { gl_Position = vec4((aPos + vec2(1)) * ratio + vec2(-1), 0, 1); vTexCoord = aTexCoord; }`; module.exports = { vertexShader }; },{}],62:[function(require,module,exports){ const lib = require('./index'); const GPU = lib.GPU; for (const p in lib) { if (!lib.hasOwnProperty(p)) continue; if (p === 'GPU') continue; GPU[p] = lib[p]; } if (typeof module !== 'undefined') { module.exports = GPU; } if (typeof window !== 'undefined') { window.GPU = GPU; } if (typeof self !== 'undefined') { self.GPU = GPU; } },{"./index":64}],63:[function(require,module,exports){ const gpuMock = require('gpu-mock.js'); const { utils } = require('./utils'); const { CPUKernel } = require('./backend/cpu/kernel'); const { HeadlessGLKernel } = require('./backend/headless-gl/kernel'); const { WebGL2Kernel } = require('./backend/web-gl2/kernel'); const { WebGLKernel } = require('./backend/web-gl/kernel'); const { kernelRunShortcut } = require('./kernel-run-shortcut'); const kernelOrder = [HeadlessGLKernel, WebGL2Kernel, WebGLKernel]; const kernelTypes = ['gpu', 'cpu']; const internalKernels = { 'headlessgl': HeadlessGLKernel, 'webgl2': WebGL2Kernel, 'webgl': WebGLKernel, }; let validate = true; class GPU { static disableValidation() { validate = false; } static enableValidation() { validate = true; } static get isGPUSupported() { return kernelOrder.some(Kernel => Kernel.isSupported); } static get isKernelMapSupported() { return kernelOrder.some(Kernel => Kernel.isSupported && Kernel.features.kernelMap); } static get isOffscreenCanvasSupported() { return (typeof Worker !== 'undefined' && typeof OffscreenCanvas !== 'undefined') || typeof importScripts !== 'undefined'; } static get isWebGLSupported() { return WebGLKernel.isSupported; } static get isWebGL2Supported() { return WebGL2Kernel.isSupported; } static get isHeadlessGLSupported() { return HeadlessGLKernel.isSupported; } static get isCanvasSupported() { return typeof HTMLCanvasElement !== 'undefined'; } static get isGPUHTMLImageArraySupported() { return WebGL2Kernel.isSupported; } static get isSinglePrecisionSupported() { return kernelOrder.some(Kernel => Kernel.isSupported && Kernel.features.isFloatRead && Kernel.features.isTextureFloat); } constructor(settings) { settings = settings || {}; this.canvas = settings.canvas || null; this.context = settings.context || null; this.mode = settings.mode; if (this.mode === 'dev') return; this.Kernel = null; this.kernels = []; this.functions = []; this.nativeFunctions = []; this.chooseKernel(); if (settings.functions) { for (let i = 0; i < settings.functions.length; i++) { this.addFunction(settings.functions[i]); } } if (settings.nativeFunctions) { for (const p in settings.nativeFunctions) { this.addNativeFunction(p, settings.nativeFunctions[p]); } } } chooseKernel() { if (this.Kernel) return; let Kernel = null; if (this.context) { for (let i = 0; i < kernelOrder.length; i++) { const ExternalKernel = kernelOrder[i]; if (ExternalKernel.isContextMatch(this.context)) { if (!ExternalKernel.isSupported) { throw new Error(`Kernel type ${ExternalKernel.name} not supported`); } Kernel = ExternalKernel; break; } } if (Kernel === null) { throw new Error('unknown Context'); } } else if (this.mode) { if (this.mode in internalKernels) { if (!validate || internalKernels[this.mode].isSupported) { Kernel = internalKernels[this.mode]; } } else if (this.mode === 'gpu') { for (let i = 0; i < kernelOrder.length; i++) { if (kernelOrder[i].isSupported) { Kernel = kernelOrder[i]; break; } } } else if (this.mode === 'cpu') { Kernel = CPUKernel; } if (!Kernel) { throw new Error(`A requested mode of "${this.mode}" and is not supported`); } } else { for (let i = 0; i < kernelOrder.length; i++) { if (kernelOrder[i].isSupported) { Kernel = kernelOrder[i]; break; } } if (!Kernel) { Kernel = CPUKernel; } } if (!this.mode) { this.mode = Kernel.mode; } this.Kernel = Kernel; } createKernel(source, settings) { if (typeof source === 'undefined') { throw new Error('Missing source parameter'); } if (typeof source !== 'object' && !utils.isFunction(source) && typeof source !== 'string') { throw new Error('source parameter not a function'); } if (this.mode === 'dev') { return gpuMock(source, upgradeDeprecatedCreateKernelSettings(settings)); } source = typeof source === 'function' ? source.toString() : source; const mergedSettings = Object.assign({ context: this.context, canvas: this.canvas, functions: this.functions, nativeFunctions: this.nativeFunctions, gpu: this, validate, onRequestFallback: (args) => { const fallbackKernel = new CPUKernel(source, mergedSettings); return fallbackKernel.apply(fallbackKernel, args); }, onRequestRecompile: (args) => {} }, upgradeDeprecatedCreateKernelSettings(settings) || {}); const kernel = kernelRunShortcut(new this.Kernel(source, mergedSettings)); if (!this.canvas) { this.canvas = kernel.canvas; } if (!this.context) { this.context = kernel.context; } this.kernels.push(kernel); return kernel; } createKernelMap() { let fn; let settings; if (typeof arguments[arguments.length - 2] === 'function') { fn = arguments[arguments.length - 2]; settings = arguments[arguments.length - 1]; } else { fn = arguments[arguments.length - 1]; } if (!this.Kernel.isSupported || !this.Kernel.features.kernelMap) { if (this.mode && kernelTypes.indexOf(this.mode) < 0) { throw new Error(`kernelMap not supported on ${this.Kernel.name}`); } } const kernel = this.createKernel(fn, upgradeDeprecatedCreateKernelSettings(settings)); if (Array.isArray(arguments[0])) { const functions = arguments[0]; for (let i = 0; i < functions.length; i++) { const source = functions[i].toString(); const name = utils.getFunctionNameFromString(source); kernel.addSubKernel({ name, source, property: i, }); } } else { const functions = arguments[0]; for (let p in functions) { if (!functions.hasOwnProperty(p)) continue; const source = functions[p].toString(); const name = utils.getFunctionNameFromString(source); kernel.addSubKernel({ name: name || p, source, property: p, }); } } return kernel; } combineKernels() { const firstKernel = arguments[0]; const combinedKernel = arguments[arguments.length - 1]; if (firstKernel.kernel.constructor.mode === 'cpu') return combinedKernel; const canvas = arguments[0].canvas; const context = arguments[0].context; const max = arguments.length - 1; for (let i = 0; i < max; i++) { arguments[i] .setCanvas(canvas) .setContext(context) .setPipeline(true); } return function() { const texture = combinedKernel.apply(this, arguments); if (texture.toArray) { return texture.toArray(); } return texture; }; } addFunction(source, settings) { this.functions.push(utils.functionToIFunction(source, settings)); return this; } addNativeFunction(name, source, settings) { if (this.kernels.length > 0) { throw new Error('Cannot call "addNativeFunction" after "createKernels" has been called.'); } settings = settings || {}; const { argumentTypes, argumentNames } = this.Kernel.nativeFunctionArguments(source) || {}; this.nativeFunctions.push({ name, source, settings, argumentTypes, argumentNames, returnType: settings.returnType || this.Kernel.nativeFunctionReturnType(source), }); return this; } destroy() { setTimeout(() => { for (let i = 0; i < this.kernels.length; i++) { this.kernels[i].destroy(true); } this.kernels[0].kernel.constructor.destroyContext(this.context); }, 0); } } function upgradeDeprecatedCreateKernelSettings(settings) { if (!settings) { return; } const upgradedSettings = Object.assign({}, settings); if (settings.hasOwnProperty('floatOutput')) { utils.warnDeprecated('setting', 'floatOutput', 'precision'); upgradedSettings.precision = settings.floatOutput ? 'single' : 'unsigned'; } if (settings.hasOwnProperty('outputToTexture')) { utils.warnDeprecated('setting', 'outputToTexture', 'pipeline'); upgradedSettings.pipeline = Boolean(settings.outputToTexture); } if (settings.hasOwnProperty('outputImmutable')) { utils.warnDeprecated('setting', 'outputImmutable', 'immutable'); upgradedSettings.immutable = Boolean(settings.outputImmutable); } if (settings.hasOwnProperty('floatTextures')) { utils.warnDeprecated('setting', 'floatTextures', 'optimizeFloatMemory'); upgradedSettings.optimizeFloatMemory = Boolean(settings.floatTextures); } return upgradedSettings; } module.exports = { GPU, kernelOrder, kernelTypes }; },{"./backend/cpu/kernel":7,"./backend/headless-gl/kernel":12,"./backend/web-gl/kernel":36,"./backend/web-gl2/kernel":60,"./kernel-run-shortcut":66,"./utils":69,"gpu-mock.js":3}],64:[function(require,module,exports){ const { GPU } = require('./gpu'); const { alias } = require('./alias'); const { utils } = require('./utils'); const { Input, input } = require('./input'); const { Texture } = require('./texture'); const { FunctionBuilder } = require('./backend/function-builder'); const { FunctionNode } = require('./backend/function-node'); const { CPUFunctionNode } = require('./backend/cpu/function-node'); const { CPUKernel } = require('./backend/cpu/kernel'); const { HeadlessGLKernel } = require('./backend/headless-gl/kernel'); const { WebGLFunctionNode } = require('./backend/web-gl/function-node'); const { WebGLKernel } = require('./backend/web-gl/kernel'); const { WebGL2FunctionNode } = require('./backend/web-gl2/function-node'); const { WebGL2Kernel } = require('./backend/web-gl2/kernel'); const { GLKernel } = require('./backend/gl/kernel'); const { Kernel } = require('./backend/kernel'); module.exports = { alias, CPUFunctionNode, CPUKernel, GPU, FunctionBuilder, FunctionNode, HeadlessGLKernel, Input, input, Texture, utils, WebGL2FunctionNode, WebGL2Kernel, WebGLFunctionNode, WebGLKernel, GLKernel, Kernel, }; },{"./alias":4,"./backend/cpu/function-node":5,"./backend/cpu/kernel":7,"./backend/function-builder":8,"./backend/function-node":9,"./backend/gl/kernel":11,"./backend/headless-gl/kernel":12,"./backend/kernel":14,"./backend/web-gl/function-node":16,"./backend/web-gl/kernel":36,"./backend/web-gl2/function-node":39,"./backend/web-gl2/kernel":60,"./gpu":63,"./input":65,"./texture":68,"./utils":69}],65:[function(require,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; } } else { 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]); } else { this.size = new Int32Array([size.x, 1, 1]); } } 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)}`); } } } function input(value, size) { return new Input(value, size); } module.exports = { Input, input }; },{}],66:[function(require,module,exports){ const { utils } = require('./utils'); function kernelRunShortcut(kernel) { const shortcut = function() { return kernel.run.apply(kernel, arguments); }; utils .allPropertiesOf(kernel) .forEach((key) => { if (key[0] === '_' && key[1] === '_') return; if (typeof kernel[key] === 'function') { if (key.substring(0, 3) === 'add' || key.substring(0, 3) === 'set') { shortcut[key] = function() { kernel[key].apply(kernel, arguments); return shortcut; }; } else if (key === 'requestFallback') { const requestFallback = kernel[key].bind(kernel); shortcut[key] = () => { kernel = requestFallback(); }; } else { shortcut[key] = kernel[key].bind(kernel); } } else { shortcut.__defineGetter__(key, () => { return kernel[key]; }); shortcut.__defineSetter__(key, (value) => { kernel[key] = value; }); } }); shortcut.kernel = kernel; return shortcut; } module.exports = { kernelRunShortcut }; },{"./utils":69}],67:[function(require,module,exports){ const source = ` uniform highp float triangle_noise_seed; highp float triangle_noise_shift = 0.000001; //https://www.shadertoy.com/view/4t2SDh //note: uniformly distributed, normalized rand, [0;1[ float nrand( vec2 n ) { return fract(sin(dot(n.xy, vec2(12.9898, 78.233)))* 43758.5453); } //note: remaps v to [0;1] in interval [a;b] float remap( float a, float b, float v ) { return clamp( (v-a) / (b-a), 0.0, 1.0 ); } float n4rand( vec2 n ) { float t = fract( triangle_noise_seed + triangle_noise_shift ); float nrnd0 = nrand( n + 0.07*t ); float nrnd1 = nrand( n + 0.11*t ); float nrnd2 = nrand( n + 0.13*t ); float nrnd3 = nrand( n + 0.17*t ); float result = (nrnd0+nrnd1+nrnd2+nrnd3) / 4.0; triangle_noise_shift = result + 0.000001; return result; }`; const name = 'triangle-noise-noise'; const functionMatch = 'Math.random()'; const functionReplace = 'n4rand(vTexCoord)'; const functionReturnType = 'Number'; const onBeforeRun = (kernel) => { kernel.setUniform1f('triangle_noise_seed', Math.random()); }; module.exports = { name, onBeforeRun, functionMatch, functionReplace, functionReturnType, source }; },{}],68:[function(require,module,exports){ class Texture { constructor(settings) { const { texture, size, dimensions, output, context, gpu, type = 'NumberTexture', } = settings; if (!output) throw new Error('settings property "output" required.'); if (!context) throw new Error('settings property "context" required.'); this.texture = texture; this.size = size; this.dimensions = dimensions; this.output = output; this.context = context; this.gpu = gpu; this.kernel = null; this.type = type; } toArray(gpu) { let { kernel } = this; if (kernel) return kernel(this); gpu = gpu || this.gpu; if (!gpu) throw new Error('settings property "gpu" or argument required.'); kernel = gpu.createKernel(function(x) { return x[this.thread.z][this.thread.y][this.thread.x]; }, { output: this.output, precision: this.getPrecision(), optimizeFloatMemory: this.type === 'MemoryOptimizedNumberTexture', }); this.kernel = kernel; return kernel(this); } getPrecision() { switch (this.type) { case 'NumberTexture': return 'unsigned'; case 'MemoryOptimizedNumberTexture': case 'ArrayTexture(1)': case 'ArrayTexture(2)': case 'ArrayTexture(3)': case 'ArrayTexture(4)': return 'single'; default: throw new Error('Unknown texture type'); } } delete() { return this.context.deleteTexture(this.texture); } } module.exports = { Texture }; },{}],69:[function(require,module,exports){ const { Input } = require('./input'); const { Texture } = require('./texture'); const FUNCTION_NAME = /function ([^(]*)/; const STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg; const ARGUMENT_NAMES = /([^\s,]+)/g; const utils = { systemEndianness() { return _systemEndianness; }, getSystemEndianness() { const b = new ArrayBuffer(4); const a = new Uint32Array(b); const c = new Uint8Array(b); a[0] = 0xdeadbeef; if (c[0] === 0xef) return 'LE'; if (c[0] === 0xde) return 'BE'; throw new Error('unknown endianness'); }, isFunction(funcObj) { return typeof(funcObj) === 'function'; }, isFunctionString(fn) { if (typeof fn === 'string') { return (fn .slice(0, 'function'.length) .toLowerCase() === 'function'); } return false; }, getFunctionNameFromString(funcStr) { return FUNCTION_NAME.exec(funcStr)[1].trim(); }, getFunctionBodyFromString(funcStr) { return funcStr.substring(funcStr.indexOf('{') + 1, funcStr.lastIndexOf('}')); }, getArgumentNamesFromString(fn) { const fnStr = fn.replace(STRIP_COMMENTS, ''); let result = fnStr.slice(fnStr.indexOf('(') + 1, fnStr.indexOf(')')).match(ARGUMENT_NAMES); if (result === null) { result = []; } return result; }, clone(obj) { if (obj === null || typeof obj !== 'object' || obj.hasOwnProperty('isActiveClone')) return obj; const temp = obj.constructor(); for (let key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { obj.isActiveClone = null; temp[key] = utils.clone(obj[key]); delete obj.isActiveClone; } } return temp; }, isArray(array) { return !isNaN(array.length); }, getVariableType(value, strictIntegers) { if (utils.isArray(value)) { if (value[0].nodeName === 'IMG') { return 'HTMLImageArray'; } return 'Array'; } switch (value.constructor) { case Boolean: return 'Boolean'; case Number: if (strictIntegers && Number.isInteger(value)) { return 'Integer'; } return 'Float'; case Texture: return value.type; case Input: return 'Input'; } if (value.nodeName === 'IMG') { return 'HTMLImage'; } else { return 'Unknown'; } }, dimToTexSize(opt, dimensions, output) { let [w, h, d] = dimensions; let texelCount = (w || 1) * (h || 1) * (d || 1); if (opt.floatTextures && (!output || opt.precision === 'single')) { w = texelCount = Math.ceil(texelCount / 4); } if (h > 1 && w * h === texelCount) { return new Int32Array([w, h]); } return utils.closestSquareDimensions(texelCount); }, closestSquareDimensions(length) { const sqrt = Math.sqrt(length); let high = Math.ceil(sqrt); let low = Math.floor(sqrt); while (high * low < length) { high--; low = Math.ceil(length / high); } return new Int32Array([low, Math.ceil(length / low)]); }, getMemoryOptimizedFloatTextureSize(dimensions, bitRatio) { const [w, h, d] = dimensions; const totalArea = utils.roundTo((w || 1) * (h || 1) * (d || 1), 4); const texelCount = totalArea / bitRatio; return utils.closestSquareDimensions(texelCount); }, getMemoryOptimizedPackedTextureSize(dimensions, bitRatio) { const [w, h, d] = dimensions; const totalArea = utils.roundTo((w || 1) * (h || 1) * (d || 1), 4); const texelCount = totalArea / (4 / bitRatio); return utils.closestSquareDimensions(texelCount); }, roundTo(n, d) { return Math.floor((n + d - 1) / d) * d; }, getDimensions(x, pad) { let ret; if (utils.isArray(x)) { const dim = []; let temp = x; while (utils.isArray(temp)) { dim.push(temp.length); temp = temp[0]; } ret = dim.reverse(); } else if (x instanceof Texture) { ret = x.output; } else if (x instanceof Input) { ret = x.size; } else { throw new Error(`Unknown dimensions of ${x}`); } if (pad) { ret = Array.from(ret); while (ret.length < 3) { ret.push(1); } } return new Int32Array(ret); }, flatten2dArrayTo(array, target) { let offset = 0; for (let y = 0; y < array.length; y++) { target.set(array[y], offset); offset += array[y].length; } }, flatten3dArrayTo(array, target) { let offset = 0; for (let z = 0; z < array.length; z++) { for (let y = 0; y < array[z].length; y++) { target.set(array[z][y], offset); offset += array[z][y].length; } } }, flattenTo(array, target) { if (utils.isArray(array[0])) { if (utils.isArray(array[0][0])) { utils.flatten3dArrayTo(array, target); } else { utils.flatten2dArrayTo(array, target); } } else { target.set(array); } }, splitArray(array, part) { const result = []; for (let i = 0; i < array.length; i += part) { result.push(new array.constructor(array.buffer, i * 4 + array.byteOffset, part)); } return result; }, getAstString(source, ast) { const lines = Array.isArray(source) ? source : source.split(/\r?\n/g); const start = ast.loc.start; const end = ast.loc.end; const result = []; result.push(lines[start.line - 1].slice(start.column)); for (let i = start.line; i < end.line - 1; i++) { result.push(lines[i]); } result.push(lines[end.line - 1].slice(0, end.column)); return result.join('\n'); }, allPropertiesOf(obj) { const props = []; do { props.push.apply(props, Object.getOwnPropertyNames(obj)); } while (obj = Object.getPrototypeOf(obj)); return props; }, linesToString(lines) { if (lines.length > 0) { return lines.join(';\n') + ';\n'; } else { return '\n'; } }, warnDeprecated(type, oldName, newName) { if (newName) { console.warn(`You are using a deprecated ${ type } "${ oldName }". It has been replaced with "${ newName }". Fixing, but please upgrade as it will soon be removed.`); } else { console.warn(`You are using a deprecated ${ type } "${ oldName }". It has been removed. Fixing, but please upgrade as it will soon be removed.`); } }, functionToIFunction(source, settings) { settings = settings || {}; if (typeof source !== 'string' && typeof source !== 'function') throw new Error('source not a string or function'); const sourceString = typeof source === 'string' ? source : source.toString(); let argumentTypes = []; if (Array.isArray(settings.argumentTypes)) { argumentTypes = settings.argumentTypes; } else if (typeof settings.argumentTypes === 'object') { argumentTypes = utils.getArgumentNamesFromString(sourceString) .map(name => settings.argumentTypes[name]) || []; } else { argumentTypes = settings.argumentTypes || []; } return { source: sourceString, argumentTypes, returnType: settings.returnType || null, }; } }; const _systemEndianness = utils.getSystemEndianness(); module.exports = { utils }; },{"./input":65,"./texture":68}]},{},[62]);