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.  This is what the profile looks like for `many_foxes` after these changes.  --------- Co-authored-by: François Mockers <mockersf@gmail.com>
121 lines
3.5 KiB
WebGPU Shading Language
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
|
|
}
|