bevy_core_pipeline/motion_blur/
pipeline.rs1use 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 texture_2d(TextureSampleType::Float { filterable: true }),
45 texture_2d(TextureSampleType::Float { filterable: true }),
47 texture_depth_2d(),
49 sampler(SamplerBindingType::Filtering),
51 uniform_buffer_sized(false, Some(MotionBlurUniform::min_size())),
53 uniform_buffer_sized(false, Some(GlobalsUniform::min_size())),
55 ),
56 );
57
58 let mb_layout_msaa = &BindGroupLayoutEntries::sequential(
59 ShaderStages::FRAGMENT,
60 (
61 texture_2d(TextureSampleType::Float { filterable: true }),
63 texture_2d_multisampled(TextureSampleType::Float { filterable: false }),
65 texture_depth_2d_multisampled(),
67 sampler(SamplerBindingType::Filtering),
69 uniform_buffer_sized(false, Some(MotionBlurUniform::min_size())),
71 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}