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_scheduler::warn_pipeline_not_compiled,
render::RenderContext,
render_passes::{
material_classify::buffers::indirect_args_offset,
material_opaque::{
bind_group::MaterialOpaqueBindGroups, edge_bind_group::MaterialEdgeBindGroupLayouts,
edge_buffers::MaterialEdgeBuffers, edge_pipeline::MaterialEdgePipelines,
pipeline::MaterialOpaquePipelines,
},
shared::material::bind_group::build_shadow_bind_group_entries,
RenderPassInitContext,
},
renderable::Renderable,
};
pub struct MaterialOpaqueRenderPass {
pub bind_groups: MaterialOpaqueBindGroups,
pub pipelines: MaterialOpaquePipelines,
pub edge_pipelines: MaterialEdgePipelines,
pub edge_bind_group_layouts: MaterialEdgeBindGroupLayouts,
}
impl MaterialOpaqueRenderPass {
pub async fn new(ctx: &mut RenderPassInitContext<'_>) -> Result<Self> {
let bind_groups = MaterialOpaqueBindGroups::new(ctx).await?;
let pipelines = MaterialOpaquePipelines::new(ctx, &bind_groups).await?;
let edge_bind_group_layouts = MaterialEdgeBindGroupLayouts::new(ctx)?;
let edge_pipelines = MaterialEdgePipelines::new();
Ok(Self {
bind_groups,
pipelines,
edge_pipelines,
edge_bind_group_layouts,
})
}
pub async fn texture_pool_changed(
&mut self,
ctx: &mut RenderPassInitContext<'_>,
) -> Result<()> {
self.bind_groups = self.bind_groups.clone_because_texture_pool_changed(ctx)?;
self.pipelines = MaterialOpaquePipelines::new(ctx, &self.bind_groups).await?;
Ok(())
}
pub fn render_shade(&self, ctx: &RenderContext, _renderables: &[Renderable]) -> Result<()> {
if ctx.anti_aliasing.msaa_sample_count.is_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),
_ => {
warn_pipeline_not_compiled(
"material_opaque::shade",
"edge buffers / layout uniform missing",
);
return Ok(());
}
};
let Some(edge_id_view) = ctx.render_texture_views.edge_id.as_ref() else {
warn_pipeline_not_compiled("material_opaque::shade", "edge_id texture view missing");
return Ok(());
};
let bucket_entries = ctx.dynamic_materials.bucket_entries_cached();
let shade_group =
self.build_shade_bind_group(ctx, edge_buffers, edge_layout_uniform, edge_id_view)?;
let (main_bind_group, lights_bind_group, texture_bind_group, _shadows_bind_group) =
self.bind_groups.get_bind_groups()?;
let classify_buffer = &ctx.material_classify_buffers.buffer;
{
let compute_pass = ctx.command_encoder.begin_compute_pass(Some(
&ComputePassDescriptor::new(Some("Material Opaque - Unified Shade")).into(),
));
compute_pass.set_bind_group(0u32, main_bind_group, None)?;
compute_pass.set_bind_group(1u32, lights_bind_group, None)?;
compute_pass.set_bind_group(2u32, texture_bind_group, None)?;
compute_pass.set_bind_group(3u32, &shade_group, None)?;
for (bucket_index, entry) in bucket_entries.iter().enumerate() {
let Some(pipeline_key) = self
.edge_pipelines
.get_shade_pipeline_key(ctx.anti_aliasing, entry.shader_id)
else {
continue;
};
compute_pass.set_pipeline(ctx.pipelines.compute.get(pipeline_key)?);
compute_pass.dispatch_workgroups_indirect_with_u32(
classify_buffer,
indirect_args_offset(bucket_index as u32),
);
}
compute_pass.end();
}
if let Some(pipeline_key) = self.edge_pipelines.final_blend_pipeline_key {
let final_blend_group =
self.build_edge_bind_groups(ctx, edge_buffers, edge_layout_uniform)?;
let compute_pass = ctx.command_encoder.begin_compute_pass(Some(
&ComputePassDescriptor::new(Some("Material Opaque - Unified Final Blend")).into(),
));
compute_pass.set_pipeline(ctx.pipelines.compute.get(pipeline_key)?);
compute_pass.set_bind_group(0u32, &final_blend_group, None)?;
compute_pass.dispatch_workgroups_indirect_with_u32(
&edge_buffers.args_buffer,
MaterialEdgeBuffers::final_blend_args_offset(),
);
compute_pass.end();
} else {
warn_pipeline_not_compiled("material_opaque::shade", "final_blend");
}
Ok(())
}
fn build_shade_bind_group(
&self,
ctx: &RenderContext,
edge_buffers: &MaterialEdgeBuffers,
edge_layout_uniform: &web_sys::GpuBuffer,
edge_id_view: &web_sys::GpuTextureView,
) -> Result<web_sys::GpuBindGroup> {
let layouts = &self.edge_bind_group_layouts;
let mut entries = build_shadow_bind_group_entries(ctx.shadows);
entries.push(BindGroupEntry::new(
10,
BindGroupResource::Buffer(BufferBinding::new(&edge_buffers.data_buffer)),
));
entries.push(BindGroupEntry::new(
11,
BindGroupResource::Buffer(BufferBinding::new(edge_layout_uniform)),
));
entries.push(BindGroupEntry::new(
12,
BindGroupResource::TextureView(Cow::Borrowed(edge_id_view)),
));
let descriptor = BindGroupDescriptor::new(
ctx.bind_group_layouts
.get(layouts.shade_extended_shadows_layout_key)?,
Some("Material Unified Shade - Extended Shadows (Group 3)"),
entries,
);
Ok(ctx.gpu.create_bind_group(&descriptor.into()))
}
fn build_edge_bind_groups(
&self,
ctx: &RenderContext,
edge_buffers: &MaterialEdgeBuffers,
edge_layout_uniform: &web_sys::GpuBuffer,
) -> Result<web_sys::GpuBindGroup> {
let layouts = &self.edge_bind_group_layouts;
let entries_final = 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(&ctx.render_texture_views.opaque)),
),
];
let descriptor_final = BindGroupDescriptor::new(
ctx.bind_group_layouts
.get(layouts.final_blend_group0_layout_key)?,
Some("Material Final Blend - Group 0"),
entries_final,
);
let final_blend_group = ctx.gpu.create_bind_group(&descriptor_final.into());
Ok(final_blend_group)
}
pub fn render(&self, ctx: &RenderContext, _renderables: &[Renderable]) -> Result<()> {
let compute_pass = ctx.command_encoder.begin_compute_pass(Some(
&ComputePassDescriptor::new(Some("Material Opaque Pass")).into(),
));
let (main_bind_group, lights_bind_group, texture_bind_group, shadows_bind_group) =
self.bind_groups.get_bind_groups()?;
compute_pass.set_bind_group(0u32, main_bind_group, None)?;
compute_pass.set_bind_group(1u32, lights_bind_group, None)?;
compute_pass.set_bind_group(2u32, texture_bind_group, None)?;
compute_pass.set_bind_group(3u32, shadows_bind_group, None)?;
let classify_buffer = &ctx.material_classify_buffers.buffer;
let bucket_entries = ctx.dynamic_materials.bucket_entries_cached();
for (bucket_index, entry) in bucket_entries.iter().enumerate() {
let Some(pipeline_key) = self
.pipelines
.get_compute_pipeline_key(ctx.anti_aliasing, entry.shader_id)
else {
continue;
};
compute_pass.set_pipeline(ctx.pipelines.compute.get(pipeline_key)?);
compute_pass.dispatch_workgroups_indirect_with_u32(
classify_buffer,
indirect_args_offset(bucket_index as u32),
);
}
compute_pass.end();
Ok(())
}
}