bevy_post_process/motion_blur/
pipeline.rs1use 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 BindGroupLayoutDescriptor, BindGroupLayoutEntries, CachedRenderPipelineId,
19 ColorTargetState, ColorWrites, FragmentState, PipelineCache, RenderPipelineDescriptor,
20 Sampler, SamplerBindingType, SamplerDescriptor, ShaderStages, ShaderType,
21 SpecializedRenderPipeline, 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: BindGroupLayoutDescriptor,
35 pub(crate) layout_msaa: BindGroupLayoutDescriptor,
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 texture_2d(TextureSampleType::Float { filterable: true }),
51 texture_2d(TextureSampleType::Float { filterable: true }),
53 texture_depth_2d(),
55 sampler(SamplerBindingType::Filtering),
57 uniform_buffer_sized(false, Some(MotionBlurUniform::min_size())),
59 uniform_buffer_sized(false, Some(GlobalsUniform::min_size())),
61 ),
62 );
63
64 let mb_layout_msaa = &BindGroupLayoutEntries::sequential(
65 ShaderStages::FRAGMENT,
66 (
67 texture_2d(TextureSampleType::Float { filterable: true }),
69 texture_2d_multisampled(TextureSampleType::Float { filterable: false }),
71 texture_depth_2d_multisampled(),
73 sampler(SamplerBindingType::Filtering),
75 uniform_buffer_sized(false, Some(MotionBlurUniform::min_size())),
77 uniform_buffer_sized(false, Some(GlobalsUniform::min_size())),
79 ),
80 );
81
82 let sampler = render_device.create_sampler(&SamplerDescriptor::default());
83 let layout = BindGroupLayoutDescriptor::new("motion_blur_layout", mb_layout);
84 let layout_msaa = BindGroupLayoutDescriptor::new("motion_blur_layout_msaa", mb_layout_msaa);
85
86 Self {
87 sampler,
88 layout,
89 layout_msaa,
90 fullscreen_shader,
91 fragment_shader,
92 }
93 }
94}
95
96pub fn init_motion_blur_pipeline(
97 mut commands: Commands,
98 render_device: Res<RenderDevice>,
99 fullscreen_shader: Res<FullscreenShader>,
100 asset_server: Res<AssetServer>,
101) {
102 let fullscreen_shader = fullscreen_shader.clone();
103 let fragment_shader = load_embedded_asset!(asset_server.as_ref(), "motion_blur.wgsl");
104 commands.insert_resource(MotionBlurPipeline::new(
105 &render_device,
106 fullscreen_shader,
107 fragment_shader,
108 ));
109}
110
111#[derive(PartialEq, Eq, Hash, Clone, Copy)]
112pub struct MotionBlurPipelineKey {
113 hdr: bool,
114 samples: u32,
115}
116
117impl SpecializedRenderPipeline for MotionBlurPipeline {
118 type Key = MotionBlurPipelineKey;
119
120 fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
121 let layout = match key.samples {
122 1 => vec![self.layout.clone()],
123 _ => vec![self.layout_msaa.clone()],
124 };
125
126 let mut shader_defs = vec![];
127
128 if key.samples > 1 {
129 shader_defs.push(ShaderDefVal::from("MULTISAMPLED"));
130 }
131
132 #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
133 {
134 shader_defs.push("NO_DEPTH_TEXTURE_SUPPORT".into());
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: if key.hdr {
147 ViewTarget::TEXTURE_FORMAT_HDR
148 } else {
149 TextureFormat::bevy_default()
150 },
151 blend: None,
152 write_mask: ColorWrites::ALL,
153 })],
154 ..default()
155 }),
156 ..default()
157 }
158 }
159}
160
161#[derive(Component)]
162pub struct MotionBlurPipelineId(pub CachedRenderPipelineId);
163
164pub(crate) fn prepare_motion_blur_pipelines(
165 mut commands: Commands,
166 pipeline_cache: Res<PipelineCache>,
167 mut pipelines: ResMut<SpecializedRenderPipelines<MotionBlurPipeline>>,
168 pipeline: Res<MotionBlurPipeline>,
169 views: Query<(Entity, &ExtractedView, &Msaa), With<MotionBlurUniform>>,
170) {
171 for (entity, view, msaa) in &views {
172 let pipeline_id = pipelines.specialize(
173 &pipeline_cache,
174 &pipeline,
175 MotionBlurPipelineKey {
176 hdr: view.hdr,
177 samples: msaa.samples(),
178 },
179 );
180
181 commands
182 .entity(entity)
183 .insert(MotionBlurPipelineId(pipeline_id));
184 }
185}