Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Undeprecate WebAssembly support (#47)
* WASM test (cherry picked from commit 11e2cde) * Cleanup the changes from the previous commit (cherry picked from commit 52016dd) * Set default build target to `wasm32-unknown-unknown` This is so that rust-analyzer will use this build target when analyzing code. (cherry picked from commit ddfa884) * Enable WASM threading and disable WASM audio I turned on some compiler flags to enable experimental support for atomics and shared memory in WebAssembly, because this will probably be needed if we end up using web workers to handle the filesystem operations in WebAssembly builds. Unfortunately, rodio doesn't support web workers, and in fact rodio crashes Luminol in WebAssembly when I simply turn on those compiler flags without making any changes to the code! The audio in WebAssembly builds will have to be reimplemented without rodio. I don't think this will be too hard, given that all we really use rodio for is straightforward audio playback... * Disable WASM threading flags Apparently you can't use shared memory from inside of WebAssembly without passing extra arguments to wasm-bindgen that Trunk doesn't support. That's okay, we'll make do without these compiler flags. We still need coi-serviceworker.js because `Atomics.wait()` can't be used without it in most browsers. rodio also still won't work because we're going to run in a web worker, where the audio APIs that rodio needs aren't available. * Revert previous commit Nevermind, there's no flag incompatibility issue with Trunk. I'm probably just sleep-deprived... * Refactor to not use `eframe::Frame` I'm making a completely custom egui app runner for WebAssembly builds, since eframe's web runner makes such widespread assumptions about the availability of certain JavaScript APIs that are only present outside of web workers that it's unsalvageable when it comes to getting it to run in a web worker. (Both egui and wgpu can run in a web worker, though, so a custom runner should work.) But I ran into problems because implementing a custom runner requires creating `eframe::Frame`s which can't be done outside of the eframe crate. In the future it would be best to stop using eframe entirely, but I don't want to also have to rewrite the native runner right now so this is the best I can do. * Rudimentary web worker rendering I added a new app runner that can run in a web worker. The entire program now runs in a web worker, which means we can now use things like mutexes, channels and other synchronization constructs without making the browser angry when they block. If you try it right now, the canvas will be very small and will not react to user input. These things will be fixed later. * Rewrite web worker launcher code in Rust The WebAssembly code now runs in both the main thread and the worker thread with the same code and the same memory (i.e. multithreading), but with different entry points. The code on the main thread gets some information from the environment and sends it to the code on the worker thread. The worker thread runs the actual program. The purpose of doing this is so that we can write event handlers in Rust, for things like detecting user input on the canvas from the main thread and sending it to the worker thread, or sending a request for a filesystem operation from the worker thread to the main thread. * Add a hook to the main thread for detecting screen size changes The main thread now listens for changes to the screen size and then sends them to the worker thread so that it can resize the canvas to fill the screen. We use a `std::sync::mpsc::Sender` on the main thread to send the new screen sizes to a `std::sync::mpsc::Receiver` on the worker thread. This has the benefit of being more convenient than `Worker.postMessage()` and also is probably faster because we don't have to deal with the serialization overhead associated with `Worker.postMessage()`. The Rust senders and receivers internally use WebAssembly's experimental atomics and shared memory features since I enabled these things in the compiler flags. They need Cross-Origin Isolation to be enabled to work, so don't remove the coi-serviceworker.js shim. * Web worker runner now responds to mouse events * Move event handler code into web_worker_runner.rs * Remove duplicate `window` from event handler code * Use Tokio channels instead of std channels We need a channel with an asynchronous `recv()` method in order to send messages from the worker thread to the main thread, so we might as well use Tokio channels everywhere. * Fix memory leak in `WebWorkerRunner::setup_render_hooks()` At least I *think* this was a memory leak. Anyways, the use of `CustomFrame` was somehow causing the rendering to slow down slightly every frame. So that makes me think there was a memory leak somewhere. I couldn't figure out how to fix it properly so I just removed `CustomFrame` since we don't need it anyways. But the most important thing is that it runs at full speed now! * Add a channel for filesystem operations This commit adds a channel from the worker thread to the main thread so that the worker thread can send the main thread requests for filesystem operations. The results are sent back using a Tokio oneshot channel. * Channel senders are now stored as clones instead of by reference This frees us from having to deal with manually specifying lifetimes while still otherwise behaving exactly the same. * Set `max_texture_side` properly in `egui::RawInput` * Implement `FileSystem` for WebAssembly builds The `File` implementation is not done yet, but it will be soon! * `FileSystemCommand` variants are now private * Implement `File` for WebAssembly builds We now have a complete filesystem implementation for web browsers. Finally. * Implement project loading for WebAssembly builds Project loading is quite slow! I will be experimenting with a path cache for the filesystem later to see if that helps with speed. * Use egui context's time instead of system time `std::time::Instant` can't be used in WASM. * Also stop using system time in tilepicker.rs * Split sprite.wgsl and tilemap.wgsl into two parts * Enable wasm-bindgen support for reference types and weakrefs * Use uniforms instead of push constants in web builds * Remove `USING_PUSH_CONSTANTS` from shader headers * Tilemap now changes the projection matrix instead of the viewport rect Before, the tilemap implemented panning and scaling by simply panning and scaling the viewport rectangle that it rendered onto. This meant if any part of the tilemap were offscreen, that part of the viewport would be offscreen, too. The WebGPU standard actually specifies that you're not allowed to have any part of the viewport outside of the screen! (i.e. the intersection of the viewport rectangle and the screen rectangle must be equal to the viewport rectangle) Consequently, most web browsers will not allow you to do this. The version of wgpu that we currently use, version 0.16.3, allows you to do this on native builds, but only because the developers forgot to implement a check for this. It's actually undefined behaviour - some drivers will allow this and some won't. It will be explicitly disallowed in a future release of wgpu and already is in their trunk branch. So, I made a minor change to the tilemap so that its viewport rectangle always stays on screen. Now, the viewport rectangle is always the same as the UI rectangle that egui allocates for the tilemap, and the viewport projection matrix changes instead to handle panning and scaling. This should be the last change needed for the map editor to work in WebAssembly, although it also needs to be applied to the event graphics and the tilepicker, which is not done yet. * Tilemap event graphics now use projection matrix The event graphics on the tilemap now render using the area allocated by egui for the map editor as the viewport and handle scaling and panning by modifying the projection matrix. The event graphics in the hover tooltips do not do this, they just render using the tooltip as the viewport because the tooltip doesn't normally go offscreen. For some reason, egui sometimes draws the tooltip offscreen for one frame before snapping it back onscreen, so a check is added to disable rendering the tooltip graphic unless the graphic is fully onscreen. * Tilepicker now uses projection matrix The map editor should now be working in web builds! Except you won't be able to use the keyboard because I haven't put the keyboard handling code into the app runner yet. * Move JavaScript bindings into bindings.js and bindings.rs * Upload bindings.js and bindings.rs * Fix native builds Native builds can now be built with `cargo build` in addition to web builds being able to be built with `trunk build`. I really don't like these hooks, but we need to enable build-std only in web builds, and Trunk provides us no other way to do this. * Apply clippy recommendation * Add web event handler for keyboard presses/releases * Add web event handler for modifier keys * Remove unnecessary field from tile shader.rs * Cleanup web keyboard handler code * Add web event handler for text input This is used by egui when typing in text boxes. * Add web event handler for scrolling * Fix log messages not appearing in web builds * Fix handling of `pixels_per_point` in web builds * Add web event handler for egui's cursor change requests * Refactor web initialization code This refactors the code for initializing Luminol in web builds to not use global state that is shared between different parts of the program, and also moves the `USE_PUSH_CONSTANTS` constant into src/components, the only place it's used from. * Add web event handlers for clipboard and opening URLs * Add `Clipboard` feature to web-sys * Remove unnecessary `mut` in src/filesystem/web.rs * Add handler for RTP loading in web builds Since we have no way to load from an arbitrary directory without the user's permission, they're currently loaded from the "RTP" subdirectory in the project directory. This can be changed in the future if required. * Remove another unnecessary `mut` in src/filesystem/web.rs * Refactor archiver to work in web builds I don't have any archives to test with, but I can't think of any reason why these simple changes wouldn't work. Feel free to prove me wrong! * Remove unnecessary `.clone()` from src/main.rs * Implement eframe storage and egui memory in web builds This allows most state to be persisted. Unfortunately, we'll need a different kind of storage to persist the project directory handles. * Implement project directory persistence for web builds Project directory handles are now stored in IndexedDB so that they can be recovered after the user closes or refreshes the page. * Unregister render loop and event listeners on panic This prevents the console from being spammed with WebGPU errors in the event of a panic. * Re-add rodio as a dependency in web builds * Save on mouseleave in web builds * Restore the fullscreen toggle in native builds * Restore playtest and console in native builds * Restore the quit button in native builds * Implement project creation for web builds I had to replace surf with reqwest because surf won't compile with wasm-bindgen > 0.2.84 and eframe won't compile with wasm-bindgen < 0.2.86. * Drop directory handles from IndexedDB on error * Implement audio in web builds I replaced the Symphonia decoders with rodio's default ones because rodio can't decode OGG files properly when using Symphonia's decoder. * Don't include `AudioWrapper` in native builds * Use `.removeEntry` to remove files/dirs instead of `.remove` `FileSystemDirectoryHandle.removeEntry` is the standard way to remove files and directories. `FileSystemHandle.remove` is nonstandard. * Fix `Drop` implementation in `AudioWrapper` It's possible for the `AudioWrapper` to be dropped on a different thread than it was created on. This commit handles that case. * Show an alert box in web builds on panic * Fix command names in hooks on Windows * Use `wgpu::Adapter::features` to detect push constants support * Don't show panic message if COI isn't enabled * Archiver filesystem now wraps a file instead of a filesystem * Apply clippy recommendations * Re-enable sound effect picker in the items window * Make some minor stylistic changes to names in src/filesystem/web.rs * Fix RTPs not being detected properly in web builds That's seriously embarassing, haha. * Auto-enable `Write` when `Truncate` or `Create` are set in web builds This probably shouldn't be necessary given that Rust's documentation states that you need to manually enable `Write` to use `Truncate` or `Create`, but I guess if it worked before in native then it should be supported in web builds, too. * Web filesystem `remove_dir` is now recursive This is for consistency with the host filesystem's implementation. * Make `project_path` in web builds consistent with native * Optimize path cache to avoid double-loading the web RTP folder * Move `skip` variable from previous commit outside of the loop * Fix crash when loading from nonexistent folder in web builds
- Loading branch information