Skip to main content

bevy_core_pipeline/upscaling/
node.rs

1use crate::{blit::BlitPipeline, upscaling::ViewUpscalingPipeline};
2use bevy_camera::{CameraOutputMode, ClearColor, ClearColorConfig};
3use bevy_ecs::prelude::*;
4use bevy_render::{
5    camera::ExtractedCamera,
6    diagnostic::RecordDiagnostics,
7    render_resource::{BindGroup, PipelineCache, RenderPassDescriptor, TextureViewId},
8    renderer::{RenderContext, ViewQuery},
9    view::ViewTarget,
10};
11
12#[derive(Default)]
13pub struct UpscalingBindGroupCache {
14    cached: Option<(TextureViewId, BindGroup)>,
15}
16
17pub fn upscaling(
18    view: ViewQuery<(
19        &ViewTarget,
20        &ViewUpscalingPipeline,
21        Option<&ExtractedCamera>,
22    )>,
23    pipeline_cache: Res<PipelineCache>,
24    blit_pipeline: Res<BlitPipeline>,
25    clear_color_global: Res<ClearColor>,
26    mut cache: Local<UpscalingBindGroupCache>,
27    mut ctx: RenderContext,
28) {
29    let (target, upscaling_target, camera) = view.into_inner();
30
31    let clear_color = if let Some(camera) = camera {
32        match camera.output_mode {
33            CameraOutputMode::Write { clear_color, .. } => clear_color,
34            CameraOutputMode::Skip => return,
35        }
36    } else {
37        ClearColorConfig::Default
38    };
39    let clear_color = match clear_color {
40        ClearColorConfig::Default => Some(clear_color_global.0),
41        ClearColorConfig::Custom(color) => Some(color),
42        ClearColorConfig::None => None,
43    };
44    let converted_clear_color = clear_color.map(Into::into);
45
46    // texture to be upscaled to the output texture
47    let main_texture_view = target.main_texture_view();
48
49    let bind_group = match &mut cache.cached {
50        Some((id, bind_group)) if main_texture_view.id() == *id => bind_group,
51        cached => {
52            let bind_group = blit_pipeline.create_bind_group(
53                ctx.render_device(),
54                main_texture_view,
55                &pipeline_cache,
56            );
57
58            let (_, bind_group) = cached.insert((main_texture_view.id(), bind_group));
59            bind_group
60        }
61    };
62
63    let Some(out_attachment) = target.out_texture_color_attachment(converted_clear_color) else {
64        return;
65    };
66
67    let pass_descriptor = RenderPassDescriptor {
68        label: Some("upscaling"),
69        color_attachments: &[Some(out_attachment)],
70        depth_stencil_attachment: None,
71        timestamp_writes: None,
72        occlusion_query_set: None,
73        multiview_mask: None,
74    };
75
76    let Some(pipeline) = pipeline_cache.get_render_pipeline(upscaling_target.0) else {
77        // we need to do some work on the swapchain to avoid pink screen uninit on macos
78        #[cfg(target_os = "macos")]
79        ctx.command_encoder().begin_render_pass(&pass_descriptor);
80        return;
81    };
82
83    let diagnostics = ctx.diagnostic_recorder();
84    let diagnostics = diagnostics.as_deref();
85    let time_span = diagnostics.time_span(ctx.command_encoder(), "upscaling");
86
87    {
88        let mut render_pass = ctx.command_encoder().begin_render_pass(&pass_descriptor);
89
90        if let Some(camera) = camera
91            && let Some(viewport) = &camera.viewport
92        {
93            let size = viewport.physical_size;
94            let position = viewport.physical_position;
95            render_pass.set_scissor_rect(position.x, position.y, size.x, size.y);
96        }
97
98        render_pass.set_pipeline(pipeline);
99        render_pass.set_bind_group(0, bind_group, &[]);
100        render_pass.draw(0..3, 0..1);
101    }
102
103    time_span.end(ctx.command_encoder());
104}