mirror of
https://github.com/bevyengine/bevy
synced 2024-11-22 12:43:34 +00:00
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.
This commit is contained in:
parent
53a5bbe2d5
commit
1523c38ce8
2 changed files with 32 additions and 31 deletions
|
@ -172,11 +172,6 @@ pub fn extract_meshes(
|
||||||
commands.insert_or_spawn_batch(not_caster_commands);
|
commands.insert_or_spawn_batch(not_caster_commands);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Resource, Debug, Default)]
|
|
||||||
pub struct ExtractedJoints {
|
|
||||||
pub buffer: Vec<Mat4>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
pub struct SkinnedMeshJoints {
|
pub struct SkinnedMeshJoints {
|
||||||
pub index: u32,
|
pub index: u32,
|
||||||
|
@ -188,19 +183,22 @@ impl SkinnedMeshJoints {
|
||||||
skin: &SkinnedMesh,
|
skin: &SkinnedMesh,
|
||||||
inverse_bindposes: &Assets<SkinnedMeshInverseBindposes>,
|
inverse_bindposes: &Assets<SkinnedMeshInverseBindposes>,
|
||||||
joints: &Query<&GlobalTransform>,
|
joints: &Query<&GlobalTransform>,
|
||||||
buffer: &mut Vec<Mat4>,
|
buffer: &mut BufferVec<Mat4>,
|
||||||
) -> Option<Self> {
|
) -> Option<Self> {
|
||||||
let inverse_bindposes = inverse_bindposes.get(&skin.inverse_bindposes)?;
|
let inverse_bindposes = inverse_bindposes.get(&skin.inverse_bindposes)?;
|
||||||
let bindposes = inverse_bindposes.iter();
|
|
||||||
let skin_joints = skin.joints.iter();
|
|
||||||
let start = buffer.len();
|
let start = buffer.len();
|
||||||
for (inverse_bindpose, joint) in bindposes.zip(skin_joints).take(MAX_JOINTS) {
|
let target = start + skin.joints.len().min(MAX_JOINTS);
|
||||||
if let Ok(joint) = joints.get(*joint) {
|
buffer.extend(
|
||||||
buffer.push(joint.affine() * *inverse_bindpose);
|
joints
|
||||||
} else {
|
.iter_many(&skin.joints)
|
||||||
buffer.truncate(start);
|
.zip(inverse_bindposes.iter())
|
||||||
return None;
|
.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
|
// Pad to 256 byte alignment
|
||||||
|
@ -221,13 +219,13 @@ impl SkinnedMeshJoints {
|
||||||
pub fn extract_skinned_meshes(
|
pub fn extract_skinned_meshes(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
mut previous_len: Local<usize>,
|
mut previous_len: Local<usize>,
|
||||||
mut previous_joint_len: Local<usize>,
|
mut uniform: ResMut<SkinnedMeshUniform>,
|
||||||
query: Extract<Query<(Entity, &ComputedVisibility, &SkinnedMesh)>>,
|
query: Extract<Query<(Entity, &ComputedVisibility, &SkinnedMesh)>>,
|
||||||
inverse_bindposes: Extract<Res<Assets<SkinnedMeshInverseBindposes>>>,
|
inverse_bindposes: Extract<Res<Assets<SkinnedMeshInverseBindposes>>>,
|
||||||
joint_query: Extract<Query<&GlobalTransform>>,
|
joint_query: Extract<Query<&GlobalTransform>>,
|
||||||
) {
|
) {
|
||||||
|
uniform.buffer.clear();
|
||||||
let mut values = Vec::with_capacity(*previous_len);
|
let mut values = Vec::with_capacity(*previous_len);
|
||||||
let mut joints = Vec::with_capacity(*previous_joint_len);
|
|
||||||
let mut last_start = 0;
|
let mut last_start = 0;
|
||||||
|
|
||||||
for (entity, computed_visibility, skin) in &query {
|
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?
|
// PERF: This can be expensive, can we move this to prepare?
|
||||||
if let Some(skinned_joints) =
|
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);
|
last_start = last_start.max(skinned_joints.index as usize);
|
||||||
values.push((entity, skinned_joints.to_buffer_index()));
|
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
|
// Pad out the buffer to ensure that there's enough space for bindings
|
||||||
while joints.len() - last_start < MAX_JOINTS {
|
while uniform.buffer.len() - last_start < MAX_JOINTS {
|
||||||
joints.push(Mat4::ZERO);
|
uniform.buffer.push(Mat4::ZERO);
|
||||||
}
|
}
|
||||||
|
|
||||||
*previous_len = values.len();
|
*previous_len = values.len();
|
||||||
*previous_joint_len = joints.len();
|
|
||||||
commands.insert_resource(ExtractedJoints { buffer: joints });
|
|
||||||
commands.insert_or_spawn_batch(values);
|
commands.insert_or_spawn_batch(values);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -779,20 +775,14 @@ impl Default for SkinnedMeshUniform {
|
||||||
pub fn prepare_skinned_meshes(
|
pub fn prepare_skinned_meshes(
|
||||||
render_device: Res<RenderDevice>,
|
render_device: Res<RenderDevice>,
|
||||||
render_queue: Res<RenderQueue>,
|
render_queue: Res<RenderQueue>,
|
||||||
extracted_joints: Res<ExtractedJoints>,
|
|
||||||
mut skinned_mesh_uniform: ResMut<SkinnedMeshUniform>,
|
mut skinned_mesh_uniform: ResMut<SkinnedMeshUniform>,
|
||||||
) {
|
) {
|
||||||
if extracted_joints.buffer.is_empty() {
|
if skinned_mesh_uniform.buffer.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
skinned_mesh_uniform.buffer.clear();
|
let len = skinned_mesh_uniform.buffer.len();
|
||||||
skinned_mesh_uniform
|
skinned_mesh_uniform.buffer.reserve(len, &render_device);
|
||||||
.buffer
|
|
||||||
.reserve(extracted_joints.buffer.len(), &render_device);
|
|
||||||
for joint in &extracted_joints.buffer {
|
|
||||||
skinned_mesh_uniform.buffer.push(*joint);
|
|
||||||
}
|
|
||||||
skinned_mesh_uniform
|
skinned_mesh_uniform
|
||||||
.buffer
|
.buffer
|
||||||
.write_buffer(&render_device, &render_queue);
|
.write_buffer(&render_device, &render_queue);
|
||||||
|
|
|
@ -131,7 +131,18 @@ impl<T: Pod> BufferVec<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn truncate(&mut self, len: usize) {
|
||||||
|
self.values.truncate(len);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
self.values.clear();
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue