use std::collections::HashMap;
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::fragment::ColorTargetState;
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_materials::MaterialShaderId;
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::masked_bind_group::GeometryMaskedBindGroup;
use crate::render_passes::geometry::pipeline::{
GeometryCullKey, VERTEX_BUFFER_LAYOUT, VERTEX_BUFFER_LAYOUT_INSTANCING,
};
use crate::render_passes::geometry::shader::cache_key::DynamicVertexShaderInfo;
use crate::render_passes::geometry::shader::custom_vertex_cache_key::ShaderCacheKeyGeometryCustomVertex;
use crate::render_passes::RenderPassInitContext;
pub const CUSTOM_VERTEX_UV0_LOCATION: u32 = 10;
#[derive(Hash, Eq, PartialEq, Copy, Clone, Debug)]
pub struct CustomVertexPipelineKeyId {
pub msaa_sample_count: Option<u32>,
pub shader_id: MaterialShaderId,
pub cull: GeometryCullKey,
}
#[derive(Clone)]
pub struct CustomVertexVariant {
pub shader_id: MaterialShaderId,
pub dynamic_vertex: DynamicVertexShaderInfo,
pub instancing_transforms: bool,
}
pub struct GeometryCustomVertexPipelines {
pipeline_layout_key: PipelineLayoutKey,
main: HashMap<CustomVertexPipelineKeyId, RenderPipelineKey>,
uv0_zero_buffer: web_sys::GpuBuffer,
}
const CULL_MODES: &[CullMode] = &[CullMode::None, CullMode::Back, CullMode::Front];
impl GeometryCustomVertexPipelines {
pub fn new(
ctx: &mut RenderPassInitContext<'_>,
masked_bind_group: &GeometryMaskedBindGroup,
geometry_bind_groups: &GeometryBindGroups,
) -> Result<Self> {
let pipeline_layout_key = resolve_layout_key(ctx, masked_bind_group, geometry_bind_groups)?;
let uv0_zero_buffer = create_uv0_zero_buffer(ctx)?;
Ok(Self {
pipeline_layout_key,
main: HashMap::new(),
uv0_zero_buffer,
})
}
pub fn relayout(
&mut self,
ctx: &mut RenderPassInitContext<'_>,
masked_bind_group: &GeometryMaskedBindGroup,
geometry_bind_groups: &GeometryBindGroups,
) -> Result<()> {
self.pipeline_layout_key =
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: &GeometryMaskedBindGroup,
variant: &CustomVertexVariant,
) -> Result<()> {
let msaa_samples = match ctx.anti_aliasing.msaa_sample_count {
Some(4) => Some(4u32),
_ => None,
};
let shader_cache = ShaderCacheKeyGeometryCustomVertex {
shader_id: variant.shader_id,
dynamic_vertex: variant.dynamic_vertex.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,
msaa_samples,
instancing_transforms: variant.instancing_transforms,
meta_storage_array: false,
};
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 color_targets = [
ColorTargetState::new(ctx.render_texture_formats.visiblity_data),
ColorTargetState::new(ctx.render_texture_formats.barycentric),
ColorTargetState::new(ctx.render_texture_formats.normal_tangent),
ColorTargetState::new(ctx.render_texture_formats.barycentric_derivatives),
];
let depth_format = ctx.render_texture_formats.depth;
let mut pipeline_cache_keys = Vec::with_capacity(CULL_MODES.len());
let mut slots = Vec::with_capacity(CULL_MODES.len());
for &cull_mode in CULL_MODES {
pipeline_cache_keys.push(build_custom_vertex_cache_key(
shader_key,
self.pipeline_layout_key,
depth_format,
&color_targets,
msaa_samples,
variant.instancing_transforms,
cull_mode,
));
slots.push(CustomVertexPipelineKeyId {
msaa_sample_count: msaa_samples,
shader_id: variant.shader_id,
cull: GeometryCullKey::from_cull_mode(cull_mode)?,
});
}
let keys = ctx
.pipelines
.render
.ensure_keys(
ctx.gpu,
ctx.shaders,
ctx.pipeline_layouts,
pipeline_cache_keys,
)
.await?;
for (slot, key) in slots.into_iter().zip(keys) {
self.main.insert(slot, key);
}
Ok(())
}
pub fn clear(&mut self) {
self.main.clear();
}
pub fn get(
&self,
msaa_sample_count: Option<u32>,
shader_id: MaterialShaderId,
cull_mode: CullMode,
) -> Option<RenderPipelineKey> {
let cull = GeometryCullKey::from_cull_mode(cull_mode).ok()?;
self.main
.get(&CustomVertexPipelineKeyId {
msaa_sample_count,
shader_id,
cull,
})
.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("Geometry Custom Vertex - uv0 zero"),
8,
BufferUsage::new().with_vertex(),
)
.into(),
)?;
Ok(buffer)
}
fn resolve_layout_key(
ctx: &mut RenderPassInitContext<'_>,
masked_bind_group: &GeometryMaskedBindGroup,
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.uniform_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_custom_vertex_cache_key(
shader_key: crate::shaders::ShaderKey,
pipeline_layout_key: PipelineLayoutKey,
depth_format: awsm_renderer_core::texture::TextureFormat,
color_targets: &[ColorTargetState],
msaa_samples: Option<u32>,
instancing: bool,
cull_mode: CullMode,
) -> RenderPipelineCacheKey {
let primitive_state = PrimitiveState::new()
.with_topology(PrimitiveTopology::TriangleList)
.with_front_face(FrontFace::Ccw)
.with_cull_mode(cull_mode);
let depth_stencil = DepthStencilState::new(depth_format)
.with_depth_write_enabled(true)
.with_depth_compare(CompareFunction::LessEqual);
let mut vertex_buffer_layouts = vec![VERTEX_BUFFER_LAYOUT.clone()];
if instancing {
vertex_buffer_layouts.push(VERTEX_BUFFER_LAYOUT_INSTANCING.clone());
}
vertex_buffer_layouts.push(uv0_vertex_buffer_layout());
let mut key = RenderPipelineCacheKey::new(shader_key, pipeline_layout_key)
.with_primitive(primitive_state)
.with_depth_stencil(depth_stencil);
for layout in vertex_buffer_layouts {
key = key.with_push_vertex_buffer_layout(layout);
}
if let Some(sample_count) = msaa_samples {
key = key.with_multisample(MultisampleState::new().with_count(sample_count));
}
for target in color_targets {
key = key.with_push_fragment_targets(vec![target.clone()]);
}
key
}