use awsm_renderer_core::command::compute_pass::ComputePassDescriptor;
use awsm_renderer_core::renderer::AwsmRendererWebGpu;
use glam::{Mat4, Vec3};
use crate::bind_group_layout::BindGroupLayouts;
use crate::cluster_lod::ClusterPage;
use crate::error::Result;
use crate::meshes::MeshKey;
use crate::render::RenderContext;
use crate::render_passes::cluster_lod::{
bind_group::{ClusterCompactionBindGroups, ClusterCutBindGroups},
buffers::ClusterLodBuffers,
pipeline::ClusterLodPipelines,
};
use crate::render_passes::RenderPassInitContext;
pub struct ClusterLodRenderPass {
pub bind_groups: ClusterCutBindGroups,
pub compaction_bind_groups: ClusterCompactionBindGroups,
pub pipelines: ClusterLodPipelines,
pub buffers: Option<ClusterLodBuffers>,
pub cluster_count: u32,
pub render_mesh: Option<MeshKey>,
}
impl ClusterLodRenderPass {
pub async fn new(ctx: &mut RenderPassInitContext<'_>) -> Result<Self> {
let bind_groups = ClusterCutBindGroups::new(ctx)?;
let compaction_bind_groups = ClusterCompactionBindGroups::new(ctx)?;
let pipelines =
ClusterLodPipelines::new(ctx, &bind_groups, &compaction_bind_groups).await?;
Ok(Self {
bind_groups,
compaction_bind_groups,
pipelines,
buffers: None,
cluster_count: 0,
render_mesh: None,
})
}
pub fn upload_pages(
&mut self,
gpu: &AwsmRendererWebGpu,
layouts: &BindGroupLayouts,
pages: &[ClusterPage],
indices: &[u32],
) -> Result<()> {
let count = pages.len() as u32;
let index_count = indices.len() as u32;
let buffers = match self.buffers.as_mut() {
Some(b) => {
b.ensure_capacity(gpu, count, index_count)?;
b
}
None => {
self.buffers = Some(ClusterLodBuffers::with_capacity(
gpu,
count.max(1),
index_count.max(3),
)?);
self.buffers.as_mut().unwrap()
}
};
buffers.write_pages(gpu, pages)?;
buffers.write_source_indices(gpu, indices)?;
self.cluster_count = count;
let buffers = self.buffers.as_ref().unwrap();
self.bind_groups.recreate(gpu, layouts, buffers)?;
self.compaction_bind_groups
.recreate(gpu, layouts, buffers)?;
Ok(())
}
pub fn dispatch(
&self,
ctx: &RenderContext,
cam_pos: Vec3,
tan_half_fov_y: f32,
viewport_h: f32,
pixel_budget: f32,
) -> Result<()> {
let Some(buffers) = self.buffers.as_ref() else {
return Ok(());
};
if self.cluster_count == 0 {
return Ok(());
}
buffers.write_params(
ctx.gpu,
&Mat4::IDENTITY,
cam_pos,
tan_half_fov_y,
viewport_h,
pixel_budget,
1.0,
self.cluster_count,
)?;
let cp = ctx.command_encoder.begin_compute_pass(Some(
&ComputePassDescriptor::new(Some("Cluster Cut")).into(),
));
cp.set_pipeline(ctx.pipelines.compute.get(self.pipelines.cut)?);
cp.set_bind_group(0, self.bind_groups.get_bind_group()?, None)?;
cp.dispatch_workgroups(
ClusterLodBuffers::dispatch_groups(self.cluster_count),
Some(1),
Some(1),
);
cp.end();
Ok(())
}
pub fn dispatch_compaction(&self, ctx: &RenderContext, first_instance: u32) -> Result<()> {
let Some(buffers) = self.buffers.as_ref() else {
return Ok(());
};
if self.cluster_count == 0 {
return Ok(());
}
buffers.init_draw_args(ctx.gpu, first_instance)?;
let cp = ctx.command_encoder.begin_compute_pass(Some(
&ComputePassDescriptor::new(Some("Cluster Compaction")).into(),
));
cp.set_pipeline(ctx.pipelines.compute.get(self.pipelines.compaction)?);
cp.set_bind_group(0, self.compaction_bind_groups.get_bind_group()?, None)?;
cp.dispatch_workgroups(
ClusterLodBuffers::dispatch_groups(self.cluster_count),
Some(1),
Some(1),
);
cp.end();
Ok(())
}
}