use bevy::platform::collections::HashSet;
use bevy::prelude::*;
use bevy::render::Extract;
use bevy::render::camera::CameraMainPassTextureFormats;
use bevy::render::render_resource::{PipelineCache, SpecializedRenderPipelines, TextureFormat};
use bevy::shader::Shader;
use super::backdrop::{BackdropBlitPipeline, BackdropBlitPipelineKey};
use super::mips::{LayerBlitPipeline, LayerBlitPipelineKey};
use super::{
LayerCompositePipeline, LayerCompositePipelineKey, LayerFilterPipeline, LayerFilterPipelineKey,
};
use crate::filters::WarmShaderList;
#[derive(Resource, Default)]
pub struct WarmShaders {
pub handles: Vec<Handle<Shader>>,
pub version: u32,
pub warmed: HashSet<TextureFormat>,
}
pub fn extract_warm_shaders(list: Extract<Res<WarmShaderList>>, mut warm: ResMut<WarmShaders>) {
if warm.version == list.version && warm.handles.len() == list.handles.len() {
return;
}
warm.handles = list.handles.clone();
warm.version = list.version;
warm.warmed.clear();
}
pub fn pending_formats(
seen: impl IntoIterator<Item = TextureFormat>,
warmed: &mut HashSet<TextureFormat>,
) -> Vec<TextureFormat> {
let mut out = Vec::new();
for format in seen {
if warmed.insert(format) {
out.push(format);
}
}
out
}
#[allow(clippy::too_many_arguments)]
pub fn warm_layer_pipelines(
formats: Res<CameraMainPassTextureFormats>,
mut warm: ResMut<WarmShaders>,
pipeline_cache: Res<PipelineCache>,
filter: Option<Res<LayerFilterPipeline>>,
composite: Option<Res<LayerCompositePipeline>>,
blit: Option<Res<LayerBlitPipeline>>,
backdrop: Option<Res<BackdropBlitPipeline>>,
mut filter_pipelines: ResMut<SpecializedRenderPipelines<LayerFilterPipeline>>,
mut composite_pipelines: ResMut<SpecializedRenderPipelines<LayerCompositePipeline>>,
mut blit_pipelines: ResMut<SpecializedRenderPipelines<LayerBlitPipeline>>,
mut backdrop_pipelines: ResMut<SpecializedRenderPipelines<BackdropBlitPipeline>>,
) {
if warm.handles.is_empty() {
return;
}
let (Some(filter), Some(composite), Some(blit), Some(backdrop)) =
(filter, composite, blit, backdrop)
else {
return;
};
let WarmShaders {
handles, warmed, ..
} = &mut *warm;
for target_format in pending_formats(formats.values().copied(), warmed) {
for shader in handles.iter() {
filter_pipelines.specialize(
&pipeline_cache,
&filter,
LayerFilterPipelineKey {
shader: shader.clone(),
target_format,
},
);
}
composite_pipelines.specialize(
&pipeline_cache,
&composite,
LayerCompositePipelineKey { target_format },
);
blit_pipelines.specialize(
&pipeline_cache,
&blit,
LayerBlitPipelineKey { target_format },
);
backdrop_pipelines.specialize(
&pipeline_cache,
&backdrop,
BackdropBlitPipelineKey { target_format },
);
tracing::debug!(
target: "bevy_react",
"precompiling {} layer pipelines for {target_format:?}",
handles.len() + 3
);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn pending_formats_reports_each_format_once() {
let mut warmed = HashSet::default();
let a = TextureFormat::Rgba8UnormSrgb;
let b = TextureFormat::Rgba16Float;
assert_eq!(pending_formats([a, a, b], &mut warmed), vec![a, b]);
assert!(pending_formats([b, a], &mut warmed).is_empty());
assert_eq!(
pending_formats([TextureFormat::Bgra8UnormSrgb], &mut warmed),
vec![TextureFormat::Bgra8UnormSrgb]
);
warmed.clear();
assert_eq!(pending_formats([a], &mut warmed), vec![a]);
}
}