Skip to content

Use multidraw for opaque meshes when GPU culling is in use. #16427

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

Merged
merged 10 commits into from
Dec 6, 2024

Conversation

pcwalton
Copy link
Contributor

@pcwalton pcwalton commented Nov 18, 2024

This commit adds support for multidraw, which is a feature that allows multiple meshes to be drawn in a single drawcall. wgpu currently implements multidraw on Vulkan, so this feature is only enabled there. Multiple meshes can be drawn at once if they're in the same vertex and index buffers and are otherwise placed in the same bin. (Thus, for example, at present the materials and textures must be identical, but see #16368.) Multidraw is a significant performance improvement during the draw phase because it reduces the number of rebindings, as well as the number of drawcalls.

This feature is currently only enabled when GPU culling is used: i.e. when GpuCulling is present on a camera. Therefore, if you run for example scene_viewer, you will not see any performance improvements, because scene_viewer doesn't add the GpuCulling component to its camera.

Additionally, the multidraw feature is only implemented for opaque 3D meshes and not for shadows or 2D meshes. I plan to make GPU culling the default and to extend the feature to shadows in the future. Also, in the future I suspect that polyfilling multidraw on APIs that don't support it will be fruitful, as even without driver-level support use of multidraw allows us to avoid expensive wgpu rebindings.

This commit adds support for *multidraw*, which is a feature that allows
multiple meshes to be drawn in a single drawcall. `wgpu` currently
implements multidraw on Vulkan, so this feature is only enabled there.
Multiple meshes can be drawn at once if they're in the same vertex and
index buffers and are otherwise placed in the same bin. (Thus, for
example, at present the materials and textures must be identical, but
see bevyengine#16368.) Multidraw is a significant performance improvement during
the draw phase because it reduces the number of rebindings, as well as
the amount of driver overhead.

This feature is currently only enabled when GPU culling is used: i.e.
when `GpuCulling` is present on a camera. Therefore, if you run for
example `scene_viewer`, you will not see any performance improvements,
because `scene_viewer` doesn't add the `GpuCulling` component to its
camera.

Additionally, the multidraw feature is only implemented for opaque 3D
meshes and not for shadows or 2D meshes. I plan to make GPU culling the
default and to extend the feature to shadows in the future. Also, in the
future I suspect that polyfilling multidraw on APIs that don't support
it will be fruitful, as even without driver-level support use of
multidraw allows us to avoid expensive `wgpu` rebindings.
@pcwalton pcwalton marked this pull request as draft November 18, 2024 08:24
@pcwalton
Copy link
Contributor Author

I'm marking this PR as a draft until 0.15 is out, to indicate that we shouldn't merge it before then. It should generally be ready to go, though; feel free to review.

@pcwalton pcwalton added C-Performance A change motivated by improving speed, memory usage or compile times A-Rendering Drawing game state to the screen S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels Nov 18, 2024
@pcwalton pcwalton added this to the 0.16 milestone Nov 18, 2024
@pcwalton pcwalton marked this pull request as ready for review December 1, 2024 23:27
@pcwalton pcwalton requested review from JMS55 and IceSentry December 1, 2024 23:27
Copy link
Contributor

@JMS55 JMS55 left a comment

Choose a reason for hiding this comment

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

Can't really test performance because I lack a desktop atm, but code lgtm.

Copy link
Contributor

@IceSentry IceSentry left a comment

Choose a reason for hiding this comment

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

I haven't tested the code locally yet, but everything LGTM

@pcwalton pcwalton 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 Dec 2, 2024
@pcwalton
Copy link
Contributor Author

pcwalton commented Dec 4, 2024

Merge conflicts addressed.

@alice-i-cecile
Copy link
Member

Merge conflicts on this one :)

@pcwalton
Copy link
Contributor Author

pcwalton commented Dec 5, 2024

Conflicts addressed.

