Use RenderStartup in custom_post_processing example (#19886)

# Objective

- This example uses a FromWorld impl to initialize a resource on startup
- #19887

## Solution

- Use RenderStartup instead

## Testing

- The example still works as expected
This commit is contained in:
IceSentry 2025-06-30 19:54:05 -04:00 committed by GitHub
parent 83cd46a4dd
commit 735eb88db9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -27,7 +27,7 @@ use bevy::{
}, },
renderer::{RenderContext, RenderDevice}, renderer::{RenderContext, RenderDevice},
view::ViewTarget, view::ViewTarget,
RenderApp, RenderApp, RenderStartup,
}, },
}; };
@ -66,6 +66,10 @@ impl Plugin for PostProcessPlugin {
return; return;
}; };
// RenderStartup runs once on startup after all plugins are built
// It is useful to initialize data that will only live in the RenderApp
render_app.add_systems(RenderStartup, setup_pipeline);
render_app render_app
// Bevy's renderer uses a render graph which is a collection of nodes in a directed acyclic graph. // Bevy's renderer uses a render graph which is a collection of nodes in a directed acyclic graph.
// It currently runs on each view/camera and executes each node in the specified order. // It currently runs on each view/camera and executes each node in the specified order.
@ -97,17 +101,6 @@ impl Plugin for PostProcessPlugin {
), ),
); );
} }
fn finish(&self, app: &mut App) {
// We need to get the render app from the main app
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};
render_app
// Initialize the pipeline
.init_resource::<PostProcessPipeline>();
}
} }
#[derive(Debug, Hash, PartialEq, Eq, Clone, RenderLabel)] #[derive(Debug, Hash, PartialEq, Eq, Clone, RenderLabel)]
@ -233,69 +226,67 @@ struct PostProcessPipeline {
pipeline_id: CachedRenderPipelineId, pipeline_id: CachedRenderPipelineId,
} }
impl FromWorld for PostProcessPipeline { fn setup_pipeline(
fn from_world(world: &mut World) -> Self { mut commands: Commands,
let render_device = world.resource::<RenderDevice>(); render_device: Res<RenderDevice>,
asset_server: Res<AssetServer>,
// We need to define the bind group layout used for our pipeline fullscreen_shader: Res<FullscreenShader>,
let layout = render_device.create_bind_group_layout( pipeline_cache: Res<PipelineCache>,
"post_process_bind_group_layout", ) {
&BindGroupLayoutEntries::sequential( // We need to define the bind group layout used for our pipeline
// The layout entries will only be visible in the fragment stage let layout = render_device.create_bind_group_layout(
ShaderStages::FRAGMENT, "post_process_bind_group_layout",
( &BindGroupLayoutEntries::sequential(
// The screen texture // The layout entries will only be visible in the fragment stage
texture_2d(TextureSampleType::Float { filterable: true }), ShaderStages::FRAGMENT,
// The sampler that will be used to sample the screen texture (
sampler(SamplerBindingType::Filtering), // The screen texture
// The settings uniform that will control the effect texture_2d(TextureSampleType::Float { filterable: true }),
uniform_buffer::<PostProcessSettings>(true), // The sampler that will be used to sample the screen texture
), sampler(SamplerBindingType::Filtering),
// The settings uniform that will control the effect
uniform_buffer::<PostProcessSettings>(true),
), ),
); ),
);
// We can create the sampler here since it won't change at runtime and doesn't depend on the view
let sampler = render_device.create_sampler(&SamplerDescriptor::default());
// We can create the sampler here since it won't change at runtime and doesn't depend on the view // Get the shader handle
let sampler = render_device.create_sampler(&SamplerDescriptor::default()); let shader = asset_server.load(SHADER_ASSET_PATH);
// This will setup a fullscreen triangle for the vertex state.
// Get the shader handle let vertex_state = fullscreen_shader.to_vertex_state();
let shader = world.load_asset(SHADER_ASSET_PATH); let pipeline_id = pipeline_cache
// This will setup a fullscreen triangle for the vertex state. // This will add the pipeline to the cache and queue its creation
let vertex_state = world.resource::<FullscreenShader>().to_vertex_state(); .queue_render_pipeline(RenderPipelineDescriptor {
label: Some("post_process_pipeline".into()),
let pipeline_id = world layout: vec![layout.clone()],
.resource_mut::<PipelineCache>() vertex: vertex_state,
// This will add the pipeline to the cache and queue its creation fragment: Some(FragmentState {
.queue_render_pipeline(RenderPipelineDescriptor { shader,
label: Some("post_process_pipeline".into()), shader_defs: vec![],
layout: vec![layout.clone()], // Make sure this matches the entry point of your shader.
vertex: vertex_state, // It can be anything as long as it matches here and in the shader.
fragment: Some(FragmentState { entry_point: "fragment".into(),
shader, targets: vec![Some(ColorTargetState {
shader_defs: vec![], format: TextureFormat::bevy_default(),
// Make sure this matches the entry point of your shader. blend: None,
// It can be anything as long as it matches here and in the shader. write_mask: ColorWrites::ALL,
entry_point: "fragment".into(), })],
targets: vec![Some(ColorTargetState { }),
format: TextureFormat::bevy_default(), // All of the following properties are not important for this effect so just use the default values.
blend: None, // This struct doesn't have the Default trait implemented because not all fields can have a default value.
write_mask: ColorWrites::ALL, primitive: PrimitiveState::default(),
})], depth_stencil: None,
}), multisample: MultisampleState::default(),
// All of the following properties are not important for this effect so just use the default values. push_constant_ranges: vec![],
// This struct doesn't have the Default trait implemented because not all fields can have a default value. zero_initialize_workgroup_memory: false,
primitive: PrimitiveState::default(), });
depth_stencil: None, commands.insert_resource(PostProcessPipeline {
multisample: MultisampleState::default(), layout,
push_constant_ranges: vec![], sampler,
zero_initialize_workgroup_memory: false, pipeline_id,
}); });
Self {
layout,
sampler,
pipeline_id,
}
}
} }
// This is the component that will get passed to the shader // This is the component that will get passed to the shader