bevy/crates/bevy_render/src/gpu_component_array_buffer.rs
Robert Swain e6405bb7b4
Use GpuArrayBuffer for MeshUniform (#9254)
# Objective

- Reduce the number of rebindings to enable batching of draw commands

## Solution

- Use the new `GpuArrayBuffer` for `MeshUniform` data to store all
`MeshUniform` data in arrays within fewer bindings
- Sort opaque/alpha mask prepass, opaque/alpha mask main, and shadow
phases also by the batch per-object data binding dynamic offset to
improve performance on WebGL2.

---

## Changelog

- Changed: Per-object `MeshUniform` data is now managed by
`GpuArrayBuffer` as arrays in buffers that need to be indexed into.

## Migration Guide

Accessing the `model` member of an individual mesh object's shader
`Mesh` struct the old way where each `MeshUniform` was stored at its own
dynamic offset:
```rust
struct Vertex {
    @location(0) position: vec3<f32>,
};

fn vertex(vertex: Vertex) -> VertexOutput {
    var out: VertexOutput;
    out.clip_position = mesh_position_local_to_clip(
        mesh.model,
        vec4<f32>(vertex.position, 1.0)
    );
    return out;
}
```

The new way where one needs to index into the array of `Mesh`es for the
batch:
```rust
struct Vertex {
    @builtin(instance_index) instance_index: u32,
    @location(0) position: vec3<f32>,
};

fn vertex(vertex: Vertex) -> VertexOutput {
    var out: VertexOutput;
    out.clip_position = mesh_position_local_to_clip(
        mesh[vertex.instance_index].model,
        vec4<f32>(vertex.position, 1.0)
    );
    return out;
}
```
Note that using the instance_index is the default way to pass the
per-object index into the shader, but if you wish to do custom rendering
approaches you can pass it in however you like.

---------

Co-authored-by: robtfm <50659922+robtfm@users.noreply.github.com>
Co-authored-by: Elabajaba <Elabajaba@users.noreply.github.com>
2023-07-30 13:17:08 +00:00

60 lines
1.9 KiB
Rust

use crate::{
render_resource::{GpuArrayBuffer, GpuArrayBufferable},
renderer::{RenderDevice, RenderQueue},
Render, RenderApp, RenderSet,
};
use bevy_app::{App, Plugin};
use bevy_ecs::{
prelude::{Component, Entity},
schedule::IntoSystemConfigs,
system::{Commands, Query, Res, ResMut},
};
use std::marker::PhantomData;
/// This plugin prepares the components of the corresponding type for the GPU
/// by storing them in a [`GpuArrayBuffer`].
pub struct GpuComponentArrayBufferPlugin<C: Component + GpuArrayBufferable>(PhantomData<C>);
impl<C: Component + GpuArrayBufferable> Plugin for GpuComponentArrayBufferPlugin<C> {
fn build(&self, app: &mut App) {
if let Ok(render_app) = app.get_sub_app_mut(RenderApp) {
render_app.add_systems(
Render,
prepare_gpu_component_array_buffers::<C>.in_set(RenderSet::Prepare),
);
}
}
fn finish(&self, app: &mut App) {
if let Ok(render_app) = app.get_sub_app_mut(RenderApp) {
render_app.insert_resource(GpuArrayBuffer::<C>::new(
render_app.world.resource::<RenderDevice>(),
));
}
}
}
impl<C: Component + GpuArrayBufferable> Default for GpuComponentArrayBufferPlugin<C> {
fn default() -> Self {
Self(PhantomData::<C>)
}
}
fn prepare_gpu_component_array_buffers<C: Component + GpuArrayBufferable>(
mut commands: Commands,
render_device: Res<RenderDevice>,
render_queue: Res<RenderQueue>,
mut gpu_array_buffer: ResMut<GpuArrayBuffer<C>>,
components: Query<(Entity, &C)>,
) {
gpu_array_buffer.clear();
let entities = components
.iter()
.map(|(entity, component)| (entity, gpu_array_buffer.push(component.clone())))
.collect::<Vec<_>>();
commands.insert_or_spawn_batch(entities);
gpu_array_buffer.write_buffer(&render_device, &render_queue);
}