Implement occlusion culling for the deferred rendering pipeline. (#17934)
Deferred rendering currently doesn't support occlusion culling. This PR implements it in a straightforward way, mirroring what we already do for the non-deferred pipeline. On the rend3 sci-fi base test scene, this resulted in roughly a 2× speedup when applied on top of my other patches. For that scene, it was useful to add another option, `--add-light`, which forces the addition of a shadow-casting light, to the scene viewer, which I included in this patch.
This commit is contained in:
parent
f15437e4dc
commit
8de6b16e9d
@ -19,7 +19,8 @@ pub mod graph {
|
||||
EarlyPrepass,
|
||||
EarlyDownsampleDepth,
|
||||
LatePrepass,
|
||||
DeferredPrepass,
|
||||
EarlyDeferredPrepass,
|
||||
LateDeferredPrepass,
|
||||
CopyDeferredLightingId,
|
||||
EndPrepasses,
|
||||
StartMainPass,
|
||||
@ -112,7 +113,8 @@ use tracing::warn;
|
||||
use crate::{
|
||||
core_3d::main_transmissive_pass_3d_node::MainTransmissivePass3dNode,
|
||||
deferred::{
|
||||
copy_lighting_id::CopyDeferredLightingIdNode, node::DeferredGBufferPrepassNode,
|
||||
copy_lighting_id::CopyDeferredLightingIdNode,
|
||||
node::{EarlyDeferredGBufferPrepassNode, LateDeferredGBufferPrepassNode},
|
||||
AlphaMask3dDeferred, Opaque3dDeferred, DEFERRED_LIGHTING_PASS_ID_FORMAT,
|
||||
DEFERRED_PREPASS_FORMAT,
|
||||
},
|
||||
@ -179,9 +181,13 @@ impl Plugin for Core3dPlugin {
|
||||
.add_render_sub_graph(Core3d)
|
||||
.add_render_graph_node::<ViewNodeRunner<EarlyPrepassNode>>(Core3d, Node3d::EarlyPrepass)
|
||||
.add_render_graph_node::<ViewNodeRunner<LatePrepassNode>>(Core3d, Node3d::LatePrepass)
|
||||
.add_render_graph_node::<ViewNodeRunner<DeferredGBufferPrepassNode>>(
|
||||
.add_render_graph_node::<ViewNodeRunner<EarlyDeferredGBufferPrepassNode>>(
|
||||
Core3d,
|
||||
Node3d::DeferredPrepass,
|
||||
Node3d::EarlyDeferredPrepass,
|
||||
)
|
||||
.add_render_graph_node::<ViewNodeRunner<LateDeferredGBufferPrepassNode>>(
|
||||
Core3d,
|
||||
Node3d::LateDeferredPrepass,
|
||||
)
|
||||
.add_render_graph_node::<ViewNodeRunner<CopyDeferredLightingIdNode>>(
|
||||
Core3d,
|
||||
@ -210,8 +216,9 @@ impl Plugin for Core3dPlugin {
|
||||
Core3d,
|
||||
(
|
||||
Node3d::EarlyPrepass,
|
||||
Node3d::EarlyDeferredPrepass,
|
||||
Node3d::LatePrepass,
|
||||
Node3d::DeferredPrepass,
|
||||
Node3d::LateDeferredPrepass,
|
||||
Node3d::CopyDeferredLightingId,
|
||||
Node3d::EndPrepasses,
|
||||
Node3d::StartMainPass,
|
||||
@ -943,7 +950,6 @@ fn configure_occlusion_culling_view_targets(
|
||||
With<OcclusionCulling>,
|
||||
Without<NoIndirectDrawing>,
|
||||
With<DepthPrepass>,
|
||||
Without<DeferredPrepass>,
|
||||
),
|
||||
>,
|
||||
) {
|
||||
|
@ -1,7 +1,8 @@
|
||||
use bevy_ecs::{prelude::*, query::QueryItem};
|
||||
use bevy_render::experimental::occlusion_culling::OcclusionCulling;
|
||||
use bevy_render::render_graph::ViewNode;
|
||||
|
||||
use bevy_render::view::ExtractedView;
|
||||
use bevy_render::view::{ExtractedView, NoIndirectDrawing};
|
||||
use bevy_render::{
|
||||
camera::ExtractedCamera,
|
||||
render_graph::{NodeRunError, RenderGraphContext},
|
||||
@ -18,76 +19,151 @@ use crate::prepass::ViewPrepassTextures;
|
||||
|
||||
use super::{AlphaMask3dDeferred, Opaque3dDeferred};
|
||||
|
||||
/// Render node used by the prepass.
|
||||
/// The phase of the deferred prepass that draws meshes that were visible last
|
||||
/// frame.
|
||||
///
|
||||
/// By default, inserted before the main pass in the render graph.
|
||||
/// If occlusion culling isn't in use, this prepass simply draws all meshes.
|
||||
///
|
||||
/// Like all prepass nodes, this is inserted before the main pass in the render
|
||||
/// graph.
|
||||
#[derive(Default)]
|
||||
pub struct DeferredGBufferPrepassNode;
|
||||
pub struct EarlyDeferredGBufferPrepassNode;
|
||||
|
||||
impl ViewNode for DeferredGBufferPrepassNode {
|
||||
impl ViewNode for EarlyDeferredGBufferPrepassNode {
|
||||
type ViewQuery = <LateDeferredGBufferPrepassNode as ViewNode>::ViewQuery;
|
||||
|
||||
fn run<'w>(
|
||||
&self,
|
||||
graph: &mut RenderGraphContext,
|
||||
render_context: &mut RenderContext<'w>,
|
||||
view_query: QueryItem<'w, Self::ViewQuery>,
|
||||
world: &'w World,
|
||||
) -> Result<(), NodeRunError> {
|
||||
run_deferred_prepass(
|
||||
graph,
|
||||
render_context,
|
||||
view_query,
|
||||
false,
|
||||
world,
|
||||
"early deferred prepass",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// The phase of the prepass that runs after occlusion culling against the
|
||||
/// meshes that were visible last frame.
|
||||
///
|
||||
/// If occlusion culling isn't in use, this is a no-op.
|
||||
///
|
||||
/// Like all prepass nodes, this is inserted before the main pass in the render
|
||||
/// graph.
|
||||
#[derive(Default)]
|
||||
pub struct LateDeferredGBufferPrepassNode;
|
||||
|
||||
impl ViewNode for LateDeferredGBufferPrepassNode {
|
||||
type ViewQuery = (
|
||||
&'static ExtractedCamera,
|
||||
&'static ExtractedView,
|
||||
&'static ViewDepthTexture,
|
||||
&'static ViewPrepassTextures,
|
||||
Has<OcclusionCulling>,
|
||||
Has<NoIndirectDrawing>,
|
||||
);
|
||||
|
||||
fn run<'w>(
|
||||
&self,
|
||||
graph: &mut RenderGraphContext,
|
||||
render_context: &mut RenderContext<'w>,
|
||||
(camera, extracted_view, view_depth_texture, view_prepass_textures): QueryItem<
|
||||
'w,
|
||||
Self::ViewQuery,
|
||||
>,
|
||||
view_query: QueryItem<'w, Self::ViewQuery>,
|
||||
world: &'w World,
|
||||
) -> Result<(), NodeRunError> {
|
||||
let (Some(opaque_deferred_phases), Some(alpha_mask_deferred_phases)) = (
|
||||
world.get_resource::<ViewBinnedRenderPhases<Opaque3dDeferred>>(),
|
||||
world.get_resource::<ViewBinnedRenderPhases<AlphaMask3dDeferred>>(),
|
||||
) else {
|
||||
let (_, _, _, _, occlusion_culling, no_indirect_drawing) = view_query;
|
||||
if !occlusion_culling || no_indirect_drawing {
|
||||
return Ok(());
|
||||
};
|
||||
}
|
||||
|
||||
let (Some(opaque_deferred_phase), Some(alpha_mask_deferred_phase)) = (
|
||||
opaque_deferred_phases.get(&extracted_view.retained_view_entity),
|
||||
alpha_mask_deferred_phases.get(&extracted_view.retained_view_entity),
|
||||
) else {
|
||||
return Ok(());
|
||||
};
|
||||
run_deferred_prepass(
|
||||
graph,
|
||||
render_context,
|
||||
view_query,
|
||||
true,
|
||||
world,
|
||||
"late deferred prepass",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
let mut color_attachments = vec![];
|
||||
color_attachments.push(
|
||||
view_prepass_textures
|
||||
.normal
|
||||
.as_ref()
|
||||
.map(|normals_texture| normals_texture.get_attachment()),
|
||||
);
|
||||
color_attachments.push(
|
||||
view_prepass_textures
|
||||
.motion_vectors
|
||||
.as_ref()
|
||||
.map(|motion_vectors_texture| motion_vectors_texture.get_attachment()),
|
||||
);
|
||||
/// Runs the deferred prepass that draws all meshes to the depth buffer and
|
||||
/// G-buffers.
|
||||
///
|
||||
/// If occlusion culling isn't in use, and a prepass is enabled, then there's
|
||||
/// only one prepass. If occlusion culling is in use, then any prepass is split
|
||||
/// into two: an *early* prepass and a *late* prepass. The early prepass draws
|
||||
/// what was visible last frame, and the last prepass performs occlusion culling
|
||||
/// against a conservative hierarchical Z buffer before drawing unoccluded
|
||||
/// meshes.
|
||||
fn run_deferred_prepass<'w>(
|
||||
graph: &mut RenderGraphContext,
|
||||
render_context: &mut RenderContext<'w>,
|
||||
(camera, extracted_view, view_depth_texture, view_prepass_textures, _, _): QueryItem<
|
||||
'w,
|
||||
<LateDeferredGBufferPrepassNode as ViewNode>::ViewQuery,
|
||||
>,
|
||||
is_late: bool,
|
||||
world: &'w World,
|
||||
label: &'static str,
|
||||
) -> Result<(), NodeRunError> {
|
||||
let (Some(opaque_deferred_phases), Some(alpha_mask_deferred_phases)) = (
|
||||
world.get_resource::<ViewBinnedRenderPhases<Opaque3dDeferred>>(),
|
||||
world.get_resource::<ViewBinnedRenderPhases<AlphaMask3dDeferred>>(),
|
||||
) else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
// If we clear the deferred texture with LoadOp::Clear(Default::default()) we get these errors:
|
||||
// Chrome: GL_INVALID_OPERATION: No defined conversion between clear value and attachment format.
|
||||
// Firefox: WebGL warning: clearBufferu?[fi]v: This attachment is of type FLOAT, but this function is of type UINT.
|
||||
// Appears to be unsupported: https://registry.khronos.org/webgl/specs/latest/2.0/#3.7.9
|
||||
// For webgl2 we fallback to manually clearing
|
||||
#[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
|
||||
let (Some(opaque_deferred_phase), Some(alpha_mask_deferred_phase)) = (
|
||||
opaque_deferred_phases.get(&extracted_view.retained_view_entity),
|
||||
alpha_mask_deferred_phases.get(&extracted_view.retained_view_entity),
|
||||
) else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let mut color_attachments = vec![];
|
||||
color_attachments.push(
|
||||
view_prepass_textures
|
||||
.normal
|
||||
.as_ref()
|
||||
.map(|normals_texture| normals_texture.get_attachment()),
|
||||
);
|
||||
color_attachments.push(
|
||||
view_prepass_textures
|
||||
.motion_vectors
|
||||
.as_ref()
|
||||
.map(|motion_vectors_texture| motion_vectors_texture.get_attachment()),
|
||||
);
|
||||
|
||||
// If we clear the deferred texture with LoadOp::Clear(Default::default()) we get these errors:
|
||||
// Chrome: GL_INVALID_OPERATION: No defined conversion between clear value and attachment format.
|
||||
// Firefox: WebGL warning: clearBufferu?[fi]v: This attachment is of type FLOAT, but this function is of type UINT.
|
||||
// Appears to be unsupported: https://registry.khronos.org/webgl/specs/latest/2.0/#3.7.9
|
||||
// For webgl2 we fallback to manually clearing
|
||||
#[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
|
||||
if !is_late {
|
||||
if let Some(deferred_texture) = &view_prepass_textures.deferred {
|
||||
render_context.command_encoder().clear_texture(
|
||||
&deferred_texture.texture.texture,
|
||||
&bevy_render::render_resource::ImageSubresourceRange::default(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
color_attachments.push(
|
||||
view_prepass_textures
|
||||
.deferred
|
||||
.as_ref()
|
||||
.map(|deferred_texture| {
|
||||
color_attachments.push(
|
||||
view_prepass_textures
|
||||
.deferred
|
||||
.as_ref()
|
||||
.map(|deferred_texture| {
|
||||
if is_late {
|
||||
deferred_texture.get_attachment()
|
||||
} else {
|
||||
#[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
|
||||
{
|
||||
bevy_render::render_resource::RenderPassColorAttachment {
|
||||
@ -105,87 +181,82 @@ impl ViewNode for DeferredGBufferPrepassNode {
|
||||
feature = "webgpu"
|
||||
))]
|
||||
deferred_texture.get_attachment()
|
||||
}),
|
||||
);
|
||||
|
||||
color_attachments.push(
|
||||
view_prepass_textures
|
||||
.deferred_lighting_pass_id
|
||||
.as_ref()
|
||||
.map(|deferred_lighting_pass_id| deferred_lighting_pass_id.get_attachment()),
|
||||
);
|
||||
|
||||
// If all color attachments are none: clear the color attachment list so that no fragment shader is required
|
||||
if color_attachments.iter().all(Option::is_none) {
|
||||
color_attachments.clear();
|
||||
}
|
||||
|
||||
let depth_stencil_attachment = Some(view_depth_texture.get_attachment(StoreOp::Store));
|
||||
|
||||
let view_entity = graph.view_entity();
|
||||
render_context.add_command_buffer_generation_task(move |render_device| {
|
||||
#[cfg(feature = "trace")]
|
||||
let _deferred_span = info_span!("deferred_prepass").entered();
|
||||
|
||||
// Command encoder setup
|
||||
let mut command_encoder =
|
||||
render_device.create_command_encoder(&CommandEncoderDescriptor {
|
||||
label: Some("deferred_prepass_command_encoder"),
|
||||
});
|
||||
|
||||
// Render pass setup
|
||||
let render_pass = command_encoder.begin_render_pass(&RenderPassDescriptor {
|
||||
label: Some("deferred_prepass"),
|
||||
color_attachments: &color_attachments,
|
||||
depth_stencil_attachment,
|
||||
timestamp_writes: None,
|
||||
occlusion_query_set: None,
|
||||
});
|
||||
let mut render_pass = TrackedRenderPass::new(&render_device, render_pass);
|
||||
if let Some(viewport) = camera.viewport.as_ref() {
|
||||
render_pass.set_camera_viewport(viewport);
|
||||
}
|
||||
|
||||
// Opaque draws
|
||||
if !opaque_deferred_phase.multidrawable_meshes.is_empty()
|
||||
|| !opaque_deferred_phase.batchable_meshes.is_empty()
|
||||
|| !opaque_deferred_phase.unbatchable_meshes.is_empty()
|
||||
{
|
||||
#[cfg(feature = "trace")]
|
||||
let _opaque_prepass_span = info_span!("opaque_deferred_prepass").entered();
|
||||
if let Err(err) = opaque_deferred_phase.render(&mut render_pass, world, view_entity)
|
||||
{
|
||||
error!("Error encountered while rendering the opaque deferred phase {err:?}");
|
||||
}
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
// Alpha masked draws
|
||||
if !alpha_mask_deferred_phase.is_empty() {
|
||||
#[cfg(feature = "trace")]
|
||||
let _alpha_mask_deferred_span = info_span!("alpha_mask_deferred_prepass").entered();
|
||||
if let Err(err) =
|
||||
alpha_mask_deferred_phase.render(&mut render_pass, world, view_entity)
|
||||
{
|
||||
error!(
|
||||
"Error encountered while rendering the alpha mask deferred phase {err:?}"
|
||||
);
|
||||
}
|
||||
}
|
||||
color_attachments.push(
|
||||
view_prepass_textures
|
||||
.deferred_lighting_pass_id
|
||||
.as_ref()
|
||||
.map(|deferred_lighting_pass_id| deferred_lighting_pass_id.get_attachment()),
|
||||
);
|
||||
|
||||
drop(render_pass);
|
||||
// If all color attachments are none: clear the color attachment list so that no fragment shader is required
|
||||
if color_attachments.iter().all(Option::is_none) {
|
||||
color_attachments.clear();
|
||||
}
|
||||
|
||||
// After rendering to the view depth texture, copy it to the prepass depth texture
|
||||
if let Some(prepass_depth_texture) = &view_prepass_textures.depth {
|
||||
command_encoder.copy_texture_to_texture(
|
||||
view_depth_texture.texture.as_image_copy(),
|
||||
prepass_depth_texture.texture.texture.as_image_copy(),
|
||||
view_prepass_textures.size,
|
||||
);
|
||||
}
|
||||
let depth_stencil_attachment = Some(view_depth_texture.get_attachment(StoreOp::Store));
|
||||
|
||||
command_encoder.finish()
|
||||
let view_entity = graph.view_entity();
|
||||
render_context.add_command_buffer_generation_task(move |render_device| {
|
||||
#[cfg(feature = "trace")]
|
||||
let _deferred_span = info_span!("deferred_prepass").entered();
|
||||
|
||||
// Command encoder setup
|
||||
let mut command_encoder = render_device.create_command_encoder(&CommandEncoderDescriptor {
|
||||
label: Some("deferred_prepass_command_encoder"),
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
// Render pass setup
|
||||
let render_pass = command_encoder.begin_render_pass(&RenderPassDescriptor {
|
||||
label: Some(label),
|
||||
color_attachments: &color_attachments,
|
||||
depth_stencil_attachment,
|
||||
timestamp_writes: None,
|
||||
occlusion_query_set: None,
|
||||
});
|
||||
let mut render_pass = TrackedRenderPass::new(&render_device, render_pass);
|
||||
if let Some(viewport) = camera.viewport.as_ref() {
|
||||
render_pass.set_camera_viewport(viewport);
|
||||
}
|
||||
|
||||
// Opaque draws
|
||||
if !opaque_deferred_phase.multidrawable_meshes.is_empty()
|
||||
|| !opaque_deferred_phase.batchable_meshes.is_empty()
|
||||
|| !opaque_deferred_phase.unbatchable_meshes.is_empty()
|
||||
{
|
||||
#[cfg(feature = "trace")]
|
||||
let _opaque_prepass_span = info_span!("opaque_deferred_prepass").entered();
|
||||
if let Err(err) = opaque_deferred_phase.render(&mut render_pass, world, view_entity) {
|
||||
error!("Error encountered while rendering the opaque deferred phase {err:?}");
|
||||
}
|
||||
}
|
||||
|
||||
// Alpha masked draws
|
||||
if !alpha_mask_deferred_phase.is_empty() {
|
||||
#[cfg(feature = "trace")]
|
||||
let _alpha_mask_deferred_span = info_span!("alpha_mask_deferred_prepass").entered();
|
||||
if let Err(err) = alpha_mask_deferred_phase.render(&mut render_pass, world, view_entity)
|
||||
{
|
||||
error!("Error encountered while rendering the alpha mask deferred phase {err:?}");
|
||||
}
|
||||
}
|
||||
|
||||
drop(render_pass);
|
||||
|
||||
// After rendering to the view depth texture, copy it to the prepass depth texture
|
||||
if let Some(prepass_depth_texture) = &view_prepass_textures.depth {
|
||||
command_encoder.copy_texture_to_texture(
|
||||
view_depth_texture.texture.as_image_copy(),
|
||||
prepass_depth_texture.texture.texture.as_image_copy(),
|
||||
view_prepass_textures.size,
|
||||
);
|
||||
}
|
||||
|
||||
command_encoder.finish()
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ use crate::{
|
||||
graph::{Core3d, Node3d},
|
||||
prepare_core_3d_depth_textures,
|
||||
},
|
||||
prepass::{DeferredPrepass, DepthPrepass},
|
||||
prepass::DepthPrepass,
|
||||
};
|
||||
|
||||
/// Identifies the `downsample_depth.wgsl` shader.
|
||||
@ -93,9 +93,10 @@ impl Plugin for MipGenerationPlugin {
|
||||
Core3d,
|
||||
(
|
||||
Node3d::EarlyPrepass,
|
||||
Node3d::EarlyDeferredPrepass,
|
||||
Node3d::EarlyDownsampleDepth,
|
||||
Node3d::LatePrepass,
|
||||
Node3d::DeferredPrepass,
|
||||
Node3d::LateDeferredPrepass,
|
||||
),
|
||||
)
|
||||
.add_render_graph_edges(
|
||||
@ -651,7 +652,6 @@ fn prepare_view_depth_pyramids(
|
||||
With<OcclusionCulling>,
|
||||
Without<NoIndirectDrawing>,
|
||||
With<DepthPrepass>,
|
||||
Without<DeferredPrepass>,
|
||||
),
|
||||
>,
|
||||
) {
|
||||
|
@ -66,6 +66,7 @@ impl ViewNode for LatePrepassNode {
|
||||
Option<&'static PreviousViewUniformOffset>,
|
||||
Has<OcclusionCulling>,
|
||||
Has<NoIndirectDrawing>,
|
||||
Has<DeferredPrepass>,
|
||||
);
|
||||
|
||||
fn run<'w>(
|
||||
@ -77,7 +78,7 @@ impl ViewNode for LatePrepassNode {
|
||||
) -> Result<(), NodeRunError> {
|
||||
// We only need a late prepass if we have occlusion culling and indirect
|
||||
// drawing.
|
||||
let (_, _, _, _, _, _, _, _, _, occlusion_culling, no_indirect_drawing) = query;
|
||||
let (_, _, _, _, _, _, _, _, _, occlusion_culling, no_indirect_drawing, _) = query;
|
||||
if !occlusion_culling || no_indirect_drawing {
|
||||
return Ok(());
|
||||
}
|
||||
@ -110,10 +111,18 @@ fn run_prepass<'w>(
|
||||
view_prev_uniform_offset,
|
||||
_,
|
||||
_,
|
||||
has_deferred,
|
||||
): QueryItem<'w, <LatePrepassNode as ViewNode>::ViewQuery>,
|
||||
world: &'w World,
|
||||
label: &'static str,
|
||||
) -> Result<(), NodeRunError> {
|
||||
// If we're using deferred rendering, there will be a deferred prepass
|
||||
// instead of this one. Just bail out so we don't have to bother looking at
|
||||
// the empty bins.
|
||||
if has_deferred {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let (Some(opaque_prepass_phases), Some(alpha_mask_prepass_phases)) = (
|
||||
world.get_resource::<ViewBinnedRenderPhases<Opaque3dPrepass>>(),
|
||||
world.get_resource::<ViewBinnedRenderPhases<AlphaMask3dPrepass>>(),
|
||||
|
@ -258,8 +258,6 @@ impl Plugin for MeshletPlugin {
|
||||
NodeMeshlet::Prepass,
|
||||
//
|
||||
NodeMeshlet::DeferredPrepass,
|
||||
Node3d::DeferredPrepass,
|
||||
Node3d::CopyDeferredLightingId,
|
||||
Node3d::EndPrepasses,
|
||||
//
|
||||
Node3d::StartMainPass,
|
||||
|
@ -13,10 +13,7 @@ use bevy_asset::{load_internal_asset, weak_handle, Handle};
|
||||
use bevy_core_pipeline::{
|
||||
core_3d::graph::{Core3d, Node3d},
|
||||
experimental::mip_generation::ViewDepthPyramid,
|
||||
prepass::{
|
||||
DeferredPrepass, DepthPrepass, PreviousViewData, PreviousViewUniformOffset,
|
||||
PreviousViewUniforms,
|
||||
},
|
||||
prepass::{DepthPrepass, PreviousViewData, PreviousViewUniformOffset, PreviousViewUniforms},
|
||||
};
|
||||
use bevy_derive::{Deref, DerefMut};
|
||||
use bevy_ecs::{
|
||||
@ -140,7 +137,6 @@ pub struct LateGpuPreprocessNode {
|
||||
Without<NoIndirectDrawing>,
|
||||
With<OcclusionCulling>,
|
||||
With<DepthPrepass>,
|
||||
Without<DeferredPrepass>,
|
||||
),
|
||||
>,
|
||||
}
|
||||
@ -159,7 +155,6 @@ pub struct EarlyPrepassBuildIndirectParametersNode {
|
||||
Without<SkipGpuPreprocess>,
|
||||
Without<NoIndirectDrawing>,
|
||||
With<DepthPrepass>,
|
||||
Without<DeferredPrepass>,
|
||||
),
|
||||
>,
|
||||
}
|
||||
@ -180,7 +175,6 @@ pub struct LatePrepassBuildIndirectParametersNode {
|
||||
Without<NoIndirectDrawing>,
|
||||
With<DepthPrepass>,
|
||||
With<OcclusionCulling>,
|
||||
Without<DeferredPrepass>,
|
||||
),
|
||||
>,
|
||||
}
|
||||
@ -527,21 +521,18 @@ impl Plugin for GpuMeshPreprocessPlugin {
|
||||
NodePbr::EarlyGpuPreprocess,
|
||||
NodePbr::EarlyPrepassBuildIndirectParameters,
|
||||
Node3d::EarlyPrepass,
|
||||
Node3d::EarlyDeferredPrepass,
|
||||
Node3d::EarlyDownsampleDepth,
|
||||
NodePbr::LateGpuPreprocess,
|
||||
NodePbr::LatePrepassBuildIndirectParameters,
|
||||
Node3d::LatePrepass,
|
||||
Node3d::LateDeferredPrepass,
|
||||
NodePbr::MainBuildIndirectParameters,
|
||||
// Shadows don't currently support occlusion culling, so we
|
||||
// treat shadows as effectively the main phase for our
|
||||
// purposes.
|
||||
NodePbr::ShadowPass,
|
||||
),
|
||||
)
|
||||
.add_render_graph_edge(
|
||||
Core3d,
|
||||
NodePbr::MainBuildIndirectParameters,
|
||||
Node3d::DeferredPrepass,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -49,6 +49,9 @@ struct Args {
|
||||
/// enable deferred shading
|
||||
#[argh(switch)]
|
||||
deferred: Option<bool>,
|
||||
/// spawn a light even if the scene already has one
|
||||
#[argh(switch)]
|
||||
add_light: Option<bool>,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
@ -204,7 +207,7 @@ fn setup_scene_after_load(
|
||||
}
|
||||
|
||||
// Spawn a default light if the scene does not have one
|
||||
if !scene_handle.has_light {
|
||||
if !scene_handle.has_light || args.add_light == Some(true) {
|
||||
info!("Spawning a directional light");
|
||||
commands.spawn((
|
||||
DirectionalLight::default(),
|
||||
|
Loading…
Reference in New Issue
Block a user