use std::borrow::Cow;
use awsm_renderer_core::bind_groups::{BindGroupDescriptor, BindGroupEntry, BindGroupResource};
use awsm_renderer_core::buffers::BufferBinding;
use awsm_renderer_core::command::compute_pass::ComputePassDescriptor;
use crate::{
error::Result,
pipeline_layouts::PipelineLayoutCacheKey,
pipelines::compute_pipeline::{ComputePipelineCacheKey, ComputePipelineKey},
render::RenderContext,
render_passes::{
material_opaque::edge_buffers::MaterialEdgeBuffers,
material_prep::{
bind_group::MaterialPrepBindGroups, buffers::EdgeShadowBuffer,
shader::cache_key::ShaderCacheKeyMaterialPrep,
},
RenderPassInitContext,
},
};
pub struct MaterialPrepRenderPass {
pub bind_groups: MaterialPrepBindGroups,
pub multisampled_pipeline_key: ComputePipelineKey,
pub singlesampled_pipeline_key: ComputePipelineKey,
pub edge_pipeline_key: Option<ComputePipelineKey>,
pub edge_shadow: Option<EdgeShadowBuffer>,
}
impl MaterialPrepRenderPass {
pub async fn new(ctx: &mut RenderPassInitContext<'_>) -> Result<Self> {
let bind_groups = MaterialPrepBindGroups::new(ctx).await?;
let multisampled_pipeline_key = build_pipeline(ctx, &bind_groups, true).await?;
let singlesampled_pipeline_key = build_pipeline(ctx, &bind_groups, false).await?;
let edge_pipeline_key = Some(build_edge_pipeline(ctx, &bind_groups).await?);
let edge_shadow = Some(EdgeShadowBuffer::new(
ctx.gpu,
ctx.max_edge_budget,
ctx.prep_config.shadow_visibility_layers(),
)?);
Ok(Self {
bind_groups,
multisampled_pipeline_key,
singlesampled_pipeline_key,
edge_pipeline_key,
edge_shadow,
})
}
pub fn render(&self, ctx: &RenderContext) -> Result<()> {
let pipeline_key = if ctx.anti_aliasing.msaa_sample_count.is_some() {
self.multisampled_pipeline_key
} else {
self.singlesampled_pipeline_key
};
let pipeline = ctx.pipelines.compute.get(pipeline_key)?;
let bind_group = self.bind_groups.get_bind_group()?;
let lights_bind_group = self.bind_groups.get_lights_bind_group()?;
let shadows_bind_group = self.bind_groups.get_shadows_bind_group()?;
let compute_pass = ctx.command_encoder.begin_compute_pass(Some(
&ComputePassDescriptor::new(Some("Material Prep Pass")).into(),
));
compute_pass.set_pipeline(pipeline);
compute_pass.set_bind_group(0, bind_group, None)?;
compute_pass.set_bind_group(1, lights_bind_group, None)?;
compute_pass.set_bind_group(2, shadows_bind_group, None)?;
let workgroups_x = ctx.render_texture_views.width.div_ceil(8);
let workgroups_y = ctx.render_texture_views.height.div_ceil(8);
compute_pass.dispatch_workgroups(workgroups_x, Some(workgroups_y), Some(1));
compute_pass.end();
Ok(())
}
pub fn set_max_edge_budget(
&mut self,
gpu: &awsm_renderer_core::renderer::AwsmRendererWebGpu,
new_budget: u32,
) -> Result<bool> {
let Some(existing) = self.edge_shadow.as_ref() else {
return Ok(false);
};
if existing.max_edge_budget == new_budget.max(1) {
return Ok(false);
}
let layers = existing.layers;
self.edge_shadow = Some(EdgeShadowBuffer::new(gpu, new_budget, layers)?);
Ok(true)
}
pub fn render_edge(&self, ctx: &RenderContext) -> Result<()> {
let (edge_pipeline_key, edge_shadow) =
match (self.edge_pipeline_key, self.edge_shadow.as_ref()) {
(Some(k), Some(b)) => (k, b),
_ => return Ok(()),
};
let edge_bgl_key = match self.bind_groups.edge_bind_group_layout_key {
Some(k) => k,
None => return Ok(()),
};
let (edge_buffers, edge_layout_uniform) =
match (ctx.material_edge_buffers, ctx.material_edge_layout_uniform) {
(Some(b), Some(u)) => (b, u),
_ => return Ok(()),
};
let pipeline = ctx.pipelines.compute.get(edge_pipeline_key)?;
let bind_group = self.bind_groups.get_bind_group()?;
let lights_bind_group = self.bind_groups.get_lights_bind_group()?;
let shadows_bind_group = self.bind_groups.get_shadows_bind_group()?;
let entries = vec![
BindGroupEntry::new(
0,
BindGroupResource::Buffer(BufferBinding::new(&edge_buffers.data_buffer)),
),
BindGroupEntry::new(
1,
BindGroupResource::Buffer(BufferBinding::new(edge_layout_uniform)),
),
BindGroupEntry::new(
2,
BindGroupResource::TextureView(Cow::Borrowed(&edge_shadow.storage_view)),
),
];
let descriptor = BindGroupDescriptor::new(
ctx.bind_group_layouts.get(edge_bgl_key)?,
Some("Material Prep Edge - Group 3"),
entries,
);
let edge_bind_group = ctx.gpu.create_bind_group(&descriptor.into());
let compute_pass = ctx.command_encoder.begin_compute_pass(Some(
&ComputePassDescriptor::new(Some("Material Prep Edge Pass")).into(),
));
compute_pass.set_pipeline(pipeline);
compute_pass.set_bind_group(0, bind_group, None)?;
compute_pass.set_bind_group(1, lights_bind_group, None)?;
compute_pass.set_bind_group(2, shadows_bind_group, None)?;
compute_pass.set_bind_group(3, &edge_bind_group, None)?;
compute_pass.dispatch_workgroups_indirect_with_u32(
&edge_buffers.args_buffer,
MaterialEdgeBuffers::final_blend_args_offset(),
);
compute_pass.end();
Ok(())
}
}
async fn build_pipeline(
ctx: &mut RenderPassInitContext<'_>,
bind_groups: &MaterialPrepBindGroups,
multisampled_geometry: bool,
) -> Result<ComputePipelineKey> {
let bgl_key = if multisampled_geometry {
bind_groups.multisampled_bind_group_layout_key
} else {
bind_groups.singlesampled_bind_group_layout_key
};
let pipeline_layout_key = ctx.pipeline_layouts.get_key(
ctx.gpu,
ctx.bind_group_layouts,
PipelineLayoutCacheKey::new(vec![
bgl_key,
bind_groups.lights_bind_group_layout_key,
bind_groups.shadows_bind_group_layout_key,
]),
)?;
let shader_key = ctx
.shaders
.get_key(
ctx.gpu,
ShaderCacheKeyMaterialPrep {
msaa_sample_count: if multisampled_geometry { Some(4) } else { None },
max_shadow_casters: ctx.prep_config.clamped_k(),
},
)
.await?;
let pipeline_key = ctx
.pipelines
.compute
.get_key(
ctx.gpu,
ctx.shaders,
ctx.pipeline_layouts,
ComputePipelineCacheKey::new(shader_key, pipeline_layout_key)
.with_entry_point("cs_prep"),
)
.await?;
Ok(pipeline_key)
}
async fn build_edge_pipeline(
ctx: &mut RenderPassInitContext<'_>,
bind_groups: &MaterialPrepBindGroups,
) -> Result<ComputePipelineKey> {
let edge_bgl_key = bind_groups
.edge_bind_group_layout_key
.expect("edge bind group layout must exist under MSAA");
let pipeline_layout_key = ctx.pipeline_layouts.get_key(
ctx.gpu,
ctx.bind_group_layouts,
PipelineLayoutCacheKey::new(vec![
bind_groups.multisampled_bind_group_layout_key,
bind_groups.lights_bind_group_layout_key,
bind_groups.shadows_bind_group_layout_key,
edge_bgl_key,
]),
)?;
let shader_key = ctx
.shaders
.get_key(
ctx.gpu,
ShaderCacheKeyMaterialPrep {
msaa_sample_count: Some(4),
max_shadow_casters: ctx.prep_config.clamped_k(),
},
)
.await?;
let pipeline_key = ctx
.pipelines
.compute
.get_key(
ctx.gpu,
ctx.shaders,
ctx.pipeline_layouts,
ComputePipelineCacheKey::new(shader_key, pipeline_layout_key)
.with_entry_point("cs_prep_edge"),
)
.await?;
Ok(pipeline_key)
}