-
-
Notifications
You must be signed in to change notification settings - Fork 42
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
Add wasm32-unknown-unknown support #92
Conversation
I still need to remove some leftover warnings, but at this point I think this is ready to review. |
Codecov Report
@@ Coverage Diff @@
## master #92 +/- ##
===========================================
- Coverage 68.6% 53.37% -15.24%
===========================================
Files 5 7 +2
Lines 755 1036 +281
===========================================
+ Hits 518 553 +35
- Misses 237 483 +246
Continue to review full report at Codecov.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks like magic to me. We might want to ask an extra review here from somebody who knows a bit of WASM interop.
spirv_cross/Cargo.toml
Outdated
crate-type = ["cdylib", "rlib"] | ||
|
||
[features] | ||
default = ["glsl", "hlsl", "msl"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's not have anything by default
#[repr(u32)] | ||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] | ||
pub enum SourceLanguage { | ||
SourceLanguageUnknown = 0, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can it be convinced to generate better enums?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah it's probably possible to improve these. These are the same bindings generated by bindgen for bindings_native and are only used internally anyway.
The idea here is that all of the bindgen parts are the same for types. For functions in native, function bindings are generated as part of bindings_native. For functions in wasm, we implement the function bindings manually.
loop { | ||
let bytes_read = bytes.len(); | ||
let offset = &JsValue::from_f64((start_offset + bytes_read) as f64); | ||
let byte = Reflect::get(heap, offset).unwrap().as_f64().unwrap() as u8; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what is as_f64()
doing here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Reflect::get
here returns a JsValue
, and in order to read the value from JS we have to move through a f64
(https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/struct.JsValue.html#method.as_f64).
In general the real issue here is that we have to go through f64
moving through the JS layer, because JS only knows Number
. So even though we're trying to read from a Uint8Array
, I don't see a way around this for now.
Although there is a slight improvement here for indexing. I asked about a better way to index into a typed array a while ago, and it appears the improvement has been merged since I originally wrote this: rustwasm/wasm-bindgen#1147
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now comes the dumb question! If we target WASM, why do we have to go through JsValue
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the part about dynamic linking mentioned briefly in my comment. I'm not sure how to dynamically two link wasm modules and make them use the same memory space. So without having a way to dynamically link, we have to copy things back and forth between wasm modules, and move everything through JS to accomplish it instead.
I searched around for resources about this, but I couldn't find much except some C++ conventions to achieve dynamic linking between two C++ modules with some help from Emscripten. But it would be amazing if we could merge the wasm modules together somehow to avoid the JS layer: it would mean we can drop the majority of the code in this PR and have one code path for both native/wasm.
I don't know if host bindings helps will help with this and unfortunately I couldn't find much about the plans for this, except some wasm design documents describing how it might eventually be accomplished.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here's a reference document so we can follow the future progress of wasm dynamic linking: https://github.com/WebAssembly/tool-conventions/blob/master/DynamicLinking.md
|
||
/// Clones all bytes from the heap into a `Vec<u8>` while `should_continue` returns `true`. | ||
/// Optionally include the last byte (i.e. to support peeking the final byte for nul-terminated strings). | ||
pub unsafe fn read_bytes_into_vec_while<F>(&self, pointer: Pointer, should_continue: F, include_last_byte: bool) -> Vec<u8> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can this be shaped as an iterator?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think so, although it's only used in a couple places internally. I think one of them would require a collect
anyway, so it might be better to leave it as a future optimization for now.
Thanks for the review! I'm happy to explain any of the magic parts to hopefully make it less magical. Unfortunately there is quite a bit going on here. I think the Rust <-> JS interop is pretty reasonable on its own expect for things like moving through To summarize the main ideas:
|
I'm going to temporarily disable the Android build until Rust 2018 is supported by android-rs-glue (rust-mobile/android-rs-glue#202). |
I'll follow-up with more tests after. At least the wasm build is working with gfx on wasm now so I'll continue testing there, and try to figure out the best way to share assets (the JS/wasm files here). bors r=kvark |
92: Add wasm32-unknown-unknown support r=kvark a=grovesNL This PR adds wasm32-unknown-unknown support for GLSL/ESSL compilation. Other compilers could easily be added to the Emscripten bundle, but it only includes the GLSL compiler for now. This works by manually writing function bindings to redirect native Rust -> C++ calls to become Rust/wasm32-unknown-unknown -> C++/Emscripten calls. Rust and C++ have separate wasm memory, so we have to do some copying between both of these in order to pass arguments back and forth. There is some more information in gfx-rs/gfx#2554 There are a few parts to this PR: - A crate to generate an Emscripten bundle from the C wrapper of SPIRV-Cross - The generated Emscripten bundle which contains everything needed for compiling SPIR-V to GLSL/ESSL - This bundle could easily include MSL and HLSL compilation too, but at the moment it's not clear how best to support all combinations (other than providing one large bundle), and we ideally don't want to require Emscripten binaries to be installed as a dependency. So for now to keep it straightforward we'll just keep the bundle limited to GLSL/ESSL, unless somebody needs HLSL/MSL support in wasm. - Some Emscripten and pointer utilities to unify access to pointers (to make Emscripten heap offsets look like pointers) so we can avoid changing the API. I think these could still be improved a lot, especially because it becomes confusing when accessing the wrong heap (Rust vs. Emscripten) but this seems to work ok for now. There is a bit of copying that could probably be made more efficient. I've been testing this so far through an example so I can run native and wasm side-by-side to check the output of reflection/compilation: ![image](https://user-images.githubusercontent.com/2113872/53678099-aa73c780-3c77-11e9-984f-749dcb42d2ec.png) In the future I want to expand the native test cases to improve coverage (there could still be a few calls omitted from my example) and figure out the best approach to run these tests in wasm too. Co-authored-by: Joshua Groves <[email protected]>
Build failed |
bors retry |
92: Add wasm32-unknown-unknown support r=kvark a=grovesNL This PR adds wasm32-unknown-unknown support for GLSL/ESSL compilation. Other compilers could easily be added to the Emscripten bundle, but it only includes the GLSL compiler for now. This works by manually writing function bindings to redirect native Rust -> C++ calls to become Rust/wasm32-unknown-unknown -> C++/Emscripten calls. Rust and C++ have separate wasm memory, so we have to do some copying between both of these in order to pass arguments back and forth. There is some more information in gfx-rs/gfx#2554 There are a few parts to this PR: - A crate to generate an Emscripten bundle from the C wrapper of SPIRV-Cross - The generated Emscripten bundle which contains everything needed for compiling SPIR-V to GLSL/ESSL - This bundle could easily include MSL and HLSL compilation too, but at the moment it's not clear how best to support all combinations (other than providing one large bundle), and we ideally don't want to require Emscripten binaries to be installed as a dependency. So for now to keep it straightforward we'll just keep the bundle limited to GLSL/ESSL, unless somebody needs HLSL/MSL support in wasm. - Some Emscripten and pointer utilities to unify access to pointers (to make Emscripten heap offsets look like pointers) so we can avoid changing the API. I think these could still be improved a lot, especially because it becomes confusing when accessing the wrong heap (Rust vs. Emscripten) but this seems to work ok for now. There is a bit of copying that could probably be made more efficient. I've been testing this so far through an example so I can run native and wasm side-by-side to check the output of reflection/compilation: ![image](https://user-images.githubusercontent.com/2113872/53678099-aa73c780-3c77-11e9-984f-749dcb42d2ec.png) In the future I want to expand the native test cases to improve coverage (there could still be a few calls omitted from my example) and figure out the best approach to run these tests in wasm too. Co-authored-by: Joshua Groves <[email protected]>
2554: Add wasm32-unknown-unknown/WebGL support r=kvark a=grovesNL Start to make a more maintainable version of my old branch from #1900 (comment). Heavily WIP but maybe somebody wants to start giving some feedback as I work through the remainder of issues. This PR replaces gl_generator with glow (https://github.com/grovesNL/glow), a experimental crate meant to unify WebGL/OpenGL types and function signatures. Currently unsupported functions will just panic, but I think it makes sense to move some of the version logic and extension fallbacks in there too (removing them from the gl backend). The WebGL types are now just keys (and `Copy`) which fixes some of the issues I mentioned previously. This is for the wasm32-unknown-unknown target and uses web-sys. TODO: - [X] ~~(major) spirv_cross wrapper on wasm32-unknown-unknown and C++ library through emscripten. For now the quad shaders are hardcoded.~~ See grovesNL/spirv_cross#92. (I'll probably leave the PR open until we work out how the integration between wasm bundles should work) - [X] ~~Consolidate render loop somehow (see WIP in glow example https://github.com/grovesNL/glow/blob/master/examples/hello/src/main.rs#L118). I need some more ideas on how to work around the `'static` bounds for wasm32 here. The basic idea is that wasm32 has to use callbacks and we use spinloops on the native target, so we would ideally like to unify these (at least for use in examples but also simple applications). For wasm32 with the quad example I just render once and quit instead.~~ Currently winit is working through the implementation of "Event Loop 2.0" and there's been [some effort to write a stdweb backend for this](rust-windowing/winit#797), which we could later help port to wasm32-unknown-unknown. I think for now we can just keep the wasm render loop path separate in gfx examples, and things will work naturally once winit wasm32-unknown-unknown support is ready. - [X] ~~Remove all remaining `#[cfg(not(target_arch = "wasm32"))]` wherever possible to make this more maintainable.~~ There are a few places remaining that can be removed when we move some logic into glow, like version parsing and extension checking. Some other parts like the runloop can't be addressed without winit support for wasm32-unknown-unknown or other workarounds in quad. - [X] ~~Decide where to put things like `index.html` for examples (I've excluded it for now) and guide about how to create wasm bundle with wasm-bindgen.~~ - [X] ~~Consider how logging should work – I don't see how to redirect stdout to `console.log` so it's a bit manually at the moment. It would be really cool if things like `info!` just work automatically with wasm32-unknown-unknown.~~ We should be able to use https://github.com/iamcodemaker/console_log for the wasm backend without any major challenges. - [X] ~~Publish glow~~ Published 0.2.0 - [x] ~~Publish updated spirv_cross~~ Published 0.14.0 - [X] ~~Add wasm-bindgen to CI~~ This has been added <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/gfx-rs/gfx/2554) <!-- Reviewable:end --> Co-authored-by: Joshua Groves <[email protected]>
This PR adds wasm32-unknown-unknown support for GLSL/ESSL compilation. Other compilers could easily be added to the Emscripten bundle, but it only includes the GLSL compiler for now.
This works by manually writing function bindings to redirect native Rust -> C++ calls to become Rust/wasm32-unknown-unknown -> C++/Emscripten calls. Rust and C++ have separate wasm memory, so we have to do some copying between both of these in order to pass arguments back and forth. There is some more information in gfx-rs/gfx#2554
There are a few parts to this PR:
I've been testing this so far through an example so I can run native and wasm side-by-side to check the output of reflection/compilation:
In the future I want to expand the native test cases to improve coverage (there could still be a few calls omitted from my example) and figure out the best approach to run these tests in wasm too.