1use 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, Or, 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<RenderApp> for NoBackgroundMotionVectors {
60 type Target = Self;
61}
62
63impl ExtractComponent<RenderApp> 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.wesl");
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 cleanup_background_motion_vectors.in_set(RenderSystems::Prepare),
129 prepare_background_motion_vectors_pipelines.in_set(RenderSystems::Prepare),
130 prepare_background_motion_vectors_bind_groups
131 .in_set(RenderSystems::PrepareBindGroups),
132 ),
133 );
134 }
135}
136
137#[derive(Resource)]
138struct BackgroundMotionVectorsPipeline {
139 bind_group_layout: BindGroupLayoutDescriptor,
140 fullscreen_shader: FullscreenShader,
141 fragment_shader: Handle<Shader>,
142}
143
144#[derive(PartialEq, Eq, Hash, Clone, Copy)]
145struct BackgroundMotionVectorsPipelineKey {
146 samples: u32,
147 normal_prepass: bool,
148}
149
150fn init_background_motion_vectors_pipeline(
151 mut commands: Commands,
152 fullscreen_shader: Res<FullscreenShader>,
153 asset_server: Res<AssetServer>,
154) {
155 commands.insert_resource(BackgroundMotionVectorsPipeline {
156 bind_group_layout: BindGroupLayoutDescriptor::new(
157 "background_motion_vectors_bind_group_layout",
158 &BindGroupLayoutEntries::sequential(
159 ShaderStages::FRAGMENT,
160 (
161 uniform_buffer::<ViewUniform>(true),
162 uniform_buffer::<PreviousViewData>(true),
163 ),
164 ),
165 ),
166 fullscreen_shader: fullscreen_shader.clone(),
167 fragment_shader: load_embedded_asset!(
168 asset_server.as_ref(),
169 "background_motion_vectors.wesl"
170 ),
171 });
172}
173
174impl SpecializedRenderPipeline for BackgroundMotionVectorsPipeline {
175 type Key = BackgroundMotionVectorsPipelineKey;
176
177 fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
178 let mut targets = prepass_target_descriptors(key.normal_prepass, true, false);
179 for target in
182 targets
183 .iter_mut()
184 .enumerate()
185 .filter_map(|(i, t)| if i == 1 { None } else { t.as_mut() })
186 {
187 target.write_mask = bevy_render::render_resource::ColorWrites::empty();
188 }
189
190 RenderPipelineDescriptor {
191 label: Some("background_motion_vectors_pipeline".into()),
192 layout: vec![self.bind_group_layout.clone()],
193 vertex: self.fullscreen_shader.to_vertex_state(),
194 depth_stencil: Some(DepthStencilState {
195 format: CORE_3D_DEPTH_FORMAT,
196 depth_write_enabled: Some(false),
197 depth_compare: Some(CompareFunction::GreaterEqual),
198 stencil: default(),
199 bias: default(),
200 }),
201 multisample: MultisampleState {
202 count: key.samples,
203 mask: !0,
204 alpha_to_coverage_enabled: false,
205 },
206 fragment: Some(FragmentState {
207 shader: self.fragment_shader.clone(),
208 targets,
209 ..default()
210 }),
211 ..default()
212 }
213 }
214}
215
216fn cleanup_background_motion_vectors(
217 mut commands: Commands,
218 views: Query<
219 Entity,
220 (
221 With<BackgroundMotionVectorsPipelineId>,
222 Or<(
223 Without<MotionVectorPrepass>,
224 With<NoBackgroundMotionVectors>,
225 )>,
226 ),
227 >,
228) {
229 for entity in &views {
230 commands.entity(entity).remove::<(
231 BackgroundMotionVectorsPipelineId,
232 BackgroundMotionVectorsBindGroup,
233 )>();
234 }
235}
236
237fn prepare_background_motion_vectors_pipelines(
238 mut commands: Commands,
239 pipeline_cache: Res<PipelineCache>,
240 mut pipelines: ResMut<SpecializedRenderPipelines<BackgroundMotionVectorsPipeline>>,
241 pipeline: Res<BackgroundMotionVectorsPipeline>,
242 views: Query<
243 (Entity, Has<NormalPrepass>, &Msaa),
244 (
245 With<MotionVectorPrepass>,
246 Without<NoBackgroundMotionVectors>,
247 ),
248 >,
249) {
250 for (entity, normal_prepass, msaa) in &views {
251 let id = pipelines.specialize(
252 &pipeline_cache,
253 &pipeline,
254 BackgroundMotionVectorsPipelineKey {
255 samples: msaa.samples(),
256 normal_prepass,
257 },
258 );
259 commands
260 .entity(entity)
261 .insert(BackgroundMotionVectorsPipelineId(id));
262 }
263}
264
265fn prepare_background_motion_vectors_bind_groups(
266 mut commands: Commands,
267 pipeline: Res<BackgroundMotionVectorsPipeline>,
268 view_uniforms: Res<ViewUniforms>,
269 prev_view_uniforms: Res<PreviousViewUniforms>,
270 render_device: Res<RenderDevice>,
271 pipeline_cache: Res<PipelineCache>,
272 views: Query<
273 Entity,
274 (
275 With<MotionVectorPrepass>,
276 Without<NoBackgroundMotionVectors>,
277 ),
278 >,
279) {
280 for entity in &views {
281 let (Some(view_binding), Some(prev_view_binding)) = (
282 view_uniforms.uniforms.binding(),
283 prev_view_uniforms.uniforms.binding(),
284 ) else {
285 continue;
286 };
287 let bind_group = render_device.create_bind_group(
288 "background_motion_vectors_bind_group",
289 &pipeline_cache.get_bind_group_layout(&pipeline.bind_group_layout),
290 &BindGroupEntries::sequential((view_binding, prev_view_binding)),
291 );
292 commands
293 .entity(entity)
294 .insert(BackgroundMotionVectorsBindGroup(bind_group));
295 }
296}