From 272e02992d2380001826febc5780a8ea23b01965 Mon Sep 17 00:00:00 2001
From: james7132 <contact@jamessliu.com>
Date: Fri, 2 Dec 2022 22:06:22 -0800
Subject: [PATCH 1/5] Directly extract skinned mesh joints into the BufferVec

---
 crates/bevy_pbr/src/render/mesh.rs            | 31 ++++++-------------
 .../src/render_resource/buffer_vec.rs         |  4 +++
 2 files changed, 13 insertions(+), 22 deletions(-)

diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs
index 15fa70b81e8df..7892697c36e31 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<Mat4>,
-}
-
 #[derive(Component)]
 pub struct SkinnedMeshJoints {
     pub index: u32,
@@ -188,7 +183,7 @@ impl SkinnedMeshJoints {
         skin: &SkinnedMesh,
         inverse_bindposes: &Assets<SkinnedMeshInverseBindposes>,
         joints: &Query<&GlobalTransform>,
-        buffer: &mut Vec<Mat4>,
+        buffer: &mut BufferVec<Mat4>,
     ) -> Option<Self> {
         let inverse_bindposes = inverse_bindposes.get(&skin.inverse_bindposes)?;
         let bindposes = inverse_bindposes.iter();
@@ -221,13 +216,13 @@ impl SkinnedMeshJoints {
 pub fn extract_skinned_meshes(
     mut commands: Commands,
     mut previous_len: Local<usize>,
-    mut previous_joint_len: Local<usize>,
+    mut uniform: ResMut<SkinnedMeshUniform>,
     query: Extract<Query<(Entity, &ComputedVisibility, &SkinnedMesh)>>,
     inverse_bindposes: Extract<Res<Assets<SkinnedMeshInverseBindposes>>>,
     joint_query: Extract<Query<&GlobalTransform>>,
 ) {
+    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 +231,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 +239,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 +772,14 @@ impl Default for SkinnedMeshUniform {
 pub fn prepare_skinned_meshes(
     render_device: Res<RenderDevice>,
     render_queue: Res<RenderQueue>,
-    extracted_joints: Res<ExtractedJoints>,
     mut skinned_mesh_uniform: ResMut<SkinnedMeshUniform>,
 ) {
-    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 1d3086796dbc5..d920afbf397dd 100644
--- a/crates/bevy_render/src/render_resource/buffer_vec.rs
+++ b/crates/bevy_render/src/render_resource/buffer_vec.rs
@@ -131,6 +131,10 @@ impl<T: Pod> BufferVec<T> {
         }
     }
 
+    pub fn truncate(&mut self, len: usize) {
+        self.values.truncate(len);
+    }
+
     pub fn clear(&mut self) {
         self.values.clear();
     }

From f07dccb43b3c4f958633dddd1e5d8ffbd8807a2a Mon Sep 17 00:00:00 2001
From: james7132 <contact@jamessliu.com>
Date: Thu, 8 Dec 2022 21:48:35 -0800
Subject: [PATCH 2/5] Use extend instead of push

---
 crates/bevy_pbr/src/render/mesh.rs            | 19 ++++++++++---------
 .../src/render_resource/buffer_vec.rs         |  7 +++++++
 2 files changed, 17 insertions(+), 9 deletions(-)

diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs
index 5efb3fd29329f..0c09d8c040fc4 100644
--- a/crates/bevy_pbr/src/render/mesh.rs
+++ b/crates/bevy_pbr/src/render/mesh.rs
@@ -186,16 +186,17 @@ impl SkinnedMeshJoints {
         buffer: &mut BufferVec<Mat4>,
     ) -> Option<Self> {
         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),
+        );
+        if buffer.len() != target {
+            buffer.truncate(start);
+            return None;
         }
 
         // Pad to 256 byte alignment
diff --git a/crates/bevy_render/src/render_resource/buffer_vec.rs b/crates/bevy_render/src/render_resource/buffer_vec.rs
index d920afbf397dd..4684e65b27b4d 100644
--- a/crates/bevy_render/src/render_resource/buffer_vec.rs
+++ b/crates/bevy_render/src/render_resource/buffer_vec.rs
@@ -139,3 +139,10 @@ impl<T: Pod> BufferVec<T> {
         self.values.clear();
     }
 }
+
+impl<T: Pod> Extend<T> for BufferVec<T> {
+    #[inline]
+    fn extend<I: IntoIterator<Item=T>>(&mut self, iter: I) {
+        self.values.extend(iter)
+    }
+}

From 935fe6c397df2eb9461227a6617e3847bf20688b Mon Sep 17 00:00:00 2001
From: james7132 <contact@jamessliu.com>
Date: Sat, 10 Dec 2022 22:32:02 -0800
Subject: [PATCH 3/5] Formatting

---
 crates/bevy_render/src/render_resource/buffer_vec.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/crates/bevy_render/src/render_resource/buffer_vec.rs b/crates/bevy_render/src/render_resource/buffer_vec.rs
index 4684e65b27b4d..72f1b08a9b503 100644
--- a/crates/bevy_render/src/render_resource/buffer_vec.rs
+++ b/crates/bevy_render/src/render_resource/buffer_vec.rs
@@ -142,7 +142,7 @@ impl<T: Pod> BufferVec<T> {
 
 impl<T: Pod> Extend<T> for BufferVec<T> {
     #[inline]
-    fn extend<I: IntoIterator<Item=T>>(&mut self, iter: I) {
+    fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
         self.values.extend(iter)
     }
 }

From 4c203b3ccb92b4d0fc4af58c5130cddf2bddae5c Mon Sep 17 00:00:00 2001
From: james7132 <contact@jamessliu.com>
Date: Sun, 11 Dec 2022 23:09:31 -0800
Subject: [PATCH 4/5] Explain why we're truncating.

---
 crates/bevy_pbr/src/render/mesh.rs | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs
index 0c09d8c040fc4..bf3b181913a70 100644
--- a/crates/bevy_pbr/src/render/mesh.rs
+++ b/crates/bevy_pbr/src/render/mesh.rs
@@ -194,6 +194,8 @@ impl SkinnedMeshJoints {
                 .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;

From 92b13ef19a0e9e9896aab4e3ee1b368c987ce436 Mon Sep 17 00:00:00 2001
From: james7132 <contact@jamessliu.com>
Date: Sun, 11 Dec 2022 23:12:22 -0800
Subject: [PATCH 5/5] Fix CI

---
 crates/bevy_render/src/render_resource/buffer_vec.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/crates/bevy_render/src/render_resource/buffer_vec.rs b/crates/bevy_render/src/render_resource/buffer_vec.rs
index 72f1b08a9b503..4a3c744e071c9 100644
--- a/crates/bevy_render/src/render_resource/buffer_vec.rs
+++ b/crates/bevy_render/src/render_resource/buffer_vec.rs
@@ -143,6 +143,6 @@ impl<T: Pod> BufferVec<T> {
 impl<T: Pod> Extend<T> for BufferVec<T> {
     #[inline]
     fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
-        self.values.extend(iter)
+        self.values.extend(iter);
     }
 }