godotengine / godot-proposals

Godot Improvement Proposals (GIPs)

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Add WASM (WASI) host support (including, but not limited to, the HTML5 target)

Type1J opened this issue · comments

Describe the project you are working on:
I am working on an app that needs updates often due to new features.

Describe the problem or limitation you are having in your project:
Replacing a GDNative dll is how we currently need to distribute new updates. Each platform needs it's own dll. The dlls are written in Rust. GDScript isn't an option due to the CPU intensive tasks in other threads that we are performing.

Describe how this feature / enhancement will help you overcome this problem or limitation:
Only 1 .dll (.wasm) file will need to be produced, and it will work on all platforms

Show a mock up screenshots/video or a flow diagram explaining how your proposal will work:

Current:
Windows -> .dll
Linux -> .so
macOS -> .dylib
HTML5 -> Currently not supported, but desired
Android -> .so
iOS -> Currently not supported, but desired (for our project)

After proposal:
Windows, Linux, macOS, HTML5, Android, and iOS (potentially more) -> .wasm

Describe implementation detail for your proposal (in code), if possible:
The .wasm dynamic library would basically implement the same API as GDNative dynamic libraries currently do. See wasmer and WASI (https://wasi.dev/) for an example host native application.

If this enhancement will not be used often, can it be worked around with a few lines of script?:
No. Currently, this is a build system and distribution problem. The feature is the only option for a GDNative library for HTML5, and it can be used to prevent the need for other proposals to go into the core.

Is there a reason why this should be core and not an add-on in the asset library?:
It enables asset libraries to make "native"-ish extensions that can be used on multiple platforms. They may not be as fast as true native, but they are faster than GDScript by far, and enable legacy code in the same way that GDNative does, but WASI also provides sandboxing support that GDNative does not have, which reduces the security audit required in some cases with asset library GDNative dynamic libraries.

Bugsquad edit (keywords for easier searching): WebAssembly

You could try using this:
https://wasmtime.dev/
This is a standalone webassembly vm, with a lot of embedding options including C, WASI included.

This one also means that Godot would supports a huge number of languages without any specific runtimes. Debugging is a real question tough, as I don't know if webassembly has any way to map the bytecode back to whatever it was compiled from.

As a stepping stone why not keep the existing structure of compiled per platform libraries, but add wasm to the list for the web platform. Later it could be tested weather allowing wasm to be used for all platform builds via some wasm runtime should be added as an option, by testing the performance impact that would introduce.

We are already using a Rust GDNative plugin that includes wasmer-runtime and loads a .wasm library that is switched out. We still don't support HTML5, but we've worked around this issue for now. I believe that it should be resolved before we expand to HTML5 and iOS.

@robbiespeed Things that are designed for the web are always fast. That's one of the main business targets. Faster execution means that your customer gets to your products sooner. Overly optimized javascript is not that much slower than native code (the parsing is the part where it has a small slowdown, yes I was surprised), and webassembly is even faster than that.
https://takahirox.github.io/WebAssembly-benchmark/

@Frontrider yes wasm is faster than javascript (in some workloads), and we have come a long way in performance on the web, but it is still slower than native compiled code.

Here's a fairly in depth paper on wasm vs native performance https://arxiv.org/pdf/1901.09056.pdf

In that paper it describes that on average in their tests wasm performed roughly 1.5x slower than native.

@robbiespeed I know that it's slower. Nothing is faster than native at this point. I personally don't think speed is going to be an issue for now (it's already faster than whatever runtime this small-ish team could produce on their own), and the plan is to compile gdscript to webassembly and that would be a big speedup on it's own. (I'm personally fond of gdscript, even tough I do not like python's syntax)
The benefits of not needing to write any specific compatibility layers for a large chunk of languages/tools is also big one.

And yes I agree, GDnative should remain and be improved, because there is a clear usecase for it.

and the plan is to compile gdscript to webassembly and that would be a big speedup on it's own.

I don't think that's currently planned.

@Frontrider this proposal if I interpreted it correctly is about introducing wasm as a build target for gdnative projects, and replacing existing gdnative generated platform specific libraries with wasm, so that you only need to have one build target.

I am very much looking forward to the first part, because we need that in order to have web as a supported build target for gdnative projects. I don't think the second part is a good idea though unless it were optional, even then I don't see much benifit.

commented

This seems like the place where HTML5 support for gdnative is tracked, is this feature (primarily the first part) on the roadmap?

@TheRawMeatball Support for GDNative in HTML5 may happen in the future, but there's no target version.

A way to call WebAssembly functions from Godot desktop apps today

I started on a project last year to integrate the Wasmtime WebAssembly runtime with Godot for desktop (Linux, Mac & Windows).

Over past couple of days I finally did a public release of the project "WASM Engine for Godot" (MIT License) in its current WIP stage if you're interested in checking it out: https://gitlab.com/RancidBacon/godot-wasm-engine

Current implementation details

The add-on is implemented on top of Foreigner--a wrapper for the libffi Foreign Function Interface library--along with some additions for buffer/struct support. (And is in part a demonstration of why I think FFI/ctypes-style support in the core would enable more functionality to be added (& added more easily) to Godot.)

The use of Foreigner means that the libwasmtime wrapper is written entirely in GDScript and further functionality from Wasmtime can be exposed without writing or compiling any C/C++: https://gitlab.com/RancidBacon/godot-wasm-engine/-/blob/main/src/addons/wasm-engine/WasmEngine.gd

The current wrapper was handwritten (in part because I was still figuring out how to embed libwasmtime as the C API was pretty under-documented when I started) but after gaining further experience with wrapping another complex library I'd probably look at doing more scripted generation of the binding moving forward.

Demonstration projects

There are two demonstration projects built on Godot which have binaries available for download for Linux, Mac & Windows:

So, even in the add-on's current WIP state (which requires functions called must have no parameters and either return a 32-bit integer or nothing; and, a module must not require any external imports) it's still possible to do interesting things.

Interested to have people try out the project & to see what WASM execution functionality can enable for Godot with some concrete examples. Thanks!

Hello! I'm another proponent of adding this feature to Godot by default. As someone who does hardware development with OpenSource ISAs (RISC-V, OpenPOWER) it would be useful to have a platform-agnostic binary that could be used to demonstrate graphical applications. Thank you!

I'm interested too, but I have a feeling it won't be added for a while. In the big scheme of things, most people aren't using GDNative for their games.

I'm interested too, but I have a feeling it won't be added for a while. In the big scheme of things, most people aren't using GDNative for their games.

Don't get me wrong, but if something is this tedious to set up, with this amount of extra steps (compared to alternatives it is like that) then of course it won't be widely used. Convenience is a big strength.

Webassemby at least has a merit of being fully integrateable to the editor as is, because you should be able to read the library in a more convenient way. It's not out of bounds to have a dropdown where you can just pick methods from the exported webassembly file, like "map my _ready function to the exported function unit_ready", because at worse (terrible solution) you can regex out the exported names from the textual representation unlike with other binary formats. The textual may even be enough for Godot, as we can build the binary as we package the game.

If we wanted to build wasm into godot engine, here's another approach:

Advantages:

  • Advantages of WASI. Runtime portabilility
    • AOT, JIT from wasm-micro-runtime
  • Can reuse GDNative
  • The runtime is in c++ so it has a high chance of being accepted.
  • NOT RUST

Disadvantages:

  • Probably slower?
  • C++ can be hard to use compared to rust

To all, I still not having the overview yet for Godot and WASM.
Is it possible to export Godot WASM with JavaScript callable methods and callback that allows the exported Godot WASM embedded in HTML5 as a Web Component serving as 3D Viewer?

If this can be done based on discussions in this thread, what will be the practical way to do that? This functionality serving as plugin? How to make that a plugin module. The plugin module essentially serve to broadcast events in godot 3D to outside world as callbacks and expose methods that allow outside JavaScript to access Godot internal Node properties and functions.

I value your feedback and suggestion.

Is it possible to export Godot WASM with JavaScript callable methods

Calling JavaScript code from GDScript is possible, but calling GDScript from JavaScript isn't (see #286).

callback that allows the exported Godot WASM embedded in HTML5 as a Web Component serving as 3D Viewer?

Godot's HTML5 export is designed to be on its own page. While you can customize the export template, it's not a Web Component you can place on arbitrary pages and expect it to work.

@Calinou
During initialization, it is possible for OUTSIDE to pass in a series of string argument through engine.start()
By using GDScript call JavaScript, perhaps a way to redirect events and messages from within Godot to OUTSIDE.

Perhaps potentially, having a WASM as plugin that serve as the BRIDGE server between INSIDE and OUTSIDE. During Engine Init, the intended URL for the bridge server is being passed as argument for engine.Start().

I hope to use Godot as a sophisticated 3D viewer.

I do not yet have the overview, looking forwards for any other ideas.

@GeorgeS2019 Please use other community channels to ask support questions. This place is meant to discuss feature proposals; please don't derail them.

@follower Were you able to match the entire GDNative api? Was curious about your https://gitlab.com/RancidBacon/godot-wasm-engine implementation.

Last night we got WASM defined functions with arguments returning to Godot Engine. We're using https://github.com/bytecodealliance/wasm-micro-runtime.

@fire: Got a demo anywhere?

Demo of the WASM stuff fire was talking about: https://drive.google.com/file/d/1CYvogO4F9URhvFvZIDsxyVQWVupymLcv/view?usp=sharing

Use the godot exe to open the project that's in the folder next to it and then hit run. WARNING: I never got around to fixing an issue with one of the particle systems. It's playing all the time and causes overdraw and lag. In the editor, zooming out all the way or disabling the particle system will fix this. In game, just keep moving so that the particles don't draw on top of each other. Sorry for the mess.

The source code for this demo is here: https://github.com/V-Sekai/WasGo/tree/main/demo

With the implementation of the new GDExtension API it should be possible to integrate WASM into Godot quite well:

  • A WASI implementation of the GDExtension API needs to be provided
  • To the outside world the WASM code would look as a normal GDExtension module / class
  • When targeting the web, the WASM code would be linked into the main WASM module with the rest of the engine
  • A special case is the online editor, in this case a WASM interpreter needs to be embedded to allow the changes in the WASM code resources at runtime
  • When targeting all other platforms a WASI compatible WASM runtime needs to be embedded into the engine that will create the bridge between the WASM code and the engine.

@kisg
Awesome news!
Where can i find some usage examples or docs about WASI usage within godot engine?
Thank you in advance.

@blockspacer AFAIK this suggestion with the GDExtension API (and the related #3370) is not implemented yet, my goal was to check if there is interest / objections from the core developer's side.
An equally important proposal is in my opinion #3369, which would simplify the engine considerably, but unfortunately it did not get much interest until now.

Apologies for a bit of necromancy but as another option Wasmtime recently reached 1.0

Not just simplify the engine, but as a side effect also remove the "tear" that the engine currently has along c# and GDScript. Typed GDScript is even a good target for webassembly, as it does not need garbage collection.

I've been sketching how to do wasm based gdscript and other extensions based on c++ wasm3.

Good to hear @fire. Do you intend to use the CodeGenerator interface in the GDScript runtime to emit WASM bytecode instead of GDScript bytecode? Would love to hear more. :)

Thinking about it, could it also allow Godot to focus more on GDScript's syntax and usability? Wasm is quite fast on it's own already. You can also try to integrate existing wasm optimizers instead of trying to develop an in-house solution.

Compilation speeds matter, but not as much as execution speed.

Thinking about it, could it also allow Godot to focus more on GDScript's syntax and usability? Wasm is quite fast on it's own already. You can also try to integrate existing wasm optimizers instead of trying to develop an in-house solution.

I don't think GDScript should depend on a WebAssembly runtime being available. There are likely cases where WebAssembly is not usable or only available in limited form (e.g. on consoles). Moreover, building the WebAssembly runtime from source is probably not easy, especially if you don't have a Rust toolchain installed. We want Godot to remain fast and easy to build from source after all 🙂

@Calinou To be clear, I am not saying that the new GDScript interpreter should be thrown out.

But the concerns that you raise are not really an issue for a WASM-based runtime:

  • There are WASM runtimes written in C / C++, like WAMR, which has a very low footprint (~85K for interpreter and ~50K for AOT).
  • WASM would provide a very nice AOT compilation solution, that would be very beneficial for production builds, especially on consoles and mobile. It would also improve the performance on the web, if the whole project could be distributed as a single WASM module, with all the scripts precompiled in the WASM bytecode.

So I think that a WASM-based alternative runtime for GDScript would have a lot of benefits, and with the WASI standard already widely implemented, it would be straightforward to provide bindings to the rest of Godot inside the WASM runtime. Now that the GDScript Runtime has a nice abstraction for bytecode generation, it should be easier to create this alternative runtime.

Hoping to keep this open. I compile the core simulation of my game to a WASM binary for portability and would love to use Godot as an interface for the sim (right now I use the web, Bevy, and raylib as interfaces that use the .wasm file).