2023-07-21 16:46:56 +00:00
|
|
|
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) {
|
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
|
|
|
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>(),
|
|
|
|
));
|
2023-07-21 16:46:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|