bevy_core_pipeline/motion_blur/
node.rs

1use bevy_ecs::{query::QueryItem, world::World};
2use bevy_render::{
3    extract_component::ComponentUniforms,
4    globals::GlobalsBuffer,
5    render_graph::{NodeRunError, RenderGraphContext, ViewNode},
6    render_resource::{
7        BindGroupEntries, Operations, PipelineCache, RenderPassColorAttachment,
8        RenderPassDescriptor,
9    },
10    renderer::RenderContext,
11    view::{Msaa, ViewTarget},
12};
13
14use crate::prepass::ViewPrepassTextures;
15
16use super::{
17    pipeline::{MotionBlurPipeline, MotionBlurPipelineId},
18    MotionBlur,
19};
20
21#[derive(Default)]
22pub struct MotionBlurNode;
23
24impl ViewNode for MotionBlurNode {
25    type ViewQuery = (
26        &'static ViewTarget,
27        &'static MotionBlurPipelineId,
28        &'static ViewPrepassTextures,
29        &'static MotionBlur,
30        &'static Msaa,
31    );
32    fn run(
33        &self,
34        _graph: &mut RenderGraphContext,
35        render_context: &mut RenderContext,
36        (view_target, pipeline_id, prepass_textures, motion_blur, msaa): QueryItem<Self::ViewQuery>,
37        world: &World,
38    ) -> Result<(), NodeRunError> {
39        if motion_blur.samples == 0 || motion_blur.shutter_angle <= 0.0 {
40            return Ok(()); // We can skip running motion blur in these cases.
41        }
42
43        let motion_blur_pipeline = world.resource::<MotionBlurPipeline>();
44        let pipeline_cache = world.resource::<PipelineCache>();
45        let settings_uniforms = world.resource::<ComponentUniforms<MotionBlur>>();
46        let Some(pipeline) = pipeline_cache.get_render_pipeline(pipeline_id.0) else {
47            return Ok(());
48        };
49
50        let Some(settings_binding) = settings_uniforms.uniforms().binding() else {
51            return Ok(());
52        };
53        let (Some(prepass_motion_vectors_texture), Some(prepass_depth_texture)) =
54            (&prepass_textures.motion_vectors, &prepass_textures.depth)
55        else {
56            return Ok(());
57        };
58        let Some(globals_uniforms) = world.resource::<GlobalsBuffer>().buffer.binding() else {
59            return Ok(());
60        };
61
62        let post_process = view_target.post_process_write();
63
64        let layout = if msaa.samples() == 1 {
65            &motion_blur_pipeline.layout
66        } else {
67            &motion_blur_pipeline.layout_msaa
68        };
69
70        let bind_group = render_context.render_device().create_bind_group(
71            Some("motion_blur_bind_group"),
72            layout,
73            &BindGroupEntries::sequential((
74                post_process.source,
75                &prepass_motion_vectors_texture.texture.default_view,
76                &prepass_depth_texture.texture.default_view,
77                &motion_blur_pipeline.sampler,
78                settings_binding.clone(),
79                globals_uniforms.clone(),
80            )),
81        );
82
83        let mut render_pass = render_context.begin_tracked_render_pass(RenderPassDescriptor {
84            label: Some("motion_blur_pass"),
85            color_attachments: &[Some(RenderPassColorAttachment {
86                view: post_process.destination,
87                resolve_target: None,
88                ops: Operations::default(),
89            })],
90            depth_stencil_attachment: None,
91            timestamp_writes: None,
92            occlusion_query_set: None,
93        });
94
95        render_pass.set_render_pipeline(pipeline);
96        render_pass.set_bind_group(0, &bind_group, &[]);
97        render_pass.draw(0..3, 0..1);
98
99        Ok(())
100    }
101}