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_edge_resolve(&self, ctx: &RenderContext) -> Result<()> {
if ctx.anti_aliasing.msaa_sample_count.is_none() {
return Ok(());
}
if self.edge_pipelines.final_blend_pipeline_key.is_none() {
warn_pipeline_not_compiled("material_opaque::edge_resolve", "final_blend");
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::edge_resolve",
"edge buffers / layout uniform missing",
);
return Ok(());
}
};
let bucket_entries = ctx.dynamic_materials.bucket_entries_cached();
let (extended_shadows_group, skybox_edge_group, final_blend_group) =
self.build_edge_bind_groups(ctx, edge_buffers, edge_layout_uniform)?;
let (main_bind_group, lights_bind_group, texture_bind_group, _shadows_bind_group) =
self.bind_groups.get_bind_groups()?;
let compute_pass = ctx.command_encoder.begin_compute_pass(Some(
&ComputePassDescriptor::new(Some("Material Opaque - Edge Resolve")).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, &extended_shadows_group, None)?;
for (bucket_index, entry) in bucket_entries.iter().enumerate() {
let Some(pipeline_key) = self
.edge_pipelines
.get_per_shader_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(
&edge_buffers.args_buffer,
MaterialEdgeBuffers::per_shader_args_offset(bucket_index as u32),
);
}
if let Some(skybox_pipeline_key) = self.edge_pipelines.skybox_edge_resolve_pipeline_key {
compute_pass.set_pipeline(ctx.pipelines.compute.get(skybox_pipeline_key)?);
compute_pass.set_bind_group(0u32, &skybox_edge_group, None)?;
compute_pass.dispatch_workgroups_indirect_with_u32(
&edge_buffers.args_buffer,
MaterialEdgeBuffers::skybox_edge_args_offset(),
);
}
if let Some(pipeline_key) = self.edge_pipelines.final_blend_pipeline_key {
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();
Ok(())
}
fn build_edge_bind_groups(
&self,
ctx: &RenderContext,
edge_buffers: &MaterialEdgeBuffers,
edge_layout_uniform: &web_sys::GpuBuffer,
) -> Result<(
web_sys::GpuBindGroup,
web_sys::GpuBindGroup,
web_sys::GpuBindGroup,
)> {
let layouts = &self.edge_bind_group_layouts;
let mut entries_shadows = build_shadow_bind_group_entries(ctx.shadows);
entries_shadows.push(BindGroupEntry::new(
10,
BindGroupResource::Buffer(BufferBinding::new(&edge_buffers.data_buffer)),
));
entries_shadows.push(BindGroupEntry::new(
11,
BindGroupResource::Buffer(BufferBinding::new(edge_layout_uniform)),
));
let descriptor_shadows = BindGroupDescriptor::new(
ctx.bind_group_layouts
.get(layouts.edge_resolve_extended_shadows_layout_key)?,
Some("Material Edge Resolve - Extended Shadows (Group 3)"),
entries_shadows,
);
let extended_shadows_group = ctx.gpu.create_bind_group(&descriptor_shadows.into());
let entries_sky = 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::Buffer(BufferBinding::new(&ctx.camera.gpu_buffer)),
),
BindGroupEntry::new(
3,
BindGroupResource::TextureView(Cow::Borrowed(&ctx.environment.skybox.texture_view)),
),
BindGroupEntry::new(
4,
BindGroupResource::Sampler(&ctx.environment.skybox.sampler),
),
];
let descriptor_sky = BindGroupDescriptor::new(
ctx.bind_group_layouts
.get(layouts.skybox_edge_group0_layout_key)?,
Some("Material Skybox Edge Resolve - Group 0"),
entries_sky,
);
let skybox_edge_group = ctx.gpu.create_bind_group(&descriptor_sky.into());
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((extended_shadows_group, skybox_edge_group, 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(())
}
}