diff --git a/src/backend/web-gl/kernel.js b/src/backend/web-gl/kernel.js index 27b2b531..ea3069b7 100644 --- a/src/backend/web-gl/kernel.js +++ b/src/backend/web-gl/kernel.js @@ -9,25 +9,39 @@ const kernelString = require('./kernel-string'); /// /// Class: WebGLKernel /// +/// Kernel Implementation for WebGL. +/// This builds the shaders and runs them on the GPU, +/// the outputs the result back as float(enabled by default) and Texture. +/// +/// Extends: +/// KernelBase +/// +/// Parameters: +/// textureCache - {Object} - webGl Texture cache +/// threadDim - {Object} - The thread dimensions, x, y and z +/// programUniformLocationCache - {Object} - Location of program variables in memory +/// framebuffer - {Object} - Webgl frameBuffer +/// buffer - {Object} - WebGL buffer +/// program - {Object} - The webGl Program +/// functionBuilder - {Object} - Function Builder instance bound to this Kernel +/// outputToTexture - {Boolean} - Set output type to Texture, instead of float +/// endianness - {String} - Endian information like Little-endian, Big-endian. +/// paramTypes - {Array} - Types of parameters sent to the Kernel +/// argumentsLength - {Number} - Number of parameters sent to the Kernel +/// compiledFragShaderString - {String} - Compiled fragment shader string +/// compiledVertShaderString - {String} - Compiled Vertical shader string +/// module.exports = class WebGLKernel extends KernelBase { - _getFragShaderArtifactMap(args) { - return { - HEADER: this._getHeaderString(), - LOOP_MAX: this._getLoopMaxString(), - CONSTANTS: this._getConstantsString(), - DECODE32_ENDIANNESS: this._getDecode32EndiannessString(), - ENCODE32_ENDIANNESS: this._getEncode32EndiannessString(), - GET_WRAPAROUND: this._getGetWraparoundString(), - GET_TEXTURE_CHANNEL: this._getGetTextureChannelString(), - GET_TEXTURE_INDEX: this._getGetTextureIndexString(), - GET_RESULT: this._getGetResultString(), - MAIN_PARAMS: this._getMainParamsString(args), - MAIN_CONSTANTS: this._getMainConstantsString(), - KERNEL: this._getKernelString(), - MAIN_RESULT: this._getMainResultString() - }; - } - + + // + // [Constructor] + // + + /// + /// Function: WebGLKernel + /// + /// Instantiates properties to the WebGl Kernel. + /// constructor(fnString, settings) { super(fnString, settings); this.textureCache = {}; @@ -50,6 +64,13 @@ module.exports = class WebGLKernel extends KernelBase { if (!this._webGl) this._webGl = utils.initWebGl(this.canvas); } + /// + /// Function: validateOptions + /// + /// Validate options related to Kernel, such as + /// floatOutputs and Textures, texSize, dimensions, + /// graphical output. + /// validateOptions() { const isReadPixel = utils.isFloatReadPixelsSupported; if (this.floatTextures === true && !utils.OES_texture_float) { @@ -97,6 +118,13 @@ module.exports = class WebGLKernel extends KernelBase { } } + /// + /// Function: build + /// + /// Builds the Kernel, by compiling Fragment and Vertical Shaders, + /// and instantiates the program. + /// + build() { this.validateOptions(); this.setupParams(arguments); @@ -145,12 +173,24 @@ module.exports = class WebGLKernel extends KernelBase { gl.attachShader(program, vertShader); gl.attachShader(program, fragShader); gl.linkProgram(program); - this.framebuffer = this._webGl.createFramebuffer(); + this.framebuffer = gl.createFramebuffer(); this.framebuffer.width = this.texSize[0]; this.framebuffer.height = this.texSize[1]; return this; } + /// + /// Function: run + /// + /// Run the kernel program, and send the output to renderOutput + /// + /// This method calls a helper method *renderOutput* to return the result. + /// + /// Returns: + /// Result {Object} The final output of the program, as float, and as Textures for reuse. + /// + /// + run() { if (this.program === null) { this.build.apply(this, arguments); @@ -274,6 +314,23 @@ module.exports = class WebGLKernel extends KernelBase { return this.renderOutput(outputTexture); } + /// + /// Function: renderOutput + /// + /// + /// Helper function to return webGl function's output. + /// Since the program runs on GPU, we need to get the + /// output of the program back to CPU and then return them. + /// + /// *Note*: This should not be called directly. + /// + /// Parameters: + /// outputTexture Output Texture returned by webGl program + /// + /// Returns: + /// result {Object|Array} + /// + /// renderOutput(outputTexture) { const texSize = this.texSize; const gl = this._webGl; @@ -307,18 +364,59 @@ module.exports = class WebGLKernel extends KernelBase { } } + /// + /// Function: getOutputTexture + /// + /// This uses *getTextureCache* to get the Texture Cache of the Output + /// + /// Returns: + /// Ouptut Texture Cache + /// getOutputTexture() { return this.getTextureCache('OUTPUT'); } + /// + /// Function: getArgumentTexture + /// + /// This uses *getTextureCache** to get the Texture Cache of the argument supplied + /// + /// Parameters: + /// name {String} Name of the argument + /// + /// Returns: + /// Texture cache for the supplied argument + /// getArgumentTexture(name) { return this.getTextureCache(`ARGUMENT_${ name }`); } + /// + /// Function: getSubKernelTexture + /// + /// This uses *getTextureCache* to get the Texture Cache of the sub-kernel + /// + /// Parameters: + /// name {String} Name of the subKernel + /// + /// Returns: + /// Texture cache for the subKernel + /// getSubKernelTexture(name) { return this.getTextureCache(`SUB_KERNEL_${ name }`); } + /// + /// Function: getTextureCache + /// + /// Returns the Texture Cache of the supplied parameter (can be kernel, sub-kernel or argument) + /// + /// Parameters: + /// name {String} Name of the subkernel, argument, or kernel. + /// + /// Returns: + /// Texture cache + /// getTextureCache(name) { if (this.outputToTexture) { // Don't retain a handle on the output texture, we might need to render on the same texture later @@ -330,6 +428,15 @@ module.exports = class WebGLKernel extends KernelBase { return this.textureCache[name] = this._webGl.createTexture(); } + /// + /// Function: setupParams + /// + /// Setup the parameter types for the parameters + /// supplied to the Kernel function + /// + /// Parameters: + /// args {Array} The actual parameters sent to the Kernel + /// setupParams(args) { const paramTypes = this.paramTypes = []; for (let i = 0; i < args.length; i++) { @@ -339,6 +446,13 @@ module.exports = class WebGLKernel extends KernelBase { } } + /// + /// Function: getUniformLocation + /// + /// Return WebGlUniformLocation for various variables + /// related to webGl program, such as user-defiend variables, + /// as well as, dimension sizes, etc. + /// getUniformLocation(name) { let location = this.programUniformLocationCache[name]; if (!location) { @@ -348,6 +462,47 @@ module.exports = class WebGLKernel extends KernelBase { return location; } + /// + /// Function: _getFragShaderArtifactMap + /// + /// Generate Shader artifacts for the kernel program. + /// The final object contains HEADER, KERNEL, MAIN_RESULT, and others. + /// + /// Parameters: + /// args {Array} The actual parameters sent to the Kernel + /// + /// Returns: + /// {Object} An object containing the Shader Artifacts(CONSTANTS, HEADER, KERNEL, etc.) + /// + _getFragShaderArtifactMap(args) { + return { + HEADER: this._getHeaderString(), + LOOP_MAX: this._getLoopMaxString(), + CONSTANTS: this._getConstantsString(), + DECODE32_ENDIANNESS: this._getDecode32EndiannessString(), + ENCODE32_ENDIANNESS: this._getEncode32EndiannessString(), + GET_WRAPAROUND: this._getGetWraparoundString(), + GET_TEXTURE_CHANNEL: this._getGetTextureChannelString(), + GET_TEXTURE_INDEX: this._getGetTextureIndexString(), + GET_RESULT: this._getGetResultString(), + MAIN_PARAMS: this._getMainParamsString(args), + MAIN_CONSTANTS: this._getMainConstantsString(), + KERNEL: this._getKernelString(), + MAIN_RESULT: this._getMainResultString() + }; + } + + /// + /// Function: _addArgument + /// + /// Adds kernel parameters to the Argument Texture, + /// binding it to the webGl instance, etc. + /// + /// Parameters: + /// value {Array|Texture|Number} The actual argument supplied to the kernel + /// type {String} Type of the argument + /// name {String} Name of the argument + /// _addArgument(value, type, name) { const gl = this._webGl; const argumentTexture = this.getArgumentTexture(name); @@ -429,6 +584,15 @@ module.exports = class WebGLKernel extends KernelBase { this.argumentsLength++; } + /// + /// Function: _getHeaderString + /// + /// Get the header string for the program. + /// This returns an empty string if no sub-kernels are defined. + /// + /// Returns: + /// result {String} + /// _getHeaderString() { return ( this.subKernels !== null || this.subKernelProperties !== null ? @@ -438,6 +602,14 @@ module.exports = class WebGLKernel extends KernelBase { ); } + /// + /// Function: _getLoopMaxString + /// + /// Get the maximum loop size String. + /// + /// Returns: + /// result {String} + /// _getLoopMaxString() { return ( this.loopMaxIterations ? @@ -446,6 +618,16 @@ module.exports = class WebGLKernel extends KernelBase { ); } + /// + /// Function: _getConstantsString + /// + /// Generate transpiled glsl Strings for constant parameters sent to a kernel + /// + /// They can be defined by *hardcodeConstants* + /// + /// Returns: + /// result {String} + /// _getConstantsString() { const result = []; const threadDim = this.threadDim; @@ -465,6 +647,14 @@ module.exports = class WebGLKernel extends KernelBase { return this._linesToString(result); } + /// + /// Function: _getTextureCoordinate + /// + /// Get texture coordinate string for the program + /// + /// Returns: + /// result {String} + /// _getTextureCoordinate() { const names = this.subKernelOutputVariableNames; if (names === null || names.length < 1) { @@ -474,6 +664,14 @@ module.exports = class WebGLKernel extends KernelBase { } } + /// + /// Function: _getDecode32EndiannessString + /// + /// Get Decode32 endianness string for little-endian and big-endian + /// + /// Returns: + /// result {String} + /// _getDecode32EndiannessString() { return ( this.endianness === 'LE' ? @@ -482,6 +680,14 @@ module.exports = class WebGLKernel extends KernelBase { ); } + /// + /// Function: _getEncode32EndiannessString + /// + /// Get Encode32 endianness string for little-endian and big-endian + /// + /// Returns: + /// result {String} + /// _getEncode32EndiannessString() { return ( this.endianness === 'LE' ? @@ -490,6 +696,9 @@ module.exports = class WebGLKernel extends KernelBase { ); } + /// + /// Function: _getGetWraparoundString + /// _getGetWraparoundString() { return ( this.wraparound ? @@ -498,6 +707,9 @@ module.exports = class WebGLKernel extends KernelBase { ); } + /// + /// Function: _getGetTextureChannelString + /// _getGetTextureChannelString() { if (!this.floatTextures) return ''; @@ -507,6 +719,13 @@ module.exports = class WebGLKernel extends KernelBase { ]); } + /// + /// Function: _getGetTextureIndexString + /// + /// Get generic texture index string, if floatTextures flag is true. + /// + /// > ' index = float(int(index)/4);\n' + /// _getGetTextureIndexString() { return ( this.floatTextures ? @@ -515,6 +734,9 @@ module.exports = class WebGLKernel extends KernelBase { ); } + /// + /// Function: _getGetResultString + /// _getGetResultString() { if (!this.floatTextures) return ' return decode32(texel);\n'; return this._linesToString([ @@ -525,6 +747,17 @@ module.exports = class WebGLKernel extends KernelBase { ]); } + /// + /// Function: _getMainParamsString + /// + /// Generate transpiled glsl Strings for user-defined parameters sent to a kernel + /// + /// Parameters: + /// args {Array} The actual parameters sent to the Kernel + /// + /// Returns: + /// result {String} + /// _getMainParamsString(args) { const result = []; const paramTypes = this.paramTypes; @@ -565,6 +798,13 @@ module.exports = class WebGLKernel extends KernelBase { } return this._linesToString(result); } + + /// + /// Function: _getMainConstantsString + /// + /// Returns: + /// Texture cache + /// _getMainConstantsString() { const result = []; if (this.constants) { @@ -581,6 +821,15 @@ module.exports = class WebGLKernel extends KernelBase { } return this._linesToString(result); } + + /// + /// Function: _getKernelString + /// + /// Get Kernel program string (in *glsl*) for a kernel. + /// + /// Returns: + /// result {String} + /// _getKernelString() { const result = []; const names = this.subKernelOutputVariableNames; @@ -607,6 +856,15 @@ module.exports = class WebGLKernel extends KernelBase { return this._linesToString(result) + this.functionBuilder.getPrototypeString('kernel'); } + + /// + /// Function: _getMainResultString + /// + /// Get main result string with checks for floatOutput, graphical, subKernelsOutputs, etc. + /// + /// Returns: + /// result {String} + /// _getMainResultString() { const names = this.subKernelOutputVariableNames; const result = []; @@ -662,6 +920,15 @@ module.exports = class WebGLKernel extends KernelBase { return this._linesToString(result); } + /// + /// Function: _linesToString + /// + /// Parameters: + /// lines {Array} An Array of strings + /// + /// Returns: + /// Single combined String, seperated by *\n* + /// _linesToString(lines) { if (lines.length > 0) { return lines.join(';\n') + ';\n'; @@ -670,6 +937,16 @@ module.exports = class WebGLKernel extends KernelBase { } } + /// + /// Function: _replaceArtifacts + /// + /// Parameters: + /// src {String} Name of the subkernel, argument, or kernel. + /// map {String} Name of the subkernel, argument, or kernel. + /// + /// Returns: + /// Texture cache + /// _replaceArtifacts(src, map) { return src.replace(/[ ]*__([A-Z]+[0-9]*([_]?[A-Z])*)__;\n/g, (match, artifact) => { if (map.hasOwnProperty(artifact)) { @@ -679,6 +956,11 @@ module.exports = class WebGLKernel extends KernelBase { }); } + /// + /// Function: _addKernels + /// + /// Adds all the sub-kernels supplied with this Kernel instance. + /// _addKernels() { const builder = this.functionBuilder; const gl = this._webGl; @@ -728,6 +1010,19 @@ module.exports = class WebGLKernel extends KernelBase { } } + /// + /// Function: _getFragShaderString + /// + /// Get the fragment shader String. + /// If the String hasn't been compiled yet, + /// then this method compiles it as well + /// + /// Parameters: + /// args {Array} The actual parameters sent to the Kernel + /// + /// Returns: + /// {String} Fragment Shader string + /// _getFragShaderString(args) { if (this.compiledFragShaderString !== null) { return this.compiledFragShaderString; @@ -735,6 +1030,17 @@ module.exports = class WebGLKernel extends KernelBase { return this.compiledFragShaderString = this._replaceArtifacts(fragShaderString, this._getFragShaderArtifactMap(args)); } + /// + /// Function: _getVertShaderString + /// + /// Get the vertical shader String + /// + /// Parameters: + /// args {Array} The actual parameters sent to the Kernel + /// + /// Returns: + /// {String} Vertical Shader string + /// _getVertShaderString(args) { if (this.compiledVertShaderString !== null) { return this.compiledVertShaderString; @@ -743,6 +1049,11 @@ module.exports = class WebGLKernel extends KernelBase { return this.compiledVertShaderString = vertShaderString; } + /// + /// Function: toString + /// + /// Returns the *pre-compiled* Kernel as a JS Object String, that can be reused. + /// toString() { return kernelString(this); } diff --git a/src/texture.js b/src/texture.js index 5df9fdf9..a5829332 100644 --- a/src/texture.js +++ b/src/texture.js @@ -1,4 +1,5 @@ let gpu = null; + /// /// Class: Texture /// @@ -11,6 +12,8 @@ module.exports = class Texture { this.webGl = webGl; } + /// + /// Function: toArray /// /// Converts the Texture into a JavaScript Array. /// @@ -27,6 +30,8 @@ module.exports = class Texture { return copy(this.texture); } /// + /// Function: delete + /// /// Deletes the Texture. /// delete() {