Skip to content

Commit 0c3b92f

Browse files
committed
.
1 parent 2314a89 commit 0c3b92f

File tree

6 files changed

+574
-10
lines changed

6 files changed

+574
-10
lines changed

crates/bevy_core_pipeline/src/core_3d/main_opaque_pass_3d_node.rs

Lines changed: 104 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@ use crate::{
33
skybox::{SkyboxBindGroup, SkyboxPipelineId},
44
};
55
use bevy_camera::{MainPassResolutionOverride, Viewport};
6-
use bevy_ecs::{prelude::World, query::QueryItem};
6+
use bevy_ecs::{prelude::*, query::QueryItem};
77
use bevy_render::{
88
camera::ExtractedCamera,
99
diagnostic::RecordDiagnostics,
1010
render_graph::{NodeRunError, RenderGraphContext, ViewNode},
1111
render_phase::{TrackedRenderPass, ViewBinnedRenderPhases},
1212
render_resource::{CommandEncoderDescriptor, PipelineCache, RenderPassDescriptor, StoreOp},
13-
renderer::RenderContext,
13+
renderer::{CurrentView, RenderContext, RenderContextParam},
1414
view::{ExtractedView, ViewDepthTexture, ViewTarget, ViewUniformOffset},
1515
};
1616
use tracing::error;
@@ -140,3 +140,105 @@ impl ViewNode for MainOpaquePass3dNode {
140140
Ok(())
141141
}
142142
}
143+
144+
// =============================================================================
145+
// Schedule-based system (new approach)
146+
// =============================================================================
147+
148+
/// Render system for the main 3D opaque pass.
149+
///
150+
/// This is the schedule-based replacement for [`MainOpaquePass3dNode`].
151+
pub fn main_opaque_pass_3d(
152+
world: &World,
153+
view: CurrentView,
154+
view_query: Query<(
155+
&ExtractedCamera,
156+
&ExtractedView,
157+
&ViewTarget,
158+
&ViewDepthTexture,
159+
Option<&SkyboxPipelineId>,
160+
Option<&SkyboxBindGroup>,
161+
&ViewUniformOffset,
162+
Option<&MainPassResolutionOverride>,
163+
)>,
164+
opaque_phases: Res<ViewBinnedRenderPhases<Opaque3d>>,
165+
alpha_mask_phases: Res<ViewBinnedRenderPhases<AlphaMask3d>>,
166+
pipeline_cache: Res<PipelineCache>,
167+
mut ctx: RenderContextParam,
168+
) {
169+
let view_entity = view.entity();
170+
171+
let Ok((
172+
camera,
173+
extracted_view,
174+
target,
175+
depth,
176+
skybox_pipeline,
177+
skybox_bind_group,
178+
view_uniform_offset,
179+
resolution_override,
180+
)) = view_query.get(view_entity)
181+
else {
182+
return;
183+
};
184+
185+
let (Some(opaque_phase), Some(alpha_mask_phase)) = (
186+
opaque_phases.get(&extracted_view.retained_view_entity),
187+
alpha_mask_phases.get(&extracted_view.retained_view_entity),
188+
) else {
189+
return;
190+
};
191+
192+
#[cfg(feature = "trace")]
193+
let _main_opaque_pass_3d_span = info_span!("main_opaque_pass_3d").entered();
194+
195+
let color_attachments = [Some(target.get_color_attachment())];
196+
let depth_stencil_attachment = Some(depth.get_attachment(StoreOp::Store));
197+
198+
let mut render_pass = ctx.begin_tracked_render_pass(RenderPassDescriptor {
199+
label: Some("main_opaque_pass_3d"),
200+
color_attachments: &color_attachments,
201+
depth_stencil_attachment,
202+
timestamp_writes: None,
203+
occlusion_query_set: None,
204+
});
205+
206+
if let Some(viewport) =
207+
Viewport::from_viewport_and_override(camera.viewport.as_ref(), resolution_override)
208+
{
209+
render_pass.set_camera_viewport(&viewport);
210+
}
211+
212+
// Opaque draws
213+
if !opaque_phase.is_empty() {
214+
#[cfg(feature = "trace")]
215+
let _opaque_main_pass_3d_span = info_span!("opaque_main_pass_3d").entered();
216+
if let Err(err) = opaque_phase.render(&mut render_pass, world, view_entity) {
217+
error!("Error encountered while rendering the opaque phase {err:?}");
218+
}
219+
}
220+
221+
// Alpha draws
222+
if !alpha_mask_phase.is_empty() {
223+
#[cfg(feature = "trace")]
224+
let _alpha_mask_main_pass_3d_span = info_span!("alpha_mask_main_pass_3d").entered();
225+
if let Err(err) = alpha_mask_phase.render(&mut render_pass, world, view_entity) {
226+
error!("Error encountered while rendering the alpha mask phase {err:?}");
227+
}
228+
}
229+
230+
// Skybox draw using a fullscreen triangle
231+
if let (Some(skybox_pipeline), Some(SkyboxBindGroup(skybox_bind_group))) =
232+
(skybox_pipeline, skybox_bind_group)
233+
{
234+
if let Some(pipeline) = pipeline_cache.get_render_pipeline(skybox_pipeline.0) {
235+
render_pass.set_render_pipeline(pipeline);
236+
render_pass.set_bind_group(
237+
0,
238+
&skybox_bind_group.0,
239+
&[view_uniform_offset.offset, skybox_bind_group.1],
240+
);
241+
render_pass.draw(0..3, 0..1);
242+
}
243+
}
244+
}

