diff options
Diffstat (limited to 'themes')
| -rw-r--r-- | themes/.gitignore | 15 | ||||
| -rw-r--r-- | themes/Makefile | 44 | ||||
| -rw-r--r-- | themes/README.md | 58 | ||||
| -rw-r--r-- | themes/compile_commands.json | 127 | ||||
| -rwxr-xr-x | themes/compile_commands.sh | 7 | ||||
| -rw-r--r-- | themes/dist/output.js | 1669 | ||||
| -rwxr-xr-x | themes/dist/output.wasm | bin | 157180 -> 101714 bytes | |||
| -rw-r--r-- | themes/emscripten.ini | 14 | ||||
| -rw-r--r-- | themes/meson.build | 122 | ||||
| -rw-r--r-- | themes/src/_shaders/sun.frag | 40 | ||||
| -rw-r--r-- | themes/src/_shaders/sun.vert | 27 | ||||
| -rw-r--r-- | themes/src/autumn/autumn_theme.cpp (renamed from themes/src/autumn/AutumnTheme.cpp) | 2 | ||||
| -rw-r--r-- | themes/src/autumn/autumn_theme.hpp (renamed from themes/src/autumn/AutumnTheme.hpp) | 6 | ||||
| -rw-r--r-- | themes/src/autumn/leaf_particle_render.cpp (renamed from themes/src/autumn/LeafParticleRender.cpp) | 6 | ||||
| -rw-r--r-- | themes/src/autumn/leaf_particle_render.h (renamed from themes/src/autumn/LeafParticleRender.h) | 2 | ||||
| -rw-r--r-- | themes/src/autumn/tree_shape.cpp (renamed from themes/src/autumn/TreeShape.cpp) | 2 | ||||
| -rw-r--r-- | themes/src/autumn/tree_shape.h (renamed from themes/src/autumn/TreeShape.h) | 2 | ||||
| -rw-r--r-- | themes/src/list.h | 2 | ||||
| -rw-r--r-- | themes/src/logger.cpp (renamed from themes/src/Logger.cpp) | 2 | ||||
| -rw-r--r-- | themes/src/logger.h (renamed from themes/src/Logger.h) | 0 | ||||
| -rw-r--r-- | themes/src/main.cpp | 21 | ||||
| -rw-r--r-- | themes/src/main_loop.cpp (renamed from themes/src/MainLoop.cpp) | 2 | ||||
| -rw-r--r-- | themes/src/main_loop.h (renamed from themes/src/MainLoop.h) | 2 | ||||
| -rw-r--r-- | themes/src/renderer_2d.cpp (renamed from themes/src/Renderer2d.cpp) | 6 | ||||
| -rw-r--r-- | themes/src/renderer_2d.h (renamed from themes/src/Renderer2d.h) | 4 | ||||
| -rw-r--r-- | themes/src/renderer_3d.cpp (renamed from themes/src/Renderer3d.cpp) | 8 | ||||
| -rw-r--r-- | themes/src/renderer_3d.h (renamed from themes/src/Renderer3d.h) | 0 | ||||
| -rw-r--r-- | themes/src/shader.cpp (renamed from themes/src/Shader.cpp) | 2 | ||||
| -rw-r--r-- | themes/src/shader.h (renamed from themes/src/Shader.h) | 0 | ||||
| -rw-r--r-- | themes/src/shaders/sun_frag.cpp | 40 | ||||
| -rw-r--r-- | themes/src/shaders/sun_vert.cpp | 27 | ||||
| -rw-r--r-- | themes/src/shapes_2d.cpp | 2 | ||||
| -rw-r--r-- | themes/src/shapes_2d.h | 2 | ||||
| -rw-r--r-- | themes/src/spring/grass_renderer.cpp (renamed from themes/src/spring/GrassRenderer.cpp) | 4 | ||||
| -rw-r--r-- | themes/src/spring/grass_renderer.hpp (renamed from themes/src/spring/GrassRenderer.hpp) | 2 | ||||
| -rw-r--r-- | themes/src/spring/spring_theme.cpp (renamed from themes/src/spring/SpringTheme.cpp) | 4 | ||||
| -rw-r--r-- | themes/src/spring/spring_theme.hpp (renamed from themes/src/spring/SpringTheme.hpp) | 2 | ||||
| -rw-r--r-- | themes/src/summer/summer_theme.cpp (renamed from themes/src/summer/SummerTheme.cpp) | 12 | ||||
| -rw-r--r-- | themes/src/summer/summer_theme.h (renamed from themes/src/summer/SummerTheme.h) | 4 | ||||
| -rw-r--r-- | themes/src/webgl_context.cpp (renamed from themes/src/WebglContext.cpp) | 2 | ||||
| -rw-r--r-- | themes/src/webgl_context.h (renamed from themes/src/WebglContext.h) | 0 | ||||
| -rw-r--r-- | themes/src/winter/snowflake.cpp (renamed from themes/src/winter/Snowflake.cpp) | 4 | ||||
| -rw-r--r-- | themes/src/winter/snowflake.h (renamed from themes/src/winter/Snowflake.h) | 2 | ||||
| -rw-r--r-- | themes/src/winter/windfield.cpp (renamed from themes/src/winter/Windfield.cpp) | 2 | ||||
| -rw-r--r-- | themes/src/winter/windfield.hpp (renamed from themes/src/winter/Windfield.hpp) | 0 | ||||
| -rw-r--r-- | themes/src/winter/winter_theme.cpp (renamed from themes/src/winter/WinterTheme.cpp) | 4 | ||||
| -rw-r--r-- | themes/src/winter/winter_theme.hpp (renamed from themes/src/winter/WinterTheme.hpp) | 4 |
47 files changed, 1247 insertions, 1062 deletions
diff --git a/themes/.gitignore b/themes/.gitignore new file mode 100644 index 0000000..a967258 --- /dev/null +++ b/themes/.gitignore @@ -0,0 +1,15 @@ +# Build directories +dist/ +builddir/ +.cache/ + +# IDE +.idea/ +.vscode/ +*.swp +*.swo +*~ + +# Meson +.meson_subdir/ +compile_commands.json diff --git a/themes/Makefile b/themes/Makefile deleted file mode 100644 index 45967f5..0000000 --- a/themes/Makefile +++ /dev/null @@ -1,44 +0,0 @@ -TARGET_EXEC ?= output.js - -BUILD_DIR ?= ./dist -SRC_DIRS ?= ./src - -CC := emcc -CXX := em++ -SRCS := $(shell find $(SRC_DIRS) -name *.cpp -or -name *.c -or -name *.s) -OBJS := $(SRCS:%=$(BUILD_DIR)/%.o) -DEPS := $(OBJS:.o=.d) - -INC_DIRS := $(shell find $(SRC_DIRS) -type d) -INC_FLAGS := $(addprefix -I,$(INC_DIRS)) -LDFLAGS = -s ALLOW_MEMORY_GROWTH=1 -s USE_WEBGL2=1 -s FULL_ES3=1 -s WASM=1 -s NO_EXIT_RUNTIME=1 -s FETCH - -CPPFLAGS ?= $(INC_FLAGS) -MMD -MP - -$(BUILD_DIR)/$(TARGET_EXEC): $(OBJS) - $(CC) $(OBJS) -o $@ $(LDFLAGS) - -# assembly -$(BUILD_DIR)/%.s.o: %.s - $(MKDIR_P) $(dir $@) - $(AS) $(ASFLAGS) -c $< -o $@ - -# c source -$(BUILD_DIR)/%.c.o: %.c - $(MKDIR_P) $(dir $@) - $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ - -# c++ source -$(BUILD_DIR)/%.cpp.o: %.cpp - $(MKDIR_P) $(dir $@) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@ - - -.PHONY: clean - -clean: - $(RM) -r $(BUILD_DIR) - --include $(DEPS) - -MKDIR_P ?= mkdir -p diff --git a/themes/README.md b/themes/README.md new file mode 100644 index 0000000..3425fb8 --- /dev/null +++ b/themes/README.md @@ -0,0 +1,58 @@ +# Themes +This subproject provides themes for the website. + +## Prerequisites + +- Emscripten SDK (emsdk) +- Meson build system (`pip install meson` or via your package manager) +- Ninja build backend (usually installed with Meson) + +## Building + +### Initial Setup + +```bash +meson setup builddir --cross-file emscripten.ini +``` + +### Compile + +```bash +meson compile -C builddir +``` + +The output files will be automatically copied to: +- `dist/output.js` +- `dist/output.wasm` + +The original build artifacts are also available in `builddir/` if needed. + +### Clean Build + +```bash +# Clean and rebuild +meson compile -C builddir --clean +meson compile -C builddir +``` + +### Complete Clean + +```bash +# Remove the build directory entirely +rm -rf builddir +``` + +## Build Options + +You can configure build options during setup: + +```bash +# Debug build +meson setup builddir --cross-file emscripten.ini --buildtype=debug + +# Release build with optimizations +meson setup builddir --cross-file emescripten.ini --buildtype=release + +# Reconfigure an existing build +meson configure builddir --buildtype=debug +``` diff --git a/themes/compile_commands.json b/themes/compile_commands.json deleted file mode 100644 index bcf5405..0000000 --- a/themes/compile_commands.json +++ /dev/null @@ -1,127 +0,0 @@ -[ - { - "directory": ".", - "command": "g++ -I./src -I./src/spring -I./src/shaders -I./src/tools -I./src/summer -I./src/winter -I./src/autumn -I./src/_shaders -MMD -MP -c src/MainLoop.cpp -o dist/./src/MainLoop.cpp.o", - "file": "dist/./src/MainLoop.cpp.o" - }, - { - "directory": ".", - "command": "g++ -I./src -I./src/spring -I./src/shaders -I./src/tools -I./src/summer -I./src/winter -I./src/autumn -I./src/_shaders -MMD -MP -c src/spring/SpringTheme.cpp -o dist/./src/spring/SpringTheme.cpp.o", - "file": "dist/./src/spring/SpringTheme.cpp.o" - }, - { - "directory": ".", - "command": "g++ -I./src -I./src/spring -I./src/shaders -I./src/tools -I./src/summer -I./src/winter -I./src/autumn -I./src/_shaders -MMD -MP -c src/spring/GrassRenderer.cpp -o dist/./src/spring/GrassRenderer.cpp.o", - "file": "dist/./src/spring/GrassRenderer.cpp.o" - }, - { - "directory": ".", - "command": "g++ -I./src -I./src/spring -I./src/shaders -I./src/tools -I./src/summer -I./src/winter -I./src/autumn -I./src/_shaders -MMD -MP -c src/mathlib.cpp -o dist/./src/mathlib.cpp.o", - "file": "dist/./src/mathlib.cpp.o" - }, - { - "directory": ".", - "command": "g++ -I./src -I./src/spring -I./src/shaders -I./src/tools -I./src/summer -I./src/winter -I./src/autumn -I./src/_shaders -MMD -MP -c src/shaders/renderer3d_frag.cpp -o dist/./src/shaders/renderer3d_frag.cpp.o", - "file": "dist/./src/shaders/renderer3d_frag.cpp.o" - }, - { - "directory": ".", - "command": "g++ -I./src -I./src/spring -I./src/shaders -I./src/tools -I./src/summer -I./src/winter -I./src/autumn -I./src/_shaders -MMD -MP -c src/shaders/sun_frag.cpp -o dist/./src/shaders/sun_frag.cpp.o", - "file": "dist/./src/shaders/sun_frag.cpp.o" - }, - { - "directory": ".", - "command": "g++ -I./src -I./src/spring -I./src/shaders -I./src/tools -I./src/summer -I./src/winter -I./src/autumn -I./src/_shaders -MMD -MP -c src/shaders/renderer3d_vert.cpp -o dist/./src/shaders/renderer3d_vert.cpp.o", - "file": "dist/./src/shaders/renderer3d_vert.cpp.o" - }, - { - "directory": ".", - "command": "g++ -I./src -I./src/spring -I./src/shaders -I./src/tools -I./src/summer -I./src/winter -I./src/autumn -I./src/_shaders -MMD -MP -c src/shaders/renderer2d_frag.cpp -o dist/./src/shaders/renderer2d_frag.cpp.o", - "file": "dist/./src/shaders/renderer2d_frag.cpp.o" - }, - { - "directory": ".", - "command": "g++ -I./src -I./src/spring -I./src/shaders -I./src/tools -I./src/summer -I./src/winter -I./src/autumn -I./src/_shaders -MMD -MP -c src/shaders/sun_vert.cpp -o dist/./src/shaders/sun_vert.cpp.o", - "file": "dist/./src/shaders/sun_vert.cpp.o" - }, - { - "directory": ".", - "command": "g++ -I./src -I./src/spring -I./src/shaders -I./src/tools -I./src/summer -I./src/winter -I./src/autumn -I./src/_shaders -MMD -MP -c src/shaders/renderer2d_vert.cpp -o dist/./src/shaders/renderer2d_vert.cpp.o", - "file": "dist/./src/shaders/renderer2d_vert.cpp.o" - }, - { - "directory": ".", - "command": "g++ -I./src -I./src/spring -I./src/shaders -I./src/tools -I./src/summer -I./src/winter -I./src/autumn -I./src/_shaders -MMD -MP -c src/shader_fetcher.cpp -o dist/./src/shader_fetcher.cpp.o", - "file": "dist/./src/shader_fetcher.cpp.o" - }, - { - "directory": ".", - "command": "g++ -I./src -I./src/spring -I./src/shaders -I./src/tools -I./src/summer -I./src/winter -I./src/autumn -I./src/_shaders -MMD -MP -c src/Logger.cpp -o dist/./src/Logger.cpp.o", - "file": "dist/./src/Logger.cpp.o" - }, - { - "directory": ".", - "command": "g++ -I./src -I./src/spring -I./src/shaders -I./src/tools -I./src/summer -I./src/winter -I./src/autumn -I./src/_shaders -MMD -MP -c src/Renderer3d.cpp -o dist/./src/Renderer3d.cpp.o", - "file": "dist/./src/Renderer3d.cpp.o" - }, - { - "directory": ".", - "command": "g++ -I./src -I./src/spring -I./src/shaders -I./src/tools -I./src/summer -I./src/winter -I./src/autumn -I./src/_shaders -MMD -MP -c src/Shader.cpp -o dist/./src/Shader.cpp.o", - "file": "dist/./src/Shader.cpp.o" - }, - { - "directory": ".", - "command": "g++ -I./src -I./src/spring -I./src/shaders -I./src/tools -I./src/summer -I./src/winter -I./src/autumn -I./src/_shaders -MMD -MP -c src/summer/SummerTheme.cpp -o dist/./src/summer/SummerTheme.cpp.o", - "file": "dist/./src/summer/SummerTheme.cpp.o" - }, - { - "directory": ".", - "command": "g++ -I./src -I./src/spring -I./src/shaders -I./src/tools -I./src/summer -I./src/winter -I./src/autumn -I./src/_shaders -MMD -MP -c src/winter/Windfield.cpp -o dist/./src/winter/Windfield.cpp.o", - "file": "dist/./src/winter/Windfield.cpp.o" - }, - { - "directory": ".", - "command": "g++ -I./src -I./src/spring -I./src/shaders -I./src/tools -I./src/summer -I./src/winter -I./src/autumn -I./src/_shaders -MMD -MP -c src/winter/WinterTheme.cpp -o dist/./src/winter/WinterTheme.cpp.o", - "file": "dist/./src/winter/WinterTheme.cpp.o" - }, - { - "directory": ".", - "command": "g++ -I./src -I./src/spring -I./src/shaders -I./src/tools -I./src/summer -I./src/winter -I./src/autumn -I./src/_shaders -MMD -MP -c src/winter/Snowflake.cpp -o dist/./src/winter/Snowflake.cpp.o", - "file": "dist/./src/winter/Snowflake.cpp.o" - }, - { - "directory": ".", - "command": "g++ -I./src -I./src/spring -I./src/shaders -I./src/tools -I./src/summer -I./src/winter -I./src/autumn -I./src/_shaders -MMD -MP -c src/autumn/AutumnTheme.cpp -o dist/./src/autumn/AutumnTheme.cpp.o", - "file": "dist/./src/autumn/AutumnTheme.cpp.o" - }, - { - "directory": ".", - "command": "g++ -I./src -I./src/spring -I./src/shaders -I./src/tools -I./src/summer -I./src/winter -I./src/autumn -I./src/_shaders -MMD -MP -c src/autumn/TreeShape.cpp -o dist/./src/autumn/TreeShape.cpp.o", - "file": "dist/./src/autumn/TreeShape.cpp.o" - }, - { - "directory": ".", - "command": "g++ -I./src -I./src/spring -I./src/shaders -I./src/tools -I./src/summer -I./src/winter -I./src/autumn -I./src/_shaders -MMD -MP -c src/autumn/LeafParticleRender.cpp -o dist/./src/autumn/LeafParticleRender.cpp.o", - "file": "dist/./src/autumn/LeafParticleRender.cpp.o" - }, - { - "directory": ".", - "command": "g++ -I./src -I./src/spring -I./src/shaders -I./src/tools -I./src/summer -I./src/winter -I./src/autumn -I./src/_shaders -MMD -MP -c src/shapes_2d.cpp -o dist/./src/shapes_2d.cpp.o", - "file": "dist/./src/shapes_2d.cpp.o" - }, - { - "directory": ".", - "command": "g++ -I./src -I./src/spring -I./src/shaders -I./src/tools -I./src/summer -I./src/winter -I./src/autumn -I./src/_shaders -MMD -MP -c src/Renderer2d.cpp -o dist/./src/Renderer2d.cpp.o", - "file": "dist/./src/Renderer2d.cpp.o" - }, - { - "directory": ".", - "command": "g++ -I./src -I./src/spring -I./src/shaders -I./src/tools -I./src/summer -I./src/winter -I./src/autumn -I./src/_shaders -MMD -MP -c src/main.cpp -o dist/./src/main.cpp.o", - "file": "dist/./src/main.cpp.o" - }, - { - "directory": ".", - "command": "g++ -I./src -I./src/spring -I./src/shaders -I./src/tools -I./src/summer -I./src/winter -I./src/autumn -I./src/_shaders -MMD -MP -c src/WebglContext.cpp -o dist/./src/WebglContext.cpp.o", - "file": "dist/./src/WebglContext.cpp.o" - } -] diff --git a/themes/compile_commands.sh b/themes/compile_commands.sh deleted file mode 100755 index 08a9eed..0000000 --- a/themes/compile_commands.sh +++ /dev/null @@ -1,7 +0,0 @@ -make --always-make --dry-run \ - | grep -wE 'em++|emcc|gcc|g\+\+|c\+\+' \ - | grep -w '\-c' \ - | jq -nR '[inputs|{directory:".", command:., file: match(" [^ ]+$").string[1:]}]' \ - > compile_commands.json - -sed -i -e 's/em++/g++/g' compile_commands.json diff --git a/themes/dist/output.js b/themes/dist/output.js index 13d1675..011ac68 100644 --- a/themes/dist/output.js +++ b/themes/dist/output.js @@ -2,7 +2,7 @@ // The Module object: Our interface to the outside world. We import // and export values on it. There are various ways Module can be used: // 1. Not defined. We create it here -// 2. A function parameter, function(Module) { ..generated code.. } +// 2. A function parameter, function(moduleArg) => Promise<Module> // 3. pre-run appended it, var Module = {}; ..generated code.. // 4. External script tag defines var Module. // We need to check if Module already exists (e.g. case 3 above). @@ -14,6 +14,25 @@ // can continue to use Module afterwards as well. var Module = typeof Module != 'undefined' ? Module : {}; +// Determine the runtime environment we are in. You can customize this by +// setting the ENVIRONMENT setting at compile time (see settings.js). + +// Attempt to auto-detect the environment +var ENVIRONMENT_IS_WEB = typeof window == 'object'; +var ENVIRONMENT_IS_WORKER = typeof importScripts == 'function'; +// N.b. Electron.js environment is simultaneously a NODE-environment, but +// also a web environment. +var ENVIRONMENT_IS_NODE = typeof process == 'object' && typeof process.versions == 'object' && typeof process.versions.node == 'string' && process.type != 'renderer'; +var ENVIRONMENT_IS_SHELL = !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIRONMENT_IS_WORKER; + +if (ENVIRONMENT_IS_NODE) { + // `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. + // TODO: Swap all `require()`'s with `import()`'s? + +} + // --pre-jses are emitted after the Module integration code, so that they can // refer to Module (if they choose; they can also define Module) @@ -31,21 +50,6 @@ var quit_ = (status, toThrow) => { throw toThrow; }; -// Determine the runtime environment we are in. You can customize this by -// setting the ENVIRONMENT setting at compile time (see settings.js). - -// Attempt to auto-detect the environment -var ENVIRONMENT_IS_WEB = typeof window == 'object'; -var ENVIRONMENT_IS_WORKER = typeof importScripts == 'function'; -// N.b. Electron.js environment is simultaneously a NODE-environment, but -// also a web environment. -var ENVIRONMENT_IS_NODE = typeof process == 'object' && typeof process.versions == 'object' && typeof process.versions.node == 'string'; -var ENVIRONMENT_IS_SHELL = !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIRONMENT_IS_WORKER; - -if (Module['ENVIRONMENT']) { - throw new Error('Module.ENVIRONMENT has been deprecated. To force the environment, use the ENVIRONMENT compile-time option (for example, -sENVIRONMENT=web or -sENVIRONMENT=node)'); -} - // `/` should be present at the end if `scriptDirectory` is not empty var scriptDirectory = ''; function locateFile(path) { @@ -56,9 +60,7 @@ function locateFile(path) { } // Hooks that are implemented differently in different runtime environments. -var read_, - readAsync, - readBinary; +var readAsync, readBinary; 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?)'); @@ -71,44 +73,31 @@ if (ENVIRONMENT_IS_NODE) { throw new Error('This emscripten-generated code requires node v16.0.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. - // TODO: Swap all `require()`'s with `import()`'s? // These modules will usually be used on Node.js. Load them eagerly to avoid // the complexity of lazy-loading. var fs = require('fs'); var nodePath = require('path'); - if (ENVIRONMENT_IS_WORKER) { - scriptDirectory = nodePath.dirname(scriptDirectory) + '/'; - } else { - scriptDirectory = __dirname + '/'; - } + scriptDirectory = __dirname + '/'; // include: node_shell_read.js -read_ = (filename, binary) => { +readBinary = (filename) => { // We need to re-wrap `file://` strings to URLs. Normalizing isn't // necessary in that case, the path should already be absolute. filename = isFileURI(filename) ? new URL(filename) : nodePath.normalize(filename); - return fs.readFileSync(filename, binary ? undefined : 'utf8'); -}; - -readBinary = (filename) => { - var ret = read_(filename, true); - if (!ret.buffer) { - ret = new Uint8Array(ret); - } + var ret = fs.readFileSync(filename); assert(ret.buffer); return ret; }; -readAsync = (filename, onload, onerror, binary = true) => { - // See the comment in the `read_` function. +readAsync = (filename, binary = true) => { + // See the comment in the `readBinary` function. filename = isFileURI(filename) ? new URL(filename) : nodePath.normalize(filename); - fs.readFile(filename, binary ? undefined : 'utf8', (err, data) => { - if (err) onerror(err); - else onload(binary ? data.buffer : data); + return new Promise((resolve, reject) => { + fs.readFile(filename, binary ? undefined : 'utf8', (err, data) => { + if (err) reject(err); + else resolve(binary ? data.buffer : data); + }); }); }; // end include: node_shell_read.js @@ -122,89 +111,16 @@ readAsync = (filename, onload, onerror, binary = true) => { module['exports'] = Module; } - process.on('uncaughtException', (ex) => { - // suppress ExitStatus exceptions from showing an error - if (ex !== 'unwind' && !(ex instanceof ExitStatus) && !(ex.context instanceof ExitStatus)) { - throw ex; - } - }); - quit_ = (status, toThrow) => { process.exitCode = status; throw toThrow; }; - Module['inspect'] = () => '[Emscripten Module object]'; - } else 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_ = read; - } - - readBinary = (f) => { - if (typeof readbuffer == 'function') { - return new Uint8Array(readbuffer(f)); - } - let data = read(f, 'binary'); - assert(typeof data == 'object'); - return data; - }; - - readAsync = (f, onload, onerror) => { - setTimeout(() => onload(readBinary(f))); - }; - - if (typeof clearTimeout == 'undefined') { - globalThis.clearTimeout = (id) => {}; - } - - if (typeof setTimeout == 'undefined') { - // spidermonkey lacks setTimeout but we use it above in readAsync. - globalThis.setTimeout = (f) => (typeof f == 'function') ? f() : abort(); - } - - if (typeof scriptArgs != 'undefined') { - arguments_ = scriptArgs; - } else if (typeof arguments != 'undefined') { - arguments_ = arguments; - } - - if (typeof quit == 'function') { - quit_ = (status, toThrow) => { - // 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; - }; - } - - if (typeof print != 'undefined') { - // Prefer to use print/printErr where they exist, as they usually work better. - if (typeof console == 'undefined') console = /** @type{!Console} */({}); - console.log = /** @type{!function(this:Console, ...*): undefined} */ (print); - console.warn = console.error = /** @type{!function(this:Console, ...*): undefined} */ (typeof printErr != 'undefined' ? printErr : print); - } - } else // Note that this includes Node.js workers when relevant (pthreads is enabled). @@ -222,26 +138,17 @@ if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { // and scriptDirectory will correctly be replaced with an empty string. // If scriptDirectory contains a query (starting with ?) or a fragment (starting with #), // they are removed because they could contain a slash. - if (scriptDirectory.indexOf('blob:') !== 0) { - scriptDirectory = scriptDirectory.substr(0, scriptDirectory.replace(/[?#].*/, "").lastIndexOf('/')+1); - } else { + if (scriptDirectory.startsWith('blob:')) { scriptDirectory = ''; + } else { + scriptDirectory = scriptDirectory.substr(0, scriptDirectory.replace(/[?#].*/, '').lastIndexOf('/')+1); } if (!(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?)'); - // Differentiate the Web Worker from the Node Worker case, as reading must - // be done differently. { // include: web_or_worker_shell_read.js -read_ = (url) => { - var xhr = new XMLHttpRequest(); - xhr.open('GET', url, false); - xhr.send(null); - return xhr.responseText; - } - - if (ENVIRONMENT_IS_WORKER) { +if (ENVIRONMENT_IS_WORKER) { readBinary = (url) => { var xhr = new XMLHttpRequest(); xhr.open('GET', url, false); @@ -251,21 +158,35 @@ read_ = (url) => { }; } - readAsync = (url, onload, onerror) => { - var xhr = new XMLHttpRequest(); - xhr.open('GET', url, true); - xhr.responseType = 'arraybuffer'; - xhr.onload = () => { - if (xhr.status == 200 || (xhr.status == 0 && xhr.response)) { // file URLs can return 0 - onload(xhr.response); - return; - } - onerror(); - }; - xhr.onerror = onerror; - xhr.send(null); - } - + readAsync = (url) => { + // 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 XHR on webview if URL is a file URL. + if (isFileURI(url)) { + return new Promise((resolve, reject) => { + var xhr = new XMLHttpRequest(); + xhr.open('GET', url, true); + xhr.responseType = 'arraybuffer'; + xhr.onload = () => { + if (xhr.status == 200 || (xhr.status == 0 && xhr.response)) { // file URLs can return 0 + resolve(xhr.response); + return; + } + reject(xhr.status); + }; + xhr.onerror = reject; + xhr.send(null); + }); + } + return fetch(url, { credentials: 'same-origin' }) + .then((response) => { + if (response.ok) { + return response.arrayBuffer(); + } + return Promise.reject(new Error(response.status + ' : ' + response.url)); + }) + }; // end include: web_or_worker_shell_read.js } } else @@ -279,7 +200,7 @@ var err = Module['printErr'] || console.error.bind(console); // Merge back in the overrides Object.assign(Module, moduleOverrides); // Free the object hierarchy contained in the overrides, this lets the GC -// reclaim data used e.g. in memoryInitializerRequest, which is a large typed array. +// reclaim data used. moduleOverrides = null; checkIncomingModuleAPI(); @@ -292,21 +213,18 @@ if (Module['arguments']) arguments_ = Module['arguments'];legacyModuleProp('argu if (Module['thisProgram']) thisProgram = Module['thisProgram'];legacyModuleProp('thisProgram', 'thisProgram'); -if (Module['quit']) quit_ = Module['quit'];legacyModuleProp('quit', 'quit_'); - // perform assertions in shell.js after we set up out() and err(), as otherwise if an assertion fails it cannot print the message // Assertions on removed incoming Module JS APIs. assert(typeof Module['memoryInitializerPrefixURL'] == 'undefined', 'Module.memoryInitializerPrefixURL option was removed, use Module.locateFile instead'); assert(typeof Module['pthreadMainPrefixURL'] == 'undefined', 'Module.pthreadMainPrefixURL option was removed, use Module.locateFile instead'); assert(typeof Module['cdInitializerPrefixURL'] == 'undefined', 'Module.cdInitializerPrefixURL option was removed, use Module.locateFile instead'); assert(typeof Module['filePackagePrefixURL'] == 'undefined', 'Module.filePackagePrefixURL option was removed, use Module.locateFile instead'); -assert(typeof Module['read'] == 'undefined', 'Module.read option was removed (modify read_ in JS)'); +assert(typeof Module['read'] == 'undefined', 'Module.read option was removed'); assert(typeof Module['readAsync'] == 'undefined', 'Module.readAsync option was removed (modify readAsync in JS)'); assert(typeof Module['readBinary'] == 'undefined', 'Module.readBinary option was removed (modify readBinary in JS)'); assert(typeof Module['setWindowTitle'] == 'undefined', 'Module.setWindowTitle option was removed (modify emscripten_set_window_title in JS)'); assert(typeof Module['TOTAL_MEMORY'] == 'undefined', 'Module.TOTAL_MEMORY has been renamed Module.INITIAL_MEMORY'); legacyModuleProp('asm', 'wasmExports'); -legacyModuleProp('read', 'read_'); legacyModuleProp('readAsync', 'readAsync'); legacyModuleProp('readBinary', 'readBinary'); legacyModuleProp('setWindowTitle', 'setWindowTitle'); @@ -320,10 +238,10 @@ var OPFS = 'OPFS is no longer included by default; build with -lopfs.js'; var NODEFS = 'NODEFS is no longer included by default; build with -lnodefs.js'; -assert(!ENVIRONMENT_IS_SHELL, "shell environment detected but not enabled at build time. Add 'shell' to `-sENVIRONMENT` to enable."); - +assert(!ENVIRONMENT_IS_SHELL, 'shell environment detected but not enabled at build time. Add `shell` to `-sENVIRONMENT` to enable.'); // end include: shell.js + // include: preamble.js // === Preamble library stuff === @@ -335,12 +253,10 @@ assert(!ENVIRONMENT_IS_SHELL, "shell environment detected but not enabled at bui // An online HTML version (which may be of a different version of Emscripten) // is up at http://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html -var wasmBinary; -if (Module['wasmBinary']) wasmBinary = Module['wasmBinary'];legacyModuleProp('wasmBinary', 'wasmBinary'); -var noExitRuntime = Module['noExitRuntime'] || true;legacyModuleProp('noExitRuntime', 'noExitRuntime'); +var wasmBinary = Module['wasmBinary'];legacyModuleProp('wasmBinary', 'wasmBinary'); if (typeof WebAssembly != 'object') { - abort('no native wasm support detected'); + err('no native wasm support detected'); } // Wasm globals @@ -360,6 +276,10 @@ var ABORT = false; // but only when noExitRuntime is false. var EXITSTATUS; +// In STRICT mode, we only define assert() when ASSERTIONS is set. i.e. we +// don't define it at all in release modes. This matches the behaviour of +// MINIMAL_RUNTIME. +// TODO(sbc): Make this the default even without STRICT enabled. /** @type {function(*, string=)} */ function assert(condition, text) { if (!condition) { @@ -390,6 +310,7 @@ var HEAP, /** @type {!Float64Array} */ HEAPF64; +// include: runtime_shared.js function updateMemoryViews() { var b = wasmMemory.buffer; Module['HEAP8'] = HEAP8 = new Int8Array(b); @@ -402,6 +323,7 @@ function updateMemoryViews() { Module['HEAPF64'] = HEAPF64 = new Float64Array(b); } +// end include: runtime_shared.js assert(!Module['STACK_SIZE'], 'STACK_SIZE can no longer be set at runtime. Use -sSTACK_SIZE at link time') assert(typeof Int32Array != 'undefined' && typeof Float64Array !== 'undefined' && Int32Array.prototype.subarray != undefined && Int32Array.prototype.set != undefined, @@ -449,16 +371,6 @@ function checkStackCookie() { } } // end include: runtime_stack_check.js -// include: runtime_assertions.js -// Endianness check -(function() { - var h16 = new Int16Array(1); - var h8 = new Int8Array(h16.buffer); - h16[0] = 0x6373; - if (h8[0] !== 0x73 || h8[1] !== 0x63) throw 'Runtime error: expected the system to be little-endian! (Run with -sSUPPORT_BIG_ENDIAN to bypass)'; -})(); - -// end include: runtime_assertions.js var __ATPRERUN__ = []; // functions called before the runtime is initialized var __ATINIT__ = []; // functions called during startup var __ATMAIN__ = []; // functions called when main() is to be run @@ -467,18 +379,11 @@ var __ATPOSTRUN__ = []; // functions called after the main() is called var runtimeInitialized = false; -var runtimeKeepaliveCounter = 0; - -function keepRuntimeAlive() { - return noExitRuntime || runtimeKeepaliveCounter > 0; -} - function preRun() { - if (Module['preRun']) { - if (typeof Module['preRun'] == 'function') Module['preRun'] = [Module['preRun']]; - while (Module['preRun'].length) { - addOnPreRun(Module['preRun'].shift()); - } + var preRuns = Module['preRun']; + if (preRuns) { + if (typeof preRuns == 'function') preRuns = [preRuns]; + preRuns.forEach(addOnPreRun); } callRuntimeCallbacks(__ATPRERUN__); } @@ -502,11 +407,10 @@ function preMain() { function postRun() { checkStackCookie(); - if (Module['postRun']) { - if (typeof Module['postRun'] == 'function') Module['postRun'] = [Module['postRun']]; - while (Module['postRun'].length) { - addOnPostRun(Module['postRun'].shift()); - } + var postRuns = Module['postRun']; + if (postRuns) { + if (typeof postRuns == 'function') postRuns = [postRuns]; + postRuns.forEach(addOnPostRun); } callRuntimeCallbacks(__ATPOSTRUN__); @@ -568,9 +472,7 @@ function getUniqueRunDependency(id) { function addRunDependency(id) { runDependencies++; - if (Module['monitorRunDependencies']) { - Module['monitorRunDependencies'](runDependencies); - } + Module['monitorRunDependencies']?.(runDependencies); if (id) { assert(!runDependencyTracking[id]); @@ -604,9 +506,7 @@ function addRunDependency(id) { function removeRunDependency(id) { runDependencies--; - if (Module['monitorRunDependencies']) { - Module['monitorRunDependencies'](runDependencies); - } + Module['monitorRunDependencies']?.(runDependencies); if (id) { assert(runDependencyTracking[id]); @@ -629,9 +529,7 @@ function removeRunDependency(id) { /** @param {string|number=} what */ function abort(what) { - if (Module['onAbort']) { - Module['onAbort'](what); - } + Module['onAbort']?.(what); what = 'Aborted(' + what + ')'; // TODO(sbc): Should we remove printing and leave it up to whoever @@ -639,7 +537,6 @@ function abort(what) { err(what); ABORT = true; - EXITSTATUS = 1; // Use a wasm runtime error, because a JS error might be seen as a foreign // exception, which means we'd run destructors on it. We need the error to @@ -651,7 +548,7 @@ function abort(what) { // allows this in the wasm spec. // Suppress closure compiler warning here. Closure compiler's builtin extern - // defintion for WebAssembly.RuntimeError claims it takes no arguments even + // definition for WebAssembly.RuntimeError claims it takes no arguments even // though it can. // TODO(https://github.com/google/closure-compiler/pull/3913): Remove if/when upstream closure gets fixed. /** @suppress {checkTypes} */ @@ -688,33 +585,40 @@ Module['FS_createPreloadedFile'] = FS.createPreloadedFile; // Prefix of data URIs emitted by SINGLE_FILE and related options. var dataURIPrefix = 'data:application/octet-stream;base64,'; -// Indicates whether filename is a base64 data URI. -function isDataURI(filename) { - // Prefix of data URIs emitted by SINGLE_FILE and related options. - return filename.startsWith(dataURIPrefix); -} - -// Indicates whether filename is delivered via file protocol (as opposed to http/https) -function isFileURI(filename) { - return filename.startsWith('file://'); -} +/** + * Indicates whether filename is a base64 data URI. + * @noinline + */ +var isDataURI = (filename) => filename.startsWith(dataURIPrefix); + +/** + * Indicates whether filename is delivered via file protocol (as opposed to http/https) + * @noinline + */ +var isFileURI = (filename) => filename.startsWith('file://'); // end include: URIUtils.js -function createExportWrapper(name) { - return function() { +function createExportWrapper(name, nargs) { + return (...args) => { assert(runtimeInitialized, `native function \`${name}\` called before runtime initialization`); var f = wasmExports[name]; assert(f, `exported native function \`${name}\` not found`); - return f.apply(null, arguments); + // Only assert for too many arguments. Too few can be valid since the missing arguments will be zero filled. + assert(args.length <= nargs, `native function \`${name}\` called with ${args.length} args but expects ${nargs}`); + return f(...args); }; } // include: runtime_exceptions.js // end include: runtime_exceptions.js +function findWasmBinary() { + var f = 'output.wasm'; + if (!isDataURI(f)) { + return locateFile(f); + } + return f; +} + var wasmBinaryFile; - wasmBinaryFile = 'output.wasm'; - if (!isDataURI(wasmBinaryFile)) { - wasmBinaryFile = locateFile(wasmBinaryFile); - } function getBinarySync(file) { if (file == wasmBinaryFile && wasmBinary) { @@ -723,33 +627,19 @@ function getBinarySync(file) { if (readBinary) { return readBinary(file); } - throw "both async and sync fetching of the wasm failed"; + throw 'both async and sync fetching of the wasm failed'; } 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 we don't have the binary yet, load it asynchronously using readAsync. if (!wasmBinary - && (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER)) { - if (typeof fetch == 'function' - && !isFileURI(binaryFile) - ) { - return fetch(binaryFile, { credentials: 'same-origin' }).then((response) => { - if (!response['ok']) { - throw "failed to load wasm binary file at '" + binaryFile + "'"; - } - return response['arrayBuffer'](); - }).catch(() => getBinarySync(binaryFile)); - } - else if (readAsync) { - // fetch is not available or url is file => try XHR (readAsync uses XHR internally) - return new Promise((resolve, reject) => { - readAsync(binaryFile, (response) => resolve(new Uint8Array(/** @type{!ArrayBuffer} */(response))), reject) - }); - } + ) { + // Fetch the binary using readAsync + return readAsync(binaryFile).then( + (response) => new Uint8Array(/** @type{!ArrayBuffer} */(response)), + // Fall back to getBinarySync if readAsync fails + () => getBinarySync(binaryFile) + ); } // Otherwise, getBinarySync should be able to get it synchronously @@ -759,8 +649,6 @@ function getBinaryPromise(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}`); @@ -808,14 +696,18 @@ function instantiateAsync(binary, binaryFile, imports, callback) { return instantiateArrayBuffer(binaryFile, imports, callback); } -// Create the wasm instance. -// Receives the wasm imports, returns the exports. -function createWasm() { +function getWasmImports() { // prepare imports - var info = { + return { 'env': wasmImports, 'wasi_snapshot_preview1': wasmImports, - }; + } +} + +// Create the wasm instance. +// Receives the wasm imports, returns the exports. +function createWasm() { + var info = getWasmImports(); // Load the wasm module and create an instance of using native support in the JS engine. // handle a generated wasm instance, receiving its exports and // performing other necessary setup @@ -827,16 +719,12 @@ function createWasm() { wasmMemory = wasmExports['memory']; - assert(wasmMemory, "memory not found in wasm exports"); - // This assertion doesn't hold when emscripten is run in --post-link - // mode. - // TODO(sbc): Read INITIAL_MEMORY out of the wasm file in post-link mode. - //assert(wasmMemory.buffer.byteLength === 16777216); + assert(wasmMemory, 'memory not found in wasm exports'); updateMemoryViews(); wasmTable = wasmExports['__indirect_function_table']; - assert(wasmTable, "table not found in wasm exports"); + assert(wasmTable, 'table not found in wasm exports'); addOnInit(wasmExports['__wasm_call_ctors']); @@ -868,7 +756,6 @@ function createWasm() { // Also pthreads and wasm workers initialize the wasm instance through this // path. if (Module['instantiateWasm']) { - try { return Module['instantiateWasm'](info, receiveInstance); } catch(e) { @@ -877,6 +764,8 @@ function createWasm() { } } + wasmBinaryFile ??= findWasmBinary(); + instantiateAsync(wasmBinary, wasmBinaryFile, info, receiveInstantiationResult); return {}; // no exports yet; we'll fill them in later } @@ -886,12 +775,24 @@ var tempDouble; var tempI64; // include: runtime_debug.js -function legacyModuleProp(prop, newName, incomming=true) { +// Endianness check +(() => { + var h16 = new Int16Array(1); + var h8 = new Int8Array(h16.buffer); + h16[0] = 0x6373; + if (h8[0] !== 0x73 || h8[1] !== 0x63) throw 'Runtime error: expected the system to be little-endian! (Run with -sSUPPORT_BIG_ENDIAN to bypass)'; +})(); + +if (Module['ENVIRONMENT']) { + throw new Error('Module.ENVIRONMENT has been deprecated. To force the environment, use the ENVIRONMENT compile-time option (for example, -sENVIRONMENT=web or -sENVIRONMENT=node)'); +} + +function legacyModuleProp(prop, newName, incoming=true) { if (!Object.getOwnPropertyDescriptor(Module, prop)) { Object.defineProperty(Module, prop, { configurable: true, get() { - let extra = incomming ? ' (the initial value can be provided on Module, but after startup the value is only looked for on a local variable of that name)' : ''; + let extra = incoming ? ' (the initial value can be provided on Module, but after startup the value is only looked for on a local variable of that name)' : ''; abort(`\`Module.${prop}\` has been replaced by \`${newName}\`` + extra); } @@ -918,46 +819,52 @@ function isExportedByForceFilesystem(name) { name === 'removeRunDependency'; } -function missingGlobal(sym, msg) { - if (typeof globalThis !== 'undefined') { +/** + * Intercept access to a global symbol. This enables us to give informative + * warnings/errors when folks attempt to use symbols they did not include in + * their build, or no symbols that no longer exist. + */ +function hookGlobalSymbolAccess(sym, func) { + if (typeof globalThis != 'undefined' && !Object.getOwnPropertyDescriptor(globalThis, sym)) { Object.defineProperty(globalThis, sym, { configurable: true, get() { - warnOnce('`' + sym + '` is not longer defined by emscripten. ' + msg); + func(); return undefined; } }); } } +function missingGlobal(sym, msg) { + hookGlobalSymbolAccess(sym, () => { + warnOnce(`\`${sym}\` is not longer defined by emscripten. ${msg}`); + }); +} + missingGlobal('buffer', 'Please use HEAP8.buffer or wasmMemory.buffer'); missingGlobal('asm', 'Please use wasmExports instead'); function missingLibrarySymbol(sym) { - if (typeof globalThis !== 'undefined' && !Object.getOwnPropertyDescriptor(globalThis, sym)) { - Object.defineProperty(globalThis, sym, { - configurable: true, - get() { - // Can't `abort()` here because it would break code that does runtime - // checks. e.g. `if (typeof SDL === 'undefined')`. - var msg = '`' + sym + '` is a library symbol and not included by default; add it to your library.js __deps or to DEFAULT_LIBRARY_FUNCS_TO_INCLUDE on the command line'; - // DEFAULT_LIBRARY_FUNCS_TO_INCLUDE requires the name as it appears in - // library.js, which means $name for a JS name with no prefix, or name - // for a JS name like _name. - var librarySymbol = sym; - if (!librarySymbol.startsWith('_')) { - librarySymbol = '$' + sym; - } - msg += " (e.g. -sDEFAULT_LIBRARY_FUNCS_TO_INCLUDE='" + librarySymbol + "')"; - if (isExportedByForceFilesystem(sym)) { - msg += '. Alternatively, forcing filesystem support (-sFORCE_FILESYSTEM) can export this for you'; - } - warnOnce(msg); - return undefined; - } - }); - } - // Any symbol that is not included from the JS libary is also (by definition) + hookGlobalSymbolAccess(sym, () => { + // Can't `abort()` here because it would break code that does runtime + // checks. e.g. `if (typeof SDL === 'undefined')`. + var msg = `\`${sym}\` is a library symbol and not included by default; add it to your library.js __deps or to DEFAULT_LIBRARY_FUNCS_TO_INCLUDE on the command line`; + // DEFAULT_LIBRARY_FUNCS_TO_INCLUDE requires the name as it appears in + // library.js, which means $name for a JS name with no prefix, or name + // for a JS name like _name. + var librarySymbol = sym; + if (!librarySymbol.startsWith('_')) { + librarySymbol = '$' + sym; + } + msg += ` (e.g. -sDEFAULT_LIBRARY_FUNCS_TO_INCLUDE='${librarySymbol}')`; + if (isExportedByForceFilesystem(sym)) { + msg += '. Alternatively, forcing filesystem support (-sFORCE_FILESYSTEM) can export this for you'; + } + warnOnce(msg); + }); + + // Any symbol that is not included from the JS library is also (by definition) // not exported on the Module object. unexportedRuntimeSymbol(sym); } @@ -967,7 +874,7 @@ function unexportedRuntimeSymbol(sym) { Object.defineProperty(Module, sym, { configurable: true, get() { - var msg = "'" + sym + "' was not exported. add it to EXPORTED_RUNTIME_METHODS (see the Emscripten FAQ)"; + var msg = `'${sym}' was not exported. add it to EXPORTED_RUNTIME_METHODS (see the Emscripten FAQ)`; if (isExportedByForceFilesystem(sym)) { msg += '. Alternatively, forcing filesystem support (-sFORCE_FILESYSTEM) can export this for you'; } @@ -978,16 +885,16 @@ function unexportedRuntimeSymbol(sym) { } // Used by XXXXX_DEBUG settings to output debug messages. -function dbg(text) { +function dbg(...args) { // TODO(sbc): Make this configurable somehow. Its not always convenient for // logging to show up as warnings. - console.warn.apply(console, arguments); + console.warn(...args); } // end include: runtime_debug.js // === Body === - // end include: preamble.js + /** @constructor */ function ExitStatus(status) { this.name = 'ExitStatus'; @@ -996,10 +903,8 @@ function dbg(text) { } var callRuntimeCallbacks = (callbacks) => { - while (callbacks.length > 0) { - // Pass the module as the first argument. - callbacks.shift()(Module); - } + // Pass the module as the first argument. + callbacks.forEach((f) => f(Module)); }; @@ -1010,8 +915,8 @@ function dbg(text) { function getValue(ptr, type = 'i8') { if (type.endsWith('*')) type = '*'; switch (type) { - case 'i1': return HEAP8[((ptr)>>0)]; - case 'i8': return HEAP8[((ptr)>>0)]; + case 'i1': return HEAP8[ptr]; + case 'i8': return HEAP8[ptr]; case 'i16': return HEAP16[((ptr)>>1)]; case 'i32': return HEAP32[((ptr)>>2)]; case 'i64': abort('to do getValue(i64) use WASM_BIGINT'); @@ -1022,6 +927,8 @@ function dbg(text) { } } + var noExitRuntime = Module['noExitRuntime'] || true; + var ptrToString = (ptr) => { assert(typeof ptr === 'number'); // With CAN_ADDRESS_2GB or MEMORY64, pointers are already unsigned. @@ -1038,8 +945,8 @@ function dbg(text) { 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 'i1': HEAP8[ptr] = value; break; + case 'i8': HEAP8[ptr] = 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'); @@ -1050,8 +957,12 @@ function dbg(text) { } } + var stackRestore = (val) => __emscripten_stack_restore(val); + + var stackSave = () => _emscripten_stack_get_current(); + var warnOnce = (text) => { - if (!warnOnce.shown) warnOnce.shown = {}; + warnOnce.shown ||= {}; if (!warnOnce.shown[text]) { warnOnce.shown[text] = 1; if (ENVIRONMENT_IS_NODE) text = 'warning: ' + text; @@ -1059,6 +970,87 @@ function dbg(text) { } }; + var UTF8Decoder = typeof TextDecoder != 'undefined' ? new TextDecoder() : 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} + */ + var UTF8ArrayToString = (heapOrArray, idx = 0, maxBytesToRead = NaN) => { + 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/NaN 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} + */ + var UTF8ToString = (ptr, maxBytesToRead) => { + assert(typeof ptr == 'number', `UTF8ToString expects a number (got ${typeof ptr})`); + return ptr ? UTF8ArrayToString(HEAPU8, ptr, maxBytesToRead) : ''; + }; + var ___assert_fail = (condition, filename, line, func) => { + abort(`Assertion failed: ${UTF8ToString(condition)}, at: ` + [filename ? UTF8ToString(filename) : 'unknown filename', line, func ? UTF8ToString(func) : 'unknown function']); + }; + + var __abort_js = () => { + abort('native code called abort()'); + }; + function __emscripten_fetch_free(id) { if (Fetch.xhrs.has(id)) { var xhr = Fetch.xhrs.get(id); @@ -1070,9 +1062,9 @@ function dbg(text) { } } - var isLeapYear = (year) => { - return year%4 === 0 && (year%100 !== 0 || year%400 === 0); - }; + var __emscripten_memcpy_js = (dest, src, num) => HEAPU8.copyWithin(dest, src, src + num); + + var isLeapYear = (year) => 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]; @@ -1091,7 +1083,7 @@ function dbg(text) { return ((hi + 0x200000) >>> 0 < 0x400001 - !!lo) ? (lo >>> 0) + hi * 4294967296 : NaN; }; function __localtime_js(time_low, time_high,tmPtr) { - var time = convertI32PairToI53Checked(time_low, time_high);; + var time = convertI32PairToI53Checked(time_low, time_high); var date = new Date(time*1000); @@ -1116,29 +1108,8 @@ function dbg(text) { ; } - var 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; - }; - var stringToUTF8Array = (str, heap, outIdx, maxBytesToWrite) => { - assert(typeof str === 'string'); + assert(typeof str === 'string', `stringToUTF8Array expects a string (got ${typeof str})`); // Parameter maxBytesToWrite is not optional. Negative values, 0, null, // undefined and false each don't write out any bytes. if (!(maxBytesToWrite > 0)) @@ -1189,13 +1160,27 @@ function dbg(text) { return stringToUTF8Array(str, HEAPU8, outPtr, maxBytesToWrite); }; - var stringToNewUTF8 = (str) => { - var size = lengthBytesUTF8(str) + 1; - var ret = _malloc(size); - if (ret) stringToUTF8(str, ret, size); - return ret; + var 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; }; - var __tzset_js = (timezone, daylight, tzname) => { + var __tzset_js = (timezone, daylight, std_name, dst_name) => { // TODO: Use (malleable) environment variables instead of system settings. var currentYear = new Date().getFullYear(); var winter = new Date(currentYear, 0, 1); @@ -1203,9 +1188,12 @@ function dbg(text) { var winterOffset = winter.getTimezoneOffset(); var summerOffset = summer.getTimezoneOffset(); - // Local standard timezone offset. Local standard time is not adjusted for daylight savings. - // This code uses the fact that getTimezoneOffset returns a greater value during Standard Time versus Daylight Saving Time (DST). - // Thus it determines the expected output during Standard Time, and it compares whether the output of the given date the same (Standard) or less (DST). + // Local standard timezone offset. Local standard time is not adjusted for + // daylight savings. This code uses the fact that getTimezoneOffset returns + // a greater value during Standard Time versus Daylight Saving Time (DST). + // Thus it determines the expected output during Standard Time, and it + // compares whether the output of the given date the same (Standard) or less + // (DST). var stdTimezoneOffset = Math.max(winterOffset, summerOffset); // timezone is specified as seconds west of UTC ("The external variable @@ -1217,51 +1205,44 @@ function dbg(text) { HEAP32[((daylight)>>2)] = Number(winterOffset != summerOffset); - function extractZone(date) { - var match = date.toTimeString().match(/\(([A-Za-z ]+)\)$/); - return match ? match[1] : "GMT"; - }; - var winterName = extractZone(winter); - var summerName = extractZone(summer); - var winterNamePtr = stringToNewUTF8(winterName); - var summerNamePtr = stringToNewUTF8(summerName); + var extractZone = (timezoneOffset) => { + // Why inverse sign? + // Read here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getTimezoneOffset + var sign = timezoneOffset >= 0 ? "-" : "+"; + + var absOffset = Math.abs(timezoneOffset) + var hours = String(Math.floor(absOffset / 60)).padStart(2, "0"); + var minutes = String(absOffset % 60).padStart(2, "0"); + + return `UTC${sign}${hours}${minutes}`; + } + + var winterName = extractZone(winterOffset); + var summerName = extractZone(summerOffset); + assert(winterName); + assert(summerName); + assert(lengthBytesUTF8(winterName) <= 16, `timezone name truncated to fit in TZNAME_MAX (${winterName})`); + assert(lengthBytesUTF8(summerName) <= 16, `timezone name truncated to fit in TZNAME_MAX (${summerName})`); if (summerOffset < winterOffset) { // Northern hemisphere - HEAPU32[((tzname)>>2)] = winterNamePtr; - HEAPU32[(((tzname)+(4))>>2)] = summerNamePtr; + stringToUTF8(winterName, std_name, 17); + stringToUTF8(summerName, dst_name, 17); } else { - HEAPU32[((tzname)>>2)] = summerNamePtr; - HEAPU32[(((tzname)+(4))>>2)] = winterNamePtr; + stringToUTF8(winterName, dst_name, 17); + stringToUTF8(summerName, std_name, 17); } }; - var _abort = () => { - abort('native code called abort()'); - }; - var _emscripten_date_now = () => Date.now(); - var withStackSave = (f) => { - var stack = stackSave(); - var ret = f(); - stackRestore(stack); - return ret; - }; var JSEvents = { - inEventHandler:0, removeAllEventListeners() { - for (var i = JSEvents.eventHandlers.length-1; i >= 0; --i) { - JSEvents._removeHandler(i); + while (JSEvents.eventHandlers.length) { + JSEvents._removeHandler(JSEvents.eventHandlers.length - 1); } - JSEvents.eventHandlers = []; JSEvents.deferredCalls = []; }, - registerRemoveEventListeners() { - if (!JSEvents.removeEventListenersRegistered) { - __ATEXIT__.push(JSEvents.removeAllEventListeners); - JSEvents.removeEventListenersRegistered = true; - } - }, + inEventHandler:0, deferredCalls:[], deferCall(targetFunction, precedence, argsList) { function arraysHaveEqualContent(arrA, arrB) { @@ -1273,8 +1254,7 @@ function dbg(text) { return true; } // Test if the given call was already queued, and if so, don't add it again. - for (var i in JSEvents.deferredCalls) { - var call = JSEvents.deferredCalls[i]; + for (var call of JSEvents.deferredCalls) { if (call.targetFunction == targetFunction && arraysHaveEqualContent(call.argsList, argsList)) { return; } @@ -1288,12 +1268,7 @@ function dbg(text) { JSEvents.deferredCalls.sort((x,y) => x.precedence < y.precedence); }, removeDeferredCalls(targetFunction) { - for (var i = 0; i < JSEvents.deferredCalls.length; ++i) { - if (JSEvents.deferredCalls[i].targetFunction == targetFunction) { - JSEvents.deferredCalls.splice(i, 1); - --i; - } - } + JSEvents.deferredCalls = JSEvents.deferredCalls.filter((call) => call.targetFunction != targetFunction); }, canPerformEventHandlerRequests() { if (navigator.userActivation) { @@ -1311,11 +1286,10 @@ function dbg(text) { if (!JSEvents.canPerformEventHandlerRequests()) { return; } - for (var i = 0; i < JSEvents.deferredCalls.length; ++i) { - var call = JSEvents.deferredCalls[i]; - JSEvents.deferredCalls.splice(i, 1); - --i; - call.targetFunction.apply(null, call.argsList); + var deferredCalls = JSEvents.deferredCalls; + JSEvents.deferredCalls = []; + for (var call of deferredCalls) { + call.targetFunction(...call.argsList); } }, eventHandlers:[], @@ -1338,25 +1312,25 @@ function dbg(text) { console.dir(eventHandler); return -4; } - var jsEventHandler = function jsEventHandler(event) { - // Increment nesting count for the event handler. - ++JSEvents.inEventHandler; - JSEvents.currentEventHandler = eventHandler; - // Process any old deferred calls the user has placed. - JSEvents.runDeferredCalls(); - // Process the actual event, calls back to user C code handler. - eventHandler.handlerFunc(event); - // Process any new deferred calls that were placed right now from this event handler. - JSEvents.runDeferredCalls(); - // Out of event handler - restore nesting count. - --JSEvents.inEventHandler; - }; - if (eventHandler.callbackfunc) { - eventHandler.eventListenerFunc = jsEventHandler; - eventHandler.target.addEventListener(eventHandler.eventTypeString, jsEventHandler, eventHandler.useCapture); + eventHandler.eventListenerFunc = function(event) { + // Increment nesting count for the event handler. + ++JSEvents.inEventHandler; + JSEvents.currentEventHandler = eventHandler; + // Process any old deferred calls the user has placed. + JSEvents.runDeferredCalls(); + // Process the actual event, calls back to user C code handler. + eventHandler.handlerFunc(event); + // Process any new deferred calls that were placed right now from this event handler. + JSEvents.runDeferredCalls(); + // Out of event handler - restore nesting count. + --JSEvents.inEventHandler; + }; + + eventHandler.target.addEventListener(eventHandler.eventTypeString, + eventHandler.eventListenerFunc, + eventHandler.useCapture); JSEvents.eventHandlers.push(eventHandler); - JSEvents.registerRemoveEventListeners(); } else { for (var i = 0; i < JSEvents.eventHandlers.length; ++i) { if (JSEvents.eventHandlers[i].target == eventHandler.target @@ -1371,7 +1345,7 @@ function dbg(text) { if (!target) return ''; if (target == window) return '#window'; if (target == screen) return '#screen'; - return (target && target.nodeName) ? target.nodeName : ''; + return target?.nodeName || ''; }, fullscreenEnabled() { return document.fullscreenEnabled @@ -1382,79 +1356,6 @@ function dbg(text) { }, }; - 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} - */ - var 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} - */ - var UTF8ToString = (ptr, maxBytesToRead) => { - assert(typeof ptr == 'number'); - return ptr ? UTF8ArrayToString(HEAPU8, ptr, maxBytesToRead) : ''; - }; var 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). @@ -1463,6 +1364,7 @@ function dbg(text) { return cString > 2 ? UTF8ToString(cString) : cString; }; + /** @type {Object} */ var specialHTMLTargets = [0, typeof document != 'undefined' ? document : 0, typeof window != 'undefined' ? window : 0]; var findEventTarget = (target) => { target = maybeCStringToJsString(target); @@ -1485,10 +1387,9 @@ function dbg(text) { var _emscripten_is_main_browser_thread = () => !ENVIRONMENT_IS_WORKER; - var _emscripten_memcpy_js = (dest, src, num) => HEAPU8.copyWithin(dest, src, src + num); - var wasmTableMirror = []; + /** @type {WebAssembly.Table} */ var wasmTable; var getWasmTableEntry = (funcPtr) => { var func = wasmTableMirror[funcPtr]; @@ -1496,7 +1397,7 @@ function dbg(text) { if (funcPtr >= wasmTableMirror.length) wasmTableMirror.length = funcPtr + 1; wasmTableMirror[funcPtr] = func = wasmTable.get(funcPtr); } - assert(wasmTable.get(funcPtr) == func, "JavaScript-side Wasm function table mirror is out of date!"); + assert(wasmTable.get(funcPtr) == func, 'JavaScript-side Wasm function table mirror is out of date!'); return func; }; var _emscripten_request_animation_frame_loop = (cb, userData) => { @@ -1515,9 +1416,14 @@ function dbg(text) { // casing all heap size related code to treat 0 specially. 2147483648; + var alignMemory = (size, alignment) => { + assert(alignment, "alignment argument is required"); + return Math.ceil(size / alignment) * alignment; + }; + var growMemory = (size) => { var b = wasmMemory.buffer; - var pages = (size - b.byteLength + 65535) / 65536; + var pages = ((size - b.byteLength + 65535) / 65536) | 0; try { // round size grow request up to wasm page size (fixed 64KB per spec) wasmMemory.grow(pages); // .grow() takes a delta compared to the previous size @@ -1562,8 +1468,6 @@ function dbg(text) { return false; } - 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 // smaller bump instead. (max 3 times, chosen somewhat arbitrarily) @@ -1572,7 +1476,7 @@ function dbg(text) { // but limit overreserving (default to capping at +96MB overgrowth at most) overGrownHeapSize = Math.min(overGrownHeapSize, requestedSize + 100663296 ); - var newSize = Math.min(maxHeapSize, alignUp(Math.max(requestedSize, overGrownHeapSize), 65536)); + var newSize = Math.min(maxHeapSize, alignMemory(Math.max(requestedSize, overGrownHeapSize), 65536)); var replacement = growMemory(newSize); if (replacement) { @@ -1585,7 +1489,7 @@ function dbg(text) { }; - var findCanvasEventTarget = (target) => findEventTarget(target); + var findCanvasEventTarget = findEventTarget; var _emscripten_set_canvas_element_size = (target, width, height) => { var canvas = findCanvasEventTarget(target); if (!canvas) return -4; @@ -1600,34 +1504,35 @@ function dbg(text) { var fillMouseEventData = (eventStruct, e, target) => { assert(eventStruct % 4 == 0); HEAPF64[((eventStruct)>>3)] = e.timeStamp; - var idx = eventStruct >> 2; + var idx = ((eventStruct)>>2); HEAP32[idx + 2] = e.screenX; HEAP32[idx + 3] = e.screenY; HEAP32[idx + 4] = e.clientX; HEAP32[idx + 5] = e.clientY; - HEAP32[idx + 6] = e.ctrlKey; - HEAP32[idx + 7] = e.shiftKey; - HEAP32[idx + 8] = e.altKey; - HEAP32[idx + 9] = e.metaKey; - HEAP16[idx*2 + 20] = e.button; - HEAP16[idx*2 + 21] = e.buttons; - - HEAP32[idx + 11] = e["movementX"] + HEAP8[eventStruct + 24] = e.ctrlKey; + HEAP8[eventStruct + 25] = e.shiftKey; + HEAP8[eventStruct + 26] = e.altKey; + HEAP8[eventStruct + 27] = e.metaKey; + HEAP16[idx*2 + 14] = e.button; + HEAP16[idx*2 + 15] = e.buttons; + + HEAP32[idx + 8] = e["movementX"] ; - HEAP32[idx + 12] = e["movementY"] + HEAP32[idx + 9] = e["movementY"] ; + // Note: rect contains doubles (truncated to placate SAFE_HEAP, which is the same behaviour when writing to HEAP32 anyway) var rect = getBoundingClientRect(target); - HEAP32[idx + 13] = e.clientX - rect.left; - HEAP32[idx + 14] = e.clientY - rect.top; + HEAP32[idx + 10] = e.clientX - (rect.left | 0); + HEAP32[idx + 11] = e.clientY - (rect.top | 0); }; var registerMouseEventCallback = (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) => { - if (!JSEvents.mouseEvent) JSEvents.mouseEvent = _malloc(72); + JSEvents.mouseEvent ||= _malloc(64); target = findEventTarget(target); var mouseEventHandlerFunc = (e = event) => { @@ -1654,7 +1559,7 @@ function dbg(text) { var registerUiEventCallback = (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) => { - if (!JSEvents.uiEvent) JSEvents.uiEvent = _malloc(36); + JSEvents.uiEvent ||= _malloc(36); target = findEventTarget(target); @@ -1672,15 +1577,15 @@ function dbg(text) { return; } var uiEvent = JSEvents.uiEvent; - HEAP32[((uiEvent)>>2)] = e.detail; + HEAP32[((uiEvent)>>2)] = 0; // always zero for resize and scroll HEAP32[(((uiEvent)+(4))>>2)] = b.clientWidth; HEAP32[(((uiEvent)+(8))>>2)] = b.clientHeight; HEAP32[(((uiEvent)+(12))>>2)] = innerWidth; HEAP32[(((uiEvent)+(16))>>2)] = innerHeight; HEAP32[(((uiEvent)+(20))>>2)] = outerWidth; HEAP32[(((uiEvent)+(24))>>2)] = outerHeight; - HEAP32[(((uiEvent)+(28))>>2)] = pageXOffset; - HEAP32[(((uiEvent)+(32))>>2)] = pageYOffset; + HEAP32[(((uiEvent)+(28))>>2)] = pageXOffset | 0; // scroll offsets are float + HEAP32[(((uiEvent)+(32))>>2)] = pageYOffset | 0; if (getWasmTableEntry(callbackfunc)(eventTypeId, uiEvent, userData)) e.preventDefault(); }; @@ -1698,40 +1603,40 @@ function dbg(text) { - function handleAllocatorInit() { - Object.assign(HandleAllocator.prototype, /** @lends {HandleAllocator.prototype} */ { - get(id) { - assert(this.allocated[id] !== undefined, `invalid handle: ${id}`); - return this.allocated[id]; - }, - has(id) { - return this.allocated[id] !== undefined; - }, - allocate(handle) { - var id = this.freelist.pop() || this.allocated.length; - this.allocated[id] = handle; - return id; - }, - free(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); - } - }); - } - /** @constructor */ - function HandleAllocator() { - // Reserve slot 0 so that 0 is always an invalid handle - this.allocated = [undefined]; - this.freelist = []; + class HandleAllocator { + constructor() { + // TODO(https://github.com/emscripten-core/emscripten/issues/21414): + // Use inline field declarations. + this.allocated = [undefined]; + this.freelist = []; + } + get(id) { + assert(this.allocated[id] !== undefined, `invalid handle: ${id}`); + return this.allocated[id]; + } + has(id) { + return this.allocated[id] !== undefined; + } + allocate(handle) { + var id = this.freelist.pop() || this.allocated.length; + this.allocated[id] = handle; + return id; + } + free(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(dbname, dbversion, onsuccess, onerror) { try { var openRequest = indexedDB.open(dbname, dbversion); - } catch (e) { return onerror(e); } + } catch (e) { + return onerror(e); + } openRequest.onupgradeneeded = (event) => { var db = /** @type {IDBDatabase} */ (event.target.result); @@ -1741,7 +1646,7 @@ function dbg(text) { db.createObjectStore('FILES'); }; openRequest.onsuccess = (event) => onsuccess(event.target.result); - openRequest.onerror = (error) => onerror(error); + openRequest.onerror = onerror; }, init() { Fetch.xhrs = new HandleAllocator(); @@ -1768,9 +1673,9 @@ function dbg(text) { } var url_ = UTF8ToString(url); - var fetch_attr = fetch + 112; + var fetch_attr = fetch + 108; var requestMethod = UTF8ToString(fetch_attr + 0); - if (!requestMethod) requestMethod = 'GET'; + requestMethod ||= 'GET'; var timeoutMsecs = HEAPU32[(((fetch_attr)+(56))>>2)]; var userName = HEAPU32[(((fetch_attr)+(68))>>2)]; var password = HEAPU32[(((fetch_attr)+(72))>>2)]; @@ -1788,7 +1693,7 @@ function dbg(text) { var passwordStr = password ? UTF8ToString(password) : undefined; var xhr = new XMLHttpRequest(); - xhr.withCredentials = !!HEAPU8[(((fetch_attr)+(60))>>0)];; + xhr.withCredentials = !!HEAPU8[(fetch_attr)+(60)];; 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) @@ -1824,7 +1729,7 @@ function dbg(text) { function saveResponseAndStatus() { var ptr = 0; var ptrLen = 0; - if (xhr.response && fetchAttrLoadToMemory && HEAPU32[fetch + 12 >> 2] === 0) { + if (xhr.response && fetchAttrLoadToMemory && HEAPU32[(((fetch)+(12))>>2)] === 0) { ptrLen = xhr.response.byteLength; } if (ptrLen > 0) { @@ -1833,7 +1738,7 @@ function dbg(text) { ptr = _malloc(ptrLen); HEAPU8.set(new Uint8Array(/** @type{Array<number>} */(xhr.response)), ptr); } - HEAPU32[fetch + 12 >> 2] = ptr; + HEAPU32[(((fetch)+(12))>>2)] = ptr writeI53ToI64(fetch + 16, ptrLen); writeI53ToI64(fetch + 24, 0); var len = xhr.response ? xhr.response.byteLength : 0; @@ -1843,8 +1748,8 @@ function dbg(text) { // the most recent XHR.onprogress handler. writeI53ToI64(fetch + 32, len); } - HEAPU16[fetch + 40 >> 1] = xhr.readyState; - HEAPU16[fetch + 42 >> 1] = xhr.status; + HEAP16[(((fetch)+(40))>>1)] = xhr.readyState + HEAP16[(((fetch)+(42))>>1)] = xhr.status if (xhr.statusText) stringToUTF8(xhr.statusText, fetch + 44, 64); } @@ -1855,9 +1760,9 @@ function dbg(text) { } saveResponseAndStatus(); if (xhr.status >= 200 && xhr.status < 300) { - if (onsuccess) onsuccess(fetch, xhr, e); + onsuccess?.(fetch, xhr, e); } else { - if (onerror) onerror(fetch, xhr, e); + onerror?.(fetch, xhr, e); } }; xhr.onerror = (e) => { @@ -1866,14 +1771,14 @@ function dbg(text) { return; } saveResponseAndStatus(); - if (onerror) onerror(fetch, xhr, e); + onerror?.(fetch, xhr, e); }; xhr.ontimeout = (e) => { // check if xhr was aborted by user and don't try to call back if (!Fetch.xhrs.has(id)) { return; } - if (onerror) onerror(fetch, xhr, e); + onerror?.(fetch, xhr, e); }; xhr.onprogress = (e) => { // check if xhr was aborted by user and don't try to call back @@ -1888,16 +1793,16 @@ function dbg(text) { ptr = _malloc(ptrLen); HEAPU8.set(new Uint8Array(/** @type{Array<number>} */(xhr.response)), ptr); } - HEAPU32[fetch + 12 >> 2] = ptr; + HEAPU32[(((fetch)+(12))>>2)] = ptr writeI53ToI64(fetch + 16, ptrLen); writeI53ToI64(fetch + 24, e.loaded - ptrLen); writeI53ToI64(fetch + 32, e.total); - HEAPU16[fetch + 40 >> 1] = xhr.readyState; + HEAP16[(((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; - HEAPU16[fetch + 42 >> 1] = xhr.status; + HEAP16[(((fetch)+(42))>>1)] = xhr.status if (xhr.statusText) stringToUTF8(xhr.statusText, fetch + 44, 64); - if (onprogress) onprogress(fetch, xhr, e); + onprogress?.(fetch, xhr, e); if (ptr) { _free(ptr); } @@ -1908,16 +1813,16 @@ function dbg(text) { return; } - HEAPU16[fetch + 40 >> 1] = xhr.readyState; + HEAP16[(((fetch)+(40))>>1)] = xhr.readyState if (xhr.readyState >= 2) { - HEAPU16[fetch + 42 >> 1] = xhr.status; + HEAP16[(((fetch)+(42))>>1)] = xhr.status } - if (onreadystatechange) onreadystatechange(fetch, xhr, e); + onreadystatechange?.(fetch, xhr, e); }; try { xhr.send(data); } catch(e) { - if (onerror) onerror(fetch, xhr, e); + onerror?.(fetch, xhr, e); } } @@ -1940,29 +1845,17 @@ function dbg(text) { }; - var SYSCALLS = { - varargs:undefined, - get() { - assert(SYSCALLS.varargs != undefined); - // the `+` prepended here is necessary to convince the JSCompiler that varargs is indeed a number. - var ret = HEAP32[((+SYSCALLS.varargs)>>2)]; - SYSCALLS.varargs += 4; - return ret; - }, - getp() { return SYSCALLS.get() }, - getStr(ptr) { - var ret = UTF8ToString(ptr); - return ret; - }, - }; + var runtimeKeepaliveCounter = 0; + var keepRuntimeAlive = () => noExitRuntime || runtimeKeepaliveCounter > 0; var _proc_exit = (code) => { EXITSTATUS = code; if (!keepRuntimeAlive()) { - if (Module['onExit']) Module['onExit'](code); + Module['onExit']?.(code); ABORT = true; } quit_(code, new ExitStatus(code)); }; + /** @suppress {duplicate } */ /** @param {boolean|number=} implicit */ var exitJS = (status, implicit) => { @@ -1980,6 +1873,7 @@ function dbg(text) { }; var _exit = exitJS; + var maybeExit = () => { if (!keepRuntimeAlive()) { try { @@ -2025,9 +1919,9 @@ function dbg(text) { return; } - var fetch_attr = fetch + 112; - var destinationPath = HEAPU32[fetch_attr + 64 >> 2]; - if (!destinationPath) destinationPath = HEAPU32[fetch + 8 >> 2]; + var fetch_attr = fetch + 108; + var destinationPath = HEAPU32[(((fetch_attr)+(64))>>2)]; + destinationPath ||= HEAPU32[(((fetch)+(8))>>2)]; var destinationPathStr = UTF8ToString(destinationPath); try { @@ -2035,8 +1929,8 @@ function dbg(text) { var packages = transaction.objectStore('FILES'); var putRequest = packages.put(data, destinationPathStr); putRequest.onsuccess = (event) => { - 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" + HEAP16[(((fetch)+(40))>>1)] = 4 // Mimic XHR readyState 4 === 'DONE: The operation is complete' + HEAP16[(((fetch)+(42))>>1)] = 200 // Mimic XHR HTTP status code 200 "OK" stringToUTF8("OK", fetch + 44, 64); onsuccess(fetch, 0, destinationPathStr); }; @@ -2044,8 +1938,8 @@ function dbg(text) { // Most likely we got an error if IndexedDB is unwilling to store any more data for this page. // TODO: Can we identify and break down different IndexedDB-provided errors and convert those // to more HTTP status codes for more information? - HEAPU16[fetch + 40 >> 1] = 4; // Mimic XHR readyState 4 === 'DONE: The operation is complete' - HEAPU16[fetch + 42 >> 1] = 413; // Mimic XHR HTTP status code 413 "Payload Too Large" + HEAP16[(((fetch)+(40))>>1)] = 4 // Mimic XHR readyState 4 === 'DONE: The operation is complete' + HEAP16[(((fetch)+(42))>>1)] = 413 // Mimic XHR HTTP status code 413 "Payload Too Large" stringToUTF8("Payload Too Large", fetch + 44, 64); onerror(fetch, 0, error); }; @@ -2060,9 +1954,9 @@ function dbg(text) { return; } - var fetch_attr = fetch + 112; - var path = HEAPU32[fetch_attr + 64 >> 2]; - if (!path) path = HEAPU32[fetch + 8 >> 2]; + var fetch_attr = fetch + 108; + var path = HEAPU32[(((fetch_attr)+(64))>>2)]; + path ||= HEAPU32[(((fetch)+(8))>>2)]; var pathStr = UTF8ToString(path); try { @@ -2077,25 +1971,25 @@ function dbg(text) { // freed when emscripten_fetch_close() is called. var ptr = _malloc(len); HEAPU8.set(new Uint8Array(value), ptr); - HEAPU32[fetch + 12 >> 2] = ptr; + HEAPU32[(((fetch)+(12))>>2)] = ptr; 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" + HEAP16[(((fetch)+(40))>>1)] = 4 // Mimic XHR readyState 4 === 'DONE: The operation is complete' + HEAP16[(((fetch)+(42))>>1)] = 200 // Mimic XHR HTTP status code 200 "OK" stringToUTF8("OK", fetch + 44, 64); onsuccess(fetch, 0, value); } else { // Succeeded to load, but the load came back with the value of undefined, treat that as an error since we never store undefined in db. - HEAPU16[fetch + 40 >> 1] = 4; // Mimic XHR readyState 4 === 'DONE: The operation is complete' - HEAPU16[fetch + 42 >> 1] = 404; // Mimic XHR HTTP status code 404 "Not Found" + HEAP16[(((fetch)+(40))>>1)] = 4 // Mimic XHR readyState 4 === 'DONE: The operation is complete' + HEAP16[(((fetch)+(42))>>1)] = 404 // Mimic XHR HTTP status code 404 "Not Found" stringToUTF8("Not Found", fetch + 44, 64); onerror(fetch, 0, 'no data'); } }; getRequest.onerror = (error) => { - HEAPU16[fetch + 40 >> 1] = 4; // Mimic XHR readyState 4 === 'DONE: The operation is complete' - HEAPU16[fetch + 42 >> 1] = 404; // Mimic XHR HTTP status code 404 "Not Found" + HEAP16[(((fetch)+(40))>>1)] = 4 // Mimic XHR readyState 4 === 'DONE: The operation is complete' + HEAP16[(((fetch)+(42))>>1)] = 404 // Mimic XHR HTTP status code 404 "Not Found" stringToUTF8("Not Found", fetch + 44, 64); onerror(fetch, 0, error); }; @@ -2110,9 +2004,10 @@ function dbg(text) { return; } - var fetch_attr = fetch + 112; - var path = HEAPU32[fetch_attr + 64 >> 2]; - if (!path) path = HEAPU32[fetch + 8 >> 2]; + var fetch_attr = fetch + 108; + var path = HEAPU32[(((fetch_attr)+(64))>>2)]; + path ||= HEAPU32[(((fetch)+(8))>>2)]; + var pathStr = UTF8ToString(path); try { @@ -2121,18 +2016,20 @@ function dbg(text) { var request = packages.delete(pathStr); request.onsuccess = (event) => { var value = event.target.result; - HEAPU32[fetch + 12 >> 2] = 0; + HEAPU32[(((fetch)+(12))>>2)] = 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" + // Mimic XHR readyState 4 === 'DONE: The operation is complete' + HEAP16[(((fetch)+(40))>>1)] = 4; + // Mimic XHR HTTP status code 200 "OK" + HEAP16[(((fetch)+(42))>>1)] = 200; stringToUTF8("OK", fetch + 44, 64); onsuccess(fetch, 0, value); }; request.onerror = (error) => { - HEAPU16[fetch + 40 >> 1] = 4; // Mimic XHR readyState 4 === 'DONE: The operation is complete' - HEAPU16[fetch + 42 >> 1] = 404; // Mimic XHR HTTP status code 404 "Not Found" + HEAP16[(((fetch)+(40))>>1)] = 4 // Mimic XHR readyState 4 === 'DONE: The operation is complete' + HEAP16[(((fetch)+(42))>>1)] = 404 // Mimic XHR HTTP status code 404 "Not Found" stringToUTF8("Not Found", fetch + 44, 64); onerror(fetch, 0, error); }; @@ -2147,12 +2044,12 @@ function dbg(text) { // response. - var fetch_attr = fetch + 112; - 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 fetch_attr = fetch + 108; + 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 fetchAttrSynchronous = !!(fetchAttributes & 64); function doCallback(f) { @@ -2167,14 +2064,14 @@ function dbg(text) { doCallback(() => { if (onsuccess) getWasmTableEntry(onsuccess)(fetch); - else if (successcb) successcb(fetch); + else successcb?.(fetch); }); }; var reportProgress = (fetch, xhr, e) => { doCallback(() => { if (onprogress) getWasmTableEntry(onprogress)(fetch); - else if (progresscb) progresscb(fetch); + else progresscb?.(fetch); }); }; @@ -2182,14 +2079,14 @@ function dbg(text) { doCallback(() => { if (onerror) getWasmTableEntry(onerror)(fetch); - else if (errorcb) errorcb(fetch); + else errorcb?.(fetch); }); }; var reportReadyStateChange = (fetch, xhr, e) => { doCallback(() => { if (onreadystatechange) getWasmTableEntry(onreadystatechange)(fetch); - else if (readystatechangecb) readystatechangecb(fetch); + else readystatechangecb?.(fetch); }); }; @@ -2202,14 +2099,14 @@ function dbg(text) { doCallback(() => { if (onsuccess) getWasmTableEntry(onsuccess)(fetch); - else if (successcb) successcb(fetch); + else successcb?.(fetch); }); }; var storeError = (fetch, xhr, e) => { doCallback(() => { if (onsuccess) getWasmTableEntry(onsuccess)(fetch); - else if (successcb) successcb(fetch); + else successcb?.(fetch); }); }; fetchCacheData(Fetch.dbInstance, fetch, xhr.response, storeSuccess, storeError); @@ -2240,9 +2137,14 @@ function dbg(text) { return fetch; } + var GLctx; + var 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'); + // Because this extension is a core function in WebGL 2, assign the extension entry points in place of + // where the core functions will reside in WebGL 2. This way the calling code can call these without + // having to dynamically branch depending if running against WebGL 1 or WebGL 2. if (ext) { ctx['vertexAttribDivisor'] = (index, divisor) => ext['vertexAttribDivisorANGLE'](index, divisor); ctx['drawArraysInstanced'] = (mode, first, count, primcount) => ext['drawArraysInstancedANGLE'](mode, first, count, primcount); @@ -2281,11 +2183,83 @@ function dbg(text) { return !!(ctx.mdibvbi = ctx.getExtension('WEBGL_multi_draw_instanced_base_vertex_base_instance')); }; + var webgl_enable_EXT_polygon_offset_clamp = (ctx) => { + return !!(ctx.extPolygonOffsetClamp = ctx.getExtension('EXT_polygon_offset_clamp')); + }; + + var webgl_enable_EXT_clip_control = (ctx) => { + return !!(ctx.extClipControl = ctx.getExtension('EXT_clip_control')); + }; + + var webgl_enable_WEBGL_polygon_mode = (ctx) => { + return !!(ctx.webglPolygonMode = ctx.getExtension('WEBGL_polygon_mode')); + }; + var 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 getEmscriptenSupportedExtensions = (ctx) => { + // Restrict the list of advertised extensions to those that we actually + // support. + var supportedExtensions = [ + // WebGL 1 extensions + 'ANGLE_instanced_arrays', + 'EXT_blend_minmax', + 'EXT_disjoint_timer_query', + 'EXT_frag_depth', + 'EXT_shader_texture_lod', + 'EXT_sRGB', + 'OES_element_index_uint', + 'OES_fbo_render_mipmap', + 'OES_standard_derivatives', + 'OES_texture_float', + 'OES_texture_half_float', + 'OES_texture_half_float_linear', + 'OES_vertex_array_object', + 'WEBGL_color_buffer_float', + 'WEBGL_depth_texture', + 'WEBGL_draw_buffers', + // WebGL 2 extensions + 'EXT_color_buffer_float', + 'EXT_conservative_depth', + 'EXT_disjoint_timer_query_webgl2', + 'EXT_texture_norm16', + 'NV_shader_noperspective_interpolation', + 'WEBGL_clip_cull_distance', + // WebGL 1 and WebGL 2 extensions + 'EXT_clip_control', + 'EXT_color_buffer_half_float', + 'EXT_depth_clamp', + 'EXT_float_blend', + 'EXT_polygon_offset_clamp', + 'EXT_texture_compression_bptc', + 'EXT_texture_compression_rgtc', + 'EXT_texture_filter_anisotropic', + 'KHR_parallel_shader_compile', + 'OES_texture_float_linear', + 'WEBGL_blend_func_extended', + 'WEBGL_compressed_texture_astc', + 'WEBGL_compressed_texture_etc', + 'WEBGL_compressed_texture_etc1', + 'WEBGL_compressed_texture_s3tc', + 'WEBGL_compressed_texture_s3tc_srgb', + 'WEBGL_debug_renderer_info', + 'WEBGL_debug_shaders', + 'WEBGL_lose_context', + 'WEBGL_multi_draw', + 'WEBGL_polygon_mode' + ]; + // .getSupportedExtensions() can return null if context is lost, so coerce to empty array. + return (ctx.getSupportedExtensions() || []).filter(ext => supportedExtensions.includes(ext)); + }; + + var registerPreMainLoop = (f) => { + // Does nothing unless $MainLoop is included/used. + typeof MainLoop != 'undefined' && MainLoop.preMainLoop.push(f); + }; + var GL = { counter:1, @@ -2312,7 +2286,8 @@ function dbg(text) { stringiCache:{ }, unpackAlignment:4, - recordError:function recordError(errorCode) { + unpackRowLength:0, + recordError:(errorCode) => { if (!GL.lastError) { GL.lastError = errorCode; } @@ -2324,11 +2299,23 @@ function dbg(text) { } return ret; }, + genObject:(n, buffers, createFunction, objectTable + ) => { + for (var i = 0; i < n; i++) { + var buffer = GLctx[createFunction](); + var id = buffer && GL.getNewId(objectTable); + if (buffer) { + buffer.name = id; + objectTable[id] = buffer; + } else { + GL.recordError(0x502 /* GL_INVALID_OPERATION */); + } + HEAP32[(((buffers)+(i*4))>>2)] = id; + } + }, MAX_TEMP_BUFFER_SIZE:2097152, numTempVertexBuffersPerSize:64, - log2ceilLookup:(i) => { - return 32 - Math.clz32(i === 0 ? 0 : i - 1); - }, + log2ceilLookup:(i) => 32 - Math.clz32(i === 0 ? 0 : i - 1), generateTempBuffers:(quads, context) => { var largestIndex = GL.log2ceilLookup(GL.MAX_TEMP_BUFFER_SIZE); context.tempVertexBufferCounters1 = []; @@ -2379,7 +2366,7 @@ function dbg(text) { context.GLctx.bindBuffer(0x8893 /*GL_ELEMENT_ARRAY_BUFFER*/, null); } }, - getTempVertexBuffer:function getTempVertexBuffer(sizeBytes) { + getTempVertexBuffer:(sizeBytes) => { var idx = GL.log2ceilLookup(sizeBytes); var ringbuffer = GL.currentContext.tempVertexBuffers1[idx]; var nextFreeBufferIndex = GL.currentContext.tempVertexBufferCounters1[idx]; @@ -2395,7 +2382,7 @@ function dbg(text) { GLctx.bindBuffer(0x8892 /*GL_ARRAY_BUFFER*/, prevVBO); return ringbuffer[nextFreeBufferIndex]; }, - getTempIndexBuffer:function getTempIndexBuffer(sizeBytes) { + getTempIndexBuffer:(sizeBytes) => { var idx = GL.log2ceilLookup(sizeBytes); var ibo = GL.currentContext.tempIndexBuffers[idx]; if (ibo) { @@ -2408,7 +2395,7 @@ function dbg(text) { GLctx.bindBuffer(0x8893 /*GL_ELEMENT_ARRAY_BUFFER*/, prevIBO); return GL.currentContext.tempIndexBuffers[idx]; }, - newRenderingFrameStarted:function newRenderingFrameStarted() { + newRenderingFrameStarted:() => { if (!GL.currentContext) { return; } @@ -2426,12 +2413,12 @@ function dbg(text) { getSource:(shader, count, string, length) => { var source = ''; for (var i = 0; i < count; ++i) { - var len = length ? HEAP32[(((length)+(i*4))>>2)] : -1; - source += UTF8ToString(HEAP32[(((string)+(i*4))>>2)], len < 0 ? undefined : len); + var len = length ? HEAPU32[(((length)+(i*4))>>2)] : undefined; + source += UTF8ToString(HEAPU32[(((string)+(i*4))>>2)], len); } return source; }, - calcBufLength:function calcBufLength(size, type, stride, count) { + calcBufLength:(size, type, stride, count) => { if (stride > 0) { return count * stride; // XXXvlad this is not exactly correct I don't think } @@ -2439,10 +2426,11 @@ function dbg(text) { return size * typeSize * count; }, usedTempBuffers:[], - preDrawHandleClientVertexAttribBindings:function preDrawHandleClientVertexAttribBindings(count) { + preDrawHandleClientVertexAttribBindings:(count) => { GL.resetBufferBinding = false; - // TODO: initial pass to detect ranges we need to upload, might not need an upload per attrib + // TODO: initial pass to detect ranges we need to upload, might not need + // an upload per attrib for (var i = 0; i < GL.currentContext.maxVertexAttribs; ++i) { var cb = GL.currentContext.clientBuffers[i]; if (!cb.clientside || !cb.enabled) continue; @@ -2458,18 +2446,22 @@ function dbg(text) { cb.vertexAttribPointerAdaptor.call(GLctx, i, cb.size, cb.type, cb.normalized, cb.stride, 0); } }, - postDrawHandleClientVertexAttribBindings:function postDrawHandleClientVertexAttribBindings() { + postDrawHandleClientVertexAttribBindings:() => { if (GL.resetBufferBinding) { GLctx.bindBuffer(0x8892 /*GL_ARRAY_BUFFER*/, GL.buffers[GLctx.currentArrayBufferBinding]); } }, createContext:(/** @type {HTMLCanvasElement} */ canvas, webGLContextAttributes) => { - // BUG: Workaround Safari WebGL issue: After successfully acquiring WebGL context on a canvas, - // calling .getContext() will always return that context independent of which 'webgl' or 'webgl2' - // context version was passed. See https://bugs.webkit.org/show_bug.cgi?id=222758 and - // https://github.com/emscripten-core/emscripten/issues/13295. - // TODO: Once the bug is fixed and shipped in Safari, adjust the Safari version field in above check. + // BUG: Workaround Safari WebGL issue: After successfully acquiring WebGL + // context on a canvas, calling .getContext() will always return that + // context independent of which 'webgl' or 'webgl2' + // context version was passed. See: + // https://bugs.webkit.org/show_bug.cgi?id=222758 + // and: + // https://github.com/emscripten-core/emscripten/issues/13295. + // TODO: Once the bug is fixed and shipped in Safari, adjust the Safari + // version field in above check. if (!canvas.getContextSafariWebGL2Fixed) { canvas.getContextSafariWebGL2Fixed = canvas.getContext; /** @type {function(this:HTMLCanvasElement, string, (Object|null)=): (Object|null)} */ @@ -2506,7 +2498,8 @@ function dbg(text) { GLctx: ctx }; - // Store the created context object so that we can access the context given a canvas without having to pass the parameters again. + // Store the created context object so that we can access the context + // given a canvas without having to pass the parameters again. if (ctx.canvas) ctx.canvas.GLctxObject = context; GL.contexts[handle] = context; if (typeof webGLContextAttributes.enableExtensionsByDefault == 'undefined' || webGLContextAttributes.enableExtensionsByDefault) { @@ -2516,7 +2509,16 @@ function dbg(text) { context.maxVertexAttribs = context.GLctx.getParameter(0x8869 /*GL_MAX_VERTEX_ATTRIBS*/); context.clientBuffers = []; for (var i = 0; i < context.maxVertexAttribs; i++) { - context.clientBuffers[i] = { enabled: false, clientside: false, size: 0, type: 0, normalized: 0, stride: 0, ptr: 0, vertexAttribPointerAdaptor: null }; + context.clientBuffers[i] = { + enabled: false, + clientside: false, + size: 0, + type: 0, + normalized: 0, + stride: 0, + ptr: 0, + vertexAttribPointerAdaptor: null, + }; } GL.generateTempBuffers(false, context); @@ -2525,31 +2527,51 @@ function dbg(text) { }, makeContextCurrent:(contextHandle) => { - GL.currentContext = GL.contexts[contextHandle]; // Active Emscripten GL layer context object. - Module.ctx = GLctx = GL.currentContext && GL.currentContext.GLctx; // Active WebGL context object. + // Active Emscripten GL layer context object. + GL.currentContext = GL.contexts[contextHandle]; + // Active WebGL context object. + Module.ctx = GLctx = GL.currentContext?.GLctx; return !(contextHandle && !GLctx); }, getContext:(contextHandle) => { return GL.contexts[contextHandle]; }, deleteContext:(contextHandle) => { - if (GL.currentContext === GL.contexts[contextHandle]) GL.currentContext = null; - if (typeof JSEvents == 'object') JSEvents.removeAllHandlersOnTarget(GL.contexts[contextHandle].GLctx.canvas); // Release all JS event handlers on the DOM element that the GL context is associated with since the context is now deleted. - if (GL.contexts[contextHandle] && GL.contexts[contextHandle].GLctx.canvas) GL.contexts[contextHandle].GLctx.canvas.GLctxObject = undefined; // Make sure the canvas object no longer refers to the context object so there are no GC surprises. + if (GL.currentContext === GL.contexts[contextHandle]) { + GL.currentContext = null; + } + if (typeof JSEvents == 'object') { + // Release all JS event handlers on the DOM element that the GL context is + // associated with since the context is now deleted. + JSEvents.removeAllHandlersOnTarget(GL.contexts[contextHandle].GLctx.canvas); + } + // Make sure the canvas object no longer refers to the context object so + // there are no GC surprises. + if (GL.contexts[contextHandle] && GL.contexts[contextHandle].GLctx.canvas) { + GL.contexts[contextHandle].GLctx.canvas.GLctxObject = undefined; + } GL.contexts[contextHandle] = null; }, initExtensions:(context) => { - // If this function is called without a specific context object, init the extensions of the currently active context. - if (!context) context = GL.currentContext; + // If this function is called without a specific context object, init the + // extensions of the currently active context. + context ||= GL.currentContext; if (context.initExtensionsDone) return; context.initExtensionsDone = true; var GLctx = context.GLctx; - // Detect the presence of a few extensions manually, ction GL interop layer itself will need to know if they exist. + // Detect the presence of a few extensions manually, ction 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) + // Extensions that are available in both WebGL 1 and WebGL 2 + webgl_enable_WEBGL_multi_draw(GLctx); + webgl_enable_EXT_polygon_offset_clamp(GLctx); + webgl_enable_EXT_clip_control(GLctx); + webgl_enable_WEBGL_polygon_mode(GLctx); + // 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); @@ -2572,12 +2594,9 @@ function dbg(text) { GLctx.disjointTimerQueryExt = GLctx.getExtension("EXT_disjoint_timer_query"); } - webgl_enable_WEBGL_multi_draw(GLctx); - - // .getSupportedExtensions() can return null if context is lost, so coerce to empty array. - var exts = GLctx.getSupportedExtensions() || []; - exts.forEach((ext) => { - // WEBGL_lose_context, WEBGL_debug_renderer_info and WEBGL_debug_shaders are not enabled by default. + getEmscriptenSupportedExtensions(GLctx).forEach((ext) => { + // WEBGL_lose_context, WEBGL_debug_renderer_info and WEBGL_debug_shaders + // are not enabled by default. if (!ext.includes('lose_context') && !ext.includes('debug')) { // Call .getExtension() to enable that extension permanently. GLctx.getExtension(ext); @@ -2587,31 +2606,31 @@ function dbg(text) { }; - var emscripten_webgl_power_preferences = ['default', 'low-power', 'high-performance']; + var webglPowerPreferences = ["default","low-power","high-performance"]; /** @suppress {duplicate } */ var _emscripten_webgl_do_create_context = (target, attributes) => { assert(attributes); - var a = attributes >> 2; - var powerPreference = HEAP32[a + (24>>2)]; + var attr32 = ((attributes)>>2); + var powerPreference = HEAP32[attr32 + (8>>2)]; var contextAttributes = { - 'alpha': !!HEAP32[a + (0>>2)], - 'depth': !!HEAP32[a + (4>>2)], - 'stencil': !!HEAP32[a + (8>>2)], - 'antialias': !!HEAP32[a + (12>>2)], - 'premultipliedAlpha': !!HEAP32[a + (16>>2)], - 'preserveDrawingBuffer': !!HEAP32[a + (20>>2)], - 'powerPreference': emscripten_webgl_power_preferences[powerPreference], - 'failIfMajorPerformanceCaveat': !!HEAP32[a + (28>>2)], + 'alpha': !!HEAP8[attributes + 0], + 'depth': !!HEAP8[attributes + 1], + 'stencil': !!HEAP8[attributes + 2], + 'antialias': !!HEAP8[attributes + 3], + 'premultipliedAlpha': !!HEAP8[attributes + 4], + 'preserveDrawingBuffer': !!HEAP8[attributes + 5], + 'powerPreference': webglPowerPreferences[powerPreference], + 'failIfMajorPerformanceCaveat': !!HEAP8[attributes + 12], // 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)], - minorVersion: HEAP32[a + (36>>2)], - enableExtensionsByDefault: HEAP32[a + (40>>2)], - explicitSwapControl: HEAP32[a + (44>>2)], - proxyContextToMainThread: HEAP32[a + (48>>2)], - renderViaOffscreenBackBuffer: HEAP32[a + (52>>2)] + majorVersion: HEAP32[attr32 + (16>>2)], + minorVersion: HEAP32[attr32 + (20>>2)], + enableExtensionsByDefault: HEAP8[attributes + 24], + explicitSwapControl: HEAP8[attributes + 25], + proxyContextToMainThread: HEAP32[attr32 + (28>>2)], + renderViaOffscreenBackBuffer: HEAP8[attributes + 32] }; var canvas = findCanvasEventTarget(target); @@ -2629,34 +2648,24 @@ function dbg(text) { }; var _emscripten_webgl_create_context = _emscripten_webgl_do_create_context; - var _emscripten_webgl_init_context_attributes = (attributes) => { - assert(attributes); - var a = attributes >> 2; - for (var i = 0; i < (56>>2); ++i) { - HEAP32[a+i] = 0; - } - - HEAP32[a + (0>>2)] = - HEAP32[a + (4>>2)] = - HEAP32[a + (12>>2)] = - HEAP32[a + (16>>2)] = - HEAP32[a + (32>>2)] = - HEAP32[a + (40>>2)] = 1; - - }; - var _emscripten_webgl_make_context_current = (contextHandle) => { var success = GL.makeContextCurrent(contextHandle); return success ? 0 : -5; }; + var SYSCALLS = { + varargs:undefined, + getStr(ptr) { + var ret = UTF8ToString(ptr); + return ret; + }, + }; var _fd_close = (fd) => { abort('fd_close called without SYSCALLS_REQUIRE_FILESYSTEM'); }; - function _fd_seek(fd,offset_low, offset_high,whence,newOffset) { - var offset = convertI32PairToI53Checked(offset_low, offset_high);; + var offset = convertI32PairToI53Checked(offset_low, offset_high); return 70; @@ -2669,7 +2678,7 @@ function dbg(text) { var buffer = printCharBuffers[stream]; assert(buffer); if (curr === 0 || curr === 10) { - (stream === 1 ? out : err)(UTF8ArrayToString(buffer, 0)); + (stream === 1 ? out : err)(UTF8ArrayToString(buffer)); buffer.length = 0; } else { buffer.push(curr); @@ -2712,9 +2721,11 @@ function dbg(text) { } if (target == 0x88EB /*GL_PIXEL_PACK_BUFFER*/) { - // In WebGL 2 glReadPixels entry point, we need to use a different WebGL 2 API function call when a buffer is bound to - // GL_PIXEL_PACK_BUFFER_BINDING point, so must keep track whether that binding point is non-null to know what is - // the proper API function to call. + // In WebGL 2 glReadPixels entry point, we need to use a different WebGL 2 + // API function call when a buffer is bound to + // GL_PIXEL_PACK_BUFFER_BINDING point, so must keep track whether that + // binding point is non-null to know what is the proper API function to + // call. GLctx.currentPixelPackBufferBinding = buffer; } else if (target == 0x88EC /*GL_PIXEL_UNPACK_BUFFER*/) { // In WebGL 2 gl(Compressed)Tex(Sub)Image[23]D entry points, we need to @@ -2733,39 +2744,42 @@ function dbg(text) { GLctx.currentElementArrayBufferBinding = ibo ? (ibo.name | 0) : 0; }; - function _glBlendFunc(x0, x1) { GLctx.blendFunc(x0, x1) } + var _glBlendFunc = (x0, x1) => GLctx.blendFunc(x0, x1); var _glBufferData = (target, size, data, usage) => { - if (GL.currentContext.version >= 2) { // WebGL 2 provides new garbage-free entry points to call to WebGL. Use those always when possible. - // If size is zero, WebGL would interpret uploading the whole input arraybuffer (starting from given offset), which would - // not make sense in WebAssembly, so avoid uploading if size is zero. However we must still call bufferData to establish a - // backing storage of zero bytes. + if (GL.currentContext.version >= 2) { + // If size is zero, WebGL would interpret uploading the whole input + // arraybuffer (starting from given offset), which would not make sense in + // WebAssembly, so avoid uploading if size is zero. However we must still + // call bufferData to establish a backing storage of zero bytes. if (data && size) { GLctx.bufferData(target, HEAPU8, usage, data, size); } else { GLctx.bufferData(target, size, usage); } - } else { - // N.b. here first form specifies a heap subarray, second form an integer size, so the ?: code here is polymorphic. It is advised to avoid - // randomly mixing both uses in calling code, to avoid any potential JS engine JIT issues. - GLctx.bufferData(target, data ? HEAPU8.subarray(data, data+size) : size, usage); + return; } + // N.b. here first form specifies a heap subarray, second form an integer + // size, so the ?: code here is polymorphic. It is advised to avoid + // randomly mixing both uses in calling code, to avoid any potential JS + // engine JIT issues. + GLctx.bufferData(target, data ? HEAPU8.subarray(data, data+size) : size, usage); }; var _glBufferSubData = (target, offset, size, data) => { - if (GL.currentContext.version >= 2) { // WebGL 2 provides new garbage-free entry points to call to WebGL. Use those always when possible. + if (GL.currentContext.version >= 2) { size && GLctx.bufferSubData(target, offset, HEAPU8, data, size); return; } GLctx.bufferSubData(target, offset, HEAPU8.subarray(data, data+size)); }; - function _glClear(x0) { GLctx.clear(x0) } + var _glClear = (x0) => GLctx.clear(x0); - function _glClearColor(x0, x1, x2, x3) { GLctx.clearColor(x0, x1, x2, x3) } + var _glClearColor = (x0, x1, x2, x3) => GLctx.clearColor(x0, x1, x2, x3); - function _glClearDepth(x0) { GLctx.clearDepth(x0) } + var _glClearDepth = (x0) => GLctx.clearDepth(x0); var _glCompileShader = (shader) => { GLctx.compileShader(GL.shaders[shader]); @@ -2776,7 +2790,8 @@ function dbg(text) { var program = GLctx.createProgram(); // Store additional information needed for each shader program: program.name = id; - // Lazy cache results of glGetProgramiv(GL_ACTIVE_UNIFORM_MAX_LENGTH/GL_ACTIVE_ATTRIBUTE_MAX_LENGTH/GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH) + // Lazy cache results of + // glGetProgramiv(GL_ACTIVE_UNIFORM_MAX_LENGTH/GL_ACTIVE_ATTRIBUTE_MAX_LENGTH/GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH) program.maxUniformLength = program.maxAttributeLength = program.maxUniformBlockNameLength = 0; program.uniformIdCounter = 1; GL.programs[id] = program; @@ -2813,7 +2828,9 @@ function dbg(text) { var _glDeleteProgram = (id) => { if (!id) return; var program = GL.programs[id]; - if (!program) { // glDeleteProgram actually signals an error when deleting a nonexisting object, unlike some other GL delete functions. + if (!program) { + // glDeleteProgram actually signals an error when deleting a nonexisting + // object, unlike some other GL delete functions. GL.recordError(0x501 /* GL_INVALID_VALUE */); return; } @@ -2825,7 +2842,9 @@ function dbg(text) { var _glDeleteShader = (id) => { if (!id) return; var shader = GL.shaders[id]; - if (!shader) { // glDeleteShader actually signals an error when deleting a nonexisting object, unlike some other GL delete functions. + if (!shader) { + // glDeleteShader actually signals an error when deleting a nonexisting + // object, unlike some other GL delete functions. GL.recordError(0x501 /* GL_INVALID_VALUE */); return; } @@ -2841,7 +2860,7 @@ function dbg(text) { } }; - function _glDepthFunc(x0) { GLctx.depthFunc(x0) } + var _glDepthFunc = (x0) => GLctx.depthFunc(x0); var _glDepthMask = (flag) => { GLctx.depthMask(!!flag); @@ -2858,19 +2877,42 @@ function dbg(text) { var _glDrawElements = (mode, count, type, indices) => { var buf; + var vertexes = 0; if (!GLctx.currentElementArrayBufferBinding) { var size = GL.calcBufLength(1, type, 0, count); buf = GL.getTempIndexBuffer(size); GLctx.bindBuffer(0x8893 /*GL_ELEMENT_ARRAY_BUFFER*/, buf); GLctx.bufferSubData(0x8893 /*GL_ELEMENT_ARRAY_BUFFER*/, - 0, - HEAPU8.subarray(indices, indices + size)); + 0, + HEAPU8.subarray(indices, indices + size)); + + // Calculating vertex count if shader's attribute data is on client side + if (count > 0) { + for (var i = 0; i < GL.currentContext.maxVertexAttribs; ++i) { + var cb = GL.currentContext.clientBuffers[i]; + if (cb.clientside && cb.enabled) { + let arrayClass; + switch(type) { + case 0x1401 /* GL_UNSIGNED_BYTE */: arrayClass = Uint8Array; break; + case 0x1403 /* GL_UNSIGNED_SHORT */: arrayClass = Uint16Array; break; + case 0x1405 /* GL_UNSIGNED_INT */: arrayClass = Uint32Array; break; + default: + GL.recordError(0x502 /* GL_INVALID_OPERATION */); + return; + } + + vertexes = new arrayClass(HEAPU8.buffer, indices, count).reduce((max, current) => Math.max(max, current)) + 1; + break; + } + } + } + // the index is now 0 indices = 0; } // bind any client-side buffers - GL.preDrawHandleClientVertexAttribBindings(count); + GL.preDrawHandleClientVertexAttribBindings(vertexes); GLctx.drawElements(mode, count, type, indices); @@ -2881,7 +2923,7 @@ function dbg(text) { } }; - function _glEnable(x0) { GLctx.enable(x0) } + var _glEnable = (x0) => GLctx.enable(x0); var _glEnableVertexAttribArray = (index) => { var cb = GL.currentContext.clientBuffers[index]; @@ -2889,31 +2931,15 @@ function dbg(text) { GLctx.enableVertexAttribArray(index); }; - var __glGenObject = (n, buffers, createFunction, objectTable - ) => { - for (var i = 0; i < n; i++) { - var buffer = GLctx[createFunction](); - var id = buffer && GL.getNewId(objectTable); - if (buffer) { - buffer.name = id; - objectTable[id] = buffer; - } else { - GL.recordError(0x502 /* GL_INVALID_OPERATION */); - } - HEAP32[(((buffers)+(i*4))>>2)] = id; - } - }; - var _glGenBuffers = (n, buffers) => { - __glGenObject(n, buffers, 'createBuffer', GL.buffers + GL.genObject(n, buffers, 'createBuffer', GL.buffers ); }; - - function _glGenVertexArrays(n, arrays) { - __glGenObject(n, arrays, 'createVertexArray', GL.vaos + var _glGenVertexArrays = (n, arrays) => { + GL.genObject(n, arrays, 'createVertexArray', GL.vaos ); - } + }; var _glGetAttribLocation = (program, name) => { @@ -2929,8 +2955,9 @@ function dbg(text) { var _glGetProgramiv = (program, pname, p) => { if (!p) { - // GLES2 specification does not specify how to behave if p is a null pointer. Since calling this function does not make sense - // if p == null, issue a GL error to notify user about it. + // GLES2 specification does not specify how to behave if p is a null + // pointer. Since calling this function does not make sense if p == null, + // issue a GL error to notify user about it. GL.recordError(0x501 /* GL_INVALID_VALUE */); return; } @@ -2948,21 +2975,24 @@ function dbg(text) { HEAP32[((p)>>2)] = log.length + 1; } else if (pname == 0x8B87 /* GL_ACTIVE_UNIFORM_MAX_LENGTH */) { if (!program.maxUniformLength) { - for (var i = 0; i < GLctx.getProgramParameter(program, 0x8B86/*GL_ACTIVE_UNIFORMS*/); ++i) { + var numActiveUniforms = GLctx.getProgramParameter(program, 0x8B86/*GL_ACTIVE_UNIFORMS*/); + for (var i = 0; i < numActiveUniforms; ++i) { program.maxUniformLength = Math.max(program.maxUniformLength, GLctx.getActiveUniform(program, i).name.length+1); } } HEAP32[((p)>>2)] = program.maxUniformLength; } else if (pname == 0x8B8A /* GL_ACTIVE_ATTRIBUTE_MAX_LENGTH */) { if (!program.maxAttributeLength) { - for (var i = 0; i < GLctx.getProgramParameter(program, 0x8B89/*GL_ACTIVE_ATTRIBUTES*/); ++i) { + var numActiveAttributes = GLctx.getProgramParameter(program, 0x8B89/*GL_ACTIVE_ATTRIBUTES*/); + for (var i = 0; i < numActiveAttributes; ++i) { program.maxAttributeLength = Math.max(program.maxAttributeLength, GLctx.getActiveAttrib(program, i).name.length+1); } } HEAP32[((p)>>2)] = program.maxAttributeLength; } else if (pname == 0x8A35 /* GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH */) { if (!program.maxUniformBlockNameLength) { - for (var i = 0; i < GLctx.getProgramParameter(program, 0x8A36/*GL_ACTIVE_UNIFORM_BLOCKS*/); ++i) { + var numActiveUniformBlocks = GLctx.getProgramParameter(program, 0x8A36/*GL_ACTIVE_UNIFORM_BLOCKS*/); + for (var i = 0; i < numActiveUniformBlocks; ++i) { program.maxUniformBlockNameLength = Math.max(program.maxUniformBlockNameLength, GLctx.getActiveUniformBlockName(program, i).length+1); } } @@ -2982,8 +3012,9 @@ function dbg(text) { var _glGetShaderiv = (shader, pname, p) => { if (!p) { - // GLES2 specification does not specify how to behave if p is a null pointer. Since calling this function does not make sense - // if p == null, issue a GL error to notify user about it. + // GLES2 specification does not specify how to behave if p is a null + // pointer. Since calling this function does not make sense if p == null, + // issue a GL error to notify user about it. GL.recordError(0x501 /* GL_INVALID_VALUE */); return; } @@ -3011,9 +3042,7 @@ function dbg(text) { var jstoi_q = (str) => parseInt(str); /** @noinline */ - var webglGetLeftBracePos = (name) => { - return name.slice(-1) == ']' && name.lastIndexOf('['); - }; + var webglGetLeftBracePos = (name) => name.slice(-1) == ']' && name.lastIndexOf('['); var webglPrepareUniformLocationsBeforeFirstUse = (program) => { var uniformLocsById = program.uniformLocsById, // Maps GLuint -> WebGLUniformLocation @@ -3028,7 +3057,8 @@ function dbg(text) { // maps integer locations back to uniform name strings, so that we can lazily fetch uniform array locations program.uniformArrayNamesById = {}; - for (i = 0; i < GLctx.getProgramParameter(program, 0x8B86/*GL_ACTIVE_UNIFORMS*/); ++i) { + var numActiveUniforms = GLctx.getProgramParameter(program, 0x8B86/*GL_ACTIVE_UNIFORMS*/); + for (i = 0; i < numActiveUniforms; ++i) { var u = GLctx.getActiveUniform(program, i); var nm = u.name; var sz = u.size; @@ -3067,11 +3097,14 @@ function dbg(text) { var arrayIndex = 0; var uniformBaseName = name; - // Invariant: when populating integer IDs for uniform locations, we must maintain the precondition that - // arrays reside in contiguous addresses, i.e. for a 'vec4 colors[10];', colors[4] must be at location colors[0]+4. - // However, user might call glGetUniformLocation(program, "colors") for an array, so we cannot discover based on the user - // input arguments whether the uniform we are dealing with is an array. The only way to discover which uniforms are arrays - // is to enumerate over all the active uniforms in the program. + // Invariant: when populating integer IDs for uniform locations, we must + // maintain the precondition that arrays reside in contiguous addresses, + // i.e. for a 'vec4 colors[10];', colors[4] must be at location + // colors[0]+4. However, user might call glGetUniformLocation(program, + // "colors") for an array, so we cannot discover based on the user input + // arguments whether the uniform we are dealing with is an array. The only + // way to discover which uniforms are arrays is to enumerate over all the + // active uniforms in the program. var leftBrace = webglGetLeftBracePos(name); // If user passed an array accessor "[index]", parse the array index off the accessor. @@ -3081,10 +3114,12 @@ function dbg(text) { } // Have we cached the location of this uniform before? - var sizeAndId = program.uniformSizeAndIdsByName[uniformBaseName]; // A pair [array length, GLint of the uniform location] + // A pair [array length, GLint of the uniform location] + var sizeAndId = program.uniformSizeAndIdsByName[uniformBaseName]; - // If an uniform with this name exists, and if its index is within the array limits (if it's even an array), - // query the WebGLlocation, or return an existing cached location. + // If an uniform with this name exists, and if its index is within the + // array limits (if it's even an array), query the WebGLlocation, or + // return an existing cached location. if (sizeAndId && arrayIndex < sizeAndId[0]) { arrayIndex += sizeAndId[1]; // Add the base location of the uniform to the array index offset. if ((uniformLocsById[arrayIndex] = uniformLocsById[arrayIndex] || GLctx.getUniformLocation(program, name))) { @@ -3093,8 +3128,9 @@ function dbg(text) { } } else { - // N.b. we are currently unable to distinguish between GL program IDs that never existed vs GL program IDs that have been deleted, - // so report GL_INVALID_VALUE in both cases. + // N.b. we are currently unable to distinguish between GL program IDs that + // never existed vs GL program IDs that have been deleted, so report + // GL_INVALID_VALUE in both cases. GL.recordError(0x501 /* GL_INVALID_VALUE */); } return -1; @@ -3120,12 +3156,12 @@ function dbg(text) { if (p) { var webglLoc = p.uniformLocsById[location]; - // p.uniformLocsById[location] stores either an integer, or a WebGLUniformLocation. - - // If an integer, we have not yet bound the location, so do it now. The integer value specifies the array index - // we should bind to. + // p.uniformLocsById[location] stores either an integer, or a + // WebGLUniformLocation. + // If an integer, we have not yet bound the location, so do it now. The + // integer value specifies the array index we should bind to. if (typeof webglLoc == 'number') { - p.uniformLocsById[location] = webglLoc = GLctx.getUniformLocation(p, p.uniformArrayNamesById[location] + (webglLoc > 0 ? '[' + webglLoc + ']' : '')); + p.uniformLocsById[location] = webglLoc = GLctx.getUniformLocation(p, p.uniformArrayNamesById[location] + (webglLoc > 0 ? `[${webglLoc}]` : '')); } // Else an already cached WebGLUniformLocation, return it. return webglLoc; @@ -3134,22 +3170,28 @@ function dbg(text) { } }; + var _glUniform1f = (location, v0) => { + GLctx.uniform1f(webglGetUniformLocation(location), v0); + }; + + var miniTempWebGLFloatBuffers = []; var _glUniformMatrix4fv = (location, count, transpose, value) => { - if (GL.currentContext.version >= 2) { // WebGL 2 provides new garbage-free entry points to call to WebGL. Use those always when possible. - count && GLctx.uniformMatrix4fv(webglGetUniformLocation(location), !!transpose, HEAPF32, value>>2, count*16); + if (GL.currentContext.version >= 2) { + count && GLctx.uniformMatrix4fv(webglGetUniformLocation(location), !!transpose, HEAPF32, ((value)>>2), count*16); return; } if (count <= 18) { // avoid allocation when uploading few enough uniforms - var view = miniTempWebGLFloatBuffers[16*count-1]; + var view = miniTempWebGLFloatBuffers[16*count]; // hoist the heap out of the loop for size and for pthreads+growth. var heap = HEAPF32; - value >>= 2; - for (var i = 0; i < 16 * count; i += 16) { + value = ((value)>>2); + count *= 16; + for (var i = 0; i < count; i += 16) { var dst = value + i; view[i] = heap[dst]; view[i + 1] = heap[dst + 1]; @@ -3170,7 +3212,7 @@ function dbg(text) { } } else { - var view = HEAPF32.subarray((value)>>2, (value+count*64)>>2); + var view = HEAPF32.subarray((((value)>>2)), ((value+count*64)>>2)); } GLctx.uniformMatrix4fv(webglGetUniformLocation(location), !!transpose, view); }; @@ -3207,34 +3249,39 @@ function dbg(text) { Fetch.init();; -handleAllocatorInit(); -var GLctx;; + + // Signal GL rendering layer that processing of a new frame is about to + // start. This helps it optimize VBO double-buffering and reduce GPU stalls. + registerPreMainLoop(() => GL.newRenderingFrameStarted()); + ; var miniTempWebGLFloatBuffersStorage = new Float32Array(288); - for (/**@suppress{duplicate}*/var i = 0; i < 288; ++i) { - miniTempWebGLFloatBuffers[i] = miniTempWebGLFloatBuffersStorage.subarray(0, i+1); - } - ; + // Create GL_POOL_TEMP_BUFFERS_SIZE+1 temporary buffers, for uploads of size 0 through GL_POOL_TEMP_BUFFERS_SIZE inclusive + for (/**@suppress{duplicate}*/var i = 0; i <= 288; ++i) { + miniTempWebGLFloatBuffers[i] = miniTempWebGLFloatBuffersStorage.subarray(0, i); + }; function checkIncomingModuleAPI() { ignoredModuleProp('fetchSettings'); } var wasmImports = { /** @export */ + __assert_fail: ___assert_fail, + /** @export */ + _abort_js: __abort_js, + /** @export */ _emscripten_fetch_free: __emscripten_fetch_free, /** @export */ + _emscripten_memcpy_js: __emscripten_memcpy_js, + /** @export */ _localtime_js: __localtime_js, /** @export */ _tzset_js: __tzset_js, /** @export */ - abort: _abort, - /** @export */ emscripten_date_now: _emscripten_date_now, /** @export */ emscripten_get_element_css_size: _emscripten_get_element_css_size, /** @export */ emscripten_is_main_browser_thread: _emscripten_is_main_browser_thread, /** @export */ - emscripten_memcpy_js: _emscripten_memcpy_js, - /** @export */ emscripten_request_animation_frame_loop: _emscripten_request_animation_frame_loop, /** @export */ emscripten_resize_heap: _emscripten_resize_heap, @@ -3249,8 +3296,6 @@ var wasmImports = { /** @export */ emscripten_webgl_create_context: _emscripten_webgl_create_context, /** @export */ - emscripten_webgl_init_context_attributes: _emscripten_webgl_init_context_attributes, - /** @export */ emscripten_webgl_make_context_current: _emscripten_webgl_make_context_current, /** @export */ fd_close: _fd_close, @@ -3323,6 +3368,8 @@ var wasmImports = { /** @export */ glShaderSource: _glShaderSource, /** @export */ + glUniform1f: _glUniform1f, + /** @export */ glUniformMatrix4fv: _glUniformMatrix4fv, /** @export */ glUseProgram: _glUseProgram, @@ -3332,22 +3379,22 @@ var wasmImports = { glVertexAttribPointer: _glVertexAttribPointer }; var wasmExports = createWasm(); -var ___wasm_call_ctors = createExportWrapper('__wasm_call_ctors'); -var _malloc = createExportWrapper('malloc'); -var _free = createExportWrapper('free'); -var _main = Module['_main'] = createExportWrapper('main'); -var ___errno_location = createExportWrapper('__errno_location'); -var _fflush = Module['_fflush'] = createExportWrapper('fflush'); -var setTempRet0 = createExportWrapper('setTempRet0'); +var ___wasm_call_ctors = createExportWrapper('__wasm_call_ctors', 0); +var _main = Module['_main'] = createExportWrapper('main', 2); +var _free = createExportWrapper('free', 1); +var _malloc = createExportWrapper('malloc', 1); +var _fflush = createExportWrapper('fflush', 1); +var _strerror = createExportWrapper('strerror', 1); +var __emscripten_tempret_set = createExportWrapper('_emscripten_tempret_set', 1); +var __emscripten_tempret_get = createExportWrapper('_emscripten_tempret_get', 0); var _emscripten_stack_init = () => (_emscripten_stack_init = wasmExports['emscripten_stack_init'])(); var _emscripten_stack_get_free = () => (_emscripten_stack_get_free = wasmExports['emscripten_stack_get_free'])(); var _emscripten_stack_get_base = () => (_emscripten_stack_get_base = wasmExports['emscripten_stack_get_base'])(); var _emscripten_stack_get_end = () => (_emscripten_stack_get_end = wasmExports['emscripten_stack_get_end'])(); -var stackSave = createExportWrapper('stackSave'); -var stackRestore = createExportWrapper('stackRestore'); -var stackAlloc = createExportWrapper('stackAlloc'); +var __emscripten_stack_restore = (a0) => (__emscripten_stack_restore = wasmExports['_emscripten_stack_restore'])(a0); +var __emscripten_stack_alloc = (a0) => (__emscripten_stack_alloc = wasmExports['_emscripten_stack_alloc'])(a0); var _emscripten_stack_get_current = () => (_emscripten_stack_get_current = wasmExports['emscripten_stack_get_current'])(); -var dynCall_jiji = Module['dynCall_jiji'] = createExportWrapper('dynCall_jiji'); +var dynCall_jiji = Module['dynCall_jiji'] = createExportWrapper('dynCall_jiji', 5); // include: postamble.js @@ -3360,24 +3407,21 @@ var missingLibrarySymbols = [ 'writeI53ToU64Signaling', 'convertI32PairToI53', 'convertU32PairToI53', + 'stackAlloc', + 'getTempRet0', + 'setTempRet0', 'zeroMemory', - 'arraySum', - 'addDays', - 'setErrNo', + 'strError', 'inetPton4', 'inetNtop4', 'inetPton6', 'inetNtop6', 'readSockaddr', 'writeSockaddr', - 'getHostByName', 'initRandomFill', 'randomFill', - 'getCallstack', 'emscriptenLog', - 'convertPCtoSourceLocation', 'readEmAsmArgs', - 'jstoi_s', 'getExecutableName', 'listenOnce', 'autoResumeAudioContext', @@ -3386,10 +3430,8 @@ var missingLibrarySymbols = [ 'dynCall', 'runtimeKeepalivePush', 'runtimeKeepalivePop', - 'safeSetTimeout', 'asmjsMangle', 'asyncLoad', - 'alignMemory', 'mmapAlloc', 'getNativeTypeSize', 'STACK_SIZE', @@ -3423,6 +3465,7 @@ var missingLibrarySymbols = [ 'UTF32ToString', 'stringToUTF32', 'lengthBytesUTF32', + 'stringToNewUTF8', 'stringToUTF8OnStack', 'writeArrayToMemory', 'registerKeyEventCallback', @@ -3460,37 +3503,41 @@ var missingLibrarySymbols = [ 'registerBatteryEventCallback', 'setCanvasElementSize', 'getCanvasElementSize', - 'demangle', - 'demangleAll', 'jsStackTrace', - 'stackTrace', + 'getCallstack', + 'convertPCtoSourceLocation', 'getEnvStrings', 'checkWasiClock', 'wasiRightsToMuslOFlags', 'wasiOFlagsToMuslOFlags', 'createDyncallWrapper', + 'safeSetTimeout', 'setImmediateWrapped', 'clearImmediateWrapped', 'polyfillSetImmediate', + 'registerPostMainLoop', 'getPromise', 'makePromise', 'idsToPromises', 'makePromiseCallback', 'ExceptionInfo', 'findMatchingCatch', - 'setMainLoop', + 'Browser_asyncPrepareDataCounter', + 'safeRequestAnimationFrame', + 'arraySum', + 'addDays', 'getSocketFromFD', 'getSocketAddress', 'FS_createPreloadedFile', 'FS_modeStringToFlags', 'FS_getMode', 'FS_stdin_getChar', - 'FS_createDataFile', 'FS_unlink', + 'FS_createDataFile', 'FS_mkdirTree', '_setNetworkCallback', 'heapObjectForWebGLType', - 'heapAccessShiftForWebGLHeap', + 'toTypedArrayIndex', 'emscriptenWebGLGet', 'computeUnpackAlignedImageSize', 'colorChannelsInGlTextureFormat', @@ -3503,15 +3550,15 @@ var missingLibrarySymbols = [ 'writeGLArray', 'registerWebGlEventCallback', 'runAndAbortIfError', - 'SDL_unicode', - 'SDL_ttfContext', - 'SDL_audio', 'emscriptenWebGLGetIndexed', 'ALLOC_NORMAL', 'ALLOC_STACK', 'allocate', 'writeStringToMemory', 'writeAsciiToMemory', + 'setErrNo', + 'demangle', + 'stackTrace', ]; missingLibrarySymbols.forEach(missingLibrarySymbol) @@ -3524,57 +3571,42 @@ var unexportedSymbols = [ 'addOnPostRun', 'addRunDependency', 'removeRunDependency', - 'FS_createFolder', - 'FS_createPath', - 'FS_createLazyFile', - 'FS_createLink', - 'FS_createDevice', - 'FS_readFile', 'out', 'err', 'callMain', 'abort', - 'keepRuntimeAlive', 'wasmMemory', 'wasmExports', - 'stackAlloc', - 'stackSave', - 'stackRestore', - 'getTempRet0', - 'setTempRet0', 'writeStackCookie', 'checkStackCookie', 'writeI53ToI64', 'readI53FromI64', 'readI53FromU64', 'convertI32PairToI53Checked', + 'stackSave', + 'stackRestore', 'ptrToString', 'exitJS', 'getHeapMax', 'growMemory', 'ENV', - 'MONTH_DAYS_REGULAR', - 'MONTH_DAYS_LEAP', - 'MONTH_DAYS_REGULAR_CUMULATIVE', - 'MONTH_DAYS_LEAP_CUMULATIVE', - 'isLeapYear', - 'ydayFromDate', 'ERRNO_CODES', - 'ERRNO_MESSAGES', 'DNS', 'Protocols', 'Sockets', 'timers', 'warnOnce', - 'UNWIND_CACHE', 'readEmAsmArgsArray', 'jstoi_q', + 'jstoi_s', 'handleException', + 'keepRuntimeAlive', 'callUserCallback', 'maybeExit', - 'handleAllocatorInit', + 'alignMemory', 'HandleAllocator', 'wasmTable', + 'noExitRuntime', 'freeTableIndexes', 'functionsInTableMap', 'setValue', @@ -3588,7 +3620,6 @@ var unexportedSymbols = [ 'stringToUTF8', 'lengthBytesUTF8', 'UTF16Decoder', - 'stringToNewUTF8', 'JSEvents', 'specialHTMLTargets', 'maybeCStringToJsString', @@ -3600,18 +3631,31 @@ var unexportedSymbols = [ 'registerUiEventCallback', 'currentFullscreenStrategy', 'restoreOldWindowedStyle', + 'UNWIND_CACHE', 'ExitStatus', 'flush_NO_FILESYSTEM', + 'registerPreMainLoop', 'promiseMap', 'uncaughtExceptionCount', 'exceptionLast', 'exceptionCaught', 'Browser', + 'getPreloadedImageData__data', 'wget', + 'MONTH_DAYS_REGULAR', + 'MONTH_DAYS_LEAP', + 'MONTH_DAYS_REGULAR_CUMULATIVE', + 'MONTH_DAYS_LEAP_CUMULATIVE', + 'isLeapYear', + 'ydayFromDate', 'SYSCALLS', 'preloadPlugins', 'FS_stdin_getChar_buffer', + 'FS_createPath', + 'FS_createDevice', + 'FS_readFile', 'FS', + 'FS_createLazyFile', 'MEMFS', 'TTY', 'PIPEFS', @@ -3623,12 +3667,13 @@ var unexportedSymbols = [ 'webgl_enable_OES_vertex_array_object', 'webgl_enable_WEBGL_draw_buffers', 'webgl_enable_WEBGL_multi_draw', + 'webgl_enable_EXT_polygon_offset_clamp', + 'webgl_enable_EXT_clip_control', + 'webgl_enable_WEBGL_polygon_mode', 'GL', - '__glGenObject', 'webglGetUniformLocation', 'webglPrepareUniformLocationsBeforeFirstUse', 'webglGetLeftBracePos', - 'emscripten_webgl_power_preferences', 'AL', 'GLUT', 'EGL', @@ -3640,6 +3685,8 @@ var unexportedSymbols = [ 'webgl_enable_WEBGL_multi_draw_instanced_base_vertex_base_instance', 'allocateUTF8', 'allocateUTF8OnStack', + 'print', + 'printErr', 'Fetch', 'fetchDeleteCachedData', 'fetchLoadCachedData', @@ -3651,6 +3698,7 @@ unexportedSymbols.forEach(unexportedRuntimeSymbol); var calledRun; +var calledPrerun; dependenciesFulfilled = function runCaller() { // If run has never been called, and we should call run (INVOKE_RUN is true, and Module.noInitialRun is not false) @@ -3660,7 +3708,7 @@ dependenciesFulfilled = function runCaller() { function callMain() { assert(runDependencies == 0, 'cannot call main when async dependencies remain! (listen on Module["onRuntimeInitialized"])'); - assert(__ATPRERUN__.length == 0, 'cannot call main when preRun functions remain to be called'); + assert(calledPrerun, 'cannot call main without calling preRun first'); var entryFunction = _main; @@ -3697,19 +3745,22 @@ function run() { stackCheckInit(); - preRun(); + if (!calledPrerun) { + calledPrerun = 1; + preRun(); - // a preRun added a dependency, run will be called later - if (runDependencies > 0) { - return; + // a preRun added a dependency, run will be called later + if (runDependencies > 0) { + return; + } } function doRun() { // run may have just been called through dependencies being fulfilled just in this very frame, // or while the async setStatus time below was happening if (calledRun) return; - calledRun = true; - Module['calledRun'] = true; + calledRun = 1; + Module['calledRun'] = 1; if (ABORT) return; @@ -3717,7 +3768,7 @@ function run() { preMain(); - if (Module['onRuntimeInitialized']) Module['onRuntimeInitialized'](); + Module['onRuntimeInitialized']?.(); if (shouldRunNow) callMain(); @@ -3726,10 +3777,8 @@ function run() { if (Module['setStatus']) { Module['setStatus']('Running...'); - setTimeout(function() { - setTimeout(function() { - Module['setStatus'](''); - }, 1); + setTimeout(() => { + setTimeout(() => Module['setStatus'](''), 1); doRun(); }, 1); } else @@ -3782,5 +3831,5 @@ if (Module['noInitialRun']) shouldRunNow = false; run(); - // end include: postamble.js + diff --git a/themes/dist/output.wasm b/themes/dist/output.wasm Binary files differindex 925d783..4c88053 100755 --- a/themes/dist/output.wasm +++ b/themes/dist/output.wasm diff --git a/themes/emscripten.ini b/themes/emscripten.ini new file mode 100644 index 0000000..5824ce6 --- /dev/null +++ b/themes/emscripten.ini @@ -0,0 +1,14 @@ +[binaries] +c = 'emcc' +cpp = 'em++' +ar = 'emar' +ranlib = 'emranlib' + +[host_machine] +system = 'emscripten' +cpu_family = 'wasm32' +cpu = 'wasm32' +endian = 'little' + +[properties] +needs_exe_wrapper = true diff --git a/themes/meson.build b/themes/meson.build new file mode 100644 index 0000000..1e4c9f1 --- /dev/null +++ b/themes/meson.build @@ -0,0 +1,122 @@ +project('themes', 'cpp', + version: '1.0.0', + default_options: [ + 'cpp_std=c++17', + 'buildtype=release', + 'warning_level=2' + ] +) + +# Emscripten link flags +emscripten_link_args = [ + '-s', 'ALLOW_MEMORY_GROWTH=1', + '-s', 'USE_WEBGL2=1', + '-s', 'FULL_ES3=1', + '-s', 'WASM=1', + '-s', 'NO_EXIT_RUNTIME=1', + '-s', 'FETCH' +] + +# Source files +sources = files( + # Main + 'src/main.cpp', + 'src/main_loop.cpp', + 'src/mathlib.cpp', + 'src/logger.cpp', + 'src/shader.cpp', + 'src/shader_fetcher.cpp', + 'src/shapes_2d.cpp', + 'src/webgl_context.cpp', + 'src/renderer_2d.cpp', + 'src/renderer_3d.cpp', + + # Shaders + 'src/shaders/renderer2d_frag.cpp', + 'src/shaders/renderer2d_vert.cpp', + 'src/shaders/renderer3d_frag.cpp', + 'src/shaders/renderer3d_vert.cpp', + 'src/shaders/sun_frag.cpp', + 'src/shaders/sun_vert.cpp', + + # Autumn theme + 'src/autumn/autumn_theme.cpp', + 'src/autumn/leaf_particle_render.cpp', + 'src/autumn/tree_shape.cpp', + + # Winter theme + 'src/winter/winter_theme.cpp', + 'src/winter/snowflake.cpp', + 'src/winter/windfield.cpp', + + # Spring theme + 'src/spring/spring_theme.cpp', + 'src/spring/grass_renderer.cpp', + + # Summer theme + 'src/summer/summer_theme.cpp' +) + +# Include directories +inc = include_directories( + 'src', + 'src/autumn', + 'src/winter', + 'src/spring', + 'src/summer', + 'src/shaders', + 'src/_shaders', + 'src/tools' +) + +# Custom target to build shaders from GLSL files +node = find_program('node') +shader_script = meson.project_source_root() + '/src/tools/shader.js' + +# List all GLSL shader files as inputs +shader_inputs = files( + 'src/_shaders/renderer2d.frag', + 'src/_shaders/renderer2d.vert', + 'src/_shaders/renderer3d.frag', + 'src/_shaders/renderer3d.vert', + 'src/_shaders/sun.frag', + 'src/_shaders/sun.vert' +) + +# Custom target that runs whenever shader files change +sh = find_program('sh') +build_shaders = custom_target('build_shaders', + input: shader_inputs, + output: 'shaders.stamp', + command: [sh, '-c', node.full_path() + ' ' + shader_script + ' && touch @OUTPUT@'], + build_always_stale: true, + build_by_default: true +) + +# Build the executable +output_exe = executable('output', + sources, + include_directories: inc, + link_args: emscripten_link_args, + name_suffix: 'js' +) + +# Custom target to copy output files to dist directory +copy_script = find_program('sh') +copy_to_dist = custom_target('copy_to_dist', + output: ['copy_to_dist.stamp'], + input: output_exe, + command: [ + copy_script, '-c', + 'mkdir -p ' + meson.project_source_root() + '/dist && ' + + 'cp $1 ' + meson.project_source_root() + '/dist/output.js && ' + + 'cp $2/output.wasm ' + meson.project_source_root() + '/dist/output.wasm && ' + + 'touch $3', + '--', '@INPUT@', meson.current_build_dir(), '@OUTPUT@' + ], + build_by_default: true, + install: false +) + +# Add an alias target for convenience +alias_target('copy-dist', copy_to_dist) diff --git a/themes/src/_shaders/sun.frag b/themes/src/_shaders/sun.frag index 8463e06..2170b39 100644 --- a/themes/src/_shaders/sun.frag +++ b/themes/src/_shaders/sun.frag @@ -1,5 +1,43 @@ varying lowp vec4 VertexColor; +varying lowp vec2 TexCoord; +uniform lowp float time; + +// Simple noise function for edge distortion +lowp float noise(lowp vec2 p) { + return sin(p.x * 10.0 + time) * cos(p.y * 10.0 + time * 0.5) * 0.5 + 0.5; +} void main() { - gl_FragColor = VertexColor; + // TexCoord is now normalized: center is (0,0), edges are at distance ~1 + lowp float dist = length(TexCoord); + + // Add animated noise to the edge + lowp float angle = atan(TexCoord.y, TexCoord.x); + lowp float wave = sin(angle * 8.0 + time * 2.0) * 0.05 + sin(angle * 4.0 - time * 1.5) * 0.03; + lowp float noiseValue = noise(TexCoord + time * 0.1) * 0.02; + + // Create soft edge using smoothstep - ensure fade reaches zero at the actual edge + lowp float innerEdge = 0.8; + lowp float outerEdge = 1.0; + lowp float alpha = 1.0 - smoothstep(innerEdge, outerEdge, dist); + + // Apply wave distortion to the edge + alpha *= 1.0 - smoothstep(0.85 + wave + noiseValue * 2.0, 1.0, dist); + + // Make edges more transparent but not too much + alpha = alpha * alpha; + + // Add slight glow effect at the edge + lowp float glow = smoothstep(0.5, 0.8, dist) * (1.0 - smoothstep(0.8, 1.0, dist)); + + // Create orange gradient from center + lowp vec3 orangeColor = vec3(1.0, 0.5, 0.1); + lowp float centerGradient = smoothstep(0.6, 0.0, dist); + lowp vec3 baseColor = mix(VertexColor.rgb, orangeColor, centerGradient * 0.6); + + // Mix in the glow with a brighter color + lowp vec3 glowColor = baseColor * 1.3; + lowp vec3 finalColor = mix(baseColor, glowColor, glow * 0.5); + + gl_FragColor = vec4(finalColor, VertexColor.a * alpha); } diff --git a/themes/src/_shaders/sun.vert b/themes/src/_shaders/sun.vert index 76150f0..5ed77d7 100644 --- a/themes/src/_shaders/sun.vert +++ b/themes/src/_shaders/sun.vert @@ -1,13 +1,22 @@ -attribute vec2 position; -attribute vec4 color; -attribute mat4 vMatrix; -uniform mat4 projection; -uniform mat4 model; +attribute vec2 position; +attribute vec4 color; +attribute mat4 vMatrix; +uniform mat4 projection; +uniform mat4 model; varying lowp vec4 VertexColor; +varying lowp vec2 TexCoord; -void main() { - vec4 fragmentPosition = projection * model * vMatrix * vec4(position.x, position.y, 0, 1); - gl_Position = fragmentPosition; - VertexColor = color; +void main() { + vec4 fragmentPosition = projection * model * vMatrix * vec4(position.x, position.y, 0, 1); + gl_Position = fragmentPosition; + VertexColor = color; + // Normalize the position - the center is at (0,0) and edge vertices are at distance 'radius' + // We want TexCoord to be in the range roughly [-1, 1] at the edges + lowp float maxDist = length(position); + if (maxDist > 0.1) { + TexCoord = position / maxDist; + } else { + TexCoord = vec2(0.0, 0.0); + } } diff --git a/themes/src/autumn/AutumnTheme.cpp b/themes/src/autumn/autumn_theme.cpp index 4b7a2e2..d88b265 100644 --- a/themes/src/autumn/AutumnTheme.cpp +++ b/themes/src/autumn/autumn_theme.cpp @@ -1,4 +1,4 @@ -#include "AutumnTheme.hpp" +#include "autumn_theme.hpp" #include "../shapes_2d.h" #include <memory> diff --git a/themes/src/autumn/AutumnTheme.hpp b/themes/src/autumn/autumn_theme.hpp index e3f5748..b61c0f3 100644 --- a/themes/src/autumn/AutumnTheme.hpp +++ b/themes/src/autumn/autumn_theme.hpp @@ -1,11 +1,11 @@ #ifndef AUTUMN_THEME_HPP #define AUTUMN_THEME_HPP -#include "TreeShape.h" -#include "LeafParticleRender.h" +#include "tree_shape.h" +#include "leaf_particle_render.h" #include "../types.h" #include "../theme.h" -#include "../Renderer2d.h" +#include "../renderer_2d.h" #include <memory> #include <vector> diff --git a/themes/src/autumn/LeafParticleRender.cpp b/themes/src/autumn/leaf_particle_render.cpp index fee3df2..569bb2d 100644 --- a/themes/src/autumn/LeafParticleRender.cpp +++ b/themes/src/autumn/leaf_particle_render.cpp @@ -1,7 +1,7 @@ -#include "LeafParticleRender.h" -#include "../Renderer2d.h" +#include "leaf_particle_render.h" +#include "../renderer_2d.h" #include "../mathlib.h" -#include "TreeShape.h" +#include "tree_shape.h" #include "../types.h" #include <math.h> diff --git a/themes/src/autumn/LeafParticleRender.h b/themes/src/autumn/leaf_particle_render.h index f6efe1f..1209e1b 100644 --- a/themes/src/autumn/LeafParticleRender.h +++ b/themes/src/autumn/leaf_particle_render.h @@ -1,4 +1,4 @@ -#include "../Renderer2d.h" +#include "../renderer_2d.h" #include "../mathlib.h" #include "../types.h" diff --git a/themes/src/autumn/TreeShape.cpp b/themes/src/autumn/tree_shape.cpp index 7c80929..622751b 100644 --- a/themes/src/autumn/TreeShape.cpp +++ b/themes/src/autumn/tree_shape.cpp @@ -1,4 +1,4 @@ -#include "TreeShape.h" +#include "tree_shape.h" #include "../mathlib.h" #include <cstdio> #include <cstdlib> diff --git a/themes/src/autumn/TreeShape.h b/themes/src/autumn/tree_shape.h index fc0d11e..0d18415 100644 --- a/themes/src/autumn/TreeShape.h +++ b/themes/src/autumn/tree_shape.h @@ -1,4 +1,4 @@ -#include "../Renderer2d.h" +#include "../renderer_2d.h" #include "../types.h" #include "../mathlib.h" diff --git a/themes/src/list.h b/themes/src/list.h index 25b236a..9b6a719 100644 --- a/themes/src/list.h +++ b/themes/src/list.h @@ -1,7 +1,7 @@ #pragma once #include <cstdlib> #include <cstring> -#include "Logger.h" +#include "logger.h" #define FOREACH(list) \ for (i32 idx = 0; idx < list.numElements; idx++) \ diff --git a/themes/src/Logger.cpp b/themes/src/logger.cpp index 1068d88..bead282 100644 --- a/themes/src/Logger.cpp +++ b/themes/src/logger.cpp @@ -1,4 +1,4 @@ -#include "Logger.h" +#include "logger.h" #include <chrono> #include <cstdarg> #include <cstdio> diff --git a/themes/src/Logger.h b/themes/src/logger.h index 7596b6f..7596b6f 100644 --- a/themes/src/Logger.h +++ b/themes/src/logger.h diff --git a/themes/src/main.cpp b/themes/src/main.cpp index 14227c9..60e6aed 100644 --- a/themes/src/main.cpp +++ b/themes/src/main.cpp @@ -1,13 +1,13 @@ -#include "WebglContext.h" -#include "MainLoop.h" -#include "Renderer2d.h" +#include "webgl_context.h" +#include "main_loop.h" +#include "renderer_2d.h" #include "mathlib.h" #include "theme.h" #include "types.h" -#include "summer/SummerTheme.h" -#include "autumn/AutumnTheme.hpp" -#include "spring/SpringTheme.hpp" -#include "winter/WinterTheme.hpp" +#include "summer/summer_theme.h" +#include "autumn/autumn_theme.hpp" +#include "spring/spring_theme.hpp" +#include "winter/winter_theme.hpp" #include <cstdio> #include <emscripten/fetch.h> @@ -67,14 +67,19 @@ void load(ThemeType theme) { } void update(f32 dtSeconds, void* userData) { + if (!active_theme) + return; active_theme->update(dtSeconds); active_theme->render(); } void unload() { delete active_theme; + active_theme = nullptr; type = ThemeType::Default; + glClearColor(0, 0, 0, 0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); if (mainLoop.isRunning) { mainLoop.stop(); } @@ -84,7 +89,7 @@ void unload() { EM_BOOL selectNone(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData) { printf("Default theme selected\n"); unload(); - return true; + return true; } EM_BOOL selectAutumn(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData) { diff --git a/themes/src/MainLoop.cpp b/themes/src/main_loop.cpp index 09aa643..e5397ca 100644 --- a/themes/src/MainLoop.cpp +++ b/themes/src/main_loop.cpp @@ -1,4 +1,4 @@ -#include "MainLoop.h" +#include "main_loop.h" #include <cstdio> #include <cstdlib> diff --git a/themes/src/MainLoop.h b/themes/src/main_loop.h index 2573bb8..07520a2 100644 --- a/themes/src/MainLoop.h +++ b/themes/src/main_loop.h @@ -26,4 +26,4 @@ struct MainLoop { void stop() { isRunning = false; } -};
\ No newline at end of file +}; diff --git a/themes/src/Renderer2d.cpp b/themes/src/renderer_2d.cpp index f1d78e3..7200669 100644 --- a/themes/src/Renderer2d.cpp +++ b/themes/src/renderer_2d.cpp @@ -1,6 +1,6 @@ -#include "Renderer2d.h" -#include "Shader.h" -#include "WebglContext.h" +#include "renderer_2d.h" +#include "shader.h" +#include "webgl_context.h" #include "mathlib.h" #include <cstdio> #include "shaders/renderer2d_vert.h" diff --git a/themes/src/Renderer2d.h b/themes/src/renderer_2d.h index 7432894..d572533 100644 --- a/themes/src/Renderer2d.h +++ b/themes/src/renderer_2d.h @@ -1,8 +1,8 @@ #pragma once -#include "WebglContext.h" +#include "webgl_context.h" #include "types.h" -#include "Shader.h" +#include "shader.h" #include "mathlib.h" struct WebglContext; diff --git a/themes/src/Renderer3d.cpp b/themes/src/renderer_3d.cpp index 00315de..cc79940 100644 --- a/themes/src/Renderer3d.cpp +++ b/themes/src/renderer_3d.cpp @@ -1,9 +1,9 @@ -#include "Renderer3d.h" -#include "Shader.h" +#include "renderer_3d.h" +#include "shader.h" #include "list.h" #include "mathlib.h" -#include "WebglContext.h" -#include "Logger.h" +#include "webgl_context.h" +#include "logger.h" #include <cstdio> // Note: In the 'transform' attribute, the transform.x is the scale, diff --git a/themes/src/Renderer3d.h b/themes/src/renderer_3d.h index 5b2c8c8..5b2c8c8 100644 --- a/themes/src/Renderer3d.h +++ b/themes/src/renderer_3d.h diff --git a/themes/src/Shader.cpp b/themes/src/shader.cpp index 5f2b00e..ed2cab5 100644 --- a/themes/src/Shader.cpp +++ b/themes/src/shader.cpp @@ -1,4 +1,4 @@ -#include "Shader.h" +#include "shader.h" #include <string> GLuint loadIndividualShader(GLenum shaderType, const GLchar* cCode) { diff --git a/themes/src/Shader.h b/themes/src/shader.h index bc81764..bc81764 100644 --- a/themes/src/Shader.h +++ b/themes/src/shader.h diff --git a/themes/src/shaders/sun_frag.cpp b/themes/src/shaders/sun_frag.cpp index d1ea160..696b3b9 100644 --- a/themes/src/shaders/sun_frag.cpp +++ b/themes/src/shaders/sun_frag.cpp @@ -1,8 +1,46 @@ #include "sun_frag.h" const char* shader_sun_frag = "varying lowp vec4 VertexColor; \n" +"varying lowp vec2 TexCoord; \n" +"uniform lowp float time; \n" +" \n" +"// Simple noise function for edge distortion \n" +"lowp float noise(lowp vec2 p) { \n" +" return sin(p.x * 10.0 + time) * cos(p.y * 10.0 + time * 0.5) * 0.5 + 0.5; \n" +"} \n" " \n" "void main() { \n" -" gl_FragColor = VertexColor; \n" +" // TexCoord is now normalized: center is (0,0), edges are at distance ~1 \n" +" lowp float dist = length(TexCoord); \n" +" \n" +" // Add animated noise to the edge \n" +" lowp float angle = atan(TexCoord.y, TexCoord.x); \n" +" lowp float wave = sin(angle * 8.0 + time * 2.0) * 0.05 + sin(angle * 4.0 - time * 1.5) * 0.03; \n" +" lowp float noiseValue = noise(TexCoord + time * 0.1) * 0.02; \n" +" \n" +" // Create soft edge using smoothstep - ensure fade reaches zero at the actual edge \n" +" lowp float innerEdge = 0.8; \n" +" lowp float outerEdge = 1.0; \n" +" lowp float alpha = 1.0 - smoothstep(innerEdge, outerEdge, dist); \n" +" \n" +" // Apply wave distortion to the edge \n" +" alpha *= 1.0 - smoothstep(0.85 + wave + noiseValue * 2.0, 1.0, dist); \n" +" \n" +" // Make edges more transparent but not too much \n" +" alpha = alpha * alpha; \n" +" \n" +" // Add slight glow effect at the edge \n" +" lowp float glow = smoothstep(0.5, 0.8, dist) * (1.0 - smoothstep(0.8, 1.0, dist)); \n" +" \n" +" // Create orange gradient from center \n" +" lowp vec3 orangeColor = vec3(1.0, 0.5, 0.1); \n" +" lowp float centerGradient = smoothstep(0.6, 0.0, dist); \n" +" lowp vec3 baseColor = mix(VertexColor.rgb, orangeColor, centerGradient * 0.6); \n" +" \n" +" // Mix in the glow with a brighter color \n" +" lowp vec3 glowColor = baseColor * 1.3; \n" +" lowp vec3 finalColor = mix(baseColor, glowColor, glow * 0.5); \n" +" \n" +" gl_FragColor = vec4(finalColor, VertexColor.a * alpha); \n" "} \n" " \n"; diff --git a/themes/src/shaders/sun_vert.cpp b/themes/src/shaders/sun_vert.cpp index ca617c0..bacf3a6 100644 --- a/themes/src/shaders/sun_vert.cpp +++ b/themes/src/shaders/sun_vert.cpp @@ -1,16 +1,25 @@ #include "sun_vert.h" const char* shader_sun_vert = " \n" -"attribute vec2 position; \n" -"attribute vec4 color; \n" -"attribute mat4 vMatrix; \n" -"uniform mat4 projection; \n" -"uniform mat4 model; \n" +"attribute vec2 position; \n" +"attribute vec4 color; \n" +"attribute mat4 vMatrix; \n" +"uniform mat4 projection; \n" +"uniform mat4 model; \n" "varying lowp vec4 VertexColor; \n" +"varying lowp vec2 TexCoord; \n" " \n" -"void main() { \n" -" vec4 fragmentPosition = projection * model * vMatrix * vec4(position.x, position.y, 0, 1); \n" -" gl_Position = fragmentPosition; \n" -" VertexColor = color; \n" +"void main() { \n" +" vec4 fragmentPosition = projection * model * vMatrix * vec4(position.x, position.y, 0, 1); \n" +" gl_Position = fragmentPosition; \n" +" VertexColor = color; \n" +" // Normalize the position - the center is at (0,0) and edge vertices are at distance 'radius' \n" +" // We want TexCoord to be in the range roughly [-1, 1] at the edges \n" +" lowp float maxDist = length(position); \n" +" if (maxDist > 0.1) { \n" +" TexCoord = position / maxDist; \n" +" } else { \n" +" TexCoord = vec2(0.0, 0.0); \n" +" } \n" "} \n" " \n"; diff --git a/themes/src/shapes_2d.cpp b/themes/src/shapes_2d.cpp index d5a29ed..e00c521 100644 --- a/themes/src/shapes_2d.cpp +++ b/themes/src/shapes_2d.cpp @@ -1,5 +1,5 @@ #include "shapes_2d.h" -#include "Renderer2d.h" +#include "renderer_2d.h" #include "mathlib.h" #include "list.h" diff --git a/themes/src/shapes_2d.h b/themes/src/shapes_2d.h index 8e08504..325d525 100644 --- a/themes/src/shapes_2d.h +++ b/themes/src/shapes_2d.h @@ -2,7 +2,7 @@ #define SHAPES_2D #include "mathlib.h" -#include "Renderer2d.h" +#include "renderer_2d.h" #include "types.h" class RectangularGradient diff --git a/themes/src/spring/GrassRenderer.cpp b/themes/src/spring/grass_renderer.cpp index b69d111..685f733 100644 --- a/themes/src/spring/GrassRenderer.cpp +++ b/themes/src/spring/grass_renderer.cpp @@ -1,5 +1,5 @@ -#include "GrassRenderer.hpp" -#include "Renderer3d.h" +#include "grass_renderer.hpp" +#include "../renderer_3d.h" void GrassRenderer::load(GrassRendererLoadData params, Renderer3d* renderer) { const f32 COLUMN_INCREMENT = GRASS_BLADES_PER_COL / params.area.x; diff --git a/themes/src/spring/GrassRenderer.hpp b/themes/src/spring/grass_renderer.hpp index 8c96724..88879f3 100644 --- a/themes/src/spring/GrassRenderer.hpp +++ b/themes/src/spring/grass_renderer.hpp @@ -1,7 +1,7 @@ #ifndef GRASS_RENDERER_HPP #define GRASS_RENDERER_HPP -#include "Renderer3d.h" +#include "../renderer_3d.h" #include "mathlib.h" #include "types.h" diff --git a/themes/src/spring/SpringTheme.cpp b/themes/src/spring/spring_theme.cpp index e39c138..8507194 100644 --- a/themes/src/spring/SpringTheme.cpp +++ b/themes/src/spring/spring_theme.cpp @@ -1,5 +1,5 @@ -#include "SpringTheme.hpp" -#include "../Renderer3d.h" +#include "spring_theme.hpp" +#include "../renderer_3d.h" #include "../shader_fetcher.hpp" #include <cstdio> #include <emscripten/fetch.h> diff --git a/themes/src/spring/SpringTheme.hpp b/themes/src/spring/spring_theme.hpp index 64f9cb5..6079958 100644 --- a/themes/src/spring/SpringTheme.hpp +++ b/themes/src/spring/spring_theme.hpp @@ -3,7 +3,7 @@ #include "../mathlib.h" #include "../types.h" -#include "../Renderer3d.h" +#include "../renderer_3d.h" #include "../theme.h" diff --git a/themes/src/summer/SummerTheme.cpp b/themes/src/summer/summer_theme.cpp index 1f76b56..6d2cfec 100644 --- a/themes/src/summer/SummerTheme.cpp +++ b/themes/src/summer/summer_theme.cpp @@ -1,5 +1,5 @@ -#include "SummerTheme.h" -#include "../Renderer2d.h" +#include "summer_theme.h" +#include "../renderer_2d.h" #include "../list.h" #include "../mathlib.h" #include "../shaders/sun_frag.h" @@ -59,19 +59,23 @@ void Sun::load(Renderer2d* renderer) { indices.add(second); indices.add(third); } - + mesh.load(&vertices.data[0], vertices.numElements, &indices.data[0], indices.numElements, renderer); mesh.model = Mat4x4().translateByVec2(Vector2(renderer->context->width / 2.f, renderer->context->height / 2.f)); + + timeUniform = getShaderUniform(renderer->shader, "time"); + vertices.deallocate(); indices.deallocate(); } void Sun::update(f32 dtSeconds) { - + elapsedTime += dtSeconds; } void Sun::render(Renderer2d* renderer) { setShaderMat4(renderer->uniforms.model, mesh.model); + glUniform1f(timeUniform, elapsedTime); glBindVertexArray(mesh.vao); glDrawElements(GL_TRIANGLES, mesh.numIndices, GL_UNSIGNED_INT, 0); glBindVertexArray(0); diff --git a/themes/src/summer/SummerTheme.h b/themes/src/summer/summer_theme.h index 2ce6b7f..eb404fd 100644 --- a/themes/src/summer/SummerTheme.h +++ b/themes/src/summer/summer_theme.h @@ -1,6 +1,6 @@ #pragma once #include "../types.h" -#include "../Renderer2d.h" +#include "../renderer_2d.h" #include "../theme.h" #include <vector> @@ -8,6 +8,8 @@ struct Sun { f32 radius = 20.f; i32 sectors = 180; Mesh2D mesh; + f32 elapsedTime = 0.f; + i32 timeUniform = -1; void load(Renderer2d* renderer); void update(f32 dtSeconds); diff --git a/themes/src/WebglContext.cpp b/themes/src/webgl_context.cpp index df49c2d..71b983e 100644 --- a/themes/src/WebglContext.cpp +++ b/themes/src/webgl_context.cpp @@ -1,4 +1,4 @@ -#include "WebglContext.h" +#include "webgl_context.h" #include <cstdio> diff --git a/themes/src/WebglContext.h b/themes/src/webgl_context.h index 1956092..1956092 100644 --- a/themes/src/WebglContext.h +++ b/themes/src/webgl_context.h diff --git a/themes/src/winter/Snowflake.cpp b/themes/src/winter/snowflake.cpp index 57f1a8f..4ce8f3a 100644 --- a/themes/src/winter/Snowflake.cpp +++ b/themes/src/winter/snowflake.cpp @@ -1,5 +1,5 @@ -#include "Snowflake.h" -#include "../Renderer2d.h" +#include "snowflake.h" +#include "../renderer_2d.h" #include "../mathlib.h" #include "../list.h" #include <cstdio> diff --git a/themes/src/winter/Snowflake.h b/themes/src/winter/snowflake.h index ad027f6..11a1438 100644 --- a/themes/src/winter/Snowflake.h +++ b/themes/src/winter/snowflake.h @@ -4,7 +4,7 @@ #include "../types.h" #include "../mathlib.h" #include "../list.h" -#include "Windfield.hpp" +#include "windfield.hpp" struct Renderer2d; struct Vertex2D; diff --git a/themes/src/winter/Windfield.cpp b/themes/src/winter/windfield.cpp index 88fb74b..f6c3be3 100644 --- a/themes/src/winter/Windfield.cpp +++ b/themes/src/winter/windfield.cpp @@ -1,4 +1,4 @@ -#include "Windfield.hpp" +#include "windfield.hpp" template <i32 Width, i32 Height, i32 CellDimension> diff --git a/themes/src/winter/Windfield.hpp b/themes/src/winter/windfield.hpp index 5bf0c38..5bf0c38 100644 --- a/themes/src/winter/Windfield.hpp +++ b/themes/src/winter/windfield.hpp diff --git a/themes/src/winter/WinterTheme.cpp b/themes/src/winter/winter_theme.cpp index 052670e..a628f18 100644 --- a/themes/src/winter/WinterTheme.cpp +++ b/themes/src/winter/winter_theme.cpp @@ -1,5 +1,5 @@ -#include "WinterTheme.hpp" -#include "../Renderer2d.h" +#include "winter_theme.hpp" +#include "../renderer_2d.h" WinterTheme::WinterTheme(WebglContext* context) { diff --git a/themes/src/winter/WinterTheme.hpp b/themes/src/winter/winter_theme.hpp index 5ba6d94..d1c3e05 100644 --- a/themes/src/winter/WinterTheme.hpp +++ b/themes/src/winter/winter_theme.hpp @@ -1,10 +1,10 @@ #ifndef WINTER_THEME_HPP #define WINTER_THEME_HPP -#include "Snowflake.h" +#include "snowflake.h" #include "../types.h" #include "../theme.h" -#include "../Renderer2d.h" +#include "../renderer_2d.h" struct WebglContext; |
