use bevy_core_pipeline::FullscreenShader;
use super::{Bloom, BLOOM_TEXTURE_FORMAT};
use bevy_asset::{load_embedded_asset, AssetServer, Handle};
use bevy_ecs::{
prelude::{Component, Entity},
resource::Resource,
system::{Commands, Query, Res, ResMut},
};
use bevy_math::{Vec2, Vec4};
use bevy_render::{
render_resource::{
binding_types::{sampler, texture_2d, uniform_buffer},
*,
},
renderer::RenderDevice,
};
use bevy_shader::Shader;
use bevy_utils::default;
#[derive(Component)]
pub struct BloomDownsamplingPipelineIds {
pub main: CachedRenderPipelineId,
pub first: CachedRenderPipelineId,
}
#[derive(Resource)]
pub struct BloomDownsamplingPipeline {
pub bind_group_layout: BindGroupLayoutDescriptor,
pub sampler: Sampler,
pub fullscreen_shader: FullscreenShader,
pub fragment_shader: Handle<Shader>,
}
#[derive(PartialEq, Eq, Hash, Clone)]
pub struct BloomDownsamplingPipelineKeys {
prefilter: bool,
first_downsample: bool,
uniform_scale: bool,
}
#[derive(Component, ShaderType, Clone)]
pub struct BloomUniforms {
pub threshold_precomputations: Vec4,
pub viewport: Vec4,
pub scale: Vec2,
pub aspect: f32,
}
pub fn init_bloom_downsampling_pipeline(
mut commands: Commands,
render_device: Res<RenderDevice>,
fullscreen_shader: Res<FullscreenShader>,
asset_server: Res<AssetServer>,
) {
let bind_group_layout = BindGroupLayoutDescriptor::new(
"bloom_downsampling_bind_group_layout_with_settings",
&BindGroupLayoutEntries::sequential(
ShaderStages::FRAGMENT,
(
texture_2d(TextureSampleType::Float { filterable: true }),
sampler(SamplerBindingType::Filtering),
uniform_buffer::<BloomUniforms>(true),
),
),
);
let sampler = render_device.create_sampler(&SamplerDescriptor {
min_filter: FilterMode::Linear,
mag_filter: FilterMode::Linear,
address_mode_u: AddressMode::ClampToEdge,
address_mode_v: AddressMode::ClampToEdge,
..Default::default()
});
commands.insert_resource(BloomDownsamplingPipeline {
bind_group_layout,
sampler,
fullscreen_shader: fullscreen_shader.clone(),
fragment_shader: load_embedded_asset!(asset_server.as_ref(), "bloom.wgsl"),
});
}
impl SpecializedRenderPipeline for BloomDownsamplingPipeline {
type Key = BloomDownsamplingPipelineKeys;
fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
let layout = vec![self.bind_group_layout.clone()];
let entry_point = if key.first_downsample {
"downsample_first".into()
} else {
"downsample".into()
};
let mut shader_defs = vec![];
if key.first_downsample {
shader_defs.push("FIRST_DOWNSAMPLE".into());
}
if key.prefilter {
shader_defs.push("USE_THRESHOLD".into());
}
if key.uniform_scale {
shader_defs.push("UNIFORM_SCALE".into());
}
RenderPipelineDescriptor {
label: Some(
if key.first_downsample {
"bloom_downsampling_pipeline_first"
} else {
"bloom_downsampling_pipeline"
}
.into(),
),
layout,
vertex: self.fullscreen_shader.to_vertex_state(),
fragment: Some(FragmentState {
shader: self.fragment_shader.clone(),
shader_defs,
entry_point: Some(entry_point),
targets: vec![Some(ColorTargetState {
format: BLOOM_TEXTURE_FORMAT,
blend: None,
write_mask: ColorWrites::ALL,
})],
}),
..default()
}
}
}
pub fn prepare_downsampling_pipeline(
mut commands: Commands,
pipeline_cache: Res<PipelineCache>,
mut pipelines: ResMut<SpecializedRenderPipelines<BloomDownsamplingPipeline>>,
pipeline: Res<BloomDownsamplingPipeline>,
views: Query<(Entity, &Bloom)>,
) {
for (entity, bloom) in &views {
let prefilter = bloom.prefilter.threshold > 0.0;
let pipeline_id = pipelines.specialize(
&pipeline_cache,
&pipeline,
BloomDownsamplingPipelineKeys {
prefilter,
first_downsample: false,
uniform_scale: bloom.scale == Vec2::ONE,
},
);
let pipeline_first_id = pipelines.specialize(
&pipeline_cache,
&pipeline,
BloomDownsamplingPipelineKeys {
prefilter,
first_downsample: true,
uniform_scale: bloom.scale == Vec2::ONE,
},
);
commands
.entity(entity)
.insert(BloomDownsamplingPipelineIds {
first: pipeline_first_id,
main: pipeline_id,
});
}
}