Skip to content

Commit bef9bc1

Browse files
committed
Reduce branching in TrackedRenderPass (#7053)
# Objective Speed up the render phase for rendering. ## Solution - Follow up #6988 and make the internals of atomic IDs `NonZeroU32`. This niches the `Option`s of the IDs in draw state, which reduces the size and branching behavior when evaluating for equality. - Require `&RenderDevice` to get the device's `Limits` when initializing a `TrackedRenderPass` to preallocate the bind groups and vertex buffer state in `DrawState`, this removes the branch on needing to resize those `Vec`s. ## Performance This produces a similar speed up akin to that of #6885. This shows an approximate 6% speed up in `main_opaque_pass_3d` on `many_foxes` (408.79 us -> 388us). This should be orthogonal to the gains seen there. ![image](https://user-images.githubusercontent.com/3137680/209906239-e430f026-63c2-4b95-957e-a2045b810d79.png) --- ## Changelog Added: `RenderContext::begin_tracked_render_pass`. Changed: `TrackedRenderPass` now requires a `&RenderDevice` on construction. Removed: `bevy_render::render_phase::DrawState`. It was not usable in any form outside of `bevy_render`. ## Migration Guide TODO
1 parent d76b53b commit bef9bc1

File tree

9 files changed

+105
-122
lines changed

9 files changed

+105
-122
lines changed

crates/bevy_core_pipeline/src/bloom/mod.rs

Lines changed: 37 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ use bevy_render::{
1717
},
1818
prelude::Camera,
1919
render_graph::{Node, NodeRunError, RenderGraph, RenderGraphContext, SlotInfo, SlotType},
20-
render_phase::TrackedRenderPass,
2120
render_resource::*,
2221
renderer::{RenderContext, RenderDevice},
2322
texture::{CachedTexture, TextureCache},
@@ -232,17 +231,15 @@ impl Node for BloomNode {
232231
{
233232
let view = &BloomTextures::texture_view(&textures.texture_a, 0);
234233
let mut prefilter_pass =
235-
TrackedRenderPass::new(render_context.command_encoder.begin_render_pass(
236-
&RenderPassDescriptor {
237-
label: Some("bloom_prefilter_pass"),
238-
color_attachments: &[Some(RenderPassColorAttachment {
239-
view,
240-
resolve_target: None,
241-
ops: Operations::default(),
242-
})],
243-
depth_stencil_attachment: None,
244-
},
245-
));
234+
render_context.begin_tracked_render_pass(RenderPassDescriptor {
235+
label: Some("bloom_prefilter_pass"),
236+
color_attachments: &[Some(RenderPassColorAttachment {
237+
view,
238+
resolve_target: None,
239+
ops: Operations::default(),
240+
})],
241+
depth_stencil_attachment: None,
242+
});
246243
prefilter_pass.set_render_pipeline(downsampling_prefilter_pipeline);
247244
prefilter_pass.set_bind_group(
248245
0,
@@ -258,17 +255,15 @@ impl Node for BloomNode {
258255
for mip in 1..textures.mip_count {
259256
let view = &BloomTextures::texture_view(&textures.texture_a, mip);
260257
let mut downsampling_pass =
261-
TrackedRenderPass::new(render_context.command_encoder.begin_render_pass(
262-
&RenderPassDescriptor {
263-
label: Some("bloom_downsampling_pass"),
264-
color_attachments: &[Some(RenderPassColorAttachment {
265-
view,
266-
resolve_target: None,
267-
ops: Operations::default(),
268-
})],
269-
depth_stencil_attachment: None,
270-
},
271-
));
258+
render_context.begin_tracked_render_pass(RenderPassDescriptor {
259+
label: Some("bloom_downsampling_pass"),
260+
color_attachments: &[Some(RenderPassColorAttachment {
261+
view,
262+
resolve_target: None,
263+
ops: Operations::default(),
264+
})],
265+
depth_stencil_attachment: None,
266+
});
272267
downsampling_pass.set_render_pipeline(downsampling_pipeline);
273268
downsampling_pass.set_bind_group(
274269
0,
@@ -284,17 +279,15 @@ impl Node for BloomNode {
284279
for mip in (1..textures.mip_count).rev() {
285280
let view = &BloomTextures::texture_view(&textures.texture_b, mip - 1);
286281
let mut upsampling_pass =
287-
TrackedRenderPass::new(render_context.command_encoder.begin_render_pass(
288-
&RenderPassDescriptor {
289-
label: Some("bloom_upsampling_pass"),
290-
color_attachments: &[Some(RenderPassColorAttachment {
291-
view,
292-
resolve_target: None,
293-
ops: Operations::default(),
294-
})],
295-
depth_stencil_attachment: None,
296-
},
297-
));
282+
render_context.begin_tracked_render_pass(RenderPassDescriptor {
283+
label: Some("bloom_upsampling_pass"),
284+
color_attachments: &[Some(RenderPassColorAttachment {
285+
view,
286+
resolve_target: None,
287+
ops: Operations::default(),
288+
})],
289+
depth_stencil_attachment: None,
290+
});
298291
upsampling_pass.set_render_pipeline(upsampling_pipeline);
299292
upsampling_pass.set_bind_group(
300293
0,
@@ -309,18 +302,16 @@ impl Node for BloomNode {
309302

310303
{
311304
let mut upsampling_final_pass =
312-
TrackedRenderPass::new(render_context.command_encoder.begin_render_pass(
313-
&RenderPassDescriptor {
314-
label: Some("bloom_upsampling_final_pass"),
315-
color_attachments: &[Some(view_target.get_unsampled_color_attachment(
316-
Operations {
317-
load: LoadOp::Load,
318-
store: true,
319-
},
320-
))],
321-
depth_stencil_attachment: None,
322-
},
323-
));
305+
render_context.begin_tracked_render_pass(RenderPassDescriptor {
306+
label: Some("bloom_upsampling_final_pass"),
307+
color_attachments: &[Some(view_target.get_unsampled_color_attachment(
308+
Operations {
309+
load: LoadOp::Load,
310+
store: true,
311+
},
312+
))],
313+
depth_stencil_attachment: None,
314+
});
324315
upsampling_final_pass.set_render_pipeline(upsampling_final_pipeline);
325316
upsampling_final_pass.set_bind_group(
326317
0,

crates/bevy_core_pipeline/src/core_2d/main_pass_2d_node.rs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ use crate::{
33
core_2d::{camera_2d::Camera2d, Transparent2d},
44
};
55
use bevy_ecs::prelude::*;
6-
use bevy_render::render_phase::TrackedRenderPass;
76
use bevy_render::{
87
camera::ExtractedCamera,
98
render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo, SlotType},
@@ -63,7 +62,8 @@ impl Node for MainPass2dNode {
6362
{
6463
#[cfg(feature = "trace")]
6564
let _main_pass_2d = info_span!("main_pass_2d").entered();
66-
let pass_descriptor = RenderPassDescriptor {
65+
66+
let mut render_pass = render_context.begin_tracked_render_pass(RenderPassDescriptor {
6767
label: Some("main_pass_2d"),
6868
color_attachments: &[Some(target.get_color_attachment(Operations {
6969
load: match camera_2d.clear_color {
@@ -76,12 +76,7 @@ impl Node for MainPass2dNode {
7676
store: true,
7777
}))],
7878
depth_stencil_attachment: None,
79-
};
80-
81-
let render_pass = render_context
82-
.command_encoder
83-
.begin_render_pass(&pass_descriptor);
84-
let mut render_pass = TrackedRenderPass::new(render_pass);
79+
});
8580

8681
if let Some(viewport) = camera.viewport.as_ref() {
8782
render_pass.set_camera_viewport(viewport);

crates/bevy_core_pipeline/src/core_3d/main_pass_3d_node.rs

Lines changed: 9 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ use crate::{
33
core_3d::{AlphaMask3d, Camera3d, Opaque3d, Transparent3d},
44
};
55
use bevy_ecs::prelude::*;
6-
use bevy_render::render_phase::TrackedRenderPass;
76
use bevy_render::{
87
camera::ExtractedCamera,
98
render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo, SlotType},
@@ -70,7 +69,8 @@ impl Node for MainPass3dNode {
7069
// NOTE: Scoped to drop the mutable borrow of render_context
7170
#[cfg(feature = "trace")]
7271
let _main_opaque_pass_3d_span = info_span!("main_opaque_pass_3d").entered();
73-
let pass_descriptor = RenderPassDescriptor {
72+
73+
let mut render_pass = render_context.begin_tracked_render_pass(RenderPassDescriptor {
7474
label: Some("main_opaque_pass_3d"),
7575
// NOTE: The opaque pass loads the color
7676
// buffer as well as writing to it.
@@ -94,12 +94,7 @@ impl Node for MainPass3dNode {
9494
}),
9595
stencil_ops: None,
9696
}),
97-
};
98-
99-
let render_pass = render_context
100-
.command_encoder
101-
.begin_render_pass(&pass_descriptor);
102-
let mut render_pass = TrackedRenderPass::new(render_pass);
97+
});
10398

10499
if let Some(viewport) = camera.viewport.as_ref() {
105100
render_pass.set_camera_viewport(viewport);
@@ -113,7 +108,8 @@ impl Node for MainPass3dNode {
113108
// NOTE: Scoped to drop the mutable borrow of render_context
114109
#[cfg(feature = "trace")]
115110
let _main_alpha_mask_pass_3d_span = info_span!("main_alpha_mask_pass_3d").entered();
116-
let pass_descriptor = RenderPassDescriptor {
111+
112+
let mut render_pass = render_context.begin_tracked_render_pass(RenderPassDescriptor {
117113
label: Some("main_alpha_mask_pass_3d"),
118114
// NOTE: The alpha_mask pass loads the color buffer as well as overwriting it where appropriate.
119115
color_attachments: &[Some(target.get_color_attachment(Operations {
@@ -129,12 +125,7 @@ impl Node for MainPass3dNode {
129125
}),
130126
stencil_ops: None,
131127
}),
132-
};
133-
134-
let render_pass = render_context
135-
.command_encoder
136-
.begin_render_pass(&pass_descriptor);
137-
let mut render_pass = TrackedRenderPass::new(render_pass);
128+
});
138129

139130
if let Some(viewport) = camera.viewport.as_ref() {
140131
render_pass.set_camera_viewport(viewport);
@@ -148,7 +139,8 @@ impl Node for MainPass3dNode {
148139
// NOTE: Scoped to drop the mutable borrow of render_context
149140
#[cfg(feature = "trace")]
150141
let _main_transparent_pass_3d_span = info_span!("main_transparent_pass_3d").entered();
151-
let pass_descriptor = RenderPassDescriptor {
142+
143+
let mut render_pass = render_context.begin_tracked_render_pass(RenderPassDescriptor {
152144
label: Some("main_transparent_pass_3d"),
153145
// NOTE: The transparent pass loads the color buffer as well as overwriting it where appropriate.
154146
color_attachments: &[Some(target.get_color_attachment(Operations {
@@ -169,12 +161,7 @@ impl Node for MainPass3dNode {
169161
}),
170162
stencil_ops: None,
171163
}),
172-
};
173-
174-
let render_pass = render_context
175-
.command_encoder
176-
.begin_render_pass(&pass_descriptor);
177-
let mut render_pass = TrackedRenderPass::new(render_pass);
164+
});
178165

179166
if let Some(viewport) = camera.viewport.as_ref() {
180167
render_pass.set_camera_viewport(viewport);

crates/bevy_pbr/src/render/light.rs

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1770,23 +1770,19 @@ impl Node for ShadowPassNode {
17701770
continue;
17711771
}
17721772

1773-
let pass_descriptor = RenderPassDescriptor {
1774-
label: Some(&view_light.pass_name),
1775-
color_attachments: &[],
1776-
depth_stencil_attachment: Some(RenderPassDepthStencilAttachment {
1777-
view: &view_light.depth_texture_view,
1778-
depth_ops: Some(Operations {
1779-
load: LoadOp::Clear(0.0),
1780-
store: true,
1773+
let mut render_pass =
1774+
render_context.begin_tracked_render_pass(RenderPassDescriptor {
1775+
label: Some(&view_light.pass_name),
1776+
color_attachments: &[],
1777+
depth_stencil_attachment: Some(RenderPassDepthStencilAttachment {
1778+
view: &view_light.depth_texture_view,
1779+
depth_ops: Some(Operations {
1780+
load: LoadOp::Clear(0.0),
1781+
store: true,
1782+
}),
1783+
stencil_ops: None,
17811784
}),
1782-
stencil_ops: None,
1783-
}),
1784-
};
1785-
1786-
let render_pass = render_context
1787-
.command_encoder
1788-
.begin_render_pass(&pass_descriptor);
1789-
let mut render_pass = TrackedRenderPass::new(render_pass);
1785+
});
17901786

17911787
shadow_phase.render(&mut render_pass, world, view_light_entity);
17921788
}

crates/bevy_render/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ image = { version = "0.24", default-features = false }
5050

5151
# misc
5252
wgpu = { version = "0.14.0", features = ["spirv"] }
53+
wgpu-hal = "0.14.1"
5354
codespan-reporting = "0.11.0"
5455
naga = { version = "0.10.0", features = ["glsl-in", "spv-in", "spv-out", "wgsl-in", "wgsl-out"] }
5556
serde = { version = "1", features = ["derive"] }

crates/bevy_render/src/render_phase/draw_state.rs

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,16 @@ use crate::{
55
BindGroup, BindGroupId, Buffer, BufferId, BufferSlice, RenderPipeline, RenderPipelineId,
66
ShaderStages,
77
},
8+
renderer::RenderDevice,
89
};
9-
use bevy_utils::tracing::trace;
10+
use bevy_utils::{default, tracing::trace};
1011
use std::ops::Range;
1112
use wgpu::{IndexFormat, RenderPass};
13+
use wgpu_hal::{MAX_BIND_GROUPS, MAX_VERTEX_BUFFERS};
1214

1315
/// Tracks the current [`TrackedRenderPass`] state to ensure draw calls are valid.
1416
#[derive(Debug, Default)]
15-
pub struct DrawState {
17+
struct DrawState {
1618
pipeline: Option<RenderPipelineId>,
1719
bind_groups: Vec<(Option<BindGroupId>, Vec<u32>)>,
1820
vertex_buffers: Vec<Option<(BufferId, u64)>>,
@@ -26,12 +28,10 @@ impl DrawState {
2628
bind_group: BindGroupId,
2729
dynamic_indices: &[u32],
2830
) {
29-
if index >= self.bind_groups.len() {
30-
self.bind_groups.resize(index + 1, (None, Vec::new()));
31-
}
32-
self.bind_groups[index].0 = Some(bind_group);
33-
self.bind_groups[index].1.clear();
34-
self.bind_groups[index].1.extend(dynamic_indices);
31+
let group = &mut self.bind_groups[index];
32+
group.0 = Some(bind_group);
33+
group.1.clear();
34+
group.1.extend(dynamic_indices);
3535
}
3636

3737
pub fn is_bind_group_set(
@@ -48,9 +48,6 @@ impl DrawState {
4848
}
4949

5050
pub fn set_vertex_buffer(&mut self, index: usize, buffer: BufferId, offset: u64) {
51-
if index >= self.vertex_buffers.len() {
52-
self.vertex_buffers.resize(index + 1, None);
53-
}
5451
self.vertex_buffers[index] = Some((buffer, offset));
5552
}
5653

@@ -98,9 +95,16 @@ pub struct TrackedRenderPass<'a> {
9895

9996
impl<'a> TrackedRenderPass<'a> {
10097
/// Tracks the supplied render pass.
101-
pub fn new(pass: RenderPass<'a>) -> Self {
98+
pub fn new(device: &RenderDevice, pass: RenderPass<'a>) -> Self {
99+
let limits = device.limits();
100+
let max_bind_groups = limits.max_bind_groups as usize;
101+
let max_vertex_buffers = limits.max_vertex_buffers as usize;
102102
Self {
103-
state: DrawState::default(),
103+
state: DrawState {
104+
bind_groups: vec![(None, Vec::new()); max_bind_groups.min(MAX_BIND_GROUPS)],
105+
vertex_buffers: vec![None; max_vertex_buffers.min(MAX_VERTEX_BUFFERS)],
106+
..default()
107+
},
104108
pass,
105109
}
106110
}

crates/bevy_render/src/render_resource/resource_macros.rs

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ macro_rules! render_resource_wrapper {
124124
macro_rules! define_atomic_id {
125125
($atomic_id_type:ident) => {
126126
#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)]
127-
pub struct $atomic_id_type(u32);
127+
pub struct $atomic_id_type(core::num::NonZeroU32);
128128

129129
// We use new instead of default to indicate that each ID created will be unique.
130130
#[allow(clippy::new_without_default)]
@@ -134,15 +134,13 @@ macro_rules! define_atomic_id {
134134

135135
static COUNTER: AtomicU32 = AtomicU32::new(1);
136136

137-
match COUNTER.fetch_add(1, Ordering::Relaxed) {
138-
0 => {
139-
panic!(
140-
"The system ran out of unique `{}`s.",
141-
stringify!($atomic_id_type)
142-
);
143-
}
144-
id => Self(id),
145-
}
137+
let counter = COUNTER.fetch_add(1, Ordering::Relaxed);
138+
Self(core::num::NonZeroU32::new(counter).unwrap_or_else(|| {
139+
panic!(
140+
"The system ran out of unique `{}`s.",
141+
stringify!($atomic_id_type)
142+
);
143+
}))
146144
}
147145
}
148146
};

crates/bevy_render/src/renderer/mod.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ pub use render_device::*;
88

99
use crate::{
1010
render_graph::RenderGraph,
11+
render_phase::TrackedRenderPass,
12+
render_resource::RenderPassDescriptor,
1113
settings::{WgpuSettings, WgpuSettingsPriority},
1214
view::{ExtractedWindows, ViewTarget},
1315
};
@@ -279,3 +281,17 @@ pub struct RenderContext {
279281
pub render_device: RenderDevice,
280282
pub command_encoder: CommandEncoder,
281283
}
284+
285+
impl RenderContext {
286+
/// Creates a new [`TrackedRenderPass`] for the context,
287+
/// configured using the provided `descriptor`.
288+
pub fn begin_tracked_render_pass<'a>(
289+
&'a mut self,
290+
descriptor: RenderPassDescriptor<'a, '_>,
291+
) -> TrackedRenderPass<'a> {
292+
TrackedRenderPass::new(
293+
&self.render_device,
294+
self.command_encoder.begin_render_pass(&descriptor),
295+
)
296+
}
297+
}

0 commit comments

Comments
 (0)