use std::collections::HashMap;
use awsm_materials::MaterialShaderId;
use crate::anti_alias::AntiAliasing;
use crate::error::Result;
use crate::pipeline_layouts::{PipelineLayoutCacheKey, PipelineLayoutKey};
use crate::pipelines::compute_pipeline::{ComputePipelineCacheKey, ComputePipelineKey};
use crate::render_passes::{
material_opaque::{
bind_group::MaterialOpaqueBindGroups, shader::cache_key::ShaderCacheKeyMaterialOpaque,
},
RenderPassInitContext,
};
#[derive(Hash, Eq, PartialEq, Copy, Clone, Debug)]
pub struct PipelineKeyId {
pub msaa_sample_count: Option<u32>,
pub mipmaps: bool,
pub shader_id: MaterialShaderId,
}
pub(crate) struct OpaqueShaderDesc {
pub(crate) shader_cache: crate::shaders::ShaderCacheKey,
pub(crate) layout_key: PipelineLayoutKey,
pub(crate) slot: OpaquePipelineSlot,
}
pub struct MaterialOpaquePipelines {
main: HashMap<PipelineKeyId, ComputePipelineKey>,
}
const OPAQUE_SHADER_IDS: &[MaterialShaderId] = &[
MaterialShaderId::SKYBOX,
MaterialShaderId::PBR,
MaterialShaderId::UNLIT,
MaterialShaderId::TOON,
MaterialShaderId::FLIPBOOK,
];
#[derive(Clone, Copy, Debug)]
pub enum OpaquePipelineSlot {
Main(PipelineKeyId),
}
pub struct MaterialOpaquePipelineDescriptors {
pub shader_cache_keys: Vec<crate::shaders::ShaderCacheKey>,
pub pipeline_cache_keys: Vec<ComputePipelineCacheKey>,
pub slots: Vec<OpaquePipelineSlot>,
}
impl MaterialOpaquePipelines {
pub async fn new(
ctx: &mut RenderPassInitContext<'_>,
bind_groups: &MaterialOpaqueBindGroups,
) -> Result<Self> {
let shader_descs = Self::shader_descriptors_and_layouts(ctx, bind_groups)?;
ctx.shaders
.ensure_keys(ctx.gpu, shader_descs.iter().map(|d| d.shader_cache.clone()))
.await?;
let descs =
Self::build_descriptors_from_shader_descs(ctx.gpu, ctx.shaders, shader_descs).await?;
let pipeline_keys = ctx
.pipelines
.compute
.ensure_keys(
ctx.gpu,
ctx.shaders,
ctx.pipeline_layouts,
descs.pipeline_cache_keys.clone(),
)
.await?;
Ok(Self::from_resolved(descs.slots, pipeline_keys))
}
fn shader_descriptors_and_layouts(
ctx: &mut RenderPassInitContext<'_>,
bind_groups: &MaterialOpaqueBindGroups,
) -> Result<Vec<OpaqueShaderDesc>> {
let bucket_entries = crate::dynamic_materials::first_party_bucket_entries();
let max_shadow_casters = ctx.prep_config.clamped_k();
Self::shader_descriptors_for_config_with(
ctx.gpu,
ctx.bind_group_layouts,
ctx.pipeline_layouts,
bind_groups,
ctx.anti_aliasing,
&bucket_entries,
false,
max_shadow_casters,
)
}
pub(crate) fn shader_descriptors_for_config_with(
gpu: &awsm_renderer_core::renderer::AwsmRendererWebGpu,
bind_group_layouts: &mut crate::bind_group_layout::BindGroupLayouts,
pipeline_layouts: &mut crate::pipeline_layouts::PipelineLayouts,
bind_groups: &MaterialOpaqueBindGroups,
anti_aliasing: &AntiAliasing,
bucket_entries: &[crate::dynamic_materials::BucketEntry],
include_first_party: bool,
max_shadow_casters: u32,
) -> Result<Vec<OpaqueShaderDesc>> {
let (active_msaa, main_bgl) = match anti_aliasing.msaa_sample_count {
Some(4) => (
Some(4_u32),
bind_groups.multisampled_main_bind_group_layout_key,
),
_ => (None, bind_groups.singlesampled_main_bind_group_layout_key),
};
let active_mipmaps = anti_aliasing.mipmap;
let layout_key = pipeline_layouts.get_key(
gpu,
bind_group_layouts,
PipelineLayoutCacheKey::new(vec![
main_bgl,
bind_groups.lights_bind_group_layout_key,
bind_groups.texture_pool_textures_bind_group_layout_key,
bind_groups.shadows_bind_group_layout_key,
]),
)?;
let texture_pool_arrays_len = bind_groups.texture_pool_arrays_len;
let texture_pool_samplers_len = bind_groups.texture_pool_sampler_keys.len() as u32;
let mut shader_descs: Vec<OpaqueShaderDesc> = Vec::with_capacity(OPAQUE_SHADER_IDS.len());
if include_first_party && active_msaa.is_none() {
for &shader_id in OPAQUE_SHADER_IDS {
shader_descs.push(OpaqueShaderDesc {
shader_cache: ShaderCacheKeyMaterialOpaque {
texture_pool_arrays_len,
texture_pool_samplers_len,
msaa_sample_count: active_msaa,
mipmaps: active_mipmaps,
max_shadow_casters,
shader_id,
base: crate::dynamic_materials::ShadingBase::for_shader_id(shader_id),
owns_skybox: shader_id == MaterialShaderId::SKYBOX,
pbr_features: bucket_entries
.iter()
.find(|e| e.shader_id == shader_id)
.map(|e| e.pbr_features)
.unwrap_or_else(|| awsm_materials::pbr::PbrFeatures::default().bits()),
dispatch_hash: 0,
dynamic_shader: None,
bucket_entries: bucket_entries.to_vec(),
}
.into(),
layout_key,
slot: OpaquePipelineSlot::Main(PipelineKeyId {
msaa_sample_count: active_msaa,
mipmaps: active_mipmaps,
shader_id,
}),
});
}
}
Ok(shader_descs)
}
pub fn build_shader_cache_keys(
ctx: &mut RenderPassInitContext<'_>,
bind_groups: &MaterialOpaqueBindGroups,
) -> Result<Vec<crate::shaders::ShaderCacheKey>> {
let shader_descs = Self::shader_descriptors_and_layouts(ctx, bind_groups)?;
Ok(shader_descs.into_iter().map(|d| d.shader_cache).collect())
}
pub async fn build_descriptors(
ctx: &mut RenderPassInitContext<'_>,
bind_groups: &MaterialOpaqueBindGroups,
) -> Result<MaterialOpaquePipelineDescriptors> {
let shader_descs = Self::shader_descriptors_and_layouts(ctx, bind_groups)?;
Self::build_descriptors_from_shader_descs(ctx.gpu, ctx.shaders, shader_descs).await
}
async fn build_descriptors_from_shader_descs(
gpu: &awsm_renderer_core::renderer::AwsmRendererWebGpu,
shaders: &mut crate::shaders::Shaders,
shader_descs: Vec<OpaqueShaderDesc>,
) -> Result<MaterialOpaquePipelineDescriptors> {
let mut shader_cache_keys = Vec::with_capacity(shader_descs.len());
let mut pipeline_cache_keys = Vec::with_capacity(shader_descs.len());
let mut slots = Vec::with_capacity(shader_descs.len());
for d in shader_descs {
let shader_key = shaders.get_key(gpu, d.shader_cache.clone()).await?;
shader_cache_keys.push(d.shader_cache);
let cache_key = match d.slot {
OpaquePipelineSlot::Main(_) => {
ComputePipelineCacheKey::new(shader_key, d.layout_key)
.with_entry_point("cs_opaque")
}
};
pipeline_cache_keys.push(cache_key);
slots.push(d.slot);
}
Ok(MaterialOpaquePipelineDescriptors {
shader_cache_keys,
pipeline_cache_keys,
slots,
})
}
pub fn from_resolved(
slots: Vec<OpaquePipelineSlot>,
pipeline_keys: Vec<ComputePipelineKey>,
) -> Self {
let mut main = HashMap::with_capacity(OPAQUE_SHADER_IDS.len() * 4);
for (slot, key) in slots.into_iter().zip(pipeline_keys) {
match slot {
OpaquePipelineSlot::Main(id) => {
main.insert(id, key);
}
}
}
Self { main }
}
pub fn merge_resolved(
&mut self,
slots: Vec<OpaquePipelineSlot>,
pipeline_keys: Vec<ComputePipelineKey>,
) {
for (slot, key) in slots.into_iter().zip(pipeline_keys) {
match slot {
OpaquePipelineSlot::Main(id) => {
self.main.insert(id, key);
}
}
}
}
pub fn get_compute_pipeline_key(
&self,
anti_aliasing: &AntiAliasing,
shader_id: MaterialShaderId,
) -> Option<ComputePipelineKey> {
let msaa = match anti_aliasing.msaa_sample_count {
Some(4) => Some(4_u32),
None => None,
_ => return None,
};
self.main
.get(&PipelineKeyId {
msaa_sample_count: msaa,
mipmaps: anti_aliasing.mipmap,
shader_id,
})
.copied()
}
pub fn insert_dynamic_pipeline(
&mut self,
key_id: PipelineKeyId,
pipeline_key: ComputePipelineKey,
) -> Option<ComputePipelineKey> {
self.main
.insert(key_id, pipeline_key)
.filter(|displaced| *displaced != pipeline_key)
}
pub fn clear_dynamic_pipelines(&mut self) -> Vec<ComputePipelineKey> {
self.main.drain().map(|(_, key)| key).collect()
}
pub fn main_len(&self) -> usize {
self.main.len()
}
}