bevy_core_pipeline/blit/
mod.rs

1use crate::FullscreenShader;
2use bevy_app::{App, Plugin};
3use bevy_asset::{embedded_asset, load_embedded_asset, AssetServer, Handle};
4use bevy_ecs::prelude::*;
5use bevy_render::{
6    render_resource::{
7        binding_types::{sampler, texture_2d},
8        *,
9    },
10    renderer::RenderDevice,
11    RenderApp, RenderStartup,
12};
13use bevy_shader::Shader;
14use bevy_utils::default;
15
16/// Adds support for specialized "blit pipelines", which can be used to write one texture to another.
17pub struct BlitPlugin;
18
19impl Plugin for BlitPlugin {
20    fn build(&self, app: &mut App) {
21        embedded_asset!(app, "blit.wgsl");
22
23        let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
24            return;
25        };
26
27        render_app
28            .allow_ambiguous_resource::<SpecializedRenderPipelines<BlitPipeline>>()
29            .init_resource::<SpecializedRenderPipelines<BlitPipeline>>()
30            .add_systems(RenderStartup, init_blit_pipeline);
31    }
32}
33
34#[derive(Resource)]
35pub struct BlitPipeline {
36    pub layout: BindGroupLayoutDescriptor,
37    pub sampler: Sampler,
38    pub fullscreen_shader: FullscreenShader,
39    pub fragment_shader: Handle<Shader>,
40}
41
42pub fn init_blit_pipeline(
43    mut commands: Commands,
44    render_device: Res<RenderDevice>,
45    fullscreen_shader: Res<FullscreenShader>,
46    asset_server: Res<AssetServer>,
47) {
48    let layout = BindGroupLayoutDescriptor::new(
49        "blit_bind_group_layout",
50        &BindGroupLayoutEntries::sequential(
51            ShaderStages::FRAGMENT,
52            (
53                texture_2d(TextureSampleType::Float { filterable: false }),
54                sampler(SamplerBindingType::NonFiltering),
55            ),
56        ),
57    );
58
59    let sampler = render_device.create_sampler(&SamplerDescriptor::default());
60
61    commands.insert_resource(BlitPipeline {
62        layout,
63        sampler,
64        fullscreen_shader: fullscreen_shader.clone(),
65        fragment_shader: load_embedded_asset!(asset_server.as_ref(), "blit.wgsl"),
66    });
67}
68
69impl BlitPipeline {
70    pub fn create_bind_group(
71        &self,
72        render_device: &RenderDevice,
73        src_texture: &TextureView,
74        pipeline_cache: &PipelineCache,
75    ) -> BindGroup {
76        render_device.create_bind_group(
77            None,
78            &pipeline_cache.get_bind_group_layout(&self.layout),
79            &BindGroupEntries::sequential((src_texture, &self.sampler)),
80        )
81    }
82}
83
84#[derive(PartialEq, Eq, Hash, Clone, Copy)]
85pub struct BlitPipelineKey {
86    pub texture_format: TextureFormat,
87    pub blend_state: Option<BlendState>,
88    pub samples: u32,
89}
90
91impl SpecializedRenderPipeline for BlitPipeline {
92    type Key = BlitPipelineKey;
93
94    fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
95        RenderPipelineDescriptor {
96            label: Some("blit pipeline".into()),
97            layout: vec![self.layout.clone()],
98            vertex: self.fullscreen_shader.to_vertex_state(),
99            fragment: Some(FragmentState {
100                shader: self.fragment_shader.clone(),
101                targets: vec![Some(ColorTargetState {
102                    format: key.texture_format,
103                    blend: key.blend_state,
104                    write_mask: ColorWrites::ALL,
105                })],
106                ..default()
107            }),
108            multisample: MultisampleState {
109                count: key.samples,
110                ..default()
111            },
112            ..default()
113        }
114    }
115}