-
Notifications
You must be signed in to change notification settings - Fork 74
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
Running WASM from inside Unity #20
Comments
It should work on any platform that supports I haven't heard of anyone trying it yet, so if you move forward I'm curious how well it works for you 🙂 |
Thanks for the intel! Looking at https://docs.unity3d.com/Manual/ScriptingRestrictions.html is looks like most Unity targets don't support System.Reflection.Emit... but some do! Universal Windows Platform, and Mono-based Standalone/Android/WiiU should be able to do it. I'm gonna try it with UWP, and if that doesn't work, Mono Standalone, and report back here. Fingers crossed! I know that using .dll files from Unity works, so if dotnet-webassembly could generate a .dll, I think that's mission accomplished. Is that what "Support saving generated assemblies as DLLs on .NET Framework via the above extensibility mechanism" in the post-1.0 means? |
Realized after posting it that UWP won't work because it's restricted to .NET Core builds (as per ScriptingRestrictions link). So, I tried the Mono Standalone versions, and alas, no luck =( It gives me this exception: PlatformNotSupportedException: Operation is not supported on this platform. Any ideas? If not, I imagine things will work once dotnet-webassembly can save .dll files. |
Oh, I should mention, I got the above exception when trying to run the "Create and execute a WebAssembly file in memory" sample, on the "var instanceCreator = module.Compile();" line. |
https://github.com/dotnet/corefx/issues/4491#issuecomment-469009414 mentions an alternative for saving .dll files, how long do you think it would take for someone like me to contribute to make dotnet-webassembly use that, and do you think that would accomplish my goals? |
If I'm mapping that offset of that exception correctly, it's dying on The alternative solution behind that link might work, though some effort may be needed to get it integrated. You're certainly welcome to make a fork and give it a try--the file you'll need to do the most work on is https://github.com/RyanLamansky/dotnet-webassembly/blob/master/WebAssembly/Runtime/Compile.cs . As far as WebAssembly for .NET goes, my most pressing concern right now is getting to 100% spec compliance. Even if it supported AoT today, that's of little use if real-world WASMs don't work as expected. I'm in the "last 10% takes 90% of the time" phase of this project in that regard 😅 |
Did some more digging, it turns out Unity doesn't support .NET Core: https://docs.unity3d.com/2019.3/Documentation/Manual/dotnetProfileSupport.html Quite unfortunate. Perhaps I could make dotnet-webassembly output .NET Standard .dll files? Looking at https://docs.microsoft.com/en-us/dotnet/api/system.reflection.emit.assemblybuilder?view=netframework-4.8 it might be pretty easy, so I'll try it out. Is that the kind of approach you had in mind for "Support saving generated assemblies as DLLs on .NET Framework via the above extensibility mechanism."? |
I'm mostly waiting on this: https://github.com/dotnet/corefx/issues/4491 When that's done, .NET Core (and maybe even .NET Standard) will have a built-in mechanism to create DLLs. Then this library could be part of a build pipeline or be used for one-time conversions. It wouldn't be too hard to make a .NET 4.x version of this library, considering that I did so officially until earlier this year, which may give you a path to what you need before .NET Core is ready. |
I've been hacking at it a bit, learning a lot, and I think I've made progress... I've made an alternate csproj which uses .NET Framework instead of core, and added an assemblyBuilder.Save("something.dll"); line (more at Verdagon@59ab3e9) I'm hitting a "System.InvalidOperationException: 'Cannot save a transient assembly.'" during that Save call, which sounds like a great stopping point for now. If you have any ideas before I dive back in, they'd be much appreciated =D |
Change |
That worked, it produced a .dll file! Thanks for the tip! It seems to be empty according to the object browser and ildasm, so the next step is for me to figure out why my functions aren't getting in there (I suspect my test program just isn't populating Module correctly). The assembly is correctly calling itself CompiledWebAssembly, this is a very promising start! |
No dice =( I did the same thing your TableImportTests did, with the same table2.wasm, and it still made an empty .dll file. Any idea what I should try next? Otherwise, I'm thinking I'll go backwards from a program that does create a non-empty .dll file, and slowly make it call the same things in the same order as your program does, to see when it breaks. |
There's probably a missing step at the very end where it actually finalizes all the generated objects into the DLL. I know the process works because what you're doing, I did myself in the early phases of this project when I wanted to make sure my code generation was correct. I'll take a look at your fork tomorrow and see if I can figure it out. |
Thanks for offering to take a look! Luckily, I found a way past that roadblock. In addition to the Save call I added, I needed to pass another parameter to DefineDynamicModule, like in this answer: https://stackoverflow.com/a/3963033. Suddenly, there's a CompiledWebAssembly.dll with things in it! The next mystery: when I try to use it from visual studio, I get the error "A reference to CompiledWebAssembly.dll could not be added. Please make sure that the file is accessible, and that it is a valid assembly or COM component." As per https://stackoverflow.com/a/15832691, I ran Some googling later, and I found https://stackoverflow.com/a/13948668 which confirms that the .dll is expected to have a DllRegisterServer method. Tomorrow I'll try generating an empty DllRegisterServer method, to see if that helps. |
Not much progress. I discovered that besides regsvr32 there's a "regasm" program which also might register my .dll, but I'm starting to suspect that I'm down the wrong path... because I opened a random other .dll in ildasm (a MathNet.Numerics dll) and it doesn't have a DllRegisterServer method at all! And Visual Studio loads it just fine... Their dll does however have a bunch of custom attributes in the manifest, which look like witchcraft. My next step is probably to copy all those weird custom attributes into mine and see what happens, and slowly make my dll more and more like theirs. Fingers crossed! |
Another clue! I tried loading it at runtime with Assembly.Load instead of through Visual Studio and I got the error "The module was expected to contain an assembly manifest." which hints that my manifest isn't right yet. Random question: does dotnet-webassembly make a managed .dll or a native .dll? |
@Verdagon System.Reflection.Emit, which dotnet-webassembly uses, produces managed assemblies but unlike .dlls typically compiled by the C# compiler which compile against Reference Assemblies, they compile against implementation assemblies, so they're littered with implementation details of a specific runtime and might not work on a different runtime. |
@Suchiman Interesting. What kind of changes are required to target reference assemblies? |
@RyanLamansky i don't know if that's possible, i wanted to peek at boo which uses SRE too but it seems they also have that issue boo-lang/boo#201 (comment) . If it's possible, then in places where you're using e.g. |
Before I dive into that, can either of you give me some insight/guess of the chances of success and how long it would take? Or perhaps, additional tips besides the one in the Boo thread comment? Some extra hope and guidance would be much appreciated! Also, hold my beer / check my math: what if we just accepted that the .dll wouldn't be portable? But alas, that would mean that the eventual machine that runs the .dll would crash... except... Unity has a mode where instead of shipping the .dll as part of the client, it instead uses the IL2CPP tool to convert, at build time, on this computer, from CLR to native code, so it can ship a game with only native code. In other words, if we run dotnet-webassembly on the same machine (and same framework version) that will be running IL2CPP, then maybe it's fine that the .dll isn't super universal/portable. Does that sound like it would work? |
WASM is fundamentally simple. It doesn't (yet) intrinsically support objects at all, for one thing, and it only has 4 types (int32/64, float32/64). So, any dependencies it takes on the .NET Framework or WebAssembly for .NET itself are purely for convenience. It never mattered for the JIT-oriented approach I've taken so far. Probably the easiest approach would be to make the minimal modifications to this library to make it emit a DLL, use an IL disassembler to convert it to IL text, use text processor to switch out the framework-specific bindings, and then reassemble. |
Questions for @Suchiman : Re: "in places where you're using e.g. typeof(int) and pass that Type object to SRE, that would need to be reflected from the Reference Assembly instead (somehow, it must be avoided to "resolve" the reference assembly when trying to inspect its types)."
Re: "Roslyn uses System.Reflection.Metadata which is a very low level and painful API to read and write assemblies."
Questions for @RyanLamansky : Re: "It doesn't (yet) intrinsically support objects at all, for one thing, and it only has 4 types (int32/64, float32/64)."
Re: "switch out the framework-specific bindings"
Thanks, to both of you! |
Primarily it's about the assembly the type is contained in. On .NET Framework, those types live in mscorlib.dll which is a huge monolith dll. On .NET Core, it lives contractually in System.Runtime.dll but practically it lives in System.Private.CoreLib.dll and System.Runtime.dll type forwards this to the latter. This is important, because in IL, each type reference is prefixed with the assembly the type is contained within, e.g.
The primary goal of that API was to allow runtime code generation, it wasn't strictly designed for compilers. Roslyn had a System.Reflection.Emit codegen backend in its early prototype releases, but they've removed that before open sourcing because its limitations don't allow to implement all of C#'s features.
That depends on what you're trying to achieve, e.g. for runtime codegen only its fine, Saving assemblies isn't supported on Core (because of the complications around implementation details) and is more considered a caching mechanismus than a distribution mechanismus. Alternate choices also include Mono.Cecil which is popular.
That i don't know how roslyn does it, System.Reflection.Metadata provides access to the metadata tables in .NET Assemblies but the IL instruction stream is stored as a blob in these tables. |
The 1.0 spec of WebAssembly's memory is accessed using unsigned 32-bit offsets from a base of 0. This gives an effective max raw memory of 4 GiB. You can theoretically go far beyond that using global variables--the spec gives you room for 2^32 of those, so that's another 32 GiB if they were all int64/float64. That's not reachable in practice: V8 has a hard-coded limit of 1 million globals and 2 GiB of memory and although WebAssembly for .NET doesn't have any hard-coded limits, you'll hit CLR limits somewhere between V8 and the theoretical maximum. Okay, onto the hard technical stuff: I can think of a few definite issues right off the top of my head, though. If the WASM uses any not-in-CIL math instructions, (like I think that can be worked around by post-processing the generated DLL with an IL disassembly+text replace+reassemble. You can write C# apps to generate .NET Standard DLLs and decompile those if you're not sure what something's supposed to look like, which would avoid the need to try to reverse engineer Rosyln. You won't be able to work around the fact that Longer term, I'm thinking I should reshuffle my priorities to do more to make AoT possible, since that's where the greatest interest in WASM on .NET has been forever and spec compliance is in an "okay" state right now... |
Doing text processing and refactoring the whole project to use System.Reflection.Metadata or Cecil or an entire text processing stage is a bit outside of my time constraints =( but I'm still attempting to make a DLL that can at least be used on this machine, to accomplish the goal. If I can do that, and show that Unity can use new languages, maybe some enterprising individual with more .NET expertise can make it portable enough to work on more than the IL2CPP target. Thank you for answering all those questions! It all makes sense. Two questions remain:
|
Just wanted to check in here - did you ever get this working? |
Alas, never did! It will take some CLR and DLL knowledge to really figure out how to make this work. |
Can https://github.com/ericsink/wasm2cil help here? |
@Verdagon I was also wondering last week if something like wasmer would help here? Basically embed wasmer in the unity sln and use that to load wasm stuff? |
Unity might finally be moving on from .NET framework to .NET Core, judging by this thread: https://forum.unity.com/threads/net-6-support.1059992/ ...in which case, we might be able to use dotnet-webassembly inside Unity! |
@Verdagon I haven't tried yet, but it seems like Wasmer might work in unity with the C# bindings. The bindings might be out of date, the Wasmer project is still active but the C# bindings were written 2 years ago. Latest runtimes can be found here: https://github.com/wasmerio/wasmer/releases |
Did a quick test using wasmer 0.7.0 (howto) and assemblyscript and that worked in 2020.3.25 |
Any chance you could upload your test somewhere? Haven't a working Unity project as a starting point would save me a ton of time. |
Oh I'm afraid I don't have the project anymore and don't seem to have made a repo for it (search is running through my local git folder tho - I'll upload it here if it happens to still exist) |
I managed to get wasmer, wasmtime, and extism to all work in unity. So far, I got i to work for the editor and windows standalone (as i have a windows box currently) but I am willing to bet it will work on linux (once I put the linux runtimes back into the project). You can check it out here. |
Do you know if we can use dotnet-webassembly to run wasm from inside Unity? That would be a gamechanger!
(I'd be happy to spend a couple days testing it out if it's not known already!)
The text was updated successfully, but these errors were encountered: