bevy_core_pipeline/upscaling/
node.rs

1use crate::{blit::BlitPipeline, upscaling::ViewUpscalingPipeline};
2use bevy_ecs::{prelude::*, query::QueryItem};
3use bevy_render::{
4    camera::{CameraOutputMode, ClearColor, ClearColorConfig, ExtractedCamera},
5    render_graph::{NodeRunError, RenderGraphContext, ViewNode},
6    render_resource::{
7        BindGroup, BindGroupEntries, PipelineCache, RenderPassDescriptor, TextureViewId,
8    },
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.get_resource::<PipelineCache>().unwrap();
34        let blit_pipeline = world.get_resource::<BlitPipeline>().unwrap();
35        let clear_color_global = world.get_resource::<ClearColor>().unwrap();
36
37        let clear_color = if let Some(camera) = camera {
38            match camera.output_mode {
39                CameraOutputMode::Write { clear_color, .. } => clear_color,
40                CameraOutputMode::Skip => return Ok(()),
41            }
42        } else {
43            ClearColorConfig::Default
44        };
45        let clear_color = match clear_color {
46            ClearColorConfig::Default => Some(clear_color_global.0),
47            ClearColorConfig::Custom(color) => Some(color),
48            ClearColorConfig::None => None,
49        };
50        let converted_clear_color = clear_color.map(Into::into);
51        let upscaled_texture = target.main_texture_view();
52
53        let mut cached_bind_group = self.cached_texture_bind_group.lock().unwrap();
54        let bind_group = match &mut *cached_bind_group {
55            Some((id, bind_group)) if upscaled_texture.id() == *id => bind_group,
56            cached_bind_group => {
57                let bind_group = render_context.render_device().create_bind_group(
58                    None,
59                    &blit_pipeline.texture_bind_group,
60                    &BindGroupEntries::sequential((upscaled_texture, &blit_pipeline.sampler)),
61                );
62
63                let (_, bind_group) = cached_bind_group.insert((upscaled_texture.id(), bind_group));
64                bind_group
65            }
66        };
67
68        let Some(pipeline) = pipeline_cache.get_render_pipeline(upscaling_target.0) else {
69            return Ok(());
70        };
71
72        let pass_descriptor = RenderPassDescriptor {
73            label: Some("upscaling_pass"),
74            color_attachments: &[Some(
75                target.out_texture_color_attachment(converted_clear_color),
76            )],
77            depth_stencil_attachment: None,
78            timestamp_writes: None,
79            occlusion_query_set: None,
80        };
81
82        let mut render_pass = render_context
83            .command_encoder()
84            .begin_render_pass(&pass_descriptor);
85
86        if let Some(camera) = camera {
87            if let Some(viewport) = &camera.viewport {
88                let size = viewport.physical_size;
89                let position = viewport.physical_position;
90                render_pass.set_scissor_rect(position.x, position.y, size.x, size.y);
91            }
92        }
93
94        render_pass.set_pipeline(pipeline);
95        render_pass.set_bind_group(0, bind_group, &[]);
96        render_pass.draw(0..3, 0..1);
97
98        Ok(())
99    }
100}