pcwalton added a commit to pcwalton/bevy that referenced this pull request Dec 6, 2024
This commit removes the undocumented `GpuCulling` component in favor of
automatically turning GPU culling on when the platform supports it. The
main reason to make GPU culling automatic is that GPU culling enables
indirect mode. Indirect mode is needed for multidraw (bevyengine#16427), because
non-indirect multidraw doesn't exist in `wgpu`. Since multidraw is such
a win for performance, when that feature is supported the small
performance tax that indirect mode incurs is virtually always worth
paying.

CPU culling is always used in addition to GPU culling unless the
`NoCpuCulling` component is placed on the camera. This results in some
amount of redundant computation on the GPU, but the overhead is
negligible. I figured that the GPU time savings gained from skipping
this computation when not needed didn't outweigh the costs of the added
complexity that would be necessarily to implement that optimization,
especially with GPU two-phase occlusion culling on the horizon.
@alice-i-cecile alice-i-cecile added the M-Needs-Release-Note Work that should be called out in the blog due to impact label Dec 6, 2024
@alice-i-cecile alice-i-cecile added this pull request to the merge queue Dec 6, 2024
Merged via the queue into bevyengine:main with commit f5de3f0 Dec 6, 2024
29 checks passed
pcwalton added a commit to pcwalton/bevy that referenced this pull request Dec 10, 2024
default.

This patch replaces the undocumented `NoGpuCulling` component with a new
component, `NoIndirectDrawing`, effectively turning indirect drawing on
by default. Indirect mode is needed for the recently-landed multidraw
feature (bevyengine#16427). Since multidraw is such a win for performance, when
that feature is supported the small performance tax that indirect mode
incurs is virtually always worth paying.

To ensure that custom drawing code such as that in the
`custom_shader_instancing` example continues to function, this commit
additionally makes GPU culling take the `NoFrustumCulling` component
into account.

This PR is an alternative to bevyengine#16670 that doesn't break the
`custom_shader_instancing` example. **PR bevyengine#16755 should land first in
order to avoid breaking deferred rendering, as multidraw currently
breaks it**.
github-merge-queue bot pushed a commit that referenced this pull request Dec 13, 2024
…y default. (#16757)

This patch replaces the undocumented `NoGpuCulling` component with a new
component, `NoIndirectDrawing`, effectively turning indirect drawing on
by default. Indirect mode is needed for the recently-landed multidraw
feature (#16427). Since multidraw is such a win for performance, when
that feature is supported the small performance tax that indirect mode
incurs is virtually always worth paying.

To ensure that custom drawing code such as that in the
`custom_shader_instancing` example continues to function, this commit
additionally makes GPU culling take the `NoFrustumCulling` component
into account.

This PR is an alternative to #16670 that doesn't break the
`custom_shader_instancing` example. **PR #16755 should land first in
order to avoid breaking deferred rendering, as multidraw currently
breaks it**.

## Migration Guide

* Indirect drawing (GPU culling) is now enabled by default, so the
`GpuCulling` component is no longer available. To disable indirect mode,
which may be useful with custom render nodes, add the new
`NoIndirectDrawing` component to your camera.
ecoskey pushed a commit to ecoskey/bevy that referenced this pull request Jan 6, 2025
…ne#16427)

This commit adds support for *multidraw*, which is a feature that allows
multiple meshes to be drawn in a single drawcall. `wgpu` currently
implements multidraw on Vulkan, so this feature is only enabled there.
Multiple meshes can be drawn at once if they're in the same vertex and
index buffers and are otherwise placed in the same bin. (Thus, for
example, at present the materials and textures must be identical, but
see bevyengine#16368.) Multidraw is a significant performance improvement during
the draw phase because it reduces the number of rebindings, as well as
the number of drawcalls.

This feature is currently only enabled when GPU culling is used: i.e.
when `GpuCulling` is present on a camera. Therefore, if you run for
example `scene_viewer`, you will not see any performance improvements,
because `scene_viewer` doesn't add the `GpuCulling` component to its
camera.

Additionally, the multidraw feature is only implemented for opaque 3D
meshes and not for shadows or 2D meshes. I plan to make GPU culling the
default and to extend the feature to shadows in the future. Also, in the
future I suspect that polyfilling multidraw on APIs that don't support
it will be fruitful, as even without driver-level support use of
multidraw allows us to avoid expensive `wgpu` rebindings.
ecoskey pushed a commit to ecoskey/bevy that referenced this pull request Jan 6, 2025
…y default. (bevyengine#16757)

This patch replaces the undocumented `NoGpuCulling` component with a new
component, `NoIndirectDrawing`, effectively turning indirect drawing on
by default. Indirect mode is needed for the recently-landed multidraw
feature (bevyengine#16427). Since multidraw is such a win for performance, when
that feature is supported the small performance tax that indirect mode
incurs is virtually always worth paying.

To ensure that custom drawing code such as that in the
`custom_shader_instancing` example continues to function, this commit
additionally makes GPU culling take the `NoFrustumCulling` component
into account.

This PR is an alternative to bevyengine#16670 that doesn't break the
`custom_shader_instancing` example. **PR bevyengine#16755 should land first in
order to avoid breaking deferred rendering, as multidraw currently
breaks it**.

## Migration Guide

* Indirect drawing (GPU culling) is now enabled by default, so the
`GpuCulling` component is no longer available. To disable indirect mode,
which may be useful with custom render nodes, add the new
`NoIndirectDrawing` component to your camera.
@alice-i-cecile
Copy link
Member

Thank you to everyone involved with the authoring or reviewing of this PR! This work is relatively important and needs release notes! Head over to bevyengine/bevy-website#1968 if you'd like to help out.

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-Performance A change motivated by improving speed, memory usage or compile times M-Needs-Release-Note Work that should be called out in the blog due to impact 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.

4 participants