Use RenderStartup for bevy_pbr for some basic cases. (#19999)

# Objective

- Progress towards #19887.

## Solution

- For cases that don't need to conditionally add systems, we can just
replace FromWorld impls with systems and then add those systems to
`RenderStartup`.

## Testing

- I ran the `lightmaps`, `reflection_probes`, `deferred_rendering`,
`volumetric_fog`, and `wireframe` examples.
This commit is contained in:
andriyDev 2025-07-14 13:28:03 -07:00 committed by GitHub
parent c994bdf71a
commit 6e3739ac96
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 226 additions and 247 deletions

View File

@ -7,7 +7,7 @@ use crate::{
}; };
use crate::{DistanceFog, MeshPipelineKey, ViewFogUniformOffset, ViewLightsUniformOffset}; use crate::{DistanceFog, MeshPipelineKey, ViewFogUniformOffset, ViewLightsUniformOffset};
use bevy_app::prelude::*; use bevy_app::prelude::*;
use bevy_asset::{embedded_asset, load_embedded_asset, Handle}; use bevy_asset::{embedded_asset, load_embedded_asset, AssetServer, Handle};
use bevy_core_pipeline::{ use bevy_core_pipeline::{
core_3d::graph::{Core3d, Node3d}, core_3d::graph::{Core3d, Node3d},
deferred::{ deferred::{
@ -19,6 +19,7 @@ use bevy_core_pipeline::{
use bevy_ecs::{prelude::*, query::QueryItem}; use bevy_ecs::{prelude::*, query::QueryItem};
use bevy_image::BevyDefault as _; use bevy_image::BevyDefault as _;
use bevy_light::{EnvironmentMapLight, ShadowFilteringMethod}; use bevy_light::{EnvironmentMapLight, ShadowFilteringMethod};
use bevy_render::RenderStartup;
use bevy_render::{ use bevy_render::{
extract_component::{ extract_component::{
ComponentUniforms, ExtractComponent, ExtractComponentPlugin, UniformComponentPlugin, ComponentUniforms, ExtractComponent, ExtractComponentPlugin, UniformComponentPlugin,
@ -104,6 +105,7 @@ impl Plugin for DeferredPbrLightingPlugin {
render_app render_app
.init_resource::<SpecializedRenderPipelines<DeferredLightingLayout>>() .init_resource::<SpecializedRenderPipelines<DeferredLightingLayout>>()
.add_systems(RenderStartup, init_deferred_lighting_layout)
.add_systems( .add_systems(
Render, Render,
(prepare_deferred_lighting_pipelines.in_set(RenderSystems::Prepare),), (prepare_deferred_lighting_pipelines.in_set(RenderSystems::Prepare),),
@ -121,14 +123,6 @@ impl Plugin for DeferredPbrLightingPlugin {
), ),
); );
} }
fn finish(&self, app: &mut App) {
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};
render_app.init_resource::<DeferredLightingLayout>();
}
} }
#[derive(Default)] #[derive(Default)]
@ -394,22 +388,27 @@ impl SpecializedRenderPipeline for DeferredLightingLayout {
} }
} }
impl FromWorld for DeferredLightingLayout { pub fn init_deferred_lighting_layout(
fn from_world(world: &mut World) -> Self { mut commands: Commands,
let render_device = world.resource::<RenderDevice>(); render_device: Res<RenderDevice>,
let layout = render_device.create_bind_group_layout( mesh_pipeline: Res<MeshPipeline>,
"deferred_lighting_layout", asset_server: Res<AssetServer>,
&BindGroupLayoutEntries::single( ) {
ShaderStages::VERTEX_FRAGMENT, let layout = render_device.create_bind_group_layout(
uniform_buffer::<PbrDeferredLightingDepthId>(false), "deferred_lighting_layout",
), &BindGroupLayoutEntries::single(
); ShaderStages::VERTEX_FRAGMENT,
Self { uniform_buffer::<PbrDeferredLightingDepthId>(false),
mesh_pipeline: world.resource::<MeshPipeline>().clone(), ),
bind_group_layout_2: layout, );
deferred_lighting_shader: load_embedded_asset!(world, "deferred_lighting.wgsl"), commands.insert_resource(DeferredLightingLayout {
} mesh_pipeline: mesh_pipeline.clone(),
} bind_group_layout_2: layout,
deferred_lighting_shader: load_embedded_asset!(
asset_server.as_ref(),
"deferred_lighting.wgsl"
),
});
} }
pub fn insert_deferred_lighting_pass_id_component( pub fn insert_deferred_lighting_pass_id_component(

View File

@ -289,9 +289,7 @@ impl Plugin for LightProbePlugin {
load_shader_library!(app, "irradiance_volume.wgsl"); load_shader_library!(app, "irradiance_volume.wgsl");
app.add_plugins(ExtractInstancesPlugin::<EnvironmentMapIds>::new()); app.add_plugins(ExtractInstancesPlugin::<EnvironmentMapIds>::new());
}
fn finish(&self, app: &mut App) {
let Some(render_app) = app.get_sub_app_mut(RenderApp) else { let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return; return;
}; };

View File

@ -42,8 +42,7 @@ use bevy_ecs::{
reflect::ReflectComponent, reflect::ReflectComponent,
resource::Resource, resource::Resource,
schedule::IntoScheduleConfigs, schedule::IntoScheduleConfigs,
system::{Query, Res, ResMut}, system::{Commands, Query, Res, ResMut},
world::{FromWorld, World},
}; };
use bevy_image::Image; use bevy_image::Image;
use bevy_math::{uvec2, vec4, Rect, UVec2}; use bevy_math::{uvec2, vec4, Rect, UVec2};
@ -57,7 +56,7 @@ use bevy_render::{
sync_world::MainEntity, sync_world::MainEntity,
texture::{FallbackImage, GpuImage}, texture::{FallbackImage, GpuImage},
view::ViewVisibility, view::ViewVisibility,
Extract, ExtractSchedule, RenderApp, Extract, ExtractSchedule, RenderApp, RenderStartup,
}; };
use bevy_render::{renderer::RenderDevice, sync_world::MainEntityHashMap}; use bevy_render::{renderer::RenderDevice, sync_world::MainEntityHashMap};
use bevy_utils::default; use bevy_utils::default;
@ -186,17 +185,16 @@ pub struct LightmapSlotIndex(pub(crate) NonMaxU16);
impl Plugin for LightmapPlugin { impl Plugin for LightmapPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
load_shader_library!(app, "lightmap.wgsl"); load_shader_library!(app, "lightmap.wgsl");
}
fn finish(&self, app: &mut App) {
let Some(render_app) = app.get_sub_app_mut(RenderApp) else { let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return; return;
}; };
render_app
render_app.init_resource::<RenderLightmaps>().add_systems( .add_systems(RenderStartup, init_render_lightmaps)
ExtractSchedule, .add_systems(
extract_lightmaps.after(MeshExtractionSystems), ExtractSchedule,
); extract_lightmaps.after(MeshExtractionSystems),
);
} }
} }
@ -334,21 +332,20 @@ impl Default for Lightmap {
} }
} }
impl FromWorld for RenderLightmaps { pub fn init_render_lightmaps(
fn from_world(world: &mut World) -> Self { mut commands: Commands,
let render_device = world.resource::<RenderDevice>(); render_device: Res<RenderDevice>,
let render_adapter = world.resource::<RenderAdapter>(); render_adapter: Res<RenderAdapter>,
) {
let bindless_supported = binding_arrays_are_usable(&render_device, &render_adapter);
let bindless_supported = binding_arrays_are_usable(render_device, render_adapter); commands.insert_resource(RenderLightmaps {
render_lightmaps: default(),
RenderLightmaps { slabs: vec![],
render_lightmaps: default(), free_slabs: FixedBitSet::new(),
slabs: vec![], pending_lightmaps: default(),
free_slabs: FixedBitSet::new(), bindless_supported,
pending_lightmaps: default(), });
bindless_supported,
}
}
} }
impl RenderLightmaps { impl RenderLightmaps {

View File

@ -267,6 +267,15 @@ impl Plugin for MaterialsPlugin {
.init_resource::<LightKeyCache>() .init_resource::<LightKeyCache>()
.init_resource::<LightSpecializationTicks>() .init_resource::<LightSpecializationTicks>()
.init_resource::<SpecializedShadowMaterialPipelineCache>() .init_resource::<SpecializedShadowMaterialPipelineCache>()
.init_resource::<DrawFunctions<Shadow>>()
.init_resource::<RenderMaterialInstances>()
.init_resource::<MaterialBindGroupAllocators>()
.add_render_command::<Shadow, DrawPrepass>()
.add_render_command::<Transmissive3d, DrawMaterial>()
.add_render_command::<Transparent3d, DrawMaterial>()
.add_render_command::<Opaque3d, DrawMaterial>()
.add_render_command::<AlphaMask3d, DrawMaterial>()
.add_systems(RenderStartup, init_material_pipeline)
.add_systems( .add_systems(
Render, Render,
( (
@ -301,21 +310,6 @@ impl Plugin for MaterialsPlugin {
); );
} }
} }
fn finish(&self, app: &mut App) {
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
render_app
.init_resource::<DrawFunctions<Shadow>>()
.init_resource::<RenderMaterialInstances>()
.init_resource::<MaterialPipeline>()
.init_resource::<MaterialBindGroupAllocators>()
.add_render_command::<Shadow, DrawPrepass>()
.add_render_command::<Transmissive3d, DrawMaterial>()
.add_render_command::<Transparent3d, DrawMaterial>()
.add_render_command::<Opaque3d, DrawMaterial>()
.add_render_command::<AlphaMask3d, DrawMaterial>();
}
}
} }
/// Adds the necessary ECS resources and render logic to enable rendering entities using the given [`Material`] /// Adds the necessary ECS resources and render logic to enable rendering entities using the given [`Material`]
@ -485,12 +479,10 @@ impl SpecializedMeshPipeline for MaterialPipelineSpecializer {
} }
} }
impl FromWorld for MaterialPipeline { pub fn init_material_pipeline(mut commands: Commands, mesh_pipeline: Res<MeshPipeline>) {
fn from_world(world: &mut World) -> Self { commands.insert_resource(MaterialPipeline {
MaterialPipeline { mesh_pipeline: mesh_pipeline.clone(),
mesh_pipeline: world.resource::<MeshPipeline>().clone(), });
}
}
} }
pub type DrawMaterial = ( pub type DrawMaterial = (

View File

@ -2,13 +2,13 @@ mod prepass_bindings;
use crate::{ use crate::{
alpha_mode_pipeline_key, binding_arrays_are_usable, buffer_layout, alpha_mode_pipeline_key, binding_arrays_are_usable, buffer_layout,
collect_meshes_for_gpu_building, set_mesh_motion_vector_flags, setup_morph_and_skinning_defs, collect_meshes_for_gpu_building, init_material_pipeline, set_mesh_motion_vector_flags,
skin, DeferredDrawFunction, DeferredFragmentShader, DeferredVertexShader, DrawMesh, setup_morph_and_skinning_defs, skin, DeferredDrawFunction, DeferredFragmentShader,
EntitySpecializationTicks, ErasedMaterialPipelineKey, Material, MaterialPipeline, DeferredVertexShader, DrawMesh, EntitySpecializationTicks, ErasedMaterialPipelineKey, Material,
MaterialProperties, MeshLayouts, MeshPipeline, MeshPipelineKey, OpaqueRendererMethod, MaterialPipeline, MaterialProperties, MeshLayouts, MeshPipeline, MeshPipelineKey,
PreparedMaterial, PrepassDrawFunction, PrepassFragmentShader, PrepassVertexShader, OpaqueRendererMethod, PreparedMaterial, PrepassDrawFunction, PrepassFragmentShader,
RenderLightmaps, RenderMaterialInstances, RenderMeshInstanceFlags, RenderMeshInstances, PrepassVertexShader, RenderLightmaps, RenderMaterialInstances, RenderMeshInstanceFlags,
RenderPhaseType, SetMaterialBindGroup, SetMeshBindGroup, ShadowView, RenderMeshInstances, RenderPhaseType, SetMaterialBindGroup, SetMeshBindGroup, ShadowView,
}; };
use bevy_app::{App, Plugin, PreUpdate}; use bevy_app::{App, Plugin, PreUpdate};
use bevy_render::{ use bevy_render::{
@ -21,11 +21,11 @@ use bevy_render::{
renderer::RenderAdapter, renderer::RenderAdapter,
sync_world::RenderEntity, sync_world::RenderEntity,
view::{RenderVisibilityRanges, RetainedViewEntity, VISIBILITY_RANGES_STORAGE_BUFFER_COUNT}, view::{RenderVisibilityRanges, RetainedViewEntity, VISIBILITY_RANGES_STORAGE_BUFFER_COUNT},
ExtractSchedule, Render, RenderApp, RenderDebugFlags, RenderSystems, ExtractSchedule, Render, RenderApp, RenderDebugFlags, RenderStartup, RenderSystems,
}; };
pub use prepass_bindings::*; pub use prepass_bindings::*;
use bevy_asset::{embedded_asset, load_embedded_asset, Handle}; use bevy_asset::{embedded_asset, load_embedded_asset, AssetServer, Handle};
use bevy_core_pipeline::{ use bevy_core_pipeline::{
core_3d::CORE_3D_DEPTH_FORMAT, deferred::*, prelude::Camera3d, prepass::*, core_3d::CORE_3D_DEPTH_FORMAT, deferred::*, prelude::Camera3d, prepass::*,
}; };
@ -87,22 +87,20 @@ impl Plugin for PrepassPipelinePlugin {
}; };
render_app render_app
.add_systems(
RenderStartup,
(
init_prepass_pipeline.after(init_material_pipeline),
init_prepass_view_bind_group,
)
.chain(),
)
.add_systems( .add_systems(
Render, Render,
prepare_prepass_view_bind_group.in_set(RenderSystems::PrepareBindGroups), prepare_prepass_view_bind_group.in_set(RenderSystems::PrepareBindGroups),
) )
.init_resource::<SpecializedMeshPipelines<PrepassPipelineSpecializer>>(); .init_resource::<SpecializedMeshPipelines<PrepassPipelineSpecializer>>();
} }
fn finish(&self, app: &mut App) {
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};
render_app
.init_resource::<PrepassPipeline>()
.init_resource::<PrepassViewBindGroup>();
}
} }
/// Sets up the prepasses for a material. /// Sets up the prepasses for a material.
@ -273,78 +271,79 @@ pub struct PrepassPipeline {
pub material_pipeline: MaterialPipeline, pub material_pipeline: MaterialPipeline,
} }
impl FromWorld for PrepassPipeline { pub fn init_prepass_pipeline(
fn from_world(world: &mut World) -> Self { mut commands: Commands,
let render_device = world.resource::<RenderDevice>(); render_device: Res<RenderDevice>,
let render_adapter = world.resource::<RenderAdapter>(); render_adapter: Res<RenderAdapter>,
let visibility_ranges_buffer_binding_type = render_device mesh_pipeline: Res<MeshPipeline>,
.get_supported_read_only_binding_type(VISIBILITY_RANGES_STORAGE_BUFFER_COUNT); material_pipeline: Res<MaterialPipeline>,
asset_server: Res<AssetServer>,
) {
let visibility_ranges_buffer_binding_type =
render_device.get_supported_read_only_binding_type(VISIBILITY_RANGES_STORAGE_BUFFER_COUNT);
let view_layout_motion_vectors = render_device.create_bind_group_layout( let view_layout_motion_vectors = render_device.create_bind_group_layout(
"prepass_view_layout_motion_vectors", "prepass_view_layout_motion_vectors",
&BindGroupLayoutEntries::with_indices( &BindGroupLayoutEntries::with_indices(
ShaderStages::VERTEX_FRAGMENT, ShaderStages::VERTEX_FRAGMENT,
(
// View
(0, uniform_buffer::<ViewUniform>(true)),
// Globals
(1, uniform_buffer::<GlobalsUniform>(false)),
// PreviousViewUniforms
(2, uniform_buffer::<PreviousViewData>(true)),
// VisibilityRanges
( (
// View 14,
(0, uniform_buffer::<ViewUniform>(true)), buffer_layout(
// Globals visibility_ranges_buffer_binding_type,
(1, uniform_buffer::<GlobalsUniform>(false)), false,
// PreviousViewUniforms Some(Vec4::min_size()),
(2, uniform_buffer::<PreviousViewData>(true)), )
// VisibilityRanges .visibility(ShaderStages::VERTEX),
(
14,
buffer_layout(
visibility_ranges_buffer_binding_type,
false,
Some(Vec4::min_size()),
)
.visibility(ShaderStages::VERTEX),
),
), ),
), ),
); ),
);
let view_layout_no_motion_vectors = render_device.create_bind_group_layout( let view_layout_no_motion_vectors = render_device.create_bind_group_layout(
"prepass_view_layout_no_motion_vectors", "prepass_view_layout_no_motion_vectors",
&BindGroupLayoutEntries::with_indices( &BindGroupLayoutEntries::with_indices(
ShaderStages::VERTEX_FRAGMENT, ShaderStages::VERTEX_FRAGMENT,
(
// View
(0, uniform_buffer::<ViewUniform>(true)),
// Globals
(1, uniform_buffer::<GlobalsUniform>(false)),
// VisibilityRanges
( (
// View 14,
(0, uniform_buffer::<ViewUniform>(true)), buffer_layout(
// Globals visibility_ranges_buffer_binding_type,
(1, uniform_buffer::<GlobalsUniform>(false)), false,
// VisibilityRanges Some(Vec4::min_size()),
( )
14, .visibility(ShaderStages::VERTEX),
buffer_layout(
visibility_ranges_buffer_binding_type,
false,
Some(Vec4::min_size()),
)
.visibility(ShaderStages::VERTEX),
),
), ),
), ),
); ),
);
let mesh_pipeline = world.resource::<MeshPipeline>(); let depth_clip_control_supported = render_device
.features()
let depth_clip_control_supported = render_device .contains(WgpuFeatures::DEPTH_CLIP_CONTROL);
.features() commands.insert_resource(PrepassPipeline {
.contains(WgpuFeatures::DEPTH_CLIP_CONTROL); view_layout_motion_vectors,
PrepassPipeline { view_layout_no_motion_vectors,
view_layout_motion_vectors, mesh_layouts: mesh_pipeline.mesh_layouts.clone(),
view_layout_no_motion_vectors, default_prepass_shader: load_embedded_asset!(asset_server.as_ref(), "prepass.wgsl"),
mesh_layouts: mesh_pipeline.mesh_layouts.clone(), skins_use_uniform_buffers: skin::skins_use_uniform_buffers(&render_device),
default_prepass_shader: load_embedded_asset!(world, "prepass.wgsl"), depth_clip_control_supported,
skins_use_uniform_buffers: skin::skins_use_uniform_buffers(render_device), binding_arrays_are_usable: binding_arrays_are_usable(&render_device, &render_adapter),
depth_clip_control_supported, empty_layout: render_device.create_bind_group_layout("prepass_empty_layout", &[]),
binding_arrays_are_usable: binding_arrays_are_usable(render_device, render_adapter), material_pipeline: material_pipeline.clone(),
empty_layout: render_device.create_bind_group_layout("prepass_empty_layout", &[]), });
material_pipeline: world.resource::<MaterialPipeline>().clone(),
}
}
} }
pub struct PrepassPipelineSpecializer { pub struct PrepassPipelineSpecializer {
@ -702,22 +701,21 @@ pub struct PrepassViewBindGroup {
pub empty_bind_group: BindGroup, pub empty_bind_group: BindGroup,
} }
impl FromWorld for PrepassViewBindGroup { pub fn init_prepass_view_bind_group(
fn from_world(world: &mut World) -> Self { mut commands: Commands,
let pipeline = world.resource::<PrepassPipeline>(); render_device: Res<RenderDevice>,
pipeline: Res<PrepassPipeline>,
let render_device = world.resource::<RenderDevice>(); ) {
let empty_bind_group = render_device.create_bind_group( let empty_bind_group = render_device.create_bind_group(
"prepass_view_empty_bind_group", "prepass_view_empty_bind_group",
&pipeline.empty_layout, &pipeline.empty_layout,
&[], &[],
); );
PrepassViewBindGroup { commands.insert_resource(PrepassViewBindGroup {
motion_vectors: None, motion_vectors: None,
no_motion_vectors: None, no_motion_vectors: None,
empty_bind_group, empty_bind_group,
} });
}
} }
pub fn prepare_prepass_view_bind_group( pub fn prepare_prepass_view_bind_group(

View File

@ -46,11 +46,11 @@ use bevy_render::{
render_graph::{RenderGraphExt, ViewNodeRunner}, render_graph::{RenderGraphExt, ViewNodeRunner},
render_resource::SpecializedRenderPipelines, render_resource::SpecializedRenderPipelines,
sync_component::SyncComponentPlugin, sync_component::SyncComponentPlugin,
ExtractSchedule, Render, RenderApp, RenderSystems, ExtractSchedule, Render, RenderApp, RenderStartup, RenderSystems,
}; };
use render::{VolumetricFogNode, VolumetricFogPipeline, VolumetricFogUniformBuffer}; use render::{VolumetricFogNode, VolumetricFogPipeline, VolumetricFogUniformBuffer};
use crate::graph::NodePbr; use crate::{graph::NodePbr, volumetric_fog::render::init_volumetric_fog_pipeline};
pub mod render; pub mod render;
@ -84,6 +84,7 @@ impl Plugin for VolumetricFogPlugin {
}) })
.init_resource::<SpecializedRenderPipelines<VolumetricFogPipeline>>() .init_resource::<SpecializedRenderPipelines<VolumetricFogPipeline>>()
.init_resource::<VolumetricFogUniformBuffer>() .init_resource::<VolumetricFogUniformBuffer>()
.add_systems(RenderStartup, init_volumetric_fog_pipeline)
.add_systems(ExtractSchedule, render::extract_volumetric_fog) .add_systems(ExtractSchedule, render::extract_volumetric_fog)
.add_systems( .add_systems(
Render, Render,
@ -94,16 +95,7 @@ impl Plugin for VolumetricFogPlugin {
.in_set(RenderSystems::Prepare) .in_set(RenderSystems::Prepare)
.before(prepare_core_3d_depth_textures), .before(prepare_core_3d_depth_textures),
), ),
); )
}
fn finish(&self, app: &mut App) {
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};
render_app
.init_resource::<VolumetricFogPipeline>()
.add_render_graph_node::<ViewNodeRunner<VolumetricFogNode>>( .add_render_graph_node::<ViewNodeRunner<VolumetricFogNode>>(
Core3d, Core3d,
NodePbr::VolumetricFog, NodePbr::VolumetricFog,

View File

@ -2,7 +2,7 @@
use core::array; use core::array;
use bevy_asset::{load_embedded_asset, AssetId, Handle}; use bevy_asset::{load_embedded_asset, AssetId, AssetServer, Handle};
use bevy_color::ColorToComponents as _; use bevy_color::ColorToComponents as _;
use bevy_core_pipeline::{ use bevy_core_pipeline::{
core_3d::Camera3d, core_3d::Camera3d,
@ -15,7 +15,7 @@ use bevy_ecs::{
query::{Has, QueryItem, With}, query::{Has, QueryItem, With},
resource::Resource, resource::Resource,
system::{lifetimeless::Read, Commands, Local, Query, Res, ResMut}, system::{lifetimeless::Read, Commands, Local, Query, Res, ResMut},
world::{FromWorld, World}, world::World,
}; };
use bevy_image::{BevyDefault, Image}; use bevy_image::{BevyDefault, Image};
use bevy_math::{vec4, Mat3A, Mat4, Vec3, Vec3A, Vec4, Vec4Swizzles as _}; use bevy_math::{vec4, Mat3A, Mat4, Vec3, Vec3A, Vec4, Vec4Swizzles as _};
@ -201,61 +201,61 @@ pub struct ViewFogVolume {
#[derive(Resource, Default, Deref, DerefMut)] #[derive(Resource, Default, Deref, DerefMut)]
pub struct VolumetricFogUniformBuffer(pub DynamicUniformBuffer<VolumetricFogUniform>); pub struct VolumetricFogUniformBuffer(pub DynamicUniformBuffer<VolumetricFogUniform>);
impl FromWorld for VolumetricFogPipeline { pub fn init_volumetric_fog_pipeline(
fn from_world(world: &mut World) -> Self { mut commands: Commands,
let render_device = world.resource::<RenderDevice>(); render_device: Res<RenderDevice>,
let mesh_view_layouts = world.resource::<MeshPipelineViewLayouts>(); mesh_view_layouts: Res<MeshPipelineViewLayouts>,
asset_server: Res<AssetServer>,
) {
// Create the bind group layout entries common to all bind group
// layouts.
let base_bind_group_layout_entries = &BindGroupLayoutEntries::single(
ShaderStages::VERTEX_FRAGMENT,
// `volumetric_fog`
uniform_buffer::<VolumetricFogUniform>(true),
);
// Create the bind group layout entries common to all bind group // For every combination of `VolumetricFogBindGroupLayoutKey` bits,
// layouts. // create a bind group layout.
let base_bind_group_layout_entries = &BindGroupLayoutEntries::single( let bind_group_layouts = array::from_fn(|bits| {
ShaderStages::VERTEX_FRAGMENT, let flags = VolumetricFogBindGroupLayoutKey::from_bits_retain(bits as u8);
// `volumetric_fog`
uniform_buffer::<VolumetricFogUniform>(true),
);
// For every combination of `VolumetricFogBindGroupLayoutKey` bits, let mut bind_group_layout_entries = base_bind_group_layout_entries.to_vec();
// create a bind group layout.
let bind_group_layouts = array::from_fn(|bits| {
let flags = VolumetricFogBindGroupLayoutKey::from_bits_retain(bits as u8);
let mut bind_group_layout_entries = base_bind_group_layout_entries.to_vec(); // `depth_texture`
bind_group_layout_entries.extend_from_slice(&BindGroupLayoutEntries::with_indices(
ShaderStages::FRAGMENT,
((
1,
if flags.contains(VolumetricFogBindGroupLayoutKey::MULTISAMPLED) {
texture_depth_2d_multisampled()
} else {
texture_depth_2d()
},
),),
));
// `depth_texture` // `density_texture` and `density_sampler`
if flags.contains(VolumetricFogBindGroupLayoutKey::DENSITY_TEXTURE) {
bind_group_layout_entries.extend_from_slice(&BindGroupLayoutEntries::with_indices( bind_group_layout_entries.extend_from_slice(&BindGroupLayoutEntries::with_indices(
ShaderStages::FRAGMENT, ShaderStages::FRAGMENT,
(( (
1, (2, texture_3d(TextureSampleType::Float { filterable: true })),
if flags.contains(VolumetricFogBindGroupLayoutKey::MULTISAMPLED) { (3, sampler(SamplerBindingType::Filtering)),
texture_depth_2d_multisampled() ),
} else {
texture_depth_2d()
},
),),
)); ));
// `density_texture` and `density_sampler`
if flags.contains(VolumetricFogBindGroupLayoutKey::DENSITY_TEXTURE) {
bind_group_layout_entries.extend_from_slice(&BindGroupLayoutEntries::with_indices(
ShaderStages::FRAGMENT,
(
(2, texture_3d(TextureSampleType::Float { filterable: true })),
(3, sampler(SamplerBindingType::Filtering)),
),
));
}
// Create the bind group layout.
let description = flags.bind_group_layout_description();
render_device.create_bind_group_layout(&*description, &bind_group_layout_entries)
});
VolumetricFogPipeline {
mesh_view_layouts: mesh_view_layouts.clone(),
volumetric_view_bind_group_layouts: bind_group_layouts,
shader: load_embedded_asset!(world, "volumetric_fog.wgsl"),
} }
}
// Create the bind group layout.
let description = flags.bind_group_layout_description();
render_device.create_bind_group_layout(&*description, &bind_group_layout_entries)
});
commands.insert_resource(VolumetricFogPipeline {
mesh_view_layouts: mesh_view_layouts.clone(),
volumetric_view_bind_group_layouts: bind_group_layouts,
shader: load_embedded_asset!(asset_server.as_ref(), "volumetric_fog.wgsl"),
});
} }
/// Extracts [`VolumetricFog`], [`FogVolume`], and [`VolumetricLight`]s /// Extracts [`VolumetricFog`], [`FogVolume`], and [`VolumetricLight`]s

View File

@ -6,7 +6,7 @@ use crate::{
use bevy_app::{App, Plugin, PostUpdate, Startup, Update}; use bevy_app::{App, Plugin, PostUpdate, Startup, Update};
use bevy_asset::{ use bevy_asset::{
embedded_asset, load_embedded_asset, prelude::AssetChanged, AsAssetId, Asset, AssetApp, embedded_asset, load_embedded_asset, prelude::AssetChanged, AsAssetId, Asset, AssetApp,
AssetEventSystems, AssetId, Assets, Handle, UntypedAssetId, AssetEventSystems, AssetId, AssetServer, Assets, Handle, UntypedAssetId,
}; };
use bevy_color::{Color, ColorToComponents}; use bevy_color::{Color, ColorToComponents};
use bevy_core_pipeline::core_3d::{ use bevy_core_pipeline::core_3d::{
@ -25,7 +25,6 @@ use bevy_platform::{
hash::FixedHasher, hash::FixedHasher,
}; };
use bevy_reflect::{std_traits::ReflectDefault, Reflect}; use bevy_reflect::{std_traits::ReflectDefault, Reflect};
use bevy_render::camera::extract_cameras;
use bevy_render::{ use bevy_render::{
batching::gpu_preprocessing::{GpuPreprocessingMode, GpuPreprocessingSupport}, batching::gpu_preprocessing::{GpuPreprocessingMode, GpuPreprocessingSupport},
camera::ExtractedCamera, camera::ExtractedCamera,
@ -54,6 +53,7 @@ use bevy_render::{
}, },
Extract, Render, RenderApp, RenderDebugFlags, RenderSystems, Extract, Render, RenderApp, RenderDebugFlags, RenderSystems,
}; };
use bevy_render::{camera::extract_cameras, RenderStartup};
use core::{hash::Hash, ops::Range}; use core::{hash::Hash, ops::Range};
use tracing::error; use tracing::error;
@ -132,6 +132,7 @@ impl Plugin for WireframePlugin {
Node3d::PostProcessing, Node3d::PostProcessing,
), ),
) )
.add_systems(RenderStartup, init_wireframe_3d_pipeline)
.add_systems( .add_systems(
ExtractSchedule, ExtractSchedule,
( (
@ -153,13 +154,6 @@ impl Plugin for WireframePlugin {
), ),
); );
} }
fn finish(&self, app: &mut App) {
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};
render_app.init_resource::<Wireframe3dPipeline>();
}
} }
/// Enables wireframe rendering for any entity it is attached to. /// Enables wireframe rendering for any entity it is attached to.
@ -331,13 +325,15 @@ pub struct Wireframe3dPipeline {
shader: Handle<Shader>, shader: Handle<Shader>,
} }
impl FromWorld for Wireframe3dPipeline { pub fn init_wireframe_3d_pipeline(
fn from_world(render_world: &mut World) -> Self { mut commands: Commands,
Wireframe3dPipeline { mesh_pipeline: Res<MeshPipeline>,
mesh_pipeline: render_world.resource::<MeshPipeline>().clone(), asset_server: Res<AssetServer>,
shader: load_embedded_asset!(render_world, "render/wireframe.wgsl"), ) {
} commands.insert_resource(Wireframe3dPipeline {
} mesh_pipeline: mesh_pipeline.clone(),
shader: load_embedded_asset!(asset_server.as_ref(), "render/wireframe.wgsl"),
});
} }
impl SpecializedMeshPipeline for Wireframe3dPipeline { impl SpecializedMeshPipeline for Wireframe3dPipeline {

View File

@ -20,6 +20,13 @@ The following are the (public) resources that are now initialized in `RenderStar
- `UiPipeline` - `UiPipeline`
- `UiMaterialPipeline<M>` - `UiMaterialPipeline<M>`
- `UiTextureSlicePipeline` - `UiTextureSlicePipeline`
- `VolumetricFogPipeline`
- `DeferredLightingLayout`
- `RenderLightmaps`
- `PrepassPipeline`
- `PrepassViewBindGroup`
- `Wireframe3dPipeline`
- `MaterialPipeline`
The vast majority of cases for initializing render resources look like so (in Bevy 0.16): The vast majority of cases for initializing render resources look like so (in Bevy 0.16):