Skip to content
This repository has been archived by the owner on Jan 29, 2025. It is now read-only.

Add initial wasm build #55

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,46 @@ repository = "https://github.com/gfx-rs/naga"
keywords = ["shader", "SPIR-V"]
license = "MPL-2.0"

[lib]
crate-type = ["cdylib", "rlib"]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately that would make the cdylib target to be generated for every build of naga as a dependency, even of other Rust code. See rust-lang/cargo#4280 .

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm, not sure how to solve that, wasm-pack told me this is needed for being able to compile to wasm 😟


[dependencies]
bitflags = "1"
fxhash = "0.2"
log = "0.4"
num-traits = "0.2"
spirv = { package = "spirv_headers", version = "1" }

[target.'cfg(target_arch = "wasm32")'.dependencies]
wasm-bindgen = "0.2"
pjoe marked this conversation as resolved.
Show resolved Hide resolved
# The `console_error_panic_hook` crate provides better debugging of panics by
# logging them with `console.error`. This is great for development, but requires
# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
# code size when deploying.
console_error_panic_hook = "0.1.1"
# `wee_alloc` is a tiny allocator for wasm that is only ~1K in code size
# compared to the default allocator's ~10K. It is slower than the default
# allocator, however.
#
# Unfortunately, `wee_alloc` requires nightly Rust when targeting wasm for now.
wee_alloc = "0.4.2"

[dev-dependencies]
env_logger = "0.6"
ron = "0.5"
serde = { version = "1", features = ["serde_derive"] }

[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
wasm-bindgen-test = "0.2"

[profile.release]
lto = true
opt-level = 's'

[package.metadata.wasm-pack.profile.release]
wasm-opt = ["-Os"]

[package.metadata.wasm-pack.profile.release.wasm-bindgen]
debug-js-glue = false
demangle-name-section = true
dwarf-debug-info = false
55 changes: 37 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,40 @@ This is an experimental shader translation library for the needs of gfx-rs proje

## Supported end-points

Front-end | Status | Notes |
--------------- | ------------------ | ----- |
SPIR-V (binary) | :construction: | |
WGSL (Tint) | :construction: | |
GLSL (Vulkan) | | |
Rust | | |

Back-end | Status | Notes |
--------------- | ------------------ | ----- |
SPIR-V (binary) | | |
WGSL | | |
Metal | :construction: | |
HLSL | | |
GLSL | | |
AIR | | |
DXIR | | |
DXIL | | |
DXBC | | |
| Front-end | Status | Notes |
| --------------- | -------------- | ----- |
| SPIR-V (binary) | :construction: | |
| WGSL (Tint) | :construction: | |
| GLSL (Vulkan) | | |
| Rust | | |

| Back-end | Status | Notes |
| --------------- | -------------- | ----- |
| SPIR-V (binary) | | |
| WGSL | | |
| Metal | :construction: | |
| HLSL | | |
| GLSL | | |
| AIR | | |
| DXIR | | |
| DXIL | | |
| DXBC | | |

## WASM

You need [wasm-pack](https://rustwasm.github.io/wasm-pack/)

Building (for nodejs):

```
wasm-pack build --release --target nodejs
```

Output is in `pkg` and can be usde like this:

```js
const naga = require("./pkg/naga");
const fs = require("fs");
const input = fs.readFileSync("test-data/quad.wgsl", "utf8");
naga.wgsl2msl(input);
```
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ mod arena;
pub mod back;
pub mod front;
pub mod proc;
pub mod wasm;

use crate::arena::{Arena, Handle};

Expand Down
41 changes: 41 additions & 0 deletions src/wasm.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#[cfg(target_arch = "wasm32")]
use wasm_bindgen::prelude::*;

// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
// allocator.
#[cfg(feature = "wee_alloc")]
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;

pub fn set_panic_hook() {
// When the `console_error_panic_hook` feature is enabled, we can call the
// `set_panic_hook` function at least once during initialization, and then
// we will get better error messages if our code ever panics.
//
// For more details see
// https://github.com/rustwasm/console_error_panic_hook#readme
#[cfg(feature = "console_error_panic_hook")]
console_error_panic_hook::set_once();
}

#[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
pub fn wgsl2msl(input: &str) -> String {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens when we add any functions to this module? If I understand correctly, these are public extern functions, so nothing is going to strip unused logic parts. If we add wgsl2hlsl for example, the produced WASM module would have to contain both.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah this isn't really the best solution, just something to get started. The wasm interface should also be split in front-end returning AST and backend taking AST and returning text(or bin) output. But that requires someway to pass the AST efficiently.

Maybe could use features to flag individual front/back-ends?

let module = super::front::wgsl::parse_str(&input).unwrap();

println!("Compiled, header {:?}", module.header);

use super::back::msl;
let mut binding_map = msl::BindingMap::default();
binding_map.insert(
msl::BindSource { set: 0, binding: 0 },
msl::BindTarget { buffer: None, texture: Some(1), sampler: None, mutable: false },
);
binding_map.insert(
msl::BindSource { set: 0, binding: 1 },
msl::BindTarget { buffer: None, texture: None, sampler: Some(1), mutable: false },
);
let options = msl::Options {
binding_map: &binding_map,
};
msl::write_string(&module, options).unwrap()
}