bevy_core_pipeline/motion_blur/
pipeline.rs

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