bevy_core_pipeline/motion_blur/
pipeline.rs

1use 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                // View target (read)
43                texture_2d(TextureSampleType::Float { filterable: true }),
44                // Motion Vectors
45                texture_2d(TextureSampleType::Float { filterable: true }),
46                // Depth
47                texture_depth_2d(),
48                // Linear Sampler
49                sampler(SamplerBindingType::Filtering),
50                // Motion blur settings uniform input
51                uniform_buffer_sized(false, Some(MotionBlur::min_size())),
52                // Globals uniform input
53                uniform_buffer_sized(false, Some(GlobalsUniform::min_size())),
54            ),
55        );
56
57        let mb_layout_msaa = &BindGroupLayoutEntries::sequential(
58            ShaderStages::FRAGMENT,
59            (
60                // View target (read)
61                texture_2d(TextureSampleType::Float { filterable: true }),
62                // Motion Vectors
63                texture_2d_multisampled(TextureSampleType::Float { filterable: false }),
64                // Depth
65                texture_depth_2d_multisampled(),
66                // Linear Sampler
67                sampler(SamplerBindingType::Filtering),
68                // Motion blur settings uniform input
69                uniform_buffer_sized(false, Some(MotionBlur::min_size())),
70                // Globals uniform input
71                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}