Skip to content

Commit 904e4bc

Browse files
committed
Directly extract joints into SkinnedMeshJoints (#6833)
# Objective Following #4402, extract systems run on the render world instead of the main world, and allow retained state operations on it's resources. We're currently extracting to `ExtractedJoints` and then copying it twice during Prepare. Once into `SkinnedMeshJoints` and again into the actual GPU buffer. This makes #4902 obsolete. ## Solution Cut out the middle copy and directly extract joints into `SkinnedMeshJoints` and remove `ExtractedJoints` entirely. This also removes the per-frame allocation that is being made to send `ExtractedJoints` into the render world. ## Performance On my local machine, this halves the time for `prepare_skinned _meshes` on `many_foxes` (195.75us -> 93.93us on average). ![image](https://user-images.githubusercontent.com/3137680/205427455-ab91a8a3-a6b0-4f0a-bd48-e54482c563b2.png) --- ## Changelog Added: `BufferVec::truncate` Added: `BufferVec::extend` Changed: `SkinnedMeshJoints::build` now takes a `&mut BufferVec` instead of a `&mut Vec` as a parameter. Removed: `ExtractedJoints`. ## Migration Guide `ExtractedJoints` has been removed. Read the bound bones from `SkinnedMeshJoints` instead.
1 parent b87622b commit 904e4bc

File tree

2 files changed

+32
-31
lines changed

2 files changed

+32
-31
lines changed

crates/bevy_pbr/src/render/mesh.rs

Lines changed: 21 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -172,11 +172,6 @@ pub fn extract_meshes(
172172
commands.insert_or_spawn_batch(not_caster_commands);
173173
}
174174

175-
#[derive(Resource, Debug, Default)]
176-
pub struct ExtractedJoints {
177-
pub buffer: Vec<Mat4>,
178-
}
179-
180175
#[derive(Component)]
181176
pub struct SkinnedMeshJoints {
182177
pub index: u32,
@@ -188,19 +183,22 @@ impl SkinnedMeshJoints {
188183
skin: &SkinnedMesh,
189184
inverse_bindposes: &Assets<SkinnedMeshInverseBindposes>,
190185
joints: &Query<&GlobalTransform>,
191-
buffer: &mut Vec<Mat4>,
186+
buffer: &mut BufferVec<Mat4>,
192187
) -> Option<Self> {
193188
let inverse_bindposes = inverse_bindposes.get(&skin.inverse_bindposes)?;
194-
let bindposes = inverse_bindposes.iter();
195-
let skin_joints = skin.joints.iter();
196189
let start = buffer.len();
197-
for (inverse_bindpose, joint) in bindposes.zip(skin_joints).take(MAX_JOINTS) {
198-
if let Ok(joint) = joints.get(*joint) {
199-
buffer.push(joint.affine() * *inverse_bindpose);
200-
} else {
201-
buffer.truncate(start);
202-
return None;
203-
}
190+
let target = start + skin.joints.len().min(MAX_JOINTS);
191+
buffer.extend(
192+
joints
193+
.iter_many(&skin.joints)
194+
.zip(inverse_bindposes.iter())
195+
.map(|(joint, bindpose)| joint.affine() * *bindpose),
196+
);
197+
// iter_many will skip any failed fetches. This will cause it to assign the wrong bones,
198+
// so just bail by truncating to the start.
199+
if buffer.len() != target {
200+
buffer.truncate(start);
201+
return None;
204202
}
205203

206204
// Pad to 256 byte alignment
@@ -221,13 +219,13 @@ impl SkinnedMeshJoints {
221219
pub fn extract_skinned_meshes(
222220
mut commands: Commands,
223221
mut previous_len: Local<usize>,
224-
mut previous_joint_len: Local<usize>,
222+
mut uniform: ResMut<SkinnedMeshUniform>,
225223
query: Extract<Query<(Entity, &ComputedVisibility, &SkinnedMesh)>>,
226224
inverse_bindposes: Extract<Res<Assets<SkinnedMeshInverseBindposes>>>,
227225
joint_query: Extract<Query<&GlobalTransform>>,
228226
) {
227+
uniform.buffer.clear();
229228
let mut values = Vec::with_capacity(*previous_len);
230-
let mut joints = Vec::with_capacity(*previous_joint_len);
231229
let mut last_start = 0;
232230

233231
for (entity, computed_visibility, skin) in &query {
@@ -236,21 +234,19 @@ pub fn extract_skinned_meshes(
236234
}
237235
// PERF: This can be expensive, can we move this to prepare?
238236
if let Some(skinned_joints) =
239-
SkinnedMeshJoints::build(skin, &inverse_bindposes, &joint_query, &mut joints)
237+
SkinnedMeshJoints::build(skin, &inverse_bindposes, &joint_query, &mut uniform.buffer)
240238
{
241239
last_start = last_start.max(skinned_joints.index as usize);
242240
values.push((entity, skinned_joints.to_buffer_index()));
243241
}
244242
}
245243

246244
// Pad out the buffer to ensure that there's enough space for bindings
247-
while joints.len() - last_start < MAX_JOINTS {
248-
joints.push(Mat4::ZERO);
245+
while uniform.buffer.len() - last_start < MAX_JOINTS {
246+
uniform.buffer.push(Mat4::ZERO);
249247
}
250248

251249
*previous_len = values.len();
252-
*previous_joint_len = joints.len();
253-
commands.insert_resource(ExtractedJoints { buffer: joints });
254250
commands.insert_or_spawn_batch(values);
255251
}
256252

@@ -779,20 +775,14 @@ impl Default for SkinnedMeshUniform {
779775
pub fn prepare_skinned_meshes(
780776
render_device: Res<RenderDevice>,
781777
render_queue: Res<RenderQueue>,
782-
extracted_joints: Res<ExtractedJoints>,
783778
mut skinned_mesh_uniform: ResMut<SkinnedMeshUniform>,
784779
) {
785-
if extracted_joints.buffer.is_empty() {
780+
if skinned_mesh_uniform.buffer.is_empty() {
786781
return;
787782
}
788783

789-
skinned_mesh_uniform.buffer.clear();
790-
skinned_mesh_uniform
791-
.buffer
792-
.reserve(extracted_joints.buffer.len(), &render_device);
793-
for joint in &extracted_joints.buffer {
794-
skinned_mesh_uniform.buffer.push(*joint);
795-
}
784+
let len = skinned_mesh_uniform.buffer.len();
785+
skinned_mesh_uniform.buffer.reserve(len, &render_device);
796786
skinned_mesh_uniform
797787
.buffer
798788
.write_buffer(&render_device, &render_queue);

crates/bevy_render/src/render_resource/buffer_vec.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,18 @@ impl<T: Pod> BufferVec<T> {
131131
}
132132
}
133133

134+
pub fn truncate(&mut self, len: usize) {
135+
self.values.truncate(len);
136+
}
137+
134138
pub fn clear(&mut self) {
135139
self.values.clear();
136140
}
137141
}
142+
143+
impl<T: Pod> Extend<T> for BufferVec<T> {
144+
#[inline]
145+
fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
146+
self.values.extend(iter);
147+
}
148+
}

0 commit comments

Comments
 (0)