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: BindGroupLayout,
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 = render_device.create_bind_group_layout(
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    ) -> BindGroup {
75        render_device.create_bind_group(
76            None,
77            &self.layout,
78            &BindGroupEntries::sequential((src_texture, &self.sampler)),
79        )
80    }
81}
82
83#[derive(PartialEq, Eq, Hash, Clone, Copy)]
84pub struct BlitPipelineKey {
85    pub texture_format: TextureFormat,
86    pub blend_state: Option<BlendState>,
87    pub samples: u32,
88}
89
90impl SpecializedRenderPipeline for BlitPipeline {
91    type Key = BlitPipelineKey;
92
93    fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
94        RenderPipelineDescriptor {
95            label: Some("blit pipeline".into()),
96            layout: vec![self.layout.clone()],
97            vertex: self.fullscreen_shader.to_vertex_state(),
98            fragment: Some(FragmentState {
99                shader: self.fragment_shader.clone(),
100                targets: vec![Some(ColorTargetState {
101                    format: key.texture_format,
102                    blend: key.blend_state,
103                    write_mask: ColorWrites::ALL,
104                })],
105                ..default()
106            }),
107            multisample: MultisampleState {
108                count: key.samples,
109                ..default()
110            },
111            ..default()
112        }
113    }
114}