crates/bevy_core_pipeline/src/core_3d/main_transmissive_pass_3d_node.rs

Lines changed: 101 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use bevy_render::{
99
render_graph::{NodeRunError, RenderGraphContext, ViewNode},
1010
render_phase::ViewSortedRenderPhases,
1111
render_resource::{RenderPassDescriptor, StoreOp},
12-
renderer::RenderContext,
12+
renderer::{CurrentView, RenderContext, RenderContextParam},
1313
view::{ExtractedView, ViewDepthTexture, ViewTarget},
1414
};
1515
use core::ops::Range;
@@ -165,3 +165,103 @@ fn split_range(range: Range<usize>, max_num_splits: usize) -> impl Iterator<Item
165165
result
166166
})
167167
}
168+
169+
// =============================================================================
170+
// Schedule-based system (new approach)
171+
// =============================================================================
172+
173+
/// Render system for the main 3D transmissive pass.
174+
///
175+
/// This is the schedule-based replacement for [`MainTransmissivePass3dNode`].
176+
pub fn main_transmissive_pass_3d(
177+
world: &World,
178+
view: CurrentView,
179+
view_query: Query<(
180+
&ExtractedCamera,
181+
&ExtractedView,
182+
&Camera3d,
183+
&ViewTarget,
184+
Option<&ViewTransmissionTexture>,
185+
&ViewDepthTexture,
186+
Option<&MainPassResolutionOverride>,
187+
)>,
188+
transmissive_phases: Res<ViewSortedRenderPhases<Transmissive3d>>,
189+
mut ctx: RenderContextParam,
190+
) {
191+
let view_entity = view.entity();
192+
193+
let Ok((camera, extracted_view, camera_3d, target, transmission, depth, resolution_override)) =
194+
view_query.get(view_entity)
195+
else {
196+
return;
197+
};
198+
199+
let Some(transmissive_phase) = transmissive_phases.get(&extracted_view.retained_view_entity)
200+
else {
201+
return;
202+
};
203+
204+
let Some(physical_target_size) = camera.physical_target_size else {
205+
return;
206+
};
207+
208+
#[cfg(feature = "trace")]
209+
let _main_transmissive_pass_3d_span = info_span!("main_transmissive_pass_3d").entered();
210+
211+
let render_pass_descriptor = RenderPassDescriptor {
212+
label: Some("main_transmissive_pass_3d"),
213+
color_attachments: &[Some(target.get_color_attachment())],
214+
depth_stencil_attachment: Some(depth.get_attachment(StoreOp::Store)),
215+
timestamp_writes: None,
216+
occlusion_query_set: None,
217+
};
218+
219+
if !transmissive_phase.items.is_empty() {
220+
let screen_space_specular_transmission_steps =
221+
camera_3d.screen_space_specular_transmission_steps;
222+
if screen_space_specular_transmission_steps > 0 {
223+
let transmission =
224+
transmission.expect("`ViewTransmissionTexture` should exist at this point");
225+
226+
// `transmissive_phase.items` are depth sorted, so we split them into N = `screen_space_specular_transmission_steps`
227+
// ranges, rendering them back-to-front in multiple steps, allowing multiple levels of transparency.
228+
for range in split_range(
229+
0..transmissive_phase.items.len(),
230+
screen_space_specular_transmission_steps,
231+
) {
232+
// Copy the main texture to the transmission texture
233+
ctx.command_encoder().copy_texture_to_texture(
234+
target.main_texture().as_image_copy(),
235+
transmission.texture.as_image_copy(),
236+
physical_target_size.to_extents(),
237+
);
238+
239+
let mut render_pass =
240+
ctx.begin_tracked_render_pass(render_pass_descriptor.clone());
241+
242+
if let Some(viewport) = camera.viewport.as_ref() {
243+
render_pass.set_camera_viewport(viewport);
244+
}
245+
246+
// render items in range
247+
if let Err(err) =
248+
transmissive_phase.render_range(&mut render_pass, world, view_entity, range)
249+
{
250+
error!("Error encountered while rendering the transmissive phase {err:?}");
251+
}
252+
}
253+
} else {
254+
let mut render_pass = ctx.begin_tracked_render_pass(render_pass_descriptor);
255+
256+
if let Some(viewport) =
257+
Viewport::from_viewport_and_override(camera.viewport.as_ref(), resolution_override)
258+
{
259+
render_pass.set_camera_viewport(&viewport);
260+
}
261+
262+
if let Err(err) = transmissive_phase.render(&mut render_pass, world, view_entity) {
263+
error!("Error encountered while rendering the transmissive phase {err:?}");
264+
}
265+
}
266+
}
267+
}

