├── webassembly ├── webassemblyDemo.wasm ├── printv.h ├── webassemblyDemo.cpp └── webassemblyDemo.js ├── server.js ├── runner.js ├── README.md ├── example ├── index.html └── script.js ├── GPGPU.js ├── LICENSE └── GPGPU.h /webassembly/webassemblyDemo.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DanRuta/GPGPU/HEAD/webassembly/webassemblyDemo.wasm -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | 3 | const http = require("http") 4 | const fs = require("fs") 5 | const url = require("url") 6 | 7 | http.createServer((request, response) => { 8 | 9 | let path = url.parse(request.url).pathname 10 | let data 11 | 12 | path = (path=="/"?"/example/index.html":path) 13 | 14 | console.log(path) 15 | 16 | switch(path){ 17 | case "/webassemblyDemo.wasm": 18 | try{ 19 | data = fs.readFileSync(__dirname+"/webassembly"+path) 20 | }catch(e){} 21 | break 22 | default: 23 | try{ 24 | data = fs.readFileSync(__dirname+path, "utf8") 25 | }catch(e){} 26 | } 27 | 28 | response.end(data) 29 | 30 | }).listen(1337, () => console.log("Server Listening on port 1337")) 31 | 32 | -------------------------------------------------------------------------------- /webassembly/printv.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | template 4 | void printv(T values) { 5 | EM_ASM(window.printfVector = []); 6 | 7 | for (int i=0; i 17 | void printv(std::vector> values) { 18 | EM_ASM(window.printfVector = []); 19 | 20 | for (int i=0; i 36 | void printv(std::vector>> values) { 37 | EM_ASM(window.printfVector = []); 38 | 39 | for (int i=0; i 60 | void printv(T values[], int size) { 61 | 62 | EM_ASM(window.printfVector = []); 63 | 64 | for (int i=0; i { 15 | willExecute = false 16 | const elapsed = Date.now() - start 17 | console.log("\x1b[32m", "\x1b[1m", `\nDone\x1b[0m in \x1b[36m${elapsed/1000}s\x1b[0m at \x1b[36m${new Date().toTimeString().split(" ")[0]}\x1b[0m`, " Watching...\n") 18 | } 19 | 20 | const compile = () => { 21 | 22 | console.log("") 23 | start = Date.now() 24 | 25 | const tasks = [] 26 | 27 | for (const dir of toChange) { 28 | const file = dir.split("\\")[0] 29 | 30 | tasks.push(new Promise((resolve, reject) => { 31 | 32 | const command = `C:/emsdk/emsdk_env.bat & echo Building... & emcc -o ./webassembly/webassemblyDemo.js ./webassembly/webassemblyDemo.cpp -O3 -s DISABLE_EXCEPTION_CATCHING=2 -s ALLOW_MEMORY_GROWTH=1 -s WASM=1 -s NO_EXIT_RUNTIME=1 -std=c++14` 33 | console.log(`[${file}] Running... `, `\x1b[4m${command}\x1b[0m`) 34 | 35 | exec(command, (error, stdout, stderr) => { 36 | 37 | if (error) { 38 | console.log("\n\x1b[31m", "\x1b[43m", "ERROR", "\x1b[0m Code:", error.code, "running", `\x1b[4m${command}\x1b[0m\n`) 39 | console.log("\x1b[0m", new Error(error)) 40 | return void reject() 41 | } 42 | 43 | if (stdout) console.log(stdout) 44 | if (stderr) console.log(stderr) 45 | resolve() 46 | }) 47 | })) 48 | 49 | 50 | toChange.delete(dir) 51 | } 52 | 53 | Promise.all(tasks).then(finish).catch(finish) 54 | } 55 | 56 | 57 | fs.watch("./", {recursive: true, persistent: true}, (eventType, filename) => { 58 | 59 | if (eventType=="change" && /\.(cpp|c|h|hpp)$/.test(filename) && !toChange.has(filename)) { 60 | if (!willExecute) { 61 | 62 | console.log(`\x1b[0m${filename}\x1b[0m`, "changed") 63 | 64 | toChange.add(filename) 65 | willExecute = true 66 | setTimeout(compile, 1000) 67 | } 68 | } 69 | }) 70 | 71 | 72 | console.log("\x1b[32m", "\x1b[1m", "\nWatching...\x1b[0m") -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GPGPU 2 | 3 | Small library for running arbitrary calculations on the GPU, using JavaScript or WebAssembly. 4 | 5 | ## Usage 6 | Check the ```example/script.js``` file for a few examples for both JavaScript and WebAssembly, as well as some benchmarks for a few examples, to see which one will suit your needs most. 7 | 8 | ### Common 9 | The general structure of the set up code is as follows: 10 | 1. Define the GPGPU instance with the size (and context, optionally) 11 | 2. Create the framebuffer (with optional custom dimensions). This is the outgoing array of data. 12 | 3. Add your textures (arrays) 13 | 4. Build program with your shader(s) 14 | 5. Add any attribs/uniform variables you need in your shaders 15 | 6. Run the computations (draw) 16 | 7. Update the texture data, if needed 17 | 8. Read the data 18 | 9. Delete to clean up 19 | 20 | ##### Quick JavaScript example: 21 | ```javascript 22 | const testData = new Float32Array(100*4) 23 | 24 | for (let i=0; i<100*4; i++) { 25 | testData[i] = i 26 | } 27 | // 1 28 | const gpu = new GPGPU({height: 100, width: 100}) 29 | // 2 30 | gpu.makeFrameBuffer() 31 | // 3 32 | gpu.makeTexture(testData) 33 | // 4 34 | gpu.buildProgram(` 35 | precision highp float; 36 | 37 | uniform sampler2D texture; 38 | varying vec2 vTextureCoord; 39 | 40 | void main() { 41 | gl_FragColor = texture2D(texture, vTextureCoord); 42 | }`) 43 | // 5 44 | gpu.addAttrib("position", {numElements: 3, stride: 20, offset: 0}) 45 | gpu.addAttrib("textureCoord", {numElements: 2, stride: 20, offset: 12}) 46 | // 6 47 | gpu.draw() 48 | // 7 49 | testData[0]++ 50 | gpu.updateTexture(0, testData) 51 | gpu.draw() 52 | // 8 53 | const results = gpu.getPixels() 54 | // 9 55 | gpu.delete() 56 | ``` 57 | 58 | ##### Quick WebAssembly (C++) example: 59 | *Note: Currently, when using the WebAssembly version, you must currently have a canvas element on the page (can be hidden with CSS) with id ```theCanvas```. In the future this hopefully shouldn't be required.* 60 | ```C++ 61 | std::string example1Fragment = R"V0G0N( 62 | precision highp float; 63 | 64 | uniform sampler2D texture; 65 | varying vec2 vTextureCoord; 66 | 67 | void main() { 68 | gl_FragColor = texture2D(texture, vTextureCoord); 69 | } 70 | )V0G0N"; 71 | 72 | float data[16384*4]; 73 | 74 | for (int i=0; i<16384*4; i++) { 75 | data[i] = (float)i; 76 | } 77 | 78 | // 1 79 | GPGPU gpu = GPGPU(128, 128); 80 | // 2 81 | gpu.makeFrameBuffer(); 82 | // 3 83 | gpu.makeTexture(data); 84 | //4 85 | gpu.buildProgram(example1Fragment); 86 | // 5 87 | gpu.addAttrib("position", 3, 20, 0); 88 | gpu.addAttrib("textureCoord", 2, 20, 12); 89 | // 6 90 | gpu.draw(); 91 | // 7 92 | float* out = gpu.getPixels(); 93 | // 8 94 | gpu.deleteGL(); 95 | ``` 96 | 97 | ## API 98 | 99 | ### JS: GPGPU({gl, height, width}) 100 | ### C++: GPGPU(height, width) 101 | _int_ **height** - Height of the context in pixels (not used when gl is given) 102 | _int_ **width** - Width of the context in pixels (not used when gl is given) 103 | _webgl context_ **gl** - Use an existing context. The height and width will be set to the given gl context's. (optional) 104 | 105 | This will create the webgl context with the given size. 106 | 107 | 108 | ### makeFrameBuffer(), makeFrameBuffer(width, height) 109 | _int_ **height** - Height of the frameBuffer in pixels (optional - will default to the context height) 110 | _int_ **width** - Width of the frameBuffer in pixels (optional - will default to the context width) 111 | 112 | This will init the outgoing array data. If your shader increases/reduces the data cardinality, you can set the size of the framebuffer to something bigger/smaller. 113 | 114 | ### makeTexture(data), makeTexture(data, width, height) 115 | _Float32Array_ **data** - The data buffer to be read as a texture by the shader 116 | _int_ **height** - Height of the texture in pixels (optional - will default to the context height) 117 | _int_ **width** - Width of the texture in pixels (optional - will default to the context width) 118 | 119 | This will add a texture (incoming data array) to the computation. You can call this several times, with different arrays, for multiple arrays/textures to be loaded by the shader. When writing your shader, you will be able to access your textures incrementally, as ```texture0```, ```texture1```, ```texture2```, etc. You can set different dimensions if their size differs from the context's (eg a second, kernel texture). 120 | 121 | ### updateTexture(data), updateTexture(data, index) 122 | _int_ **index** - The index of the texture to replace. Defaults to 0. 123 | _Float32Array_ **data** - The data buffer to update with 124 | 125 | This will update the data used as a texture. The index of the texture matches the order in which the textures were defined. 126 | 127 | ### buildProgram(fragment), buildProgram(fragment, vertex) 128 | _string_ **fragment** - The GLSL fragment shader to carry out the compute for each pixel 129 | _string_ **vertex** - The GLSL vertex shader (Optional) This defaults to a standard vertex shader: 130 | ```glsl 131 | attribute vec3 position; 132 | attribute vec2 textureCoord; 133 | 134 | varying highp vec2 vTextureCoord; 135 | 136 | void main() { 137 | gl_Position = vec4(position, 1.0); 138 | vTextureCoord = textureCoord; 139 | } 140 | ``` 141 | This will bind the two shaders to the context, compile them, and throw any errors. 142 | 143 | ### JS: addAttrib(name), addAttrib(name, {numElements, stride, offset}) 144 | ### C++: addAttrib(name), addAttrib(name, numElements, stride, offset) 145 | _string_ **name** - The name of the attribute 146 | _int_ **numElements** - How many elements in the vector (Optional - will default to 3). Can be 1-4. 147 | _int_ **stride** - The stride in bytes (Optional - will default to 20) 148 | _int_ **offset** - Pointer/offset (Optional - will default to 0) 149 | 150 | This will set a read-only global variable which may change per vertex (used in the vertex shader). 151 | 152 | ### addUniform(name), addUniform(name, value), addUniform(name, value, type) 153 | _string_ **name** - The name of the uniform 154 | _number_ **value** - The value assigned to the uniform 155 | _string_ **type** - Override for the function used to set the uniform (Optional - defaults to "uniform1f") 156 | 157 | This will set a global variable for use in the fragment shader. It is unchanged for the entire draw call. 158 | 159 | ### draw(), draw(texture) 160 | _webgl texture_ **texture** - You can set a different GPGPU's framebuffer as an input, and the shader will take it as input , instead of the first given texture (Optional) 161 | 162 | This will bind all the textures (once) and will run the shaders on the input texture(s). 163 | 164 | ### getPixels(), getPixels(startX, startY, spanX, spanY) 165 | _int_ **startX** - The x position in the framebuffer to start reading from (Optional - will default to 0) 166 | _int_ **startY** - The y position in the framebuffer to start reading from (Optional - will default to 0) 167 | _int_ **spanX** - How many x pixels to read from the framebuffer (Optional - will default to the end) 168 | _int_ **spanY** - How many y pixels to read from the framebuffer (Optional - will default to the end) 169 | 170 | Once the ```draw()``` function was called, this can be used to read the data from the framebuffer. 171 | 172 | ### delete() - C++: deleteGL() 173 | 174 | This will clean up by deleting the context and associated data. 175 | 176 | ## Demos 177 | 178 | Run ```node server``` and go to ```localhost:1337``` to see some basic demos with performance comparison vs javascript. 179 | 180 | ## Compiling 181 | 182 | To edit/compile the C++ demo, first run ```node runner.js``` to watch for file changes and automatically compile. -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | GPGPU Examples 5 | 6 | 7 | 8 | 9 | 138 | 139 | 140 | 141 | Open the console to see the data logged out, alongside timings (in ms). Change the values below to see the perf diffences with different inputs.


142 |

143 | 144 | Iterations:

145 | Map size (square):

146 | Max plotting iterations:

147 | 148 |
Identity function (x=x)
149 | 150 | 151 | 152 | 153 | 154 |
155 |
156 |
157 |
158 | 159 |
Quadratic function (x=x*x)
160 | 161 | 162 | 163 | 164 | 165 |
166 |
167 |
168 |
169 | 170 |
Element-wise addition across 3 identical arrays, + a variable:
171 | 172 | 173 | 174 | 175 | 176 |
177 |
178 |
179 |
180 | 181 |
Convolution of a 3x3 kernel (single channel) on a tiny, 5x5 map
182 | 183 | 184 | 185 | 186 | 187 |
188 |
189 |
190 |
191 |
192 | 193 | 194 | 195 | -------------------------------------------------------------------------------- /GPGPU.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | 3 | class GPGPU { 4 | 5 | constructor ({gl, height, width}) { 6 | 7 | this.ready = false 8 | this.height = height 9 | this.width = width 10 | this.attribs = {} 11 | this.uniforms = {} 12 | this.textures = [] 13 | 14 | this.standardGeometryVals = new Float32Array([ 15 | -1.0, 1.0, 0.0, 0.0, 1.0, // top left 16 | -1.0, -1.0, 0.0, 0.0, 0.0, // bottom left 17 | 1.0, 1.0, 0.0, 1.0, 1.0, // top right 18 | 1.0, -1.0, 0.0, 1.0, 0.0 // bottom right 19 | ]) 20 | 21 | this.standardVertex = ` 22 | attribute vec3 position; 23 | attribute vec2 textureCoord; 24 | 25 | varying highp vec2 vTextureCoord; 26 | 27 | void main() { 28 | gl_Position = vec4(position, 1.0); 29 | vTextureCoord = textureCoord; 30 | } 31 | ` 32 | 33 | if (gl) { 34 | this.gl = gl 35 | this.height = gl.drawingBufferHeight 36 | this.width = gl.drawingBufferWidth 37 | } else { 38 | const canvas = document.createElement("canvas") 39 | canvas.width = width 40 | canvas.height = height 41 | this.gl = canvas.getContext("webgl", {premultipliedAlpha: false}) 42 | } 43 | 44 | const textureFloat = this.gl.getExtension("OES_texture_float") 45 | 46 | if (!textureFloat) { 47 | alert("Floating point textures not supported") 48 | } 49 | } 50 | 51 | makeTexture (data, width=this.width, height=this.height) { 52 | 53 | this.textures.push({tex: this.gl.createTexture(), width, height}) 54 | this.gl.bindTexture(this.gl.TEXTURE_2D, this.textures[this.textures.length-1].tex) 55 | 56 | this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.NEAREST) 57 | this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.NEAREST) 58 | this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE) 59 | this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE) 60 | 61 | this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, width, height, 0, this.gl.RGBA, this.gl.FLOAT, data) 62 | } 63 | 64 | updateTexture (data, index=0) { 65 | const tex = this.textures[index] 66 | this.gl.bindTexture(this.gl.TEXTURE_2D, tex.tex) 67 | this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, tex.width, tex.height, 0, this.gl.RGBA, this.gl.FLOAT, data) 68 | } 69 | 70 | makeFrameBuffer (width=this.width, height=this.height) { 71 | 72 | const texture = this.gl.createTexture() 73 | 74 | this.gl.bindTexture(this.gl.TEXTURE_2D, texture) 75 | this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, width, height, 0, this.gl.RGBA, this.gl.FLOAT, null) 76 | this.gl.bindTexture(this.gl.TEXTURE_2D, null) 77 | 78 | this.framebuffer = this.gl.createFramebuffer() 79 | this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.framebuffer) 80 | this.gl.framebufferTexture2D(this.gl.FRAMEBUFFER, this.gl.COLOR_ATTACHMENT0, this.gl.TEXTURE_2D, texture, 0) 81 | 82 | // Check the frameBuffer status 83 | const status = this.gl.checkFramebufferStatus(this.gl.FRAMEBUFFER) 84 | 85 | if (status != this.gl.FRAMEBUFFER_COMPLETE) { 86 | switch (status) { 87 | case this.gl.FRAMEBUFFER_UNSUPPORTED: 88 | throw("FRAMEBUFFER_UNSUPPORTED") 89 | case this.gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT: 90 | throw("FRAMEBUFFER_INCOMPLETE_ATTACHMENT") 91 | case this.gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS: 92 | throw("FRAMEBUFFER_INCOMPLETE_DIMENSIONS") 93 | case this.gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: 94 | throw("FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT") 95 | default: 96 | throw(`Framebuffer error: ${status}`) 97 | } 98 | } 99 | } 100 | 101 | buildProgram (fragmentSource, vertexSource=this.standardVertex) { 102 | this.program = this.gl.createProgram() 103 | const vertex = this.compileShader(vertexSource, this.gl.VERTEX_SHADER) 104 | const fragment = this.compileShader(fragmentSource, this.gl.FRAGMENT_SHADER) 105 | 106 | this.gl.attachShader(this.program, vertex) 107 | this.gl.attachShader(this.program, fragment) 108 | 109 | this.gl.linkProgram(this.program) 110 | this.gl.deleteShader(fragment) 111 | this.gl.deleteShader(vertex) 112 | 113 | this.gl.useProgram(this.program) 114 | 115 | const standardGeometry = this.gl.createBuffer() 116 | this.gl.bindBuffer(this.gl.ARRAY_BUFFER, standardGeometry) 117 | this.gl.bufferData(this.gl.ARRAY_BUFFER, this.standardGeometryVals, this.gl.STATIC_DRAW) 118 | } 119 | 120 | compileShader (source, type) { 121 | const shader = this.gl.createShader(type) 122 | this.gl.shaderSource(shader, source) 123 | this.gl.compileShader(shader) 124 | 125 | const compileStatus = this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS) 126 | 127 | if (!compileStatus) { 128 | throw `${type} compilation failed: ${this.gl.getShaderInfoLog(shader)}` 129 | } 130 | 131 | return shader 132 | } 133 | 134 | addAttrib (name, {numElements=3, stride=20, offset=0}={}) { 135 | this.gl.useProgram(this.program) 136 | this.attribs[name] = this.gl.getAttribLocation(this.program, name) 137 | this.gl.enableVertexAttribArray(this.attribs[name]) 138 | this.gl.vertexAttribPointer(this.attribs[name], numElements, this.gl.FLOAT, this.gl.FALSE, stride, offset) 139 | } 140 | 141 | addUniform (name, value, type="uniform1f") { 142 | this.uniforms[name] = this.gl.getUniformLocation(this.program, name) 143 | 144 | if (value != undefined) { 145 | this.gl[type](this.uniforms[name], value) 146 | } 147 | } 148 | 149 | draw (texture=this.textures[this.textures.length-1]) { 150 | 151 | this.gl.activeTexture(this.gl.TEXTURE0) 152 | this.gl.bindTexture(this.gl.TEXTURE_2D, texture.tex) 153 | 154 | if (!this.ready) { 155 | 156 | this.ready = true 157 | this.addUniform("texture0", 0, "uniform1i") 158 | 159 | const texToAdd = texture==this.textures[this.textures.length-1] ? this.textures.length-1 : this.textures.length 160 | 161 | for (let t=0; t 2 | #include 3 | #include 4 | 5 | // Copied over only the necessary code from the html5.h emscripten library 6 | // https://github.com/kripken/emscripten/blob/master/system/include/emscripten/html5.h 7 | // http://kripken.github.io/emscripten-site/docs/api_reference/html5.h 8 | extern "C" { 9 | 10 | #define EM_BOOL int 11 | #define EMSCRIPTEN_RESULT int 12 | 13 | typedef int EMSCRIPTEN_WEBGL_CONTEXT_HANDLE; 14 | typedef struct EmscriptenWebGLContextAttributes { 15 | EM_BOOL alpha; 16 | EM_BOOL depth; 17 | EM_BOOL stencil; 18 | EM_BOOL antialias; 19 | EM_BOOL premultipliedAlpha; 20 | EM_BOOL preserveDrawingBuffer; 21 | EM_BOOL preferLowPowerToHighPerformance; 22 | EM_BOOL failIfMajorPerformanceCaveat; 23 | 24 | int majorVersion; 25 | int minorVersion; 26 | 27 | EM_BOOL enableExtensionsByDefault; 28 | EM_BOOL explicitSwapControl; 29 | } EmscriptenWebGLContextAttributes; 30 | 31 | extern EMSCRIPTEN_WEBGL_CONTEXT_HANDLE emscripten_webgl_create_context(const char *target, const EmscriptenWebGLContextAttributes *attributes); 32 | 33 | extern EMSCRIPTEN_RESULT emscripten_webgl_destroy_context(EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context); 34 | 35 | extern EMSCRIPTEN_RESULT emscripten_webgl_make_context_current(EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context); 36 | 37 | } 38 | 39 | class Tex { 40 | public: 41 | 42 | GLuint texId; 43 | int width; 44 | int height; 45 | 46 | Tex (GLuint id, int w, int h) { 47 | texId = id; 48 | width = w; 49 | height = h; 50 | } 51 | }; 52 | 53 | class GPGPU { 54 | public: 55 | 56 | bool ready = false; 57 | int height = 0; 58 | int width = 0; 59 | std::unordered_map attribs; 60 | std::unordered_map uniforms; 61 | 62 | GLuint program; 63 | std::vector textures; 64 | 65 | EMSCRIPTEN_WEBGL_CONTEXT_HANDLE gl; 66 | 67 | float standardGeometryVals[20] = { 68 | -1.0, 1.0, 0.0, 0.0, 1.0, 69 | -1.0, -1.0, 0.0, 0.0, 0.0, 70 | 1.0, 1.0, 0.0, 1.0, 1.0, 71 | 1.0, -1.0, 0.0, 1.0, 0.0 72 | }; 73 | 74 | std::string standardVertex = R"V0G0N( 75 | attribute vec3 position; 76 | attribute vec2 textureCoord; 77 | 78 | varying highp vec2 vTextureCoord; 79 | 80 | void main() { 81 | gl_Position = vec4(position, 1.0); 82 | vTextureCoord = textureCoord; 83 | } 84 | )V0G0N"; 85 | 86 | // TODO, accept existing context as parameter 87 | GPGPU (int h, int w) { 88 | 89 | height = h; 90 | width = w; 91 | 92 | // Context configurations 93 | EmscriptenWebGLContextAttributes attrs; 94 | attrs.explicitSwapControl = 0; 95 | attrs.depth = 1; 96 | attrs.stencil = 1; 97 | attrs.antialias = 1; 98 | attrs.majorVersion = 2; 99 | attrs.minorVersion = 0; 100 | 101 | char id[10] = "theCanvas"; 102 | gl = emscripten_webgl_create_context(id, &attrs); 103 | emscripten_webgl_make_context_current(gl); 104 | 105 | // Check for the OES_texture_float extension support 106 | std::string availableExtensions = std::string((char*)glGetString(GL_EXTENSIONS)); 107 | 108 | if (availableExtensions.find("OES_texture_float") == std::string::npos) { 109 | printf("Floating point textures not supported\n"); 110 | } 111 | }; 112 | 113 | ~GPGPU() { 114 | emscripten_webgl_destroy_context(gl); 115 | }; 116 | 117 | void makeTexture (void) { 118 | makeTexture({}); 119 | } 120 | void makeTexture (float* buffer) { 121 | makeTexture(buffer, width, height); 122 | } 123 | 124 | void makeTexture (float* buffer, int w, int h) { 125 | 126 | GLuint texId; 127 | glGenTextures(1, &texId); 128 | glBindTexture(GL_TEXTURE_2D, texId); 129 | 130 | glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 131 | glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 132 | glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 133 | glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 134 | 135 | glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA32F, w, h, 0, GL_RGBA, GL_FLOAT, buffer); 136 | Tex tex = Tex(texId, w, h); 137 | textures.push_back(tex); 138 | } 139 | 140 | void updateTexture (float* buffer, int textureIndex=0) { 141 | Tex tex = textures[textureIndex]; 142 | glBindTexture(GL_TEXTURE_2D, tex.texId); 143 | glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA32F, tex.width, tex.height, 0, GL_RGBA, GL_FLOAT, buffer); 144 | } 145 | 146 | void makeFrameBuffer (void) { 147 | makeFrameBuffer(width, height); 148 | } 149 | 150 | void makeFrameBuffer (int w, int h) { 151 | 152 | GLuint texId; 153 | glGenTextures(1, &texId); 154 | glBindTexture(GL_TEXTURE_2D, texId); 155 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, w, h, 0, GL_RGBA, GL_FLOAT, 0); 156 | glBindTexture(GL_TEXTURE_2D, 0); 157 | 158 | GLuint fbId; 159 | glGenFramebuffers(1, &fbId); 160 | glBindFramebuffer(GL_FRAMEBUFFER, fbId); 161 | glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texId, 0); 162 | 163 | int status = glCheckFramebufferStatus(GL_FRAMEBUFFER); 164 | 165 | if (status != GL_FRAMEBUFFER_COMPLETE) { 166 | switch (status) { 167 | case GL_FRAMEBUFFER_UNSUPPORTED: 168 | printf("FRAMEBUFFER_UNSUPPORTED\n"); 169 | break; 170 | case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: 171 | printf("GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT\n"); 172 | break; 173 | case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: 174 | printf("GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS\n"); 175 | break; 176 | case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: 177 | printf("GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT\n"); 178 | break; 179 | default: 180 | printf("Framebuffer error: %d\n", status); 181 | } 182 | } 183 | } 184 | 185 | void buildProgram (std::string &fragmentSource) { 186 | buildProgram(fragmentSource, standardVertex); 187 | } 188 | 189 | void buildProgram (std::string &fragmentSource, std::string &vertexSource) { 190 | 191 | program = glCreateProgram(); 192 | GLuint vertex = compileShader(&vertexSource, GL_VERTEX_SHADER); 193 | GLuint fragment = compileShader(&fragmentSource, GL_FRAGMENT_SHADER); 194 | 195 | glAttachShader(program, vertex); 196 | glAttachShader(program, fragment); 197 | 198 | glLinkProgram(program); 199 | glDeleteShader(vertex); 200 | glDeleteShader(fragment); 201 | 202 | glUseProgram(program); 203 | 204 | GLuint standardGeometry; 205 | 206 | glGenBuffers(1, &standardGeometry); 207 | glBindBuffer(GL_ARRAY_BUFFER, standardGeometry); 208 | glBufferData(GL_ARRAY_BUFFER, 20*4, standardGeometryVals, GL_STATIC_DRAW); 209 | } 210 | 211 | GLuint compileShader (std::string *source, GLenum type) { 212 | 213 | const GLchar* sourceString[1]; 214 | GLint sourceStringLengths[1]; 215 | 216 | sourceString[0] = source->c_str(); 217 | sourceStringLengths[0] = source->length(); 218 | GLuint shader = glCreateShader(type); 219 | 220 | glShaderSource(shader, 1, sourceString, sourceStringLengths); 221 | glCompileShader(shader); 222 | 223 | // Check if there were errors 224 | int infoLen = 0; 225 | glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); 226 | 227 | if (infoLen > 1) { 228 | 229 | char infoLog[infoLen]; 230 | 231 | // And print them out 232 | glGetShaderInfoLog(shader, infoLen, NULL, infoLog); 233 | printf("%s\n", infoLog); 234 | } 235 | 236 | return shader; 237 | } 238 | 239 | void addAttrib (std::string name, int numElements=3, int stride=20, int offset=0) { 240 | glUseProgram(program); 241 | int loc = glGetAttribLocation(program, name.c_str()); 242 | attribs[name] = loc; 243 | glEnableVertexAttribArray(loc); 244 | glVertexAttribPointer(loc, numElements, GL_FLOAT, GL_FALSE, stride, (GLvoid*)offset); 245 | } 246 | 247 | template 248 | void addUniform (std::string name, T value=0, std::string type="uniform1f") { 249 | int loc = glGetUniformLocation(program, name.c_str()); 250 | uniforms[name] = loc; 251 | 252 | if (type=="uniform1f") { 253 | glUniform1f(loc, value); 254 | } else if (type=="uniform1i") { 255 | glUniform1i(loc, value); 256 | } 257 | } 258 | 259 | void draw (int textureIndex=-1) { 260 | 261 | Tex texture = textures[textures.size()-1]; 262 | 263 | if (textureIndex!=-1) { 264 | texture = textures[textureIndex]; 265 | } 266 | 267 | glActiveTexture(GL_TEXTURE0); 268 | glBindTexture(GL_TEXTURE_2D, texture.texId); 269 | 270 | if (!ready) { 271 | 272 | ready = true; 273 | addUniform("texture0", 0, "uniform1i"); 274 | 275 | int texToAdd = textureIndex==textures.size()-1 ? textures.size()-1 : textures.size(); 276 | 277 | for (int t=0; t 2 | #include 3 | #include 4 | #include "printv.h" // for logging out array data 5 | #include 6 | #include 7 | #include "../GPGPU.h" 8 | 9 | std::string example1Fragment = R"V0G0N( 10 | #ifdef GL_FRAGMENT_PRECISION_HIGH 11 | precision highp float; 12 | #else 13 | precision mediump float; 14 | #endif 15 | 16 | uniform sampler2D texture; 17 | varying vec2 vTextureCoord; 18 | 19 | void main() { 20 | gl_FragColor = texture2D(texture, vTextureCoord); 21 | } 22 | )V0G0N"; 23 | 24 | std::string example2Fragment = R"V0G0N( 25 | #ifdef GL_FRAGMENT_PRECISION_HIGH 26 | precision highp float; 27 | #else 28 | precision mediump float; 29 | #endif 30 | 31 | uniform sampler2D texture; 32 | varying vec2 vTextureCoord; 33 | 34 | void main() { 35 | gl_FragColor = texture2D(texture, vTextureCoord) * texture2D(texture, vTextureCoord); 36 | } 37 | )V0G0N"; 38 | 39 | std::string example3Fragment = R"V0G0N( 40 | #ifdef GL_FRAGMENT_PRECISION_HIGH 41 | precision highp float; 42 | uniform highp sampler2D texture1; 43 | uniform highp sampler2D texture2; 44 | uniform highp sampler2D texture3; 45 | #else 46 | precision mediump float; 47 | uniform mediump sampler2D texture1; 48 | uniform mediump sampler2D texture2; 49 | uniform mediump sampler2D texture3; 50 | #endif 51 | 52 | varying vec2 vTextureCoord; 53 | uniform float variable; 54 | 55 | void main() { 56 | 57 | vec4 pixel1 = texture2D(texture1, vTextureCoord); 58 | vec4 pixel2 = texture2D(texture2, vTextureCoord); 59 | vec4 pixel3 = texture2D(texture3, vTextureCoord); 60 | 61 | // pixel3.r = 1.0; 62 | 63 | gl_FragColor.r = pixel1.r + pixel2.r + pixel3.r + variable; 64 | gl_FragColor.g = pixel1.g + pixel2.g + pixel3.g + variable; 65 | gl_FragColor.b = pixel1.b + pixel2.b + pixel3.b + variable; 66 | gl_FragColor.a = pixel1.a + pixel2.a + pixel3.a + variable; 67 | } 68 | )V0G0N"; 69 | 70 | 71 | std::string example4Fragment = R"V0G0N( 72 | #ifdef GL_FRAGMENT_PRECISION_HIGH 73 | precision highp float; 74 | uniform highp sampler2D texture1; 75 | uniform highp sampler2D texture2; // kernel 76 | #else 77 | precision mediump float; 78 | uniform mediump sampler2D texture1; 79 | uniform mediump sampler2D texture2; // kernel 80 | #endif 81 | 82 | varying vec2 vTextureCoord; 83 | uniform float w; 84 | 85 | void main() { 86 | 87 | const int kSpan = 3; 88 | const int spread = kSpan/2; 89 | 90 | // Gather the input values into a map 91 | vec4 n[kSpan*kSpan]; 92 | 93 | for (int i=-spread; i<=spread; i++) { 94 | for (int j=-spread; j<=spread; j++) { 95 | 96 | vec4 value; 97 | 98 | // Zero out values out of bounds 99 | if ( (vTextureCoord.x + float(j)*w) > 1.0 || 100 | (vTextureCoord.x + float(j)*w) < 0.0 || 101 | (vTextureCoord.y + float(i)*w) > 1.0 || 102 | (vTextureCoord.y + float(i)*w) < 0.0 ) { 103 | value.r = 0.0; 104 | } else { 105 | value = texture2D( texture1, vTextureCoord + vec2(float(j)*w, float(i)*w) ); 106 | } 107 | 108 | n[(j+spread)+(i+spread)*kSpan] = value; 109 | } 110 | } 111 | 112 | 113 | gl_FragColor.r = 0.0; 114 | 115 | float step = 1.0 / float(1 + kSpan); 116 | 117 | // Dot product against the kernel 118 | for (int i=0; i= 0 && yCoord < inputSpan && 346 | xCoord >= 0 && xCoord < inputSpan) { 347 | n[(j+spread)*kSpan+(k+spread)] = inputData[(yCoord*inputSpan + xCoord) * 4]; 348 | } else { 349 | n[(j+spread)*kSpan+(k+spread)] = 0; 350 | } 351 | } 352 | } 353 | 354 | for (int j=0; j { 7 | 8 | let testData 9 | let mapSize 10 | let mapSpan 11 | let log = true 12 | let times = parseInt(counterInput.value) 13 | let charts = [] 14 | 15 | const vals = [0,0,2,2,2, 16 | 1,1,0,2,0, 17 | 1,2,1,1,2, 18 | 0,1,2,2,1, 19 | 1,2,0,0,1] 20 | 21 | const kernelVals = [-1, 0,-1, 22 | 1, 0, 1, 23 | 1,-1, 0] 24 | 25 | const eg4Data = new Float32Array(vals.length * 4) 26 | const kernel = new Float32Array(kernelVals.length * 4) 27 | 28 | for (let i=0; i { 38 | 39 | testData = new Float32Array(mapSize*4) 40 | 41 | for (let i=0; i { 50 | setupTimer = Date.now() 51 | 52 | const gpu = new GPGPU({height: mapSpan, width: mapSpan}) 53 | gpu.makeFrameBuffer() 54 | gpu.makeTexture(testData) 55 | gpu.buildProgram(example1Fragment) 56 | 57 | gpu.addAttrib("position", {numElements: 3, stride: 20, offset: 0}) 58 | gpu.addAttrib("textureCoord", {numElements: 2, stride: 20, offset: 12}) 59 | timer = Date.now() 60 | 61 | for (let t=0; t { 79 | timer = Date.now() 80 | 81 | const data = new Float32Array(mapSize*4) 82 | 83 | for (let t=0; t { 96 | setupTimer = Date.now() 97 | 98 | const result = ccallArrays("runExample1WAGPU", "array", ["array", "number"], [testData, times], {heapIn: "HEAPF32", heapOut: "HEAPF32", returnArraySize: mapSize*4}) 99 | const setUpTime = Date.now() - setupTimer - waRunTime 100 | 101 | if (log) { 102 | console.log(`Example 1 (WA GPU - ${times} times): `, result, `\nElapsed set-up: ${setUpTime}\nElapsed run: ${waRunTime}`) 103 | } 104 | 105 | return [setUpTime, waRunTime] 106 | } 107 | 108 | window.runExample1WA = () => { 109 | setupTimer = Date.now() 110 | 111 | const result = ccallArrays("runExample1WA", "array", ["array", "number"], [testData, times], {heapIn: "HEAPF32", heapOut: "HEAPF32", returnArraySize: mapSize*4}) 112 | const setUpTime = Date.now() - setupTimer - waRunTime 113 | 114 | if (log) { 115 | console.log(`Example 1 (WA - ${times} times): `, result, `\nElapsed set-up: ${setUpTime}\nElapsed run: ${waRunTime}`) 116 | } 117 | return [setUpTime, waRunTime] 118 | } 119 | 120 | window.runExample2GPU = () => { 121 | setupTimer = Date.now() 122 | 123 | const gpu = new GPGPU({height: mapSpan, width: mapSpan}) 124 | gpu.makeFrameBuffer() 125 | gpu.makeTexture(testData) 126 | gpu.buildProgram(example2Fragment) 127 | 128 | gpu.addAttrib("position", {numElements: 3, stride: 20, offset: 0}) 129 | gpu.addAttrib("textureCoord", {numElements: 2, stride: 20, offset: 12}) 130 | 131 | timer = Date.now() 132 | 133 | for (let t=0; t { 148 | timer = Date.now() 149 | 150 | const data = new Float32Array(mapSize*4) 151 | 152 | for (let t=0; t { 165 | setupTimer = Date.now() 166 | 167 | const result = ccallArrays("runExample2WAGPU", "array", ["array", "number"], [testData, times], {heapIn: "HEAPF32", heapOut: "HEAPF32", returnArraySize: mapSize*4}) 168 | const setUpTime = Date.now() - setupTimer - waRunTime 169 | 170 | if (log) { 171 | console.log(`Example 2 (WA GPU - ${times} times): `, result, `\nElapsed set-up: ${setUpTime}\nElapsed run: ${waRunTime}`) 172 | } 173 | 174 | return [setUpTime, waRunTime] 175 | } 176 | 177 | window.runExample2WA = () => { 178 | setupTimer = Date.now() 179 | 180 | const result = ccallArrays("runExample2WA", "array", ["array", "number"], [testData, times], {heapIn: "HEAPF32", heapOut: "HEAPF32", returnArraySize: mapSize*4}) 181 | const setUpTime = Date.now() - setupTimer - waRunTime 182 | 183 | if (log) { 184 | console.log(`Example 2 (WA - ${times} times): `, result, `\nElapsed set-up: ${setUpTime}\nElapsed run: ${waRunTime}`) 185 | } 186 | 187 | return [setUpTime, waRunTime] 188 | } 189 | 190 | 191 | window.runExample3GPU = () => { 192 | setupTimer = Date.now() 193 | 194 | const gpu = new GPGPU({height: mapSpan, width: mapSpan}) 195 | gpu.makeFrameBuffer() 196 | gpu.makeTexture(testData) 197 | gpu.makeTexture(testData) 198 | gpu.makeTexture(testData) 199 | gpu.buildProgram(example3Fragment) 200 | 201 | gpu.addAttrib("position", {numElements: 3, stride: 20, offset: 0}) 202 | gpu.addAttrib("textureCoord", {numElements: 2, stride: 20, offset: 12}) 203 | 204 | gpu.addUniform("variable", parseFloat(varInput.value)) 205 | 206 | timer = Date.now() 207 | 208 | for (let t=0; t { 223 | timer = Date.now() 224 | 225 | const data = new Float32Array(mapSize*4) 226 | let variable = parseFloat(varInput.value) 227 | 228 | for (let t=0; t { 241 | setupTimer = Date.now() 242 | 243 | let variable = parseFloat(varInput.value) 244 | const result = ccallArrays("runExample3WAGPU", "array", ["array", "number", "number"], [testData, variable, times], {heapIn: "HEAPF32", heapOut: "HEAPF32", returnArraySize: mapSize*4}) 245 | const setUpTime = Date.now() - setupTimer - waRunTime 246 | 247 | if (log) { 248 | console.log(`Example 3 (WA GPU- ${times} times): `, result, `\nElapsed set-up: ${setUpTime}\nElapsed run: ${waRunTime}`) 249 | } 250 | 251 | return [setUpTime, waRunTime] 252 | } 253 | 254 | window.runExample3WA = () => { 255 | setupTimer = Date.now() 256 | 257 | let variable = parseFloat(varInput.value) 258 | const result = ccallArrays("runExample3WA", "array", ["array", "number", "number"], [testData, variable, times], {heapIn: "HEAPF32", heapOut: "HEAPF32", returnArraySize: mapSize*4}) 259 | const setUpTime = Date.now() - setupTimer - waRunTime 260 | 261 | if (log) { 262 | console.log(`Example 3 (WA - ${times} times): `, result, `\nElapsed set-up: ${setUpTime}\nElapsed run: ${waRunTime}`) 263 | } 264 | 265 | return [setUpTime, waRunTime] 266 | } 267 | 268 | 269 | window.runExample4GPU = () => { 270 | setupTimer = Date.now() 271 | 272 | const gpu = new GPGPU({height: 5, width: 5}) 273 | gpu.makeFrameBuffer() 274 | gpu.makeTexture(eg4Data) 275 | gpu.makeTexture(kernel, 3, 3) 276 | gpu.buildProgram(example4Fragment) 277 | 278 | gpu.addAttrib("position", {numElements: 3, stride: 20, offset: 0}) 279 | gpu.addAttrib("textureCoord", {numElements: 2, stride: 20, offset: 12}) 280 | gpu.addUniform("w", 1/5) 281 | 282 | timer = Date.now() 283 | 284 | for (let t=0; t { 301 | const data = new Float32Array(5*5*4) 302 | timer = Date.now() 303 | const inputSpan = 5 304 | const kSpan = 3 305 | const spread = 1 306 | 307 | for (let t=0; t= 0 && yCoord < inputSpan && 323 | xCoord >= 0 && xCoord < inputSpan) { 324 | n[(j+spread)*kSpan+(k+spread)] = eg4Data[(yCoord*inputSpan + xCoord) * 4] 325 | } else { 326 | n[(j+spread)*kSpan+(k+spread)] = 0 327 | } 328 | } 329 | } 330 | 331 | for (let j=0; j { 350 | setupTimer = Date.now() 351 | 352 | const result = ccallArrays("runExample4WAGPU", "array", ["array", "array", "number"], [eg4Data, kernel, times], {heapIn: "HEAPF32", heapOut: "HEAPF32", returnArraySize: 100}) 353 | const setUpTime = Date.now() - setupTimer - waRunTime 354 | 355 | if (log) { 356 | console.log(`Example 4 (WA GPU - ${times} times): `, result, `\nElapsed set-up: ${setUpTime}\nElapsed run: ${waRunTime}`) 357 | } 358 | 359 | return [setUpTime, waRunTime] 360 | } 361 | 362 | window.runExample4WA = () => { 363 | setupTimer = Date.now() 364 | 365 | const result = ccallArrays("runExample4WA", "array", ["array", "array", "number"], [eg4Data, kernel, times], {heapIn: "HEAPF32", heapOut: "HEAPF32", returnArraySize: 100}) 366 | const setUpTime = Date.now() - setupTimer - waRunTime 367 | 368 | if (log) { 369 | console.log(`Example 4 (WA - ${times} times): `, result, `\nElapsed set-up: ${setUpTime}\nElapsed run: ${waRunTime}`) 370 | } 371 | 372 | return [setUpTime, waRunTime] 373 | } 374 | 375 | 376 | 377 | example1GPU.addEventListener("click", runExample1GPU) 378 | example1JS.addEventListener("click", runExample1JS) 379 | example1WAGPU.addEventListener("click", runExample1WAGPU) 380 | example1WA.addEventListener("click", runExample1WA) 381 | 382 | example2GPU.addEventListener("click", runExample2GPU) 383 | example2JS.addEventListener("click", runExample2JS) 384 | example2WAGPU.addEventListener("click", runExample2WAGPU) 385 | example2WA.addEventListener("click", runExample2WA) 386 | 387 | example3GPU.addEventListener("click", runExample3GPU) 388 | example3JS.addEventListener("click", runExample3JS) 389 | example3WAGPU.addEventListener("click", runExample3WAGPU) 390 | example3WA.addEventListener("click", runExample3WA) 391 | 392 | example4GPU.addEventListener("click", runExample4GPU) 393 | example4JS.addEventListener("click", runExample4JS) 394 | example4WAGPU.addEventListener("click", runExample4WAGPU) 395 | example4WA.addEventListener("click", runExample4WA) 396 | 397 | 398 | mapSizeInput.addEventListener("change", () => { 399 | mapSize = parseInt(mapSizeInput.value) 400 | generateData() 401 | }) 402 | 403 | mapSizeInput.addEventListener("keyup", () => { 404 | mapSize = parseInt(mapSizeInput.value) 405 | generateData() 406 | }) 407 | 408 | counterInput.addEventListener("change", () => { 409 | times = parseInt(counterInput.value) 410 | generateData() 411 | }) 412 | 413 | counterInput.addEventListener("keyup", () => { 414 | times = parseInt(counterInput.value) 415 | generateData() 416 | }) 417 | 418 | const makeChart = () => { 419 | 420 | const canvas = document.createElement("canvas") 421 | canvas.width = window.innerWidth * 0.9 422 | canvas.height = 300 423 | 424 | return [canvas, new Chart(canvas.getContext("2d"), { 425 | type: "line", 426 | data: { 427 | datasets: [{ 428 | label: "GPU", 429 | borderColor: "rgba(150, 26, 31, 0.5)", 430 | backgroundColor: "rgba(150, 26, 31, 0.5)", 431 | data: [], 432 | pointRadius: 0 433 | },{ 434 | label: "GPU (set-up time)", 435 | borderDash: [5, 5], 436 | borderColor: "rgba(150, 26, 31, 0.5)", 437 | backgroundColor: "rgba(150, 26, 31, 0.5)", 438 | fill: false, 439 | data: [], 440 | pointRadius: 0 441 | },{ 442 | label: "JS", 443 | borderColor: "rgba(250, 222, 52, 0.5)", 444 | backgroundColor: "rgba(250, 222, 52, 0.5)", 445 | data: [], 446 | pointRadius: 0 447 | },{ 448 | label: "WebAssembly + GPU (set-up time)", 449 | borderDash: [5, 5], 450 | borderColor: "rgba(126, 53, 136, 0.5)", 451 | backgroundColor: "rgba(126, 53, 136, 0.5)", 452 | fill: false, 453 | data: [], 454 | pointRadius: 0 455 | },{ 456 | label: "WebAssembly + GPU", 457 | borderColor: "rgba(126, 53, 136, 0.5)", 458 | backgroundColor: "rgba(126, 53, 136, 0.5)", 459 | data: [], 460 | pointRadius: 0 461 | },{ 462 | label: "WebAssembly", 463 | borderColor: "rgba(101, 79, 240, 0.5)", 464 | backgroundColor: "rgba(101, 79, 240, 0.5)", 465 | data: [], 466 | pointRadius: 0 467 | },{ 468 | label: "WebAssembly (set-up time)", 469 | borderDash: [5, 5], 470 | borderColor: "rgba(101, 79, 240, 0.5)", 471 | backgroundColor: "rgba(101, 79, 240, 0.5)", 472 | fill: false, 473 | data: [], 474 | pointRadius: 0 475 | }] 476 | }, 477 | options: { 478 | scales: { 479 | xAxes: [{ 480 | type: "linear", 481 | position: "bottom" 482 | }], 483 | yAxes: [{ 484 | ticks: { 485 | beginAtZero: true 486 | } 487 | }] 488 | }, 489 | tooltips: { 490 | enabled: false 491 | }, 492 | responsive: false 493 | } 494 | })] 495 | } 496 | 497 | const plot = (i, chartI, egI) => { 498 | log = false 499 | times = Math.min(Math.floor(Math.pow(2, i/6)), parseInt(maxPlotItsInput.value)) 500 | const [setUp, elapsed] = window[`runExample${egI+1}GPU`]() 501 | const [jsElapsed] = window[`runExample${egI+1}JS`]() 502 | const [waGPUSetUp, waGPUElapsed] = window[`runExample${egI+1}WAGPU`]() 503 | 504 | charts[chartI].data.datasets[0].data.push({x: times, y: elapsed}) 505 | charts[chartI].data.datasets[1].data.push({x: times, y: setUp}) 506 | charts[chartI].data.datasets[2].data.push({x: times, y: jsElapsed}) 507 | charts[chartI].data.datasets[3].data.push({x: times, y: waGPUSetUp}) 508 | charts[chartI].data.datasets[4].data.push({x: times, y: waGPUElapsed}) 509 | 510 | if (window[`runExample${egI+1}WA`]) { 511 | const [waSetUp, waElapsed] = window[`runExample${egI+1}WA`]() 512 | charts[chartI].data.datasets[5].data.push({x: times, y: waElapsed}) 513 | charts[chartI].data.datasets[6].data.push({x: times, y: waSetUp}) 514 | } 515 | 516 | charts[chartI].update() 517 | 518 | if (++i<=100) { 519 | setTimeout(() => plot(i, chartI, egI), 0) 520 | } else { 521 | if (chartI plot(25, chartI+1, egI+1), 0) 523 | } else { 524 | times = parseInt(counterInput.value) 525 | log = true 526 | } 527 | } 528 | } 529 | 530 | benchmarkExample1Button.addEventListener("click", () => { 531 | const [iterationsCanvas, identityChart] = makeChart() 532 | identityChartIterations.innerHTML = "" 533 | identityChartIterations.appendChild(iterationsCanvas) 534 | charts = [identityChart] 535 | plot(25, 0, 0) 536 | }) 537 | 538 | benchmarkExample2Button.addEventListener("click", () => { 539 | const [quadraticCanvas, quadraticChart] = makeChart() 540 | quadraticChartIterations.innerHTML = "" 541 | quadraticChartIterations.appendChild(quadraticCanvas) 542 | charts = [quadraticChart] 543 | plot(25, 0, 1) 544 | }) 545 | 546 | 547 | benchmarkExample3Button.addEventListener("click", () => { 548 | const [ewAdd3p1Canvas, ewAdd3p1Chart] = makeChart() 549 | ewAdd3p1ChartIterations.innerHTML = "" 550 | ewAdd3p1ChartIterations.appendChild(ewAdd3p1Canvas) 551 | charts = [ewAdd3p1Chart] 552 | plot(25, 0, 2) 553 | }) 554 | 555 | benchmarkExample4Button.addEventListener("click", () => { 556 | const [conv3x3_5x5Canvas, conv3x3_5x5Chart] = makeChart() 557 | conv3x3_5x5ChartIterations.innerHTML = "" 558 | conv3x3_5x5ChartIterations.appendChild(conv3x3_5x5Canvas) 559 | charts = [conv3x3_5x5Chart] 560 | plot(25, 0, 3) 561 | }) 562 | 563 | plotBenchmarkButton.addEventListener("click", () => { 564 | charts = [] 565 | 566 | const [iterationsCanvas, identityChart] = makeChart() 567 | identityChartIterations.innerHTML = "" 568 | identityChartIterations.appendChild(iterationsCanvas) 569 | 570 | const [quadraticCanvas, quadraticChart] = makeChart() 571 | quadraticChartIterations.innerHTML = "" 572 | quadraticChartIterations.appendChild(quadraticCanvas) 573 | 574 | const [ewAdd3p1Canvas, ewAdd3p1Chart] = makeChart() 575 | ewAdd3p1ChartIterations.innerHTML = "" 576 | ewAdd3p1ChartIterations.appendChild(ewAdd3p1Canvas) 577 | 578 | const [conv3x3_5x5Canvas, conv3x3_5x5Chart] = makeChart() 579 | conv3x3_5x5ChartIterations.innerHTML = "" 580 | conv3x3_5x5ChartIterations.appendChild(conv3x3_5x5Canvas) 581 | 582 | charts.push(identityChart) 583 | charts.push(quadraticChart) 584 | charts.push(ewAdd3p1Chart) 585 | charts.push(conv3x3_5x5Chart) 586 | 587 | plot(25, 0, 0) 588 | }) 589 | }) -------------------------------------------------------------------------------- /webassembly/webassemblyDemo.js: -------------------------------------------------------------------------------- 1 | var Module;if(!Module)Module=(typeof Module!=="undefined"?Module:null)||{};var moduleOverrides={};for(var key in Module){if(Module.hasOwnProperty(key)){moduleOverrides[key]=Module[key]}}var ENVIRONMENT_IS_WEB=false;var ENVIRONMENT_IS_WORKER=false;var ENVIRONMENT_IS_NODE=false;var ENVIRONMENT_IS_SHELL=false;if(Module["ENVIRONMENT"]){if(Module["ENVIRONMENT"]==="WEB"){ENVIRONMENT_IS_WEB=true}else if(Module["ENVIRONMENT"]==="WORKER"){ENVIRONMENT_IS_WORKER=true}else if(Module["ENVIRONMENT"]==="NODE"){ENVIRONMENT_IS_NODE=true}else if(Module["ENVIRONMENT"]==="SHELL"){ENVIRONMENT_IS_SHELL=true}else{throw new Error("The provided Module['ENVIRONMENT'] value is not valid. It must be one of: WEB|WORKER|NODE|SHELL.")}}else{ENVIRONMENT_IS_WEB=typeof window==="object";ENVIRONMENT_IS_WORKER=typeof importScripts==="function";ENVIRONMENT_IS_NODE=typeof process==="object"&&typeof require==="function"&&!ENVIRONMENT_IS_WEB&&!ENVIRONMENT_IS_WORKER;ENVIRONMENT_IS_SHELL=!ENVIRONMENT_IS_WEB&&!ENVIRONMENT_IS_NODE&&!ENVIRONMENT_IS_WORKER}if(ENVIRONMENT_IS_NODE){if(!Module["print"])Module["print"]=console.log;if(!Module["printErr"])Module["printErr"]=console.warn;var nodeFS;var nodePath;Module["read"]=function shell_read(filename,binary){if(!nodeFS)nodeFS=require("fs");if(!nodePath)nodePath=require("path");filename=nodePath["normalize"](filename);var ret=nodeFS["readFileSync"](filename);return binary?ret:ret.toString()};Module["readBinary"]=function readBinary(filename){var ret=Module["read"](filename,true);if(!ret.buffer){ret=new Uint8Array(ret)}assert(ret.buffer);return ret};Module["load"]=function load(f){globalEval(read(f))};if(!Module["thisProgram"]){if(process["argv"].length>1){Module["thisProgram"]=process["argv"][1].replace(/\\/g,"/")}else{Module["thisProgram"]="unknown-program"}}Module["arguments"]=process["argv"].slice(2);if(typeof module!=="undefined"){module["exports"]=Module}process["on"]("uncaughtException",(function(ex){if(!(ex instanceof ExitStatus)){throw ex}}));Module["inspect"]=(function(){return"[Emscripten Module object]"})}else if(ENVIRONMENT_IS_SHELL){if(!Module["print"])Module["print"]=print;if(typeof printErr!="undefined")Module["printErr"]=printErr;if(typeof read!="undefined"){Module["read"]=read}else{Module["read"]=function shell_read(){throw"no read() available"}}Module["readBinary"]=function readBinary(f){if(typeof readbuffer==="function"){return new Uint8Array(readbuffer(f))}var data=read(f,"binary");assert(typeof data==="object");return data};if(typeof scriptArgs!="undefined"){Module["arguments"]=scriptArgs}else if(typeof arguments!="undefined"){Module["arguments"]=arguments}if(typeof quit==="function"){Module["quit"]=(function(status,toThrow){quit(status)})}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){Module["read"]=function shell_read(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText};if(ENVIRONMENT_IS_WORKER){Module["readBinary"]=function readBinary(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}Module["readAsync"]=function readAsync(url,onload,onerror){var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=function xhr_onload(){if(xhr.status==200||xhr.status==0&&xhr.response){onload(xhr.response)}else{onerror()}};xhr.onerror=onerror;xhr.send(null)};if(typeof arguments!="undefined"){Module["arguments"]=arguments}if(typeof console!=="undefined"){if(!Module["print"])Module["print"]=function shell_print(x){console.log(x)};if(!Module["printErr"])Module["printErr"]=function shell_printErr(x){console.warn(x)}}else{var TRY_USE_DUMP=false;if(!Module["print"])Module["print"]=TRY_USE_DUMP&&typeof dump!=="undefined"?(function(x){dump(x)}):(function(x){})}if(ENVIRONMENT_IS_WORKER){Module["load"]=importScripts}if(typeof Module["setWindowTitle"]==="undefined"){Module["setWindowTitle"]=(function(title){document.title=title})}}else{throw"Unknown runtime environment. Where are we?"}function globalEval(x){eval.call(null,x)}if(!Module["load"]&&Module["read"]){Module["load"]=function load(f){globalEval(Module["read"](f))}}if(!Module["print"]){Module["print"]=(function(){})}if(!Module["printErr"]){Module["printErr"]=Module["print"]}if(!Module["arguments"]){Module["arguments"]=[]}if(!Module["thisProgram"]){Module["thisProgram"]="./this.program"}if(!Module["quit"]){Module["quit"]=(function(status,toThrow){throw toThrow})}Module.print=Module["print"];Module.printErr=Module["printErr"];Module["preRun"]=[];Module["postRun"]=[];for(var key in moduleOverrides){if(moduleOverrides.hasOwnProperty(key)){Module[key]=moduleOverrides[key]}}moduleOverrides=undefined;var Runtime={setTempRet0:(function(value){tempRet0=value;return value}),getTempRet0:(function(){return tempRet0}),stackSave:(function(){return STACKTOP}),stackRestore:(function(stackTop){STACKTOP=stackTop}),getNativeTypeSize:(function(type){switch(type){case"i1":case"i8":return 1;case"i16":return 2;case"i32":return 4;case"i64":return 8;case"float":return 4;case"double":return 8;default:{if(type[type.length-1]==="*"){return Runtime.QUANTUM_SIZE}else if(type[0]==="i"){var bits=parseInt(type.substr(1));assert(bits%8===0);return bits/8}else{return 0}}}}),getNativeFieldSize:(function(type){return Math.max(Runtime.getNativeTypeSize(type),Runtime.QUANTUM_SIZE)}),STACK_ALIGN:16,prepVararg:(function(ptr,type){if(type==="double"||type==="i64"){if(ptr&7){assert((ptr&7)===4);ptr+=4}}else{assert((ptr&3)===0)}return ptr}),getAlignSize:(function(type,size,vararg){if(!vararg&&(type=="i64"||type=="double"))return 8;if(!type)return Math.min(size,8);return Math.min(size||(type?Runtime.getNativeFieldSize(type):0),Runtime.QUANTUM_SIZE)}),dynCall:(function(sig,ptr,args){if(args&&args.length){return Module["dynCall_"+sig].apply(null,[ptr].concat(args))}else{return Module["dynCall_"+sig].call(null,ptr)}}),functionPointers:[],addFunction:(function(func){for(var i=0;i>2];var end=(ret+size+15|0)&-16;HEAP32[DYNAMICTOP_PTR>>2]=end;if(end>=TOTAL_MEMORY){var success=enlargeMemory();if(!success){HEAP32[DYNAMICTOP_PTR>>2]=ret;return 0}}return ret}),alignMemory:(function(size,quantum){var ret=size=Math.ceil(size/(quantum?quantum:16))*(quantum?quantum:16);return ret}),makeBigInt:(function(low,high,unsigned){var ret=unsigned?+(low>>>0)+ +(high>>>0)*4294967296:+(low>>>0)+ +(high|0)*4294967296;return ret}),GLOBAL_BASE:1024,QUANTUM_SIZE:4,__dummy__:0};Module["Runtime"]=Runtime;var ABORT=0;var EXITSTATUS=0;function assert(condition,text){if(!condition){abort("Assertion failed: "+text)}}function getCFunc(ident){var func=Module["_"+ident];if(!func){try{func=eval("_"+ident)}catch(e){}}assert(func,"Cannot call unknown function "+ident+" (perhaps LLVM optimizations or closure removed it?)");return func}var cwrap,ccall;((function(){var JSfuncs={"stackSave":(function(){Runtime.stackSave()}),"stackRestore":(function(){Runtime.stackRestore()}),"arrayToC":(function(arr){var ret=Runtime.stackAlloc(arr.length);writeArrayToMemory(arr,ret);return ret}),"stringToC":(function(str){var ret=0;if(str!==null&&str!==undefined&&str!==0){var len=(str.length<<2)+1;ret=Runtime.stackAlloc(len);stringToUTF8(str,ret,len)}return ret})};var toC={"string":JSfuncs["stringToC"],"array":JSfuncs["arrayToC"]};ccall=function ccallFunc(ident,returnType,argTypes,args,opts){var func=getCFunc(ident);var cArgs=[];var stack=0;if(args){for(var i=0;i>0]=value;break;case"i8":HEAP8[ptr>>0]=value;break;case"i16":HEAP16[ptr>>1]=value;break;case"i32":HEAP32[ptr>>2]=value;break;case"i64":tempI64=[value>>>0,(tempDouble=value,+Math_abs(tempDouble)>=1?tempDouble>0?(Math_min(+Math_floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math_ceil((tempDouble- +(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[ptr>>2]=tempI64[0],HEAP32[ptr+4>>2]=tempI64[1];break;case"float":HEAPF32[ptr>>2]=value;break;case"double":HEAPF64[ptr>>3]=value;break;default:abort("invalid type for setValue: "+type)}}Module["setValue"]=setValue;function getValue(ptr,type,noSafe){type=type||"i8";if(type.charAt(type.length-1)==="*")type="i32";switch(type){case"i1":return HEAP8[ptr>>0];case"i8":return HEAP8[ptr>>0];case"i16":return HEAP16[ptr>>1];case"i32":return HEAP32[ptr>>2];case"i64":return HEAP32[ptr>>2];case"float":return HEAPF32[ptr>>2];case"double":return HEAPF64[ptr>>3];default:abort("invalid type for setValue: "+type)}return null}Module["getValue"]=getValue;var ALLOC_NORMAL=0;var ALLOC_STACK=1;var ALLOC_STATIC=2;var ALLOC_DYNAMIC=3;var ALLOC_NONE=4;Module["ALLOC_NORMAL"]=ALLOC_NORMAL;Module["ALLOC_STACK"]=ALLOC_STACK;Module["ALLOC_STATIC"]=ALLOC_STATIC;Module["ALLOC_DYNAMIC"]=ALLOC_DYNAMIC;Module["ALLOC_NONE"]=ALLOC_NONE;function allocate(slab,types,allocator,ptr){var zeroinit,size;if(typeof slab==="number"){zeroinit=true;size=slab}else{zeroinit=false;size=slab.length}var singleType=typeof types==="string"?types:null;var ret;if(allocator==ALLOC_NONE){ret=ptr}else{ret=[typeof _malloc==="function"?_malloc:Runtime.staticAlloc,Runtime.stackAlloc,Runtime.staticAlloc,Runtime.dynamicAlloc][allocator===undefined?ALLOC_STATIC:allocator](Math.max(size,singleType?1:types.length))}if(zeroinit){var ptr=ret,stop;assert((ret&3)==0);stop=ret+(size&~3);for(;ptr>2]=0}stop=ret+size;while(ptr>0]=0}return ret}if(singleType==="i8"){if(slab.subarray||slab.slice){HEAPU8.set(slab,ret)}else{HEAPU8.set(new Uint8Array(slab),ret)}return ret}var i=0,type,typeSize,previousType;while(i>0];hasUtf|=t;if(t==0&&!length)break;i++;if(length&&i==length)break}if(!length)length=i;var ret="";if(hasUtf<128){var MAX_CHUNK=1024;var curr;while(length>0){curr=String.fromCharCode.apply(String,HEAPU8.subarray(ptr,ptr+Math.min(length,MAX_CHUNK)));ret=ret?ret+curr:curr;ptr+=MAX_CHUNK;length-=MAX_CHUNK}return ret}return Module["UTF8ToString"](ptr)}Module["Pointer_stringify"]=Pointer_stringify;function AsciiToString(ptr){var str="";while(1){var ch=HEAP8[ptr++>>0];if(!ch)return str;str+=String.fromCharCode(ch)}}Module["AsciiToString"]=AsciiToString;function stringToAscii(str,outPtr){return writeAsciiToMemory(str,outPtr,false)}Module["stringToAscii"]=stringToAscii;var UTF8Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf8"):undefined;function UTF8ArrayToString(u8Array,idx){var endPtr=idx;while(u8Array[endPtr])++endPtr;if(endPtr-idx>16&&u8Array.subarray&&UTF8Decoder){return UTF8Decoder.decode(u8Array.subarray(idx,endPtr))}else{var u0,u1,u2,u3,u4,u5;var str="";while(1){u0=u8Array[idx++];if(!u0)return str;if(!(u0&128)){str+=String.fromCharCode(u0);continue}u1=u8Array[idx++]&63;if((u0&224)==192){str+=String.fromCharCode((u0&31)<<6|u1);continue}u2=u8Array[idx++]&63;if((u0&240)==224){u0=(u0&15)<<12|u1<<6|u2}else{u3=u8Array[idx++]&63;if((u0&248)==240){u0=(u0&7)<<18|u1<<12|u2<<6|u3}else{u4=u8Array[idx++]&63;if((u0&252)==248){u0=(u0&3)<<24|u1<<18|u2<<12|u3<<6|u4}else{u5=u8Array[idx++]&63;u0=(u0&1)<<30|u1<<24|u2<<18|u3<<12|u4<<6|u5}}}if(u0<65536){str+=String.fromCharCode(u0)}else{var ch=u0-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}}}}Module["UTF8ArrayToString"]=UTF8ArrayToString;function UTF8ToString(ptr){return UTF8ArrayToString(HEAPU8,ptr)}Module["UTF8ToString"]=UTF8ToString;function stringToUTF8Array(str,outU8Array,outIdx,maxBytesToWrite){if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i=55296&&u<=57343)u=65536+((u&1023)<<10)|str.charCodeAt(++i)&1023;if(u<=127){if(outIdx>=endIdx)break;outU8Array[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;outU8Array[outIdx++]=192|u>>6;outU8Array[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;outU8Array[outIdx++]=224|u>>12;outU8Array[outIdx++]=128|u>>6&63;outU8Array[outIdx++]=128|u&63}else if(u<=2097151){if(outIdx+3>=endIdx)break;outU8Array[outIdx++]=240|u>>18;outU8Array[outIdx++]=128|u>>12&63;outU8Array[outIdx++]=128|u>>6&63;outU8Array[outIdx++]=128|u&63}else if(u<=67108863){if(outIdx+4>=endIdx)break;outU8Array[outIdx++]=248|u>>24;outU8Array[outIdx++]=128|u>>18&63;outU8Array[outIdx++]=128|u>>12&63;outU8Array[outIdx++]=128|u>>6&63;outU8Array[outIdx++]=128|u&63}else{if(outIdx+5>=endIdx)break;outU8Array[outIdx++]=252|u>>30;outU8Array[outIdx++]=128|u>>24&63;outU8Array[outIdx++]=128|u>>18&63;outU8Array[outIdx++]=128|u>>12&63;outU8Array[outIdx++]=128|u>>6&63;outU8Array[outIdx++]=128|u&63}}outU8Array[outIdx]=0;return outIdx-startIdx}Module["stringToUTF8Array"]=stringToUTF8Array;function stringToUTF8(str,outPtr,maxBytesToWrite){return stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite)}Module["stringToUTF8"]=stringToUTF8;function lengthBytesUTF8(str){var len=0;for(var i=0;i=55296&&u<=57343)u=65536+((u&1023)<<10)|str.charCodeAt(++i)&1023;if(u<=127){++len}else if(u<=2047){len+=2}else if(u<=65535){len+=3}else if(u<=2097151){len+=4}else if(u<=67108863){len+=5}else{len+=6}}return len}Module["lengthBytesUTF8"]=lengthBytesUTF8;var UTF16Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf-16le"):undefined;function demangle(func){var __cxa_demangle_func=Module["___cxa_demangle"]||Module["__cxa_demangle"];if(__cxa_demangle_func){try{var s=func.substr(1);var len=lengthBytesUTF8(s)+1;var buf=_malloc(len);stringToUTF8(s,buf,len);var status=_malloc(4);var ret=__cxa_demangle_func(buf,0,0,status);if(getValue(status,"i32")===0&&ret){return Pointer_stringify(ret)}}catch(e){}finally{if(buf)_free(buf);if(status)_free(status);if(ret)_free(ret)}return func}Runtime.warnOnce("warning: build with -s DEMANGLE_SUPPORT=1 to link in libcxxabi demangling");return func}function demangleAll(text){var regex=/__Z[\w\d_]+/g;return text.replace(regex,(function(x){var y=demangle(x);return x===y?x:x+" ["+y+"]"}))}function jsStackTrace(){var err=new Error;if(!err.stack){try{throw new Error(0)}catch(e){err=e}if(!err.stack){return"(no stack trace available)"}}return err.stack.toString()}function stackTrace(){var js=jsStackTrace();if(Module["extraStackTrace"])js+="\n"+Module["extraStackTrace"]();return demangleAll(js)}Module["stackTrace"]=stackTrace;var WASM_PAGE_SIZE=65536;var ASMJS_PAGE_SIZE=16777216;var MIN_TOTAL_MEMORY=16777216;function alignUp(x,multiple){if(x%multiple>0){x+=multiple-x%multiple}return x}var HEAP,buffer,HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateGlobalBuffer(buf){Module["buffer"]=buffer=buf}function updateGlobalBufferViews(){Module["HEAP8"]=HEAP8=new Int8Array(buffer);Module["HEAP16"]=HEAP16=new Int16Array(buffer);Module["HEAP32"]=HEAP32=new Int32Array(buffer);Module["HEAPU8"]=HEAPU8=new Uint8Array(buffer);Module["HEAPU16"]=HEAPU16=new Uint16Array(buffer);Module["HEAPU32"]=HEAPU32=new Uint32Array(buffer);Module["HEAPF32"]=HEAPF32=new Float32Array(buffer);Module["HEAPF64"]=HEAPF64=new Float64Array(buffer)}var STATIC_BASE,STATICTOP,staticSealed;var STACK_BASE,STACKTOP,STACK_MAX;var DYNAMIC_BASE,DYNAMICTOP_PTR;STATIC_BASE=STATICTOP=STACK_BASE=STACKTOP=STACK_MAX=DYNAMIC_BASE=DYNAMICTOP_PTR=0;staticSealed=false;function abortOnCannotGrowMemory(){abort("Cannot enlarge memory arrays. Either (1) compile with -s TOTAL_MEMORY=X with X higher than the current value "+TOTAL_MEMORY+", (2) compile with -s ALLOW_MEMORY_GROWTH=1 which allows increasing the size at runtime, or (3) if you want malloc to return NULL (0) instead of this abort, compile with -s ABORTING_MALLOC=0 ")}if(!Module["reallocBuffer"])Module["reallocBuffer"]=(function(size){var ret;try{if(ArrayBuffer.transfer){ret=ArrayBuffer.transfer(buffer,size)}else{var oldHEAP8=HEAP8;ret=new ArrayBuffer(size);var temp=new Int8Array(ret);temp.set(oldHEAP8)}}catch(e){return false}var success=_emscripten_replace_memory(ret);if(!success)return false;return ret});function enlargeMemory(){var PAGE_MULTIPLE=Module["usingWasm"]?WASM_PAGE_SIZE:ASMJS_PAGE_SIZE;var LIMIT=2147483648-PAGE_MULTIPLE;if(HEAP32[DYNAMICTOP_PTR>>2]>LIMIT){return false}var OLD_TOTAL_MEMORY=TOTAL_MEMORY;TOTAL_MEMORY=Math.max(TOTAL_MEMORY,MIN_TOTAL_MEMORY);while(TOTAL_MEMORY>2]){if(TOTAL_MEMORY<=536870912){TOTAL_MEMORY=alignUp(2*TOTAL_MEMORY,PAGE_MULTIPLE)}else{TOTAL_MEMORY=Math.min(alignUp((3*TOTAL_MEMORY+2147483648)/4,PAGE_MULTIPLE),LIMIT)}}var replacement=Module["reallocBuffer"](TOTAL_MEMORY);if(!replacement||replacement.byteLength!=TOTAL_MEMORY){TOTAL_MEMORY=OLD_TOTAL_MEMORY;return false}updateGlobalBuffer(replacement);updateGlobalBufferViews();return true}var byteLength;try{byteLength=Function.prototype.call.bind(Object.getOwnPropertyDescriptor(ArrayBuffer.prototype,"byteLength").get);byteLength(new ArrayBuffer(4))}catch(e){byteLength=(function(buffer){return buffer.byteLength})}var TOTAL_STACK=Module["TOTAL_STACK"]||5242880;var TOTAL_MEMORY=Module["TOTAL_MEMORY"]||16777216;if(TOTAL_MEMORY0){var callback=callbacks.shift();if(typeof callback=="function"){callback();continue}var func=callback.func;if(typeof func==="number"){if(callback.arg===undefined){Module["dynCall_v"](func)}else{Module["dynCall_vi"](func,callback.arg)}}else{func(callback.arg===undefined?null:callback.arg)}}}var __ATPRERUN__=[];var __ATINIT__=[];var __ATMAIN__=[];var __ATEXIT__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;var runtimeExited=false;function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function ensureInitRuntime(){if(runtimeInitialized)return;runtimeInitialized=true;callRuntimeCallbacks(__ATINIT__)}function preMain(){callRuntimeCallbacks(__ATMAIN__)}function exitRuntime(){callRuntimeCallbacks(__ATEXIT__);runtimeExited=true}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}Module["addOnPreRun"]=addOnPreRun;function addOnInit(cb){__ATINIT__.unshift(cb)}Module["addOnInit"]=addOnInit;function addOnPreMain(cb){__ATMAIN__.unshift(cb)}Module["addOnPreMain"]=addOnPreMain;function addOnExit(cb){__ATEXIT__.unshift(cb)}Module["addOnExit"]=addOnExit;function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}Module["addOnPostRun"]=addOnPostRun;function intArrayFromString(stringy,dontAddNull,length){var len=length>0?length:lengthBytesUTF8(stringy)+1;var u8array=new Array(len);var numBytesWritten=stringToUTF8Array(stringy,u8array,0,u8array.length);if(dontAddNull)u8array.length=numBytesWritten;return u8array}Module["intArrayFromString"]=intArrayFromString;function intArrayToString(array){var ret=[];for(var i=0;i255){chr&=255}ret.push(String.fromCharCode(chr))}return ret.join("")}Module["intArrayToString"]=intArrayToString;function writeStringToMemory(string,buffer,dontAddNull){Runtime.warnOnce("writeStringToMemory is deprecated and should not be called! Use stringToUTF8() instead!");var lastChar,end;if(dontAddNull){end=buffer+lengthBytesUTF8(string);lastChar=HEAP8[end]}stringToUTF8(string,buffer,Infinity);if(dontAddNull)HEAP8[end]=lastChar}Module["writeStringToMemory"]=writeStringToMemory;function writeArrayToMemory(array,buffer){HEAP8.set(array,buffer)}Module["writeArrayToMemory"]=writeArrayToMemory;function writeAsciiToMemory(str,buffer,dontAddNull){for(var i=0;i>0]=str.charCodeAt(i)}if(!dontAddNull)HEAP8[buffer>>0]=0}Module["writeAsciiToMemory"]=writeAsciiToMemory;if(!Math["imul"]||Math["imul"](4294967295,5)!==-5)Math["imul"]=function imul(a,b){var ah=a>>>16;var al=a&65535;var bh=b>>>16;var bl=b&65535;return al*bl+(ah*bl+al*bh<<16)|0};Math.imul=Math["imul"];if(!Math["fround"]){var froundBuffer=new Float32Array(1);Math["fround"]=(function(x){froundBuffer[0]=x;return froundBuffer[0]})}Math.fround=Math["fround"];if(!Math["clz32"])Math["clz32"]=(function(x){x=x>>>0;for(var i=0;i<32;i++){if(x&1<<31-i)return i}return 32});Math.clz32=Math["clz32"];if(!Math["trunc"])Math["trunc"]=(function(x){return x<0?Math.ceil(x):Math.floor(x)});Math.trunc=Math["trunc"];var Math_abs=Math.abs;var Math_cos=Math.cos;var Math_sin=Math.sin;var Math_tan=Math.tan;var Math_acos=Math.acos;var Math_asin=Math.asin;var Math_atan=Math.atan;var Math_atan2=Math.atan2;var Math_exp=Math.exp;var Math_log=Math.log;var Math_sqrt=Math.sqrt;var Math_ceil=Math.ceil;var Math_floor=Math.floor;var Math_pow=Math.pow;var Math_imul=Math.imul;var Math_fround=Math.fround;var Math_round=Math.round;var Math_min=Math.min;var Math_clz32=Math.clz32;var Math_trunc=Math.trunc;var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function addRunDependency(id){runDependencies++;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}}Module["addRunDependency"]=addRunDependency;function removeRunDependency(id){runDependencies--;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}Module["removeRunDependency"]=removeRunDependency;Module["preloadedImages"]={};Module["preloadedAudios"]={};var memoryInitializer=null;function integrateWasmJS(Module){var method=Module["wasmJSMethod"]||"native-wasm";Module["wasmJSMethod"]=method;var wasmTextFile=Module["wasmTextFile"]||"webassemblyDemo.wast";var wasmBinaryFile=Module["wasmBinaryFile"]||"webassemblyDemo.wasm";var asmjsCodeFile=Module["asmjsCodeFile"]||"webassemblyDemo.temp.asm.js";if(typeof Module["locateFile"]==="function"){wasmTextFile=Module["locateFile"](wasmTextFile);wasmBinaryFile=Module["locateFile"](wasmBinaryFile);asmjsCodeFile=Module["locateFile"](asmjsCodeFile)}var wasmPageSize=64*1024;var asm2wasmImports={"f64-rem":(function(x,y){return x%y}),"f64-to-int":(function(x){return x|0}),"i32s-div":(function(x,y){return(x|0)/(y|0)|0}),"i32u-div":(function(x,y){return(x>>>0)/(y>>>0)>>>0}),"i32s-rem":(function(x,y){return(x|0)%(y|0)|0}),"i32u-rem":(function(x,y){return(x>>>0)%(y>>>0)>>>0}),"debugger":(function(){debugger})};var info={"global":null,"env":null,"asm2wasm":asm2wasmImports,"parent":Module};var exports=null;function lookupImport(mod,base){var lookup=info;if(mod.indexOf(".")<0){lookup=(lookup||{})[mod]}else{var parts=mod.split(".");lookup=(lookup||{})[parts[0]];lookup=(lookup||{})[parts[1]]}if(base){lookup=(lookup||{})[base]}if(lookup===undefined){abort("bad lookupImport to ("+mod+")."+base)}return lookup}function mergeMemory(newBuffer){var oldBuffer=Module["buffer"];if(newBuffer.byteLength=0||Module["wasmJSMethod"].indexOf("interpret-asm2wasm")>=0?"webassemblyDemo.js.mem":null;var STATIC_BUMP=10976;Module["STATIC_BASE"]=STATIC_BASE;Module["STATIC_BUMP"]=STATIC_BUMP;var tempDoublePtr=STATICTOP;STATICTOP+=16;var GL={counter:1,lastError:0,buffers:[],mappedBuffers:{},programs:[],framebuffers:[],renderbuffers:[],textures:[],uniforms:[],shaders:[],vaos:[],contexts:[],currentContext:null,offscreenCanvases:{},timerQueriesEXT:[],byteSizeByTypeRoot:5120,byteSizeByType:[1,1,2,2,4,4,4,2,3,4,8],programInfos:{},stringCache:{},tempFixedLengthArray:[],packAlignment:4,unpackAlignment:4,init:(function(){GL.miniTempBuffer=new Float32Array(GL.MINI_TEMP_BUFFER_SIZE);for(var i=0;i>2];if(len<0){frag=Pointer_stringify(HEAP32[string+i*4>>2])}else{frag=Pointer_stringify(HEAP32[string+i*4>>2],len)}}else{frag=Pointer_stringify(HEAP32[string+i*4>>2])}source+=frag}return source}),createContext:(function(canvas,webGLContextAttributes){if(typeof webGLContextAttributes["majorVersion"]==="undefined"&&typeof webGLContextAttributes["minorVersion"]==="undefined"){webGLContextAttributes["majorVersion"]=1;webGLContextAttributes["minorVersion"]=0}var ctx;var errorInfo="?";function onContextCreationError(event){errorInfo=event.statusMessage||errorInfo}try{canvas.addEventListener("webglcontextcreationerror",onContextCreationError,false);try{if(webGLContextAttributes["majorVersion"]==1&&webGLContextAttributes["minorVersion"]==0){ctx=canvas.getContext("webgl",webGLContextAttributes)||canvas.getContext("experimental-webgl",webGLContextAttributes)}else if(webGLContextAttributes["majorVersion"]==2&&webGLContextAttributes["minorVersion"]==0){ctx=canvas.getContext("webgl2",webGLContextAttributes)}else{throw"Unsupported WebGL context version "+majorVersion+"."+minorVersion+"!"}}finally{canvas.removeEventListener("webglcontextcreationerror",onContextCreationError,false)}if(!ctx)throw":("}catch(e){Module.print("Could not create canvas: "+[errorInfo,e,JSON.stringify(webGLContextAttributes)]);return 0}if(!ctx)return 0;return GL.registerContext(ctx,webGLContextAttributes)}),registerContext:(function(ctx,webGLContextAttributes){var handle=GL.getNewId(GL.contexts);var context={handle:handle,attributes:webGLContextAttributes,version:webGLContextAttributes["majorVersion"],GLctx:ctx};if(ctx.canvas)ctx.canvas.GLctxObject=context;GL.contexts[handle]=context;if(typeof webGLContextAttributes["enableExtensionsByDefault"]==="undefined"||webGLContextAttributes["enableExtensionsByDefault"]){GL.initExtensions(context)}return handle}),makeContextCurrent:(function(contextHandle){var context=GL.contexts[contextHandle];if(!context)return false;GLctx=Module.ctx=context.GLctx;GL.currentContext=context;return true}),getContext:(function(contextHandle){return GL.contexts[contextHandle]}),deleteContext:(function(contextHandle){if(GL.currentContext===GL.contexts[contextHandle])GL.currentContext=null;if(typeof JSEvents==="object")JSEvents.removeAllHandlersOnTarget(GL.contexts[contextHandle].GLctx.canvas);if(GL.contexts[contextHandle]&&GL.contexts[contextHandle].GLctx.canvas)GL.contexts[contextHandle].GLctx.canvas.GLctxObject=undefined;GL.contexts[contextHandle]=null}),initExtensions:(function(context){if(!context)context=GL.currentContext;if(context.initExtensionsDone)return;context.initExtensionsDone=true;var GLctx=context.GLctx;context.maxVertexAttribs=GLctx.getParameter(GLctx.MAX_VERTEX_ATTRIBS);if(context.version<2){var instancedArraysExt=GLctx.getExtension("ANGLE_instanced_arrays");if(instancedArraysExt){GLctx["vertexAttribDivisor"]=(function(index,divisor){instancedArraysExt["vertexAttribDivisorANGLE"](index,divisor)});GLctx["drawArraysInstanced"]=(function(mode,first,count,primcount){instancedArraysExt["drawArraysInstancedANGLE"](mode,first,count,primcount)});GLctx["drawElementsInstanced"]=(function(mode,count,type,indices,primcount){instancedArraysExt["drawElementsInstancedANGLE"](mode,count,type,indices,primcount)})}var vaoExt=GLctx.getExtension("OES_vertex_array_object");if(vaoExt){GLctx["createVertexArray"]=(function(){return vaoExt["createVertexArrayOES"]()});GLctx["deleteVertexArray"]=(function(vao){vaoExt["deleteVertexArrayOES"](vao)});GLctx["bindVertexArray"]=(function(vao){vaoExt["bindVertexArrayOES"](vao)});GLctx["isVertexArray"]=(function(vao){return vaoExt["isVertexArrayOES"](vao)})}var drawBuffersExt=GLctx.getExtension("WEBGL_draw_buffers");if(drawBuffersExt){GLctx["drawBuffers"]=(function(n,bufs){drawBuffersExt["drawBuffersWEBGL"](n,bufs)})}}GLctx.disjointTimerQueryExt=GLctx.getExtension("EXT_disjoint_timer_query");var automaticallyEnabledExtensions=["OES_texture_float","OES_texture_half_float","OES_standard_derivatives","OES_vertex_array_object","WEBGL_compressed_texture_s3tc","WEBGL_depth_texture","OES_element_index_uint","EXT_texture_filter_anisotropic","ANGLE_instanced_arrays","OES_texture_float_linear","OES_texture_half_float_linear","WEBGL_compressed_texture_atc","WEBGL_compressed_texture_pvrtc","EXT_color_buffer_half_float","WEBGL_color_buffer_float","EXT_frag_depth","EXT_sRGB","WEBGL_draw_buffers","WEBGL_shared_resources","EXT_shader_texture_lod","EXT_color_buffer_float"];var exts=GLctx.getSupportedExtensions();if(exts&&exts.length>0){GLctx.getSupportedExtensions().forEach((function(ext){if(automaticallyEnabledExtensions.indexOf(ext)!=-1){GLctx.getExtension(ext)}}))}}),populateUniformTable:(function(program){var p=GL.programs[program];GL.programInfos[program]={uniforms:{},maxUniformLength:0,maxAttributeLength:-1,maxUniformBlockNameLength:-1};var ptable=GL.programInfos[program];var utable=ptable.uniforms;var numUniforms=GLctx.getProgramParameter(p,GLctx.ACTIVE_UNIFORMS);for(var i=0;i>2,pixels+bytes>>2);case 5125:case 34042:return HEAPU32.subarray(pixels>>2,pixels+bytes>>2);case 5123:case 33635:case 32819:case 32820:case 36193:return HEAPU16.subarray(pixels>>1,pixels+bytes>>1);default:GL.recordError(1280);return null}}function _glTexImage2D(target,level,internalFormat,width,height,border,format,type,pixels){var pixelData=null;if(pixels)pixelData=emscriptenWebGLGetTexPixelData(type,format,width,height,pixels,internalFormat);GLctx.texImage2D(target,level,internalFormat,width,height,border,format,type,pixelData)}function __ZSt18uncaught_exceptionv(){return!!__ZSt18uncaught_exceptionv.uncaught_exception}var EXCEPTIONS={last:0,caught:[],infos:{},deAdjust:(function(adjusted){if(!adjusted||EXCEPTIONS.infos[adjusted])return adjusted;for(var ptr in EXCEPTIONS.infos){var info=EXCEPTIONS.infos[ptr];if(info.adjusted===adjusted){return ptr}}return adjusted}),addRef:(function(ptr){if(!ptr)return;var info=EXCEPTIONS.infos[ptr];info.refcount++}),decRef:(function(ptr){if(!ptr)return;var info=EXCEPTIONS.infos[ptr];assert(info.refcount>0);info.refcount--;if(info.refcount===0&&!info.rethrown){if(info.destructor){Module["dynCall_vi"](info.destructor,ptr)}delete EXCEPTIONS.infos[ptr];___cxa_free_exception(ptr)}}),clearRef:(function(ptr){if(!ptr)return;var info=EXCEPTIONS.infos[ptr];info.refcount=0})};function ___resumeException(ptr){if(!EXCEPTIONS.last){EXCEPTIONS.last=ptr}throw ptr}function ___cxa_find_matching_catch(){var thrown=EXCEPTIONS.last;if(!thrown){return(Runtime.setTempRet0(0),0)|0}var info=EXCEPTIONS.infos[thrown];var throwntype=info.type;if(!throwntype){return(Runtime.setTempRet0(0),thrown)|0}var typeArray=Array.prototype.slice.call(arguments);var pointer=Module["___cxa_is_pointer_type"](throwntype);if(!___cxa_find_matching_catch.buffer)___cxa_find_matching_catch.buffer=_malloc(4);HEAP32[___cxa_find_matching_catch.buffer>>2]=thrown;thrown=___cxa_find_matching_catch.buffer;for(var i=0;i>2];info.adjusted=thrown;return(Runtime.setTempRet0(typeArray[i]),thrown)|0}}thrown=HEAP32[thrown>>2];return(Runtime.setTempRet0(throwntype),thrown)|0}function ___cxa_throw(ptr,type,destructor){EXCEPTIONS.infos[ptr]={ptr:ptr,adjusted:ptr,type:type,destructor:destructor,refcount:0,caught:false,rethrown:false};EXCEPTIONS.last=ptr;if(!("uncaught_exception"in __ZSt18uncaught_exceptionv)){__ZSt18uncaught_exceptionv.uncaught_exception=1}else{__ZSt18uncaught_exceptionv.uncaught_exception++}throw ptr}function _glUseProgram(program){GLctx.useProgram(program?GL.programs[program]:null)}function _glVertexAttribPointer(index,size,type,normalized,stride,ptr){GLctx.vertexAttribPointer(index,size,type,!!normalized,stride,ptr)}function _glGetShaderInfoLog(shader,maxLength,length,infoLog){var log=GLctx.getShaderInfoLog(GL.shaders[shader]);if(log===null)log="(unknown error)";if(maxLength>0&&infoLog){var numBytesWrittenExclNull=stringToUTF8(log,infoLog,maxLength);if(length)HEAP32[length>>2]=numBytesWrittenExclNull}else{if(length)HEAP32[length>>2]=0}}function _glBindTexture(target,texture){GLctx.bindTexture(target,texture?GL.textures[texture]:null)}function _abort(){Module["abort"]()}function _glLinkProgram(program){GLctx.linkProgram(GL.programs[program]);GL.programInfos[program]=null;GL.populateUniformTable(program)}function _glShaderSource(shader,count,string,length){var source=GL.getSource(shader,count,string,length);GLctx.shaderSource(GL.shaders[shader],source)}function _glGetUniformLocation(program,name){name=Pointer_stringify(name);var arrayOffset=0;if(name.indexOf("]",name.length-1)!==-1){var ls=name.lastIndexOf("[");var arrayIndex=name.slice(ls+1,-1);if(arrayIndex.length>0){arrayOffset=parseInt(arrayIndex);if(arrayOffset<0){return-1}}name=name.slice(0,ls)}var ptable=GL.programInfos[program];if(!ptable){return-1}var utable=ptable.uniforms;var uniformInfo=utable[name];if(uniformInfo&&arrayOffset>2]=PTHREAD_SPECIFIC_NEXT_KEY;PTHREAD_SPECIFIC[PTHREAD_SPECIFIC_NEXT_KEY]=0;PTHREAD_SPECIFIC_NEXT_KEY++;return 0}function _glBufferData(target,size,data,usage){if(!data){GLctx.bufferData(target,size,usage)}else{GLctx.bufferData(target,HEAPU8.subarray(data,data+size),usage)}}function _glGenBuffers(n,buffers){for(var i=0;i>2]=0;return}var id=GL.getNewId(GL.buffers);buffer.name=id;GL.buffers[id]=buffer;HEAP32[buffers+i*4>>2]=id}}function _glActiveTexture(x0){GLctx["activeTexture"](x0)}function _glEnableVertexAttribArray(index){GLctx.enableVertexAttribArray(index)}function _glGetAttribLocation(program,name){program=GL.programs[program];name=Pointer_stringify(name);return GLctx.getAttribLocation(program,name)}function _glBindBuffer(target,buffer){var bufferObj=buffer?GL.buffers[buffer]:null;GLctx.bindBuffer(target,bufferObj)}var JSEvents={keyEvent:0,mouseEvent:0,wheelEvent:0,uiEvent:0,focusEvent:0,deviceOrientationEvent:0,deviceMotionEvent:0,fullscreenChangeEvent:0,pointerlockChangeEvent:0,visibilityChangeEvent:0,touchEvent:0,lastGamepadState:null,lastGamepadStateFrame:null,numGamepadsConnected:0,previousFullscreenElement:null,previousScreenX:null,previousScreenY:null,removeEventListenersRegistered:false,staticInit:(function(){if(typeof window!=="undefined"){window.addEventListener("gamepadconnected",(function(){++JSEvents.numGamepadsConnected}));window.addEventListener("gamepaddisconnected",(function(){--JSEvents.numGamepadsConnected}));var firstState=navigator.getGamepads?navigator.getGamepads():navigator.webkitGetGamepads?navigator.webkitGetGamepads():null;if(firstState){JSEvents.numGamepadsConnected=firstState.length}}}),registerRemoveEventListeners:(function(){if(!JSEvents.removeEventListenersRegistered){__ATEXIT__.push((function(){for(var i=JSEvents.eventHandlers.length-1;i>=0;--i){JSEvents._removeHandler(i)}}));JSEvents.removeEventListenersRegistered=true}}),findEventTarget:(function(target){if(target){if(typeof target=="number"){target=Pointer_stringify(target)}if(target=="#window")return window;else if(target=="#document")return document;else if(target=="#screen")return window.screen;else if(target=="#canvas")return Module["canvas"];if(typeof target=="string")return document.getElementById(target);else return target}else{return window}}),deferredCalls:[],deferCall:(function(targetFunction,precedence,argsList){function arraysHaveEqualContent(arrA,arrB){if(arrA.length!=arrB.length)return false;for(var i in arrA){if(arrA[i]!=arrB[i])return false}return true}for(var i in JSEvents.deferredCalls){var call=JSEvents.deferredCalls[i];if(call.targetFunction==targetFunction&&arraysHaveEqualContent(call.argsList,argsList)){return}}JSEvents.deferredCalls.push({targetFunction:targetFunction,precedence:precedence,argsList:argsList});JSEvents.deferredCalls.sort((function(x,y){return x.precedence0}),removeAllHandlersOnTarget:(function(target,eventTypeString){for(var i=0;i>2]=e.location;HEAP32[JSEvents.keyEvent+68>>2]=e.ctrlKey;HEAP32[JSEvents.keyEvent+72>>2]=e.shiftKey;HEAP32[JSEvents.keyEvent+76>>2]=e.altKey;HEAP32[JSEvents.keyEvent+80>>2]=e.metaKey;HEAP32[JSEvents.keyEvent+84>>2]=e.repeat;stringToUTF8(e.locale?e.locale:"",JSEvents.keyEvent+88,32);stringToUTF8(e.char?e.char:"",JSEvents.keyEvent+120,32);HEAP32[JSEvents.keyEvent+152>>2]=e.charCode;HEAP32[JSEvents.keyEvent+156>>2]=e.keyCode;HEAP32[JSEvents.keyEvent+160>>2]=e.which;var shouldCancel=Module["dynCall_iiii"](callbackfunc,eventTypeId,JSEvents.keyEvent,userData);if(shouldCancel){e.preventDefault()}});var eventHandler={target:JSEvents.findEventTarget(target),allowsDeferredCalls:JSEvents.isInternetExplorer()?false:true,eventTypeString:eventTypeString,callbackfunc:callbackfunc,handlerFunc:handlerFunc,useCapture:useCapture};JSEvents.registerOrRemoveHandler(eventHandler)}),getBoundingClientRectOrZeros:(function(target){return target.getBoundingClientRect?target.getBoundingClientRect():{left:0,top:0}}),fillMouseEventData:(function(eventStruct,e,target){HEAPF64[eventStruct>>3]=JSEvents.tick();HEAP32[eventStruct+8>>2]=e.screenX;HEAP32[eventStruct+12>>2]=e.screenY;HEAP32[eventStruct+16>>2]=e.clientX;HEAP32[eventStruct+20>>2]=e.clientY;HEAP32[eventStruct+24>>2]=e.ctrlKey;HEAP32[eventStruct+28>>2]=e.shiftKey;HEAP32[eventStruct+32>>2]=e.altKey;HEAP32[eventStruct+36>>2]=e.metaKey;HEAP16[eventStruct+40>>1]=e.button;HEAP16[eventStruct+42>>1]=e.buttons;HEAP32[eventStruct+44>>2]=e["movementX"]||e["mozMovementX"]||e["webkitMovementX"]||e.screenX-JSEvents.previousScreenX;HEAP32[eventStruct+48>>2]=e["movementY"]||e["mozMovementY"]||e["webkitMovementY"]||e.screenY-JSEvents.previousScreenY;if(Module["canvas"]){var rect=Module["canvas"].getBoundingClientRect();HEAP32[eventStruct+60>>2]=e.clientX-rect.left;HEAP32[eventStruct+64>>2]=e.clientY-rect.top}else{HEAP32[eventStruct+60>>2]=0;HEAP32[eventStruct+64>>2]=0}if(target){var rect=JSEvents.getBoundingClientRectOrZeros(target);HEAP32[eventStruct+52>>2]=e.clientX-rect.left;HEAP32[eventStruct+56>>2]=e.clientY-rect.top}else{HEAP32[eventStruct+52>>2]=0;HEAP32[eventStruct+56>>2]=0}if(e.type!=="wheel"&&e.type!=="mousewheel"){JSEvents.previousScreenX=e.screenX;JSEvents.previousScreenY=e.screenY}}),registerMouseEventCallback:(function(target,userData,useCapture,callbackfunc,eventTypeId,eventTypeString){if(!JSEvents.mouseEvent){JSEvents.mouseEvent=_malloc(72)}target=JSEvents.findEventTarget(target);var handlerFunc=(function(event){var e=event||window.event;JSEvents.fillMouseEventData(JSEvents.mouseEvent,e,target);var shouldCancel=Module["dynCall_iiii"](callbackfunc,eventTypeId,JSEvents.mouseEvent,userData);if(shouldCancel){e.preventDefault()}});var eventHandler={target:target,allowsDeferredCalls:eventTypeString!="mousemove"&&eventTypeString!="mouseenter"&&eventTypeString!="mouseleave",eventTypeString:eventTypeString,callbackfunc:callbackfunc,handlerFunc:handlerFunc,useCapture:useCapture};if(JSEvents.isInternetExplorer()&&eventTypeString=="mousedown")eventHandler.allowsDeferredCalls=false;JSEvents.registerOrRemoveHandler(eventHandler)}),registerWheelEventCallback:(function(target,userData,useCapture,callbackfunc,eventTypeId,eventTypeString){if(!JSEvents.wheelEvent){JSEvents.wheelEvent=_malloc(104)}target=JSEvents.findEventTarget(target);var wheelHandlerFunc=(function(event){var e=event||window.event;JSEvents.fillMouseEventData(JSEvents.wheelEvent,e,target);HEAPF64[JSEvents.wheelEvent+72>>3]=e["deltaX"];HEAPF64[JSEvents.wheelEvent+80>>3]=e["deltaY"];HEAPF64[JSEvents.wheelEvent+88>>3]=e["deltaZ"];HEAP32[JSEvents.wheelEvent+96>>2]=e["deltaMode"];var shouldCancel=Module["dynCall_iiii"](callbackfunc,eventTypeId,JSEvents.wheelEvent,userData);if(shouldCancel){e.preventDefault()}});var mouseWheelHandlerFunc=(function(event){var e=event||window.event;JSEvents.fillMouseEventData(JSEvents.wheelEvent,e,target);HEAPF64[JSEvents.wheelEvent+72>>3]=e["wheelDeltaX"]||0;HEAPF64[JSEvents.wheelEvent+80>>3]=-(e["wheelDeltaY"]?e["wheelDeltaY"]:e["wheelDelta"]);HEAPF64[JSEvents.wheelEvent+88>>3]=0;HEAP32[JSEvents.wheelEvent+96>>2]=0;var shouldCancel=Module["dynCall_iiii"](callbackfunc,eventTypeId,JSEvents.wheelEvent,userData);if(shouldCancel){e.preventDefault()}});var eventHandler={target:target,allowsDeferredCalls:true,eventTypeString:eventTypeString,callbackfunc:callbackfunc,handlerFunc:eventTypeString=="wheel"?wheelHandlerFunc:mouseWheelHandlerFunc,useCapture:useCapture};JSEvents.registerOrRemoveHandler(eventHandler)}),pageScrollPos:(function(){if(window.pageXOffset>0||window.pageYOffset>0){return[window.pageXOffset,window.pageYOffset]}if(typeof document.documentElement.scrollLeft!=="undefined"||typeof document.documentElement.scrollTop!=="undefined"){return[document.documentElement.scrollLeft,document.documentElement.scrollTop]}return[document.body.scrollLeft|0,document.body.scrollTop|0]}),registerUiEventCallback:(function(target,userData,useCapture,callbackfunc,eventTypeId,eventTypeString){if(!JSEvents.uiEvent){JSEvents.uiEvent=_malloc(36)}if(eventTypeString=="scroll"&&!target){target=document}else{target=JSEvents.findEventTarget(target)}var handlerFunc=(function(event){var e=event||window.event;if(e.target!=target){return}var scrollPos=JSEvents.pageScrollPos();HEAP32[JSEvents.uiEvent>>2]=e.detail;HEAP32[JSEvents.uiEvent+4>>2]=document.body.clientWidth;HEAP32[JSEvents.uiEvent+8>>2]=document.body.clientHeight;HEAP32[JSEvents.uiEvent+12>>2]=window.innerWidth;HEAP32[JSEvents.uiEvent+16>>2]=window.innerHeight;HEAP32[JSEvents.uiEvent+20>>2]=window.outerWidth;HEAP32[JSEvents.uiEvent+24>>2]=window.outerHeight;HEAP32[JSEvents.uiEvent+28>>2]=scrollPos[0];HEAP32[JSEvents.uiEvent+32>>2]=scrollPos[1];var shouldCancel=Module["dynCall_iiii"](callbackfunc,eventTypeId,JSEvents.uiEvent,userData);if(shouldCancel){e.preventDefault()}});var eventHandler={target:target,allowsDeferredCalls:false,eventTypeString:eventTypeString,callbackfunc:callbackfunc,handlerFunc:handlerFunc,useCapture:useCapture};JSEvents.registerOrRemoveHandler(eventHandler)}),getNodeNameForTarget:(function(target){if(!target)return"";if(target==window)return"#window";if(target==window.screen)return"#screen";return target&&target.nodeName?target.nodeName:""}),registerFocusEventCallback:(function(target,userData,useCapture,callbackfunc,eventTypeId,eventTypeString){if(!JSEvents.focusEvent){JSEvents.focusEvent=_malloc(256)}var handlerFunc=(function(event){var e=event||window.event;var nodeName=JSEvents.getNodeNameForTarget(e.target);var id=e.target.id?e.target.id:"";stringToUTF8(nodeName,JSEvents.focusEvent+0,128);stringToUTF8(id,JSEvents.focusEvent+128,128);var shouldCancel=Module["dynCall_iiii"](callbackfunc,eventTypeId,JSEvents.focusEvent,userData);if(shouldCancel){e.preventDefault()}});var eventHandler={target:JSEvents.findEventTarget(target),allowsDeferredCalls:false,eventTypeString:eventTypeString,callbackfunc:callbackfunc,handlerFunc:handlerFunc,useCapture:useCapture};JSEvents.registerOrRemoveHandler(eventHandler)}),tick:(function(){if(window["performance"]&&window["performance"]["now"])return window["performance"]["now"]();else return Date.now()}),registerDeviceOrientationEventCallback:(function(target,userData,useCapture,callbackfunc,eventTypeId,eventTypeString){if(!JSEvents.deviceOrientationEvent){JSEvents.deviceOrientationEvent=_malloc(40)}var handlerFunc=(function(event){var e=event||window.event;HEAPF64[JSEvents.deviceOrientationEvent>>3]=JSEvents.tick();HEAPF64[JSEvents.deviceOrientationEvent+8>>3]=e.alpha;HEAPF64[JSEvents.deviceOrientationEvent+16>>3]=e.beta;HEAPF64[JSEvents.deviceOrientationEvent+24>>3]=e.gamma;HEAP32[JSEvents.deviceOrientationEvent+32>>2]=e.absolute;var shouldCancel=Module["dynCall_iiii"](callbackfunc,eventTypeId,JSEvents.deviceOrientationEvent,userData);if(shouldCancel){e.preventDefault()}});var eventHandler={target:JSEvents.findEventTarget(target),allowsDeferredCalls:false,eventTypeString:eventTypeString,callbackfunc:callbackfunc,handlerFunc:handlerFunc,useCapture:useCapture};JSEvents.registerOrRemoveHandler(eventHandler)}),registerDeviceMotionEventCallback:(function(target,userData,useCapture,callbackfunc,eventTypeId,eventTypeString){if(!JSEvents.deviceMotionEvent){JSEvents.deviceMotionEvent=_malloc(80)}var handlerFunc=(function(event){var e=event||window.event;HEAPF64[JSEvents.deviceMotionEvent>>3]=JSEvents.tick();HEAPF64[JSEvents.deviceMotionEvent+8>>3]=e.acceleration.x;HEAPF64[JSEvents.deviceMotionEvent+16>>3]=e.acceleration.y;HEAPF64[JSEvents.deviceMotionEvent+24>>3]=e.acceleration.z;HEAPF64[JSEvents.deviceMotionEvent+32>>3]=e.accelerationIncludingGravity.x;HEAPF64[JSEvents.deviceMotionEvent+40>>3]=e.accelerationIncludingGravity.y;HEAPF64[JSEvents.deviceMotionEvent+48>>3]=e.accelerationIncludingGravity.z;HEAPF64[JSEvents.deviceMotionEvent+56>>3]=e.rotationRate.alpha;HEAPF64[JSEvents.deviceMotionEvent+64>>3]=e.rotationRate.beta;HEAPF64[JSEvents.deviceMotionEvent+72>>3]=e.rotationRate.gamma;var shouldCancel=Module["dynCall_iiii"](callbackfunc,eventTypeId,JSEvents.deviceMotionEvent,userData);if(shouldCancel){e.preventDefault()}});var eventHandler={target:JSEvents.findEventTarget(target),allowsDeferredCalls:false,eventTypeString:eventTypeString,callbackfunc:callbackfunc,handlerFunc:handlerFunc,useCapture:useCapture};JSEvents.registerOrRemoveHandler(eventHandler)}),screenOrientation:(function(){if(!window.screen)return undefined;return window.screen.orientation||window.screen.mozOrientation||window.screen.webkitOrientation||window.screen.msOrientation}),fillOrientationChangeEventData:(function(eventStruct,e){var orientations=["portrait-primary","portrait-secondary","landscape-primary","landscape-secondary"];var orientations2=["portrait","portrait","landscape","landscape"];var orientationString=JSEvents.screenOrientation();var orientation=orientations.indexOf(orientationString);if(orientation==-1){orientation=orientations2.indexOf(orientationString)}HEAP32[eventStruct>>2]=1<>2]=window.orientation}),registerOrientationChangeEventCallback:(function(target,userData,useCapture,callbackfunc,eventTypeId,eventTypeString){if(!JSEvents.orientationChangeEvent){JSEvents.orientationChangeEvent=_malloc(8)}if(!target){target=window.screen}else{target=JSEvents.findEventTarget(target)}var handlerFunc=(function(event){var e=event||window.event;JSEvents.fillOrientationChangeEventData(JSEvents.orientationChangeEvent,e);var shouldCancel=Module["dynCall_iiii"](callbackfunc,eventTypeId,JSEvents.orientationChangeEvent,userData);if(shouldCancel){e.preventDefault()}});if(eventTypeString=="orientationchange"&&window.screen.mozOrientation!==undefined){eventTypeString="mozorientationchange"}var eventHandler={target:target,allowsDeferredCalls:false,eventTypeString:eventTypeString,callbackfunc:callbackfunc,handlerFunc:handlerFunc,useCapture:useCapture};JSEvents.registerOrRemoveHandler(eventHandler)}),fullscreenEnabled:(function(){return document.fullscreenEnabled||document.mozFullScreenEnabled||document.webkitFullscreenEnabled||document.msFullscreenEnabled}),fillFullscreenChangeEventData:(function(eventStruct,e){var fullscreenElement=document.fullscreenElement||document.mozFullScreenElement||document.webkitFullscreenElement||document.msFullscreenElement;var isFullscreen=!!fullscreenElement;HEAP32[eventStruct>>2]=isFullscreen;HEAP32[eventStruct+4>>2]=JSEvents.fullscreenEnabled();var reportedElement=isFullscreen?fullscreenElement:JSEvents.previousFullscreenElement;var nodeName=JSEvents.getNodeNameForTarget(reportedElement);var id=reportedElement&&reportedElement.id?reportedElement.id:"";stringToUTF8(nodeName,eventStruct+8,128);stringToUTF8(id,eventStruct+136,128);HEAP32[eventStruct+264>>2]=reportedElement?reportedElement.clientWidth:0;HEAP32[eventStruct+268>>2]=reportedElement?reportedElement.clientHeight:0;HEAP32[eventStruct+272>>2]=screen.width;HEAP32[eventStruct+276>>2]=screen.height;if(isFullscreen){JSEvents.previousFullscreenElement=fullscreenElement}}),registerFullscreenChangeEventCallback:(function(target,userData,useCapture,callbackfunc,eventTypeId,eventTypeString){if(!JSEvents.fullscreenChangeEvent){JSEvents.fullscreenChangeEvent=_malloc(280)}if(!target){target=document}else{target=JSEvents.findEventTarget(target)}var handlerFunc=(function(event){var e=event||window.event;JSEvents.fillFullscreenChangeEventData(JSEvents.fullscreenChangeEvent,e);var shouldCancel=Module["dynCall_iiii"](callbackfunc,eventTypeId,JSEvents.fullscreenChangeEvent,userData);if(shouldCancel){e.preventDefault()}});var eventHandler={target:target,allowsDeferredCalls:false,eventTypeString:eventTypeString,callbackfunc:callbackfunc,handlerFunc:handlerFunc,useCapture:useCapture};JSEvents.registerOrRemoveHandler(eventHandler)}),resizeCanvasForFullscreen:(function(target,strategy){var restoreOldStyle=__registerRestoreOldStyle(target);var cssWidth=strategy.softFullscreen?window.innerWidth:screen.width;var cssHeight=strategy.softFullscreen?window.innerHeight:screen.height;var rect=target.getBoundingClientRect();var windowedCssWidth=rect.right-rect.left;var windowedCssHeight=rect.bottom-rect.top;var windowedRttWidth=target.width;var windowedRttHeight=target.height;if(strategy.scaleMode==3){__setLetterbox(target,(cssHeight-windowedCssHeight)/2,(cssWidth-windowedCssWidth)/2);cssWidth=windowedCssWidth;cssHeight=windowedCssHeight}else if(strategy.scaleMode==2){if(cssWidth*windowedRttHeight>2]=isPointerlocked;var nodeName=JSEvents.getNodeNameForTarget(pointerLockElement);var id=pointerLockElement&&pointerLockElement.id?pointerLockElement.id:"";stringToUTF8(nodeName,eventStruct+4,128);stringToUTF8(id,eventStruct+132,128)}),registerPointerlockChangeEventCallback:(function(target,userData,useCapture,callbackfunc,eventTypeId,eventTypeString){if(!JSEvents.pointerlockChangeEvent){JSEvents.pointerlockChangeEvent=_malloc(260)}if(!target){target=document}else{target=JSEvents.findEventTarget(target)}var handlerFunc=(function(event){var e=event||window.event;JSEvents.fillPointerlockChangeEventData(JSEvents.pointerlockChangeEvent,e);var shouldCancel=Module["dynCall_iiii"](callbackfunc,eventTypeId,JSEvents.pointerlockChangeEvent,userData);if(shouldCancel){e.preventDefault()}});var eventHandler={target:target,allowsDeferredCalls:false,eventTypeString:eventTypeString,callbackfunc:callbackfunc,handlerFunc:handlerFunc,useCapture:useCapture};JSEvents.registerOrRemoveHandler(eventHandler)}),registerPointerlockErrorEventCallback:(function(target,userData,useCapture,callbackfunc,eventTypeId,eventTypeString){if(!target){target=document}else{target=JSEvents.findEventTarget(target)}var handlerFunc=(function(event){var e=event||window.event;var shouldCancel=Module["dynCall_iiii"](callbackfunc,eventTypeId,0,userData);if(shouldCancel){e.preventDefault()}});var eventHandler={target:target,allowsDeferredCalls:false,eventTypeString:eventTypeString,callbackfunc:callbackfunc,handlerFunc:handlerFunc,useCapture:useCapture};JSEvents.registerOrRemoveHandler(eventHandler)}),requestPointerLock:(function(target){if(target.requestPointerLock){target.requestPointerLock()}else if(target.mozRequestPointerLock){target.mozRequestPointerLock()}else if(target.webkitRequestPointerLock){target.webkitRequestPointerLock()}else if(target.msRequestPointerLock){target.msRequestPointerLock()}else{if(document.body.requestPointerLock||document.body.mozRequestPointerLock||document.body.webkitRequestPointerLock||document.body.msRequestPointerLock){return-3}else{return-1}}return 0}),fillVisibilityChangeEventData:(function(eventStruct,e){var visibilityStates=["hidden","visible","prerender","unloaded"];var visibilityState=visibilityStates.indexOf(document.visibilityState);HEAP32[eventStruct>>2]=document.hidden;HEAP32[eventStruct+4>>2]=visibilityState}),registerVisibilityChangeEventCallback:(function(target,userData,useCapture,callbackfunc,eventTypeId,eventTypeString){if(!JSEvents.visibilityChangeEvent){JSEvents.visibilityChangeEvent=_malloc(8)}if(!target){target=document}else{target=JSEvents.findEventTarget(target)}var handlerFunc=(function(event){var e=event||window.event;JSEvents.fillVisibilityChangeEventData(JSEvents.visibilityChangeEvent,e);var shouldCancel=Module["dynCall_iiii"](callbackfunc,eventTypeId,JSEvents.visibilityChangeEvent,userData);if(shouldCancel){e.preventDefault()}});var eventHandler={target:target,allowsDeferredCalls:false,eventTypeString:eventTypeString,callbackfunc:callbackfunc,handlerFunc:handlerFunc,useCapture:useCapture};JSEvents.registerOrRemoveHandler(eventHandler)}),registerTouchEventCallback:(function(target,userData,useCapture,callbackfunc,eventTypeId,eventTypeString){if(!JSEvents.touchEvent){JSEvents.touchEvent=_malloc(1684)}target=JSEvents.findEventTarget(target);var handlerFunc=(function(event){var e=event||window.event;var touches={};for(var i=0;i>2]=e.ctrlKey;HEAP32[ptr+8>>2]=e.shiftKey;HEAP32[ptr+12>>2]=e.altKey;HEAP32[ptr+16>>2]=e.metaKey;ptr+=20;var canvasRect=Module["canvas"]?Module["canvas"].getBoundingClientRect():undefined;var targetRect=JSEvents.getBoundingClientRectOrZeros(target);var numTouches=0;for(var i in touches){var t=touches[i];HEAP32[ptr>>2]=t.identifier;HEAP32[ptr+4>>2]=t.screenX;HEAP32[ptr+8>>2]=t.screenY;HEAP32[ptr+12>>2]=t.clientX;HEAP32[ptr+16>>2]=t.clientY;HEAP32[ptr+20>>2]=t.pageX;HEAP32[ptr+24>>2]=t.pageY;HEAP32[ptr+28>>2]=t.changed;HEAP32[ptr+32>>2]=t.onTarget;if(canvasRect){HEAP32[ptr+44>>2]=t.clientX-canvasRect.left;HEAP32[ptr+48>>2]=t.clientY-canvasRect.top}else{HEAP32[ptr+44>>2]=0;HEAP32[ptr+48>>2]=0}HEAP32[ptr+36>>2]=t.clientX-targetRect.left;HEAP32[ptr+40>>2]=t.clientY-targetRect.top;ptr+=52;if(++numTouches>=32){break}}HEAP32[JSEvents.touchEvent>>2]=numTouches;var shouldCancel=Module["dynCall_iiii"](callbackfunc,eventTypeId,JSEvents.touchEvent,userData);if(shouldCancel){e.preventDefault()}});var eventHandler={target:target,allowsDeferredCalls:eventTypeString=="touchstart"||eventTypeString=="touchend",eventTypeString:eventTypeString,callbackfunc:callbackfunc,handlerFunc:handlerFunc,useCapture:useCapture};JSEvents.registerOrRemoveHandler(eventHandler)}),fillGamepadEventData:(function(eventStruct,e){HEAPF64[eventStruct>>3]=e.timestamp;for(var i=0;i>3]=e.axes[i]}for(var i=0;i>3]=e.buttons[i].value}else{HEAPF64[eventStruct+i*8+528>>3]=e.buttons[i]}}for(var i=0;i>2]=e.buttons[i].pressed}else{HEAP32[eventStruct+i*4+1040>>2]=e.buttons[i]==1}}HEAP32[eventStruct+1296>>2]=e.connected;HEAP32[eventStruct+1300>>2]=e.index;HEAP32[eventStruct+8>>2]=e.axes.length;HEAP32[eventStruct+12>>2]=e.buttons.length;stringToUTF8(e.id,eventStruct+1304,64);stringToUTF8(e.mapping,eventStruct+1368,64)}),registerGamepadEventCallback:(function(target,userData,useCapture,callbackfunc,eventTypeId,eventTypeString){if(!JSEvents.gamepadEvent){JSEvents.gamepadEvent=_malloc(1432)}var handlerFunc=(function(event){var e=event||window.event;JSEvents.fillGamepadEventData(JSEvents.gamepadEvent,e.gamepad);var shouldCancel=Module["dynCall_iiii"](callbackfunc,eventTypeId,JSEvents.gamepadEvent,userData);if(shouldCancel){e.preventDefault()}});var eventHandler={target:JSEvents.findEventTarget(target),allowsDeferredCalls:true,eventTypeString:eventTypeString,callbackfunc:callbackfunc,handlerFunc:handlerFunc,useCapture:useCapture};JSEvents.registerOrRemoveHandler(eventHandler)}),registerBeforeUnloadEventCallback:(function(target,userData,useCapture,callbackfunc,eventTypeId,eventTypeString){var handlerFunc=(function(event){var e=event||window.event;var confirmationMessage=Module["dynCall_iiii"](callbackfunc,eventTypeId,0,userData);if(confirmationMessage){confirmationMessage=Pointer_stringify(confirmationMessage)}if(confirmationMessage){e.preventDefault();e.returnValue=confirmationMessage;return confirmationMessage}});var eventHandler={target:JSEvents.findEventTarget(target),allowsDeferredCalls:false,eventTypeString:eventTypeString,callbackfunc:callbackfunc,handlerFunc:handlerFunc,useCapture:useCapture};JSEvents.registerOrRemoveHandler(eventHandler)}),battery:(function(){return navigator.battery||navigator.mozBattery||navigator.webkitBattery}),fillBatteryEventData:(function(eventStruct,e){HEAPF64[eventStruct>>3]=e.chargingTime;HEAPF64[eventStruct+8>>3]=e.dischargingTime;HEAPF64[eventStruct+16>>3]=e.level;HEAP32[eventStruct+24>>2]=e.charging}),registerBatteryEventCallback:(function(target,userData,useCapture,callbackfunc,eventTypeId,eventTypeString){if(!JSEvents.batteryEvent){JSEvents.batteryEvent=_malloc(32)}var handlerFunc=(function(event){var e=event||window.event;JSEvents.fillBatteryEventData(JSEvents.batteryEvent,JSEvents.battery());var shouldCancel=Module["dynCall_iiii"](callbackfunc,eventTypeId,JSEvents.batteryEvent,userData);if(shouldCancel){e.preventDefault()}});var eventHandler={target:JSEvents.findEventTarget(target),allowsDeferredCalls:false,eventTypeString:eventTypeString,callbackfunc:callbackfunc,handlerFunc:handlerFunc,useCapture:useCapture};JSEvents.registerOrRemoveHandler(eventHandler)}),registerWebGlEventCallback:(function(target,userData,useCapture,callbackfunc,eventTypeId,eventTypeString){if(!target){target=Module["canvas"]}var handlerFunc=(function(event){var e=event||window.event;var shouldCancel=Module["dynCall_iiii"](callbackfunc,eventTypeId,0,userData);if(shouldCancel){e.preventDefault()}});var eventHandler={target:JSEvents.findEventTarget(target),allowsDeferredCalls:false,eventTypeString:eventTypeString,callbackfunc:callbackfunc,handlerFunc:handlerFunc,useCapture:useCapture};JSEvents.registerOrRemoveHandler(eventHandler)})};function _emscripten_webgl_destroy_context(contextHandle){GL.deleteContext(contextHandle)}function _glReadPixels(x,y,width,height,format,type,pixels){var pixelData=emscriptenWebGLGetTexPixelData(type,format,width,height,pixels,format);if(!pixelData){GL.recordError(1280);return}GLctx.readPixels(x,y,width,height,format,type,pixelData)}function _glAttachShader(program,shader){GLctx.attachShader(GL.programs[program],GL.shaders[shader])}function _glCompileShader(shader){GLctx.compileShader(GL.shaders[shader])}function _glDeleteShader(id){if(!id)return;var shader=GL.shaders[id];if(!shader){GL.recordError(1281);return}GLctx.deleteShader(shader);GL.shaders[id]=null}function _glFramebufferTexture2D(target,attachment,textarget,texture,level){GLctx.framebufferTexture2D(target,attachment,textarget,GL.textures[texture],level)}function _glCreateProgram(){var id=GL.getNewId(GL.programs);var program=GLctx.createProgram();program.name=id;GL.programs[id]=program;return id}function _glDeleteProgram(id){if(!id)return;var program=GL.programs[id];if(!program){GL.recordError(1281);return}GLctx.deleteProgram(program);program.name=0;GL.programs[id]=null;GL.programInfos[id]=null}function _glCheckFramebufferStatus(x0){return GLctx["checkFramebufferStatus"](x0)}function _pthread_setspecific(key,value){if(!(key in PTHREAD_SPECIFIC)){return ERRNO_CODES.EINVAL}PTHREAD_SPECIFIC[key]=value;return 0}function _emscripten_webgl_create_context(target,attributes){var contextAttributes={};contextAttributes["alpha"]=!!HEAP32[attributes>>2];contextAttributes["depth"]=!!HEAP32[attributes+4>>2];contextAttributes["stencil"]=!!HEAP32[attributes+8>>2];contextAttributes["antialias"]=!!HEAP32[attributes+12>>2];contextAttributes["premultipliedAlpha"]=!!HEAP32[attributes+16>>2];contextAttributes["preserveDrawingBuffer"]=!!HEAP32[attributes+20>>2];contextAttributes["preferLowPowerToHighPerformance"]=!!HEAP32[attributes+24>>2];contextAttributes["failIfMajorPerformanceCaveat"]=!!HEAP32[attributes+28>>2];contextAttributes["majorVersion"]=HEAP32[attributes+32>>2];contextAttributes["minorVersion"]=HEAP32[attributes+36>>2];contextAttributes["explicitSwapControl"]=HEAP32[attributes+44>>2];target=Pointer_stringify(target);var canvas;if((!target||target==="#canvas")&&Module["canvas"]){canvas=Module["canvas"].id?GL.offscreenCanvases[Module["canvas"].id]||JSEvents.findEventTarget(Module["canvas"].id):Module["canvas"]}else{canvas=GL.offscreenCanvases[target]||JSEvents.findEventTarget(target)}if(!canvas){return 0}if(contextAttributes["explicitSwapControl"]){console.error("emscripten_webgl_create_context failed: explicitSwapControl is not supported, please rebuild with -s OFFSCREENCANVAS_SUPPORT=1 to enable targeting the experimental OffscreenCanvas specification!");return 0}var contextHandle=GL.createContext(canvas,contextAttributes);return contextHandle}function _glGetError(){if(GL.lastError){var error=GL.lastError;GL.lastError=0;return error}else{return GLctx.getError()}}function ___cxa_begin_catch(ptr){var info=EXCEPTIONS.infos[ptr];if(info&&!info.caught){info.caught=true;__ZSt18uncaught_exceptionv.uncaught_exception--}if(info)info.rethrown=false;EXCEPTIONS.caught.push(ptr);EXCEPTIONS.addRef(EXCEPTIONS.deAdjust(ptr));return ptr}function _emscripten_memcpy_big(dest,src,num){HEAPU8.set(HEAPU8.subarray(src,src+num),dest);return dest}var SYSCALLS={varargs:0,get:(function(varargs){SYSCALLS.varargs+=4;var ret=HEAP32[SYSCALLS.varargs-4>>2];return ret}),getStr:(function(){var ret=Pointer_stringify(SYSCALLS.get());return ret}),get64:(function(){var low=SYSCALLS.get(),high=SYSCALLS.get();if(low>=0)assert(high===0);else assert(high===-1);return low}),getZero:(function(){assert(SYSCALLS.get()===0)})};function ___syscall6(which,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD();FS.close(stream);return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}function _glGenFramebuffers(n,ids){for(var i=0;i>2]=0;return}var id=GL.getNewId(GL.framebuffers);framebuffer.name=id;GL.framebuffers[id]=framebuffer;HEAP32[ids+i*4>>2]=id}}function ___setErrNo(value){if(Module["___errno_location"])HEAP32[Module["___errno_location"]()>>2]=value;return value}function _llvm_stacksave(){var self=_llvm_stacksave;if(!self.LLVM_SAVEDSTACKS){self.LLVM_SAVEDSTACKS=[]}self.LLVM_SAVEDSTACKS.push(Runtime.stackSave());return self.LLVM_SAVEDSTACKS.length-1}function _glGenTextures(n,textures){for(var i=0;i>2]=0;return}var id=GL.getNewId(GL.textures);texture.name=id;GL.textures[id]=texture;HEAP32[textures+i*4>>2]=id}}function ___cxa_allocate_exception(size){return _malloc(size)}function _emscripten_webgl_make_context_current(contextHandle){var success=GL.makeContextCurrent(contextHandle);return success?0:-5}function ___gxx_personality_v0(){}function ___syscall54(which,varargs){SYSCALLS.varargs=varargs;try{return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}function _glUniform1f(location,v0){GLctx.uniform1f(GL.uniforms[location],v0)}function _pthread_once(ptr,func){if(!_pthread_once.seen)_pthread_once.seen={};if(ptr in _pthread_once.seen)return;Module["dynCall_v"](func);_pthread_once.seen[ptr]=1}function _glTexParameteri(x0,x1,x2){GLctx["texParameteri"](x0,x1,x2)}function _glGetShaderiv(shader,pname,p){if(!p){GL.recordError(1281);return}if(pname==35716){var log=GLctx.getShaderInfoLog(GL.shaders[shader]);if(log===null)log="(unknown error)";HEAP32[p>>2]=log.length+1}else if(pname==35720){var source=GLctx.getShaderSource(GL.shaders[shader]);var sourceLength=source===null||source.length==0?0:source.length+1;HEAP32[p>>2]=sourceLength}else{HEAP32[p>>2]=GLctx.getShaderParameter(GL.shaders[shader],pname)}}function _glCreateShader(shaderType){var id=GL.getNewId(GL.shaders);GL.shaders[id]=GLctx.createShader(shaderType);return id}function ___syscall140(which,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(),offset_high=SYSCALLS.get(),offset_low=SYSCALLS.get(),result=SYSCALLS.get(),whence=SYSCALLS.get();var offset=offset_low;FS.llseek(stream,offset,whence);HEAP32[result>>2]=stream.position;if(stream.getdents&&offset===0&&whence===0)stream.getdents=null;return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}function ___syscall146(which,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.get(),iov=SYSCALLS.get(),iovcnt=SYSCALLS.get();var ret=0;if(!___syscall146.buffer){___syscall146.buffers=[null,[],[]];___syscall146.printChar=(function(stream,curr){var buffer=___syscall146.buffers[stream];assert(buffer);if(curr===0||curr===10){(stream===1?Module["print"]:Module["printErr"])(UTF8ArrayToString(buffer,0));buffer.length=0}else{buffer.push(curr)}})}for(var i=0;i>2];var len=HEAP32[iov+(i*8+4)>>2];for(var j=0;j>2]=DYNAMIC_BASE;staticSealed=true;Module["wasmTableSize"]=44;Module["wasmMaxTableSize"]=44;function invoke_iiii(index,a1,a2,a3){try{return Module["dynCall_iiii"](index,a1,a2,a3)}catch(e){if(typeof e!=="number"&&e!=="longjmp")throw e;Module["setThrew"](1,0)}}function invoke_viiiii(index,a1,a2,a3,a4,a5){try{Module["dynCall_viiiii"](index,a1,a2,a3,a4,a5)}catch(e){if(typeof e!=="number"&&e!=="longjmp")throw e;Module["setThrew"](1,0)}}function invoke_vi(index,a1){try{Module["dynCall_vi"](index,a1)}catch(e){if(typeof e!=="number"&&e!=="longjmp")throw e;Module["setThrew"](1,0)}}function invoke_ii(index,a1){try{return Module["dynCall_ii"](index,a1)}catch(e){if(typeof e!=="number"&&e!=="longjmp")throw e;Module["setThrew"](1,0)}}function invoke_v(index){try{Module["dynCall_v"](index)}catch(e){if(typeof e!=="number"&&e!=="longjmp")throw e;Module["setThrew"](1,0)}}function invoke_viiiiii(index,a1,a2,a3,a4,a5,a6){try{Module["dynCall_viiiiii"](index,a1,a2,a3,a4,a5,a6)}catch(e){if(typeof e!=="number"&&e!=="longjmp")throw e;Module["setThrew"](1,0)}}function invoke_viiii(index,a1,a2,a3,a4){try{Module["dynCall_viiii"](index,a1,a2,a3,a4)}catch(e){if(typeof e!=="number"&&e!=="longjmp")throw e;Module["setThrew"](1,0)}}Module.asmGlobalArg={"Math":Math,"Int8Array":Int8Array,"Int16Array":Int16Array,"Int32Array":Int32Array,"Uint8Array":Uint8Array,"Uint16Array":Uint16Array,"Uint32Array":Uint32Array,"Float32Array":Float32Array,"Float64Array":Float64Array,"NaN":NaN,"Infinity":Infinity,"byteLength":byteLength};Module.asmLibraryArg={"abort":abort,"assert":assert,"enlargeMemory":enlargeMemory,"getTotalMemory":getTotalMemory,"abortOnCannotGrowMemory":abortOnCannotGrowMemory,"invoke_iiii":invoke_iiii,"invoke_viiiii":invoke_viiiii,"invoke_vi":invoke_vi,"invoke_ii":invoke_ii,"invoke_v":invoke_v,"invoke_viiiiii":invoke_viiiiii,"invoke_viiii":invoke_viiii,"_glUseProgram":_glUseProgram,"_glDeleteShader":_glDeleteShader,"_glVertexAttribPointer":_glVertexAttribPointer,"_glShaderSource":_glShaderSource,"_pthread_key_create":_pthread_key_create,"_abort":_abort,"emscriptenWebGLComputeImageSize":emscriptenWebGLComputeImageSize,"_glCheckFramebufferStatus":_glCheckFramebufferStatus,"_emscripten_webgl_create_context":_emscripten_webgl_create_context,"___gxx_personality_v0":___gxx_personality_v0,"_llvm_stackrestore":_llvm_stackrestore,"_glDeleteProgram":_glDeleteProgram,"___cxa_allocate_exception":___cxa_allocate_exception,"___cxa_find_matching_catch":___cxa_find_matching_catch,"_glLinkProgram":_glLinkProgram,"_glGetShaderInfoLog":_glGetShaderInfoLog,"_glBindTexture":_glBindTexture,"___setErrNo":___setErrNo,"_emscripten_asm_const_id":_emscripten_asm_const_id,"_glGenFramebuffers":_glGenFramebuffers,"_glGetUniformLocation":_glGetUniformLocation,"_glGetAttribLocation":_glGetAttribLocation,"___cxa_begin_catch":___cxa_begin_catch,"_emscripten_memcpy_big":_emscripten_memcpy_big,"___resumeException":___resumeException,"__ZSt18uncaught_exceptionv":__ZSt18uncaught_exceptionv,"_glUniform1i":_glUniform1i,"_glActiveTexture":_glActiveTexture,"_pthread_getspecific":_pthread_getspecific,"_glDrawArrays":_glDrawArrays,"_glReadPixels":_glReadPixels,"_glCreateShader":_glCreateShader,"_glGetError":_glGetError,"_emscripten_webgl_destroy_context":_emscripten_webgl_destroy_context,"_llvm_stacksave":_llvm_stacksave,"_pthread_once":_pthread_once,"_glGenTextures":_glGenTextures,"_glGetString":_glGetString,"___syscall54":___syscall54,"_glFramebufferTexture2D":_glFramebufferTexture2D,"_emscripten_webgl_make_context_current":_emscripten_webgl_make_context_current,"_glGenBuffers":_glGenBuffers,"_glAttachShader":_glAttachShader,"_pthread_setspecific":_pthread_setspecific,"_glCreateProgram":_glCreateProgram,"_glCompileShader":_glCompileShader,"___cxa_throw":___cxa_throw,"_glEnableVertexAttribArray":_glEnableVertexAttribArray,"emscriptenWebGLGetTexPixelData":emscriptenWebGLGetTexPixelData,"___syscall6":___syscall6,"_glBindBuffer":_glBindBuffer,"_emscripten_asm_const_i":_emscripten_asm_const_i,"_glBindFramebuffer":_glBindFramebuffer,"_glTexParameteri":_glTexParameteri,"_glBufferData":_glBufferData,"_glGetShaderiv":_glGetShaderiv,"_glTexImage2D":_glTexImage2D,"___syscall140":___syscall140,"_glUniform1f":_glUniform1f,"___syscall146":___syscall146,"DYNAMICTOP_PTR":DYNAMICTOP_PTR,"tempDoublePtr":tempDoublePtr,"ABORT":ABORT,"STACKTOP":STACKTOP,"STACK_MAX":STACK_MAX};var asm=Module["asm"](Module.asmGlobalArg,Module.asmLibraryArg,buffer);Module["asm"]=asm;var _runExample3WA=Module["_runExample3WA"]=(function(){return Module["asm"]["_runExample3WA"].apply(null,arguments)});var _runExample4WA=Module["_runExample4WA"]=(function(){return Module["asm"]["_runExample4WA"].apply(null,arguments)});var _main=Module["_main"]=(function(){return Module["asm"]["_main"].apply(null,arguments)});var stackSave=Module["stackSave"]=(function(){return Module["asm"]["stackSave"].apply(null,arguments)});var setThrew=Module["setThrew"]=(function(){return Module["asm"]["setThrew"].apply(null,arguments)});var _runExample2WA=Module["_runExample2WA"]=(function(){return Module["asm"]["_runExample2WA"].apply(null,arguments)});var __GLOBAL__sub_I_webassemblyDemo_cpp=Module["__GLOBAL__sub_I_webassemblyDemo_cpp"]=(function(){return Module["asm"]["__GLOBAL__sub_I_webassemblyDemo_cpp"].apply(null,arguments)});var ___cxa_is_pointer_type=Module["___cxa_is_pointer_type"]=(function(){return Module["asm"]["___cxa_is_pointer_type"].apply(null,arguments)});var _memset=Module["_memset"]=(function(){return Module["asm"]["_memset"].apply(null,arguments)});var _runExample4WAGPU=Module["_runExample4WAGPU"]=(function(){return Module["asm"]["_runExample4WAGPU"].apply(null,arguments)});var _sbrk=Module["_sbrk"]=(function(){return Module["asm"]["_sbrk"].apply(null,arguments)});var _memcpy=Module["_memcpy"]=(function(){return Module["asm"]["_memcpy"].apply(null,arguments)});var _llvm_bswap_i32=Module["_llvm_bswap_i32"]=(function(){return Module["asm"]["_llvm_bswap_i32"].apply(null,arguments)});var stackAlloc=Module["stackAlloc"]=(function(){return Module["asm"]["stackAlloc"].apply(null,arguments)});var _runExample1WAGPU=Module["_runExample1WAGPU"]=(function(){return Module["asm"]["_runExample1WAGPU"].apply(null,arguments)});var getTempRet0=Module["getTempRet0"]=(function(){return Module["asm"]["getTempRet0"].apply(null,arguments)});var setTempRet0=Module["setTempRet0"]=(function(){return Module["asm"]["setTempRet0"].apply(null,arguments)});var ___cxa_can_catch=Module["___cxa_can_catch"]=(function(){return Module["asm"]["___cxa_can_catch"].apply(null,arguments)});var _runExample3WAGPU=Module["_runExample3WAGPU"]=(function(){return Module["asm"]["_runExample3WAGPU"].apply(null,arguments)});var _emscripten_get_global_libc=Module["_emscripten_get_global_libc"]=(function(){return Module["asm"]["_emscripten_get_global_libc"].apply(null,arguments)});var _runExample2WAGPU=Module["_runExample2WAGPU"]=(function(){return Module["asm"]["_runExample2WAGPU"].apply(null,arguments)});var ___errno_location=Module["___errno_location"]=(function(){return Module["asm"]["___errno_location"].apply(null,arguments)});var _Example1=Module["_Example1"]=(function(){return Module["asm"]["_Example1"].apply(null,arguments)});var _Example2=Module["_Example2"]=(function(){return Module["asm"]["_Example2"].apply(null,arguments)});var _Example3=Module["_Example3"]=(function(){return Module["asm"]["_Example3"].apply(null,arguments)});var _free=Module["_free"]=(function(){return Module["asm"]["_free"].apply(null,arguments)});var runPostSets=Module["runPostSets"]=(function(){return Module["asm"]["runPostSets"].apply(null,arguments)});var establishStackSpace=Module["establishStackSpace"]=(function(){return Module["asm"]["establishStackSpace"].apply(null,arguments)});var _memmove=Module["_memmove"]=(function(){return Module["asm"]["_memmove"].apply(null,arguments)});var stackRestore=Module["stackRestore"]=(function(){return Module["asm"]["stackRestore"].apply(null,arguments)});var _malloc=Module["_malloc"]=(function(){return Module["asm"]["_malloc"].apply(null,arguments)});var _emscripten_replace_memory=Module["_emscripten_replace_memory"]=(function(){return Module["asm"]["_emscripten_replace_memory"].apply(null,arguments)});var _runExample1WA=Module["_runExample1WA"]=(function(){return Module["asm"]["_runExample1WA"].apply(null,arguments)});var dynCall_iiii=Module["dynCall_iiii"]=(function(){return Module["asm"]["dynCall_iiii"].apply(null,arguments)});var dynCall_viiiii=Module["dynCall_viiiii"]=(function(){return Module["asm"]["dynCall_viiiii"].apply(null,arguments)});var dynCall_vi=Module["dynCall_vi"]=(function(){return Module["asm"]["dynCall_vi"].apply(null,arguments)});var dynCall_ii=Module["dynCall_ii"]=(function(){return Module["asm"]["dynCall_ii"].apply(null,arguments)});var dynCall_v=Module["dynCall_v"]=(function(){return Module["asm"]["dynCall_v"].apply(null,arguments)});var dynCall_viiiiii=Module["dynCall_viiiiii"]=(function(){return Module["asm"]["dynCall_viiiiii"].apply(null,arguments)});var dynCall_viiii=Module["dynCall_viiii"]=(function(){return Module["asm"]["dynCall_viiii"].apply(null,arguments)});Runtime.stackAlloc=Module["stackAlloc"];Runtime.stackSave=Module["stackSave"];Runtime.stackRestore=Module["stackRestore"];Runtime.establishStackSpace=Module["establishStackSpace"];Runtime.setTempRet0=Module["setTempRet0"];Runtime.getTempRet0=Module["getTempRet0"];Module["asm"]=asm;if(memoryInitializer){if(typeof Module["locateFile"]==="function"){memoryInitializer=Module["locateFile"](memoryInitializer)}else if(Module["memoryInitializerPrefixURL"]){memoryInitializer=Module["memoryInitializerPrefixURL"]+memoryInitializer}if(ENVIRONMENT_IS_NODE||ENVIRONMENT_IS_SHELL){var data=Module["readBinary"](memoryInitializer);HEAPU8.set(data,Runtime.GLOBAL_BASE)}else{addRunDependency("memory initializer");var applyMemoryInitializer=(function(data){if(data.byteLength)data=new Uint8Array(data);HEAPU8.set(data,Runtime.GLOBAL_BASE);if(Module["memoryInitializerRequest"])delete Module["memoryInitializerRequest"].response;removeRunDependency("memory initializer")});function doBrowserLoad(){Module["readAsync"](memoryInitializer,applyMemoryInitializer,(function(){throw"could not load memory initializer "+memoryInitializer}))}if(Module["memoryInitializerRequest"]){function useRequest(){var request=Module["memoryInitializerRequest"];if(request.status!==200&&request.status!==0){console.warn("a problem seems to have happened with Module.memoryInitializerRequest, status: "+request.status+", retrying "+memoryInitializer);doBrowserLoad();return}applyMemoryInitializer(request.response)}if(Module["memoryInitializerRequest"].response){setTimeout(useRequest,0)}else{Module["memoryInitializerRequest"].addEventListener("load",useRequest)}}else{doBrowserLoad()}}}function ExitStatus(status){this.name="ExitStatus";this.message="Program terminated with exit("+status+")";this.status=status}ExitStatus.prototype=new Error;ExitStatus.prototype.constructor=ExitStatus;var initialStackTop;var preloadStartTime=null;var calledMain=false;dependenciesFulfilled=function runCaller(){if(!Module["calledRun"])run();if(!Module["calledRun"])dependenciesFulfilled=runCaller};Module["callMain"]=Module.callMain=function callMain(args){args=args||[];ensureInitRuntime();var argc=args.length+1;function pad(){for(var i=0;i<4-1;i++){argv.push(0)}}var argv=[allocate(intArrayFromString(Module["thisProgram"]),"i8",ALLOC_NORMAL)];pad();for(var i=0;i0){return}preRun();if(runDependencies>0)return;if(Module["calledRun"])return;function doRun(){if(Module["calledRun"])return;Module["calledRun"]=true;if(ABORT)return;ensureInitRuntime();preMain();if(Module["onRuntimeInitialized"])Module["onRuntimeInitialized"]();if(Module["_main"]&&shouldRunNow)Module["callMain"](args);postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout((function(){setTimeout((function(){Module["setStatus"]("")}),1);doRun()}),1)}else{doRun()}}Module["run"]=Module.run=run;function exit(status,implicit){if(implicit&&Module["noExitRuntime"]){return}if(Module["noExitRuntime"]){}else{ABORT=true;EXITSTATUS=status;STACKTOP=initialStackTop;exitRuntime();if(Module["onExit"])Module["onExit"](status)}if(ENVIRONMENT_IS_NODE){process["exit"](status)}Module["quit"](status,new ExitStatus(status))}Module["exit"]=Module.exit=exit;var abortDecorators=[];function abort(what){if(Module["onAbort"]){Module["onAbort"](what)}if(what!==undefined){Module.print(what);Module.printErr(what);what=JSON.stringify(what)}else{what=""}ABORT=true;EXITSTATUS=1;var extra="\nIf this abort() is unexpected, build with -s ASSERTIONS=1 which can give more information.";var output="abort("+what+") at "+stackTrace()+extra;if(abortDecorators){abortDecorators.forEach((function(decorator){output=decorator(output,what)}))}throw output}Module["abort"]=Module.abort=abort;if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}var shouldRunNow=true;if(Module["noInitialRun"]){shouldRunNow=false}Module["noExitRuntime"]=true;run() 2 | 3 | 4 | 5 | --------------------------------------------------------------------------------