use std::collections::HashMap;
use awsm_materials::MaterialShaderId;
use awsm_renderer_core::buffers::{BufferDescriptor, BufferUsage};
use awsm_renderer_core::compare::CompareFunction;
use awsm_renderer_core::pipeline::depth_stencil::DepthStencilState;
use awsm_renderer_core::pipeline::multisample::MultisampleState;
use awsm_renderer_core::pipeline::primitive::{
CullMode, FrontFace, PrimitiveState, PrimitiveTopology,
};
use awsm_renderer_core::pipeline::vertex::{VertexAttribute, VertexBufferLayout, VertexFormat};
use awsm_renderer_core::texture::TextureFormat;
use crate::dynamic_materials::ShadingBase;
use crate::error::Result;
use crate::pipeline_layouts::{PipelineLayoutCacheKey, PipelineLayoutKey};
use crate::pipelines::render_pipeline::{RenderPipelineCacheKey, RenderPipelineKey};
use crate::render_passes::geometry::bind_group::GeometryBindGroups;
use crate::render_passes::geometry::custom_vertex_pipeline::CUSTOM_VERTEX_UV0_LOCATION;
use crate::render_passes::geometry::pipeline::VERTEX_BUFFER_LAYOUT;
use crate::render_passes::geometry::shader::cache_key::DynamicVertexShaderInfo;
use crate::render_passes::geometry::shader::masked_cache_key::DynamicAlphaShaderInfo;
use crate::render_passes::shadow_masked::bind_group::ShadowMaskedBindGroup;
use crate::render_passes::RenderPassInitContext;
use crate::shadows::shader::masked_custom_vertex_cache_key::ShaderCacheKeyShadowMaskedCustomVertex;
#[derive(Hash, Eq, PartialEq, Copy, Clone, Debug)]
pub struct ShadowMaskedCustomVertexPipelineKeyId {
pub shader_id: MaterialShaderId,
pub cube_face: bool,
pub double_sided: bool,
}
#[derive(Clone)]
pub struct ShadowMaskedCustomVertexVariant {
pub shader_id: MaterialShaderId,
pub base: ShadingBase,
pub dynamic_vertex: DynamicVertexShaderInfo,
pub dynamic_alpha: Option<DynamicAlphaShaderInfo>,
}
pub struct ShadowMaskedCustomVertexPipelines {
pipeline_layout_key_storage: PipelineLayoutKey,
main: HashMap<ShadowMaskedCustomVertexPipelineKeyId, RenderPipelineKey>,
uv0_zero_buffer: web_sys::GpuBuffer,
}
impl ShadowMaskedCustomVertexPipelines {
pub fn new(
ctx: &mut RenderPassInitContext<'_>,
masked_bind_group: &ShadowMaskedBindGroup,
geometry_bind_groups: &GeometryBindGroups,
) -> Result<Self> {
let pipeline_layout_key_storage =
resolve_layout_key(ctx, masked_bind_group, geometry_bind_groups)?;
let uv0_zero_buffer = create_uv0_zero_buffer(ctx)?;
Ok(Self {
pipeline_layout_key_storage,
main: HashMap::new(),
uv0_zero_buffer,
})
}
pub fn relayout(
&mut self,
ctx: &mut RenderPassInitContext<'_>,
masked_bind_group: &ShadowMaskedBindGroup,
geometry_bind_groups: &GeometryBindGroups,
) -> Result<()> {
self.pipeline_layout_key_storage =
resolve_layout_key(ctx, masked_bind_group, geometry_bind_groups)?;
self.main.clear();
Ok(())
}
pub async fn ensure_variant(
&mut self,
ctx: &mut RenderPassInitContext<'_>,
masked_bind_group: &ShadowMaskedBindGroup,
variant: &ShadowMaskedCustomVertexVariant,
) -> Result<()> {
let shader_cache = ShaderCacheKeyShadowMaskedCustomVertex {
shader_id: variant.shader_id,
base: variant.base,
dynamic_vertex: variant.dynamic_vertex.clone(),
dynamic_alpha: variant.dynamic_alpha.clone(),
texture_pool_arrays_len: masked_bind_group.texture_pool_arrays_len,
texture_pool_samplers_len: masked_bind_group.texture_pool_sampler_keys.len() as u32,
};
ctx.shaders
.ensure_keys(ctx.gpu, vec![shader_cache.clone().into()])
.await?;
let shader_key = ctx.shaders.get_key(ctx.gpu, shader_cache).await?;
let combos: Vec<(bool, bool)> = [false, true]
.into_iter()
.flat_map(|cube_face| [false, true].into_iter().map(move |ds| (cube_face, ds)))
.collect();
let cache_keys: Vec<_> = combos
.iter()
.map(|&(cube_face, double_sided)| {
build_cache_key(
shader_key,
self.pipeline_layout_key_storage,
cube_face,
double_sided,
)
})
.collect();
let keys = ctx
.pipelines
.render
.ensure_keys(ctx.gpu, ctx.shaders, ctx.pipeline_layouts, cache_keys)
.await?;
for ((cube_face, double_sided), key) in combos.into_iter().zip(keys) {
self.main.insert(
ShadowMaskedCustomVertexPipelineKeyId {
shader_id: variant.shader_id,
cube_face,
double_sided,
},
key,
);
}
Ok(())
}
pub fn clear(&mut self) {
self.main.clear();
}
pub fn get(
&self,
shader_id: MaterialShaderId,
cube_face: bool,
double_sided: bool,
) -> Option<RenderPipelineKey> {
self.main
.get(&ShadowMaskedCustomVertexPipelineKeyId {
shader_id,
cube_face,
double_sided,
})
.copied()
}
pub fn uv0_zero_buffer(&self) -> &web_sys::GpuBuffer {
&self.uv0_zero_buffer
}
}
fn create_uv0_zero_buffer(ctx: &mut RenderPassInitContext<'_>) -> Result<web_sys::GpuBuffer> {
let buffer = ctx.gpu.create_buffer(
&BufferDescriptor::new(
Some("Shadow Masked Custom Vertex - uv0 zero"),
8,
BufferUsage::new().with_vertex(),
)
.into(),
)?;
Ok(buffer)
}
fn resolve_layout_key(
ctx: &mut RenderPassInitContext<'_>,
masked_bind_group: &ShadowMaskedBindGroup,
geometry_bind_groups: &GeometryBindGroups,
) -> Result<PipelineLayoutKey> {
Ok(ctx.pipeline_layouts.get_key(
ctx.gpu,
ctx.bind_group_layouts,
PipelineLayoutCacheKey::new(vec![
masked_bind_group.bind_group_layout_key,
geometry_bind_groups.transforms.bind_group_layout_key,
geometry_bind_groups.meta.storage_layout_key,
geometry_bind_groups.animation.bind_group_layout_key,
]),
)?)
}
fn uv0_vertex_buffer_layout() -> VertexBufferLayout {
VertexBufferLayout {
array_stride: 0,
step_mode: None,
attributes: vec![VertexAttribute {
format: VertexFormat::Float32x2,
offset: 0,
shader_location: CUSTOM_VERTEX_UV0_LOCATION,
}],
}
}
fn build_cache_key(
shader_key: crate::shaders::ShaderKey,
pipeline_layout_key: PipelineLayoutKey,
cube_face: bool,
double_sided: bool,
) -> RenderPipelineCacheKey {
let front_face = if cube_face {
FrontFace::Cw
} else {
FrontFace::Ccw
};
let cull_mode = if double_sided {
CullMode::None
} else {
CullMode::Front
};
let primitive = PrimitiveState::new()
.with_topology(PrimitiveTopology::TriangleList)
.with_front_face(front_face)
.with_cull_mode(cull_mode);
let depth_stencil = DepthStencilState::new(TextureFormat::Depth32float)
.with_depth_write_enabled(true)
.with_depth_compare(CompareFunction::LessEqual)
.with_depth_bias(1)
.with_depth_bias_slope_scale(1.5);
let multisample = MultisampleState::new().with_count(1);
RenderPipelineCacheKey::new(shader_key, pipeline_layout_key)
.with_primitive(primitive)
.with_depth_stencil(depth_stencil)
.with_multisample(multisample)
.with_push_vertex_buffer_layout(VERTEX_BUFFER_LAYOUT.clone())
.with_push_vertex_buffer_layout(uv0_vertex_buffer_layout())
.with_force_fragment_stage()
}