1use core::mem::{self, size_of};
2
3use bevy_asset::{prelude::AssetChanged, Assets};
4use bevy_camera::visibility::ViewVisibility;
5use bevy_ecs::prelude::*;
6use bevy_math::Mat4;
7use bevy_mesh::skinning::{SkinnedMesh, SkinnedMeshInverseBindposes};
8use bevy_render::render_resource::{Buffer, BufferDescriptor};
9use bevy_render::settings::WgpuLimits;
10use bevy_render::sync_world::{MainEntity, MainEntityHashMap};
11use bevy_render::{
12 batching::NoAutomaticBatching,
13 render_resource::BufferUsages,
14 renderer::{RenderDevice, RenderQueue},
15 Extract,
16};
17use bevy_transform::prelude::GlobalTransform;
18use offset_allocator::{Allocation, Allocator};
19use tracing::error;
20
21pub const MAX_JOINTS: usize = 256;
29
30const MAX_TOTAL_JOINTS: u32 = 1024 * 1024 * 1024;
35
36const JOINTS_PER_ALLOCATION_UNIT: u32 = (256 / size_of::<Mat4>()) as u32;
42
43#[derive(Clone, Copy)]
45pub struct SkinByteOffset {
46 pub byte_offset: u32,
48}
49
50impl SkinByteOffset {
51 const fn from_index(index: usize) -> Self {
53 SkinByteOffset {
54 byte_offset: (index * size_of::<Mat4>()) as u32,
55 }
56 }
57
58 pub fn index(&self) -> u32 {
62 self.byte_offset / size_of::<Mat4>() as u32
63 }
64}
65
66#[derive(Resource)]
76pub struct SkinUniforms {
77 pub current_staging_buffer: Vec<Mat4>,
80 pub current_buffer: Buffer,
83 pub prev_buffer: Buffer,
86 allocator: Allocator,
89 skin_uniform_info: MainEntityHashMap<SkinUniformInfo>,
91 total_joints: usize,
96}
97
98pub fn skin_uniforms_from_world(device: Res<RenderDevice>, mut commands: Commands) {
99 let buffer_usages = (if skins_use_uniform_buffers(&device.limits()) {
100 BufferUsages::UNIFORM
101 } else {
102 BufferUsages::STORAGE
103 }) | BufferUsages::COPY_DST;
104
105 let current_buffer = device.create_buffer(&BufferDescriptor {
109 label: Some("skin uniform buffer"),
110 size: MAX_JOINTS as u64 * size_of::<Mat4>() as u64,
111 usage: buffer_usages,
112 mapped_at_creation: false,
113 });
114 let prev_buffer = device.create_buffer(&BufferDescriptor {
115 label: Some("skin uniform buffer"),
116 size: MAX_JOINTS as u64 * size_of::<Mat4>() as u64,
117 usage: buffer_usages,
118 mapped_at_creation: false,
119 });
120
121 let res = SkinUniforms {
122 current_staging_buffer: vec![],
123 current_buffer,
124 prev_buffer,
125 allocator: Allocator::new(MAX_TOTAL_JOINTS),
126 skin_uniform_info: MainEntityHashMap::default(),
127 total_joints: 0,
128 };
129
130 commands.insert_resource(res);
131}
132
133impl SkinUniforms {
134 pub fn skin_index(&self, skin: MainEntity) -> Option<u32> {
136 self.skin_uniform_info
137 .get(&skin)
138 .map(SkinUniformInfo::offset)
139 }
140
141 pub fn skin_byte_offset(&self, skin: MainEntity) -> Option<SkinByteOffset> {
143 self.skin_uniform_info.get(&skin).map(|skin_uniform_info| {
144 SkinByteOffset::from_index(skin_uniform_info.offset() as usize)
145 })
146 }
147
148 pub fn all_skins(&self) -> impl Iterator<Item = &MainEntity> {
150 self.skin_uniform_info.keys()
151 }
152}
153
154struct SkinUniformInfo {
156 allocation: Allocation,
158 joints: Vec<MainEntity>,
160}
161
162impl SkinUniformInfo {
163 fn offset(&self) -> u32 {
165 self.allocation.offset * JOINTS_PER_ALLOCATION_UNIT
166 }
167}
168
169pub fn skins_use_uniform_buffers(limits: &WgpuLimits) -> bool {
172 bevy_render::storage_buffers_are_unsupported(limits)
173}
174
175pub fn prepare_skins(
177 render_device: Res<RenderDevice>,
178 render_queue: Res<RenderQueue>,
179 uniform: ResMut<SkinUniforms>,
180) {
181 let uniform = uniform.into_inner();
182
183 if uniform.current_staging_buffer.is_empty() {
184 return;
185 }
186
187 mem::swap(&mut uniform.current_buffer, &mut uniform.prev_buffer);
189
190 let needed_size = (uniform.current_staging_buffer.len() as u64 + MAX_JOINTS as u64)
194 * size_of::<Mat4>() as u64;
195 if uniform.current_buffer.size() < needed_size {
196 let mut new_size = uniform.current_buffer.size();
197 while new_size < needed_size {
198 new_size = (new_size + new_size / 2).next_multiple_of(4);
200 }
201
202 let buffer_usages = if skins_use_uniform_buffers(&render_device.limits()) {
204 BufferUsages::UNIFORM
205 } else {
206 BufferUsages::STORAGE
207 } | BufferUsages::COPY_DST;
208 uniform.current_buffer = render_device.create_buffer(&BufferDescriptor {
209 label: Some("skin uniform buffer"),
210 usage: buffer_usages,
211 size: new_size,
212 mapped_at_creation: false,
213 });
214 uniform.prev_buffer = render_device.create_buffer(&BufferDescriptor {
215 label: Some("skin uniform buffer"),
216 usage: buffer_usages,
217 size: new_size,
218 mapped_at_creation: false,
219 });
220
221 render_queue.write_buffer(
228 &uniform.prev_buffer,
229 0,
230 bytemuck::must_cast_slice(&uniform.current_staging_buffer[..]),
231 );
232 }
233
234 render_queue.write_buffer(
237 &uniform.current_buffer,
238 0,
239 bytemuck::must_cast_slice(&uniform.current_staging_buffer[..]),
240 );
241
242 }
245
246pub fn extract_skins(
273 skin_uniforms: ResMut<SkinUniforms>,
274 skinned_meshes: Extract<Query<(Entity, &SkinnedMesh)>>,
275 changed_skinned_meshes: Extract<
276 Query<
277 (Entity, &ViewVisibility, &SkinnedMesh),
278 Or<(
279 Changed<ViewVisibility>,
280 Changed<SkinnedMesh>,
281 AssetChanged<SkinnedMesh>,
282 )>,
283 >,
284 >,
285 skinned_mesh_inverse_bindposes: Extract<Res<Assets<SkinnedMeshInverseBindposes>>>,
286 changed_transforms: Extract<Query<(Entity, &GlobalTransform), Changed<GlobalTransform>>>,
287 joints: Extract<Query<&GlobalTransform>>,
288 mut removed_skinned_meshes_query: Extract<RemovedComponents<SkinnedMesh>>,
289) {
290 let skin_uniforms = skin_uniforms.into_inner();
291
292 add_or_delete_skins(
295 skin_uniforms,
296 &changed_skinned_meshes,
297 &skinned_mesh_inverse_bindposes,
298 &joints,
299 );
300
301 for (skin_entity, skin) in &skinned_meshes {
304 extract_joints_for_skin(
305 skin_entity.into(),
306 skin,
307 skin_uniforms,
308 &changed_skinned_meshes,
309 &skinned_mesh_inverse_bindposes,
310 &changed_transforms,
311 );
312 }
313
314 for skinned_mesh_entity in removed_skinned_meshes_query.read() {
316 if !changed_skinned_meshes.contains(skinned_mesh_entity) {
320 remove_skin(skin_uniforms, skinned_mesh_entity.into());
321 }
322 }
323}
324
325fn add_or_delete_skins(
328 skin_uniforms: &mut SkinUniforms,
329 changed_skinned_meshes: &Query<
330 (Entity, &ViewVisibility, &SkinnedMesh),
331 Or<(
332 Changed<ViewVisibility>,
333 Changed<SkinnedMesh>,
334 AssetChanged<SkinnedMesh>,
335 )>,
336 >,
337 skinned_mesh_inverse_bindposes: &Assets<SkinnedMeshInverseBindposes>,
338 joints: &Query<&GlobalTransform>,
339) {
340 for (skinned_mesh_entity, skinned_mesh_view_visibility, skinned_mesh) in changed_skinned_meshes
344 {
345 let skinned_mesh_entity = MainEntity::from(skinned_mesh_entity);
347 remove_skin(skin_uniforms, skinned_mesh_entity);
348
349 if !(*skinned_mesh_view_visibility).get() {
351 continue;
352 }
353
354 add_skin(
356 skinned_mesh_entity,
357 skinned_mesh,
358 skin_uniforms,
359 skinned_mesh_inverse_bindposes,
360 joints,
361 );
362 }
363}
364
365fn extract_joints_for_skin(
368 skin_entity: MainEntity,
369 skin: &SkinnedMesh,
370 skin_uniforms: &mut SkinUniforms,
371 changed_skinned_meshes: &Query<
372 (Entity, &ViewVisibility, &SkinnedMesh),
373 Or<(
374 Changed<ViewVisibility>,
375 Changed<SkinnedMesh>,
376 AssetChanged<SkinnedMesh>,
377 )>,
378 >,
379 skinned_mesh_inverse_bindposes: &Assets<SkinnedMeshInverseBindposes>,
380 changed_transforms: &Query<(Entity, &GlobalTransform), Changed<GlobalTransform>>,
381) {
382 if changed_skinned_meshes.contains(*skin_entity) {
385 return;
386 }
387
388 let Some(skin_uniform_info) = skin_uniforms.skin_uniform_info.get(&skin_entity) else {
390 return;
391 };
392 let Some(skinned_mesh_inverse_bindposes) =
393 skinned_mesh_inverse_bindposes.get(&skin.inverse_bindposes)
394 else {
395 return;
396 };
397
398 for (joint_index, (&joint, skinned_mesh_inverse_bindpose)) in skin
400 .joints
401 .iter()
402 .zip(skinned_mesh_inverse_bindposes.iter())
403 .enumerate()
404 {
405 let Ok((_, joint_transform)) = changed_transforms.get(joint) else {
407 continue;
408 };
409
410 let joint_matrix = joint_transform.affine() * *skinned_mesh_inverse_bindpose;
411 skin_uniforms.current_staging_buffer[skin_uniform_info.offset() as usize + joint_index] =
412 joint_matrix;
413 }
414}
415
416fn add_skin(
418 skinned_mesh_entity: MainEntity,
419 skinned_mesh: &SkinnedMesh,
420 skin_uniforms: &mut SkinUniforms,
421 skinned_mesh_inverse_bindposes: &Assets<SkinnedMeshInverseBindposes>,
422 joints: &Query<&GlobalTransform>,
423) {
424 let Some(allocation) = skin_uniforms.allocator.allocate(
426 skinned_mesh
427 .joints
428 .len()
429 .div_ceil(JOINTS_PER_ALLOCATION_UNIT as usize) as u32,
430 ) else {
431 error!(
432 "Out of space for skin: {:?}. Tried to allocate space for {:?} joints.",
433 skinned_mesh_entity,
434 skinned_mesh.joints.len()
435 );
436 return;
437 };
438
439 let skin_uniform_info = SkinUniformInfo {
441 allocation,
442 joints: skinned_mesh
443 .joints
444 .iter()
445 .map(|entity| MainEntity::from(*entity))
446 .collect(),
447 };
448
449 let skinned_mesh_inverse_bindposes =
450 skinned_mesh_inverse_bindposes.get(&skinned_mesh.inverse_bindposes);
451
452 for (joint_index, &joint) in skinned_mesh.joints.iter().enumerate() {
453 let skinned_mesh_inverse_bindpose =
455 skinned_mesh_inverse_bindposes.and_then(|skinned_mesh_inverse_bindposes| {
456 skinned_mesh_inverse_bindposes.get(joint_index)
457 });
458 let joint_matrix = match (skinned_mesh_inverse_bindpose, joints.get(joint)) {
459 (Some(skinned_mesh_inverse_bindpose), Ok(transform)) => {
460 transform.affine() * *skinned_mesh_inverse_bindpose
461 }
462 _ => Mat4::IDENTITY,
463 };
464
465 let buffer_index = skin_uniform_info.offset() as usize + joint_index;
468 if skin_uniforms.current_staging_buffer.len() < buffer_index + 1 {
469 skin_uniforms
470 .current_staging_buffer
471 .resize(buffer_index + 1, Mat4::IDENTITY);
472 }
473 skin_uniforms.current_staging_buffer[buffer_index] = joint_matrix;
474 }
475
476 skin_uniforms.total_joints += skinned_mesh.joints.len();
478
479 skin_uniforms
480 .skin_uniform_info
481 .insert(skinned_mesh_entity, skin_uniform_info);
482}
483
484fn remove_skin(skin_uniforms: &mut SkinUniforms, skinned_mesh_entity: MainEntity) {
486 let Some(old_skin_uniform_info) = skin_uniforms.skin_uniform_info.remove(&skinned_mesh_entity)
487 else {
488 return;
489 };
490
491 skin_uniforms
493 .allocator
494 .free(old_skin_uniform_info.allocation);
495
496 skin_uniforms.total_joints -= old_skin_uniform_info.joints.len();
498}
499
500pub fn no_automatic_skin_batching(
503 mut commands: Commands,
504 query: Query<Entity, (With<SkinnedMesh>, Without<NoAutomaticBatching>)>,
505 render_device: Res<RenderDevice>,
506) {
507 if !skins_use_uniform_buffers(&render_device.limits()) {
508 return;
509 }
510
511 for entity in &query {
512 commands.entity(entity).try_insert(NoAutomaticBatching);
513 }
514}