Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compile Fornjot to WebAssembly #815

Closed
hannobraun opened this issue Jul 14, 2022 · 5 comments · Fixed by #1221
Closed

Compile Fornjot to WebAssembly #815

hannobraun opened this issue Jul 14, 2022 · 5 comments · Fixed by #1221
Labels
good first issue Good for newcomers topic: build Anything relating to the build system. type: feature New features and improvements to existing features

Comments

@hannobraun
Copy link
Owner

One of Fornjot's goals is support for embedding Fornjot models into websites, without any need for a custom backend. To make that possible we need to compile most Fornjot components to WebAssembly, specifically the wasm32-unknown-unknown target.

The object of this issue is to lay the foundation for browser support, by making sure the required components can be compiled this way. The following steps would need to be taken:

  1. Determine which components need to support WebAssembly. I think most of them can and should. The only exception I can think of is fj-app, which contains desktop-specific functionality, like handling command-line arguments. But I could be missing something.
  2. Compile those components to wasm32-unknown-unknown and fix any issues that come up.
  3. Update the CI build (see ci.yml) to guard against accidental regressions.
  4. Also update justfile, to make sure just build stays close to the CI build (context).

Once we have this capability in place, further steps towards web support can be taken.

Labeling as https://github.com/hannobraun/Fornjot/labels/good%20first%20issue, since this is more of a general Rust topic and likely won't require much, if any, knowledge of Fornjot.

@hannobraun hannobraun added type: feature New features and improvements to existing features good first issue Good for newcomers topic: build Anything relating to the build system. topic: ecosystem labels Jul 14, 2022
@hannobraun hannobraun added this to the Initial Web Support milestone Jul 14, 2022
@Michael-F-Bryan
Copy link
Contributor

From previous experience with using WebAssembly, I think the biggest pain points will be 1) making sure each of Fornjot's dependencies can cross-compile, and 2) setting up a JavaScript project which can import Fornjot's WebAssembly module.

Surprisingly, most of Fornjot's existing dependencies compile to WebAssembly out of the box. I went through fj-app's top-level dependencies and ran cargo build --target wasm32-unknown-unknown --package blah and as far as I can tell these are the only two crates that fail to build:

  • libloading (via fj-host) will obviously fail to compile because dlopen() isn't a thing in the browser
    • Shouldn't be a problem because we can compile models into the Fornjot web app to start with
  • the arboard crate (via fj-viewer > egui-winit) doesn't have a PlatformClipboard implementation when compiled to WebAssembly
    • Ideally, we could add a js feature1 which, when compiled to wasm32-unknown-unknown, will use wasm-bindgen to access the Clipboard API
    • This is going to be problematic because the browser's clipboard APIs are async while arboard's APIs are synchronous

Compiling to WebAssembly is actually pretty easy as long as we stick within the world of cargo and wasm-pack, but because the JavaScript "modules" situation is a mess these packages can be difficult for downstream users to consume. Passing the bundler target to wasm-bindgen (via wasm-pack build --target bundler) will generate code which uses a normal ES module import to import the WebAssembly module, however when I was trying to do this earlier in the year only Webpack supported it (for example, we ran into this issue with Parcel v2). The web target could be promising, but I'm not sure if it'll work in JavaScript projects using a bundler (i.e. pretty much every web app in existence).

This won't affect a demo app because we have full control over the JavaScript build process, but could cause a bit of friction for someone importing a potential @fornjot/viewer package from NPM.

Footnotes

  1. Note: Having a separate js feature is important because wasm32-unknown-unknown is not a synonym for "Browser". Several crates make this assumption, so when you try to use a standalone WebAssembly runtime like Wasmer or Wasm3 to load something which transitively depends on such a crate, you run into errors at runtime because wasm-bindgen's browser-specific functions haven't been provided.

@hannobraun
Copy link
Owner Author

Thanks for looking into this, @Michael-F-Bryan!

Let's take it one step at a time:

  1. We can extend the CI build to compile the crates for which that already works to wasm32-unknown-unknown.
  2. The fj-viewer crate should eventually support wasm32-unknown-unknown. I think that should be easy to achieve (see below). If not, we can open a more specific follow-up issue to track that.
  3. fj-app and fj-host can be considered out of scope. Their functionality is inherently not applicable to the web.

