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_platform::collections::HashSet;
6use bevy_render::{
7    camera::ExtractedCamera, render_resource::*, view::ViewTarget, Render, RenderApp, RenderSystems,
8};
9
10mod node;
11
12pub use node::UpscalingNode;
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        }
31    }
32}
33
34#[derive(Component)]
35pub struct ViewUpscalingPipeline(CachedRenderPipelineId);
36
37fn prepare_view_upscaling_pipelines(
38    mut commands: Commands,
39    mut pipeline_cache: ResMut<PipelineCache>,
40    mut pipelines: ResMut<SpecializedRenderPipelines<BlitPipeline>>,
41    blit_pipeline: Res<BlitPipeline>,
42    view_targets: Query<(Entity, &ViewTarget, Option<&ExtractedCamera>)>,
43) {
44    let mut output_textures = <HashSet<_>>::default();
45    for (entity, view_target, camera) in view_targets.iter() {
46        let out_texture_id = view_target.out_texture().id();
47        let blend_state = if let Some(extracted_camera) = camera {
48            match extracted_camera.output_mode {
49                CameraOutputMode::Skip => None,
50                CameraOutputMode::Write { blend_state, .. } => {
51                    let already_seen = output_textures.contains(&out_texture_id);
52                    output_textures.insert(out_texture_id);
53
54                    match blend_state {
55                        None => {
56                            // If we've already seen this output for a camera and it doesn't have an output blend
57                            // mode configured, default to alpha blend so that we don't accidentally overwrite
58                            // the output texture
59                            if already_seen {
60                                Some(BlendState::ALPHA_BLENDING)
61                            } else {
62                                None
63                            }
64                        }
65                        _ => blend_state,
66                    }
67                }
68            }
69        } else {
70            output_textures.insert(out_texture_id);
71            None
72        };
73
74        let key = BlitPipelineKey {
75            texture_format: view_target.out_texture_format(),
76            blend_state,
77            samples: 1,
78        };
79        let pipeline = pipelines.specialize(&pipeline_cache, &blit_pipeline, key);
80
81        // Ensure the pipeline is loaded before continuing the frame to prevent frames without any GPU work submitted
82        pipeline_cache.block_on_render_pipeline(pipeline);
83
84        commands
85            .entity(entity)
86            .insert(ViewUpscalingPipeline(pipeline));
87    }
88}