Skip to content

Commit 89579d2

Browse files
committed
.
1 parent 0c3b92f commit 89579d2

File tree

2 files changed

+307
-2
lines changed

2 files changed

+307
-2
lines changed

crates/bevy_core_pipeline/src/deferred/copy_lighting_id.rs

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use super::DEFERRED_LIGHTING_PASS_ID_DEPTH_FORMAT;
2020
use bevy_ecs::query::QueryItem;
2121
use bevy_render::{
2222
render_graph::{NodeRunError, RenderGraphContext, ViewNode},
23-
renderer::RenderContext,
23+
renderer::{CurrentView, RenderContext, RenderContextParam},
2424
};
2525
use bevy_utils::default;
2626

@@ -190,3 +190,62 @@ fn prepare_deferred_lighting_id_textures(
190190
}
191191
}
192192
}
193+
194+
// =============================================================================
195+
// Schedule-based system (new approach)
196+
// =============================================================================
197+
198+
/// Render system that copies the deferred lighting pass ID to a depth texture.
199+
///
200+
/// This is the schedule-based replacement for [`CopyDeferredLightingIdNode`].
201+
pub fn copy_deferred_lighting_id(
202+
view: CurrentView,
203+
view_query: Query<(
204+
&ViewTarget,
205+
&ViewPrepassTextures,
206+
&DeferredLightingIdDepthTexture,
207+
)>,
208+
copy_pipeline: Res<CopyDeferredLightingIdPipeline>,
209+
pipeline_cache: Res<PipelineCache>,
210+
mut ctx: RenderContextParam,
211+
) {
212+
let view_entity = view.entity();
213+
let Ok((_view_target, view_prepass_textures, deferred_lighting_id_depth_texture)) =
214+
view_query.get(view_entity)
215+
else {
216+
return;
217+
};
218+
219+
let Some(pipeline) = pipeline_cache.get_render_pipeline(copy_pipeline.pipeline_id) else {
220+
return;
221+
};
222+
let Some(deferred_lighting_pass_id_texture) = &view_prepass_textures.deferred_lighting_pass_id
223+
else {
224+
return;
225+
};
226+
227+
let bind_group = ctx.render_device().create_bind_group(
228+
"copy_deferred_lighting_id_bind_group",
229+
&pipeline_cache.get_bind_group_layout(&copy_pipeline.layout),
230+
&BindGroupEntries::single(&deferred_lighting_pass_id_texture.texture.default_view),
231+
);
232+
233+
let mut render_pass = ctx.begin_tracked_render_pass(RenderPassDescriptor {
234+
label: Some("copy_deferred_lighting_id"),
235+
color_attachments: &[],
236+
depth_stencil_attachment: Some(RenderPassDepthStencilAttachment {
237+
view: &deferred_lighting_id_depth_texture.texture.default_view,
238+
depth_ops: Some(Operations {
239+
load: LoadOp::Clear(0.0),
240+
store: StoreOp::Store,
241+
}),
242+
stencil_ops: None,
243+
}),
244+
timestamp_writes: None,
245+
occlusion_query_set: None,
246+
});
247+
248+
render_pass.set_render_pipeline(pipeline);
249+
render_pass.set_bind_group(0, &bind_group, &[]);
250+
render_pass.draw(0..3, 0..1);
251+
}

crates/bevy_core_pipeline/src/deferred/node.rs

Lines changed: 247 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use bevy_render::{
1010
render_graph::{NodeRunError, RenderGraphContext},
1111
render_phase::{TrackedRenderPass, ViewBinnedRenderPhases},
1212
render_resource::{CommandEncoderDescriptor, RenderPassDescriptor, StoreOp},
13-
renderer::RenderContext,
13+
renderer::{CurrentView, RenderContext, RenderContextParam},
1414
view::ViewDepthTexture,
1515
};
1616
use tracing::error;
@@ -271,3 +271,249 @@ fn run_deferred_prepass<'w>(
271271

