bevy_core_pipeline/prepass/
background_motion_vectors.rs1use bevy_app::{App, Plugin};
11use bevy_asset::{embedded_asset, load_embedded_asset, AssetServer, Handle};
12use bevy_ecs::{
13 component::Component,
14 entity::Entity,
15 query::{Has, QueryItem, With, Without},
16 reflect::ReflectComponent,
17 resource::Resource,
18 schedule::IntoScheduleConfigs,
19 system::{lifetimeless::Read, Commands, Query, Res, ResMut},
20};
21use bevy_log::warn;
22use bevy_reflect::{std_traits::ReflectDefault, Reflect};
23use bevy_render::{
24 extract_component::{ExtractComponent, ExtractComponentPlugin},
25 render_resource::{
26 binding_types::uniform_buffer, BindGroup, BindGroupEntries, BindGroupLayoutDescriptor,
27 BindGroupLayoutEntries, CachedRenderPipelineId, CompareFunction, DepthStencilState,
28 DownlevelFlags, FragmentState, MultisampleState, PipelineCache, RenderPipelineDescriptor,
29 ShaderStages, SpecializedRenderPipeline, SpecializedRenderPipelines,
30 },
31 renderer::{RenderAdapter, RenderDevice},
32 sync_component::SyncComponent,
33 view::{Msaa, ViewUniform, ViewUniforms},
34 GpuResourceAppExt, Render, RenderApp, RenderStartup, RenderSystems,
35};
36use bevy_shader::Shader;
37use bevy_utils::prelude::default;
38
39use crate::{
40 core_3d::CORE_3D_DEPTH_FORMAT,
41 prepass::{
42 prepass_target_descriptors, MotionVectorPrepass, NormalPrepass, PreviousViewData,
43 PreviousViewUniforms,
44 },
45 FullscreenShader,
46};
47
48#[derive(Component, Default, Reflect, Clone)]
56#[reflect(Component, Default, Clone)]
57pub struct NoBackgroundMotionVectors;
58
59impl SyncComponent for NoBackgroundMotionVectors {
60 type Target = Self;
61}
62
63impl ExtractComponent for NoBackgroundMotionVectors {
64 type QueryData = Read<NoBackgroundMotionVectors>;
65 type QueryFilter = ();
66 type Out = Self;
67
68 fn extract_component(_item: QueryItem<'_, '_, Self::QueryData>) -> Option<Self::Out> {
69 Some(NoBackgroundMotionVectors)
70 }
71}
72
73#[derive(Component)]
75pub struct BackgroundMotionVectorsPipelineId(pub CachedRenderPipelineId);
76
77#[derive(Component)]
79pub struct BackgroundMotionVectorsBindGroup(pub BindGroup);
80
81#[derive(Default)]
86pub struct BackgroundMotionVectorsPlugin;
87
88impl BackgroundMotionVectorsPlugin {
89 pub fn required_downlevel_flags() -> DownlevelFlags {
91 DownlevelFlags::INDEPENDENT_BLEND
92 }
93}
94
95impl Plugin for BackgroundMotionVectorsPlugin {
96 fn build(&self, app: &mut App) {
97 embedded_asset!(app, "background_motion_vectors.wgsl");
98 app.register_type::<NoBackgroundMotionVectors>()
99 .add_plugins(ExtractComponentPlugin::<NoBackgroundMotionVectors>::default());
100
101 let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
102 return;
103 };
104 render_app.init_gpu_resource::<PreviousViewUniforms>();
105 }
106
107 fn finish(&self, app: &mut App) {
108 let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
109 return;
110 };
111
112 let render_adapter = render_app.world().resource::<RenderAdapter>();
113 let downlevel_flags = render_adapter.get_downlevel_capabilities().flags;
114 if !downlevel_flags.contains(BackgroundMotionVectorsPlugin::required_downlevel_flags()) {
115 warn!(
116 "BackgroundMotionVectorsPlugin not loaded. GPU lacks support for required downlevel capability flags: {:?}.",
117 BackgroundMotionVectorsPlugin::required_downlevel_flags().difference(downlevel_flags)
118 );
119 return;
120 }
121
122 render_app
123 .init_gpu_resource::<SpecializedRenderPipelines<BackgroundMotionVectorsPipeline>>()
124 .add_systems(RenderStartup, init_background_motion_vectors_pipeline)
125 .add_systems(
126 Render,
127 (
128 prepare_background_motion_vectors_pipelines.in_set(RenderSystems::Prepare),
129 prepare_background_motion_vectors_bind_groups
130 .in_set(RenderSystems::PrepareBindGroups),
131 ),
132 );
133 }
134}
135
136#[derive(Resource)]
137struct BackgroundMotionVectorsPipeline {
138 bind_group_layout: BindGroupLayoutDescriptor,
139 fullscreen_shader: FullscreenShader,
140 fragment_shader: Handle<Shader>,
141}
142
143#[derive(PartialEq, Eq, Hash, Clone, Copy)]
144struct BackgroundMotionVectorsPipelineKey {
145 samples: u32,
146 normal_prepass: bool,
147}
148
149fn init_background_motion_vectors_pipeline(
150 mut commands: Commands,
151 fullscreen_shader: Res<FullscreenShader>,
152 asset_server: Res<AssetServer>,
153) {
154 commands.insert_resource(BackgroundMotionVectorsPipeline {
155 bind_group_layout: BindGroupLayoutDescriptor::new(
156 "background_motion_vectors_bind_group_layout",
157 &BindGroupLayoutEntries::sequential(
158 ShaderStages::FRAGMENT,
159 (
160 uniform_buffer::<ViewUniform>(true),
161 uniform_buffer::<PreviousViewData>(true),
162 ),
163 ),
164 ),
165 fullscreen_shader: fullscreen_shader.clone(),
166 fragment_shader: load_embedded_asset!(
167 asset_server.as_ref(),
168 "background_motion_vectors.wgsl"
169 ),
170 });
171}
172
173impl SpecializedRenderPipeline for BackgroundMotionVectorsPipeline {
174 type Key = BackgroundMotionVectorsPipelineKey;
175
176 fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
177 let mut targets = prepass_target_descriptors(key.normal_prepass, true, false);
178 for target in
181 targets
182 .iter_mut()
183 .enumerate()
184 .filter_map(|(i, t)| if i == 1 { None } else { t.as_mut() })
185 {
186 target.write_mask = bevy_render::render_resource::ColorWrites::empty();
187 }
188
189 RenderPipelineDescriptor {
190 label: Some("background_motion_vectors_pipeline".into()),
191 layout: vec![self.bind_group_layout.clone()],
192 vertex: self.fullscreen_shader.to_vertex_state(),
193 depth_stencil: Some(DepthStencilState {
194 format: CORE_3D_DEPTH_FORMAT,
195 depth_write_enabled: Some(false),
196 depth_compare: Some(CompareFunction::GreaterEqual),
197 stencil: default(),
198 bias: default(),
199 }),
200 multisample: MultisampleState {
201 count: key.samples,
202 mask: !0,
203 alpha_to_coverage_enabled: false,
204 },
205 fragment: Some(FragmentState {
206 shader: self.fragment_shader.clone(),
207 targets,
208 ..default()
209 }),
210 ..default()
211 }
212 }
213}
214
215fn prepare_background_motion_vectors_pipelines(
216 mut commands: Commands,
217 pipeline_cache: Res<PipelineCache>,
218 mut pipelines: ResMut<SpecializedRenderPipelines<BackgroundMotionVectorsPipeline>>,
219 pipeline: Res<BackgroundMotionVectorsPipeline>,
220 views: Query<
221 (Entity, Has<NormalPrepass>, &Msaa),
222 (
223 With<MotionVectorPrepass>,
224 Without<NoBackgroundMotionVectors>,
225 ),
226 >,
227) {
228 for (entity, normal_prepass, msaa) in &views {
229 let id = pipelines.specialize(
230 &pipeline_cache,
231 &pipeline,
232 BackgroundMotionVectorsPipelineKey {
233 samples: msaa.samples(),
234 normal_prepass,
235 },
236 );
237 commands
238 .entity(entity)
239 .insert(BackgroundMotionVectorsPipelineId(id));
240 }
241}
242
243fn prepare_background_motion_vectors_bind_groups(
244 mut commands: Commands,
245 pipeline: Res<BackgroundMotionVectorsPipeline>,
246 view_uniforms: Res<ViewUniforms>,
247 prev_view_uniforms: Res<PreviousViewUniforms>,
248 render_device: Res<RenderDevice>,
249 pipeline_cache: Res<PipelineCache>,
250 views: Query<
251 Entity,
252 (
253 With<MotionVectorPrepass>,
254 Without<NoBackgroundMotionVectors>,
255 ),
256 >,
257) {
258 for entity in &views {
259 let (Some(view_binding), Some(prev_view_binding)) = (
260 view_uniforms.uniforms.binding(),
261 prev_view_uniforms.uniforms.binding(),
262 ) else {
263 continue;
264 };
265 let bind_group = render_device.create_bind_group(
266 "background_motion_vectors_bind_group",
267 &pipeline_cache.get_bind_group_layout(&pipeline.bind_group_layout),
268 &BindGroupEntries::sequential((view_binding, prev_view_binding)),
269 );
270 commands
271 .entity(entity)
272 .insert(BackgroundMotionVectorsBindGroup(bind_group));
273 }
274}