bevy/crates/bevy_pbr/src/render/mesh.wgsl
Patrick Walton 3188e5af61
Batch skinned meshes on platforms where storage buffers are available. (#16599)
This commit makes skinned meshes batchable on platforms other than WebGL
2. On supported platforms, it replaces the two uniform buffers used for
joint matrices with a pair of storage buffers containing all matrices
for all skinned meshes packed together. The indices into the buffer are
stored in the mesh uniform and mesh input uniform. The GPU mesh
preprocessing step copies the indices in if that step is enabled.

On the `many_foxes` demo, I observed a frame time decrease from 15.470ms
to 11.935ms. This is the result of reducing the `submit_graph_commands`
time from an average of 5.45ms to 0.489ms, an 11x speedup in that
portion of rendering.

![Screenshot 2024-12-01
192838](https://github.com/user-attachments/assets/7d2db997-8939-466e-8b9e-050d4a6a78ee)

This is what the profile looks like for `many_foxes` after these
changes.

![Screenshot 2024-12-01
193026](https://github.com/user-attachments/assets/68983fc3-01b8-41fd-835e-3d93cb65d0fa)

---------

Co-authored-by: François Mockers <mockersf@gmail.com>
2024-12-10 17:50:03 +00:00

121 lines
3.5 KiB
WebGPU Shading Language

#import bevy_pbr::{
mesh_bindings::mesh,
mesh_functions,
skinning,
morph::morph,
forward_io::{Vertex, VertexOutput},
view_transformations::position_world_to_clip,
}
#ifdef MORPH_TARGETS
fn morph_vertex(vertex_in: Vertex) -> Vertex {
var vertex = vertex_in;
let first_vertex = mesh[vertex.instance_index].first_vertex_index;
let vertex_index = vertex.index - first_vertex;
let weight_count = bevy_pbr::morph::layer_count();
for (var i: u32 = 0u; i < weight_count; i ++) {
let weight = bevy_pbr::morph::weight_at(i);
if weight == 0.0 {
continue;
}
vertex.position += weight * morph(vertex_index, bevy_pbr::morph::position_offset, i);
#ifdef VERTEX_NORMALS
vertex.normal += weight * morph(vertex_index, bevy_pbr::morph::normal_offset, i);
#endif
#ifdef VERTEX_TANGENTS
vertex.tangent += vec4(weight * morph(vertex_index, bevy_pbr::morph::tangent_offset, i), 0.0);
#endif
}
return vertex;
}
#endif
@vertex
fn vertex(vertex_no_morph: Vertex) -> VertexOutput {
var out: VertexOutput;
#ifdef MORPH_TARGETS
var vertex = morph_vertex(vertex_no_morph);
#else
var vertex = vertex_no_morph;
#endif
let mesh_world_from_local = mesh_functions::get_world_from_local(vertex_no_morph.instance_index);
#ifdef SKINNED
var world_from_local = skinning::skin_model(
vertex.joint_indices,
vertex.joint_weights,
vertex_no_morph.instance_index
);
#else
// Use vertex_no_morph.instance_index instead of vertex.instance_index to work around a wgpu dx12 bug.
// See https://github.com/gfx-rs/naga/issues/2416 .
var world_from_local = mesh_world_from_local;
#endif
#ifdef VERTEX_NORMALS
#ifdef SKINNED
out.world_normal = skinning::skin_normals(world_from_local, vertex.normal);
#else
out.world_normal = mesh_functions::mesh_normal_local_to_world(
vertex.normal,
// Use vertex_no_morph.instance_index instead of vertex.instance_index to work around a wgpu dx12 bug.
// See https://github.com/gfx-rs/naga/issues/2416
vertex_no_morph.instance_index
);
#endif
#endif
#ifdef VERTEX_POSITIONS
out.world_position = mesh_functions::mesh_position_local_to_world(world_from_local, vec4<f32>(vertex.position, 1.0));
out.position = position_world_to_clip(out.world_position.xyz);
#endif
#ifdef VERTEX_UVS_A
out.uv = vertex.uv;
#endif
#ifdef VERTEX_UVS_B
out.uv_b = vertex.uv_b;
#endif
#ifdef VERTEX_TANGENTS
out.world_tangent = mesh_functions::mesh_tangent_local_to_world(
world_from_local,
vertex.tangent,
// Use vertex_no_morph.instance_index instead of vertex.instance_index to work around a wgpu dx12 bug.
// See https://github.com/gfx-rs/naga/issues/2416
vertex_no_morph.instance_index
);
#endif
#ifdef VERTEX_COLORS
out.color = vertex.color;
#endif
#ifdef VERTEX_OUTPUT_INSTANCE_INDEX
// Use vertex_no_morph.instance_index instead of vertex.instance_index to work around a wgpu dx12 bug.
// See https://github.com/gfx-rs/naga/issues/2416
out.instance_index = vertex_no_morph.instance_index;
#endif
#ifdef VISIBILITY_RANGE_DITHER
out.visibility_range_dither = mesh_functions::get_visibility_range_dither_level(
vertex_no_morph.instance_index, mesh_world_from_local[3]);
#endif
return out;
}
@fragment
fn fragment(
mesh: VertexOutput,
) -> @location(0) vec4<f32> {
#ifdef VERTEX_COLORS
return mesh.color;
#else
return vec4<f32>(1.0, 0.0, 1.0, 1.0);
#endif
}