272272
Ok(())
273273
}
274+
275+
// =============================================================================
276+
// Schedule-based systems (new approach)
277+
// =============================================================================
278+
279+
/// Type alias for the deferred prepass view query.
280+
type DeferredPrepassViewQuery = (
281+
&'static ExtractedCamera,
282+
&'static ExtractedView,
283+
&'static ViewDepthTexture,
284+
&'static ViewPrepassTextures,
285+
Option<&'static MainPassResolutionOverride>,
286+
Has<OcclusionCulling>,
287+
Has<NoIndirectDrawing>,
288+
);
289+
290+
/// Render system for the early deferred G-buffer prepass.
291+
///
292+
/// This is the schedule-based replacement for [`EarlyDeferredGBufferPrepassNode`].
293+
/// It draws meshes that were visible last frame to the G-buffer.
294+
/// If occlusion culling isn't in use, this simply draws all meshes.
295+
pub fn early_deferred_prepass(
296+
world: &World,
297+
view: CurrentView,
298+
view_query: Query<DeferredPrepassViewQuery>,
299+
opaque_deferred_phases: Res<ViewBinnedRenderPhases<Opaque3dDeferred>>,
300+
alpha_mask_deferred_phases: Res<ViewBinnedRenderPhases<AlphaMask3dDeferred>>,
301+
mut ctx: RenderContextParam,
302+
) {
303+
let view_entity = view.entity();
304+
let Ok((camera, extracted_view, view_depth_texture, view_prepass_textures, resolution_override, _, _)) =
305+
view_query.get(view_entity)
306+
else {
307+
return;
308+
};
309+
310+
run_deferred_prepass_system(
311+
world,
312+
view_entity,
313+
camera,
314+
extracted_view,
315+
view_depth_texture,
316+
view_prepass_textures,
317+
resolution_override,
318+
false,
319+
&opaque_deferred_phases,
320+
&alpha_mask_deferred_phases,
321+
&mut ctx,
322+
"early deferred prepass",
323+
);
324+
}
325+
326+
/// Render system for the late deferred G-buffer prepass.
327+
///
328+
/// This is the schedule-based replacement for [`LateDeferredGBufferPrepassNode`].
329+
/// It runs after occlusion culling against meshes that were visible last frame.
330+
/// If occlusion culling isn't in use, this is a no-op.
331+
pub fn late_deferred_prepass(
332+
world: &World,
333+
view: CurrentView,
334+
view_query: Query<DeferredPrepassViewQuery>,
335+
opaque_deferred_phases: Res<ViewBinnedRenderPhases<Opaque3dDeferred>>,
336+
alpha_mask_deferred_phases: Res<ViewBinnedRenderPhases<AlphaMask3dDeferred>>,
337+
mut ctx: RenderContextParam,
338+
) {
339+
let view_entity = view.entity();
340+
let Ok((
341+
camera,
342+
extracted_view,
343+
view_depth_texture,
344+
view_prepass_textures,
345+
resolution_override,
346+
occlusion_culling,
347+
no_indirect_drawing,
348+
)) = view_query.get(view_entity)
349+
else {
350+
return;
351+
};
352+
353+
// We only need a late prepass if we have occlusion culling and indirect drawing
354+
if !occlusion_culling || no_indirect_drawing {
355+
return;
356+
}
357+
358+
run_deferred_prepass_system(
359+
world,
360+
view_entity,
361+
camera,
362+
extracted_view,
363+
view_depth_texture,
364+
view_prepass_textures,
365+
resolution_override,
366+
true,
367+
&opaque_deferred_phases,
368+
&alpha_mask_deferred_phases,
369+
&mut ctx,
370+
"late deferred prepass",
371+
);
372+
}
373+
374+
/// Shared implementation for deferred prepass systems.
375+
#[allow(clippy::too_many_arguments)]
376+
fn run_deferred_prepass_system(
377+
world: &World,
378+
view_entity: Entity,
379+
camera: &ExtractedCamera,
380+
extracted_view: &ExtractedView,
381+
view_depth_texture: &ViewDepthTexture,
382+
view_prepass_textures: &ViewPrepassTextures,
383+
resolution_override: Option<&MainPassResolutionOverride>,
384+
is_late: bool,
385+
opaque_deferred_phases: &ViewBinnedRenderPhases<Opaque3dDeferred>,
386+
alpha_mask_deferred_phases: &ViewBinnedRenderPhases<AlphaMask3dDeferred>,
387+
ctx: &mut RenderContextParam,
388+
label: &'static str,
389+
) {
390+
let (Some(opaque_deferred_phase), Some(alpha_mask_deferred_phase)) = (
391+
opaque_deferred_phases.get(&extracted_view.retained_view_entity),
392+
alpha_mask_deferred_phases.get(&extracted_view.retained_view_entity),
393+
) else {
394+
return;
395+
};
396+
397+
#[cfg(feature = "trace")]
398+
let _deferred_span = info_span!("deferred_prepass").entered();
399+
400+
let mut color_attachments = vec![];
401+
color_attachments.push(
402+
view_prepass_textures
403+
.normal
404+
.as_ref()
405+
.map(|normals_texture| normals_texture.get_attachment()),
406+
);
407+
color_attachments.push(
408+
view_prepass_textures
409+
.motion_vectors
410+
.as_ref()
411+
.map(|motion_vectors_texture| motion_vectors_texture.get_attachment()),
412+
);
413+
414+
// If we clear the deferred texture with LoadOp::Clear(Default::default()) we get these errors:
415+
// Chrome: GL_INVALID_OPERATION: No defined conversion between clear value and attachment format.
416+
// Firefox: WebGL warning: clearBufferu?[fi]v: This attachment is of type FLOAT, but this function is of type UINT.
417+
// Appears to be unsupported: https://registry.khronos.org/webgl/specs/latest/2.0/#3.7.9
418+
// For webgl2 we fallback to manually clearing
419+
#[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
420+
if !is_late {
421+
if let Some(deferred_texture) = &view_prepass_textures.deferred {
422+
ctx.command_encoder().clear_texture(
423+
&deferred_texture.texture.texture,
424+
&bevy_render::render_resource::ImageSubresourceRange::default(),
425+
);
426+
}
427+
}
428+
429+
color_attachments.push(
430+
view_prepass_textures
431+
.deferred
432+
.as_ref()
433+
.map(|deferred_texture| {
434+
if is_late {
435+
deferred_texture.get_attachment()
436+
} else {
437+
#[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
438+
{
439+
bevy_render::render_resource::RenderPassColorAttachment {
440+
view: &deferred_texture.texture.default_view,
441+
resolve_target: None,
442+
ops: bevy_render::render_resource::Operations {
443+
load: bevy_render::render_resource::LoadOp::Load,
444+
store: StoreOp::Store,
445+
},
446+
depth_slice: None,
447+
}
448+
}
449+
#[cfg(any(
450+
not(feature = "webgl"),
451+
not(target_arch = "wasm32"),
452+
feature = "webgpu"
453+
))]
454+
deferred_texture.get_attachment()
455+
}
456+
}),
457+
);
458+
459+
color_attachments.push(
460+
view_prepass_textures
461+
.deferred_lighting_pass_id
462+
.as_ref()
463+
.map(|deferred_lighting_pass_id| deferred_lighting_pass_id.get_attachment()),
464+
);
465+
466+
// If all color attachments are none: clear the color attachment list so that no fragment shader is required
467+
if color_attachments.iter().all(Option::is_none) {
468+
color_attachments.clear();
469+
}
470+
471+
let depth_stencil_attachment = Some(view_depth_texture.get_attachment(StoreOp::Store));
472+
473+
let mut render_pass = ctx.begin_tracked_render_pass(RenderPassDescriptor {
474+
label: Some(label),
475+
color_attachments: &color_attachments,
476+
depth_stencil_attachment,
477+
timestamp_writes: None,
478+
occlusion_query_set: None,
479+
});
480+
481+
if let Some(viewport) =
482+
Viewport::from_viewport_and_override(camera.viewport.as_ref(), resolution_override)
483+
{
484+
render_pass.set_camera_viewport(&viewport);
485+
}
486+
487+
// Opaque draws
488+
if !opaque_deferred_phase.multidrawable_meshes.is_empty()
489+
|| !opaque_deferred_phase.batchable_meshes.is_empty()
490+
|| !opaque_deferred_phase.unbatchable_meshes.is_empty()
491+
{
492+
#[cfg(feature = "trace")]
493+
let _opaque_prepass_span = info_span!("opaque_deferred_prepass").entered();
494+
if let Err(err) = opaque_deferred_phase.render(&mut render_pass, world, view_entity) {
495+
error!("Error encountered while rendering the opaque deferred phase {err:?}");
496+
}
497+
}
498+
499+
// Alpha masked draws
500+
if !alpha_mask_deferred_phase.is_empty() {
501+
#[cfg(feature = "trace")]
502+
let _alpha_mask_deferred_span = info_span!("alpha_mask_deferred_prepass").entered();
503+
if let Err(err) = alpha_mask_deferred_phase.render(&mut render_pass, world, view_entity) {
504+
error!("Error encountered while rendering the alpha mask deferred phase {err:?}");
505+
}
506+
}
507+
508+
// Drop the render pass before the texture copy
509+
drop(render_pass);
510+
511+
// After rendering to the view depth texture, copy it to the prepass depth texture
512+
if let Some(prepass_depth_texture) = &view_prepass_textures.depth {
513+
ctx.command_encoder().copy_texture_to_texture(
514+
view_depth_texture.texture.as_image_copy(),
515+
prepass_depth_texture.texture.texture.as_image_copy(),
516+
view_prepass_textures.size,
517+
);
518+
}
519+
}

0 commit comments

Comments
 (0)