use std::borrow::Cow;
use awsm_renderer_core::bind_groups::{
BindGroupDescriptor, BindGroupEntry, BindGroupLayoutResource, BindGroupResource,
BufferBindingLayout, BufferBindingType, SamplerBindingLayout, SamplerBindingType,
StorageTextureAccess, StorageTextureBindingLayout, TextureBindingLayout,
};
use awsm_renderer_core::buffers::BufferBinding;
use awsm_renderer_core::texture::{TextureSampleType, TextureViewDimension};
use indexmap::IndexSet;
use crate::bind_group_layout::{BindGroupLayoutCacheKey, BindGroupLayoutCacheKeyEntry};
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, TexturePoolDeps,
TexturePoolVisibility,
};
use crate::textures::SamplerKey;
use crate::{bind_group_layout::BindGroupLayoutKey, render_passes::RenderPassInitContext};
pub const MATERIAL_OPAQUE_CORE_TEXTURES_START_GROUP: u32 = 1;
pub const MATERIAL_OPAQUE_CORE_TEXTURES_START_BINDING: u32 = 0;
pub struct MaterialOpaqueBindGroups {
pub multisampled_main_bind_group_layout_key: BindGroupLayoutKey,
pub singlesampled_main_bind_group_layout_key: BindGroupLayoutKey,
pub lights_bind_group_layout_key: BindGroupLayoutKey,
pub texture_pool_textures_bind_group_layout_key: BindGroupLayoutKey,
pub shadows_bind_group_layout_key: BindGroupLayoutKey,
pub texture_pool_arrays_len: u32,
pub texture_pool_sampler_keys: IndexSet<SamplerKey>,
_main_bind_group: Option<web_sys::GpuBindGroup>,
_lights_bind_group: Option<web_sys::GpuBindGroup>,
_texture_bind_group: Option<web_sys::GpuBindGroup>,
_shadows_bind_group: Option<web_sys::GpuBindGroup>,
}
impl MaterialOpaqueBindGroups {
pub async fn new(ctx: &mut RenderPassInitContext<'_>) -> Result<Self> {
let multisampled_main_bind_group_layout_key =
create_main_bind_group_layout_key(ctx, true).await?;
let singlesampled_main_bind_group_layout_key =
create_main_bind_group_layout_key(ctx, false).await?;
let light_entries = vec![
BindGroupLayoutCacheKeyEntry {
resource: BindGroupLayoutResource::Buffer(
BufferBindingLayout::new().with_binding_type(BufferBindingType::Uniform),
),
visibility_vertex: false,
visibility_fragment: false,
visibility_compute: true,
},
BindGroupLayoutCacheKeyEntry {
resource: BindGroupLayoutResource::Buffer(
BufferBindingLayout::new().with_binding_type(BufferBindingType::Uniform),
),
visibility_vertex: false,
visibility_fragment: false,
visibility_compute: true,
},
BindGroupLayoutCacheKeyEntry {
resource: BindGroupLayoutResource::Buffer(
BufferBindingLayout::new()
.with_binding_type(BufferBindingType::ReadOnlyStorage),
),
visibility_vertex: false,
visibility_fragment: false,
visibility_compute: true,
},
BindGroupLayoutCacheKeyEntry {
resource: BindGroupLayoutResource::Buffer(
BufferBindingLayout::new().with_binding_type(BufferBindingType::Uniform),
),
visibility_vertex: false,
visibility_fragment: false,
visibility_compute: true,
},
];
let lights_bind_group_layout_key = ctx.bind_group_layouts.get_key(
ctx.gpu,
BindGroupLayoutCacheKey {
entries: light_entries,
},
)?;
let shadows_bind_group_layout_key = ctx.bind_group_layouts.get_key(
ctx.gpu,
BindGroupLayoutCacheKey {
entries: shadow_bind_group_layout_entries(true),
},
)?;
let TexturePoolDeps {
bind_group_layout_key: texture_pool_textures_bind_group_layout_key,
arrays_len: texture_pool_arrays_len,
sampler_keys: texture_pool_sampler_keys,
} = TexturePoolDeps::new(ctx, TexturePoolVisibility::Compute)?;
Ok(Self {
singlesampled_main_bind_group_layout_key,
multisampled_main_bind_group_layout_key,
lights_bind_group_layout_key,
texture_pool_textures_bind_group_layout_key,
shadows_bind_group_layout_key,
texture_pool_arrays_len,
texture_pool_sampler_keys,
_main_bind_group: None,
_lights_bind_group: None,
_texture_bind_group: None,
_shadows_bind_group: None,
})
}
pub fn clone_because_texture_pool_changed(
&self,
ctx: &mut RenderPassInitContext<'_>,
) -> Result<Self> {
let TexturePoolDeps {
bind_group_layout_key: texture_pool_textures_bind_group_layout_key,
arrays_len: texture_pool_arrays_len,
sampler_keys: texture_pool_sampler_keys,
} = TexturePoolDeps::new(ctx, TexturePoolVisibility::Compute)?;
let mut _self = Self {
multisampled_main_bind_group_layout_key: self.multisampled_main_bind_group_layout_key,
singlesampled_main_bind_group_layout_key: self.singlesampled_main_bind_group_layout_key,
lights_bind_group_layout_key: self.lights_bind_group_layout_key,
texture_pool_textures_bind_group_layout_key,
shadows_bind_group_layout_key: self.shadows_bind_group_layout_key,
texture_pool_arrays_len,
texture_pool_sampler_keys,
_main_bind_group: self._main_bind_group.clone(),
_lights_bind_group: self._lights_bind_group.clone(),
_texture_bind_group: None,
_shadows_bind_group: self._shadows_bind_group.clone(),
};
Ok(_self)
}
pub fn get_bind_groups(
&self,
) -> std::result::Result<
(
&web_sys::GpuBindGroup,
&web_sys::GpuBindGroup,
&web_sys::GpuBindGroup,
&web_sys::GpuBindGroup,
),
AwsmBindGroupError,
> {
match (
&self._main_bind_group,
&self._lights_bind_group,
&self._texture_bind_group,
&self._shadows_bind_group,
) {
(
Some(main_bind_group),
Some(lights_bind_group),
Some(texture_bind_group),
Some(shadows_bind_group),
) => Ok((
main_bind_group,
lights_bind_group,
texture_bind_group,
shadows_bind_group,
)),
(None, _, _, _) => Err(AwsmBindGroupError::NotFound(
"Material Opaque - Main".to_string(),
)),
(_, None, _, _) => Err(AwsmBindGroupError::NotFound(
"Material Opaque - Lights".to_string(),
)),
(_, _, None, _) => Err(AwsmBindGroupError::NotFound(
"Material Opaque - Texture Pool".to_string(),
)),
(_, _, _, None) => Err(AwsmBindGroupError::NotFound(
"Material Opaque - Shadows".to_string(),
)),
}
}
pub fn recreate_main(&mut self, ctx: &BindGroupRecreateContext<'_>) -> Result<()> {
let mut entries = Vec::new();
entries.push(BindGroupEntry::new(
entries.len() as u32,
BindGroupResource::TextureView(Cow::Borrowed(
&ctx.render_texture_views.visibility_data,
)),
));
entries.push(BindGroupEntry::new(
entries.len() as u32,
BindGroupResource::TextureView(Cow::Borrowed(&ctx.render_texture_views.barycentric)),
));
entries.push(BindGroupEntry::new(
entries.len() as u32,
BindGroupResource::TextureView(Cow::Borrowed(&ctx.render_texture_views.depth)),
));
entries.push(BindGroupEntry::new(
entries.len() as u32,
BindGroupResource::TextureView(Cow::Borrowed(&ctx.render_texture_views.normal_tangent)),
));
entries.push(BindGroupEntry::new(
entries.len() as u32,
BindGroupResource::TextureView(Cow::Borrowed(
&ctx.render_texture_views.barycentric_derivatives,
)),
));
entries.push(BindGroupEntry::new(
entries.len() as u32,
BindGroupResource::Buffer(BufferBinding::new(
ctx.meshes.visibility_geometry_data_gpu_buffer(),
)),
));
entries.push(BindGroupEntry::new(
entries.len() as u32,
BindGroupResource::Buffer(BufferBinding::new(ctx.meshes.meta.material_gpu_buffer())),
));
entries.push(BindGroupEntry::new(
entries.len() as u32,
BindGroupResource::Buffer(BufferBinding::new(&ctx.materials.gpu_buffer)),
));
entries.push(BindGroupEntry::new(
entries.len() as u32,
BindGroupResource::Buffer(BufferBinding::new(&ctx.transforms.gpu_buffer)),
));
entries.push(BindGroupEntry::new(
entries.len() as u32,
BindGroupResource::Buffer(BufferBinding::new(
&ctx.textures.texture_transforms_gpu_buffer,
)),
));
entries.push(BindGroupEntry::new(
entries.len() as u32,
BindGroupResource::Buffer(BufferBinding::new(&ctx.camera.gpu_buffer)),
));
entries.push(BindGroupEntry::new(
entries.len() as u32,
BindGroupResource::TextureView(Cow::Borrowed(&ctx.environment.skybox.texture_view)),
));
entries.push(BindGroupEntry::new(
entries.len() as u32,
BindGroupResource::Sampler(&ctx.environment.skybox.sampler),
));
entries.push(BindGroupEntry::new(
entries.len() as u32,
BindGroupResource::TextureView(Cow::Borrowed(
&ctx.lights.ibl.prefiltered_env.texture_view,
)),
));
entries.push(BindGroupEntry::new(
entries.len() as u32,
BindGroupResource::Sampler(&ctx.lights.ibl.prefiltered_env.sampler),
));
entries.push(BindGroupEntry::new(
entries.len() as u32,
BindGroupResource::TextureView(Cow::Borrowed(&ctx.lights.ibl.irradiance.texture_view)),
));
entries.push(BindGroupEntry::new(
entries.len() as u32,
BindGroupResource::Sampler(&ctx.lights.ibl.irradiance.sampler),
));
entries.push(BindGroupEntry::new(
entries.len() as u32,
BindGroupResource::TextureView(Cow::Borrowed(&ctx.lights.brdf_lut.view)),
));
entries.push(BindGroupEntry::new(
entries.len() as u32,
BindGroupResource::Sampler(&ctx.lights.brdf_lut.sampler),
));
entries.push(BindGroupEntry::new(
entries.len() as u32,
BindGroupResource::TextureView(Cow::Borrowed(&ctx.render_texture_views.opaque)),
));
entries.push(BindGroupEntry::new(
entries.len() as u32,
BindGroupResource::Buffer(BufferBinding::new(ctx.instances.gpu_attribute_buffer())),
));
entries.push(BindGroupEntry::new(
entries.len() as u32,
BindGroupResource::Buffer(BufferBinding::new(&ctx.material_classify_buffers.buffer)),
));
entries.push(BindGroupEntry::new(
entries.len() as u32,
BindGroupResource::Buffer(BufferBinding::new(&ctx.frame_globals.gpu_buffer)),
));
entries.push(BindGroupEntry::new(
entries.len() as u32,
BindGroupResource::Buffer(BufferBinding::new(&ctx.extras_pool.buffer)),
));
if ctx.render_texture_views.prep_uv.is_some() {
let prep_uv = ctx.render_texture_views.prep_uv.as_ref().ok_or_else(|| {
AwsmBindGroupError::NotFound("Material Opaque - prep_uv".to_string())
})?;
let prep_vcolor = ctx
.render_texture_views
.prep_vcolor
.as_ref()
.ok_or_else(|| {
AwsmBindGroupError::NotFound("Material Opaque - prep_vcolor".to_string())
})?;
entries.push(BindGroupEntry::new(
entries.len() as u32,
BindGroupResource::TextureView(Cow::Borrowed(prep_uv)),
));
entries.push(BindGroupEntry::new(
entries.len() as u32,
BindGroupResource::TextureView(Cow::Borrowed(prep_vcolor)),
));
let prep_shadow_visibility = ctx
.render_texture_views
.prep_shadow_visibility
.as_ref()
.ok_or_else(|| {
AwsmBindGroupError::NotFound("Material Opaque - prep_shadow_visibility".to_string())
})?;
entries.push(BindGroupEntry::new(
entries.len() as u32,
BindGroupResource::TextureView(Cow::Borrowed(prep_shadow_visibility)),
));
if ctx.anti_aliasing.msaa_sample_count.is_some() {
let prep_edge_shadow = ctx.prep_edge_shadow_view.as_ref().ok_or_else(|| {
AwsmBindGroupError::NotFound("Material Opaque - prep_edge_shadow".to_string())
})?;
entries.push(BindGroupEntry::new(
entries.len() as u32,
BindGroupResource::TextureView(Cow::Borrowed(prep_edge_shadow)),
));
}
}
let descriptor = BindGroupDescriptor::new(
ctx.bind_group_layouts
.get(if ctx.anti_aliasing.msaa_sample_count.is_some() {
self.multisampled_main_bind_group_layout_key
} else {
self.singlesampled_main_bind_group_layout_key
})?,
Some("Material Opaque - Main"),
entries,
);
self._main_bind_group = Some(ctx.gpu.create_bind_group(&descriptor.into()));
Ok(())
}
pub fn recreate_lights(&mut self, ctx: &BindGroupRecreateContext<'_>) -> Result<()> {
let mut entries = Vec::new();
entries.push(BindGroupEntry::new(
entries.len() as u32,
BindGroupResource::Buffer(BufferBinding::new(&ctx.lights.gpu_info_buffer)),
));
entries.push(BindGroupEntry::new(
entries.len() as u32,
BindGroupResource::Buffer(BufferBinding::new(&ctx.lights.gpu_punctual_buffer)),
));
entries.push(BindGroupEntry::new(
entries.len() as u32,
BindGroupResource::Buffer(BufferBinding::new(
&ctx.light_culling_buffers.storage_buffer,
)),
));
entries.push(BindGroupEntry::new(
entries.len() as u32,
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 Opaque - Lights"),
entries,
);
self._lights_bind_group = Some(ctx.gpu.create_bind_group(&descriptor.into()));
Ok(())
}
pub 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 Opaque - Shadows"),
entries,
);
self._shadows_bind_group = Some(ctx.gpu.create_bind_group(&descriptor.into()));
Ok(())
}
pub fn recreate_texture_pool(&mut self, ctx: &BindGroupRecreateContext<'_>) -> Result<()> {
let mut entries = Vec::new();
for view in ctx.textures.pool.texture_views() {
entries.push(BindGroupEntry::new(
entries.len() as u32,
BindGroupResource::TextureView(Cow::Borrowed(view)),
));
}
for sampler_key in self.texture_pool_sampler_keys.iter() {
let sampler = ctx.textures.get_sampler(*sampler_key)?;
entries.push(BindGroupEntry::new(
entries.len() as u32,
BindGroupResource::Sampler(sampler),
));
}
let descriptor = BindGroupDescriptor::new(
ctx.bind_group_layouts
.get(self.texture_pool_textures_bind_group_layout_key)?,
Some("Material Opaque - Texture Pool"),
entries,
);
self._texture_bind_group = Some(ctx.gpu.create_bind_group(&descriptor.into()));
Ok(())
}
}
async fn create_main_bind_group_layout_key(
ctx: &mut RenderPassInitContext<'_>,
multisampled_geometry: bool,
) -> Result<BindGroupLayoutKey> {
let mut entries = vec![
BindGroupLayoutCacheKeyEntry {
resource: BindGroupLayoutResource::Texture(
TextureBindingLayout::new()
.with_view_dimension(TextureViewDimension::N2d)
.with_sample_type(TextureSampleType::Uint)
.with_multisampled(multisampled_geometry),
),
visibility_vertex: false,
visibility_fragment: false,
visibility_compute: true,
},
BindGroupLayoutCacheKeyEntry {
resource: BindGroupLayoutResource::Texture(
TextureBindingLayout::new()
.with_view_dimension(TextureViewDimension::N2d)
.with_sample_type(TextureSampleType::Uint)
.with_multisampled(multisampled_geometry),
),
visibility_vertex: false,
visibility_fragment: false,
visibility_compute: true,
},
BindGroupLayoutCacheKeyEntry {
resource: BindGroupLayoutResource::Texture(
TextureBindingLayout::new()
.with_view_dimension(TextureViewDimension::N2d)
.with_sample_type(TextureSampleType::Depth)
.with_multisampled(multisampled_geometry),
),
visibility_vertex: false,
visibility_fragment: false,
visibility_compute: true,
},
BindGroupLayoutCacheKeyEntry {
resource: BindGroupLayoutResource::Texture(
TextureBindingLayout::new()
.with_view_dimension(TextureViewDimension::N2d)
.with_sample_type(TextureSampleType::UnfilterableFloat)
.with_multisampled(multisampled_geometry),
),
visibility_vertex: false,
visibility_fragment: false,
visibility_compute: true,
},
BindGroupLayoutCacheKeyEntry {
resource: BindGroupLayoutResource::Texture(
TextureBindingLayout::new()
.with_view_dimension(TextureViewDimension::N2d)
.with_sample_type(TextureSampleType::UnfilterableFloat)
.with_multisampled(multisampled_geometry),
),
visibility_vertex: false,
visibility_fragment: false,
visibility_compute: true,
},
BindGroupLayoutCacheKeyEntry {
resource: BindGroupLayoutResource::Buffer(
BufferBindingLayout::new().with_binding_type(BufferBindingType::ReadOnlyStorage),
),
visibility_vertex: false,
visibility_fragment: false,
visibility_compute: true,
},
BindGroupLayoutCacheKeyEntry {
resource: BindGroupLayoutResource::Buffer(
BufferBindingLayout::new().with_binding_type(BufferBindingType::ReadOnlyStorage),
),
visibility_vertex: false,
visibility_fragment: false,
visibility_compute: true,
},
BindGroupLayoutCacheKeyEntry {
resource: BindGroupLayoutResource::Buffer(
BufferBindingLayout::new().with_binding_type(BufferBindingType::ReadOnlyStorage),
),
visibility_vertex: false,
visibility_fragment: false,
visibility_compute: true,
},
BindGroupLayoutCacheKeyEntry {
resource: BindGroupLayoutResource::Buffer(
BufferBindingLayout::new().with_binding_type(BufferBindingType::ReadOnlyStorage),
),
visibility_vertex: false,
visibility_fragment: false,
visibility_compute: true,
},
BindGroupLayoutCacheKeyEntry {
resource: BindGroupLayoutResource::Buffer(
BufferBindingLayout::new().with_binding_type(BufferBindingType::ReadOnlyStorage),
),
visibility_vertex: false,
visibility_fragment: false,
visibility_compute: true,
},
BindGroupLayoutCacheKeyEntry {
resource: BindGroupLayoutResource::Buffer(
BufferBindingLayout::new().with_binding_type(BufferBindingType::Uniform),
),
visibility_vertex: false,
visibility_fragment: false,
visibility_compute: true,
},
BindGroupLayoutCacheKeyEntry {
resource: BindGroupLayoutResource::Texture(
TextureBindingLayout::new().with_view_dimension(TextureViewDimension::Cube),
),
visibility_vertex: false,
visibility_fragment: false,
visibility_compute: true,
},
BindGroupLayoutCacheKeyEntry {
resource: BindGroupLayoutResource::Sampler(
SamplerBindingLayout::new().with_binding_type(SamplerBindingType::Filtering),
),
visibility_vertex: false,
visibility_fragment: false,
visibility_compute: true,
},
BindGroupLayoutCacheKeyEntry {
resource: BindGroupLayoutResource::Texture(
TextureBindingLayout::new().with_view_dimension(TextureViewDimension::Cube),
),
visibility_vertex: false,
visibility_fragment: false,
visibility_compute: true,
},
BindGroupLayoutCacheKeyEntry {
resource: BindGroupLayoutResource::Sampler(
SamplerBindingLayout::new().with_binding_type(SamplerBindingType::Filtering),
),
visibility_vertex: false,
visibility_fragment: false,
visibility_compute: true,
},
BindGroupLayoutCacheKeyEntry {
resource: BindGroupLayoutResource::Texture(
TextureBindingLayout::new().with_view_dimension(TextureViewDimension::Cube),
),
visibility_vertex: false,
visibility_fragment: false,
visibility_compute: true,
},
BindGroupLayoutCacheKeyEntry {
resource: BindGroupLayoutResource::Sampler(
SamplerBindingLayout::new().with_binding_type(SamplerBindingType::Filtering),
),
visibility_vertex: false,
visibility_fragment: false,
visibility_compute: true,
},
BindGroupLayoutCacheKeyEntry {
resource: BindGroupLayoutResource::Texture(
TextureBindingLayout::new().with_view_dimension(TextureViewDimension::N2d),
),
visibility_vertex: false,
visibility_fragment: false,
visibility_compute: true,
},
BindGroupLayoutCacheKeyEntry {
resource: BindGroupLayoutResource::Sampler(
SamplerBindingLayout::new().with_binding_type(SamplerBindingType::Filtering),
),
visibility_vertex: false,
visibility_fragment: false,
visibility_compute: true,
},
BindGroupLayoutCacheKeyEntry {
resource: BindGroupLayoutResource::StorageTexture(
StorageTextureBindingLayout::new(ctx.render_texture_formats.color)
.with_view_dimension(TextureViewDimension::N2d)
.with_access(StorageTextureAccess::WriteOnly),
),
visibility_vertex: false,
visibility_fragment: false,
visibility_compute: true,
},
BindGroupLayoutCacheKeyEntry {
resource: BindGroupLayoutResource::Buffer(
BufferBindingLayout::new().with_binding_type(BufferBindingType::ReadOnlyStorage),
),
visibility_vertex: false,
visibility_fragment: false,
visibility_compute: true,
},
BindGroupLayoutCacheKeyEntry {
resource: BindGroupLayoutResource::Buffer(
BufferBindingLayout::new().with_binding_type(BufferBindingType::ReadOnlyStorage),
),
visibility_vertex: false,
visibility_fragment: false,
visibility_compute: true,
},
BindGroupLayoutCacheKeyEntry {
resource: BindGroupLayoutResource::Buffer(
BufferBindingLayout::new().with_binding_type(BufferBindingType::Uniform),
),
visibility_vertex: false,
visibility_fragment: false,
visibility_compute: true,
},
BindGroupLayoutCacheKeyEntry {
resource: BindGroupLayoutResource::Buffer(
BufferBindingLayout::new().with_binding_type(BufferBindingType::ReadOnlyStorage),
),
visibility_vertex: false,
visibility_fragment: false,
visibility_compute: true,
},
];
{
entries.push(BindGroupLayoutCacheKeyEntry {
resource: BindGroupLayoutResource::Texture(
TextureBindingLayout::new()
.with_view_dimension(TextureViewDimension::N2dArray)
.with_sample_type(TextureSampleType::UnfilterableFloat)
.with_multisampled(false),
),
visibility_vertex: false,
visibility_fragment: false,
visibility_compute: true,
});
entries.push(BindGroupLayoutCacheKeyEntry {
resource: BindGroupLayoutResource::Texture(
TextureBindingLayout::new()
.with_view_dimension(TextureViewDimension::N2dArray)
.with_sample_type(TextureSampleType::UnfilterableFloat)
.with_multisampled(false),
),
visibility_vertex: false,
visibility_fragment: false,
visibility_compute: true,
});
entries.push(BindGroupLayoutCacheKeyEntry {
resource: BindGroupLayoutResource::Texture(
TextureBindingLayout::new()
.with_view_dimension(TextureViewDimension::N2dArray)
.with_sample_type(TextureSampleType::Float)
.with_multisampled(false),
),
visibility_vertex: false,
visibility_fragment: false,
visibility_compute: true,
});
if multisampled_geometry {
entries.push(BindGroupLayoutCacheKeyEntry {
resource: BindGroupLayoutResource::Texture(
TextureBindingLayout::new()
.with_view_dimension(TextureViewDimension::N2dArray)
.with_sample_type(TextureSampleType::Float)
.with_multisampled(false),
),
visibility_vertex: false,
visibility_fragment: false,
visibility_compute: true,
});
}
}
Ok(ctx
.bind_group_layouts
.get_key(ctx.gpu, BindGroupLayoutCacheKey { entries })?)
}