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 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 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 = 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}