use std::borrow::Cow;
use awsm_renderer_core::bind_groups::{
BindGroupDescriptor, BindGroupEntry, BindGroupLayoutResource, BindGroupResource,
BufferBindingLayout, BufferBindingType, StorageTextureAccess, StorageTextureBindingLayout,
TextureBindingLayout,
};
use awsm_renderer_core::buffers::BufferBinding;
use awsm_renderer_core::texture::{TextureFormat, TextureSampleType, TextureViewDimension};
use crate::bind_group_layout::{
BindGroupLayoutCacheKey, BindGroupLayoutCacheKeyEntry, BindGroupLayoutKey,
};
use crate::bind_groups::{AwsmBindGroupError, BindGroupRecreateContext};
use crate::error::Result;
use crate::render_passes::shared::material::bind_group::{
build_shadow_bind_group_entries, shadow_bind_group_layout_entries,
};
use crate::render_passes::RenderPassInitContext;
pub struct MaterialPrepBindGroups {
pub multisampled_bind_group_layout_key: BindGroupLayoutKey,
pub singlesampled_bind_group_layout_key: BindGroupLayoutKey,
pub lights_bind_group_layout_key: BindGroupLayoutKey,
pub shadows_bind_group_layout_key: BindGroupLayoutKey,
pub edge_bind_group_layout_key: Option<BindGroupLayoutKey>,
bind_group: Option<web_sys::GpuBindGroup>,
lights_bind_group: Option<web_sys::GpuBindGroup>,
shadows_bind_group: Option<web_sys::GpuBindGroup>,
}
impl MaterialPrepBindGroups {
pub async fn new(ctx: &mut RenderPassInitContext<'_>) -> Result<Self> {
let multisampled_bind_group_layout_key = create_main_bind_group_layout_key(ctx, true)?;
let singlesampled_bind_group_layout_key = create_main_bind_group_layout_key(ctx, false)?;
let lights_bind_group_layout_key = create_lights_bind_group_layout_key(ctx)?;
let shadows_bind_group_layout_key = ctx.bind_group_layouts.get_key(
ctx.gpu,
BindGroupLayoutCacheKey {
entries: shadow_bind_group_layout_entries(true),
},
)?;
let edge_bind_group_layout_key = Some(create_edge_bind_group_layout_key(ctx)?);
Ok(Self {
multisampled_bind_group_layout_key,
singlesampled_bind_group_layout_key,
lights_bind_group_layout_key,
shadows_bind_group_layout_key,
edge_bind_group_layout_key,
bind_group: None,
lights_bind_group: None,
shadows_bind_group: None,
})
}
pub fn get_bind_group(
&self,
) -> std::result::Result<&web_sys::GpuBindGroup, AwsmBindGroupError> {
self.bind_group
.as_ref()
.ok_or_else(|| AwsmBindGroupError::NotFound("Material Prep".to_string()))
}
pub fn get_lights_bind_group(
&self,
) -> std::result::Result<&web_sys::GpuBindGroup, AwsmBindGroupError> {
self.lights_bind_group
.as_ref()
.ok_or_else(|| AwsmBindGroupError::NotFound("Material Prep - Lights".to_string()))
}
pub fn get_shadows_bind_group(
&self,
) -> std::result::Result<&web_sys::GpuBindGroup, AwsmBindGroupError> {
self.shadows_bind_group
.as_ref()
.ok_or_else(|| AwsmBindGroupError::NotFound("Material Prep - Shadows".to_string()))
}
pub fn recreate(&mut self, ctx: &BindGroupRecreateContext<'_>) -> Result<()> {
self.recreate_main(ctx)?;
self.recreate_lights(ctx)?;
self.recreate_shadows(ctx)?;
Ok(())
}
fn recreate_main(&mut self, ctx: &BindGroupRecreateContext<'_>) -> Result<()> {
let msaa = ctx.anti_aliasing.msaa_sample_count.is_some();
let layout_key = if msaa {
self.multisampled_bind_group_layout_key
} else {
self.singlesampled_bind_group_layout_key
};
let uv_out =
ctx.render_texture_views.prep_uv.as_ref().ok_or_else(|| {
AwsmBindGroupError::NotFound("Material Prep - uv_out".to_string())
})?;
let vcolor_out = ctx
.render_texture_views
.prep_vcolor
.as_ref()
.ok_or_else(|| {
AwsmBindGroupError::NotFound("Material Prep - vcolor_out".to_string())
})?;
let shadow_visibility_out = ctx
.render_texture_views
.prep_shadow_visibility
.as_ref()
.ok_or_else(|| {
AwsmBindGroupError::NotFound("Material Prep - shadow_visibility_out".to_string())
})?;
let entries = vec![
BindGroupEntry::new(
0,
BindGroupResource::TextureView(Cow::Borrowed(
&ctx.render_texture_views.visibility_data,
)),
),
BindGroupEntry::new(
1,
BindGroupResource::TextureView(Cow::Borrowed(
&ctx.render_texture_views.barycentric,
)),
),
BindGroupEntry::new(
2,
BindGroupResource::Buffer(BufferBinding::new(
ctx.meshes.visibility_geometry_data_gpu_buffer(),
)),
),
BindGroupEntry::new(
3,
BindGroupResource::Buffer(BufferBinding::new(
ctx.meshes.meta.material_gpu_buffer(),
)),
),
BindGroupEntry::new(4, BindGroupResource::TextureView(Cow::Borrowed(uv_out))),
BindGroupEntry::new(5, BindGroupResource::TextureView(Cow::Borrowed(vcolor_out))),
BindGroupEntry::new(
6,
BindGroupResource::TextureView(Cow::Borrowed(&ctx.render_texture_views.depth)),
),
BindGroupEntry::new(
7,
BindGroupResource::TextureView(Cow::Borrowed(
&ctx.render_texture_views.normal_tangent,
)),
),
BindGroupEntry::new(
8,
BindGroupResource::Buffer(BufferBinding::new(&ctx.camera.gpu_buffer)),
),
BindGroupEntry::new(
9,
BindGroupResource::TextureView(Cow::Borrowed(shadow_visibility_out)),
),
];
let descriptor = BindGroupDescriptor::new(
ctx.bind_group_layouts.get(layout_key)?,
Some("Material Prep"),
entries,
);
self.bind_group = Some(ctx.gpu.create_bind_group(&descriptor.into()));
Ok(())
}
fn recreate_lights(&mut self, ctx: &BindGroupRecreateContext<'_>) -> Result<()> {
let entries = vec![
BindGroupEntry::new(
0,
BindGroupResource::Buffer(BufferBinding::new(&ctx.lights.gpu_info_buffer)),
),
BindGroupEntry::new(
1,
BindGroupResource::Buffer(BufferBinding::new(&ctx.lights.gpu_punctual_buffer)),
),
BindGroupEntry::new(
2,
BindGroupResource::Buffer(BufferBinding::new(
&ctx.light_culling_buffers.storage_buffer,
)),
),
BindGroupEntry::new(
3,
BindGroupResource::Buffer(BufferBinding::new(
&ctx.light_culling_buffers.params_buffer,
)),
),
];
let descriptor = BindGroupDescriptor::new(
ctx.bind_group_layouts
.get(self.lights_bind_group_layout_key)?,
Some("Material Prep - Lights"),
entries,
);
self.lights_bind_group = Some(ctx.gpu.create_bind_group(&descriptor.into()));
Ok(())
}
fn recreate_shadows(&mut self, ctx: &BindGroupRecreateContext<'_>) -> Result<()> {
let entries = build_shadow_bind_group_entries(ctx.shadows);
let descriptor = BindGroupDescriptor::new(
ctx.bind_group_layouts
.get(self.shadows_bind_group_layout_key)?,
Some("Material Prep - Shadows"),
entries,
);
self.shadows_bind_group = Some(ctx.gpu.create_bind_group(&descriptor.into()));
Ok(())
}
}
fn create_main_bind_group_layout_key(
ctx: &mut RenderPassInitContext<'_>,
multisampled_geometry: bool,
) -> Result<BindGroupLayoutKey> {
let compute = |resource: BindGroupLayoutResource| BindGroupLayoutCacheKeyEntry {
resource,
visibility_vertex: false,
visibility_fragment: false,
visibility_compute: true,
};
let entries = vec![
compute(BindGroupLayoutResource::Texture(
TextureBindingLayout::new()
.with_view_dimension(TextureViewDimension::N2d)
.with_sample_type(TextureSampleType::Uint)
.with_multisampled(multisampled_geometry),
)),
compute(BindGroupLayoutResource::Texture(
TextureBindingLayout::new()
.with_view_dimension(TextureViewDimension::N2d)
.with_sample_type(TextureSampleType::Uint)
.with_multisampled(multisampled_geometry),
)),
compute(BindGroupLayoutResource::Buffer(
BufferBindingLayout::new().with_binding_type(BufferBindingType::ReadOnlyStorage),
)),
compute(BindGroupLayoutResource::Buffer(
BufferBindingLayout::new().with_binding_type(BufferBindingType::ReadOnlyStorage),
)),
compute(BindGroupLayoutResource::StorageTexture(
StorageTextureBindingLayout::new(TextureFormat::Rg32float)
.with_view_dimension(TextureViewDimension::N2dArray)
.with_access(StorageTextureAccess::WriteOnly),
)),
compute(BindGroupLayoutResource::StorageTexture(
StorageTextureBindingLayout::new(TextureFormat::Rgba32float)
.with_view_dimension(TextureViewDimension::N2dArray)
.with_access(StorageTextureAccess::WriteOnly),
)),
compute(BindGroupLayoutResource::Texture(
TextureBindingLayout::new()
.with_view_dimension(TextureViewDimension::N2d)
.with_sample_type(TextureSampleType::Depth)
.with_multisampled(multisampled_geometry),
)),
compute(BindGroupLayoutResource::Texture(
TextureBindingLayout::new()
.with_view_dimension(TextureViewDimension::N2d)
.with_sample_type(TextureSampleType::UnfilterableFloat)
.with_multisampled(multisampled_geometry),
)),
compute(BindGroupLayoutResource::Buffer(
BufferBindingLayout::new().with_binding_type(BufferBindingType::Uniform),
)),
compute(BindGroupLayoutResource::StorageTexture(
StorageTextureBindingLayout::new(TextureFormat::Rgba8unorm)
.with_view_dimension(TextureViewDimension::N2dArray)
.with_access(StorageTextureAccess::WriteOnly),
)),
];
Ok(ctx
.bind_group_layouts
.get_key(ctx.gpu, BindGroupLayoutCacheKey { entries })?)
}
fn create_edge_bind_group_layout_key(
ctx: &mut RenderPassInitContext<'_>,
) -> Result<BindGroupLayoutKey> {
let compute = |resource: BindGroupLayoutResource| BindGroupLayoutCacheKeyEntry {
resource,
visibility_vertex: false,
visibility_fragment: false,
visibility_compute: true,
};
let entries = vec![
compute(BindGroupLayoutResource::Buffer(
BufferBindingLayout::new().with_binding_type(BufferBindingType::ReadOnlyStorage),
)),
compute(BindGroupLayoutResource::Buffer(
BufferBindingLayout::new().with_binding_type(BufferBindingType::Uniform),
)),
compute(BindGroupLayoutResource::StorageTexture(
StorageTextureBindingLayout::new(TextureFormat::Rgba8unorm)
.with_view_dimension(TextureViewDimension::N2dArray)
.with_access(StorageTextureAccess::WriteOnly),
)),
];
Ok(ctx
.bind_group_layouts
.get_key(ctx.gpu, BindGroupLayoutCacheKey { entries })?)
}
fn create_lights_bind_group_layout_key(
ctx: &mut RenderPassInitContext<'_>,
) -> Result<BindGroupLayoutKey> {
let compute = |binding_type: BufferBindingType| BindGroupLayoutCacheKeyEntry {
resource: BindGroupLayoutResource::Buffer(
BufferBindingLayout::new().with_binding_type(binding_type),
),
visibility_vertex: false,
visibility_fragment: false,
visibility_compute: true,
};
let entries = vec![
compute(BufferBindingType::Uniform),
compute(BufferBindingType::Uniform),
compute(BufferBindingType::ReadOnlyStorage),
compute(BufferBindingType::Uniform),
];
Ok(ctx
.bind_group_layouts
.get_key(ctx.gpu, BindGroupLayoutCacheKey { entries })?)
}