Reduce number of copies in the Mesh API
- Dominant language
- Rust
- Stars
- 48.2k
- Forks
- 4.8k
- Avg merge
- 3d 22h
- Merged PRs (30d)
- 161
Description
## What problem does this solve or what need does it fill?
Currently, using the Mesh API incurs a lot of unnecessary memcpy. If you have a custom mesh representation, it's even worse because you have to build a Mesh copying all your data in, and then go through all the normal Mesh overhead.
Current state of Mesh copies:
1. Mesh gets extracted which may involve a clone in `extract_render_asset`
[`extracted_assets.push((id, asset.clone()));`](https://github.com/bevyengine/bevy/blob/de888a373de666ff9d1bbdf8275e7fb625584394/crates/bevy_render/src/render_asset.rs#L296)
2. then it gets prepared into a GpuMesh, which interleave-copies the vertex data into a `Vec` in `Mesh::get_vertex_buffer_data`:
[`let mut attributes_interleaved_buffer = vec![0; vertex_count * vertex_size];`](https://github.com/bevyengine/bevy/blob/de888a373de666ff9d1bbdf8275e7fb625584394/crates/bevy_render/src/mesh/mesh/mod.rs#L469)
3. the interleave copy actually happens twice, the first time just to get the length and pre-allocate a slab 😬
[`mesh.get_vertex_buffer_data().len() as u64,`](https://github.com/bevyengine/bevy/blob/de888a373de666ff9d1bbdf8275e7fb625584394/crates/bevy_render/src/mesh/allocator.rs#L430)
and then to fill in the buffer
[`let vertex_data = mesh.get_vertex_buffer_data();`](https://github.com/bevyengine/bevy/blob/de888a373de666ff9d1bbdf8275e7fb625584394/crates/bevy_render/src/mesh/allocator.rs#L477)
4. which then gets passed by reference to wgpu `create_buffer_with_data` which immediately copies it yet again in `DeviceExt::create_buffer_init`:
[`buffer.slice(..).get_mapped_range_mut()[..unpadded_size as usize].copy_from_slice(descriptor.contents);`](https://github.com/gfx-rs/wgpu/blob/6db097694ce121f9cd927ef6dbb5608a69906760/wgpu/src/util/device.rs#L95)
which then actually gets uploaded to the gpu
## What solution would you like?
To solve 1:
Retained render world will help here
To solve 2:
introduce an api which both Mesh and custom mesh representations uses, that permits writing directly into a u8 buffer thats already mapped. this can be via a builder style api potentially.
To solve 3:
a. length can be computed without copying, b. write directly into the slab instead of creating a vec and then copying
To solve 4:
`create_buffer_init()` is a convenience function. If you want to minimize copies, then call `create_buffer()` with `mapped_at_creation: true`, then write into the mapping. - kpreid
## What alternative(s) have you considered?
Not changing it
## Additional context
i mostly want to expose an api that allows alternative mesh representations to exist easily within the renderer, and make Mesh use that api.
Contributor guide
Assessment
This issue has not been assessed yet.