Skip to content

Don't mark newly-hidden meshes invisible until all visibility-determining systems run.#17922

Merged
superdump merged 4 commits intobevyengine:mainfrom
pcwalton:split-visibility-checking
Feb 18, 2025
Merged

Don't mark newly-hidden meshes invisible until all visibility-determining systems run.#17922
superdump merged 4 commits intobevyengine:mainfrom
pcwalton:split-visibility-checking

Conversation

@pcwalton
Copy link
Copy Markdown
Contributor

@pcwalton pcwalton commented Feb 18, 2025

The check_visibility system currently follows this algorithm:

  1. Store all meshes that were visible last frame in the PreviousVisibleMeshes set.

  2. Determine which meshes are visible. For each such visible mesh, remove it from PreviousVisibleMeshes.

  3. Mark all meshes that remain in PreviousVisibleMeshes as invisible.

This algorithm would be correct if the check_visibility were the only system that marked meshes visible. However, it's not: the shadow-related systems check_dir_light_mesh_visibility and
check_point_light_mesh_visibility can as well. This results in the following sequence of events for meshes that are in a shadow map but not visible from a camera:

A. check_visibility runs, finds that no camera contains these meshes,
and marks them hidden, which sets the changed flag.

B. check_dir_light_mesh_visibility and/or
check_point_light_mesh_visibility run, discover that these meshes
are visible in the shadow map, and marks them as visible, again
setting the ViewVisibility changed flag.

C. During the extraction phase, the mesh extraction system sees that
ViewVisibility is changed and re-extracts the mesh.

This is inefficient and results in needless work during rendering.

This patch fixes the issue in two ways:

  • The check_dir_light_mesh_visibility and check_point_light_mesh_visibility systems now remove meshes that they discover from PreviousVisibleMeshes.

  • Step (3) above has been moved from check_visibility to a separate system, mark_newly_hidden_entities_invisible. This system runs after all visibility-determining systems, ensuring that PreviousVisibleMeshes contains only those meshes that truly became invisible on this frame.

This fix dramatically improves the performance of the Caldera benchmark, when combined with several other patches I've submitted.

visibility-determining systems run.

The `check_visibility` system currently follows this algorithm:

1. Store all meshes that were visible last frame in
   the `PreviousVisibleMeshes` set.

2. Determine which meshes are visible. For each such visible mesh,
   remove it from `PreviousVisibleMeshes`.

3. Mark all meshes that remain in `PreviousVisibleMeshes` as invisible.

This algorithm would be correct if the `check_visibility` were the only
system that marked meshes visible. However, it's not: the shadow-related
systems `check_dir_light_mesh_visibility` and
`check_point_light_mesh_visibility` can as well. This results in the
following sequence of events for meshes that are in a shadow map but
*not* visible from a camera:

A. `check_visibility` runs, finds that no camera contains these meshes,
   and marks them hidden, which sets the changed flag.

B. `check_dir_light_mesh_visibility` and/or
   `check_point_light_mesh_visibility` run, discover that these meshes
   are visible in the shadow map, and marks them as visible, again
   setting the `ViewVisibility` changed flag.

C. During the extraction phase, the mesh extraction system sees that
   `ViewVisibility` is changed and re-extracts the mesh.

This is inefficient and results in needless work during rendering.

This patch fixes the issue in two ways:

* The `check_dir_light_mesh_visibility` and
  `check_point_light_mesh_visibility` systems now remove meshes that
  they discover from `PreviousVisibleMeshes`.

* Step (3) above has been moved from `check_visibility` to a separate
  system, `mark_newly_hidden_entities_invisible`. This system runs after
  all visibility-determining systems, ensuring that
  `PreviousVisibleMeshes` contains only those meshes that truly became
  invisible on this frame.

This fix dramatically improves the performance of [the Caldera
benchmark], when combined with several other patches I've submitted.
@pcwalton pcwalton added C-Performance A change motivated by improving speed, memory usage or compile times C-Bug An unexpected or incorrect behavior A-Rendering Drawing game state to the screen S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels Feb 18, 2025
Copy link
Copy Markdown
Member

@tychedelia tychedelia left a comment

Choose a reason for hiding this comment

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

Awesome, this makes a lot more sense.

@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 18, 2025
@superdump superdump added this pull request to the merge queue Feb 18, 2025
Merged via the queue into bevyengine:main with commit 73970d0 Feb 18, 2025
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 S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it

Projects

No open projects
Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants