-
-
Notifications
You must be signed in to change notification settings - Fork 4k
Fix panic when a mesh has skinning attributes but no SkinnedMesh
component
#18074
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Fix panic when a mesh has skinning attributes but no SkinnedMesh
component
#18074
Conversation
…fixed forward mode missing passes required for motion blur.
- Removed render mode cycling and motion blur toggling - all renderer paths can be tested just by enabling motion blur. - Added a text overlay documenting each mesh. - Various tidy ups.
…h for whether a mesh is skinned. This meant adding `MeshPipelineKey::SKINNED`. - Fixed warning spam by making `extract_skins` check if the mesh has skinning attributes. - Fixed panic when using uniform buffers for skinning. If `SkinUniforms::current_buffer` is grown the `SkinUniforms::prev_buffer` should grow too.
size: new_size, | ||
mapped_at_creation: false, | ||
}); | ||
// TODO: This is wrong. We should be using last frame's data for prev_buffer. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awkward TODO - see "Previous Buffers Too Small" section.
Let's drop that; the rest of this is a clear improvement that we shouldn't let get bogged down. |
… but no skinning attributes.
I've reverted the attribute checks in I'll investigate using |
I've changed the specialize systems to take a Note that the struct is |
I've pulled one of the fixes out to #18715. Also put this PR into draft mode - once the other PR shakes out I'll need to update the description and fix conflicts. |
## Objective Fix motion blur not working on skinned meshes. ## Solution `set_mesh_motion_vector_flags` can set `RenderMeshInstanceFlags::HAS_PREVIOUS_SKIN` after specialization has already cached the material. This can lead to `MeshPipelineKey::HAS_PREVIOUS_SKIN` never getting set, disabling motion blur. The fix is to make sure `set_mesh_motion_vector_flags` happens before specialization. Note that the bug is fixed in a different way by #18074, which includes other fixes but is a much larger change. ## Testing Open the `animated_mesh` example and add these components to the `Camera3d` entity: ```rust MotionBlur { shutter_angle: 5.0, samples: 2, #[cfg(all(feature = "webgl2", target_arch = "wasm32", not(feature = "webgpu")))] _webgl2_padding: Default::default(), }, #[cfg(all(feature = "webgl2", target_arch = "wasm32", not(feature = "webgpu")))] Msaa::Off, ``` Tested on `animated_mesh`, `many_foxes`, `custom_skinned_mesh`, Win10/Nvidia with Vulkan, WebGL/Chrome, WebGPU/Chrome. Note that testing `many_foxes` WebGL requires #18715.
## Objective Fix motion blur not working on skinned meshes. ## Solution `set_mesh_motion_vector_flags` can set `RenderMeshInstanceFlags::HAS_PREVIOUS_SKIN` after specialization has already cached the material. This can lead to `MeshPipelineKey::HAS_PREVIOUS_SKIN` never getting set, disabling motion blur. The fix is to make sure `set_mesh_motion_vector_flags` happens before specialization. Note that the bug is fixed in a different way by #18074, which includes other fixes but is a much larger change. ## Testing Open the `animated_mesh` example and add these components to the `Camera3d` entity: ```rust MotionBlur { shutter_angle: 5.0, samples: 2, #[cfg(all(feature = "webgl2", target_arch = "wasm32", not(feature = "webgpu")))] _webgl2_padding: Default::default(), }, #[cfg(all(feature = "webgl2", target_arch = "wasm32", not(feature = "webgpu")))] Msaa::Off, ``` Tested on `animated_mesh`, `many_foxes`, `custom_skinned_mesh`, Win10/Nvidia with Vulkan, WebGL/Chrome, WebGPU/Chrome. Note that testing `many_foxes` WebGL requires #18715.
## Objective Fix #18714. ## Solution Make sure `SkinUniforms::prev_buffer` is resized at the same time as `current_buffer`. There will be a one frame visual glitch when the buffers are resized, since `prev_buffer` is incorrectly initialised with the current joint transforms. Note that #18074 includes the same fix. I'm assuming this smaller PR will land first. ## Testing See repro instructions in #18714. Tested on `animated_mesh`, `many_foxes`, `custom_skinned_mesh`, Win10/Nvidia with Vulkan, WebGL/Chrome, WebGPU/Chrome.
## Objective Fix #18714. ## Solution Make sure `SkinUniforms::prev_buffer` is resized at the same time as `current_buffer`. There will be a one frame visual glitch when the buffers are resized, since `prev_buffer` is incorrectly initialised with the current joint transforms. Note that #18074 includes the same fix. I'm assuming this smaller PR will land first. ## Testing See repro instructions in #18714. Tested on `animated_mesh`, `many_foxes`, `custom_skinned_mesh`, Win10/Nvidia with Vulkan, WebGL/Chrome, WebGPU/Chrome.
## Objective Add a test that would have caught #16929 and #18712. ## Solution The PR adds a `test_invalid_skinned_mesh` example that creates various valid and invalid skinned meshes. This is designed to catch panics via CI, and can be inspected visually. It also tests skinned meshes + motion blur.  The screenshot shows all the tests, but two are currently disabled as they cause panics. #18074 will re-enable them. ### Concerns - The test is not currently suitable for screenshot comparison. - I didn't add the test to CI. I'm a bit unsure if this should be part of the PR or a follow up discussion. - Visual inspection requires understanding why some meshes are deliberately broken and what that looks like. - I wasn't sure about naming conventions. I put `test` in the name so it's not confused with a real example. ## Testing ``` cargo run --example test_invalid_skinned_mesh ``` Tested on Win10/Nvidia, across Vulkan, WebGL/Chrome, WebGPU/Chrome.
PR is in draft mode as it needs to be redone following other changes. Currently waiting on #18762 and #18763 to land.
Objective
SkinnedMesh
component.SkinnedMesh
component but mesh has no skinning attributes).many_foxes
).Summary
My goal was to fix #16929 - a wgpu panic when a mesh with skinning attributes is instantiated as a
Mesh3d
but without aSkinnedMesh
component.The problem was:
setup_morph_and_skinning_defs
chooses a skinning bind group layout if the mesh has skinning attributes.SetMeshBindGroup::render
chooses a skinning bind group if the mesh is inskin_uniforms
.skin_uniforms
(because it doesn't have aSkinnedMesh
component).The fix is to make
skin_uniforms
the source of truth.This was mostly the case already - the exception was
setup_morph_and_skinning_defs
. So I changed the pipeline specialization functions to checkskin_uniforms
, set a newMeshPipelineKey::SKINNED
, and then pick that up insetup_morph_and_skinning_defs
.While working this through I noticed a few more issues.
Incorrect Pipeline Caches
The pipeline specialization used
RenderMeshInstanceFlags::HAS_PREVIOUS_SKIN
to decide if the mesh needed motion blur. But that flag is set by theset_mesh_motion_vector_flags
system, which can run after specialization. So in some cases the wrong pipeline was cached and skinned meshes had no motion blur.This is fixed as a side effect of using
skin_uniforms
as the source of truth. It also meansRenderMeshInstanceFlags::HAS_PREVIOUS_SKIN
can be removed, andset_mesh_motion_vector_flags
no longer has to iterate over all skinned entities.The bad news is that
HAS_PREVIOUS_MORPH
follows a similar pattern, so I suspect motion blur is broken on morph targets in some cases.Previous Buffers Too Small
When testing with
skins_use_uniform_buffers
forced on I got a panic aboutSkinUniforms::prev_buffer
being too small for the mapped range. I think the problem is thatprepare_skins
growscurrent_buffer
without also growingprev_buffer
, so it's too small for one frame.I've changed
prepare_skins
to grow both buffers. However, this will cause a single frame visual glitch as the newprev_buffer
is initialised with the current frame's joints. For now I think reducing a panic to a glitch is sufficient.I suspect there's also a bug where
prev_buffer
has wrong transforms the first frame after a skin is added. And at the last minute I realisedMeshPipelineKey::HAS_PREVIOUS_SKIN
is probably obsolete, since all skins now have a previous. I might chase these down in a follow-up PR.System Parameter Limits
When adding a
skin_uniforms
parameter to systems likespecialize_material_meshes
I hit a limit on the number of parameters. I fixed this by moving some parameters into aSystemParam
shared between specialize systems (85d1a00).New Test
The PR adds a test that repros most of the issues. It's designed to be run on CI to detect panics, but can be visually inspected as well. I haven't actually added it to the CI - I wasn't sure whether to do that in this PR or a follow-up.
The test currently spams warnings as it repros a bug not addressed by this PR (
SkinnedMesh
without skinning attributes - the inverse of #16929).Follow Ups
#9360 made the glTF importer try to skip any meshes that would triggered the bug. Arguably this is no longer necessary, but that's best handled in a separate PR as it may be contentious.
Performance
Tested with
many_foxes
. The cost ofset_mesh_motion_vector_flags
is much reduced (8.97us -> 0.3us).Testing
Tested on Win10/Vulkan/Nvidia, Wasm/Chrome/WebGL2/Nvidia.