bevyengine / bevyengine/bevy

Reduce number of copies in the Mesh API

Open
#15,558 1 comment 0 reactions 0 assignees View on GitHub
A-Rendering C-Performance S-Ready-For-Implementation
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.