bevy_core_pipeline/upscaling/
node.rs

1use crate::{blit::BlitPipeline, upscaling::ViewUpscalingPipeline};
2use bevy_camera::{CameraOutputMode, ClearColor, ClearColorConfig};
3use bevy_ecs::{prelude::*, query::QueryItem};
4use bevy_render::{
5    camera::ExtractedCamera,
6    diagnostic::RecordDiagnostics,
7    render_graph::{NodeRunError, RenderGraphContext, ViewNode},
8    render_resource::{BindGroup, PipelineCache, RenderPassDescriptor, TextureViewId},
9    renderer::RenderContext,
10    view::ViewTarget,
11};
12use std::sync::Mutex;
13
14#[derive(Default)]
15pub struct UpscalingNode {
16    cached_texture_bind_group: Mutex<Option<(TextureViewId, BindGroup)>>,
17}
18
19impl ViewNode for UpscalingNode {
20    type ViewQuery = (
21        &'static ViewTarget,
22        &'static ViewUpscalingPipeline,
23        Option<&'static ExtractedCamera>,
24    );
25
26    fn run(
27        &self,
28        _graph: &mut RenderGraphContext,
29        render_context: &mut RenderContext,
30        (target, upscaling_target, camera): QueryItem<Self::ViewQuery>,
31        world: &World,
32    ) -> Result<(), NodeRunError> {
33        let pipeline_cache = world.resource::<PipelineCache>();
34        let blit_pipeline = world.resource::<BlitPipeline>();
35        let clear_color_global = world.resource::<ClearColor>();
36
37        let diagnostics = render_context.diagnostic_recorder();
38
39        let clear_color = if let Some(camera) = camera {
40            match camera.output_mode {
41                CameraOutputMode::Write { clear_color, .. } => clear_color,
42                CameraOutputMode::Skip => return Ok(()),
43            }
44        } else {
45            ClearColorConfig::Default
46        };
47        let clear_color = match clear_color {
48            ClearColorConfig::Default => Some(clear_color_global.0),
49            ClearColorConfig::Custom(color) => Some(color),
50            ClearColorConfig::None => None,
51        };
52        let converted_clear_color = clear_color.map(Into::into);
53        // texture to be upscaled to the output texture
54        let main_texture_view = target.main_texture_view();
55
56        let mut cached_bind_group = self.cached_texture_bind_group.lock().unwrap();
57        let bind_group = match &mut *cached_bind_group {
58            Some((id, bind_group)) if main_texture_view.id() == *id => bind_group,
59            cached_bind_group => {
60                let bind_group = blit_pipeline
61                    .create_bind_group(render_context.render_device(), main_texture_view);
62
63                let (_, bind_group) =
64                    cached_bind_group.insert((main_texture_view.id(), bind_group));
65                bind_group
66            }
67        };
68
69        let Some(pipeline) = pipeline_cache.get_render_pipeline(upscaling_target.0) else {
70            return Ok(());
71        };
72
73        let pass_descriptor = RenderPassDescriptor {
74            label: Some("upscaling"),
75            color_attachments: &[Some(
76                target.out_texture_color_attachment(converted_clear_color),
77            )],
78            depth_stencil_attachment: None,
79            timestamp_writes: None,
80            occlusion_query_set: None,
81        };
82
83        let mut render_pass = render_context
84            .command_encoder()
85            .begin_render_pass(&pass_descriptor);
86        let pass_span = diagnostics.pass_span(&mut render_pass, "upscaling");
87
88        if let Some(camera) = camera
89            && let Some(viewport) = &camera.viewport
90        {
91            let size = viewport.physical_size;
92            let position = viewport.physical_position;
93            render_pass.set_scissor_rect(position.x, position.y, size.x, size.y);
94        }
95
96        render_pass.set_pipeline(pipeline);
97        render_pass.set_bind_group(0, bind_group, &[]);
98        render_pass.draw(0..3, 0..1);
99
100        pass_span.end(&mut render_pass);
101
102        Ok(())
103    }
104}