the arboard crate (via fj-viewer > egui-winit) doesn't have a PlatformClipboard implementation when compiled to WebAssembly

  • Ideally, we could add a js feature1 which, when compiled to wasm32-unknown-unknown, will use wasm-bindgen to access the Clipboard API

  • This is going to be problematic because the browser's clipboard APIs are async while arboard's APIs are synchronous

It looks like clipboard support is optional in egui-winit: https://github.com/emilk/egui/blob/898f4804b7b998ffeb1ff9f457b935e1364d6827/egui-winit/Cargo.toml#L28

We can just disable it for now. Once that turns into a problem, we can decide between two solutions:

  1. Make it work.
  2. Declare that anything requiring a clipboard is out of scope for the egui-based core UI, and expect that to be handled with platform-specific UI. I think that might be a reasonable position to take.

@hannobraun
Copy link
Owner Author

Clipboard support in egui-winit is disabled now (see #807 (comment)), so I figured I'd give this a try. A new problem I encountered is robust-predicates:

The following warnings were emitted during compilation:

warning: src/predicates.c:116:10: fatal error: 'stdio.h' file not found
warning: #include <stdio.h>
warning:          ^~~~~~~~~
warning: 1 error generated.

error: failed to run custom build command for `robust-predicates v0.1.3`

Caused by:
  process didn't exit successfully: `/home/hanno/Projects/main/fornjot/target/debug/build/robust-predicates-7139d933cc63d105/build-script-build` (exit status: 1)
  --- stdout
  TARGET = Some("wasm32-unknown-unknown")
  OPT_LEVEL = Some("0")
  HOST = Some("x86_64-unknown-linux-gnu")
  CC_wasm32-unknown-unknown = None
  CC_wasm32_unknown_unknown = None
  TARGET_CC = None
  CC = None
  CFLAGS_wasm32-unknown-unknown = None
  CFLAGS_wasm32_unknown_unknown = None
  TARGET_CFLAGS = None
  CFLAGS = None
  CRATE_CC_NO_DEFAULTS = None
  DEBUG = Some("true")
  running: "clang" "-O0" "-ffunction-sections" "-fdata-sections" "-fPIC" "-g" "-fno-omit-frame-pointer" "--target=wasm32-unknown-unknown" "-Wall" "-Wextra" "-w" "-o" "/home/hanno/Projects/main/fornjot/target/wasm32-unknown-unknown/debug/build/robust-predicates-98296a2bdf601536/out/src/predicates.o" "-c" "src/predicates.c"
  cargo:warning=src/predicates.c:116:10: fatal error: 'stdio.h' file not found
  cargo:warning=#include <stdio.h>
  cargo:warning=         ^~~~~~~~~
  cargo:warning=1 error generated.
  exit status: 1

  --- stderr


  error occurred: Command "clang" "-O0" "-ffunction-sections" "-fdata-sections" "-fPIC" "-g" "-fno-omit-frame-pointer" "--target=wasm32-unknown-unknown" "-Wall" "-Wextra" "-w" "-o" "/home/hanno/Projects/main/fornjot/target/wasm32-unknown-unknown/debug/build/robust-predicates-98296a2bdf601536/out/src/predicates.o" "-c" "src/predicates.c" with args "clang" did not execute successfully (status code exit status: 1).

We were using robust before, which is a Rust translation of the same C code, but doesn't have all the predicates we need. Maybe robust-predicates can be translated to Rust in an automated way.

Since robust-predicates is depended on from almost everything (fj-math and upwards), this is a complete blocker.

@hannobraun
Copy link
Owner Author

Another option might be to replace robust-predicates with simplicity, which implements the same predicates using a different technique, in pure Rust.

@hannobraun
Copy link
Owner Author

I have a (currently blocked) draft PR, which would address this issue: #1221

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
good first issue Good for newcomers topic: build Anything relating to the build system. type: feature New features and improvements to existing features
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants