bevy_core_pipeline/motion_blur/
pipeline.rs1use bevy_ecs::{
2 component::Component,
3 entity::Entity,
4 query::With,
5 system::{Commands, Query, Res, ResMut, Resource},
6 world::FromWorld,
7};
8use bevy_image::BevyDefault as _;
9use bevy_render::{
10 globals::GlobalsUniform,
11 render_resource::{
12 binding_types::{
13 sampler, texture_2d, texture_2d_multisampled, texture_depth_2d,
14 texture_depth_2d_multisampled, uniform_buffer_sized,
15 },
16 BindGroupLayout, BindGroupLayoutEntries, CachedRenderPipelineId, ColorTargetState,
17 ColorWrites, FragmentState, MultisampleState, PipelineCache, PrimitiveState,
18 RenderPipelineDescriptor, Sampler, SamplerBindingType, SamplerDescriptor, ShaderDefVal,
19 ShaderStages, ShaderType, SpecializedRenderPipeline, SpecializedRenderPipelines,
20 TextureFormat, TextureSampleType,
21 },
22 renderer::RenderDevice,
23 view::{ExtractedView, Msaa, ViewTarget},
24};
25
26use crate::fullscreen_vertex_shader::fullscreen_shader_vertex_state;
27
28use super::{MotionBlur, MOTION_BLUR_SHADER_HANDLE};
29
30#[derive(Resource)]
31pub struct MotionBlurPipeline {
32 pub(crate) sampler: Sampler,
33 pub(crate) layout: BindGroupLayout,
34 pub(crate) layout_msaa: BindGroupLayout,
35}
36
37impl MotionBlurPipeline {
38 pub(crate) fn new(render_device: &RenderDevice) -> Self {
39 let mb_layout = &BindGroupLayoutEntries::sequential(
40 ShaderStages::FRAGMENT,
41 (
42 texture_2d(TextureSampleType::Float { filterable: true }),
44 texture_2d(TextureSampleType::Float { filterable: true }),
46 texture_depth_2d(),
48 sampler(SamplerBindingType::Filtering),
50 uniform_buffer_sized(false, Some(MotionBlur::min_size())),
52 uniform_buffer_sized(false, Some(GlobalsUniform::min_size())),
54 ),
55 );
56
57 let mb_layout_msaa = &BindGroupLayoutEntries::sequential(
58 ShaderStages::FRAGMENT,
59 (
60 texture_2d(TextureSampleType::Float { filterable: true }),
62 texture_2d_multisampled(TextureSampleType::Float { filterable: false }),
64 texture_depth_2d_multisampled(),
66 sampler(SamplerBindingType::Filtering),
68 uniform_buffer_sized(false, Some(MotionBlur::min_size())),
70 uniform_buffer_sized(false, Some(GlobalsUniform::min_size())),
72 ),
73 );
74
75 let sampler = render_device.create_sampler(&SamplerDescriptor::default());
76 let layout = render_device.create_bind_group_layout("motion_blur_layout", mb_layout);
77 let layout_msaa =
78 render_device.create_bind_group_layout("motion_blur_layout_msaa", mb_layout_msaa);
79
80 Self {
81 sampler,
82 layout,
83 layout_msaa,
84 }
85 }
86}
87
88impl FromWorld for MotionBlurPipeline {
89 fn from_world(render_world: &mut bevy_ecs::world::World) -> Self {
90 let render_device = render_world.resource::<RenderDevice>().clone();
91 MotionBlurPipeline::new(&render_device)
92 }
93}
94
95#[derive(PartialEq, Eq, Hash, Clone, Copy)]
96pub struct MotionBlurPipelineKey {
97 hdr: bool,
98 samples: u32,
99}
100
101impl SpecializedRenderPipeline for MotionBlurPipeline {
102 type Key = MotionBlurPipelineKey;
103
104 fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
105 let layout = match key.samples {
106 1 => vec![self.layout.clone()],
107 _ => vec![self.layout_msaa.clone()],
108 };
109
110 let mut shader_defs = vec![];
111
112 if key.samples > 1 {
113 shader_defs.push(ShaderDefVal::from("MULTISAMPLED"));
114 }
115
116 #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
117 {
118 shader_defs.push("NO_DEPTH_TEXTURE_SUPPORT".into());
119 shader_defs.push("SIXTEEN_BYTE_ALIGNMENT".into());
120 }
121
122 RenderPipelineDescriptor {
123 label: Some("motion_blur_pipeline".into()),
124 layout,
125 vertex: fullscreen_shader_vertex_state(),
126 fragment: Some(FragmentState {
127 shader: MOTION_BLUR_SHADER_HANDLE,
128 shader_defs,
129 entry_point: "fragment".into(),
130 targets: vec![Some(ColorTargetState {
131 format: if key.hdr {
132 ViewTarget::TEXTURE_FORMAT_HDR
133 } else {
134 TextureFormat::bevy_default()
135 },
136 blend: None,
137 write_mask: ColorWrites::ALL,
138 })],
139 }),
140 primitive: PrimitiveState::default(),
141 depth_stencil: None,
142 multisample: MultisampleState::default(),
143 push_constant_ranges: vec![],
144 zero_initialize_workgroup_memory: false,
145 }
146 }
147}
148
149#[derive(Component)]
150pub struct MotionBlurPipelineId(pub CachedRenderPipelineId);
151
152pub(crate) fn prepare_motion_blur_pipelines(
153 mut commands: Commands,
154 pipeline_cache: Res<PipelineCache>,
155 mut pipelines: ResMut<SpecializedRenderPipelines<MotionBlurPipeline>>,
156 pipeline: Res<MotionBlurPipeline>,
157 views: Query<(Entity, &ExtractedView, &Msaa), With<MotionBlur>>,
158) {
159 for (entity, view, msaa) in &views {
160 let pipeline_id = pipelines.specialize(
161 &pipeline_cache,
162 &pipeline,
163 MotionBlurPipelineKey {
164 hdr: view.hdr,
165 samples: msaa.samples(),
166 },
167 );
168
169 commands
170 .entity(entity)
171 .insert(MotionBlurPipelineId(pipeline_id));
172 }
173}