bevy_core_pipeline/upscaling/
mod.rs

1use crate::blit::{BlitPipeline, BlitPipelineKey};
2use bevy_app::prelude::*;
3use bevy_ecs::prelude::*;
4use bevy_render::{
5    camera::{CameraOutputMode, ExtractedCamera},
6    render_resource::*,
7    view::ViewTarget,
8    Render, RenderApp, RenderSet,
9};
10use bevy_utils::HashSet;
11
12mod node;
13
14pub use node::UpscalingNode;
15
16pub struct UpscalingPlugin;
17
18impl Plugin for UpscalingPlugin {
19    fn build(&self, app: &mut App) {
20        if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
21            render_app.add_systems(
22                Render,
23                // This system should probably technically be run *after* all of the other systems
24                // that might modify `PipelineCache` via interior mutability, but for now,
25                // we've chosen to simply ignore the ambiguities out of a desire for a better refactor
26                // and aversion to extensive and intrusive system ordering.
27                // See https://github.com/bevyengine/bevy/issues/14770 for more context.
28                prepare_view_upscaling_pipelines
29                    .in_set(RenderSet::Prepare)
30                    .ambiguous_with_all(),
31            );
32        }
33    }
34}
35
36#[derive(Component)]
37pub struct ViewUpscalingPipeline(CachedRenderPipelineId);
38
39fn prepare_view_upscaling_pipelines(
40    mut commands: Commands,
41    mut pipeline_cache: ResMut<PipelineCache>,
42    mut pipelines: ResMut<SpecializedRenderPipelines<BlitPipeline>>,
43    blit_pipeline: Res<BlitPipeline>,
44    view_targets: Query<(Entity, &ViewTarget, Option<&ExtractedCamera>)>,
45) {
46    let mut output_textures = HashSet::new();
47    for (entity, view_target, camera) in view_targets.iter() {
48        let out_texture_id = view_target.out_texture().id();
49        let blend_state = if let Some(extracted_camera) = camera {
50            match extracted_camera.output_mode {
51                CameraOutputMode::Skip => None,
52                CameraOutputMode::Write { blend_state, .. } => {
53                    let already_seen = output_textures.contains(&out_texture_id);
54                    output_textures.insert(out_texture_id);
55
56                    match blend_state {
57                        None => {
58                            // If we've already seen this output for a camera and it doesn't have a output blend
59                            // mode configured, default to alpha blend so that we don't accidentally overwrite
60                            // the output texture
61                            if already_seen {
62                                Some(BlendState::ALPHA_BLENDING)
63                            } else {
64                                None
65                            }
66                        }
67                        _ => blend_state,
68                    }
69                }
70            }
71        } else {
72            output_textures.insert(out_texture_id);
73            None
74        };
75
76        let key = BlitPipelineKey {
77            texture_format: view_target.out_texture_format(),
78            blend_state,
79            samples: 1,
80        };
81        let pipeline = pipelines.specialize(&pipeline_cache, &blit_pipeline, key);
82
83        // Ensure the pipeline is loaded before continuing the frame to prevent frames without any GPU work submitted
84        pipeline_cache.block_on_render_pipeline(pipeline);
85
86        commands
87            .entity(entity)
88            .insert(ViewUpscalingPipeline(pipeline));
89    }
90}