-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
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
Build batches across phases in parallel. #17764
Build batches across phases in parallel. #17764
Conversation
Currently, invocations of `batch_and_prepare_binned_render_phase` and `batch_and_prepare_sorted_render_phase` can't run in parallel because they write to scene-global GPU buffers. After PR bevyengine#17698, `batch_and_prepare_binned_render_phase` started accounting for the lion's share of the CPU time, causing us to be strongly CPU bound on scenes like Caldera when occlusion culling was on (because of the overhead of batching for the Z-prepass). Although I eventually plan to optimize `batch_and_prepare_binned_render_phase`, we can obtain significant wins now by parallelizing that system across phases. This commit splits all GPU buffers that `batch_and_prepare_binned_render_phase` and `batch_and_prepare_sorted_render_phase` touches into separate buffers for each phase so that the scheduler will run those phases in parallel. At the end of batch preparation, we gather the render phases up into a single resource with a new *collection* phase. Because we already run mesh preprocessing separately for each phase in order to make occlusion culling work, this is actually a cleaner separation. For example, mesh output indices (the unique ID that identifies each mesh instance on GPU) are now guaranteed to be sequential starting from 0, which will simplify the forthcoming work to remove them in favor of the compute dispatch ID. On Caldera, this brings the frame time down to approximately 9.1 ms with occlusion culling on.
08de0fe
to
c1f9764
Compare
examples/3d/occlusion_culling.rs
Outdated
@@ -185,6 +190,10 @@ fn main() { | |||
.set(RenderPlugin { | |||
allow_copies_from_indirect_parameters: true, | |||
..default() | |||
}) | |||
.set(PbrPlugin { | |||
allow_copies_from_indirect_parameters: true, |
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.
Could this be reused from the RenderPlugin? Rather than having to set it in two places.
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.
Could you elaborate as to how that would work? The problem is that the PbrPlugin
can't reach into the RenderPlugin
to check its value.
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.
I think this is fine for now but it's a rather particular option and I might suggest subsuming it some kind of broader "debug renderer" setting if we ever have a second instance of this kind of thing.
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.
Yeah, good point. Or maybe a "debug flags"?
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.
I went ahead and switched this to a RenderDebugFlags
so that we can have more of them.
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.
Lgtm. Thanks for creating the debug flags, a lot cleaner. Being able to lean on the ECS scheduler for this is nice and clean.
Currently, invocations of
batch_and_prepare_binned_render_phase
andbatch_and_prepare_sorted_render_phase
can't run in parallel because they write to scene-global GPU buffers. After PR #17698,batch_and_prepare_binned_render_phase
started accounting for the lion's share of the CPU time, causing us to be strongly CPU bound on scenes like Caldera when occlusion culling was on (because of the overhead of batching for the Z-prepass). Although I eventually plan to optimizebatch_and_prepare_binned_render_phase
, we can obtain significant wins now by parallelizing that system across phases.This commit splits all GPU buffers that
batch_and_prepare_binned_render_phase
andbatch_and_prepare_sorted_render_phase
touches into separate buffers for each phase so that the scheduler will run those phases in parallel. At the end of batch preparation, we gather the render phases up into a single resource with a new collection phase. Because we already run mesh preprocessing separately for each phase in order to make occlusion culling work, this is actually a cleaner separation. For example, mesh output indices (the unique ID that identifies each mesh instance on GPU) are now guaranteed to be sequential starting from 0, which will simplify the forthcoming work to remove them in favor of the compute dispatch ID.On Caldera, this brings the frame time down to approximately 9.1 ms with occlusion culling on.