Skip to content

Commit e79106c

Browse files
committed
.
1 parent e610c53 commit e79106c

File tree

17 files changed

+1330
-210
lines changed

17 files changed

+1330
-210
lines changed

crates/bevy_core_pipeline/src/core_2d/main_opaque_pass_2d_node.rs

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::core_2d::Opaque2d;
2-
use bevy_ecs::{prelude::World, query::QueryItem};
2+
use bevy_ecs::{query::QueryItem, world::World};
33
use bevy_render::{
44
camera::ExtractedCamera,
55
diagnostic::RecordDiagnostics,
@@ -15,6 +15,94 @@ use tracing::info_span;
1515

1616
use super::AlphaMask2d;
1717

18+
// =============================================================================
19+
// Schedule-based system (new approach)
20+
// =============================================================================
21+
22+
use bevy_ecs::prelude::*;
23+
use bevy_render::renderer::{CurrentView, RenderContextParam};
24+
25+
/// Render system for the 2D opaque and alpha mask passes.
26+
///
27+
/// This is the schedule-based replacement for [`MainOpaquePass2dNode`].
28+
pub fn main_opaque_pass_2d(
29+
world: &World,
30+
view: CurrentView,
31+
view_query: Query<(
32+
&ExtractedCamera,
33+
&ExtractedView,
34+
&ViewTarget,
35+
&ViewDepthTexture,
36+
)>,
37+
opaque_phases: Res<ViewBinnedRenderPhases<Opaque2d>>,
38+
alpha_mask_phases: Res<ViewBinnedRenderPhases<AlphaMask2d>>,
39+
mut ctx: RenderContextParam,
40+
) {
41+
let view_entity = view.entity();
42+
43+
// Get view components
44+
let Ok((camera, extracted_view, target, depth)) = view_query.get(view_entity) else {
45+
return;
46+
};
47+
48+
// Get render phases
49+
let (Some(opaque_phase), Some(alpha_mask_phase)) = (
50+
opaque_phases.get(&extracted_view.retained_view_entity),
51+
alpha_mask_phases.get(&extracted_view.retained_view_entity),
52+
) else {
53+
return;
54+
};
55+
56+
// Skip if both phases are empty
57+
if opaque_phase.is_empty() && alpha_mask_phase.is_empty() {
58+
return;
59+
}
60+
61+
#[cfg(feature = "trace")]
62+
let _span = info_span!("main_opaque_pass_2d").entered();
63+
64+
// Build render pass descriptor from view components
65+
let color_attachments = [Some(target.get_color_attachment())];
66+
let depth_stencil_attachment = Some(depth.get_attachment(StoreOp::Store));
67+
68+
// Create tracked render pass via RenderContextParam
69+
let mut render_pass = ctx.begin_tracked_render_pass(RenderPassDescriptor {
70+
label: Some("main_opaque_pass_2d"),
71+
color_attachments: &color_attachments,
72+
depth_stencil_attachment,
73+
timestamp_writes: None,
74+
occlusion_query_set: None,
75+
});
76+
77+
if let Some(viewport) = camera.viewport.as_ref() {
78+
render_pass.set_camera_viewport(viewport);
79+
}
80+
81+
// Opaque draws
82+
if !opaque_phase.is_empty() {
83+
#[cfg(feature = "trace")]
84+
let _opaque_span = info_span!("opaque_main_pass_2d").entered();
85+
if let Err(err) = opaque_phase.render(&mut render_pass, world, view_entity) {
86+
error!("Error encountered while rendering the 2d opaque phase {err:?}");
87+
}
88+
}
89+
90+
// Alpha mask draws
91+
if !alpha_mask_phase.is_empty() {
92+
#[cfg(feature = "trace")]
93+
let _alpha_mask_span = info_span!("alpha_mask_main_pass_2d").entered();
94+
if let Err(err) = alpha_mask_phase.render(&mut render_pass, world, view_entity) {
95+
error!("Error encountered while rendering the 2d alpha mask phase {err:?}");
96+
}
97+
}
98+
99+
// Command buffer submission happens automatically via SystemBuffer::apply()
100+
}
101+
102+
// =============================================================================
103+
// Render graph node (legacy approach)
104+
// =============================================================================
105+
18106
/// A [`bevy_render::render_graph::Node`] that runs the
19107
/// [`Opaque2d`] [`ViewBinnedRenderPhases`] and [`AlphaMask2d`] [`ViewBinnedRenderPhases`]
20108
#[derive(Default)]

crates/bevy_core_pipeline/src/core_2d/main_transparent_pass_2d_node.rs

Lines changed: 92 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::core_2d::Transparent2d;
2-
use bevy_ecs::prelude::*;
2+
use bevy_ecs::{prelude::*, world::World};
33
use bevy_render::{
44
camera::ExtractedCamera,
55
diagnostic::RecordDiagnostics,
@@ -9,10 +9,101 @@ use bevy_render::{
99
renderer::RenderContext,
1010
view::{ExtractedView, ViewDepthTexture, ViewTarget},
1111
};
12+
use bevy_render::renderer::{CurrentView, RenderContextParam};
1213
use tracing::error;
1314
#[cfg(feature = "trace")]
1415
use tracing::info_span;
1516

17+
// =============================================================================
18+
// Schedule-based system (new approach)
19+
// =============================================================================
20+
21+
/// Render system for the 2D transparent pass.
22+
///
23+
/// This is the schedule-based replacement for [`MainTransparentPass2dNode`].
24+
pub fn main_transparent_pass_2d(
25+
world: &World,
26+
view: CurrentView,
27+
view_query: Query<(
28+
&ExtractedCamera,
29+
&ExtractedView,
30+
&ViewTarget,
31+
&ViewDepthTexture,
32+
)>,
33+
transparent_phases: Res<ViewSortedRenderPhases<Transparent2d>>,
34+
mut ctx: RenderContextParam,
35+
) {
36+
let view_entity = view.entity();
37+
38+
// Get view components
39+
let Ok((camera, extracted_view, target, depth)) = view_query.get(view_entity) else {
40+
return;
41+
};
42+
43+
// Get render phase
44+
let Some(transparent_phase) = transparent_phases.get(&extracted_view.retained_view_entity)
45+
else {
46+
return;
47+
};
48+
49+
#[cfg(feature = "trace")]
50+
let _span = info_span!("main_transparent_pass_2d").entered();
51+
52+
let color_attachments = [Some(target.get_color_attachment())];
53+
// NOTE: For the transparent pass we load the depth buffer. There should be no
54+
// need to write to it, but store is set to `true` as a workaround for issue #3776,
55+
// https://github.com/bevyengine/bevy/issues/3776
56+
// so that wgpu does not clear the depth buffer.
57+
// As the opaque and alpha mask passes run first, opaque meshes can occlude
58+
// transparent ones.
59+
let depth_stencil_attachment = Some(depth.get_attachment(StoreOp::Store));
60+
61+
// This needs to run at least once to clear the background color, even if there are no items to render
62+
{
63+
let mut render_pass = ctx.begin_tracked_render_pass(RenderPassDescriptor {
64+
label: Some("main_transparent_pass_2d"),
65+
color_attachments: &color_attachments,
66+
depth_stencil_attachment,
67+
timestamp_writes: None,
68+
occlusion_query_set: None,
69+
});
70+
71+
if let Some(viewport) = camera.viewport.as_ref() {
72+
render_pass.set_camera_viewport(viewport);
73+
}
74+
75+
if !transparent_phase.items.is_empty() {
76+
#[cfg(feature = "trace")]
77+
let _transparent_span = info_span!("transparent_main_pass_2d").entered();
78+
if let Err(err) = transparent_phase.render(&mut render_pass, world, view_entity) {
79+
error!("Error encountered while rendering the transparent 2D phase {err:?}");
80+
}
81+
}
82+
}
83+
84+
// WebGL2 quirk: if ending with a render pass with a custom viewport, the viewport isn't
85+
// reset for the next render pass so add an empty render pass without a custom viewport
86+
#[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
87+
if camera.viewport.is_some() {
88+
#[cfg(feature = "trace")]
89+
let _reset_viewport_pass_2d = info_span!("reset_viewport_pass_2d").entered();
90+
91+
let _reset_pass = ctx.begin_tracked_render_pass(RenderPassDescriptor {
92+
label: Some("reset_viewport_pass_2d"),
93+
color_attachments: &[Some(target.get_color_attachment())],
94+
depth_stencil_attachment: None,
95+
timestamp_writes: None,
96+
occlusion_query_set: None,
97+
});
98+
}
99+
100+
// Command buffer submission happens automatically via SystemBuffer::apply()
101+
}
102+
103+
// =============================================================================
104+
// Render graph node (legacy approach)
105+
// =============================================================================
106+
16107
#[derive(Default)]
17108
pub struct MainTransparentPass2dNode {}
18109

crates/bevy_core_pipeline/src/core_2d/mod.rs

Lines changed: 8 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -46,17 +46,14 @@ use bevy_render::{
4646
pub use main_opaque_pass_2d_node::*;
4747
pub use main_transparent_pass_2d_node::*;
4848

49-
use crate::{
50-
tonemapping::{DebandDither, Tonemapping, TonemappingNode},
51-
upscaling::UpscalingNode,
52-
};
49+
use crate::schedule::Core2dSchedule;
50+
use crate::tonemapping::{DebandDither, Tonemapping};
5351
use bevy_app::{App, Plugin};
5452
use bevy_ecs::prelude::*;
5553
use bevy_math::FloatOrd;
5654
use bevy_render::{
5755
camera::ExtractedCamera,
5856
extract_component::ExtractComponentPlugin,
59-
render_graph::{EmptyNode, RenderGraphExt, ViewNodeRunner},
6057
render_phase::{
6158
sort_phase_system, BinnedPhaseItem, CachedRenderPipelinePhaseItem, DrawFunctionId,
6259
DrawFunctions, PhaseItem, PhaseItemExtraIndex, SortedPhaseItem, ViewBinnedRenderPhases,
@@ -73,8 +70,6 @@ use bevy_render::{
7370
Extract, ExtractSchedule, Render, RenderApp, RenderSystems,
7471
};
7572

76-
use self::graph::{Core2d, Node2d};
77-
7873
pub const CORE_2D_DEPTH_FORMAT: TextureFormat = TextureFormat::Depth32Float;
7974

8075
pub struct Core2dPlugin;
@@ -83,7 +78,7 @@ impl Plugin for Core2dPlugin {
8378
fn build(&self, app: &mut App) {
8479
app.register_required_components::<Camera2d, DebandDither>()
8580
.register_required_components_with::<Camera2d, CameraRenderGraph>(|| {
86-
CameraRenderGraph::new(Core2d)
81+
CameraRenderGraph::new(Core2dSchedule)
8782
})
8883
.register_required_components_with::<Camera2d, Tonemapping>(|| Tonemapping::None)
8984
.add_plugins(ExtractComponentPlugin::<Camera2d>::default());
@@ -107,35 +102,11 @@ impl Plugin for Core2dPlugin {
107102
),
108103
);
109104

110-
render_app
111-
.add_render_sub_graph(Core2d)
112-
.add_render_graph_node::<EmptyNode>(Core2d, Node2d::StartMainPass)
113-
.add_render_graph_node::<ViewNodeRunner<MainOpaquePass2dNode>>(
114-
Core2d,
115-
Node2d::MainOpaquePass,
116-
)
117-
.add_render_graph_node::<ViewNodeRunner<MainTransparentPass2dNode>>(
118-
Core2d,
119-
Node2d::MainTransparentPass,
120-
)
121-
.add_render_graph_node::<EmptyNode>(Core2d, Node2d::EndMainPass)
122-
.add_render_graph_node::<EmptyNode>(Core2d, Node2d::StartMainPassPostProcessing)
123-
.add_render_graph_node::<ViewNodeRunner<TonemappingNode>>(Core2d, Node2d::Tonemapping)
124-
.add_render_graph_node::<EmptyNode>(Core2d, Node2d::EndMainPassPostProcessing)
125-
.add_render_graph_node::<ViewNodeRunner<UpscalingNode>>(Core2d, Node2d::Upscaling)
126-
.add_render_graph_edges(
127-
Core2d,
128-
(
129-
Node2d::StartMainPass,
130-
Node2d::MainOpaquePass,
131-
Node2d::MainTransparentPass,
132-
Node2d::EndMainPass,
133-
Node2d::StartMainPassPostProcessing,
134-
Node2d::Tonemapping,
135-
Node2d::EndMainPassPostProcessing,
136-
Node2d::Upscaling,
137-
),
138-
);
105+
// Register the schedule-based render pipeline
106+
crate::schedule::register_core_2d_schedule(render_app);
107+
108+
// Note: The render graph (Core2d sub-graph) has been replaced by Core2dSchedule.
109+
// The schedule is run by camera_driver for each 2D camera.
139110
}
140111
}
141112

crates/bevy_core_pipeline/src/core_3d/mod.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ use crate::{
128128
Opaque3dPrepass, OpaqueNoLightmap3dBatchSetKey, OpaqueNoLightmap3dBinKey,
129129
ViewPrepassTextures, MOTION_VECTOR_PREPASS_FORMAT, NORMAL_PREPASS_FORMAT,
130130
},
131+
schedule::Core3dSchedule,
131132
skybox::SkyboxPlugin,
132133
tonemapping::{DebandDither, Tonemapping, TonemappingNode},
133134
upscaling::UpscalingNode,
@@ -141,7 +142,7 @@ impl Plugin for Core3dPlugin {
141142
fn build(&self, app: &mut App) {
142143
app.register_required_components_with::<Camera3d, DebandDither>(|| DebandDither::Enabled)
143144
.register_required_components_with::<Camera3d, CameraRenderGraph>(|| {
144-
CameraRenderGraph::new(Core3d)
145+
CameraRenderGraph::new(Core3dSchedule)
145146
})
146147
.register_required_components::<Camera3d, Tonemapping>()
147148
.add_plugins((SkyboxPlugin, ExtractComponentPlugin::<Camera3d>::default()))
@@ -183,6 +184,9 @@ impl Plugin for Core3dPlugin {
183184
),
184185
);
185186

187+
// Register the schedule-based render pipeline (for future migration)
188+
crate::schedule::register_core_3d_schedule(render_app);
189+
186190
render_app
187191
.add_render_sub_graph(Core3d)
188192
.add_render_graph_node::<ViewNodeRunner<EarlyPrepassNode>>(Core3d, Node3d::EarlyPrepass)

crates/bevy_core_pipeline/src/lib.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,15 @@ pub mod deferred;
1313
pub mod experimental;
1414
pub mod oit;
1515
pub mod prepass;
16+
pub mod schedule;
1617
pub mod tonemapping;
1718
pub mod upscaling;
1819

1920
pub use fullscreen_vertex_shader::FullscreenShader;
21+
pub use schedule::{
22+
register_core_2d_schedule, register_core_3d_schedule, register_main_render_schedule,
23+
Core2dSchedule, Core2dSystems, Core3dSchedule, Core3dSystems, MainRenderSchedule,
24+
};
2025
pub use skybox::Skybox;
2126

2227
mod fullscreen_vertex_shader;
@@ -52,5 +57,9 @@ impl Plugin for CorePipelinePlugin {
5257
return;
5358
};
5459
render_app.init_resource::<FullscreenShader>();
60+
61+
// Register the main render schedule which contains `camera_driver`.
62+
// This schedule is run by `render_system` and replaces the main render graph.
63+
register_main_render_schedule(render_app);
5564
}
5665
}

0 commit comments

Comments
 (0)