crates/bevy_core_pipeline/src/core_3d/main_transparent_pass_3d_node.rs

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use bevy_render::{
77
render_graph::{NodeRunError, RenderGraphContext, ViewNode},
88
render_phase::ViewSortedRenderPhases,
99
render_resource::{RenderPassDescriptor, StoreOp},
10-
renderer::RenderContext,
10+
renderer::{CurrentView, RenderContext, RenderContextParam},
1111
view::{ExtractedView, ViewDepthTexture, ViewTarget},
1212
};
1313
use tracing::error;
@@ -105,3 +105,83 @@ impl ViewNode for MainTransparentPass3dNode {
105105
Ok(())
106106
}
107107
}
108+
109+
// =============================================================================
110+
// Schedule-based system (new approach)
111+
// =============================================================================
112+
113+
/// Render system for the main 3D transparent pass.
114+
///
115+
/// This is the schedule-based replacement for [`MainTransparentPass3dNode`].
116+
pub fn main_transparent_pass_3d(
117+
world: &World,
118+
view: CurrentView,
119+
view_query: Query<(
120+
&ExtractedCamera,
121+
&ExtractedView,
122+
&ViewTarget,
123+
&ViewDepthTexture,
124+
Option<&MainPassResolutionOverride>,
125+
)>,
126+
transparent_phases: Res<ViewSortedRenderPhases<Transparent3d>>,
127+
mut ctx: RenderContextParam,
128+
) {
129+
let view_entity = view.entity();
130+
131+
let Ok((camera, extracted_view, target, depth, resolution_override)) =
132+
view_query.get(view_entity)
133+
else {
134+
return;
135+
};
136+
137+
let Some(transparent_phase) = transparent_phases.get(&extracted_view.retained_view_entity)
138+
else {
139+
return;
140+
};
141+
142+
if !transparent_phase.items.is_empty() {
143+
#[cfg(feature = "trace")]
144+
let _main_transparent_pass_3d_span = info_span!("main_transparent_pass_3d").entered();
145+
146+
let mut render_pass = ctx.begin_tracked_render_pass(RenderPassDescriptor {
147+
label: Some("main_transparent_pass_3d"),
148+
color_attachments: &[Some(target.get_color_attachment())],
149+
// NOTE: For the transparent pass we load the depth buffer. There should be no
150+
// need to write to it, but store is set to `true` as a workaround for issue #3776,
151+
// https://github.com/bevyengine/bevy/issues/3776
152+
// so that wgpu does not clear the depth buffer.
153+
// As the opaque and alpha mask passes run first, opaque meshes can occlude
154+
// transparent ones.
155+
depth_stencil_attachment: Some(depth.get_attachment(StoreOp::Store)),
156+
timestamp_writes: None,
157+
occlusion_query_set: None,
158+
});
159+
160+
if let Some(viewport) =
161+
Viewport::from_viewport_and_override(camera.viewport.as_ref(), resolution_override)
162+
{
163+
render_pass.set_camera_viewport(&viewport);
164+
}
165+
166+
if let Err(err) = transparent_phase.render(&mut render_pass, world, view_entity) {
167+
error!("Error encountered while rendering the transparent phase {err:?}");
168+
}
169+
}
170+
171+
// WebGL2 quirk: if ending with a render pass with a custom viewport, the viewport isn't
172+
// reset for the next render pass so add an empty render pass without a custom viewport
173+
#[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
174+
if camera.viewport.is_some() {
175+
#[cfg(feature = "trace")]
176+
let _reset_viewport_pass_3d = info_span!("reset_viewport_pass_3d").entered();
177+
let pass_descriptor = RenderPassDescriptor {
178+
label: Some("reset_viewport_pass_3d"),
179+
color_attachments: &[Some(target.get_color_attachment())],
180+
depth_stencil_attachment: None,
181+
timestamp_writes: None,
182+
occlusion_query_set: None,
183+
};
184+
185+
ctx.command_encoder().begin_render_pass(&pass_descriptor);
186+
}
187+
}

crates/bevy_core_pipeline/src/core_3d/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ use bevy_render::{
8282
view::{prepare_view_targets, NoIndirectDrawing, RetainedViewEntity},
8383
};
8484
pub use main_opaque_pass_3d_node::*;
85+
pub use main_transmissive_pass_3d_node::*;
8586
pub use main_transparent_pass_3d_node::*;
8687

8788
use bevy_app::{App, Plugin, PostUpdate};

0 commit comments

Comments
 (0)