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_image::BevyDefault as _;
11use bevy_render::{
12    globals::GlobalsUniform,
13    render_resource::{
14        binding_types::{
15            sampler, texture_2d, texture_2d_multisampled, texture_depth_2d,
16            texture_depth_2d_multisampled, uniform_buffer_sized,
17        },
18        BindGroupLayout, BindGroupLayoutEntries, CachedRenderPipelineId, ColorTargetState,
19        ColorWrites, FragmentState, PipelineCache, RenderPipelineDescriptor, Sampler,
20        SamplerBindingType, SamplerDescriptor, ShaderStages, ShaderType, SpecializedRenderPipeline,
21        SpecializedRenderPipelines, TextureFormat, TextureSampleType,
22    },
23    renderer::RenderDevice,
24    view::{ExtractedView, Msaa, ViewTarget},
25};
26use bevy_shader::{Shader, ShaderDefVal};
27use bevy_utils::default;
28
29use super::MotionBlurUniform;
30
31#[derive(Resource)]
32pub struct MotionBlurPipeline {
33    pub(crate) sampler: Sampler,
34    pub(crate) layout: BindGroupLayout,
35    pub(crate) layout_msaa: BindGroupLayout,
36    pub(crate) fullscreen_shader: FullscreenShader,
37    pub(crate) fragment_shader: Handle<Shader>,
38}
39
40impl MotionBlurPipeline {
41    pub(crate) fn new(
42        render_device: &RenderDevice,
43        fullscreen_shader: FullscreenShader,
44        fragment_shader: Handle<Shader>,
45    ) -> Self {
46        let mb_layout = &BindGroupLayoutEntries::sequential(
47            ShaderStages::FRAGMENT,
48            (
49                // View target (read)
50                texture_2d(TextureSampleType::Float { filterable: true }),
51                // Motion Vectors
52                texture_2d(TextureSampleType::Float { filterable: true }),
53                // Depth
54                texture_depth_2d(),
55                // Linear Sampler
56                sampler(SamplerBindingType::Filtering),
57                // Motion blur settings uniform input
58                uniform_buffer_sized(false, Some(MotionBlurUniform::min_size())),
59                // Globals uniform input
60                uniform_buffer_sized(false, Some(GlobalsUniform::min_size())),
61            ),
62        );
63
64        let mb_layout_msaa = &BindGroupLayoutEntries::sequential(
65            ShaderStages::FRAGMENT,
66            (
67                // View target (read)
68                texture_2d(TextureSampleType::Float { filterable: true }),
69                // Motion Vectors
70                texture_2d_multisampled(TextureSampleType::Float { filterable: false }),
71                // Depth
72                texture_depth_2d_multisampled(),
73                // Linear Sampler
74                sampler(SamplerBindingType::Filtering),
75                // Motion blur settings uniform input
76                uniform_buffer_sized(false, Some(MotionBlurUniform::min_size())),
77                // Globals uniform input
78                uniform_buffer_sized(false, Some(GlobalsUniform::min_size())),
79            ),
80        );
81
82        let sampler = render_device.create_sampler(&SamplerDescriptor::default());
83        let layout = render_device.create_bind_group_layout("motion_blur_layout", mb_layout);
84        let layout_msaa =
85            render_device.create_bind_group_layout("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    hdr: bool,
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("NO_DEPTH_TEXTURE_SUPPORT".into());
136            shader_defs.push("SIXTEEN_BYTE_ALIGNMENT".into());
137        }
138
139        RenderPipelineDescriptor {
140            label: Some("motion_blur_pipeline".into()),
141            layout,
142            vertex: self.fullscreen_shader.to_vertex_state(),
143            fragment: Some(FragmentState {
144                shader: self.fragment_shader.clone(),
145                shader_defs,
146                targets: vec![Some(ColorTargetState {
147                    format: if key.hdr {
148                        ViewTarget::TEXTURE_FORMAT_HDR
149                    } else {
150                        TextureFormat::bevy_default()
151                    },
152                    blend: None,
153                    write_mask: ColorWrites::ALL,
154                })],
155                ..default()
156            }),
157            ..default()
158        }
159    }
160}
161
162#[derive(Component)]
163pub struct MotionBlurPipelineId(pub CachedRenderPipelineId);
164
165pub(crate) fn prepare_motion_blur_pipelines(
166    mut commands: Commands,
167    pipeline_cache: Res<PipelineCache>,
168    mut pipelines: ResMut<SpecializedRenderPipelines<MotionBlurPipeline>>,
169    pipeline: Res<MotionBlurPipeline>,
170    views: Query<(Entity, &ExtractedView, &Msaa), With<MotionBlurUniform>>,
171) {
172    for (entity, view, msaa) in &views {
173        let pipeline_id = pipelines.specialize(
174            &pipeline_cache,
175            &pipeline,
176            MotionBlurPipelineKey {
177                hdr: view.hdr,
178                samples: msaa.samples(),
179            },
180        );
181
182        commands
183            .entity(entity)
184            .insert(MotionBlurPipelineId(pipeline_id));
185    }
186}