diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index 1c88cc9988..bf3b181913 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -172,11 +172,6 @@ pub fn extract_meshes( commands.insert_or_spawn_batch(not_caster_commands); } -#[derive(Resource, Debug, Default)] -pub struct ExtractedJoints { - pub buffer: Vec, -} - #[derive(Component)] pub struct SkinnedMeshJoints { pub index: u32, @@ -188,19 +183,22 @@ impl SkinnedMeshJoints { skin: &SkinnedMesh, inverse_bindposes: &Assets, joints: &Query<&GlobalTransform>, - buffer: &mut Vec, + buffer: &mut BufferVec, ) -> Option { let inverse_bindposes = inverse_bindposes.get(&skin.inverse_bindposes)?; - let bindposes = inverse_bindposes.iter(); - let skin_joints = skin.joints.iter(); let start = buffer.len(); - for (inverse_bindpose, joint) in bindposes.zip(skin_joints).take(MAX_JOINTS) { - if let Ok(joint) = joints.get(*joint) { - buffer.push(joint.affine() * *inverse_bindpose); - } else { - buffer.truncate(start); - return None; - } + let target = start + skin.joints.len().min(MAX_JOINTS); + buffer.extend( + joints + .iter_many(&skin.joints) + .zip(inverse_bindposes.iter()) + .map(|(joint, bindpose)| joint.affine() * *bindpose), + ); + // iter_many will skip any failed fetches. This will cause it to assign the wrong bones, + // so just bail by truncating to the start. + if buffer.len() != target { + buffer.truncate(start); + return None; } // Pad to 256 byte alignment @@ -221,13 +219,13 @@ impl SkinnedMeshJoints { pub fn extract_skinned_meshes( mut commands: Commands, mut previous_len: Local, - mut previous_joint_len: Local, + mut uniform: ResMut, query: Extract>, inverse_bindposes: Extract>>, joint_query: Extract>, ) { + uniform.buffer.clear(); let mut values = Vec::with_capacity(*previous_len); - let mut joints = Vec::with_capacity(*previous_joint_len); let mut last_start = 0; for (entity, computed_visibility, skin) in &query { @@ -236,7 +234,7 @@ pub fn extract_skinned_meshes( } // PERF: This can be expensive, can we move this to prepare? if let Some(skinned_joints) = - SkinnedMeshJoints::build(skin, &inverse_bindposes, &joint_query, &mut joints) + SkinnedMeshJoints::build(skin, &inverse_bindposes, &joint_query, &mut uniform.buffer) { last_start = last_start.max(skinned_joints.index as usize); values.push((entity, skinned_joints.to_buffer_index())); @@ -244,13 +242,11 @@ pub fn extract_skinned_meshes( } // Pad out the buffer to ensure that there's enough space for bindings - while joints.len() - last_start < MAX_JOINTS { - joints.push(Mat4::ZERO); + while uniform.buffer.len() - last_start < MAX_JOINTS { + uniform.buffer.push(Mat4::ZERO); } *previous_len = values.len(); - *previous_joint_len = joints.len(); - commands.insert_resource(ExtractedJoints { buffer: joints }); commands.insert_or_spawn_batch(values); } @@ -779,20 +775,14 @@ impl Default for SkinnedMeshUniform { pub fn prepare_skinned_meshes( render_device: Res, render_queue: Res, - extracted_joints: Res, mut skinned_mesh_uniform: ResMut, ) { - if extracted_joints.buffer.is_empty() { + if skinned_mesh_uniform.buffer.is_empty() { return; } - skinned_mesh_uniform.buffer.clear(); - skinned_mesh_uniform - .buffer - .reserve(extracted_joints.buffer.len(), &render_device); - for joint in &extracted_joints.buffer { - skinned_mesh_uniform.buffer.push(*joint); - } + let len = skinned_mesh_uniform.buffer.len(); + skinned_mesh_uniform.buffer.reserve(len, &render_device); skinned_mesh_uniform .buffer .write_buffer(&render_device, &render_queue); diff --git a/crates/bevy_render/src/render_resource/buffer_vec.rs b/crates/bevy_render/src/render_resource/buffer_vec.rs index 1d3086796d..4a3c744e07 100644 --- a/crates/bevy_render/src/render_resource/buffer_vec.rs +++ b/crates/bevy_render/src/render_resource/buffer_vec.rs @@ -131,7 +131,18 @@ impl BufferVec { } } + pub fn truncate(&mut self, len: usize) { + self.values.truncate(len); + } + pub fn clear(&mut self) { self.values.clear(); } } + +impl Extend for BufferVec { + #[inline] + fn extend>(&mut self, iter: I) { + self.values.extend(iter); + } +}