-
Notifications
You must be signed in to change notification settings - Fork 108
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
WIP: Wasm bindings #394
base: main
Are you sure you want to change the base?
WIP: Wasm bindings #394
Conversation
Hmm... I'm having trouble with the Send + Sync bounds on measure functions. The JS function handle I get from isn't |
Thoughts on deno bindings aswell? should be easy to do. |
I'm very happy to include them so long as:
My understanding is that this shouldn't require any extra Rust code, and it's just the packaging that would differ. So it seems like those requirements should be easy to meet. I also already want to do somewhat custom packaging so as to make single package that works with both node and web. So this could likely fit quite neatly into that. |
sounds good |
8fc5f58
to
fa32ac1
Compare
Hi , i try to create 10000 nodes on browser and it takes 600 ms , i think it is too slow . Yoga wasm takes only 43ms for same task. |
Is that using this PR? How are you compiling/running it? Perhaps we ought to expose |
fa32ac1
to
4fe585d
Compare
yes , i use this pr, i try Taffy::with_capacity too, but the result is the same. |
Ok, this will need some investigation. If you have a minimal test script that reproduces the issue that would be helpful. |
const allocator = new Allocator(11000);
const container = new Node(allocator, {
position: Position.Absolute,
width: 500,
height: 500,
justifyContent: AlignContent.FlexStart,
// gapWidth: 50
});
container.setStyle({flexDirection: FlexDirection.Row})
console.time('create1')
const arr = [];
for (let i = 0; i < 10000; i++) {
const child0 = new Node(allocator, {
flexGrow:1,
height: "100%"
});
container.addChild(
child0,
);
arr.push(child0);
}
console.timeEnd('create1')
const layout = container.computeLayout({}); result: create1: 556.701171875 ms |
#[wasm_bindgen]
impl Allocator {
#[wasm_bindgen(constructor)]
pub fn new(size: usize) -> Self {
Self { taffy: Rc::new(RefCell::new(taffy::Taffy::with_capacity(size))) }
}
} |
i try to create 10000 nodes from rust side but it has the same result . |
@gigasource Thanks! Sorry to keep bothering you, but do you also have:
? |
import {Node, Allocator, Position, AlignContent, Layout, FlexDirection} from '@/shared/taffy/taffy_layout'; and i need to setup vite: const wasm = require("vite-plugin-wasm").default;
\\...
plugins: [
wasm()
] |
for compile i use wasm-pack build --release in your bindings/wasm |
import initYoga, { FLEX_DIRECTION_ROW } from "yoga-wasm-web";
import wasmUrl from 'yoga-wasm-web/dist/yoga.wasm?url';
const Yoga = await initYoga(
await fetch(wasmUrl).then(res => res.arrayBuffer())
)
const page = Yoga.Node.create();
console.time('create nodes')
for (let i = 0; i < 10000; i++) {
const node = Yoga.Node.create();
page.insertChild(node, i);
}
console.timeEnd('create nodes') result: create nodes: 26.303955078125 ms |
i think it takes time for allocate memory because the yoga-wasm-web takes 16MB in head at beginning |
@gigasource Thanks! I'll look into this properly later, but I think you ought to set the You may well be right that it's allocations:
|
Interesting. We probably ought to test how long it takes when compiling Rust natively! |
#[wasm_bindgen]
#[derive(Clone)]
pub struct NodeHub {
}
#[wasm_bindgen]
impl NodeHub {
#[wasm_bindgen(js_name = createNodes)]
pub fn createNodes(&mut self, allocator: &Allocator, style: &JsValue) {
let mut v: Vec<taffy::node::Node> = Vec::new();
for number in (1..10000).rev() {
let node = allocator.taffy.borrow_mut().new_leaf(parse_style(&style)).unwrap();
v.push(node);
}
}
} yes, i try it without style but the same result. |
https://github.com/facebook/yoga/blob/main/yoga/Yoga.cpp#L159 |
#[wasm_bindgen(js_name = getLayout)]
pub fn get_layout(&mut self) -> Layout {
Layout::new(&self.allocator, self.node)
} and i think it is necessary for Node , so i can get directly layout from one node. |
Can you open a separate issue for that? Because that will need to be added to the base library as well as just the WASM bindings. Are the semantics of this method "the layout of this node was updated in the most recent call to |
This one seems easy enough, although we'll need to check what the performance is like. It may be that the API is designed the way that it is in order to minimise the number of times the JS <-> WASM boundary is crossed (although it may well also be the case that the cost of that has decreased significantly since the API was designed). |
Yes , correct, but if the result of getHasNewLayout false is, so i don't need to check all the sub-nodes. |
Ok, so the thing that's being slow is the Modifying the bindings to use With a warmup, timings are more consistent and:
Speaking of a warmup period. I tried using 1000 iterations for the warmup instead of 100 and both Yoga and Taffy crash if tree creation code is repeated 1000 times! Which is not great. I think they're hitting OOM errors, but I'm not sure why because the code should be clearing up after itself. In any case, I think we will need to abandon these wasm binding's current public API and create an API more like Yoga's which has a seperate setter for each style property. We could also try an API which accepted a single CSS string containing all desired styles and parsed it as CSS. But that's a lot more work, so I think we'll leave that for the time being. |
dc73ae5
to
2355399
Compare
@nicoburns The bindings are generating 2 Duplicate identifier 'childCount'.ts(2300)
All declarations of 'childCount' must have identical modifiers.ts(2687)
Subsequent property declarations must have the same type. Property 'childCount' must be of type '() => number', but here has type 'number'.ts(2717)
taffy_layout.d.ts(351, 3): 'childCount' was also declared here.
(method) Node.childCount(): number |
This was very much not intentional. Does the latest commit fix it? Out of interest, how are you consuming these bindings? (I'm trying to work out how this is likely to be consumed in the real world so that I can make it as easy as possible) |
Yes, it's fixed. I also noticed an inconsistency between
Currently, I do Ideally, once this is published to npm, I'd just do |
Gotcha. One further question: in what context is that where is that
Hmm... that represents reality in that on Layout it's a local property whereas on Node it has to do work to fetch the count. But I suppose we could potentially hide that from the user in the name of consistency. |
There's also a missing type here for some reason: computeLayout(size: any): Layout; What is |
Bundler. However, since |
It's an: interface {
width: number | "min-content" | "max-content";
height: number | "min-content" | "max-content";
} I guess it will be coming through as |
@nicoburns Here are a few things I found lacking after trying out the WASM bindings:
|
Objective
Fixes #241
Context
@load1n9 has published wasm bindings for Taffy as gelatin, but:
#[wasm_bindgen]
attributesNotes
#[wasm_bindgen]
annotations the main Taffy style types (behind awasm
feature flag) where appropriate (mostly the plain enums). Thelib.rs
in the bindings crate is down to ~350 lines now, although that will need to increase slightly for grid support.FromStr
andTryFrom
impls) into the main Taffy crate as these seem generally useful (and are more easily maintained when they live next the type definitions).Style
struct from a CSS string #393, but that increased the binary size from ~200kb to ~2mb which seems unacceptable to me. So we'll need to use a different API. Luckily an API for most styles is already implemented, but we may need to implement a parser for some of the grid styles.Tasks
Allocator
type to something more friendlywasm
feature and the bindings on CIFeedback wanted