The docs show Float32Array::view() as unsafe while the webgl example does not use this function - rather it uses a combo of Float32Array::new() and Float32Array::subarray
Is there a performance tradeoff happening with this approach, that would be gained by switching to view() ?
If we are always able to get a view into Float32Array, without really allocating, I wonder if we actually gain from bufferSubData() ? (e.g. https://docs.rs/web-sys/0.3.7/web_sys/struct.WebGl2RenderingContext.html#method.buffer_sub_data_with_i32_and_array_buffer)
Is there a performance tradeoff happening with this approach, that would be gained by switching to view() ?
Ah that was just an old example which predated the existence of view, I've sent a PR to update it since using view is more idiomatic!
If we are always able to get a view into Float32Array, without really allocating, I wonder if we actually gain from bufferSubData() ? (
I think so! I believe that's a method on WebGl2RenderingContext though while the example is currently using WebGlRenderingContext (lack of 2)
Nice!
Re: optimization w/bufferSubData...
For typical JS I think the performance win is not needing to allocate new TypedArrays each time- rather one can just keep a big ArrayBuffer around and then use bufferSubData to upload from different parts of it.
Since WASM is pretty much already doing that (e.g. we get a TypedArray precisely the same way), I'm wondering if we already get that benefit for free... in fact bufferSubData might even be a very slight loss due to calculating offsets twice!
I'm new to webgl2 though and it could be I'm just missing a totally different use case
Oh no you're right about the perf optimization, I think it's just that webgl doesn't have support for that but webgl2 does.
Closing with the example update in https://github.com/rustwasm/wasm-bindgen/pull/1618
@dakom (Little late, but...)
The reason for bufferSubData in WebGL2 isn't to avoid copying.
When you call view in Rust, it doesn't need to copy the memory, but it still has to allocate a new JS Float32Array object, and then use that object, and then garbage collect it.
When the bufferSubData method is called a lot, this extra allocation and garbage collection can have a significant cost.
So the point of bufferSubData is that it doesn't need to create a new Float32Array object.
In other words, with WebGL1 the Rust code looks like this (including the code for view):
unsafe fn view(slice: &[f32]) -> Float32Array {
let buf = wasm_bindgen::memory();
let mem = buf.unchecked_ref::<WebAssembly::Memory>();
// Allocates a new Float32Array object in JS
Float32Array::new_with_byte_offset_and_length(
&mem.buffer(),
slice.as_ptr() as u32,
slice.len() as u32,
)
}
gl.buffer_sub_data_with_i32_and_array_buffer_view(
target,
offset,
&unsafe { view(&slice) },
);
But with WebGL2 it would look like this:
let buf = wasm_bindgen::memory();
let mem = buf.unchecked_ref::<WebAssembly::Memory>();
gl.buffer_sub_data_with_i32_and_array_buffer_view(
target,
offset,
&mem.buffer(),
slice.as_ptr() as u32,
slice.len() as u32,
);
Note the lack of needing to create a new Float32Array object, it can just index into the wasm memory directly.
Most helpful comment
@dakom (Little late, but...)
The reason for
bufferSubDatain WebGL2 isn't to avoid copying.When you call
viewin Rust, it doesn't need to copy the memory, but it still has to allocate a new JSFloat32Arrayobject, and then use that object, and then garbage collect it.When the
bufferSubDatamethod is called a lot, this extra allocation and garbage collection can have a significant cost.So the point of
bufferSubDatais that it doesn't need to create a newFloat32Arrayobject.In other words, with WebGL1 the Rust code looks like this (including the code for
view):But with WebGL2 it would look like this:
Note the lack of needing to create a new
Float32Arrayobject, it can just index into the wasm memory directly.