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