Skip to main content

bevy_core_pipeline/upscaling/
mod.rs

1use crate::blit::{BlitPipeline, BlitPipelineKey};
2use bevy_app::prelude::*;
3use bevy_camera::CameraOutputMode;
4use bevy_ecs::prelude::*;
5use bevy_render::{
6    camera::ExtractedCamera, render_resource::*, view::ViewTarget, Render, RenderApp,
7    RenderStartup, RenderSystems,
8};
9
10mod node;
11
12pub use node::upscaling;
13
14pub struct UpscalingPlugin;
15
16impl Plugin for UpscalingPlugin {
17    fn build(&self, app: &mut App) {
18        if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
19            render_app.add_systems(
20                Render,
21                // This system should probably technically be run *after* all of the other systems
22                // that might modify `PipelineCache` via interior mutability, but for now,
23                // we've chosen to simply ignore the ambiguities out of a desire for a better refactor
24                // and aversion to extensive and intrusive system ordering.
25                // See https://github.com/bevyengine/bevy/issues/14770 for more context.
26                prepare_view_upscaling_pipelines
27                    .in_set(RenderSystems::Prepare)
28                    .ambiguous_with_all(),
29            );
30            render_app.add_systems(RenderStartup, clear_view_upscaling_pipelines);
31        }
32    }
33}
34
35#[derive(Component)]
36pub struct ViewUpscalingPipeline(CachedRenderPipelineId, BlitPipelineKey);
37
38/// This is not required on first startup but is required during render recovery
39fn clear_view_upscaling_pipelines(
40    mut commands: Commands,
41    views: Query<Entity, With<ViewUpscalingPipeline>>,
42) {
43    for entity in &views {
44        commands.entity(entity).remove::<ViewUpscalingPipeline>();
45    }
46}
47
48fn prepare_view_upscaling_pipelines(
49    mut commands: Commands,
50    mut pipeline_cache: ResMut<PipelineCache>,
51    mut pipelines: ResMut<SpecializedRenderPipelines<BlitPipeline>>,
52    blit_pipeline: Res<BlitPipeline>,
53    view_targets: Query<(
54        Entity,
55        &ViewTarget,
56        Option<&ExtractedCamera>,
57        Option<&ViewUpscalingPipeline>,
58    )>,
59) {
60    for (entity, view_target, camera, maybe_pipeline) in view_targets.iter() {
61        let blend_state = if let Some(extracted_camera) = camera {
62            match extracted_camera.output_mode {
63                CameraOutputMode::Skip => None,
64                CameraOutputMode::Write { blend_state, .. } => {
65                    match blend_state {
66                        None => {
67                            // Auto-detect: the first camera to render to this output
68                            // (sorted_camera_index_for_target == 0) uses replace mode;
69                            // subsequent cameras default to alpha blending so they don't
70                            // accidentally overwrite earlier cameras' output.
71                            if extracted_camera.sorted_camera_index_for_target > 0 {
72                                Some(BlendState::ALPHA_BLENDING)
73                            } else {
74                                None
75                            }
76                        }
77                        _ => blend_state,
78                    }
79                }
80            }
81        } else {
82            None
83        };
84
85        let Some(target_format) = view_target.out_texture_view_format() else {
86            continue;
87        };
88
89        let key = BlitPipelineKey {
90            target_format,
91            blend_state,
92            samples: 1,
93            source_space: view_target.compositing_space,
94        };
95
96        if maybe_pipeline.is_none_or(|ViewUpscalingPipeline(_, cached_key)| *cached_key != key) {
97            let pipeline = pipelines.specialize(&pipeline_cache, &blit_pipeline, key);
98
99            // Ensure the pipeline is loaded before continuing the frame to prevent frames without
100            // any GPU work submitted
101            pipeline_cache.block_on_render_pipeline(pipeline);
102
103            commands
104                .entity(entity)
105                .insert(ViewUpscalingPipeline(pipeline, key));
106        }
107    }
108}