Skip to content
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

Don't mark a previous mesh transform as changed if it didn't actually change. #17688

Merged
merged 2 commits into from
Feb 5, 2025

Conversation

pcwalton
Copy link
Contributor

@pcwalton pcwalton commented Feb 5, 2025

This patch fixes a bug whereby we're re-extracting every mesh every frame. It's a regression from PR #17413. The code in question has actually been in the tree with this bug for quite a while; it's that just the code didn't actually run unless the renderer considered the previous view transforms necessary. Occlusion culling expanded the set of circumstances under which Bevy computes the previous view transforms, causing this bug to appear more often.

This patch fixes the issue by checking to see if the previous transform of a mesh actually differs from the current transform before copying the current transform to the previous transform.

change.

This patch fixes a bug whereby we're re-extracting every mesh every
frame. It's a regression from PR bevyengine#17413. The code in question has
actually been in the tree with this bug for quite a while; it's that
just the code didn't actually run unless the renderer considered the
previous view transforms necessary. Occlusion culling expanded the set
of circumstances under which Bevy computes the previous view transforms,
causing this bug to appear more often.

This patch fixes the issue by checking to see if the previous transform
of a mesh actually differs from the current transform before copying the
current transform to the previous transform.
@pcwalton pcwalton added A-Rendering Drawing game state to the screen C-Performance A change motivated by improving speed, memory usage or compile times P-Regression Functionality that used to work but no longer does. Add a test for this! C-Bug An unexpected or incorrect behavior S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels Feb 5, 2025
// Make sure not to trigger change detection on
// `PreviousGlobalTransform` if the previous transform hasn't
// changed.
if old_previous_transform != Some(&new_previous_transform) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC there's a way to skip change detection while letting you still mutate the component directly instead of reinserting. I don't know if it would have a significant perf impact on this though.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We would still need to potentially insert because the mesh might not have a PreviousGlobalTransform to begin with.

@tychedelia tychedelia added S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it and removed S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels Feb 5, 2025
@bushrat011899
Copy link
Contributor

Wasm CI issues should be resolved once you upstream from main.

@superdump superdump added this pull request to the merge queue Feb 5, 2025
Merged via the queue into bevyengine:main with commit 48049f7 Feb 5, 2025
28 checks passed
pcwalton added a commit to pcwalton/bevy that referenced this pull request Feb 7, 2025
PR bevyengine#17688 broke motion vector computation, and therefore motion blur,
because it enabled retention of `MeshInputUniform`s, and
`MeshInputUniform`s contain the indices of the previous frame's
transform and the previous frame's skinned mesh joint matrices. On frame
N, if a `MeshInputUniform` is retained on GPU from the previous frame,
the `previous_input_index` and `previous_skin_index` would refer to the
indices for frame N - 2, not the index for frame N - 1.

This patch fixes the problems. It solves these issues in two different
ways, one for transforms and one for skins:

1. To fix transforms, this patch supplies the *frame index* to the
   shader as part of the view uniforms, and specifies which frame index
   each mesh's previous transform refers to. So, in the situation
   described above, the frame index would be N, the previous frame index
   would be N - 1, and the `previous_input_frame_number` would be N - 2.
   The shader can now detect this situation and infer that the mesh has
   been retained, and can therefore conclude that the mesh's transform
   hasn't changed.

2. To fix skins, this patch replaces the explicit `previous_skin_index`
   with an invariant that the index of the joints for the current frame
   and the index of the joints for the previous frame are the same. This
   means that the `MeshInputUniform` never has to be updated even if the
   skin is animated. The downside is that we have to copy joint matrices
   from the previous frame's buffer to the current frame's buffer in
   `extract_skins`.

The rationale behind (2) is that we currently have no mechanism to
detect when joints that affect a skin have been updated, short of
comparing all the transforms and setting a flag for
`extract_meshes_for_gpu_building` to consume, which would regress
performance as we want `extract_skins` and
`extract_meshes_for_gpu_building` to be able to run in parallel.

To test this change, use `cargo run --example motion_blur`.
mrchantey pushed a commit to mrchantey/bevy that referenced this pull request Feb 17, 2025
… change. (bevyengine#17688)

This patch fixes a bug whereby we're re-extracting every mesh every
frame. It's a regression from PR bevyengine#17413. The code in question has
actually been in the tree with this bug for quite a while; it's that
just the code didn't actually run unless the renderer considered the
previous view transforms necessary. Occlusion culling expanded the set
of circumstances under which Bevy computes the previous view transforms,
causing this bug to appear more often.

This patch fixes the issue by checking to see if the previous transform
of a mesh actually differs from the current transform before copying the
current transform to the previous transform.
github-merge-queue bot pushed a commit that referenced this pull request Feb 18, 2025
PR #17688 broke motion vector computation, and therefore motion blur,
because it enabled retention of `MeshInputUniform`s, and
`MeshInputUniform`s contain the indices of the previous frame's
transform and the previous frame's skinned mesh joint matrices. On frame
N, if a `MeshInputUniform` is retained on GPU from the previous frame,
the `previous_input_index` and `previous_skin_index` would refer to the
indices for frame N - 2, not the index for frame N - 1.

This patch fixes the problems. It solves these issues in two different
ways, one for transforms and one for skins:

1. To fix transforms, this patch supplies the *frame index* to the
shader as part of the view uniforms, and specifies which frame index
each mesh's previous transform refers to. So, in the situation described
above, the frame index would be N, the previous frame index would be N -
1, and the `previous_input_frame_number` would be N - 2. The shader can
now detect this situation and infer that the mesh has been retained, and
can therefore conclude that the mesh's transform hasn't changed.

2. To fix skins, this patch replaces the explicit `previous_skin_index`
with an invariant that the index of the joints for the current frame and
the index of the joints for the previous frame are the same. This means
that the `MeshInputUniform` never has to be updated even if the skin is
animated. The downside is that we have to copy joint matrices from the
previous frame's buffer to the current frame's buffer in
`extract_skins`.

The rationale behind (2) is that we currently have no mechanism to
detect when joints that affect a skin have been updated, short of
comparing all the transforms and setting a flag for
`extract_meshes_for_gpu_building` to consume, which would regress
performance as we want `extract_skins` and
`extract_meshes_for_gpu_building` to be able to run in parallel.

To test this change, use `cargo run --example motion_blur`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-Rendering Drawing game state to the screen C-Bug An unexpected or incorrect behavior C-Performance A change motivated by improving speed, memory usage or compile times P-Regression Functionality that used to work but no longer does. Add a test for this! S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

5 participants