Skip to main content

bevy_post_process/motion_blur/
pipeline.rs

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