From b6a666e96ffd04bd6d52be8fd9899faf27b751db Mon Sep 17 00:00:00 2001 From: mattkae Date: Mon, 31 Jul 2023 07:31:52 -0400 Subject: Some initial shader loading work --- themes/dist/output.js | 1071 ++++++++++++++++++---------------- themes/dist/output.wasm | Bin 147254 -> 147365 bytes themes/src/Renderer2d.cpp | 10 +- themes/src/Renderer2d.h | 5 +- themes/src/_shaders/renderer2d.frag | 0 themes/src/_shaders/renderer2d.vert | 0 themes/src/_shaders/renderer3d.frag | 7 + themes/src/_shaders/renderer3d.vert | 15 + themes/src/_shaders/sun.vert | 0 themes/src/main.cpp | 5 +- themes/src/shaders/renderer2d.frag | 0 themes/src/shaders/renderer2d.vert | 0 themes/src/shaders/renderer2d_frag.h | 4 + themes/src/shaders/renderer2d_vert.h | 4 + themes/src/shaders/renderer3d.frag | 7 - themes/src/shaders/renderer3d.vert | 15 - themes/src/shaders/renderer3d_frag.h | 11 + themes/src/shaders/renderer3d_vert.h | 19 + themes/src/shaders/sun_vert.h | 4 + themes/src/summer/SummerTheme.cpp | 3 +- themes/src/summer/SummerTheme.h | 2 +- themes/src/tools/shader.js | 30 + themes/tools/bundle_shaders.py | 4 - 23 files changed, 674 insertions(+), 542 deletions(-) create mode 100644 themes/src/_shaders/renderer2d.frag create mode 100644 themes/src/_shaders/renderer2d.vert create mode 100644 themes/src/_shaders/renderer3d.frag create mode 100644 themes/src/_shaders/renderer3d.vert create mode 100644 themes/src/_shaders/sun.vert delete mode 100644 themes/src/shaders/renderer2d.frag delete mode 100644 themes/src/shaders/renderer2d.vert create mode 100644 themes/src/shaders/renderer2d_frag.h create mode 100644 themes/src/shaders/renderer2d_vert.h delete mode 100644 themes/src/shaders/renderer3d.frag delete mode 100644 themes/src/shaders/renderer3d.vert create mode 100644 themes/src/shaders/renderer3d_frag.h create mode 100644 themes/src/shaders/renderer3d_vert.h create mode 100644 themes/src/shaders/sun_vert.h create mode 100644 themes/src/tools/shader.js delete mode 100644 themes/tools/bundle_shaders.py (limited to 'themes') diff --git a/themes/dist/output.js b/themes/dist/output.js index e814b4e..75522f9 100644 --- a/themes/dist/output.js +++ b/themes/dist/output.js @@ -61,24 +61,17 @@ var read_, readBinary, setWindowTitle; -// Normally we don't log exceptions but instead let them bubble out the top -// level where the embedding environment (e.g. the browser) can handle -// them. -// However under v8 and node we sometimes exit the process direcly in which case -// its up to use us to log the exception before exiting. -// If we fix https://github.com/emscripten-core/emscripten/issues/15080 -// this may no longer be needed under node. -function logExceptionOnExit(e) { - if (e instanceof ExitStatus) return; - let toLog = e; - if (e && typeof e == 'object' && e.stack) { - toLog = [e, e.stack]; - } - err('exiting due to exception: ' + toLog); -} - if (ENVIRONMENT_IS_NODE) { if (typeof process == 'undefined' || !process.release || process.release.name !== 'node') throw new Error('not compiled for this environment (did you build to HTML and try to run it not on the web, or set ENVIRONMENT to something - like node - and run it someplace else - like on the web?)'); + + var nodeVersion = process.versions.node; + var numericVersion = nodeVersion.split('.').slice(0, 3); + numericVersion = (numericVersion[0] * 10000) + (numericVersion[1] * 100) + (numericVersion[2].split('-')[0] * 1); + var minVersion = 101900; + if (numericVersion < 101900) { + throw new Error('This emscripten-generated code requires node v10.19.19.0 (detected v' + nodeVersion + ')'); + } + // `require()` is no-op in an ESM module, use `createRequire()` to construct // the require()` function. This is only necessary for multi-environment // builds, `-sENVIRONMENT=node` emits a static import declaration instead. @@ -111,29 +104,29 @@ readBinary = (filename) => { return ret; }; -readAsync = (filename, onload, onerror) => { +readAsync = (filename, onload, onerror, binary = true) => { // See the comment in the `read_` function. filename = isFileURI(filename) ? new URL(filename) : nodePath.normalize(filename); - fs.readFile(filename, function(err, data) { + fs.readFile(filename, binary ? undefined : 'utf8', (err, data) => { if (err) onerror(err); - else onload(data.buffer); + else onload(binary ? data.buffer : data); }); }; // end include: node_shell_read.js - if (process['argv'].length > 1) { - thisProgram = process['argv'][1].replace(/\\/g, '/'); + if (!Module['thisProgram'] && process.argv.length > 1) { + thisProgram = process.argv[1].replace(/\\/g, '/'); } - arguments_ = process['argv'].slice(2); + arguments_ = process.argv.slice(2); if (typeof module != 'undefined') { module['exports'] = Module; } - process['on']('uncaughtException', function(ex) { + process.on('uncaughtException', (ex) => { // suppress ExitStatus exceptions from showing an error - if (!(ex instanceof ExitStatus)) { + if (ex !== 'unwind' && !(ex instanceof ExitStatus) && !(ex.context instanceof ExitStatus)) { throw ex; } }); @@ -143,21 +136,17 @@ readAsync = (filename, onload, onerror) => { // not be needed with node v15 and about because it is now the default // behaviour: // See https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode - var nodeMajor = process.version.match(/^v(\d+)\./)[1]; + var nodeMajor = process.versions.node.split(".")[0]; if (nodeMajor < 15) { - process['on']('unhandledRejection', function(reason) { throw reason; }); + process.on('unhandledRejection', (reason) => { throw reason; }); } quit_ = (status, toThrow) => { - if (keepRuntimeAlive()) { - process['exitCode'] = status; - throw toThrow; - } - logExceptionOnExit(toThrow); - process['exit'](status); + process.exitCode = status; + throw toThrow; }; - Module['inspect'] = function () { return '[Emscripten Module object]'; }; + Module['inspect'] = () => '[Emscripten Module object]'; } else if (ENVIRONMENT_IS_SHELL) { @@ -165,12 +154,12 @@ if (ENVIRONMENT_IS_SHELL) { if ((typeof process == 'object' && typeof require === 'function') || typeof window == 'object' || typeof importScripts == 'function') throw new Error('not compiled for this environment (did you build to HTML and try to run it not on the web, or set ENVIRONMENT to something - like node - and run it someplace else - like on the web?)'); if (typeof read != 'undefined') { - read_ = function shell_read(f) { + read_ = (f) => { return read(f); }; } - readBinary = function readBinary(f) { + readBinary = (f) => { let data; if (typeof readbuffer == 'function') { return new Uint8Array(readbuffer(f)); @@ -180,7 +169,7 @@ if (ENVIRONMENT_IS_SHELL) { return data; }; - readAsync = function readAsync(f, onload, onerror) { + readAsync = (f, onload, onerror) => { setTimeout(() => onload(readBinary(f)), 0); }; @@ -196,8 +185,26 @@ if (ENVIRONMENT_IS_SHELL) { if (typeof quit == 'function') { quit_ = (status, toThrow) => { - logExceptionOnExit(toThrow); - quit(status); + // Unlike node which has process.exitCode, d8 has no such mechanism. So we + // have no way to set the exit code and then let the program exit with + // that code when it naturally stops running (say, when all setTimeouts + // have completed). For that reason, we must call `quit` - the only way to + // set the exit code - but quit also halts immediately. To increase + // consistency with node (and the web) we schedule the actual quit call + // using a setTimeout to give the current stack and any exception handlers + // a chance to run. This enables features such as addOnPostRun (which + // expected to be able to run code after main returns). + setTimeout(() => { + if (!(toThrow instanceof ExitStatus)) { + let toLog = toThrow; + if (toThrow && typeof toThrow == 'object' && toThrow.stack) { + toLog = [toThrow, toThrow.stack]; + } + err(`exiting due to exception: ${toLog}`); + } + quit(status); + }); + throw toThrow; }; } @@ -279,7 +286,7 @@ read_ = (url) => { } var out = Module['print'] || console.log.bind(console); -var err = Module['printErr'] || console.warn.bind(console); +var err = Module['printErr'] || console.error.bind(console); // Merge back in the overrides Object.assign(Module, moduleOverrides); @@ -369,195 +376,6 @@ function assert(condition, text) { // We used to include malloc/free by default in the past. Show a helpful error in // builds with assertions. -// include: runtime_strings.js -// runtime_strings.js: String related runtime functions that are part of both -// MINIMAL_RUNTIME and regular runtime. - -var UTF8Decoder = typeof TextDecoder != 'undefined' ? new TextDecoder('utf8') : undefined; - -/** - * Given a pointer 'idx' to a null-terminated UTF8-encoded string in the given - * array that contains uint8 values, returns a copy of that string as a - * Javascript String object. - * heapOrArray is either a regular array, or a JavaScript typed array view. - * @param {number} idx - * @param {number=} maxBytesToRead - * @return {string} - */ -function UTF8ArrayToString(heapOrArray, idx, maxBytesToRead) { - var endIdx = idx + maxBytesToRead; - var endPtr = idx; - // TextDecoder needs to know the byte length in advance, it doesn't stop on - // null terminator by itself. Also, use the length info to avoid running tiny - // strings through TextDecoder, since .subarray() allocates garbage. - // (As a tiny code save trick, compare endPtr against endIdx using a negation, - // so that undefined means Infinity) - while (heapOrArray[endPtr] && !(endPtr >= endIdx)) ++endPtr; - - if (endPtr - idx > 16 && heapOrArray.buffer && UTF8Decoder) { - return UTF8Decoder.decode(heapOrArray.subarray(idx, endPtr)); - } - var str = ''; - // If building with TextDecoder, we have already computed the string length - // above, so test loop end condition against that - while (idx < endPtr) { - // For UTF8 byte structure, see: - // http://en.wikipedia.org/wiki/UTF-8#Description - // https://www.ietf.org/rfc/rfc2279.txt - // https://tools.ietf.org/html/rfc3629 - var u0 = heapOrArray[idx++]; - if (!(u0 & 0x80)) { str += String.fromCharCode(u0); continue; } - var u1 = heapOrArray[idx++] & 63; - if ((u0 & 0xE0) == 0xC0) { str += String.fromCharCode(((u0 & 31) << 6) | u1); continue; } - var u2 = heapOrArray[idx++] & 63; - if ((u0 & 0xF0) == 0xE0) { - u0 = ((u0 & 15) << 12) | (u1 << 6) | u2; - } else { - if ((u0 & 0xF8) != 0xF0) warnOnce('Invalid UTF-8 leading byte ' + ptrToString(u0) + ' encountered when deserializing a UTF-8 string in wasm memory to a JS string!'); - u0 = ((u0 & 7) << 18) | (u1 << 12) | (u2 << 6) | (heapOrArray[idx++] & 63); - } - - if (u0 < 0x10000) { - str += String.fromCharCode(u0); - } else { - var ch = u0 - 0x10000; - str += String.fromCharCode(0xD800 | (ch >> 10), 0xDC00 | (ch & 0x3FF)); - } - } - return str; -} - -/** - * Given a pointer 'ptr' to a null-terminated UTF8-encoded string in the - * emscripten HEAP, returns a copy of that string as a Javascript String object. - * - * @param {number} ptr - * @param {number=} maxBytesToRead - An optional length that specifies the - * maximum number of bytes to read. You can omit this parameter to scan the - * string until the first \0 byte. If maxBytesToRead is passed, and the string - * at [ptr, ptr+maxBytesToReadr[ contains a null byte in the middle, then the - * string will cut short at that byte index (i.e. maxBytesToRead will not - * produce a string of exact length [ptr, ptr+maxBytesToRead[) N.B. mixing - * frequent uses of UTF8ToString() with and without maxBytesToRead may throw - * JS JIT optimizations off, so it is worth to consider consistently using one - * @return {string} - */ -function UTF8ToString(ptr, maxBytesToRead) { - return ptr ? UTF8ArrayToString(HEAPU8, ptr, maxBytesToRead) : ''; -} - -/** - * Copies the given Javascript String object 'str' to the given byte array at - * address 'outIdx', encoded in UTF8 form and null-terminated. The copy will - * require at most str.length*4+1 bytes of space in the HEAP. Use the function - * lengthBytesUTF8 to compute the exact number of bytes (excluding null - * terminator) that this function will write. - * - * @param {string} str - The Javascript string to copy. - * @param {ArrayBufferView|Array} heap - The array to copy to. Each - * index in this array is assumed - * to be one 8-byte element. - * @param {number} outIdx - The starting offset in the array to begin the copying. - * @param {number} maxBytesToWrite - The maximum number of bytes this function - * can write to the array. This count should - * include the null terminator, i.e. if - * maxBytesToWrite=1, only the null terminator - * will be written and nothing else. - * maxBytesToWrite=0 does not write any bytes - * to the output, not even the null - * terminator. - * @return {number} The number of bytes written, EXCLUDING the null terminator. - */ -function stringToUTF8Array(str, heap, outIdx, maxBytesToWrite) { - // Parameter maxBytesToWrite is not optional. Negative values, 0, null, - // undefined and false each don't write out any bytes. - if (!(maxBytesToWrite > 0)) - return 0; - - var startIdx = outIdx; - var endIdx = outIdx + maxBytesToWrite - 1; // -1 for string null terminator. - for (var i = 0; i < str.length; ++i) { - // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code - // unit, not a Unicode code point of the character! So decode - // UTF16->UTF32->UTF8. - // See http://unicode.org/faq/utf_bom.html#utf16-3 - // For UTF8 byte structure, see http://en.wikipedia.org/wiki/UTF-8#Description - // and https://www.ietf.org/rfc/rfc2279.txt - // and https://tools.ietf.org/html/rfc3629 - var u = str.charCodeAt(i); // possibly a lead surrogate - if (u >= 0xD800 && u <= 0xDFFF) { - var u1 = str.charCodeAt(++i); - u = 0x10000 + ((u & 0x3FF) << 10) | (u1 & 0x3FF); - } - if (u <= 0x7F) { - if (outIdx >= endIdx) break; - heap[outIdx++] = u; - } else if (u <= 0x7FF) { - if (outIdx + 1 >= endIdx) break; - heap[outIdx++] = 0xC0 | (u >> 6); - heap[outIdx++] = 0x80 | (u & 63); - } else if (u <= 0xFFFF) { - if (outIdx + 2 >= endIdx) break; - heap[outIdx++] = 0xE0 | (u >> 12); - heap[outIdx++] = 0x80 | ((u >> 6) & 63); - heap[outIdx++] = 0x80 | (u & 63); - } else { - if (outIdx + 3 >= endIdx) break; - if (u > 0x10FFFF) warnOnce('Invalid Unicode code point ' + ptrToString(u) + ' encountered when serializing a JS string to a UTF-8 string in wasm memory! (Valid unicode code points should be in range 0-0x10FFFF).'); - heap[outIdx++] = 0xF0 | (u >> 18); - heap[outIdx++] = 0x80 | ((u >> 12) & 63); - heap[outIdx++] = 0x80 | ((u >> 6) & 63); - heap[outIdx++] = 0x80 | (u & 63); - } - } - // Null-terminate the pointer to the buffer. - heap[outIdx] = 0; - return outIdx - startIdx; -} - -/** - * Copies the given Javascript String object 'str' to the emscripten HEAP at - * address 'outPtr', null-terminated and encoded in UTF8 form. The copy will - * require at most str.length*4+1 bytes of space in the HEAP. - * Use the function lengthBytesUTF8 to compute the exact number of bytes - * (excluding null terminator) that this function will write. - * - * @return {number} The number of bytes written, EXCLUDING the null terminator. - */ -function stringToUTF8(str, outPtr, maxBytesToWrite) { - assert(typeof maxBytesToWrite == 'number', 'stringToUTF8(str, outPtr, maxBytesToWrite) is missing the third parameter that specifies the length of the output buffer!'); - return stringToUTF8Array(str, HEAPU8,outPtr, maxBytesToWrite); -} - -/** - * Returns the number of bytes the given Javascript string takes if encoded as a - * UTF8 byte array, EXCLUDING the null terminator byte. - * - * @param {string} str - JavaScript string to operator on - * @return {number} Length, in bytes, of the UTF8 encoded string. - */ -function lengthBytesUTF8(str) { - var len = 0; - for (var i = 0; i < str.length; ++i) { - // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code - // unit, not a Unicode code point of the character! So decode - // UTF16->UTF32->UTF8. - // See http://unicode.org/faq/utf_bom.html#utf16-3 - var c = str.charCodeAt(i); // possibly a lead surrogate - if (c <= 0x7F) { - len++; - } else if (c <= 0x7FF) { - len += 2; - } else if (c >= 0xD800 && c <= 0xDFFF) { - len += 4; ++i; - } else { - len += 3; - } - } - return len; -} - -// end include: runtime_strings.js // Memory management var HEAP, @@ -612,18 +430,18 @@ function writeStackCookie() { var max = _emscripten_stack_get_end(); assert((max & 3) == 0); // If the stack ends at address zero we write our cookies 4 bytes into the - // stack. This prevents interference with the (separate) address-zero check - // below. + // stack. This prevents interference with SAFE_HEAP and ASAN which also + // monitor writes to address zero. if (max == 0) { max += 4; } // The stack grow downwards towards _emscripten_stack_get_end. // We write cookies to the final two words in the stack and detect if they are // ever overwritten. - HEAPU32[((max)>>2)] = 0x2135467; + HEAPU32[((max)>>2)] = 0x02135467; HEAPU32[(((max)+(4))>>2)] = 0x89BACDFE; // Also test the global address 0 for integrity. - HEAPU32[0] = 0x63736d65; /* 'emsc' */ + HEAPU32[((0)>>2)] = 1668509029; } function checkStackCookie() { @@ -635,11 +453,11 @@ function checkStackCookie() { } var cookie1 = HEAPU32[((max)>>2)]; var cookie2 = HEAPU32[(((max)+(4))>>2)]; - if (cookie1 != 0x2135467 || cookie2 != 0x89BACDFE) { - abort('Stack overflow! Stack cookie has been overwritten at ' + ptrToString(max) + ', expected hex dwords 0x89BACDFE and 0x2135467, but received ' + ptrToString(cookie2) + ' ' + ptrToString(cookie1)); + if (cookie1 != 0x02135467 || cookie2 != 0x89BACDFE) { + abort(`Stack overflow! Stack cookie has been overwritten at ${ptrToString(max)}, expected hex dwords 0x89BACDFE and 0x2135467, but received ${ptrToString(cookie2)} ${ptrToString(cookie1)}`); } // Also test the global address 0 for integrity. - if (HEAPU32[0] !== 0x63736d65 /* 'emsc' */) { + if (HEAPU32[((0)>>2)] != 0x63736d65 /* 'emsc' */) { abort('Runtime error: The application has corrupted its heap memory area (address zero)!'); } } @@ -663,8 +481,10 @@ var __ATPOSTRUN__ = []; // functions called after the main() is called var runtimeInitialized = false; +var runtimeKeepaliveCounter = 0; + function keepRuntimeAlive() { - return noExitRuntime; + return noExitRuntime || runtimeKeepaliveCounter > 0; } function preRun() { @@ -772,7 +592,7 @@ function addRunDependency(id) { runDependencyTracking[id] = 1; if (runDependencyWatcher === null && typeof setInterval != 'undefined') { // Check for missing dependencies every few seconds - runDependencyWatcher = setInterval(function() { + runDependencyWatcher = setInterval(() => { if (ABORT) { clearInterval(runDependencyWatcher); runDependencyWatcher = null; @@ -873,7 +693,6 @@ var FS = { mkdev: function() { FS.error() }, registerDevice: function() { FS.error() }, analyzePath: function() { FS.error() }, - loadFilesFromDB: function() { FS.error() }, ErrnoError: function ErrnoError() { FS.error() }, }; @@ -912,6 +731,8 @@ function createExportWrapper(name, fixedasm) { }; } +// include: runtime_exceptions.js +// end include: runtime_exceptions.js var wasmBinaryFile; wasmBinaryFile = 'output.wasm'; if (!isDataURI(wasmBinaryFile)) { @@ -933,37 +754,88 @@ function getBinary(file) { } } -function getBinaryPromise() { - // If we don't have the binary yet, try to to load it asynchronously. +function getBinaryPromise(binaryFile) { + // If we don't have the binary yet, try to load it asynchronously. // Fetch has some additional restrictions over XHR, like it can't be used on a file:// url. // See https://github.com/github/fetch/pull/92#issuecomment-140665932 // Cordova or Electron apps are typically loaded from a file:// url. // So use fetch if it is available and the url is not a file, otherwise fall back to XHR. if (!wasmBinary && (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER)) { if (typeof fetch == 'function' - && !isFileURI(wasmBinaryFile) + && !isFileURI(binaryFile) ) { - return fetch(wasmBinaryFile, { credentials: 'same-origin' }).then(function(response) { + return fetch(binaryFile, { credentials: 'same-origin' }).then((response) => { if (!response['ok']) { - throw "failed to load wasm binary file at '" + wasmBinaryFile + "'"; + throw "failed to load wasm binary file at '" + binaryFile + "'"; } return response['arrayBuffer'](); - }).catch(function () { - return getBinary(wasmBinaryFile); - }); + }).catch(() => getBinary(binaryFile)); } else { if (readAsync) { // fetch is not available or url is file => try XHR (readAsync uses XHR internally) - return new Promise(function(resolve, reject) { - readAsync(wasmBinaryFile, function(response) { resolve(new Uint8Array(/** @type{!ArrayBuffer} */(response))) }, reject) + return new Promise((resolve, reject) => { + readAsync(binaryFile, (response) => resolve(new Uint8Array(/** @type{!ArrayBuffer} */(response))), reject) }); } } } // Otherwise, getBinary should be able to get it synchronously - return Promise.resolve().then(function() { return getBinary(wasmBinaryFile); }); + return Promise.resolve().then(() => getBinary(binaryFile)); +} + +function instantiateArrayBuffer(binaryFile, imports, receiver) { + return getBinaryPromise(binaryFile).then((binary) => { + return WebAssembly.instantiate(binary, imports); + }).then((instance) => { + return instance; + }).then(receiver, (reason) => { + err('failed to asynchronously prepare wasm: ' + reason); + + // Warn on some common problems. + if (isFileURI(wasmBinaryFile)) { + err('warning: Loading from a file URI (' + wasmBinaryFile + ') is not supported in most browsers. See https://emscripten.org/docs/getting_started/FAQ.html#how-do-i-run-a-local-webserver-for-testing-why-does-my-program-stall-in-downloading-or-preparing'); + } + abort(reason); + }); +} + +function instantiateAsync(binary, binaryFile, imports, callback) { + if (!binary && + typeof WebAssembly.instantiateStreaming == 'function' && + !isDataURI(binaryFile) && + // Don't use streaming for file:// delivered objects in a webview, fetch them synchronously. + !isFileURI(binaryFile) && + // Avoid instantiateStreaming() on Node.js environment for now, as while + // Node.js v18.1.0 implements it, it does not have a full fetch() + // implementation yet. + // + // Reference: + // https://github.com/emscripten-core/emscripten/pull/16917 + !ENVIRONMENT_IS_NODE && + typeof fetch == 'function') { + return fetch(binaryFile, { credentials: 'same-origin' }).then((response) => { + // Suppress closure warning here since the upstream definition for + // instantiateStreaming only allows Promise rather than + // an actual Response. + // TODO(https://github.com/google/closure-compiler/pull/3913): Remove if/when upstream closure is fixed. + /** @suppress {checkTypes} */ + var result = WebAssembly.instantiateStreaming(response, imports); + + return result.then( + callback, + function(reason) { + // We expect the most common failure cause to be a bad MIME type for the binary, + // in which case falling back to ArrayBuffer instantiation should work. + err('wasm streaming compile failed: ' + reason); + err('falling back to ArrayBuffer instantiation'); + return instantiateArrayBuffer(binaryFile, imports, callback); + }); + }); + } else { + return instantiateArrayBuffer(binaryFile, imports, callback); + } } // Create the wasm instance. @@ -997,7 +869,7 @@ function createWasm() { addOnInit(Module['asm']['__wasm_call_ctors']); removeRunDependency('wasm-instantiate'); - + return exports; } // wait for the pthread pool (if any) addRunDependency('wasm-instantiate'); @@ -1013,78 +885,27 @@ function createWasm() { assert(Module === trueModule, 'the Module object should not be replaced during async compilation - perhaps the order of HTML elements is wrong?'); trueModule = null; // TODO: Due to Closure regression https://github.com/google/closure-compiler/issues/3193, the above line no longer optimizes out down to the following line. - // When the regression is fixed, can restore the above USE_PTHREADS-enabled path. + // When the regression is fixed, can restore the above PTHREADS-enabled path. receiveInstance(result['instance']); } - function instantiateArrayBuffer(receiver) { - return getBinaryPromise().then(function(binary) { - return WebAssembly.instantiate(binary, info); - }).then(function (instance) { - return instance; - }).then(receiver, function(reason) { - err('failed to asynchronously prepare wasm: ' + reason); - - // Warn on some common problems. - if (isFileURI(wasmBinaryFile)) { - err('warning: Loading from a file URI (' + wasmBinaryFile + ') is not supported in most browsers. See https://emscripten.org/docs/getting_started/FAQ.html#how-do-i-run-a-local-webserver-for-testing-why-does-my-program-stall-in-downloading-or-preparing'); - } - abort(reason); - }); - } - - function instantiateAsync() { - if (!wasmBinary && - typeof WebAssembly.instantiateStreaming == 'function' && - !isDataURI(wasmBinaryFile) && - // Don't use streaming for file:// delivered objects in a webview, fetch them synchronously. - !isFileURI(wasmBinaryFile) && - // Avoid instantiateStreaming() on Node.js environment for now, as while - // Node.js v18.1.0 implements it, it does not have a full fetch() - // implementation yet. - // - // Reference: - // https://github.com/emscripten-core/emscripten/pull/16917 - !ENVIRONMENT_IS_NODE && - typeof fetch == 'function') { - return fetch(wasmBinaryFile, { credentials: 'same-origin' }).then(function(response) { - // Suppress closure warning here since the upstream definition for - // instantiateStreaming only allows Promise rather than - // an actual Response. - // TODO(https://github.com/google/closure-compiler/pull/3913): Remove if/when upstream closure is fixed. - /** @suppress {checkTypes} */ - var result = WebAssembly.instantiateStreaming(response, info); - - return result.then( - receiveInstantiationResult, - function(reason) { - // We expect the most common failure cause to be a bad MIME type for the binary, - // in which case falling back to ArrayBuffer instantiation should work. - err('wasm streaming compile failed: ' + reason); - err('falling back to ArrayBuffer instantiation'); - return instantiateArrayBuffer(receiveInstantiationResult); - }); - }); - } else { - return instantiateArrayBuffer(receiveInstantiationResult); - } - } - // User shell pages can write their own Module.instantiateWasm = function(imports, successCallback) callback - // to manually instantiate the Wasm module themselves. This allows pages to run the instantiation parallel - // to any other async startup actions they are performing. - // Also pthreads and wasm workers initialize the wasm instance through this path. + // to manually instantiate the Wasm module themselves. This allows pages to + // run the instantiation parallel to any other async startup actions they are + // performing. + // Also pthreads and wasm workers initialize the wasm instance through this + // path. if (Module['instantiateWasm']) { + try { - var exports = Module['instantiateWasm'](info, receiveInstance); - return exports; + return Module['instantiateWasm'](info, receiveInstance); } catch(e) { err('Module.instantiateWasm callback failed with error: ' + e); return false; } } - instantiateAsync(); + instantiateAsync(wasmBinary, wasmBinaryFile, info, receiveInstantiationResult); return {}; // no exports yet; we'll fill them in later } @@ -1161,7 +982,7 @@ function missingLibrarySymbol(sym) { } }); } - // Any symbol that is not included from the JS libary is also (by definttion) + // Any symbol that is not included from the JS libary is also (by definition) // not exported on the Module object. unexportedRuntimeSymbol(sym); } @@ -1181,6 +1002,13 @@ function unexportedRuntimeSymbol(sym) { } } +// Used by XXXXX_DEBUG settings to output debug messages. +function dbg(text) { + // TODO(sbc): Make this configurable somehow. Its not always convenient for + // logging to show up as warnings. + console.warn.apply(console, arguments); +} + // end include: runtime_debug.js // === Body === @@ -1190,7 +1018,7 @@ function unexportedRuntimeSymbol(sym) { /** @constructor */ function ExitStatus(status) { this.name = 'ExitStatus'; - this.message = 'Program terminated with exit(' + status + ')'; + this.message = `Program terminated with exit(${status})`; this.status = status; } @@ -1207,20 +1035,19 @@ function unexportedRuntimeSymbol(sym) { * @param {string} type */ function getValue(ptr, type = 'i8') { - if (type.endsWith('*')) type = '*'; - 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)]; - case '*': return HEAPU32[((ptr)>>2)]; - default: abort('invalid type for getValue: ' + type); - } - return null; + if (type.endsWith('*')) type = '*'; + 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': abort('to do getValue(i64) use WASM_BIGINT'); + case 'float': return HEAPF32[((ptr)>>2)]; + case 'double': return HEAPF64[((ptr)>>3)]; + case '*': return HEAPU32[((ptr)>>2)]; + default: abort(`invalid type for getValue: ${type}`); } + } function ptrToString(ptr) { assert(typeof ptr === 'number'); @@ -1234,19 +1061,19 @@ function unexportedRuntimeSymbol(sym) { * @param {string} type */ function setValue(ptr, value, type = 'i8') { - if (type.endsWith('*')) type = '*'; - switch (type) { - case 'i1': HEAP8[((ptr)>>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.0 ? (tempDouble > 0.0 ? ((Math.min((+(Math.floor((tempDouble)/4294967296.0))), 4294967295.0))|0)>>>0 : (~~((+(Math.ceil((tempDouble - +(((~~(tempDouble)))>>>0))/4294967296.0)))))>>>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; - case '*': HEAPU32[((ptr)>>2)] = value; break; - default: abort('invalid type for setValue: ' + type); - } + if (type.endsWith('*')) type = '*'; + switch (type) { + case 'i1': HEAP8[((ptr)>>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': abort('to do setValue(i64) use WASM_BIGINT'); + case 'float': HEAPF32[((ptr)>>2)] = value; break; + case 'double': HEAPF64[((ptr)>>3)] = value; break; + case '*': HEAPU32[((ptr)>>2)] = value; break; + default: abort(`invalid type for setValue: ${type}`); } + } function warnOnce(text) { if (!warnOnce.shown) warnOnce.shown = {}; @@ -1258,30 +1085,30 @@ function unexportedRuntimeSymbol(sym) { } function __emscripten_fetch_free(id) { - var xhr = Fetch.xhrs[id]; - if (xhr) { - delete Fetch.xhrs[id]; - // check if fetch is still in progress and should be aborted - if (xhr.readyState > 0 && xhr.readyState < 4) { - xhr.abort(); - } + if (Fetch.xhrs.has(id)) { + var xhr = Fetch.xhrs.get(id); + Fetch.xhrs.free(id); + // check if fetch is still in progress and should be aborted + if (xhr.readyState > 0 && xhr.readyState < 4) { + xhr.abort(); } + } } function readI53FromI64(ptr) { return HEAPU32[ptr>>2] + HEAP32[ptr+4>>2] * 4294967296; } - function __isLeapYear(year) { + function isLeapYear(year) { return year%4 === 0 && (year%100 !== 0 || year%400 === 0); } - var __MONTH_DAYS_LEAP_CUMULATIVE = [0,31,60,91,121,152,182,213,244,274,305,335]; + var MONTH_DAYS_LEAP_CUMULATIVE = [0,31,60,91,121,152,182,213,244,274,305,335]; - var __MONTH_DAYS_REGULAR_CUMULATIVE = [0,31,59,90,120,151,181,212,243,273,304,334]; - function __yday_from_date(date) { - var isLeapYear = __isLeapYear(date.getFullYear()); - var monthDaysCumulative = (isLeapYear ? __MONTH_DAYS_LEAP_CUMULATIVE : __MONTH_DAYS_REGULAR_CUMULATIVE); + var MONTH_DAYS_REGULAR_CUMULATIVE = [0,31,59,90,120,151,181,212,243,273,304,334]; + function ydayFromDate(date) { + var leap = isLeapYear(date.getFullYear()); + var monthDaysCumulative = (leap ? MONTH_DAYS_LEAP_CUMULATIVE : MONTH_DAYS_REGULAR_CUMULATIVE); var yday = monthDaysCumulative[date.getMonth()] + date.getDate() - 1; // -1 since it's days since Jan 1 return yday; @@ -1296,7 +1123,7 @@ function unexportedRuntimeSymbol(sym) { HEAP32[(((tmPtr)+(20))>>2)] = date.getFullYear()-1900; HEAP32[(((tmPtr)+(24))>>2)] = date.getDay(); - var yday = __yday_from_date(date)|0; + var yday = ydayFromDate(date)|0; HEAP32[(((tmPtr)+(28))>>2)] = yday; HEAP32[(((tmPtr)+(36))>>2)] = -(date.getTimezoneOffset() * 60); @@ -1308,10 +1135,83 @@ function unexportedRuntimeSymbol(sym) { HEAP32[(((tmPtr)+(32))>>2)] = dst; } - function allocateUTF8(str) { + function lengthBytesUTF8(str) { + var len = 0; + for (var i = 0; i < str.length; ++i) { + // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code + // unit, not a Unicode code point of the character! So decode + // UTF16->UTF32->UTF8. + // See http://unicode.org/faq/utf_bom.html#utf16-3 + var c = str.charCodeAt(i); // possibly a lead surrogate + if (c <= 0x7F) { + len++; + } else if (c <= 0x7FF) { + len += 2; + } else if (c >= 0xD800 && c <= 0xDFFF) { + len += 4; ++i; + } else { + len += 3; + } + } + return len; + } + + function stringToUTF8Array(str, heap, outIdx, maxBytesToWrite) { + assert(typeof str === 'string'); + // Parameter maxBytesToWrite is not optional. Negative values, 0, null, + // undefined and false each don't write out any bytes. + if (!(maxBytesToWrite > 0)) + return 0; + + var startIdx = outIdx; + var endIdx = outIdx + maxBytesToWrite - 1; // -1 for string null terminator. + for (var i = 0; i < str.length; ++i) { + // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code + // unit, not a Unicode code point of the character! So decode + // UTF16->UTF32->UTF8. + // See http://unicode.org/faq/utf_bom.html#utf16-3 + // For UTF8 byte structure, see http://en.wikipedia.org/wiki/UTF-8#Description + // and https://www.ietf.org/rfc/rfc2279.txt + // and https://tools.ietf.org/html/rfc3629 + var u = str.charCodeAt(i); // possibly a lead surrogate + if (u >= 0xD800 && u <= 0xDFFF) { + var u1 = str.charCodeAt(++i); + u = 0x10000 + ((u & 0x3FF) << 10) | (u1 & 0x3FF); + } + if (u <= 0x7F) { + if (outIdx >= endIdx) break; + heap[outIdx++] = u; + } else if (u <= 0x7FF) { + if (outIdx + 1 >= endIdx) break; + heap[outIdx++] = 0xC0 | (u >> 6); + heap[outIdx++] = 0x80 | (u & 63); + } else if (u <= 0xFFFF) { + if (outIdx + 2 >= endIdx) break; + heap[outIdx++] = 0xE0 | (u >> 12); + heap[outIdx++] = 0x80 | ((u >> 6) & 63); + heap[outIdx++] = 0x80 | (u & 63); + } else { + if (outIdx + 3 >= endIdx) break; + if (u > 0x10FFFF) warnOnce('Invalid Unicode code point ' + ptrToString(u) + ' encountered when serializing a JS string to a UTF-8 string in wasm memory! (Valid unicode code points should be in range 0-0x10FFFF).'); + heap[outIdx++] = 0xF0 | (u >> 18); + heap[outIdx++] = 0x80 | ((u >> 12) & 63); + heap[outIdx++] = 0x80 | ((u >> 6) & 63); + heap[outIdx++] = 0x80 | (u & 63); + } + } + // Null-terminate the pointer to the buffer. + heap[outIdx] = 0; + return outIdx - startIdx; + } + function stringToUTF8(str, outPtr, maxBytesToWrite) { + assert(typeof maxBytesToWrite == 'number', 'stringToUTF8(str, outPtr, maxBytesToWrite) is missing the third parameter that specifies the length of the output buffer!'); + return stringToUTF8Array(str, HEAPU8,outPtr, maxBytesToWrite); + } + + function stringToNewUTF8(str) { var size = lengthBytesUTF8(str) + 1; var ret = _malloc(size); - if (ret) stringToUTF8Array(str, HEAP8, ret, size); + if (ret) stringToUTF8(str, ret, size); return ret; } function __tzset_js(timezone, daylight, tzname) { @@ -1342,8 +1242,8 @@ function unexportedRuntimeSymbol(sym) { }; var winterName = extractZone(winter); var summerName = extractZone(summer); - var winterNamePtr = allocateUTF8(winterName); - var summerNamePtr = allocateUTF8(summerName); + var winterNamePtr = stringToNewUTF8(winterName); + var summerNamePtr = stringToNewUTF8(summerName); if (summerOffset < winterOffset) { // Northern hemisphere HEAPU32[((tzname)>>2)] = winterNamePtr; @@ -1433,6 +1333,11 @@ function unexportedRuntimeSymbol(sym) { h.target.removeEventListener(h.eventTypeString, h.eventListenerFunc, h.useCapture); JSEvents.eventHandlers.splice(i, 1); },registerOrRemoveHandler:function(eventHandler) { + if (!eventHandler.target) { + err('registerOrRemoveHandler: the target element for event handler registration does not exist, when processing the following event handler registration:'); + console.dir(eventHandler); + return -4; + } var jsEventHandler = function jsEventHandler(event) { // Increment nesting count for the event handler. ++JSEvents.inEventHandler; @@ -1460,6 +1365,7 @@ function unexportedRuntimeSymbol(sym) { } } } + return 0; },getNodeNameForTarget:function(target) { if (!target) return ''; if (target == window) return '#window'; @@ -1473,6 +1379,80 @@ function unexportedRuntimeSymbol(sym) { ; }}; + var UTF8Decoder = typeof TextDecoder != 'undefined' ? new TextDecoder('utf8') : undefined; + + /** + * Given a pointer 'idx' to a null-terminated UTF8-encoded string in the given + * array that contains uint8 values, returns a copy of that string as a + * Javascript String object. + * heapOrArray is either a regular array, or a JavaScript typed array view. + * @param {number} idx + * @param {number=} maxBytesToRead + * @return {string} + */ + function UTF8ArrayToString(heapOrArray, idx, maxBytesToRead) { + var endIdx = idx + maxBytesToRead; + var endPtr = idx; + // TextDecoder needs to know the byte length in advance, it doesn't stop on + // null terminator by itself. Also, use the length info to avoid running tiny + // strings through TextDecoder, since .subarray() allocates garbage. + // (As a tiny code save trick, compare endPtr against endIdx using a negation, + // so that undefined means Infinity) + while (heapOrArray[endPtr] && !(endPtr >= endIdx)) ++endPtr; + + if (endPtr - idx > 16 && heapOrArray.buffer && UTF8Decoder) { + return UTF8Decoder.decode(heapOrArray.subarray(idx, endPtr)); + } + var str = ''; + // If building with TextDecoder, we have already computed the string length + // above, so test loop end condition against that + while (idx < endPtr) { + // For UTF8 byte structure, see: + // http://en.wikipedia.org/wiki/UTF-8#Description + // https://www.ietf.org/rfc/rfc2279.txt + // https://tools.ietf.org/html/rfc3629 + var u0 = heapOrArray[idx++]; + if (!(u0 & 0x80)) { str += String.fromCharCode(u0); continue; } + var u1 = heapOrArray[idx++] & 63; + if ((u0 & 0xE0) == 0xC0) { str += String.fromCharCode(((u0 & 31) << 6) | u1); continue; } + var u2 = heapOrArray[idx++] & 63; + if ((u0 & 0xF0) == 0xE0) { + u0 = ((u0 & 15) << 12) | (u1 << 6) | u2; + } else { + if ((u0 & 0xF8) != 0xF0) warnOnce('Invalid UTF-8 leading byte ' + ptrToString(u0) + ' encountered when deserializing a UTF-8 string in wasm memory to a JS string!'); + u0 = ((u0 & 7) << 18) | (u1 << 12) | (u2 << 6) | (heapOrArray[idx++] & 63); + } + + if (u0 < 0x10000) { + str += String.fromCharCode(u0); + } else { + var ch = u0 - 0x10000; + str += String.fromCharCode(0xD800 | (ch >> 10), 0xDC00 | (ch & 0x3FF)); + } + } + return str; + } + + + /** + * Given a pointer 'ptr' to a null-terminated UTF8-encoded string in the + * emscripten HEAP, returns a copy of that string as a Javascript String object. + * + * @param {number} ptr + * @param {number=} maxBytesToRead - An optional length that specifies the + * maximum number of bytes to read. You can omit this parameter to scan the + * string until the first 0 byte. If maxBytesToRead is passed, and the string + * at [ptr, ptr+maxBytesToReadr[ contains a null byte in the middle, then the + * string will cut short at that byte index (i.e. maxBytesToRead will not + * produce a string of exact length [ptr, ptr+maxBytesToRead[) N.B. mixing + * frequent uses of UTF8ToString() with and without maxBytesToRead may throw + * JS JIT optimizations off, so it is worth to consider consistently using one + * @return {string} + */ + function UTF8ToString(ptr, maxBytesToRead) { + assert(typeof ptr == 'number'); + return ptr ? UTF8ArrayToString(HEAPU8, ptr, maxBytesToRead) : ''; + } function maybeCStringToJsString(cString) { // "cString > 2" checks if the input is a number, and isn't of the special // values we accept here, EMSCRIPTEN_EVENT_TARGET_* (which map to 0, 1, 2). @@ -1540,13 +1520,14 @@ function unexportedRuntimeSymbol(sym) { function emscripten_realloc_buffer(size) { var b = wasmMemory.buffer; + var pages = (size - b.byteLength + 65535) >>> 16; try { // round size grow request up to wasm page size (fixed 64KB per spec) - wasmMemory.grow((size - b.byteLength + 65535) >>> 16); // .grow() takes a delta compared to the previous size + wasmMemory.grow(pages); // .grow() takes a delta compared to the previous size updateMemoryViews(); return 1 /*success*/; } catch(e) { - err('emscripten_realloc_buffer: Attempted to grow heap from ' + b.byteLength + ' bytes to ' + size + ' bytes, but got error: ' + e); + err(`emscripten_realloc_buffer: Attempted to grow heap from ${b.byteLength} bytes to ${size} bytes, but got error: ${e}`); } // implicit 0 return to save code size (caller will cast "undefined" into 0 // anyhow) @@ -1579,11 +1560,11 @@ function unexportedRuntimeSymbol(sym) { // (the wasm binary specifies it, so if we tried, we'd fail anyhow). var maxHeapSize = getHeapMax(); if (requestedSize > maxHeapSize) { - err('Cannot enlarge memory, asked to go up to ' + requestedSize + ' bytes, but the limit is ' + maxHeapSize + ' bytes!'); + err(`Cannot enlarge memory, asked to go up to ${requestedSize} bytes, but the limit is ${maxHeapSize} bytes!`); return false; } - let alignUp = (x, multiple) => x + (multiple - x % multiple) % multiple; + var alignUp = (x, multiple) => x + (multiple - x % multiple) % multiple; // Loop through potential heap size increases. If we attempt a too eager // reservation that fails, cut down on the attempted size and reserve a @@ -1601,7 +1582,7 @@ function unexportedRuntimeSymbol(sym) { return true; } } - err('Failed to grow the heap from ' + oldSize + ' bytes to ' + newSize + ' bytes, not enough memory!'); + err(`Failed to grow the heap from ${oldSize} bytes to ${newSize} bytes, not enough memory!`); return false; } @@ -1646,6 +1627,7 @@ function unexportedRuntimeSymbol(sym) { } + function registerMouseEventCallback(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) { if (!JSEvents.mouseEvent) JSEvents.mouseEvent = _malloc( 72 ); target = findEventTarget(target); @@ -1665,15 +1647,15 @@ function unexportedRuntimeSymbol(sym) { handlerFunc: mouseEventHandlerFunc, useCapture: useCapture }; - JSEvents.registerOrRemoveHandler(eventHandler); + return JSEvents.registerOrRemoveHandler(eventHandler); } function _emscripten_set_click_callback_on_thread(target, userData, useCapture, callbackfunc, targetThread) { - registerMouseEventCallback(target, userData, useCapture, callbackfunc, 4, "click", targetThread); - return 0; + return registerMouseEventCallback(target, userData, useCapture, callbackfunc, 4, "click", targetThread); } + function registerUiEventCallback(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) { if (!JSEvents.uiEvent) JSEvents.uiEvent = _malloc( 36 ); @@ -1712,17 +1694,40 @@ function unexportedRuntimeSymbol(sym) { handlerFunc: uiEventHandlerFunc, useCapture: useCapture }; - JSEvents.registerOrRemoveHandler(eventHandler); + return JSEvents.registerOrRemoveHandler(eventHandler); } function _emscripten_set_resize_callback_on_thread(target, userData, useCapture, callbackfunc, targetThread) { - registerUiEventCallback(target, userData, useCapture, callbackfunc, 10, "resize", targetThread); - return 0; + return registerUiEventCallback(target, userData, useCapture, callbackfunc, 10, "resize", targetThread); } - var Fetch = {xhrs:{},setu64:function(addr, val) { - HEAPU32[addr >> 2] = val; - HEAPU32[addr + 4 >> 2] = (val / 4294967296)|0; - },openDatabase:function(dbname, dbversion, onsuccess, onerror) { + + + /** @constructor */ + function HandleAllocator() { + // Reserve slot 0 so that 0 is always an invalid handle + this.allocated = [undefined]; + this.freelist = []; + this.get = function(id) { + assert(this.allocated[id] !== undefined, `invalid handle: ${id}`); + return this.allocated[id]; + }; + this.has = function(id) { + return this.allocated[id] !== undefined; + }; + this.allocate = function(handle) { + var id = this.freelist.pop() || this.allocated.length; + this.allocated[id] = handle; + return id; + }; + this.free = function(id) { + assert(this.allocated[id] !== undefined); + // Set the slot to `undefined` rather than using `delete` here since + // apparently arrays with holes in them can be less efficient. + this.allocated[id] = undefined; + this.freelist.push(id); + }; + } + var Fetch = {openDatabase:function(dbname, dbversion, onsuccess, onerror) { try { var openRequest = indexedDB.open(dbname, dbversion); } catch (e) { return onerror(e); } @@ -1736,7 +1741,8 @@ function unexportedRuntimeSymbol(sym) { }; openRequest.onsuccess = (event) => onsuccess(event.target.result); openRequest.onerror = (error) => onerror(error); - },staticInit:function() { + },init:function() { + Fetch.xhrs = new HandleAllocator(); var onsuccess = (db) => { Fetch.dbInstance = db; removeRunDependency('library_fetch_init'); @@ -1752,7 +1758,7 @@ function unexportedRuntimeSymbol(sym) { }}; function fetchXHR(fetch, onsuccess, onerror, onprogress, onreadystatechange) { - var url = HEAPU32[fetch + 8 >> 2]; + var url = HEAPU32[(((fetch)+(8))>>2)]; if (!url) { onerror(fetch, 0, 'no url specified!'); return; @@ -1760,33 +1766,26 @@ function unexportedRuntimeSymbol(sym) { var url_ = UTF8ToString(url); var fetch_attr = fetch + 112; - var requestMethod = UTF8ToString(fetch_attr); + var requestMethod = UTF8ToString(fetch_attr + 0); if (!requestMethod) requestMethod = 'GET'; - var userData = HEAPU32[fetch + 4 >> 2]; - var fetchAttributes = HEAPU32[fetch_attr + 52 >> 2]; - var timeoutMsecs = HEAPU32[fetch_attr + 56 >> 2]; - var withCredentials = !!HEAPU32[fetch_attr + 60 >> 2]; - var destinationPath = HEAPU32[fetch_attr + 64 >> 2]; - var userName = HEAPU32[fetch_attr + 68 >> 2]; - var password = HEAPU32[fetch_attr + 72 >> 2]; - var requestHeaders = HEAPU32[fetch_attr + 76 >> 2]; - var overriddenMimeType = HEAPU32[fetch_attr + 80 >> 2]; - var dataPtr = HEAPU32[fetch_attr + 84 >> 2]; - var dataLength = HEAPU32[fetch_attr + 88 >> 2]; - + var timeoutMsecs = HEAPU32[(((fetch_attr)+(56))>>2)]; + var userName = HEAPU32[(((fetch_attr)+(68))>>2)]; + var password = HEAPU32[(((fetch_attr)+(72))>>2)]; + var requestHeaders = HEAPU32[(((fetch_attr)+(76))>>2)]; + var overriddenMimeType = HEAPU32[(((fetch_attr)+(80))>>2)]; + var dataPtr = HEAPU32[(((fetch_attr)+(84))>>2)]; + var dataLength = HEAPU32[(((fetch_attr)+(88))>>2)]; + + var fetchAttributes = HEAPU32[(((fetch_attr)+(52))>>2)]; var fetchAttrLoadToMemory = !!(fetchAttributes & 1); var fetchAttrStreamData = !!(fetchAttributes & 2); - var fetchAttrPersistFile = !!(fetchAttributes & 4); - var fetchAttrAppend = !!(fetchAttributes & 8); - var fetchAttrReplace = !!(fetchAttributes & 16); var fetchAttrSynchronous = !!(fetchAttributes & 64); - var fetchAttrWaitable = !!(fetchAttributes & 128); var userNameStr = userName ? UTF8ToString(userName) : undefined; var passwordStr = password ? UTF8ToString(password) : undefined; var xhr = new XMLHttpRequest(); - xhr.withCredentials = withCredentials; + xhr.withCredentials = !!HEAPU8[(((fetch_attr)+(60))>>0)];; xhr.open(requestMethod, url_, !fetchAttrSynchronous, userNameStr, passwordStr); if (!fetchAttrSynchronous) xhr.timeout = timeoutMsecs; // XHR timeout field is only accessible in async XHRs, and must be set after .open() but before .send(). xhr.url_ = url_; // Save the url for debugging purposes (and for comparing to the responseURL that server side advertised) @@ -1799,9 +1798,9 @@ function unexportedRuntimeSymbol(sym) { } if (requestHeaders) { for (;;) { - var key = HEAPU32[requestHeaders >> 2]; + var key = HEAPU32[((requestHeaders)>>2)]; if (!key) break; - var value = HEAPU32[requestHeaders + 4 >> 2]; + var value = HEAPU32[(((requestHeaders)+(4))>>2)]; if (!value) break; requestHeaders += 8; var keyStr = UTF8ToString(key); @@ -1809,8 +1808,9 @@ function unexportedRuntimeSymbol(sym) { xhr.setRequestHeader(keyStr, valueStr); } } - var id = HEAPU32[fetch + 0 >> 2]; - Fetch.xhrs[id] = xhr; + + var id = Fetch.xhrs.allocate(xhr); + HEAPU32[((fetch)>>2)] = id; var data = (dataPtr && dataLength) ? HEAPU8.slice(dataPtr, dataPtr + dataLength) : null; // TODO: Support specifying custom headers to the request. @@ -1831,14 +1831,14 @@ function unexportedRuntimeSymbol(sym) { HEAPU8.set(new Uint8Array(/** @type{Array} */(xhr.response)), ptr); } HEAPU32[fetch + 12 >> 2] = ptr; - Fetch.setu64(fetch + 16, ptrLen); - Fetch.setu64(fetch + 24, 0); + writeI53ToI64(fetch + 16, ptrLen); + writeI53ToI64(fetch + 24, 0); var len = xhr.response ? xhr.response.byteLength : 0; if (len) { // If the final XHR.onload handler receives the bytedata to compute total length, report that, // otherwise don't write anything out here, which will retain the latest byte size reported in // the most recent XHR.onprogress handler. - Fetch.setu64(fetch + 32, len); + writeI53ToI64(fetch + 32, len); } HEAPU16[fetch + 40 >> 1] = xhr.readyState; HEAPU16[fetch + 42 >> 1] = xhr.status; @@ -1847,7 +1847,7 @@ function unexportedRuntimeSymbol(sym) { xhr.onload = (e) => { // check if xhr was aborted by user and don't try to call back - if (!(id in Fetch.xhrs)) { + if (!Fetch.xhrs.has(id)) { return; } saveResponseAndStatus(); @@ -1859,7 +1859,7 @@ function unexportedRuntimeSymbol(sym) { }; xhr.onerror = (e) => { // check if xhr was aborted by user and don't try to call back - if (!(id in Fetch.xhrs)) { + if (!Fetch.xhrs.has(id)) { return; } saveResponseAndStatus(); @@ -1867,14 +1867,14 @@ function unexportedRuntimeSymbol(sym) { }; xhr.ontimeout = (e) => { // check if xhr was aborted by user and don't try to call back - if (!(id in Fetch.xhrs)) { + if (!Fetch.xhrs.has(id)) { return; } if (onerror) onerror(fetch, xhr, e); }; xhr.onprogress = (e) => { // check if xhr was aborted by user and don't try to call back - if (!(id in Fetch.xhrs)) { + if (!Fetch.xhrs.has(id)) { return; } var ptrLen = (fetchAttrLoadToMemory && fetchAttrStreamData && xhr.response) ? xhr.response.byteLength : 0; @@ -1886,9 +1886,9 @@ function unexportedRuntimeSymbol(sym) { HEAPU8.set(new Uint8Array(/** @type{Array} */(xhr.response)), ptr); } HEAPU32[fetch + 12 >> 2] = ptr; - Fetch.setu64(fetch + 16, ptrLen); - Fetch.setu64(fetch + 24, e.loaded - ptrLen); - Fetch.setu64(fetch + 32, e.total); + writeI53ToI64(fetch + 16, ptrLen); + writeI53ToI64(fetch + 24, e.loaded - ptrLen); + writeI53ToI64(fetch + 32, e.total); HEAPU16[fetch + 40 >> 1] = xhr.readyState; // If loading files from a source that does not give HTTP status code, assume success if we get data bytes if (xhr.readyState >= 3 && xhr.status === 0 && e.loaded > 0) xhr.status = 200; @@ -1901,7 +1901,7 @@ function unexportedRuntimeSymbol(sym) { }; xhr.onreadystatechange = (e) => { // check if xhr was aborted by user and don't try to call back - if (!(id in Fetch.xhrs)) { + if (!Fetch.xhrs.has(id)) { return; } @@ -1930,11 +1930,56 @@ function unexportedRuntimeSymbol(sym) { checkStackCookie(); if (e instanceof WebAssembly.RuntimeError) { if (_emscripten_stack_get_current() <= 0) { - err('Stack overflow detected. You can try increasing -sSTACK_SIZE (currently set to ' + 65536 + ')'); + err('Stack overflow detected. You can try increasing -sSTACK_SIZE (currently set to 65536)'); } } quit_(1, e); } + + + var SYSCALLS = {varargs:undefined,get:function() { + assert(SYSCALLS.varargs != undefined); + SYSCALLS.varargs += 4; + var ret = HEAP32[(((SYSCALLS.varargs)-(4))>>2)]; + return ret; + },getStr:function(ptr) { + var ret = UTF8ToString(ptr); + return ret; + }}; + function _proc_exit(code) { + EXITSTATUS = code; + if (!keepRuntimeAlive()) { + if (Module['onExit']) Module['onExit'](code); + ABORT = true; + } + quit_(code, new ExitStatus(code)); + } + /** @suppress {duplicate } */ + /** @param {boolean|number=} implicit */ + function exitJS(status, implicit) { + EXITSTATUS = status; + + checkUnflushedContent(); + + // if exit() was called explicitly, warn the user if the runtime isn't actually being shut down + if (keepRuntimeAlive() && !implicit) { + var msg = `program exited (with status: ${status}), but keepRuntimeAlive() is set (counter=${runtimeKeepaliveCounter}) due to an async operation, so halting execution but not exiting the runtime or preventing further async execution (you can use emscripten_force_exit, if you want to force a true shutdown)`; + err(msg); + } + + _proc_exit(status); + } + var _exit = exitJS; + + function maybeExit() { + if (!keepRuntimeAlive()) { + try { + _exit(EXITSTATUS); + } catch (e) { + handleException(e); + } + } + } function callUserCallback(func) { if (ABORT) { err('user callback triggered after runtime exited or application aborted. Ignoring.'); @@ -1942,11 +1987,24 @@ function unexportedRuntimeSymbol(sym) { } try { func(); + maybeExit(); } catch (e) { handleException(e); } } + + function readI53FromU64(ptr) { + return HEAPU32[ptr>>2] + HEAPU32[ptr+4>>2] * 4294967296; + } + function writeI53ToI64(ptr, num) { + HEAPU32[ptr>>2] = num; + HEAPU32[ptr+4>>2] = (num - HEAPU32[ptr>>2])/4294967296; + var deserialized = (num >= 0) ? readI53FromU64(ptr) : readI53FromI64(ptr); + if (deserialized != num) warnOnce('writeI53ToI64() out of range: serialized JS Number ' + num + ' to Wasm heap as bytes lo=' + ptrToString(HEAPU32[ptr>>2]) + ', hi=' + ptrToString(HEAPU32[ptr+4>>2]) + ', which deserializes back to ' + deserialized + ' instead!'); + } + + function fetchCacheData(/** @type {IDBDatabase} */ db, fetch, data, onsuccess, onerror) { if (!db) { onerror(fetch, 0, 'IndexedDB not available!'); @@ -2006,9 +2064,9 @@ function unexportedRuntimeSymbol(sym) { var ptr = _malloc(len); HEAPU8.set(new Uint8Array(value), ptr); HEAPU32[fetch + 12 >> 2] = ptr; - Fetch.setu64(fetch + 16, len); - Fetch.setu64(fetch + 24, 0); - Fetch.setu64(fetch + 32, len); + writeI53ToI64(fetch + 16, len); + writeI53ToI64(fetch + 24, 0); + writeI53ToI64(fetch + 32, len); HEAPU16[fetch + 40 >> 1] = 4; // Mimic XHR readyState 4 === 'DONE: The operation is complete' HEAPU16[fetch + 42 >> 1] = 200; // Mimic XHR HTTP status code 200 "OK" stringToUTF8("OK", fetch + 44, 64); @@ -2050,9 +2108,9 @@ function unexportedRuntimeSymbol(sym) { request.onsuccess = (event) => { var value = event.target.result; HEAPU32[fetch + 12 >> 2] = 0; - Fetch.setu64(fetch + 16, 0); - Fetch.setu64(fetch + 24, 0); - Fetch.setu64(fetch + 32, 0); + writeI53ToI64(fetch + 16, 0); + writeI53ToI64(fetch + 24, 0); + writeI53ToI64(fetch + 32, 0); HEAPU16[fetch + 40 >> 1] = 4; // Mimic XHR readyState 4 === 'DONE: The operation is complete' HEAPU16[fetch + 42 >> 1] = 200; // Mimic XHR HTTP status code 200 "OK" stringToUTF8("OK", fetch + 44, 64); @@ -2069,24 +2127,18 @@ function unexportedRuntimeSymbol(sym) { } } + function _emscripten_start_fetch(fetch, successcb, errorcb, progresscb, readystatechangecb) { // Avoid shutting down the runtime since we want to wait for the async // response. var fetch_attr = fetch + 112; - var requestMethod = UTF8ToString(fetch_attr); var onsuccess = HEAPU32[fetch_attr + 36 >> 2]; var onerror = HEAPU32[fetch_attr + 40 >> 2]; var onprogress = HEAPU32[fetch_attr + 44 >> 2]; var onreadystatechange = HEAPU32[fetch_attr + 48 >> 2]; var fetchAttributes = HEAPU32[fetch_attr + 52 >> 2]; - var fetchAttrLoadToMemory = !!(fetchAttributes & 1); - var fetchAttrStreamData = !!(fetchAttributes & 2); - var fetchAttrPersistFile = !!(fetchAttributes & 4); - var fetchAttrNoDownload = !!(fetchAttributes & 32); - var fetchAttrAppend = !!(fetchAttributes & 8); - var fetchAttrReplace = !!(fetchAttributes & 16); var fetchAttrSynchronous = !!(fetchAttributes & 64); function doCallback(f) { @@ -2153,10 +2205,15 @@ function unexportedRuntimeSymbol(sym) { fetchXHR(fetch, cacheResultAndReportSuccess, reportError, reportProgress, reportReadyStateChange); }; + var requestMethod = UTF8ToString(fetch_attr + 0); + var fetchAttrReplace = !!(fetchAttributes & 16); + var fetchAttrPersistFile = !!(fetchAttributes & 4); + var fetchAttrNoDownload = !!(fetchAttributes & 32); if (requestMethod === 'EM_IDB_STORE') { // TODO(?): Here we perform a clone of the data, because storing shared typed arrays to IndexedDB does not seem to be allowed. - var ptr = HEAPU32[fetch_attr + 84 >> 2]; - fetchCacheData(Fetch.dbInstance, fetch, HEAPU8.slice(ptr, ptr + HEAPU32[fetch_attr + 88 >> 2]), reportSuccess, reportError); + var ptr = HEAPU32[(((fetch_attr)+(84))>>2)]; + var size = HEAPU32[(((fetch_attr)+(88))>>2)]; + fetchCacheData(Fetch.dbInstance, fetch, HEAPU8.slice(ptr, ptr + size), reportSuccess, reportError); } else if (requestMethod === 'EM_IDB_DELETE') { fetchDeleteCachedData(Fetch.dbInstance, fetch, reportSuccess, reportError); } else if (!fetchAttrReplace) { @@ -2169,7 +2226,7 @@ function unexportedRuntimeSymbol(sym) { return fetch; } - function __webgl_enable_ANGLE_instanced_arrays(ctx) { + function webgl_enable_ANGLE_instanced_arrays(ctx) { // Extension available in WebGL 1 from Firefox 26 and Google Chrome 30 onwards. Core feature in WebGL 2. var ext = ctx.getExtension('ANGLE_instanced_arrays'); if (ext) { @@ -2180,7 +2237,7 @@ function unexportedRuntimeSymbol(sym) { } } - function __webgl_enable_OES_vertex_array_object(ctx) { + function webgl_enable_OES_vertex_array_object(ctx) { // Extension available in WebGL 1 from Firefox 25 and WebKit 536.28/desktop Safari 6.0.3 onwards. Core feature in WebGL 2. var ext = ctx.getExtension('OES_vertex_array_object'); if (ext) { @@ -2192,7 +2249,7 @@ function unexportedRuntimeSymbol(sym) { } } - function __webgl_enable_WEBGL_draw_buffers(ctx) { + function webgl_enable_WEBGL_draw_buffers(ctx) { // Extension available in WebGL 1 from Firefox 28 onwards. Core feature in WebGL 2. var ext = ctx.getExtension('WEBGL_draw_buffers'); if (ext) { @@ -2201,21 +2258,22 @@ function unexportedRuntimeSymbol(sym) { } } - function __webgl_enable_WEBGL_draw_instanced_base_vertex_base_instance(ctx) { + function webgl_enable_WEBGL_draw_instanced_base_vertex_base_instance(ctx) { // Closure is expected to be allowed to minify the '.dibvbi' property, so not accessing it quoted. return !!(ctx.dibvbi = ctx.getExtension('WEBGL_draw_instanced_base_vertex_base_instance')); } - function __webgl_enable_WEBGL_multi_draw_instanced_base_vertex_base_instance(ctx) { + function webgl_enable_WEBGL_multi_draw_instanced_base_vertex_base_instance(ctx) { // Closure is expected to be allowed to minify the '.mdibvbi' property, so not accessing it quoted. return !!(ctx.mdibvbi = ctx.getExtension('WEBGL_multi_draw_instanced_base_vertex_base_instance')); } - function __webgl_enable_WEBGL_multi_draw(ctx) { + function webgl_enable_WEBGL_multi_draw(ctx) { // Closure is expected to be allowed to minify the '.multiDrawWebgl' property, so not accessing it quoted. return !!(ctx.multiDrawWebgl = ctx.getExtension('WEBGL_multi_draw')); } + var GL = {counter:1,buffers:[],mappedBuffers:{},programs:[],framebuffers:[],renderbuffers:[],textures:[],shaders:[],vaos:[],contexts:[],offscreenCanvases:{},queries:[],samplers:[],transformFeedbacks:[],syncs:[],byteSizeByTypeRoot:5120,byteSizeByType:[1,1,2,2,4,4,4,2,3,4,8],stringCache:{},stringiCache:{},unpackAlignment:4,recordError:function recordError(errorCode) { if (!GL.lastError) { GL.lastError = errorCode; @@ -2435,12 +2493,12 @@ function unexportedRuntimeSymbol(sym) { // Detect the presence of a few extensions manually, this GL interop layer itself will need to know if they exist. // Extensions that are only available in WebGL 1 (the calls will be no-ops if called on a WebGL 2 context active) - __webgl_enable_ANGLE_instanced_arrays(GLctx); - __webgl_enable_OES_vertex_array_object(GLctx); - __webgl_enable_WEBGL_draw_buffers(GLctx); + webgl_enable_ANGLE_instanced_arrays(GLctx); + webgl_enable_OES_vertex_array_object(GLctx); + webgl_enable_WEBGL_draw_buffers(GLctx); // Extensions that are available from WebGL >= 2 (no-op if called on a WebGL 1 context active) - __webgl_enable_WEBGL_draw_instanced_base_vertex_base_instance(GLctx); - __webgl_enable_WEBGL_multi_draw_instanced_base_vertex_base_instance(GLctx); + webgl_enable_WEBGL_draw_instanced_base_vertex_base_instance(GLctx); + webgl_enable_WEBGL_multi_draw_instanced_base_vertex_base_instance(GLctx); // On WebGL 2, EXT_disjoint_timer_query is replaced with an alternative // that's based on core APIs, and exposes only the queryCounterEXT() @@ -2457,7 +2515,7 @@ function unexportedRuntimeSymbol(sym) { GLctx.disjointTimerQueryExt = GLctx.getExtension("EXT_disjoint_timer_query"); } - __webgl_enable_WEBGL_multi_draw(GLctx); + webgl_enable_WEBGL_multi_draw(GLctx); // .getSupportedExtensions() can return null if context is lost, so coerce to empty array. var exts = GLctx.getSupportedExtensions() || []; @@ -2471,9 +2529,11 @@ function unexportedRuntimeSymbol(sym) { }}; - var __emscripten_webgl_power_preferences = ['default', 'low-power', 'high-performance']; + var emscripten_webgl_power_preferences = ['default', 'low-power', 'high-performance']; + + /** @suppress {duplicate } */ function _emscripten_webgl_do_create_context(target, attributes) { assert(attributes); var a = attributes >> 2; @@ -2485,7 +2545,7 @@ function unexportedRuntimeSymbol(sym) { 'antialias': !!HEAP32[a + (12>>2)], 'premultipliedAlpha': !!HEAP32[a + (16>>2)], 'preserveDrawingBuffer': !!HEAP32[a + (20>>2)], - 'powerPreference': __emscripten_webgl_power_preferences[powerPreference], + 'powerPreference': emscripten_webgl_power_preferences[powerPreference], 'failIfMajorPerformanceCaveat': !!HEAP32[a + (28>>2)], // The following are not predefined WebGL context attributes in the WebGL specification, so the property names can be minified by Closure. majorVersion: HEAP32[a + (32>>2)], @@ -2532,15 +2592,6 @@ function unexportedRuntimeSymbol(sym) { return success ? 0 : -5; } - var SYSCALLS = {varargs:undefined,get:function() { - assert(SYSCALLS.varargs != undefined); - SYSCALLS.varargs += 4; - var ret = HEAP32[(((SYSCALLS.varargs)-(4))>>2)]; - return ret; - },getStr:function(ptr) { - var ret = UTF8ToString(ptr); - return ret; - }}; function _fd_close(fd) { abort('fd_close called without SYSCALLS_REQUIRE_FILESYSTEM'); } @@ -2559,6 +2610,7 @@ function unexportedRuntimeSymbol(sym) { } var printCharBuffers = [null,[],[]]; + function printChar(stream, curr) { var buffer = printCharBuffers[stream]; assert(buffer); @@ -2622,12 +2674,12 @@ function unexportedRuntimeSymbol(sym) { } function _glBindVertexArray(vao) { - GLctx['bindVertexArray'](GL.vaos[vao]); + GLctx.bindVertexArray(GL.vaos[vao]); var ibo = GLctx.getParameter(0x8895 /*ELEMENT_ARRAY_BUFFER_BINDING*/); GLctx.currentElementArrayBufferBinding = ibo ? (ibo.name | 0) : 0; } - function _glBlendFunc(x0, x1) { GLctx['blendFunc'](x0, x1) } + function _glBlendFunc(x0, x1) { GLctx.blendFunc(x0, x1) } function _glBufferData(target, size, data, usage) { @@ -2655,11 +2707,11 @@ function unexportedRuntimeSymbol(sym) { GLctx.bufferSubData(target, offset, HEAPU8.subarray(data, data+size)); } - function _glClear(x0) { GLctx['clear'](x0) } + function _glClear(x0) { GLctx.clear(x0) } - function _glClearColor(x0, x1, x2, x3) { GLctx['clearColor'](x0, x1, x2, x3) } + function _glClearColor(x0, x1, x2, x3) { GLctx.clearColor(x0, x1, x2, x3) } - function _glClearDepth(x0) { GLctx['clearDepth'](x0) } + function _glClearDepth(x0) { GLctx.clearDepth(x0) } function _glCompileShader(shader) { GLctx.compileShader(GL.shaders[shader]); @@ -2730,12 +2782,12 @@ function unexportedRuntimeSymbol(sym) { function _glDeleteVertexArrays(n, vaos) { for (var i = 0; i < n; i++) { var id = HEAP32[(((vaos)+(i*4))>>2)]; - GLctx['deleteVertexArray'](GL.vaos[id]); + GLctx.deleteVertexArray(GL.vaos[id]); GL.vaos[id] = null; } } - function _glDepthFunc(x0) { GLctx['depthFunc'](x0) } + function _glDepthFunc(x0) { GLctx.depthFunc(x0) } function _glDepthMask(flag) { GLctx.depthMask(!!flag); @@ -2775,7 +2827,7 @@ function unexportedRuntimeSymbol(sym) { } } - function _glEnable(x0) { GLctx['enable'](x0) } + function _glEnable(x0) { GLctx.enable(x0) } function _glEnableVertexAttribArray(index) { var cb = GL.currentContext.clientBuffers[index]; @@ -2809,6 +2861,7 @@ function unexportedRuntimeSymbol(sym) { ); } + function _glGetAttribLocation(program, name) { return GLctx.getAttribLocation(GL.programs[program], UTF8ToString(name)); } @@ -2865,6 +2918,7 @@ function unexportedRuntimeSymbol(sym) { } } + function _glGetShaderInfoLog(shader, maxLength, length, infoLog) { var log = GLctx.getShaderInfoLog(GL.shaders[shader]); if (log === null) log = '(unknown error)'; @@ -2950,6 +3004,7 @@ function unexportedRuntimeSymbol(sym) { } + function _glGetUniformLocation(program, name) { name = UTF8ToString(name); @@ -3077,7 +3132,7 @@ function unexportedRuntimeSymbol(sym) { } function _glVertexAttribDivisor(index, divisor) { - GLctx['vertexAttribDivisor'](index, divisor); + GLctx.vertexAttribDivisor(index, divisor); } function _glVertexAttribPointer(index, size, type, normalized, stride, ptr) { @@ -3098,31 +3153,8 @@ function unexportedRuntimeSymbol(sym) { GLctx.vertexAttribPointer(index, size, type, !!normalized, stride, ptr); } - - function _proc_exit(code) { - EXITSTATUS = code; - if (!keepRuntimeAlive()) { - if (Module['onExit']) Module['onExit'](code); - ABORT = true; - } - quit_(code, new ExitStatus(code)); - } - /** @param {boolean|number=} implicit */ - function exitJS(status, implicit) { - EXITSTATUS = status; - - checkUnflushedContent(); - - // if exit() was called explicitly, warn the user if the runtime isn't actually being shut down - if (keepRuntimeAlive() && !implicit) { - var msg = 'program exited (with status: ' + status + '), but EXIT_RUNTIME is not set, so halting execution but not exiting the runtime or preventing further async execution (build with EXIT_RUNTIME=1, if you want a true shutdown)'; - err(msg); - } - - _proc_exit(status); - } -Fetch.staticInit();; +Fetch.init();; var GLctx;; var miniTempWebGLFloatBuffersStorage = new Float32Array(288); for (/**@suppress{duplicate}*/var i = 0; i < 288; ++i) { @@ -3243,7 +3275,8 @@ var dynCall_jiji = Module["dynCall_jiji"] = createExportWrapper("dynCall_jiji"); var missingLibrarySymbols = [ 'zeroMemory', - 'stringToNewUTF8', + 'arraySum', + 'addDays', 'setErrNo', 'inetPton4', 'inetNtop4', @@ -3252,8 +3285,11 @@ var missingLibrarySymbols = [ 'readSockaddr', 'writeSockaddr', 'getHostByName', - 'getRandomDevice', + 'initRandomFill', + 'randomFill', 'traverseStack', + 'getCallstack', + 'emscriptenLog', 'convertPCtoSourceLocation', 'readEmAsmArgs', 'jstoi_s', @@ -3265,24 +3301,20 @@ var missingLibrarySymbols = [ 'dynCall', 'runtimeKeepalivePush', 'runtimeKeepalivePop', - 'maybeExit', 'safeSetTimeout', 'asmjsMangle', 'asyncLoad', 'alignMemory', 'mmapAlloc', - 'handleAllocator', 'getNativeTypeSize', 'STACK_SIZE', 'STACK_ALIGN', 'POINTER_SIZE', 'ASSERTIONS', - 'writeI53ToI64', 'writeI53ToI64Clamped', 'writeI53ToI64Signaling', 'writeI53ToU64Clamped', 'writeI53ToU64Signaling', - 'readI53FromU64', 'convertI32PairToI53', 'convertU32PairToI53', 'getCFunc', @@ -3312,12 +3344,8 @@ var missingLibrarySymbols = [ 'UTF32ToString', 'stringToUTF32', 'lengthBytesUTF32', - 'allocateUTF8OnStack', - 'writeStringToMemory', + 'stringToUTF8OnStack', 'writeArrayToMemory', - 'writeAsciiToMemory', - 'getSocketFromFD', - 'getSocketAddress', 'registerKeyEventCallback', 'registerWheelEventCallback', 'registerFocusEventCallback', @@ -3359,46 +3387,53 @@ var missingLibrarySymbols = [ 'stackTrace', 'getEnvStrings', 'checkWasiClock', + 'wasiRightsToMuslOFlags', + 'wasiOFlagsToMuslOFlags', 'createDyncallWrapper', 'setImmediateWrapped', 'clearImmediateWrapped', 'polyfillSetImmediate', - 'newNativePromise', 'getPromise', + 'makePromise', + 'idsToPromises', + 'makePromiseCallback', 'ExceptionInfo', - 'exception_addRef', - 'exception_decRef', 'setMainLoop', + 'getSocketFromFD', + 'getSocketAddress', + 'FS_createPreloadedFile', + 'FS_modeStringToFlags', + 'FS_getMode', '_setNetworkCallback', 'heapObjectForWebGLType', 'heapAccessShiftForWebGLHeap', 'emscriptenWebGLGet', 'computeUnpackAlignedImageSize', + 'colorChannelsInGlTextureFormat', 'emscriptenWebGLGetTexPixelData', 'emscriptenWebGLGetUniform', 'emscriptenWebGLGetVertexAttrib', + '__glGetActiveAttribOrUniform', 'emscriptenWebGLGetBufferBinding', 'emscriptenWebGLValidateMapBufferTarget', 'writeGLArray', + 'registerWebGlEventCallback', + 'runAndAbortIfError', 'SDL_unicode', 'SDL_ttfContext', 'SDL_audio', 'GLFW_Window', - 'runAndAbortIfError', 'emscriptenWebGLGetIndexed', 'ALLOC_NORMAL', 'ALLOC_STACK', 'allocate', + 'writeStringToMemory', + 'writeAsciiToMemory', ]; missingLibrarySymbols.forEach(missingLibrarySymbol) var unexportedSymbols = [ 'run', - 'UTF8ArrayToString', - 'UTF8ToString', - 'stringToUTF8Array', - 'stringToUTF8', - 'lengthBytesUTF8', 'addOnPreRun', 'addOnInit', 'addOnPreMain', @@ -3409,7 +3444,6 @@ var unexportedSymbols = [ 'FS_createFolder', 'FS_createPath', 'FS_createDataFile', - 'FS_createPreloadedFile', 'FS_createLazyFile', 'FS_createLink', 'FS_createDevice', @@ -3432,6 +3466,12 @@ var unexportedSymbols = [ 'getHeapMax', 'emscripten_realloc_buffer', 'ENV', + 'MONTH_DAYS_REGULAR', + 'MONTH_DAYS_LEAP', + 'MONTH_DAYS_REGULAR_CUMULATIVE', + 'MONTH_DAYS_LEAP_CUMULATIVE', + 'isLeapYear', + 'ydayFromDate', 'ERRNO_CODES', 'ERRNO_MESSAGES', 'DNS', @@ -3444,7 +3484,11 @@ var unexportedSymbols = [ 'jstoi_q', 'handleException', 'callUserCallback', + 'maybeExit', + 'HandleAllocator', + 'writeI53ToI64', 'readI53FromI64', + 'readI53FromU64', 'convertI32PairToI53Checked', 'freeTableIndexes', 'functionsInTableMap', @@ -3452,9 +3496,14 @@ var unexportedSymbols = [ 'getValue', 'PATH', 'PATH_FS', + 'UTF8Decoder', + 'UTF8ArrayToString', + 'UTF8ToString', + 'stringToUTF8Array', + 'stringToUTF8', + 'lengthBytesUTF8', 'UTF16Decoder', - 'allocateUTF8', - 'SYSCALLS', + 'stringToNewUTF8', 'JSEvents', 'specialHTMLTargets', 'maybeCStringToJsString', @@ -3475,6 +3524,8 @@ var unexportedSymbols = [ 'exceptionCaught', 'Browser', 'wget', + 'SYSCALLS', + 'preloadPlugins', 'FS', 'MEMFS', 'TTY', @@ -3482,18 +3533,29 @@ var unexportedSymbols = [ 'SOCKFS', 'tempFixedLengthArray', 'miniTempWebGLFloatBuffers', + 'miniTempWebGLIntBuffers', + 'webgl_enable_ANGLE_instanced_arrays', + 'webgl_enable_OES_vertex_array_object', + 'webgl_enable_WEBGL_draw_buffers', + 'webgl_enable_WEBGL_multi_draw', 'GL', + '__glGenObject', 'webglGetUniformLocation', 'webglPrepareUniformLocationsBeforeFirstUse', 'webglGetLeftBracePos', + 'emscripten_webgl_power_preferences', 'AL', - 'SDL', - 'SDL_gfx', 'GLUT', 'EGL', - 'GLFW', 'GLEW', 'IDBStore', + 'SDL', + 'SDL_gfx', + 'GLFW', + 'webgl_enable_WEBGL_draw_instanced_base_vertex_base_instance', + 'webgl_enable_WEBGL_multi_draw_instanced_base_vertex_base_instance', + 'allocateUTF8', + 'allocateUTF8OnStack', 'Fetch', 'fetchDeleteCachedData', 'fetchLoadCachedData', @@ -3525,8 +3587,6 @@ function callMain() { var ret = entryFunction(argc, argv); - // In PROXY_TO_PTHREAD builds, we should never exit the runtime below, as - // execution is asynchronously handed off to a pthread. // if we're not running an evented main loop, it's time to exit exitJS(ret, /* implicit = */ true); return ret; @@ -3545,7 +3605,6 @@ function stackCheckInit() { writeStackCookie(); } -/** @type {function(Array=)} */ function run() { if (runDependencies > 0) { diff --git a/themes/dist/output.wasm b/themes/dist/output.wasm index 1b24bbd..b6d9fdc 100755 Binary files a/themes/dist/output.wasm and b/themes/dist/output.wasm differ diff --git a/themes/src/Renderer2d.cpp b/themes/src/Renderer2d.cpp index c303e83..06f0838 100644 --- a/themes/src/Renderer2d.cpp +++ b/themes/src/Renderer2d.cpp @@ -6,7 +6,7 @@ // Note: In the 'transform' attribute, the transform.x is the scale, // transform.y is the rotation, and transform.zw is the translation. -const char* Vertex2DShader = +const char* DEFAULT_VERTEX_SHADER = "attribute vec2 position; \n" "attribute vec4 color; \n" "attribute mat4 vMatrix; \n" @@ -19,16 +19,18 @@ const char* Vertex2DShader = " VertexColor = color; \n" "}"; -const char* renderer2dFragmentShader = +const char* DEFAULT_FRAGMENT_SHADER = "varying lowp vec4 VertexColor; \n" "void main() { \n" " gl_FragColor = VertexColor; \n" "}"; -void Renderer2d::load(WebglContext* inContext) { +void Renderer2d::load(WebglContext* inContext, const char* inVertexShader, const char* inFragmentShader) { + auto vertexShader = inVertexShader ? inVertexShader : DEFAULT_VERTEX_SHADER; + auto fragmentShader = inFragmentShader ? inFragmentShader : DEFAULT_FRAGMENT_SHADER; context = inContext; printf("Compiling Renderer2d shader...\n"); - shader = loadShader(Vertex2DShader, renderer2dFragmentShader); + shader = loadShader(vertexShader, fragmentShader); useShader(shader); attributes.position = getShaderAttribute(shader, "position"); diff --git a/themes/src/Renderer2d.h b/themes/src/Renderer2d.h index 909f088..8a96503 100644 --- a/themes/src/Renderer2d.h +++ b/themes/src/Renderer2d.h @@ -7,6 +7,7 @@ struct WebglContext; +/// Responsible for rendering Mesh2Ds struct Renderer2d { WebglContext* context = NULL; Mat4x4 projection; @@ -24,7 +25,9 @@ struct Renderer2d { i32 model; } uniforms; - void load(WebglContext* context); + /// Load with the provided context and shader programs. If the shaders are NULL, the default + /// shader is used + void load(WebglContext* context, const char* vertexShader = NULL, const char* fragmentShader = NULL); void render(); void unload(); }; diff --git a/themes/src/_shaders/renderer2d.frag b/themes/src/_shaders/renderer2d.frag new file mode 100644 index 0000000..e69de29 diff --git a/themes/src/_shaders/renderer2d.vert b/themes/src/_shaders/renderer2d.vert new file mode 100644 index 0000000..e69de29 diff --git a/themes/src/_shaders/renderer3d.frag b/themes/src/_shaders/renderer3d.frag new file mode 100644 index 0000000..2f50347 --- /dev/null +++ b/themes/src/_shaders/renderer3d.frag @@ -0,0 +1,7 @@ +varying lowp vec4 VertexColor; +varying lowp vec4 VertexNormal; + +void main() { + const lowp vec3 lightDirection = vec3(0.0, 1.0, 0.0); + gl_FragColor = vec4(VertexColor.xyz * dot(VertexNormal.xyz, lightDirection), 1); +} diff --git a/themes/src/_shaders/renderer3d.vert b/themes/src/_shaders/renderer3d.vert new file mode 100644 index 0000000..026285f --- /dev/null +++ b/themes/src/_shaders/renderer3d.vert @@ -0,0 +1,15 @@ +attribute vec4 position; +attribute vec4 color; +attribute vec4 normal; +uniform mat4 projection; +uniform mat4 view; +uniform mat4 model; +varying lowp vec4 VertexColor; +varying lowp vec4 VertexNormal; + +void main() { + vec4 fragmentPosition = projection * view * model * position; + gl_Position = fragmentPosition; + VertexColor = color; + VertexNormal = normal; +} diff --git a/themes/src/_shaders/sun.vert b/themes/src/_shaders/sun.vert new file mode 100644 index 0000000..e69de29 diff --git a/themes/src/main.cpp b/themes/src/main.cpp index 4e1a646..297a498 100644 --- a/themes/src/main.cpp +++ b/themes/src/main.cpp @@ -74,8 +74,7 @@ void load(Theme theme) { break; } case Theme::Summer: - renderer2d.load(&context); - summerTheme.load(&renderer2d); + summerTheme.load(&renderer2d, &context); break; default: break; @@ -177,4 +176,4 @@ EM_BOOL selectSummer(int eventType, const EmscriptenMouseEvent* mouseEvent, void printf("Summer theme selected\n"); load(Theme::Summer); return true; -} \ No newline at end of file +} diff --git a/themes/src/shaders/renderer2d.frag b/themes/src/shaders/renderer2d.frag deleted file mode 100644 index e69de29..0000000 diff --git a/themes/src/shaders/renderer2d.vert b/themes/src/shaders/renderer2d.vert deleted file mode 100644 index e69de29..0000000 diff --git a/themes/src/shaders/renderer2d_frag.h b/themes/src/shaders/renderer2d_frag.h new file mode 100644 index 0000000..6f5e472 --- /dev/null +++ b/themes/src/shaders/renderer2d_frag.h @@ -0,0 +1,4 @@ +#ifndef SHADER_RENDERER2D_FRAG +#define SHADER_RENDERER2D_FRAG +const char* shader_renderer2d_frag = " \n"; +#endif diff --git a/themes/src/shaders/renderer2d_vert.h b/themes/src/shaders/renderer2d_vert.h new file mode 100644 index 0000000..b32cf9f --- /dev/null +++ b/themes/src/shaders/renderer2d_vert.h @@ -0,0 +1,4 @@ +#ifndef SHADER_RENDERER2D_VERT +#define SHADER_RENDERER2D_VERT +const char* shader_renderer2d_vert = " \n"; +#endif diff --git a/themes/src/shaders/renderer3d.frag b/themes/src/shaders/renderer3d.frag deleted file mode 100644 index 2f50347..0000000 --- a/themes/src/shaders/renderer3d.frag +++ /dev/null @@ -1,7 +0,0 @@ -varying lowp vec4 VertexColor; -varying lowp vec4 VertexNormal; - -void main() { - const lowp vec3 lightDirection = vec3(0.0, 1.0, 0.0); - gl_FragColor = vec4(VertexColor.xyz * dot(VertexNormal.xyz, lightDirection), 1); -} diff --git a/themes/src/shaders/renderer3d.vert b/themes/src/shaders/renderer3d.vert deleted file mode 100644 index 026285f..0000000 --- a/themes/src/shaders/renderer3d.vert +++ /dev/null @@ -1,15 +0,0 @@ -attribute vec4 position; -attribute vec4 color; -attribute vec4 normal; -uniform mat4 projection; -uniform mat4 view; -uniform mat4 model; -varying lowp vec4 VertexColor; -varying lowp vec4 VertexNormal; - -void main() { - vec4 fragmentPosition = projection * view * model * position; - gl_Position = fragmentPosition; - VertexColor = color; - VertexNormal = normal; -} diff --git a/themes/src/shaders/renderer3d_frag.h b/themes/src/shaders/renderer3d_frag.h new file mode 100644 index 0000000..6b3a936 --- /dev/null +++ b/themes/src/shaders/renderer3d_frag.h @@ -0,0 +1,11 @@ +#ifndef SHADER_RENDERER3D_FRAG +#define SHADER_RENDERER3D_FRAG +const char* shader_renderer3d_frag = "varying lowp vec4 VertexColor; \n" +"varying lowp vec4 VertexNormal; \n" +" \n" +"void main() { \n" +" const lowp vec3 lightDirection = vec3(0.0, 1.0, 0.0); \n" +" gl_FragColor = vec4(VertexColor.xyz * dot(VertexNormal.xyz, lightDirection), 1); \n" +"} \n" +" \n"; +#endif diff --git a/themes/src/shaders/renderer3d_vert.h b/themes/src/shaders/renderer3d_vert.h new file mode 100644 index 0000000..20e69f9 --- /dev/null +++ b/themes/src/shaders/renderer3d_vert.h @@ -0,0 +1,19 @@ +#ifndef SHADER_RENDERER3D_VERT +#define SHADER_RENDERER3D_VERT +const char* shader_renderer3d_vert = "attribute vec4 position; \n" +"attribute vec4 color; \n" +"attribute vec4 normal; \n" +"uniform mat4 projection; \n" +"uniform mat4 view; \n" +"uniform mat4 model; \n" +"varying lowp vec4 VertexColor; \n" +"varying lowp vec4 VertexNormal; \n" +" \n" +"void main() { \n" +" vec4 fragmentPosition = projection * view * model * position; \n" +" gl_Position = fragmentPosition; \n" +" VertexColor = color; \n" +" VertexNormal = normal; \n" +"} \n" +" \n"; +#endif diff --git a/themes/src/shaders/sun_vert.h b/themes/src/shaders/sun_vert.h new file mode 100644 index 0000000..2f9bf69 --- /dev/null +++ b/themes/src/shaders/sun_vert.h @@ -0,0 +1,4 @@ +#ifndef SHADER_SUN_VERT +#define SHADER_SUN_VERT +const char* shader_sun_vert = " \n"; +#endif diff --git a/themes/src/summer/SummerTheme.cpp b/themes/src/summer/SummerTheme.cpp index 406cd22..81cc43b 100644 --- a/themes/src/summer/SummerTheme.cpp +++ b/themes/src/summer/SummerTheme.cpp @@ -4,7 +4,8 @@ #include "../mathlib.h" #include -void SummerTheme::load(Renderer2d* renderer) { +void SummerTheme::load(Renderer2d* renderer, WebglContext* context) { + renderer->load(context); renderer->clearColor = Vector4(0, 181, 286, 255.f).toNormalizedColor(); sun.sectors = 180; sun.radius = renderer->context->width / 4.f; diff --git a/themes/src/summer/SummerTheme.h b/themes/src/summer/SummerTheme.h index 4a9f76b..dad4ac9 100644 --- a/themes/src/summer/SummerTheme.h +++ b/themes/src/summer/SummerTheme.h @@ -16,7 +16,7 @@ struct Sun { struct SummerTheme { Sun sun; - void load(Renderer2d* renderer); + void load(Renderer2d*, WebglContext*); void update(f32 dtSeconds); void render(Renderer2d* renderer); void unload(); diff --git a/themes/src/tools/shader.js b/themes/src/tools/shader.js new file mode 100644 index 0000000..27c8682 --- /dev/null +++ b/themes/src/tools/shader.js @@ -0,0 +1,30 @@ +/* + Responsible for generating .h files from GLSL files. + */ + + +const path = require('path'); +const fs = require('fs'); +const directory = path.join(__dirname, "..", "_shaders"); +const out_directory = path.join(__dirname, "..", "shaders"); + +const files = fs.readdirSync(directory); +files.forEach(file => { + const filePath = path.join(directory, file); + const text = String(fs.readFileSync(filePath)); + const splitText = text.split('\n'); + const splitName = file.split('.'); + const def = `SHADER_${splitName.join('_').toUpperCase()}`; + let result = `#ifndef ${def} \n`; + result += `#define ${def} \n`; + result += `const char* ${def.toLowerCase()} = ` + splitText.forEach((line, index) => { + result += "\"" + line + " \\n\""; + if (index == splitText.length - 1) + result += ";\n"; + else + result += "\n"; + }); + result += '#endif\n'; + fs.writeFileSync(path.join(out_directory, splitName.join('_') + '.h'), result); +}); diff --git a/themes/tools/bundle_shaders.py b/themes/tools/bundle_shaders.py deleted file mode 100644 index 19cf5db..0000000 --- a/themes/tools/bundle_shaders.py +++ /dev/null @@ -1,4 +0,0 @@ - -# Renderer 2D -with open('../Renderer2d_Vertex.glsl') as vs: - \ No newline at end of file -- cgit v1.2.1